diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..842b503ffa3924b5327ceed30ddd85ac9feccb21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# 忽略所有 si4project 目录及其内容 +**/aosp/huaweicloud-cloud-phone-aosp14.si4project/ +# 若有其他 si4project 文件,可添加更通用的规则 +*.si4project/ +*.si4project diff --git a/aosp/external/ccyagent/Agentbin b/aosp/external/ccyagent/Agentbin new file mode 100755 index 0000000000000000000000000000000000000000..79c06f97cc257677d326c47229c5cbfe35f9f77d Binary files /dev/null and b/aosp/external/ccyagent/Agentbin differ diff --git a/aosp/external/ccyagent/Android.mk b/aosp/external/ccyagent/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..fc9808e9df04806129af2f238d3ae09af6093ae8 --- /dev/null +++ b/aosp/external/ccyagent/Android.mk @@ -0,0 +1,29 @@ +LOCAL_PATH := $(call my-dir) +DEMOCOMMON_DIR := $(LOCAL_PATH) + +######################################################################## +# CcyAgent +######################################################################## +include $(CLEAR_VARS) +LOCAL_MODULE := Agentbin +LOCAL_INIT_RC := CcyAgentAndroidP.rc +LOCAL_SRC_FILES := Agentbin # Remove the ./ + +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES) + +LOCAL_SHARED_LIBRARIES := libbase libbinder libc++ libcrypto libcurl \ + libcutils libgui libhwui libjsoncpp liblog \ + libjpeg libpng libssl libui libutils libyuv + +# For prebuilt executables, we don't need to specify shared libraries +# as they should already be linked in the binary + +ifeq ($(ENABLE_ASAN), 1) +LOCAL_SANITIZE := hwaddress alignment bounds null unreachable integer +LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer +endif + + +include $(BUILD_PREBUILT) # Changed from BUILD_EXECUTABLE to BUILD_PREBUILT diff --git a/aosp/external/ccyagent/CcyAgentAndroidP.rc b/aosp/external/ccyagent/CcyAgentAndroidP.rc new file mode 100644 index 0000000000000000000000000000000000000000..79046b3a4c98f2bf58431d891138bb8d45082e3a --- /dev/null +++ b/aosp/external/ccyagent/CcyAgentAndroidP.rc @@ -0,0 +1,5 @@ +service CcyAgent /system/bin/Agentbin + class core + user root + group root + onrestart restart RTCPushService diff --git a/aosp/external/ccyagent/Common/Application.mk b/aosp/external/ccyagent/Common/Application.mk new file mode 100755 index 0000000000000000000000000000000000000000..ecad01af3f99b650abca22bacb61249ba1822a55 --- /dev/null +++ b/aosp/external/ccyagent/Common/Application.mk @@ -0,0 +1,6 @@ +NDK_TOOLCHAIN_VERSION := clang + +APP_ABI := arm64-v8a armeabi-v7a +APP_PLATFORM := android-24 +APP_CPPFLAGS += -std=c++14 -fexceptions +APP_STL := c++_shared diff --git a/aosp/external/ccyagent/Common/shell/startpp.sh b/aosp/external/ccyagent/Common/shell/startpp.sh new file mode 100755 index 0000000000000000000000000000000000000000..4a06d49a08d8736b831be2d04a96d08ba08672b7 --- /dev/null +++ b/aosp/external/ccyagent/Common/shell/startpp.sh @@ -0,0 +1,19 @@ +#!/system/bin/sh + +# This script is used to start the pp service with a specified configuration file. +# Usage: startpp.sh [config_file] +CONFIG_FILE="/etc/ac/pp.ini" +if [ -n "$1" ]; then + CONFIG_FILE="$1" +fi +if [ ! -f "$CONFIG_FILE" ]; then + echo "error: Configuration file not found: $CONFIG_FILE" + exit 1 +fi +# Start the pp +/system/bin/pp & +if [ $? -ne 0 ]; then + echo "error: Failed to start pp " + exit 1 +fi +echo "pp started successfully " diff --git a/aosp/external/ccyexpansion/Android.mk b/aosp/external/ccyexpansion/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..455753ee615f36be40e6425a0d5687a3d5ef5490 --- /dev/null +++ b/aosp/external/ccyexpansion/Android.mk @@ -0,0 +1,25 @@ + +LOCAL_PATH := $(call my-dir) +DEMOCOMMON_DIR := $(LOCAL_PATH) + +######################################################################## +# CcyAgent +######################################################################## +include $(CLEAR_VARS) +LOCAL_MODULE := androidexservice +LOCAL_INIT_RC := CcyExpansionService.rc +LOCAL_SRC_FILES := androidexservice # Remove the ./ + +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES) + +LOCAL_SHARED_LIBRARIES := libbinder libc++ libcurl \ + libcutils libjsoncpp liblog \ + libutils + +# For prebuilt executables, we don't need to specify shared libraries +# as they should already be linked in the binary + + +include $(BUILD_PREBUILT) # Changed from BUILD_EXECUTABLE to BUILD_PREBUILT diff --git a/aosp/external/ccyexpansion/CcyExpansionService.rc b/aosp/external/ccyexpansion/CcyExpansionService.rc new file mode 100755 index 0000000000000000000000000000000000000000..71b59993a5ac8f9839ccfe9acce25d5608bde710 --- /dev/null +++ b/aosp/external/ccyexpansion/CcyExpansionService.rc @@ -0,0 +1,9 @@ +service CcyEpService /system/bin/androidexservice + class main + user root + group root + oneshot + disabled + +on property:sys.boot_completed=1 + start CcyEpService \ No newline at end of file diff --git a/aosp/external/ccyexpansion/CcyExpansionService.te b/aosp/external/ccyexpansion/CcyExpansionService.te new file mode 100755 index 0000000000000000000000000000000000000000..a209d05377cc467def8f65e133fb54de2999474a --- /dev/null +++ b/aosp/external/ccyexpansion/CcyExpansionService.te @@ -0,0 +1,13 @@ +type CcyExpansionService, domain; +type CcyExpansionService_exec, exec_type, file_type; + +init_daemon_domain(CcyExpansionService) +typeattribute CcyExpansionService mlstrustedsubject; + +# Perform Binder IPC to system server. +binder_use(CcyExpansionService) +binder_call(CcyExpansionService, system_server) +binder_call(CcyExpansionService, appdomain) +binder_service(CcyExpansionService) + +allow CcyExpansionService CcyExpansionService_service:service_manager add; diff --git a/aosp/external/ccyexpansion/androidexservice b/aosp/external/ccyexpansion/androidexservice new file mode 100755 index 0000000000000000000000000000000000000000..465578d7042cad9c53939139c095780e751df1c5 Binary files /dev/null and b/aosp/external/ccyexpansion/androidexservice differ diff --git a/aosp/external/openssh/.depend b/aosp/external/openssh/.depend new file mode 100644 index 0000000000000000000000000000000000000000..cd38d15f8f52d2225a17c35d8c2c87ac2a0eb394 --- /dev/null +++ b/aosp/external/openssh/.depend @@ -0,0 +1,182 @@ +# Automatically generated by makedepend. +# Run "make depend" to rebuild. + +# DO NOT DELETE +addr.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h addr.h +addrmatch.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h addr.h match.h log.h ssherr.h +atomicio.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h atomicio.h +audit-bsm.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +audit-linux.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +audit.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +auth-bsdauth.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +auth-krb5.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssh.h packet.h openbsd-compat/sys-queue.h dispatch.h log.h ssherr.h sshbuf.h sshkey.h misc.h servconf.h uidswap.h hostfile.h auth.h auth-pam.h audit.h loginrec.h +auth-options.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssherr.h log.h sshbuf.h misc.h sshkey.h match.h ssh2.h auth-options.h +auth-pam.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +auth-passwd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h packet.h openbsd-compat/sys-queue.h dispatch.h sshbuf.h ssherr.h log.h misc.h servconf.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h +auth-rhosts.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h packet.h openbsd-compat/sys-queue.h dispatch.h uidswap.h pathnames.h log.h ssherr.h misc.h xmalloc.h sshbuf.h sshkey.h servconf.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h +auth-shadow.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +auth-sia.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +auth.o: authfile.h monitor_wrap.h compat.h channels.h +auth.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h match.h groupaccess.h log.h ssherr.h sshbuf.h misc.h servconf.h openbsd-compat/sys-queue.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h canohost.h uidswap.h packet.h dispatch.h +auth2-chall.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssh2.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h sshbuf.h packet.h openbsd-compat/sys-queue.h dispatch.h ssherr.h log.h misc.h servconf.h +auth2-gss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +auth2-hostbased.o: canohost.h monitor_wrap.h pathnames.h match.h +auth2-hostbased.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssh2.h packet.h openbsd-compat/sys-queue.h dispatch.h kex.h mac.h crypto_api.h sshbuf.h log.h ssherr.h misc.h servconf.h compat.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h +auth2-kbdint.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h packet.h openbsd-compat/sys-queue.h dispatch.h hostfile.h auth.h auth-pam.h audit.h loginrec.h log.h ssherr.h misc.h servconf.h +auth2-none.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h atomicio.h xmalloc.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h packet.h openbsd-compat/sys-queue.h dispatch.h log.h ssherr.h misc.h servconf.h compat.h ssh2.h monitor_wrap.h +auth2-passwd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h packet.h openbsd-compat/sys-queue.h dispatch.h ssherr.h log.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h monitor_wrap.h misc.h servconf.h +auth2-pubkey.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssh.h ssh2.h packet.h openbsd-compat/sys-queue.h dispatch.h kex.h mac.h crypto_api.h sshbuf.h log.h ssherr.h misc.h servconf.h compat.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h +auth2-pubkey.o: pathnames.h uidswap.h auth-options.h canohost.h monitor_wrap.h authfile.h match.h channels.h session.h sk-api.h +auth2.o: digest.h +auth2.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h atomicio.h xmalloc.h ssh2.h packet.h openbsd-compat/sys-queue.h dispatch.h log.h ssherr.h sshbuf.h misc.h servconf.h compat.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h pathnames.h monitor_wrap.h +authfd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssh.h sshbuf.h sshkey.h authfd.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h compat.h log.h ssherr.h atomicio.h misc.h +authfile.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h ssh.h log.h ssherr.h authfile.h misc.h atomicio.h sshkey.h sshbuf.h krl.h +bitmap.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h bitmap.h +canohost.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h packet.h openbsd-compat/sys-queue.h dispatch.h log.h ssherr.h canohost.h misc.h +chacha.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h chacha.h +channels.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h ssherr.h sshbuf.h packet.h dispatch.h log.h misc.h channels.h compat.h canohost.h sshkey.h authfd.h pathnames.h match.h +cipher-aes.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/openssl-compat.h +cipher-aesctr.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h cipher-aesctr.h rijndael.h +cipher-chachapoly-libcrypto.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +cipher-chachapoly.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h sshbuf.h cipher-chachapoly.h chacha.h poly1305.h +cipher-ctr.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +cipher.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h misc.h sshbuf.h ssherr.h digest.h openbsd-compat/openssl-compat.h +cleanup.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h +clientloop.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h packet.h dispatch.h sshbuf.h compat.h channels.h sshkey.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h kex.h mac.h crypto_api.h +clientloop.o: myproposal.h log.h ssherr.h misc.h readconf.h clientloop.h sshconnect.h authfd.h atomicio.h sshpty.h match.h msg.h hostfile.h +compat.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h packet.h openbsd-compat/sys-queue.h dispatch.h compat.h log.h ssherr.h match.h kex.h mac.h crypto_api.h +dh.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +digest-libc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h sshbuf.h digest.h +digest-openssl.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +dispatch.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssh2.h log.h ssherr.h dispatch.h packet.h openbsd-compat/sys-queue.h compat.h +dns.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h sshkey.h ssherr.h dns.h log.h digest.h +ed25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h ge25519.h fe25519.h sc25519.h +entropy.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +fatal.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h +fe25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h fe25519.h crypto_api.h +ge25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h fe25519.h crypto_api.h sc25519.h ge25519.h ge25519_base.data +groupaccess.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h groupaccess.h match.h log.h ssherr.h +gss-genr.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +gss-serv-krb5.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +gss-serv.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +hash.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h +hmac.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshbuf.h digest.h hmac.h +hostfile.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h match.h sshkey.h hostfile.h log.h ssherr.h misc.h pathnames.h digest.h hmac.h sshbuf.h +kex.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssh.h ssh2.h atomicio.h version.h packet.h openbsd-compat/sys-queue.h dispatch.h compat.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h sshkey.h kex.h mac.h crypto_api.h log.h ssherr.h +kex.o: match.h misc.h monitor.h sshbuf.h digest.h +kexc25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshkey.h kex.h mac.h crypto_api.h sshbuf.h digest.h ssherr.h ssh2.h +kexdh.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +kexecdh.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h +kexgen.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshkey.h kex.h mac.h crypto_api.h log.h ssherr.h packet.h openbsd-compat/sys-queue.h dispatch.h ssh2.h sshbuf.h digest.h +kexgex.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +kexgexc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +kexgexs.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +kexsntrup761x25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h +krl.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ./openbsd-compat/sys-tree.h openbsd-compat/sys-queue.h sshbuf.h ssherr.h sshkey.h authfile.h misc.h log.h digest.h bitmap.h utf8.h krl.h +log.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h match.h +loginrec.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h sshkey.h hostfile.h ssh.h loginrec.h log.h ssherr.h atomicio.h packet.h openbsd-compat/sys-queue.h dispatch.h canohost.h auth.h auth-pam.h audit.h sshbuf.h misc.h +logintest.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h loginrec.h +mac.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h digest.h hmac.h umac.h mac.h misc.h ssherr.h sshbuf.h openbsd-compat/openssl-compat.h +match.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h match.h misc.h +misc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h misc.h log.h ssherr.h ssh.h sshbuf.h +moduli.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +monitor.o: chacha.h poly1305.h cipher-aesctr.h rijndael.h kex.h mac.h crypto_api.h dh.h packet.h dispatch.h auth-options.h sshpty.h channels.h session.h sshlogin.h canohost.h log.h ssherr.h misc.h servconf.h monitor.h monitor_wrap.h monitor_fdpass.h compat.h ssh2.h authfd.h match.h sk-api.h +monitor.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ./openbsd-compat/sys-tree.h openbsd-compat/sys-queue.h openbsd-compat/openssl-compat.h atomicio.h xmalloc.h ssh.h sshkey.h sshbuf.h hostfile.h auth.h auth-pam.h audit.h loginrec.h cipher.h cipher-chachapoly.h +monitor_fdpass.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h monitor_fdpass.h +monitor_wrap.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h sshbuf.h sshkey.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h kex.h mac.h crypto_api.h hostfile.h auth.h auth-pam.h audit.h +monitor_wrap.o: loginrec.h auth-options.h packet.h dispatch.h log.h ssherr.h monitor.h monitor_wrap.h atomicio.h monitor_fdpass.h misc.h channels.h session.h servconf.h +msg.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshbuf.h ssherr.h log.h atomicio.h msg.h misc.h +mux.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h log.h ssherr.h ssh.h ssh2.h pathnames.h misc.h match.h sshbuf.h channels.h msg.h packet.h dispatch.h monitor_fdpass.h sshpty.h sshkey.h readconf.h clientloop.h +nchan.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h ssh2.h sshbuf.h ssherr.h packet.h dispatch.h channels.h compat.h log.h +packet.o: channels.h ssh.h packet.h dispatch.h sshbuf.h +packet.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h compat.h ssh2.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h sshkey.h kex.h mac.h crypto_api.h digest.h log.h ssherr.h canohost.h misc.h +platform-misc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +platform-pledge.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +platform-tracing.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h +platform.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h misc.h servconf.h openbsd-compat/sys-queue.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h +poly1305.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h poly1305.h +progressmeter.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h progressmeter.h atomicio.h misc.h utf8.h +readconf.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/glob.h xmalloc.h ssh.h ssherr.h compat.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h pathnames.h log.h sshkey.h misc.h readconf.h match.h kex.h mac.h crypto_api.h +readconf.o: uidswap.h myproposal.h digest.h +readpass.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h misc.h pathnames.h log.h ssherr.h ssh.h uidswap.h +rijndael.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h rijndael.h +sandbox-capsicum.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-darwin.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-null.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-pledge.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-rlimit.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-seccomp-filter.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-solaris.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sandbox-systrace.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sc25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sc25519.h crypto_api.h +scp.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/glob.h xmalloc.h ssh.h atomicio.h pathnames.h log.h ssherr.h misc.h progressmeter.h utf8.h sftp.h sftp-common.h sftp-client.h +servconf.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/glob.h openbsd-compat/sys-queue.h xmalloc.h ssh.h log.h ssherr.h sshbuf.h misc.h servconf.h compat.h pathnames.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h sshkey.h +servconf.o: kex.h mac.h crypto_api.h match.h channels.h groupaccess.h canohost.h packet.h dispatch.h hostfile.h auth.h auth-pam.h audit.h loginrec.h myproposal.h digest.h +serverloop.o: cipher-aesctr.h rijndael.h kex.h mac.h crypto_api.h hostfile.h auth.h auth-pam.h audit.h loginrec.h session.h auth-options.h serverloop.h +serverloop.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h packet.h dispatch.h sshbuf.h log.h ssherr.h misc.h servconf.h canohost.h sshpty.h channels.h compat.h ssh2.h sshkey.h cipher.h cipher-chachapoly.h chacha.h poly1305.h +session.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h sshpty.h packet.h dispatch.h sshbuf.h ssherr.h match.h uidswap.h compat.h channels.h sshkey.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h +session.o: rijndael.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h authfd.h pathnames.h log.h misc.h servconf.h sshlogin.h serverloop.h canohost.h session.h kex.h mac.h crypto_api.h monitor_wrap.h sftp.h atomicio.h +sftp-client.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssherr.h sshbuf.h log.h atomicio.h progressmeter.h misc.h utf8.h sftp.h sftp-common.h sftp-client.h openbsd-compat/glob.h +sftp-common.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssherr.h sshbuf.h log.h misc.h sftp.h sftp-common.h +sftp-glob.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h sftp.h sftp-common.h sftp-client.h openbsd-compat/glob.h +sftp-realpath.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sftp-server-main.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h sftp.h misc.h xmalloc.h +sftp-server.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h atomicio.h xmalloc.h sshbuf.h ssherr.h log.h misc.h match.h uidswap.h sftp.h sftp-common.h +sftp.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h log.h ssherr.h pathnames.h misc.h utf8.h sftp.h sshbuf.h sftp-common.h sftp-client.h openbsd-compat/glob.h +sk-usbhid.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sntrup761.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +srclimit.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h addr.h canohost.h log.h ssherr.h misc.h srclimit.h xmalloc.h +ssh-add.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h ssh.h log.h ssherr.h sshkey.h sshbuf.h authfd.h authfile.h pathnames.h misc.h digest.h ssh-sk.h sk-api.h hostfile.h +ssh-agent.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h sshbuf.h sshkey.h authfd.h compat.h log.h ssherr.h misc.h digest.h match.h msg.h pathnames.h ssh-pkcs11.h sk-api.h myproposal.h +ssh-dss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +ssh-ecdsa-sk.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/openssl-compat.h sshbuf.h ssherr.h digest.h sshkey.h +ssh-ecdsa.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +ssh-ed25519-sk.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h log.h ssherr.h sshbuf.h sshkey.h ssh.h digest.h +ssh-ed25519.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h log.h ssherr.h sshbuf.h sshkey.h ssh.h +ssh-keygen.o: cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h +ssh-keygen.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h sshkey.h authfile.h sshbuf.h pathnames.h log.h ssherr.h misc.h match.h hostfile.h dns.h ssh.h ssh2.h ssh-pkcs11.h atomicio.h krl.h digest.h utf8.h authfd.h sshsig.h ssh-sk.h sk-api.h cipher.h +ssh-keyscan.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h sshbuf.h sshkey.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h kex.h mac.h crypto_api.h compat.h myproposal.h packet.h dispatch.h log.h +ssh-keyscan.o: ssherr.h atomicio.h misc.h hostfile.h ssh_api.h ssh2.h dns.h +ssh-keysign.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h log.h ssherr.h sshkey.h ssh.h ssh2.h misc.h sshbuf.h authfile.h msg.h canohost.h pathnames.h readconf.h uidswap.h +ssh-pkcs11-client.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +ssh-pkcs11-helper.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h sshbuf.h log.h ssherr.h misc.h sshkey.h authfd.h ssh-pkcs11.h +ssh-pkcs11.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h sshkey.h +ssh-rsa.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +ssh-sk-client.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h sshbuf.h sshkey.h msg.h digest.h pathnames.h ssh-sk.h misc.h +ssh-sk-helper.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h log.h ssherr.h sshkey.h authfd.h misc.h sshbuf.h msg.h uidswap.h ssh-sk.h +ssh-sk.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +ssh-xmss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +ssh.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/openssl-compat.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h canohost.h compat.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h packet.h dispatch.h sshbuf.h channels.h +ssh.o: sshkey.h authfd.h authfile.h pathnames.h clientloop.h log.h ssherr.h misc.h readconf.h sshconnect.h kex.h mac.h crypto_api.h sshpty.h match.h msg.h version.h myproposal.h utf8.h +ssh_api.o: authfile.h misc.h version.h myproposal.h sshbuf.h openbsd-compat/openssl-compat.h +ssh_api.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssh_api.h openbsd-compat/sys-queue.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h sshkey.h kex.h mac.h crypto_api.h ssh.h ssh2.h packet.h dispatch.h compat.h log.h ssherr.h +sshbuf-getput-basic.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h sshbuf.h +sshbuf-getput-crypto.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sshbuf-io.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h sshbuf.h atomicio.h +sshbuf-misc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h sshbuf.h +sshbuf.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ssherr.h sshbuf.h misc.h +sshconnect.o: authfd.h kex.h mac.h crypto_api.h +sshconnect.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h hostfile.h ssh.h sshbuf.h packet.h openbsd-compat/sys-queue.h dispatch.h compat.h sshkey.h sshconnect.h log.h ssherr.h misc.h readconf.h atomicio.h dns.h monitor_fdpass.h ssh2.h version.h authfile.h +sshconnect2.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h sshbuf.h packet.h dispatch.h compat.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h sshkey.h kex.h mac.h crypto_api.h +sshconnect2.o: myproposal.h sshconnect.h authfile.h dh.h authfd.h log.h ssherr.h misc.h readconf.h match.h canohost.h msg.h pathnames.h uidswap.h hostfile.h utf8.h ssh-sk.h sk-api.h +sshd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h ./openbsd-compat/sys-tree.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h sshpty.h packet.h dispatch.h log.h ssherr.h sshbuf.h misc.h match.h servconf.h uidswap.h compat.h cipher.h cipher-chachapoly.h chacha.h +sshd.o: poly1305.h cipher-aesctr.h rijndael.h digest.h sshkey.h kex.h mac.h crypto_api.h myproposal.h authfile.h pathnames.h atomicio.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h authfd.h msg.h channels.h session.h monitor.h monitor_wrap.h ssh-sandbox.h auth-options.h version.h sk-api.h srclimit.h dh.h +ssherr.o: ssherr.h +sshkey-xmss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +sshkey.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h ssh2.h ssherr.h misc.h sshbuf.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h sshkey.h match.h ssh-sk.h openbsd-compat/openssl-compat.h +sshlogin.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshlogin.h ssherr.h loginrec.h log.h sshbuf.h misc.h servconf.h openbsd-compat/sys-queue.h +sshpty.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshpty.h log.h ssherr.h misc.h +sshsig.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h authfd.h authfile.h log.h ssherr.h misc.h sshbuf.h sshsig.h sshkey.h match.h digest.h +sshtty.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h sshpty.h +ttymodes.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h packet.h openbsd-compat/sys-queue.h dispatch.h log.h ssherr.h compat.h sshbuf.h ttymodes.h +uidswap.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h log.h ssherr.h uidswap.h xmalloc.h +umac.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h umac.h misc.h rijndael.h +umac128.o: umac.c includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h umac.h misc.h rijndael.h +utf8.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h utf8.h +verify.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h crypto_api.h +xmalloc.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h xmalloc.h log.h ssherr.h +xmss_commons.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +xmss_fast.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +xmss_hash.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +xmss_hash_address.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h +xmss_wots.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/fnmatch.h openbsd-compat/getopt.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h diff --git a/aosp/external/openssh/.skipped-commit-ids b/aosp/external/openssh/.skipped-commit-ids new file mode 100644 index 0000000000000000000000000000000000000000..c606eaee6c51ca85553876fd01be1efcdd8d89da --- /dev/null +++ b/aosp/external/openssh/.skipped-commit-ids @@ -0,0 +1,52 @@ +5317f294d63a876bfc861e19773b1575f96f027d remove libssh from makefiles +a337e886a49f96701ccbc4832bed086a68abfa85 Makefile changes +f2c9feb26963615c4fece921906cf72e248b61ee more Makefile +fa728823ba21c4b45212750e1d3a4b2086fd1a62 more Makefile refactoring +1de0e85522051eb2ffa00437e1885e9d7b3e0c2e moduli update +814b2f670df75759e1581ecef530980b2b3d7e0f remove redundant make defs +04431e8e7872f49a2129bf080a6b73c19d576d40 moduli update +c07772f58028fda683ee6abd41c73da3ff70d403 moduli update +db6375fc302e3bdf07d96430c63c991b2c2bd3ff moduli update +5ea3d63ab972691f43e9087ab5fd8376d48e898f uuencode.c Makefile accident +99dd10e72c04e93849981d43d64c946619efa474 include sshbuf-misc.c +9e1c23476bb845f3cf3d15d9032da3ed0cb2fcf5 sshbuf-misc.c in regress +569f08445c27124ec7c7f6c0268d844ec56ac061 Makefile tweaks for !openssl +58ec755be4e51978ecfee73539090eb68652a987 moduli update +4bd5551b306df55379afe17d841207990eb773bf Makefile.inc +14806a59353152f843eb349e618abbf6f4dd3ada Makefile.inc +8ea4455a2d9364a0a04f9e4a2cbfa4c9fcefe77e Makefile.inc +d9b910e412d139141b072a905e66714870c38ac0 Makefile.inc +7b7b619c1452a459310b0cf4391c5757c6bdbc0f moduli update +5010ff08f7ad92082e87dde098b20f5c24921a8f moduli regen script update +3bcae7a754db3fc5ad3cab63dd46774edb35b8ae moduli regen script update +52ff0e3205036147b2499889353ac082e505ea54 moduli update +07b5031e9f49f2b69ac5e85b8da4fc9e393992a0 Makefile.inc +cc12a9029833d222043aecd252d654965c351a69 moduli-gen Makefile +7ac6c252d2a5be8fbad4c66d9d35db507c9dac5b moduli update +6b52cd2b637f3d29ef543f0ce532a2bce6d86af5 makefile change + +Old upstream tree: + +321065a95a7ccebdd5fd08482a1e19afbf524e35 Update DH groups +d4f699a421504df35254cf1c6f1a7c304fb907ca Remove 1k bit groups +aafe246655b53b52bc32c8a24002bc262f4230f7 Remove intermediate moduli +8fa9cd1dee3c3339ae329cf20fb591db6d605120 put back SSH1 for 6.9 +f31327a48dd4103333cc53315ec53fe65ed8a17a Generate new moduli +edbfde98c40007b7752a4ac106095e060c25c1ef Regen moduli +052fd565e3ff2d8cec3bc957d1788f50c827f8e2 Switch to tame-based sandbox +7cf73737f357492776223da1c09179fa6ba74660 Remove moduli <2k +180d84674be1344e45a63990d60349988187c1ae Update moduli +f6ae971186ba68d066cd102e57d5b0b2c211a5ee systrace is dead. +96c5054e3e1f170c6276902d5bc65bb3b87a2603 remove DEBUGLIBS from Makefile +6da9a37f74aef9f9cc639004345ad893cad582d8 Update moduli file +77bcb50e47b68c7209c7f0a5a020d73761e5143b unset REGRESS_FAIL_EARLY +38c2133817cbcae75c88c63599ac54228f0fa384 Change COMPILER_VERSION tests +30c20180c87cbc99fa1020489fe7fd8245b6420c resync integrity.sh shell +1e6b51ddf767cbad0a4e63eb08026c127e654308 integrity.sh reliability +fe5b31f69a60d47171836911f144acff77810217 Makefile.inc bits +5781670c0578fe89663c9085ed3ba477cf7e7913 Delete sshconnect1.c +ea80f445e819719ccdcb237022cacfac990fdc5c Makefile.inc warning flags +b92c93266d8234d493857bb822260dacf4366157 moduli-gen.sh tweak +b25bf747544265b39af74fe0716dc8d9f5b63b95 Updated moduli +1bd41cba06a7752de4df304305a8153ebfb6b0ac rsa.[ch] already removed +e39b3902fe1d6c4a7ba6a3c58e072219f3c1e604 Makefile changes diff --git a/aosp/external/openssh/Android.bp b/aosp/external/openssh/Android.bp new file mode 100755 index 0000000000000000000000000000000000000000..5114a7457ea35f8e905bc376c4f3b853491e0b0b --- /dev/null +++ b/aosp/external/openssh/Android.bp @@ -0,0 +1,265 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_defaults { + name: "ssh_defaults", + cflags: [ + "-Wno-incompatible-pointer-types", + "-Wno-pointer-sign", + "-Wno-unused-parameter", + ], + include_dirs: [ + "external/openssh/prebuilt-intermediates", + ], + shared_libs: [ + "libdl", + "libcrypto", + "libssl", + "libz", + ], +} + +cc_library { + name: "libssh", + defaults: ["ssh_defaults"], + include_dirs: [ + "external/openssh/openbsd-compat", + ], + cflags: [], + srcs: [ + "addr.c", + "addrmatch.c", + "atomicio.c", + "authfd.c", + "authfile.c", + "bitmap.c", + "canohost.c", + "chacha.c", + "channels.c", + "cipher-aes.c", + "cipher-aesctr.c", + "cipher-chachapoly.c", + "cipher-ctr.c", + "cipher.c", + "cleanup.c", + "compat.c", + "dh.c", + "digest-openssl.c", + "dispatch.c", + "dns.c", + "ed25519.c", + "entropy.c", + "fatal.c", + "fe25519.c", + "ge25519.c", + "gss-genr.c", + "hash.c", + "hmac.c", + "hostfile.c", + "kex.c", + "kexc25519.c", + "kexdh.c", + "kexecdh.c", + "kexgen.c", + "kexgex.c", + "kexgexc.c", + "kexsntrup761x25519.c", + "krl.c", + "log.c", + "mac.c", + "match.c", + "misc.c", + "moduli.c", + "monitor_fdpass.c", + "msg.c", + "nchan.c", + "openbsd-compat/bcrypt_pbkdf.c", + "openbsd-compat/bindresvport.c", + "openbsd-compat/blowfish.c", + "openbsd-compat/bsd-closefrom.c", + "openbsd-compat/bsd-err.c", + "openbsd-compat/bsd-getpagesize.c", + "openbsd-compat/bsd-getpeereid.c", + "openbsd-compat/bsd-malloc.c", + "openbsd-compat/bsd-misc.c", + "openbsd-compat/bsd-openpty.c", + "openbsd-compat/bsd-pselect.c", + "openbsd-compat/bsd-signal.c", + "openbsd-compat/bsd-statvfs.c", + "openbsd-compat/explicit_bzero.c", + "openbsd-compat/fmt_scaled.c", + "openbsd-compat/freezero.c", + "openbsd-compat/getopt_long.c", + "openbsd-compat/glob.c", + "openbsd-compat/libressl-api-compat.c", + "openbsd-compat/openssl-compat.c", + "openbsd-compat/port-linux.c", + "openbsd-compat/port-net.c", + "openbsd-compat/pwcache.c", + "openbsd-compat/readpassphrase.c", + "openbsd-compat/reallocarray.c", + "openbsd-compat/recallocarray.c", + "openbsd-compat/rresvport.c", + "openbsd-compat/setproctitle.c", + "openbsd-compat/strcasestr.c", + "openbsd-compat/strmode.c", + "openbsd-compat/strtonum.c", + "openbsd-compat/timingsafe_bcmp.c", + "openbsd-compat/vis.c", + "packet.c", + "platform-misc.c", + "platform-pledge.c", + "poly1305.c", + "readpass.c", + "rijndael.c", + "sc25519.c", + "sntrup761.c", + "smult_curve25519_ref.c", + "ssh-dss.c", + "ssh-ecdsa.c", + "ssh-ecdsa-sk.c", + "ssh-ed25519.c", + "ssh-ed25519-sk.c", + "ssh-rsa.c", + "ssh-sk.c", + "sshbuf-io.c", + "sshbuf-getput-basic.c", + "sshbuf-getput-crypto.c", + "sshbuf-misc.c", + "sshbuf.c", + "ssherr.c", + "sshkey.c", + "ttymodes.c", + "uidswap.c", + "umac.c", + "umac128.c", + "utf8.c", + "verify.c", + "xmalloc.c", + ], +} + +cc_binary { + name: "ssh", + defaults: ["ssh_defaults"], + srcs: [ + "clientloop.c", + "mux.c", + "readconf.c", + "ssh.c", + "sshconnect.c", + "sshconnect2.c", + "sshtty.c", + ], + shared_libs: ["libssh"], +} + +cc_binary { + name: "sftp", + defaults: ["ssh_defaults"], + srcs: [ + "sftp.c", + "sftp-client.c", + "sftp-common.c", + "sftp-glob.c", + "progressmeter.c", + ], + shared_libs: ["libssh"], +} + +cc_binary { + name: "scp", + defaults: ["ssh_defaults"], + srcs: [ + "scp.c", + "sftp-client.c", + "sftp-common.c", + "sftp-glob.c", + "progressmeter.c", + ], + shared_libs: ["libssh"], +} + +cc_binary { + name: "sshd", + defaults: ["ssh_defaults"], + srcs: [ + "audit-bsm.c", + "audit-linux.c", + "audit.c", + "auth-bsdauth.c", + "auth-krb5.c", + "auth-options.c", + "auth-pam.c", + "auth-rhosts.c", + "auth-shadow.c", + "auth-sia.c", + "auth.c", + "auth2-chall.c", + "auth2-gss.c", + "auth2-hostbased.c", + "auth2-kbdint.c", + "auth2-none.c", + "auth2-passwd.c", + "auth2-pubkey.c", + "auth2.c", + "groupaccess.c", + "gss-serv-krb5.c", + "gss-serv.c", + "kexgexs.c", + "loginrec.c", + "monitor.c", + "monitor_wrap.c", + "platform.c", + "platform-tracing.c", + "sandbox-null.c", + "sandbox-rlimit.c", + "sandbox-systrace.c", + "servconf.c", + "serverloop.c", + "session.c", + "sftp-common.c", + "sftp-realpath.c", + "sftp-server.c", + "srclimit.c", + "sshd.c", + "sshlogin.c", + "sshpty.c", + ], + shared_libs: [ + "libcutils", + "libssh", + ], +} + +cc_binary { + name: "ssh-keygen", + defaults: ["ssh_defaults"], + srcs: [ + "ssh-keygen.c", + "sshsig.c", + ], + shared_libs: ["libssh"], +} + +prebuilt_etc { + name: "sshd_config", + src: "sshd_config.android", + sub_dir: "ssh", +} + +sh_binary { + name: "start-ssh", + src: "start-ssh", +} diff --git a/aosp/external/openssh/CREDITS b/aosp/external/openssh/CREDITS new file mode 100644 index 0000000000000000000000000000000000000000..6cc3512515ebfc88b4ca0b3756b17b56524002ef --- /dev/null +++ b/aosp/external/openssh/CREDITS @@ -0,0 +1,102 @@ +Tatu Ylonen - Creator of SSH + +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt, and Dug Song - Creators of OpenSSH + +Ahsan Rashid - UnixWare long passwords +Alain St-Denis - Irix fix +Alexandre Oliva - AIX fixes +Andre Lucas - new login code, many fixes +Andreas Steinmetz - Shadow password expiry support +Andrew McGill - SCO fixes +Andrew Morgan - PAM bugfixes +Andrew Stribblehill - Bugfixes +Andy Sloane - bugfixes +Aran Cox - SCO bugfixes +Arkadiusz Miskiewicz - IPv6 compat fixes +Ben Lindstrom - NeXT support +Ben Taylor - Solaris debugging and fixes +Bratislav ILICH - Configure fix +Charles Levert - SunOS 4 & bug fixes +Chip Salzenberg - Assorted patches +Chris Adams - OSF SIA support +Chris Saia - SuSE packaging +Chris, the Young One - Password auth fixes +Christos Zoulas - Autoconf fixes +Chun-Chung Chen - RPM fixes +Corinna Vinschen - Cygwin support +Chad Mynhier - Solaris Process Contract support +Dan Brosemer - Autoconf support, build fixes +Darren Hall - AIX patches +Darren Tucker - AIX BFF package scripts +David Agraz - Build fixes +David Del Piero - bug fixes +David Hesprich - Configure fixes +David Rankin - libwrap, AIX, NetBSD fixes +Dag-Erling Smørgrav - Challenge-Response PAM code. +Dhiraj Gulati - UnixWare long passwords +Ed Eden - configure fixes +Garrick James - configure fixes +Gary E. Miller - SCO support +Ged Lodder - HPUX fixes and enhancements +Gert Doering - bug and portability fixes +HARUYAMA Seigo - Translations & doc fixes +Hideaki YOSHIFUJI - IPv6 and bug fixes +Hiroshi Takekawa - Configure fixes +Holger Trapp - KRB4/AFS config patch +IWAMURO Motonori - bugfixes +Jani Hakala - Patches +Jarno Huuskonen - Bugfixes +Jim Knoble - Many patches +Jonchen (email unknown) - the original author of PAM support of SSH +Juergen Keil - scp bugfixing +KAMAHARA Junzo - Configure fixes +Kees Cook - scp fixes +Kenji Miyake - Configure fixes +Kevin Cawlfield - AIX fixes. +Kevin O'Connor - RSAless operation +Kevin Steves - HP support, bugfixes, improvements +Kiyokazu SUTO - Bugfixes +Larry Jones - Bugfixes +Lutz Jaenicke - Bugfixes +Marc G. Fournier - Solaris patches +Mark D. Baushke - bug fixes +Martin Johansson - Linux fixes +Mark D. Roth - Features, bug fixes +Mark Miller - Bugfixes +Matt Richards - AIX patches +Michael Steffens - HP-UX fixes +Michael Stone - Irix enhancements +Nakaji Hiroyuki - Sony News-OS patch +Nalin Dahyabhai - PAM environment patch +Nate Itkin - SunOS 4.1.x fixes +Niels Kristian Bech Jensen - Assorted patches +Pavel Kankovsky - Security fixes +Pavel Troller - Bugfixes +Pekka Savola - Bugfixes +Peter Kocks - Makefile fixes +Peter Stuge - mdoc2man.awk script +Phil Hands - Debian scripts, assorted patches +Phil Karn - Autoconf fixes +Philippe WILLEM - Bugfixes +Phill Camp - login code fix +Rip Loomis - Solaris package support, fixes +Robert Dahlem - Reliant Unix fixes +Roumen Petrov - Compile & configure fixes +SAKAI Kiyotaka - Multiple bugfixes +Simon Wilkinson - PAM fixes, Compat with MIT KrbV +Solar Designer - many patches and technical assistance +Svante Signell - Bugfixes +Thomas Neumann - Shadow passwords +Tim Rice - Portability & SCO fixes +Tobias Oetiker - Bugfixes +Tom Bertelson's - AIX auth fixes +Tor-Ake Fransson - AIX support +Tudor Bosman - MD5 password support +Udo Schweigert - ReliantUNIX support +Wendy Palm - Cray support. +Zack Weinberg - GNOME askpass enhancement + +Apologies to anyone I have missed. + +Damien Miller diff --git a/aosp/external/openssh/INSTALL b/aosp/external/openssh/INSTALL new file mode 100644 index 0000000000000000000000000000000000000000..b6e53ab6c0888ae4de29b3766d150f90a2a3648f --- /dev/null +++ b/aosp/external/openssh/INSTALL @@ -0,0 +1,284 @@ +1. Prerequisites +---------------- + +A C compiler. Any C89 or better compiler should work. Where supported, +configure will attempt to enable the compiler's run-time integrity checking +options. Some notes about specific compilers: + - clang: -ftrapv and -sanitize=integer require the compiler-rt runtime + (CC=clang LDFLAGS=--rtlib=compiler-rt ./configure) + +To support Privilege Separation (which is now required) you will need +to create the user, group and directory used by sshd for privilege +separation. See README.privsep for details. + + +The remaining items are optional. + +A working installation of zlib: +Zlib 1.1.4 or 1.2.1.2 or greater (earlier 1.2.x versions have problems): +http://www.gzip.org/zlib/ + +libcrypto from either of LibreSSL or OpenSSL. Building without libcrypto +is supported but severely restricts the available ciphers and algorithms. + - LibreSSL (https://www.libressl.org/) + - OpenSSL (https://www.openssl.org) with any of the following versions: + - 1.0.x >= 1.0.1 or 1.1.0 >= 1.1.0g or any 1.1.1 + +Note that due to a bug in EVP_CipherInit OpenSSL 1.1 versions prior to +1.1.0g can't be used. + +LibreSSL/OpenSSL should be compiled as a position-independent library +(i.e. -fPIC, eg by configuring OpenSSL as "./config [options] -fPIC" +or LibreSSL as "CFLAGS=-fPIC ./configure") otherwise OpenSSH will not +be able to link with it. If you must use a non-position-independent +libcrypto, then you may need to configure OpenSSH --without-pie. + +If you build either from source, running the OpenSSL self-test ("make +tests") or the LibreSSL equivalent ("make check") and ensuring that all +tests pass is strongly recommended. + +NB. If you operating system supports /dev/random, you should configure +libcrypto (LibreSSL/OpenSSL) to use it. OpenSSH relies on libcrypto's +direct support of /dev/random, or failing that, either prngd or egd. + +PRNGD: + +If your system lacks kernel-based random collection, the use of Lutz +Jaenicke's PRNGd is recommended. It requires that libcrypto be configured +to support it. + +http://prngd.sourceforge.net/ + +EGD: + +The Entropy Gathering Daemon (EGD) supports the same interface as prngd. +It also supported only if libcrypto is configured to support it. + +http://egd.sourceforge.net/ + +PAM: + +OpenSSH can utilise Pluggable Authentication Modules (PAM) if your +system supports it. PAM is standard most Linux distributions, Solaris, +HP-UX 11, AIX >= 5.2, FreeBSD, NetBSD and Mac OS X. + +Information about the various PAM implementations are available: + +Solaris PAM: http://www.sun.com/software/solaris/pam/ +Linux PAM: http://www.kernel.org/pub/linux/libs/pam/ +OpenPAM: http://www.openpam.org/ + +If you wish to build the GNOME passphrase requester, you will need the GNOME +libraries and headers. + +GNOME: +http://www.gnome.org/ + +Alternatively, Jim Knoble has written an excellent X11 +passphrase requester. This is maintained separately at: + +http://www.jmknoble.net/software/x11-ssh-askpass/ + +LibEdit: + +sftp supports command-line editing via NetBSD's libedit. If your platform +has it available natively you can use that, alternatively you might try +these multi-platform ports: + +http://www.thrysoee.dk/editline/ +http://sourceforge.net/projects/libedit/ + +LDNS: + +LDNS is a DNS BSD-licensed resolver library which supports DNSSEC. + +http://nlnetlabs.nl/projects/ldns/ + +Autoconf: + +If you modify configure.ac or configure doesn't exist (eg if you checked +the code out of git yourself) then you will need autoconf-2.69 and +automake-1.16.1 to rebuild the automatically generated files by running +"autoreconf". Earlier versions may also work but this is not guaranteed. + +http://www.gnu.org/software/autoconf/ +http://www.gnu.org/software/automake/ + +Basic Security Module (BSM): + +Native BSM support is known to exist in Solaris from at least 2.5.1, +FreeBSD 6.1 and OS X. Alternatively, you may use the OpenBSM +implementation (http://www.openbsm.org). + +makedepend: + +https://www.x.org/archive/individual/util/ + +If you are making significant changes to the code you may need to rebuild +the dependency (.depend) file using "make depend", which requires the +"makedepend" tool from the X11 distribution. + +libfido2: + +libfido2 allows the use of hardware security keys over USB. libfido2 +in turn depends on libcbor. libfido2 >= 1.5.0 is strongly recommended. +Limited functionality is possible with earlier libfido2 versions. + +https://github.com/Yubico/libfido2 +https://github.com/pjk/libcbor + + +2. Building / Installation +-------------------------- + +To install OpenSSH with default options: + +./configure +make +make install + +This will install the OpenSSH binaries in /usr/local/bin, configuration files +in /usr/local/etc, the server in /usr/local/sbin, etc. To specify a different +installation prefix, use the --prefix option to configure: + +./configure --prefix=/opt +make +make install + +Will install OpenSSH in /opt/{bin,etc,lib,sbin}. You can also override +specific paths, for example: + +./configure --prefix=/opt --sysconfdir=/etc/ssh +make +make install + +This will install the binaries in /opt/{bin,lib,sbin}, but will place the +configuration files in /etc/ssh. + +If you are using PAM, you may need to manually install a PAM control +file as "/etc/pam.d/sshd" (or wherever your system prefers to keep +them). Note that the service name used to start PAM is __progname, +which is the basename of the path of your sshd (e.g., the service name +for /usr/sbin/osshd will be osshd). If you have renamed your sshd +executable, your PAM configuration may need to be modified. + +A generic PAM configuration is included as "contrib/sshd.pam.generic", +you may need to edit it before using it on your system. If you are +using a recent version of Red Hat Linux, the config file in +contrib/redhat/sshd.pam should be more useful. Failure to install a +valid PAM file may result in an inability to use password +authentication. On HP-UX 11 and Solaris, the standard /etc/pam.conf +configuration will work with sshd (sshd will match the other service +name). + +There are a few other options to the configure script: + +--with-audit=[module] enable additional auditing via the specified module. +Currently, drivers for "debug" (additional info via syslog) and "bsm" +(Sun's Basic Security Module) are supported. + +--with-pam enables PAM support. If PAM support is compiled in, it must +also be enabled in sshd_config (refer to the UsePAM directive). + +--with-prngd-socket=/some/file allows you to enable EGD or PRNGD +support and to specify a PRNGd socket. Use this if your Unix lacks +/dev/random. + +--with-prngd-port=portnum allows you to enable EGD or PRNGD support +and to specify a EGD localhost TCP port. Use this if your Unix lacks +/dev/random. + +--with-lastlog=FILE will specify the location of the lastlog file. +./configure searches a few locations for lastlog, but may not find +it if lastlog is installed in a different place. + +--without-lastlog will disable lastlog support entirely. + +--with-osfsia, --without-osfsia will enable or disable OSF1's Security +Integration Architecture. The default for OSF1 machines is enable. + +--with-utmpx enables utmpx support. utmpx support is automatic for +some platforms. + +--without-shadow disables shadow password support. + +--with-ipaddr-display forces the use of a numeric IP address in the +$DISPLAY environment variable. Some broken systems need this. + +--with-default-path=PATH allows you to specify a default $PATH for sessions +started by sshd. This replaces the standard path entirely. + +--with-pid-dir=PATH specifies the directory in which the sshd.pid file is +created. + +--with-xauth=PATH specifies the location of the xauth binary + +--with-ssl-dir=DIR allows you to specify where your Libre/OpenSSL +libraries are installed. + +--with-ssl-engine enables Libre/OpenSSL's (hardware) ENGINE support + +--without-openssl builds without using OpenSSL. Only a subset of ciphers +and algorithms are supported in this configuration. + +--without-zlib builds without zlib. This disables the Compression option. + +--with-4in6 Check for IPv4 in IPv6 mapped addresses and convert them to +real (AF_INET) IPv4 addresses. Works around some quirks on Linux. + +If you need to pass special options to the compiler or linker, you +can specify these as environment variables before running ./configure. +For example: + +CC="/usr/foo/cc" CFLAGS="-O" LDFLAGS="-s" LIBS="-lrubbish" ./configure + +3. Configuration +---------------- + +The runtime configuration files are installed by in ${prefix}/etc or +whatever you specified as your --sysconfdir (/usr/local/etc by default). + +The default configuration should be instantly usable, though you should +review it to ensure that it matches your security requirements. + +To generate a host key, run "make host-key". Alternately you can do so +manually using the following commands: + + ssh-keygen -t [type] -f /etc/ssh/ssh_host_key -N "" + +for each of the types you wish to generate (rsa, dsa or ecdsa) or + + ssh-keygen -A + +to generate keys for all supported types. + +Replacing /etc/ssh with the correct path to the configuration directory. +(${prefix}/etc or whatever you specified with --sysconfdir during +configuration). + +If you have configured OpenSSH with EGD/prngd support, ensure that EGD or +prngd is running and has collected some entropy first. + +For more information on configuration, please refer to the manual pages +for sshd, ssh and ssh-agent. + +4. (Optional) Send survey +------------------------- + +$ make survey +[check the contents of the file "survey" to ensure there's no information +that you consider sensitive] +$ make send-survey + +This will send configuration information for the currently configured +host to a survey address. This will help determine which configurations +are actually in use, and what valid combinations of configure options +exist. The raw data is available only to the OpenSSH developers, however +summary data may be published. + +5. Problems? +------------ + +If you experience problems compiling, installing or running OpenSSH, +please refer to the "reporting bugs" section of the webpage at +https://www.openssh.com/ diff --git a/aosp/external/openssh/LICENCE b/aosp/external/openssh/LICENCE new file mode 100644 index 0000000000000000000000000000000000000000..77ef5769971295cecc758109e8a4d812f825c0c9 --- /dev/null +++ b/aosp/external/openssh/LICENCE @@ -0,0 +1,370 @@ +This file is part of the OpenSSH software. + +The licences which components of this software fall under are as +follows. First, we will summarize and say that all components +are under a BSD licence, or a licence more free than that. + +OpenSSH contains no GPL code. + +1) + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + + [Tatu continues] + * However, I am not implying to give any licenses to any patents or + * copyrights held by third parties, and the software includes parts that + * are not under my direct control. As far as I know, all included + * source code is used in accordance with the relevant license agreements + * and can be used freely for any purpose (the GNU license being the most + * restrictive); see below for details. + + [However, none of that term is relevant at this point in time. All of + these restrictively licenced software components which he talks about + have been removed from OpenSSH, i.e., + + - RSA is no longer included, found in the OpenSSL library + - IDEA is no longer included, its use is deprecated + - DES is now external, in the OpenSSL library + - GMP is no longer used, and instead we call BN code from OpenSSL + - Zlib is now external, in a library + - The make-ssh-known-hosts script is no longer included + - TSS has been removed + - MD5 is now external, in the OpenSSL library + - RC4 support has been replaced with ARC4 support from OpenSSL + - Blowfish is now external, in the OpenSSL library + + [The licence continues] + + Note that any information and cryptographic algorithms used in this + software are publicly available on the Internet and at any major + bookstore, scientific library, and patent office worldwide. More + information can be found e.g. at "http://www.cs.hut.fi/crypto". + + The legal status of this program is some combination of all these + permissions and restrictions. Use only at your own responsibility. + You will be responsible for any legal consequences yourself; I am not + making any claims whether possessing or using this is legal or not in + your country, and I am not taking any responsibility on your behalf. + + + NO WARRANTY + + BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS + TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, + REPAIR OR CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED + TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY + YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + +3) + ssh-keyscan was contributed by David Mazieres under a BSD-style + license. + + * Copyright 1995, 1996 by David Mazieres . + * + * Modification and redistribution in source and binary forms is + * permitted provided that due credit is given to the author and the + * OpenBSD project by leaving this copyright notice intact. + +4) + The Rijndael implementation by Vincent Rijmen, Antoon Bosselaers + and Paulo Barreto is in the public domain and distributed + with the following license: + + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +5) + One component of the ssh source code is under a 3-clause BSD license, + held by the University of California, since we pulled these parts from + original Berkeley code. + + * Copyright (c) 1983, 1990, 1992, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + +6) + Remaining components of the software are provided under a standard + 2-term BSD licence with the following names as copyright holders: + + Markus Friedl + Theo de Raadt + Niels Provos + Dug Song + Aaron Campbell + Damien Miller + Kevin Steves + Daniel Kouril + Wesley Griffin + Per Allansson + Nils Nordman + Simon Wilkinson + + Portable OpenSSH additionally includes code from the following copyright + holders, also under the 2-term BSD license: + + Ben Lindstrom + Tim Rice + Andre Lucas + Chris Adams + Corinna Vinschen + Cray Inc. + Denis Parker + Gert Doering + Jakob Schlyter + Jason Downs + Juha Yrjölä + Michael Stone + Networks Associates Technology, Inc. + Solar Designer + Todd C. Miller + Wayne Schroeder + William Jones + Darren Tucker + Sun Microsystems + The SCO Group + Daniel Walsh + Red Hat, Inc + Simon Vallet / Genoscope + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +8) Portable OpenSSH contains the following additional licenses: + + a) snprintf replacement + + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell + * (papowell@astart.com) It may be used for any purpose as long as this + * notice remains intact on all source code distributions + + b) Compatibility code (openbsd-compat) + + Apart from the previously mentioned licenses, various pieces of code + in the openbsd-compat/ subdirectory are licensed as follows: + + Some code is licensed under a 3-term BSD license, to the following + copyright holders: + + Todd C. Miller + Theo de Raadt + Damien Miller + Eric P. Allman + The Regents of the University of California + Constantin S. Svintsoff + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + + Some code is licensed under an ISC-style license, to the following + copyright holders: + + Internet Software Consortium. + Todd C. Miller + Reyk Floeter + Chad Mynhier + + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + Some code is licensed under a MIT-style license to the following + copyright holders: + + Free Software Foundation, Inc. + + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + + The Blowfish cipher implementation is licensed by Niels Provos under + a 3-clause BSD license: + + * Blowfish - a fast block cipher designed by Bruce Schneier + * + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Some replacement code is licensed by the NetBSD foundation under a + 2-clause BSD license: + + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Todd Vierling. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + +------ +$OpenBSD: LICENCE,v 1.20 2017/04/30 23:26:16 djm Exp $ diff --git a/aosp/external/openssh/METADATA b/aosp/external/openssh/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..4669b8ddf161c5ee4e0a3b0d9452587137bd949b --- /dev/null +++ b/aosp/external/openssh/METADATA @@ -0,0 +1,17 @@ +name: "openssh" +description: + "Secure Shell (SSH) protocol client, server and utilities" + +third_party { + url { + type: HOMEPAGE + value: "https://www.openssh.com/" + } + url { + type: GIT + value: "https://github.com/openssh/openssh-portable" + } + version: "9.0p1" + last_upgrade_date { year: 2022 month: 4 day: 15 } + license_type: NOTICE +} diff --git a/aosp/external/openssh/Makefile.in b/aosp/external/openssh/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..7250d3f315e13c2c5b46aeaaf2cbef5a7a65ab6f --- /dev/null +++ b/aosp/external/openssh/Makefile.in @@ -0,0 +1,780 @@ +SHELL=@SH@ + +AUTORECONF=autoreconf + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +sbindir=@sbindir@ +libexecdir=@libexecdir@ +datadir=@datadir@ +datarootdir=@datarootdir@ +mandir=@mandir@ +mansubdir=@mansubdir@ +sysconfdir=@sysconfdir@ +piddir=@piddir@ +srcdir=@srcdir@ +top_srcdir=@top_srcdir@ +abs_top_srcdir=@abs_top_srcdir@ +abs_top_builddir=@abs_top_builddir@ + +DESTDIR= +VPATH=@srcdir@ +SSH_PROGRAM=@bindir@/ssh +ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass +SFTP_SERVER=$(libexecdir)/sftp-server +SSH_KEYSIGN=$(libexecdir)/ssh-keysign +SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper +SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper +PRIVSEP_PATH=@PRIVSEP_PATH@ +SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ +STRIP_OPT=@STRIP_OPT@ +TEST_SHELL=@TEST_SHELL@ +BUILDDIR=@abs_top_builddir@ + +PATHS= -DSSHDIR=\"$(sysconfdir)\" \ + -D_PATH_SSH_PROGRAM=\"$(SSH_PROGRAM)\" \ + -D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \ + -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ + -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ + -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \ + -D_PATH_SSH_SK_HELPER=\"$(SSH_SK_HELPER)\" \ + -D_PATH_SSH_PIDDIR=\"$(piddir)\" \ + -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" + +CC=@CC@ +LD=@LD@ +CFLAGS=@CFLAGS@ +CFLAGS_NOPIE=@CFLAGS_NOPIE@ +CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ +PICFLAG=@PICFLAG@ +LIBS=@LIBS@ +K5LIBS=@K5LIBS@ +GSSLIBS=@GSSLIBS@ +SSHDLIBS=@SSHDLIBS@ +LIBEDIT=@LIBEDIT@ +LIBFIDO2=@LIBFIDO2@ +AR=@AR@ +AWK=@AWK@ +RANLIB=@RANLIB@ +INSTALL=@INSTALL@ +SED=@SED@ +XAUTH_PATH=@XAUTH_PATH@ +LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ +LDFLAGS_NOPIE=-L. -Lopenbsd-compat/ @LDFLAGS_NOPIE@ +EXEEXT=@EXEEXT@ +MANFMT=@MANFMT@ +MKDIR_P=@MKDIR_P@ + +.SUFFIXES: .lo + +TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) + +XMSS_OBJS=\ + ssh-xmss.o \ + sshkey-xmss.o \ + xmss_commons.o \ + xmss_fast.o \ + xmss_hash.o \ + xmss_hash_address.o \ + xmss_wots.o + +LIBOPENSSH_OBJS=\ + ssh_api.o \ + ssherr.o \ + sshbuf.o \ + sshkey.o \ + sshbuf-getput-basic.o \ + sshbuf-misc.o \ + sshbuf-getput-crypto.o \ + krl.o \ + bitmap.o \ + ${XMSS_OBJS} + +LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + authfd.o authfile.o \ + canohost.o channels.o cipher.o cipher-aes.o cipher-aesctr.o \ + cipher-ctr.o cleanup.o \ + compat.o fatal.o hostfile.o \ + log.o match.o moduli.o nchan.o packet.o \ + readpass.o ttymodes.o xmalloc.o addr.o addrmatch.o \ + atomicio.o dispatch.o mac.o misc.o utf8.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-ecdsa-sk.o \ + ssh-ed25519-sk.o ssh-rsa.o dh.o \ + msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ + ssh-pkcs11.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o cipher-chachapoly-libcrypto.o \ + ssh-ed25519.o digest-openssl.o digest-libc.o \ + hmac.o sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexgexc.o kexgexs.o \ + kexsntrup761x25519.o sntrup761.o kexgen.o \ + sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \ + sshbuf-io.o + +SKOBJS= ssh-sk-client.o + +SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o $(SKOBJS) + +SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \ + audit.o audit-bsm.o audit-linux.o platform.o \ + sshpty.o sshlogin.o servconf.o serverloop.o \ + auth.o auth2.o auth-options.o session.o \ + auth2-chall.o groupaccess.o \ + auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ + auth2-none.o auth2-passwd.o auth2-pubkey.o \ + monitor.o monitor_wrap.o auth-krb5.o \ + auth2-gss.o gss-serv.o gss-serv-krb5.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o \ + srclimit.o sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ + sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ + sandbox-solaris.o uidswap.o $(SKOBJS) + +SFTP_CLIENT_OBJS=sftp-common.o sftp-client.o sftp-glob.o + +SCP_OBJS= scp.o progressmeter.o $(SFTP_CLIENT_OBJS) + +SSHADD_OBJS= ssh-add.o $(SKOBJS) + +SSHAGENT_OBJS= ssh-agent.o ssh-pkcs11-client.o $(SKOBJS) + +SSHKEYGEN_OBJS= ssh-keygen.o sshsig.o $(SKOBJS) + +SSHKEYSIGN_OBJS=ssh-keysign.o readconf.o uidswap.o $(SKOBJS) + +P11HELPER_OBJS= ssh-pkcs11-helper.o ssh-pkcs11.o $(SKOBJS) + +SKHELPER_OBJS= ssh-sk-helper.o ssh-sk.o sk-usbhid.o + +SSHKEYSCAN_OBJS=ssh-keyscan.o $(SKOBJS) + +SFTPSERVER_OBJS=sftp-common.o sftp-server.o sftp-server-main.o + +SFTP_OBJS= sftp.o progressmeter.o $(SFTP_CLIENT_OBJS) + +MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out ssh-sk-helper.8.out sshd_config.5.out ssh_config.5.out +MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 ssh-sk-helper.8 sshd_config.5 ssh_config.5 +MANTYPE = @MANTYPE@ + +CONFIGFILES=sshd_config.out ssh_config.out moduli.out +CONFIGFILES_IN=sshd_config ssh_config moduli + +PATHSUBS = \ + -e 's|/etc/ssh/ssh_config|$(sysconfdir)/ssh_config|g' \ + -e 's|/etc/ssh/ssh_known_hosts|$(sysconfdir)/ssh_known_hosts|g' \ + -e 's|/etc/ssh/sshd_config|$(sysconfdir)/sshd_config|g' \ + -e 's|/usr/libexec|$(libexecdir)|g' \ + -e 's|/etc/shosts.equiv|$(sysconfdir)/shosts.equiv|g' \ + -e 's|/etc/ssh/ssh_host_key|$(sysconfdir)/ssh_host_key|g' \ + -e 's|/etc/ssh/ssh_host_ecdsa_key|$(sysconfdir)/ssh_host_ecdsa_key|g' \ + -e 's|/etc/ssh/ssh_host_dsa_key|$(sysconfdir)/ssh_host_dsa_key|g' \ + -e 's|/etc/ssh/ssh_host_rsa_key|$(sysconfdir)/ssh_host_rsa_key|g' \ + -e 's|/etc/ssh/ssh_host_ed25519_key|$(sysconfdir)/ssh_host_ed25519_key|g' \ + -e 's|/var/run/sshd.pid|$(piddir)/sshd.pid|g' \ + -e 's|/etc/moduli|$(sysconfdir)/moduli|g' \ + -e 's|/etc/ssh/moduli|$(sysconfdir)/moduli|g' \ + -e 's|/etc/ssh/sshrc|$(sysconfdir)/sshrc|g' \ + -e 's|/usr/X11R6/bin/xauth|$(XAUTH_PATH)|g' \ + -e 's|/var/empty|$(PRIVSEP_PATH)|g' \ + -e 's|/usr/bin:/bin:/usr/sbin:/sbin|@user_path@|g' + +FIXPATHSCMD = $(SED) $(PATHSUBS) +FIXALGORITHMSCMD= $(SHELL) $(srcdir)/fixalgorithms $(SED) \ + @UNSUPPORTED_ALGORITHMS@ + +all: configure-check $(CONFIGFILES) $(MANPAGES) $(TARGETS) + +$(LIBSSH_OBJS): Makefile.in config.h +$(SSHOBJS): Makefile.in config.h +$(SSHDOBJS): Makefile.in config.h +configure-check: $(srcdir)/configure + +$(srcdir)/configure: configure.ac $(srcdir)/m4/*.m4 + @echo "ERROR: configure is out of date; please run ${AUTORECONF} (and configure)" 1>&2 + @exit 1 + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +LIBCOMPAT=openbsd-compat/libopenbsd-compat.a +$(LIBCOMPAT): always + (cd openbsd-compat && $(MAKE)) +always: + +libssh.a: $(LIBSSH_OBJS) + $(AR) rv $@ $(LIBSSH_OBJS) + $(RANLIB) $@ + +ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) + $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(GSSLIBS) + +sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) + $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) + +scp$(EXEEXT): $(LIBCOMPAT) libssh.a $(SCP_OBJS) + $(LD) -o $@ $(SCP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + +ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHADD_OBJS) + $(LD) -o $@ $(SSHADD_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + +ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHAGENT_OBJS) + $(LD) -o $@ $(SSHAGENT_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + +ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYGEN_OBJS) + $(LD) -o $@ $(SSHKEYGEN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + +ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYSIGN_OBJS) + $(LD) -o $@ $(SSHKEYSIGN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + +ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a $(P11HELPER_OBJS) + $(LD) -o $@ $(P11HELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +ssh-sk-helper$(EXEEXT): $(LIBCOMPAT) libssh.a $(SKHELPER_OBJS) + $(LD) -o $@ $(SKHELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(LIBFIDO2) + +ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYSCAN_OBJS) + $(LD) -o $@ $(SSHKEYSCAN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a $(SFTPSERVER_OBJS) + $(LD) -o $@ $(SFTPSERVER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +sftp$(EXEEXT): $(LIBCOMPAT) libssh.a $(SFTP_OBJS) + $(LD) -o $@ $(SFTP_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) + +# test driver for the loginrec code - not built by default +logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o + $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) + +$(MANPAGES): $(MANPAGES_IN) + if test "$(MANTYPE)" = "cat"; then \ + manpage=$(srcdir)/`echo $@ | sed 's/\.[1-9]\.out$$/\.0/'`; \ + else \ + manpage=$(srcdir)/`echo $@ | sed 's/\.out$$//'`; \ + fi; \ + if test "$(MANTYPE)" = "man"; then \ + $(FIXPATHSCMD) $${manpage} | $(FIXALGORITHMSCMD) | \ + $(AWK) -f $(srcdir)/mdoc2man.awk > $@; \ + else \ + $(FIXPATHSCMD) $${manpage} | $(FIXALGORITHMSCMD) > $@; \ + fi + +$(CONFIGFILES): $(CONFIGFILES_IN) + conffile=`echo $@ | sed 's/.out$$//'`; \ + $(FIXPATHSCMD) $(srcdir)/$${conffile} > $@ + +# fake rule to stop make trying to compile moduli.o into a binary "moduli.o" +moduli: + echo + +clean: regressclean + rm -f *.o *.lo *.a $(TARGETS) logintest config.cache config.log + rm -f *.out core survey + rm -f regress/check-perm$(EXEEXT) + rm -f regress/mkdtemp$(EXEEXT) + rm -f regress/unittests/test_helper/*.a + rm -f regress/unittests/test_helper/*.o + rm -f regress/unittests/authopt/*.o + rm -f regress/unittests/authopt/test_authopt$(EXEEXT) + rm -f regress/unittests/bitmap/*.o + rm -f regress/unittests/bitmap/test_bitmap$(EXEEXT) + rm -f regress/unittests/conversion/*.o + rm -f regress/unittests/conversion/test_conversion$(EXEEXT) + rm -f regress/unittests/hostkeys/*.o + rm -f regress/unittests/hostkeys/test_hostkeys$(EXEEXT) + rm -f regress/unittests/kex/*.o + rm -f regress/unittests/kex/test_kex$(EXEEXT) + rm -f regress/unittests/match/*.o + rm -f regress/unittests/match/test_match$(EXEEXT) + rm -f regress/unittests/misc/*.o + rm -f regress/unittests/misc/test_misc$(EXEEXT) + rm -f regress/unittests/sshbuf/*.o + rm -f regress/unittests/sshbuf/test_sshbuf$(EXEEXT) + rm -f regress/unittests/sshkey/*.o + rm -f regress/unittests/sshkey/test_sshkey$(EXEEXT) + rm -f regress/unittests/sshsig/*.o + rm -f regress/unittests/sshsig/test_sshsig$(EXEEXT) + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8$(EXEEXT) + rm -f regress/misc/sk-dummy/*.o + rm -f regress/misc/sk-dummy/*.lo + rm -f regress/misc/sk-dummy/sk-dummy.so + (cd openbsd-compat && $(MAKE) clean) + +distclean: regressclean + rm -f *.o *.a $(TARGETS) logintest config.cache config.log + rm -f *.out core opensshd.init openssh.xml + rm -f Makefile buildpkg.sh config.h config.status + rm -f survey.sh openbsd-compat/regress/Makefile *~ + rm -rf autom4te.cache + rm -f regress/check-perm + rm -f regress/mkdtemp + rm -f regress/unittests/test_helper/*.a + rm -f regress/unittests/test_helper/*.o + rm -f regress/unittests/authopt/*.o + rm -f regress/unittests/authopt/test_authopt + rm -f regress/unittests/bitmap/*.o + rm -f regress/unittests/bitmap/test_bitmap + rm -f regress/unittests/conversion/*.o + rm -f regress/unittests/conversion/test_conversion + rm -f regress/unittests/hostkeys/*.o + rm -f regress/unittests/hostkeys/test_hostkeys + rm -f regress/unittests/kex/*.o + rm -f regress/unittests/kex/test_kex + rm -f regress/unittests/match/*.o + rm -f regress/unittests/match/test_match + rm -f regress/unittests/misc/*.o + rm -f regress/unittests/misc/test_misc + rm -f regress/unittests/sshbuf/*.o + rm -f regress/unittests/sshbuf/test_sshbuf + rm -f regress/unittests/sshkey/*.o + rm -f regress/unittests/sshkey/test_sshkey + rm -f regress/unittests/sshsig/*.o + rm -f regress/unittests/sshsig/test_sshsig + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8 + rm -f regress/misc/sk-dummy/*.o + rm -f regress/misc/sk-dummy/*.lo + rm -f regress/misc/sk-dummy/sk-dummy.so + (cd openbsd-compat && $(MAKE) distclean) + if test -d pkg ; then \ + rm -fr pkg ; \ + fi + +veryclean: distclean + rm -f configure config.h.in *.0 + +cleandir: veryclean + +mrproper: veryclean + +realclean: veryclean + +catman-do: + @for f in $(MANPAGES_IN) ; do \ + base=`echo $$f | sed 's/\..*$$//'` ; \ + echo "$$f -> $$base.0" ; \ + $(MANFMT) $$f | cat -v | sed -e 's/.\^H//g' \ + >$$base.0 ; \ + done + +depend: depend-rebuild + rm -f .depend.bak + +depend-rebuild: + mv .depend .depend.old + rm -f config.h .depend + touch config.h .depend + makedepend -w1000 -Y. -f .depend *.c 2>/dev/null + (echo '# Automatically generated by makedepend.'; \ + echo '# Run "make depend" to rebuild.'; sort .depend ) >.depend.tmp + mv .depend.tmp .depend + rm -f .depend.bak + mv .depend.old .depend.bak + rm -f config.h + +depend-check: depend-rebuild + cmp .depend .depend.bak || (echo .depend stale && exit 1) + +distprep: catman-do depend-check + $(AUTORECONF) + -rm -rf autom4te.cache .depend.bak + +install: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf host-key check-config +install-nokeys: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf +install-nosysconf: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files + +check-config: + -$(DESTDIR)$(sbindir)/sshd -t -f $(DESTDIR)$(sysconfdir)/sshd_config + +install-files: + $(MKDIR_P) $(DESTDIR)$(bindir) + $(MKDIR_P) $(DESTDIR)$(sbindir) + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)1 + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)5 + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8 + $(MKDIR_P) $(DESTDIR)$(libexecdir) + $(MKDIR_P) -m 0755 $(DESTDIR)$(PRIVSEP_PATH) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh$(EXEEXT) $(DESTDIR)$(bindir)/ssh$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) scp$(EXEEXT) $(DESTDIR)$(bindir)/scp$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-add$(EXEEXT) $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-agent$(EXEEXT) $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) + $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-sk-helper$(EXEEXT) $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) + $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 + $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 + $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 + $(INSTALL) -m 644 ssh-agent.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 + $(INSTALL) -m 644 ssh-keygen.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 + $(INSTALL) -m 644 ssh-keyscan.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 + $(INSTALL) -m 644 moduli.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/moduli.5 + $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5 + $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5 + $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 + $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 + $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 + $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 + $(INSTALL) -m 644 ssh-sk-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-sk-helper.8 + +install-sysconf: + $(MKDIR_P) $(DESTDIR)$(sysconfdir) + @if [ ! -f $(DESTDIR)$(sysconfdir)/ssh_config ]; then \ + $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/ssh_config; \ + else \ + echo "$(DESTDIR)$(sysconfdir)/ssh_config already exists, install will not overwrite"; \ + fi + @if [ ! -f $(DESTDIR)$(sysconfdir)/sshd_config ]; then \ + $(INSTALL) -m 644 sshd_config.out $(DESTDIR)$(sysconfdir)/sshd_config; \ + else \ + echo "$(DESTDIR)$(sysconfdir)/sshd_config already exists, install will not overwrite"; \ + fi + @if [ ! -f $(DESTDIR)$(sysconfdir)/moduli ]; then \ + if [ -f $(DESTDIR)$(sysconfdir)/primes ]; then \ + echo "moving $(DESTDIR)$(sysconfdir)/primes to $(DESTDIR)$(sysconfdir)/moduli"; \ + mv "$(DESTDIR)$(sysconfdir)/primes" "$(DESTDIR)$(sysconfdir)/moduli"; \ + else \ + $(INSTALL) -m 644 moduli.out $(DESTDIR)$(sysconfdir)/moduli; \ + fi ; \ + else \ + echo "$(DESTDIR)$(sysconfdir)/moduli already exists, install will not overwrite"; \ + fi + +host-key: ssh-keygen$(EXEEXT) + @if [ -z "$(DESTDIR)" ] ; then \ + ./ssh-keygen -A; \ + fi + +host-key-force: ssh-keygen$(EXEEXT) ssh$(EXEEXT) + ./ssh-keygen -t dsa -f $(DESTDIR)$(sysconfdir)/ssh_host_dsa_key -N "" + ./ssh-keygen -t rsa -f $(DESTDIR)$(sysconfdir)/ssh_host_rsa_key -N "" + ./ssh-keygen -t ed25519 -f $(DESTDIR)$(sysconfdir)/ssh_host_ed25519_key -N "" + if ./ssh -Q key | grep ecdsa >/dev/null ; then \ + ./ssh-keygen -t ecdsa -f $(DESTDIR)$(sysconfdir)/ssh_host_ecdsa_key -N ""; \ + fi + +uninstallall: uninstall + -rm -f $(DESTDIR)$(sysconfdir)/ssh_config + -rm -f $(DESTDIR)$(sysconfdir)/sshd_config + -rmdir $(DESTDIR)$(sysconfdir) + -rmdir $(DESTDIR)$(bindir) + -rmdir $(DESTDIR)$(sbindir) + -rmdir $(DESTDIR)$(mandir)/$(mansubdir)1 + -rmdir $(DESTDIR)$(mandir)/$(mansubdir)8 + -rmdir $(DESTDIR)$(mandir) + -rmdir $(DESTDIR)$(libexecdir) + +uninstall: + -rm -f $(DESTDIR)$(bindir)/ssh$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/scp$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) + -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) + -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) + -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) + -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) + -rm -f $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT) + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-sk-helper.8 + +regress-prep: + $(MKDIR_P) `pwd`/regress/unittests/test_helper + $(MKDIR_P) `pwd`/regress/unittests/authopt + $(MKDIR_P) `pwd`/regress/unittests/bitmap + $(MKDIR_P) `pwd`/regress/unittests/conversion + $(MKDIR_P) `pwd`/regress/unittests/hostkeys + $(MKDIR_P) `pwd`/regress/unittests/kex + $(MKDIR_P) `pwd`/regress/unittests/match + $(MKDIR_P) `pwd`/regress/unittests/misc + $(MKDIR_P) `pwd`/regress/unittests/sshbuf + $(MKDIR_P) `pwd`/regress/unittests/sshkey + $(MKDIR_P) `pwd`/regress/unittests/sshsig + $(MKDIR_P) `pwd`/regress/unittests/utf8 + $(MKDIR_P) `pwd`/regress/misc/sk-dummy + [ -f `pwd`/regress/Makefile ] || \ + ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile + +REGRESSLIBS=libssh.a $(LIBCOMPAT) + +regress/modpipe$(EXEEXT): $(srcdir)/regress/modpipe.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/modpipe.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +regress/setuid-allowed$(EXEEXT): $(srcdir)/regress/setuid-allowed.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/setuid-allowed.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +regress/netcat$(EXEEXT): $(srcdir)/regress/netcat.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +regress/mkdtemp$(EXEEXT): $(srcdir)/regress/mkdtemp.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/mkdtemp.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_HELPER_OBJS=\ + regress/unittests/test_helper/test_helper.o \ + regress/unittests/test_helper/fuzz.o + +regress/unittests/test_helper/libtest_helper.a: ${UNITTESTS_TEST_HELPER_OBJS} + $(AR) rv $@ $(UNITTESTS_TEST_HELPER_OBJS) + $(RANLIB) $@ + +UNITTESTS_TEST_SSHBUF_OBJS=\ + regress/unittests/sshbuf/tests.o \ + regress/unittests/sshbuf/test_sshbuf.o \ + regress/unittests/sshbuf/test_sshbuf_getput_basic.o \ + regress/unittests/sshbuf/test_sshbuf_getput_crypto.o \ + regress/unittests/sshbuf/test_sshbuf_misc.o \ + regress/unittests/sshbuf/test_sshbuf_fuzz.o \ + regress/unittests/sshbuf/test_sshbuf_getput_fuzz.o \ + regress/unittests/sshbuf/test_sshbuf_fixed.o + +regress/unittests/sshbuf/test_sshbuf$(EXEEXT): ${UNITTESTS_TEST_SSHBUF_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHBUF_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_SSHKEY_OBJS=\ + regress/unittests/sshkey/test_fuzz.o \ + regress/unittests/sshkey/tests.o \ + regress/unittests/sshkey/common.o \ + regress/unittests/sshkey/test_file.o \ + regress/unittests/sshkey/test_sshkey.o \ + $(SKOBJS) + +regress/unittests/sshkey/test_sshkey$(EXEEXT): ${UNITTESTS_TEST_SSHKEY_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHKEY_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_SSHSIG_OBJS=\ + sshsig.o \ + regress/unittests/sshsig/tests.o \ + $(SKOBJS) + +regress/unittests/sshsig/test_sshsig$(EXEEXT): ${UNITTESTS_TEST_SSHSIG_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHSIG_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_BITMAP_OBJS=\ + regress/unittests/bitmap/tests.o + +regress/unittests/bitmap/test_bitmap$(EXEEXT): ${UNITTESTS_TEST_BITMAP_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_BITMAP_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_AUTHOPT_OBJS=\ + regress/unittests/authopt/tests.o \ + auth-options.o \ + $(SKOBJS) + +regress/unittests/authopt/test_authopt$(EXEEXT): \ + ${UNITTESTS_TEST_AUTHOPT_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_AUTHOPT_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_CONVERSION_OBJS=\ + regress/unittests/conversion/tests.o + +regress/unittests/conversion/test_conversion$(EXEEXT): \ + ${UNITTESTS_TEST_CONVERSION_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_CONVERSION_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_KEX_OBJS=\ + regress/unittests/kex/tests.o \ + regress/unittests/kex/test_kex.o \ + $(SKOBJS) + +regress/unittests/kex/test_kex$(EXEEXT): ${UNITTESTS_TEST_KEX_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_KEX_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_HOSTKEYS_OBJS=\ + regress/unittests/hostkeys/tests.o \ + regress/unittests/hostkeys/test_iterate.o \ + $(SKOBJS) + +regress/unittests/hostkeys/test_hostkeys$(EXEEXT): \ + ${UNITTESTS_TEST_HOSTKEYS_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_HOSTKEYS_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_MATCH_OBJS=\ + regress/unittests/match/tests.o + +regress/unittests/match/test_match$(EXEEXT): \ + ${UNITTESTS_TEST_MATCH_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MATCH_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_MISC_OBJS=\ + regress/unittests/misc/tests.o \ + regress/unittests/misc/test_parse.o \ + regress/unittests/misc/test_expand.o \ + regress/unittests/misc/test_convtime.o \ + regress/unittests/misc/test_argv.o \ + regress/unittests/misc/test_strdelim.o \ + regress/unittests/misc/test_hpdelim.o + +regress/unittests/misc/test_misc$(EXEEXT): \ + ${UNITTESTS_TEST_MISC_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MISC_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +UNITTESTS_TEST_UTF8_OBJS=\ + regress/unittests/utf8/tests.o + +regress/unittests/utf8/test_utf8$(EXEEXT): \ + ${UNITTESTS_TEST_UTF8_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_UTF8_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + +# These all need to be compiled -fPIC, so they are treated differently. +SK_DUMMY_OBJS=\ + regress/misc/sk-dummy/sk-dummy.lo \ + regress/misc/sk-dummy/fatal.lo \ + ed25519.lo hash.lo ge25519.lo fe25519.lo sc25519.lo verify.lo + +SK_DUMMY_LIBRARY=@SK_DUMMY_LIBRARY@ + +.c.lo: Makefile.in config.h + $(CC) $(CFLAGS_NOPIE) $(PICFLAG) $(CPPFLAGS) -c $< -o $@ + +regress/misc/sk-dummy/sk-dummy.so: $(SK_DUMMY_OBJS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(PICFLAG) -shared -o $@ $(SK_DUMMY_OBJS) \ + -L. -Lopenbsd-compat -lopenbsd-compat $(LDFLAGS_NOPIE) $(LIBS) + +regress-binaries: regress-prep $(LIBCOMPAT) \ + regress/modpipe$(EXEEXT) \ + regress/setuid-allowed$(EXEEXT) \ + regress/netcat$(EXEEXT) \ + regress/check-perm$(EXEEXT) \ + regress/mkdtemp$(EXEEXT) \ + $(SK_DUMMY_LIBRARY) + +regress-unit-binaries: regress-prep $(REGRESSLIBS) \ + regress/unittests/authopt/test_authopt$(EXEEXT) \ + regress/unittests/bitmap/test_bitmap$(EXEEXT) \ + regress/unittests/conversion/test_conversion$(EXEEXT) \ + regress/unittests/hostkeys/test_hostkeys$(EXEEXT) \ + regress/unittests/kex/test_kex$(EXEEXT) \ + regress/unittests/match/test_match$(EXEEXT) \ + regress/unittests/misc/test_misc$(EXEEXT) \ + regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ + regress/unittests/sshkey/test_sshkey$(EXEEXT) \ + regress/unittests/sshsig/test_sshsig$(EXEEXT) \ + regress/unittests/utf8/test_utf8$(EXEEXT) + +tests: file-tests t-exec interop-tests unit + echo all tests passed + +unit: regress-unit-binaries + cd $(srcdir)/regress || exit $$?; \ + $(MAKE) \ + .CURDIR="$(abs_top_srcdir)/regress" \ + .OBJDIR="$(BUILDDIR)/regress" \ + OBJ="$(BUILDDIR)/regress" \ + $@ && echo $@ tests passed + +interop-tests t-exec file-tests: regress-prep regress-binaries $(TARGETS) + cd $(srcdir)/regress || exit $$?; \ + EGREP='@EGREP@' \ + $(MAKE) \ + .CURDIR="$(abs_top_srcdir)/regress" \ + .OBJDIR="$(BUILDDIR)/regress" \ + BUILDDIR="$(BUILDDIR)" \ + OBJ="$(BUILDDIR)/regress" \ + PATH="$(BUILDDIR):$${PATH}" \ + TEST_ENV=MALLOC_OPTIONS="@TEST_MALLOC_OPTIONS@" \ + TEST_MALLOC_OPTIONS="@TEST_MALLOC_OPTIONS@" \ + TEST_SSH_SCP="$(BUILDDIR)/scp" \ + TEST_SSH_SSH="$(BUILDDIR)/ssh" \ + TEST_SSH_SSHD="$(BUILDDIR)/sshd" \ + TEST_SSH_SSHAGENT="$(BUILDDIR)/ssh-agent" \ + TEST_SSH_SSHADD="$(BUILDDIR)/ssh-add" \ + TEST_SSH_SSHKEYGEN="$(BUILDDIR)/ssh-keygen" \ + TEST_SSH_SSHPKCS11HELPER="$(BUILDDIR)/ssh-pkcs11-helper" \ + TEST_SSH_SSHKEYSCAN="$(BUILDDIR)/ssh-keyscan" \ + TEST_SSH_SFTP="$(BUILDDIR)/sftp" \ + TEST_SSH_PKCS11_HELPER="$(BUILDDIR)/ssh-pkcs11-helper" \ + TEST_SSH_SK_HELPER="$(BUILDDIR)/ssh-sk-helper" \ + TEST_SSH_SFTPSERVER="$(BUILDDIR)/sftp-server" \ + TEST_SSH_MODULI_FILE="$(abs_top_srcdir)/moduli" \ + TEST_SSH_PLINK="plink" \ + TEST_SSH_PUTTYGEN="puttygen" \ + TEST_SSH_CONCH="conch" \ + TEST_SSH_IPV6="@TEST_SSH_IPV6@" \ + TEST_SSH_UTF8="@TEST_SSH_UTF8@" \ + TEST_SHELL="$(TEST_SHELL)" \ + EXEEXT="$(EXEEXT)" \ + $@ && echo all $@ passed + +compat-tests: $(LIBCOMPAT) + (cd openbsd-compat/regress && $(MAKE)) + +regressclean: + if [ -f regress/Makefile ] && [ -r regress/Makefile ]; then \ + (cd regress && $(MAKE) clean) \ + fi + +survey: survey.sh ssh + @$(SHELL) ./survey.sh > survey + @echo 'The survey results have been placed in the file "survey" in the' + @echo 'current directory. Please review the file then send with' + @echo '"make send-survey".' + +send-survey: survey + mail portable-survey@mindrot.org +Updated 17 Nov 1995. +Updated 19 Oct 1999 for OpenSSH-1.2 +Updated 20 May 2001 note obsolete for > OpenSSH-1.2 + +The software consists of ssh (client), sshd (server), scp, sdist, and +the auxiliary programs ssh-keygen, ssh-agent, ssh-add, and +make-ssh-known-hosts. The main program for each of these is in a .c +file with the same name. + +There are some subsystems/abstractions that are used by a number of +these programs. + + Buffer manipulation routines + + - These provide an arbitrary size buffer, where data can be appended. + Data can be consumed from either end. The code is used heavily + throughout ssh. The buffer manipulation functions are in + sshbuf*.c (header sshbuf.h). + + Compression Library + + - Ssh uses the GNU GZIP compression library (ZLIB). + + Encryption/Decryption + + - Ssh contains several encryption algorithms. These are all + accessed through the cipher.h interface. The interface code is + in cipher.c, and the implementations are either in libc or + LibreSSL. + + Multiple Precision Integer Library + + - Uses the LibreSSL BIGNUM sublibrary. + + Random Numbers + + - Uses arc4random() and such. + + RSA key generation, encryption, decryption + + - Ssh uses the RSA routines in libssl. + + RSA key files + + - RSA keys are stored in files with a special format. The code to + read/write these files is in authfile.c. The files are normally + encrypted with a passphrase. The functions to read passphrases + are in readpass.c (the same code is used to read passwords). + + Binary packet protocol + + - The ssh binary packet protocol is implemented in packet.c. The + code in packet.c does not concern itself with packet types or their + execution; it contains code to build packets, to receive them and + extract data from them, and the code to compress and/or encrypt + packets. + + - The code in packet.c calls the buffer manipulation routines + (buffer.c, bufaux.c), compression routines (zlib), and the + encryption routines. + + X11, TCP/IP, and Agent forwarding + + - Code for various types of channel forwarding is in channels.c. + The file defines a generic framework for arbitrary communication + channels inside the secure channel, and uses this framework to + implement X11 forwarding, TCP/IP forwarding, and authentication + agent forwarding. + The new, Protocol 1.5, channel close implementation is in nchan.c + + Authentication agent + + - Code to communicate with the authentication agent is in authfd.c. + + Authentication methods + + - Code for various authentication methods resides in auth-*.c + (auth-passwd.c, auth-rh-rsa.c, auth-rhosts.c, auth-rsa.c). This + code is linked into the server. The routines also manipulate + known hosts files using code in hostfile.c. Code in canohost.c + is used to retrieve the canonical host name of the remote host. + Code in match.c is used to match host names. + + - In the client end, authentication code is in sshconnect.c. It + reads Passwords/passphrases using code in readpass.c. It reads + RSA key files with authfile.c. It communicates the + authentication agent using authfd.c. + + The ssh client + + - The client main program is in ssh.c. It first parses arguments + and reads configuration (readconf.c), then calls ssh_connect (in + sshconnect.c) to open a connection to the server (possibly via a + proxy), and performs authentication (ssh_login in sshconnect.c). + It then makes any pty, forwarding, etc. requests. It may call + code in ttymodes.c to encode current tty modes. Finally it + calls client_loop in clientloop.c. This does the real work for + the session. + + Pseudo-tty manipulation and tty modes + + - Code to allocate and use a pseudo tty is in pty.c. Code to + encode and set terminal modes is in ttymodes.c. + + Logging in (updating utmp, lastlog, etc.) + + - The code to do things that are done when a user logs in are in + login.c. This includes things such as updating the utmp, wtmp, + and lastlog files. Some of the code is in sshd.c. + + Writing to the system log and terminal + + - The programs use the functions fatal(), log(), debug(), error() + in many places to write messages to system log or user's + terminal. The implementation that logs to system log is in + log-server.c; it is used in the server program. The other + programs use an implementation that sends output to stderr; it + is in log-client.c. The definitions are in ssh.h. + + The sshd server (daemon) + + - The sshd daemon starts by processing arguments and reading the + configuration file (servconf.c). It then reads the host key, + starts listening for connections, and generates the server key. + The server key will be regenerated every hour by an alarm. + + - When the server receives a connection, it forks, disables the + regeneration alarm, and starts communicating with the client. + They first perform identification string exchange, then + negotiate encryption, then perform authentication, preparatory + operations, and finally the server enters the normal session + mode by calling server_loop in serverloop.c. This does the real + work, calling functions in other modules. + + - The code for the server is in sshd.c. It contains a lot of + stuff, including: + - server main program + - waiting for connections + - processing new connection + - authentication + - preparatory operations + - building up the execution environment for the user program + - starting the user program. + + Auxiliary files + + - There are several other files in the distribution that contain + various auxiliary routines: + ssh.h the main header file for ssh (various definitions) + uidswap.c uid-swapping + xmalloc.c "safe" malloc routines + +$OpenBSD: OVERVIEW,v 1.15 2018/10/23 05:56:35 djm Exp $ diff --git a/aosp/external/openssh/OWNERS b/aosp/external/openssh/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..f3c79ccc4fa73a1a0378076625f5b9196183cda7 --- /dev/null +++ b/aosp/external/openssh/OWNERS @@ -0,0 +1,3 @@ +# Default code reviewers picked from top 3 or more developers. +# Please update this list if you find better candidates. +adelva@google.com diff --git a/aosp/external/openssh/PROTOCOL b/aosp/external/openssh/PROTOCOL new file mode 100644 index 0000000000000000000000000000000000000000..2d50b5cb0528d550f9acdc83ea16e8cfa3604259 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL @@ -0,0 +1,652 @@ +This documents OpenSSH's deviations and extensions to the published SSH +protocol. + +Note that OpenSSH's sftp and sftp-server implement revision 3 of the SSH +filexfer protocol described in: + +https://www.openssh.com/txt/draft-ietf-secsh-filexfer-02.txt + +Newer versions of the draft will not be supported, though some features +are individually implemented as extensions described below. + +The protocol used by OpenSSH's ssh-agent is described in the file +PROTOCOL.agent + +1. Transport protocol changes + +1.1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com" + +This is a new transport-layer MAC method using the UMAC algorithm +(rfc4418). This method is identical to the "umac-64" method documented +in: + +https://www.openssh.com/txt/draft-miller-secsh-umac-01.txt + +1.2. transport: Protocol 2 compression algorithm "zlib@openssh.com" + +This transport-layer compression method uses the zlib compression +algorithm (identical to the "zlib" method in rfc4253), but delays the +start of compression until after authentication has completed. This +avoids exposing compression code to attacks from unauthenticated users. + +The method is documented in: + +https://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt + +1.3. transport: New public key algorithms "ssh-rsa-cert-v01@openssh.com", + "ssh-dsa-cert-v01@openssh.com", + "ecdsa-sha2-nistp256-cert-v01@openssh.com", + "ecdsa-sha2-nistp384-cert-v01@openssh.com" and + "ecdsa-sha2-nistp521-cert-v01@openssh.com" + +OpenSSH introduces new public key algorithms to support certificate +authentication for users and host keys. These methods are documented +in the file PROTOCOL.certkeys + +1.4. transport: Elliptic Curve cryptography + +OpenSSH supports ECC key exchange and public key authentication as +specified in RFC5656. Only the ecdsa-sha2-nistp256, ecdsa-sha2-nistp384 +and ecdsa-sha2-nistp521 curves over GF(p) are supported. Elliptic +curve points encoded using point compression are NOT accepted or +generated. + +1.5 transport: Protocol 2 Encrypt-then-MAC MAC algorithms + +OpenSSH supports MAC algorithms, whose names contain "-etm", that +perform the calculations in a different order to that defined in RFC +4253. These variants use the so-called "encrypt then MAC" ordering, +calculating the MAC over the packet ciphertext rather than the +plaintext. This ordering closes a security flaw in the SSH transport +protocol, where decryption of unauthenticated ciphertext provided a +"decryption oracle" that could, in conjunction with cipher flaws, reveal +session plaintext. + +Specifically, the "-etm" MAC algorithms modify the transport protocol +to calculate the MAC over the packet ciphertext and to send the packet +length unencrypted. This is necessary for the transport to obtain the +length of the packet and location of the MAC tag so that it may be +verified without decrypting unauthenticated data. + +As such, the MAC covers: + + mac = MAC(key, sequence_number || packet_length || encrypted_packet) + +where "packet_length" is encoded as a uint32 and "encrypted_packet" +contains: + + byte padding_length + byte[n1] payload; n1 = packet_length - padding_length - 1 + byte[n2] random padding; n2 = padding_length + +1.6 transport: AES-GCM + +OpenSSH supports the AES-GCM algorithm as specified in RFC 5647. +Because of problems with the specification of the key exchange +the behaviour of OpenSSH differs from the RFC as follows: + +AES-GCM is only negotiated as the cipher algorithms +"aes128-gcm@openssh.com" or "aes256-gcm@openssh.com" and never as +an MAC algorithm. Additionally, if AES-GCM is selected as the cipher +the exchanged MAC algorithms are ignored and there doesn't have to be +a matching MAC. + +1.7 transport: chacha20-poly1305@openssh.com authenticated encryption + +OpenSSH supports authenticated encryption using ChaCha20 and Poly1305 +as described in PROTOCOL.chacha20poly1305. + +1.8 transport: curve25519-sha256@libssh.org key exchange algorithm + +OpenSSH supports the use of ECDH in Curve25519 for key exchange as +described at: +http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.org.txt?h=curve25519 + +2. Connection protocol changes + +2.1. connection: Channel write close extension "eow@openssh.com" + +The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF +message to allow an endpoint to signal its peer that it will send no +more data over a channel. Unfortunately, there is no symmetric way for +an endpoint to request that its peer should cease sending data to it +while still keeping the channel open for the endpoint to send data to +the peer. + +This is desirable, since it saves the transmission of data that would +otherwise need to be discarded and it allows an endpoint to signal local +processes of the condition, e.g. by closing the corresponding file +descriptor. + +OpenSSH implements a channel extension message to perform this +signalling: "eow@openssh.com" (End Of Write). This message is sent by +an endpoint when the local output of a session channel is closed or +experiences a write error. The message is formatted as follows: + + byte SSH_MSG_CHANNEL_REQUEST + uint32 recipient channel + string "eow@openssh.com" + boolean FALSE + +On receiving this message, the peer SHOULD cease sending data of +the channel and MAY signal the process from which the channel data +originates (e.g. by closing its read file descriptor). + +As with the symmetric SSH_MSG_CHANNEL_EOF message, the channel does +remain open after a "eow@openssh.com" has been sent and more data may +still be sent in the other direction. This message does not consume +window space and may be sent even if no window space is available. + +NB. due to certain broken SSH implementations aborting upon receipt +of this message (in contravention of RFC4254 section 5.4), this +message is only sent to OpenSSH peers (identified by banner). +Other SSH implementations may be listed to receive this message +upon request. + +2.2. connection: disallow additional sessions extension + "no-more-sessions@openssh.com" + +Most SSH connections will only ever request a single session, but a +attacker may abuse a running ssh client to surreptitiously open +additional sessions under their control. OpenSSH provides a global +request "no-more-sessions@openssh.com" to mitigate this attack. + +When an OpenSSH client expects that it will never open another session +(i.e. it has been started with connection multiplexing disabled), it +will send the following global request: + + byte SSH_MSG_GLOBAL_REQUEST + string "no-more-sessions@openssh.com" + char want-reply + +On receipt of such a message, an OpenSSH server will refuse to open +future channels of type "session" and instead immediately abort the +connection. + +Note that this is not a general defence against compromised clients +(that is impossible), but it thwarts a simple attack. + +NB. due to certain broken SSH implementations aborting upon receipt +of this message, the no-more-sessions request is only sent to OpenSSH +servers (identified by banner). Other SSH implementations may be +listed to receive this message upon request. + +2.3. connection: Tunnel forward extension "tun@openssh.com" + +OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" +channel type. This channel type supports forwarding of network packets +with datagram boundaries intact between endpoints equipped with +interfaces like the BSD tun(4) device. Tunnel forwarding channels are +requested by the client with the following packet: + + byte SSH_MSG_CHANNEL_OPEN + string "tun@openssh.com" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + uint32 tunnel mode + uint32 remote unit number + +The "tunnel mode" parameter specifies whether the tunnel should forward +layer 2 frames or layer 3 packets. It may take one of the following values: + + SSH_TUNMODE_POINTOPOINT 1 /* layer 3 packets */ + SSH_TUNMODE_ETHERNET 2 /* layer 2 frames */ + +The "tunnel unit number" specifies the remote interface number, or may +be 0x7fffffff to allow the server to automatically choose an interface. A +server that is not willing to open a client-specified unit should refuse +the request with a SSH_MSG_CHANNEL_OPEN_FAILURE error. On successful +open, the server should reply with SSH_MSG_CHANNEL_OPEN_SUCCESS. + +Once established the client and server may exchange packet or frames +over the tunnel channel by encapsulating them in SSH protocol strings +and sending them as channel data. This ensures that packet boundaries +are kept intact. Specifically, packets are transmitted using normal +SSH_MSG_CHANNEL_DATA packets: + + byte SSH_MSG_CHANNEL_DATA + uint32 recipient channel + string data + +The contents of the "data" field for layer 3 packets is: + + uint32 packet length + uint32 address family + byte[packet length - 4] packet data + +The "address family" field identifies the type of packet in the message. +It may be one of: + + SSH_TUN_AF_INET 2 /* IPv4 */ + SSH_TUN_AF_INET6 24 /* IPv6 */ + +The "packet data" field consists of the IPv4/IPv6 datagram itself +without any link layer header. + +The contents of the "data" field for layer 2 packets is: + + uint32 packet length + byte[packet length] frame + +The "frame" field contains an IEEE 802.3 Ethernet frame, including +header. + +2.4. connection: Unix domain socket forwarding + +OpenSSH supports local and remote Unix domain socket forwarding +using the "streamlocal" extension. Forwarding is initiated as per +TCP sockets but with a single path instead of a host and port. + +Similar to direct-tcpip, direct-streamlocal is sent by the client +to request that the server make a connection to a Unix domain socket. + + byte SSH_MSG_CHANNEL_OPEN + string "direct-streamlocal@openssh.com" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string socket path + string reserved + uint32 reserved + +Similar to forwarded-tcpip, forwarded-streamlocal is sent by the +server when the client has previously send the server a streamlocal-forward +GLOBAL_REQUEST. + + byte SSH_MSG_CHANNEL_OPEN + string "forwarded-streamlocal@openssh.com" + uint32 sender channel + uint32 initial window size + uint32 maximum packet size + string socket path + string reserved for future use + +The reserved field is not currently defined and is ignored on the +remote end. It is intended to be used in the future to pass +information about the socket file, such as ownership and mode. +The client currently sends the empty string for this field. + +Similar to tcpip-forward, streamlocal-forward is sent by the client +to request remote forwarding of a Unix domain socket. + + byte SSH2_MSG_GLOBAL_REQUEST + string "streamlocal-forward@openssh.com" + boolean TRUE + string socket path + +Similar to cancel-tcpip-forward, cancel-streamlocal-forward is sent +by the client cancel the forwarding of a Unix domain socket. + + byte SSH2_MSG_GLOBAL_REQUEST + string "cancel-streamlocal-forward@openssh.com" + boolean FALSE + string socket path + +2.5. connection: hostkey update and rotation "hostkeys-00@openssh.com" +and "hostkeys-prove-00@openssh.com" + +OpenSSH supports a protocol extension allowing a server to inform +a client of all its protocol v.2 host keys after user-authentication +has completed. + + byte SSH_MSG_GLOBAL_REQUEST + string "hostkeys-00@openssh.com" + char 0 /* want-reply */ + string[] hostkeys + +Upon receiving this message, a client should check which of the +supplied host keys are present in known_hosts. + +Note that the server may send key types that the client does not +support. The client should disregard such keys if they are received. + +If the client identifies any keys that are not present for the host, +it should send a "hostkeys-prove@openssh.com" message to request the +server prove ownership of the private half of the key. + + byte SSH_MSG_GLOBAL_REQUEST + string "hostkeys-prove-00@openssh.com" + char 1 /* want-reply */ + string[] hostkeys + +When a server receives this message, it should generate a signature +using each requested key over the following: + + string "hostkeys-prove-00@openssh.com" + string session identifier + string hostkey + +These signatures should be included in the reply, in the order matching +the hostkeys in the request: + + byte SSH_MSG_REQUEST_SUCCESS + string[] signatures + +When the client receives this reply (and not a failure), it should +validate the signatures and may update its known_hosts file, adding keys +that it has not seen before and deleting keys for the server host that +are no longer offered. + +These extensions let a client learn key types that it had not previously +encountered, thereby allowing it to potentially upgrade from weaker +key algorithms to better ones. It also supports graceful key rotation: +a server may offer multiple keys of the same type for a period (to +give clients an opportunity to learn them using this extension) before +removing the deprecated key from those offered. + +2.6. connection: SIGINFO support for "signal" channel request + +The SSH channels protocol (RFC4254 section 6.9) supports sending a +signal to a session attached to a channel. OpenSSH supports one +extension signal "INFO@openssh.com" that allows sending SIGINFO on +BSD-derived systems. + +3. Authentication protocol changes + +3.1. Host-bound public key authentication + +This is trivial change to the traditional "publickey" authentication +method. The authentication request is identical to the original method +but for the name and one additional field: + + byte SSH2_MSG_USERAUTH_REQUEST + string username + string "ssh-connection" + string "publickey-hostbound-v00@openssh.com" + bool has_signature + string pkalg + string public key + string server host key + +Because the entire SSH2_MSG_USERAUTH_REQUEST message is included in +the signed data, this ensures that a binding between the destination +user, the server identity and the session identifier is visible to the +signer. OpenSSH uses this binding via signed data to implement per-key +restrictions in ssh-agent. + +A server may advertise this method using the SSH2_MSG_EXT_INFO +mechanism (RFC8308), with the following message: + + string "publickey-hostbound@openssh.com" + string "0" (version) + +Clients should prefer host-bound authentication when advertised by +server. + +4. SFTP protocol changes + +4.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK + +When OpenSSH's sftp-server was implemented, the order of the arguments +to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, +the reversal was not noticed until the server was widely deployed. Since +fixing this to follow the specification would cause incompatibility, the +current order was retained. For correct operation, clients should send +SSH_FXP_SYMLINK as follows: + + uint32 id + string targetpath + string linkpath + +4.2. sftp: Server extension announcement in SSH_FXP_VERSION + +OpenSSH's sftp-server lists the extensions it supports using the +standard extension announcement mechanism in the SSH_FXP_VERSION server +hello packet: + + uint32 3 /* protocol version */ + string ext1-name + string ext1-version + string ext2-name + string ext2-version + ... + string extN-name + string extN-version + +Each extension reports its integer version number as an ASCII encoded +string, e.g. "1". The version will be incremented if the extension is +ever changed in an incompatible way. The server MAY advertise the same +extension with multiple versions (though this is unlikely). Clients MUST +check the version number before attempting to use the extension. + +4.3. sftp: Extension request "posix-rename@openssh.com" + +This operation provides a rename operation with POSIX semantics, which +are different to those provided by the standard SSH_FXP_RENAME in +draft-ietf-secsh-filexfer-02.txt. This request is implemented as a +SSH_FXP_EXTENDED request with the following format: + + uint32 id + string "posix-rename@openssh.com" + string oldpath + string newpath + +On receiving this request the server will perform the POSIX operation +rename(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +4.4. sftp: Extension requests "statvfs@openssh.com" and + "fstatvfs@openssh.com" + +These requests correspond to the statvfs and fstatvfs POSIX system +interfaces. The "statvfs@openssh.com" request operates on an explicit +pathname, and is formatted as follows: + + uint32 id + string "statvfs@openssh.com" + string path + +The "fstatvfs@openssh.com" operates on an open file handle: + + uint32 id + string "fstatvfs@openssh.com" + string handle + +These requests return a SSH_FXP_STATUS reply on failure. On success they +return the following SSH_FXP_EXTENDED_REPLY reply: + + uint32 id + uint64 f_bsize /* file system block size */ + uint64 f_frsize /* fundamental fs block size */ + uint64 f_blocks /* number of blocks (unit f_frsize) */ + uint64 f_bfree /* free blocks in file system */ + uint64 f_bavail /* free blocks for non-root */ + uint64 f_files /* total file inodes */ + uint64 f_ffree /* free file inodes */ + uint64 f_favail /* free file inodes for to non-root */ + uint64 f_fsid /* file system id */ + uint64 f_flag /* bit mask of f_flag values */ + uint64 f_namemax /* maximum filename length */ + +The values of the f_flag bitmask are as follows: + + #define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */ + #define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */ + +Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are +advertised in the SSH_FXP_VERSION hello with version "2". + +4.5. sftp: Extension request "hardlink@openssh.com" + +This request is for creating a hard link to a regular file. This +request is implemented as a SSH_FXP_EXTENDED request with the +following format: + + uint32 id + string "hardlink@openssh.com" + string oldpath + string newpath + +On receiving this request the server will perform the operation +link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +4.6. sftp: Extension request "fsync@openssh.com" + +This request asks the server to call fsync(2) on an open file handle. + + uint32 id + string "fsync@openssh.com" + string handle + +On receiving this request, a server will call fsync(handle_fd) and will +respond with a SSH_FXP_STATUS message. + +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +4.7. sftp: Extension request "lsetstat@openssh.com" + +This request is like the "setstat" command, but sets file attributes on +symlinks. It is implemented as a SSH_FXP_EXTENDED request with the +following format: + + uint32 id + string "lsetstat@openssh.com" + string path + ATTRS attrs + +See the "setstat" command for more details. + +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +4.8. sftp: Extension request "limits@openssh.com" + +This request is used to determine various limits the server might impose. +Clients should not attempt to exceed these limits as the server might sever +the connection immediately. + + uint32 id + string "limits@openssh.com" + +The server will respond with a SSH_FXP_EXTENDED_REPLY reply: + + uint32 id + uint64 max-packet-length + uint64 max-read-length + uint64 max-write-length + uint64 max-open-handles + +The 'max-packet-length' applies to the total number of bytes in a +single SFTP packet. Servers SHOULD set this at least to 34000. + +The 'max-read-length' is the largest length in a SSH_FXP_READ packet. +Even if the client requests a larger size, servers will usually respond +with a shorter SSH_FXP_DATA packet. Servers SHOULD set this at least to +32768. + +The 'max-write-length' is the largest length in a SSH_FXP_WRITE packet +the server will accept. Servers SHOULD set this at least to 32768. + +The 'max-open-handles' is the maximum number of active handles that the +server allows (e.g. handles created by SSH_FXP_OPEN and SSH_FXP_OPENDIR +packets). Servers MAY count internal file handles against this limit +(e.g. system logging or stdout/stderr), so clients SHOULD NOT expect to +open this many handles in practice. + +If the server doesn't enforce a specific limit, then the field may be +set to 0. This implies the server relies on the OS to enforce limits +(e.g. available memory or file handles), and such limits might be +dynamic. The client SHOULD take care to not try to exceed reasonable +limits. + +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +4.9. sftp: Extension request "expand-path@openssh.com" + +This request supports canonicalisation of relative paths and +those that need tilde-expansion, i.e. "~", "~/..." and "~user/..." +These paths are expanded using shell-like rules and the resultant +path is canonicalised similarly to SSH2_FXP_REALPATH. + +It is implemented as a SSH_FXP_EXTENDED request with the following +format: + + uint32 id + string "expand-path@openssh.com" + string path + +Its reply is the same format as that of SSH2_FXP_REALPATH. + +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +4.10. sftp: Extension request "copy-data" + +This request asks the server to copy data from one open file handle and +write it to a different open file handle. This avoids needing to transfer +the data across the network twice (a download followed by an upload). + + byte SSH_FXP_EXTENDED + uint32 id + string "copy-data" + string read-from-handle + uint64 read-from-offset + uint64 read-data-length + string write-to-handle + uint64 write-to-offset + +The server will copy read-data-length bytes starting from +read-from-offset from the read-from-handle and write them to +write-to-handle starting from write-to-offset, and then respond with a +SSH_FXP_STATUS message. + +It's equivalent to issuing a series of SSH_FXP_READ requests on +read-from-handle and a series of requests of SSH_FXP_WRITE on +write-to-handle. + +If read-from-handle and write-to-handle are the same, the server will +fail the request and respond with a SSH_FX_INVALID_PARAMETER message. + +If read-data-length is 0, then the server will read data from the +read-from-handle until EOF is reached. + +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + +This request is identical to the "copy-data" request documented in: + +https://tools.ietf.org/html/draft-ietf-secsh-filexfer-extensions-00#section-7 + +5. Miscellaneous changes + +5.1 Public key format + +OpenSSH public keys, as generated by ssh-keygen(1) and appearing in +authorized_keys files, are formatted as a single line of text consisting +of the public key algorithm name followed by a base64-encoded key blob. +The public key blob (before base64 encoding) is the same format used for +the encoding of public keys sent on the wire: as described in RFC4253 +section 6.6 for RSA and DSA keys, RFC5656 section 3.1 for ECDSA keys +and the "New public key formats" section of PROTOCOL.certkeys for the +OpenSSH certificate formats. + +5.2 Private key format + +OpenSSH private keys, as generated by ssh-keygen(1) use the format +described in PROTOCOL.key by default. As a legacy option, PEM format +(RFC7468) private keys are also supported for RSA, DSA and ECDSA keys +and were the default format before OpenSSH 7.8. + +5.3 KRL format + +OpenSSH supports a compact format for Key Revocation Lists (KRLs). This +format is described in the PROTOCOL.krl file. + +5.4 Connection multiplexing + +OpenSSH's connection multiplexing uses messages as described in +PROTOCOL.mux over a Unix domain socket for communications between a +master instance and later clients. + +5.5. Agent protocol extensions + +OpenSSH extends the usual agent protocol. These changes are documented +in the PROTOCOL.agent file. + +$OpenBSD: PROTOCOL,v 1.44 2022/03/31 03:05:49 djm Exp $ diff --git a/aosp/external/openssh/PROTOCOL.agent b/aosp/external/openssh/PROTOCOL.agent new file mode 100644 index 0000000000000000000000000000000000000000..67302c34495ef23056d6eeadb18724cd5dfeb905 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.agent @@ -0,0 +1,84 @@ +The SSH agent protocol is described in +https://tools.ietf.org/html/draft-miller-ssh-agent-04 + +This file document's OpenSSH's extensions to the agent protocol. + +1. session-bind@openssh.com extension + +This extension allows a ssh client to bind an agent connection to a +particular SSH session identifier as derived from the initial key +exchange (as per RFC4253 section 7.2) and the host key used for that +exchange. This binding is verifiable at the agent by including the +initial KEX signature made by the host key. + +The message format is: + + byte SSH_AGENTC_EXTENSION (0x1b) + string session-bind@openssh.com + string hostkey + string session identifier + string signature + bool is_forwarding + +Where 'hostkey' is the encoded server host public key, 'session +identifier' is the exchange hash derived from the initial key +exchange, 'signature' is the server's signature of the session +identifier using the private hostkey, as sent in the final +SSH2_MSG_KEXDH_REPLY/SSH2_MSG_KEXECDH_REPLY message of the initial key +exchange. 'is_forwarding' is a flag indicating whether this connection +should be bound for user authentication or forwarding. + +When an agent received this message, it will verify the signature and +check the consistency of its contents, including refusing to accept +a duplicate session identifier, or any attempt to bind a connection +previously bound for authentication. It will then then record the +binding for the life of the connection for use later in testing per-key +destination constraints. + +2. restrict-destination-v00@openssh.com key constraint extension + +The key constraint extension supports destination- and forwarding path- +restricted keys. It may be attached as a constraint when keys or +smartcard keys are added to an agent. + + byte SSH_AGENT_CONSTRAIN_EXTENSION (0xff) + string restrict-destination-v00@openssh.com + constraint[] constraints + +Where a constraint consists of: + + string from_username (must be empty) + string from_hostname + keyspec[] from_hostkeys + string to_username + string to_hostname + keyspec[] to_hostkeys + +An a keyspec consists of: + + string keyblob + bool is_ca + +When receiving this message, the agent will ensure that the +'from_username' field is empty, and that 'to_hostname' and 'to_hostkeys' +have been supplied (empty 'from_hostname' and 'from_hostkeys' are valid +and signify the initial hop from the host running ssh-agent). The agent +will then record the constraint against the key. + +Subsequent operations on this key including add/remove/request +identities and, in particular, signature requests will check the key +constraints against the session-bind@openssh.com bindings recorded for +the agent connection over which they were received. + +3. SSH_AGENT_CONSTRAIN_MAXSIGN key constraint + +This key constraint allows communication to an agent of the maximum +number of signatures that may be made with an XMSS key. The format of +the constraint is: + + byte SSH_AGENT_CONSTRAIN_MAXSIGN (0x03) + uint32 max_signatures + +This option is only valid for XMSS keys. + +$OpenBSD: PROTOCOL.agent,v 1.16 2022/01/01 01:55:30 jsg Exp $ diff --git a/aosp/external/openssh/PROTOCOL.certkeys b/aosp/external/openssh/PROTOCOL.certkeys new file mode 100644 index 0000000000000000000000000000000000000000..68622e60743cb6c92984c9df93590f01c47dd1c0 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.certkeys @@ -0,0 +1,321 @@ +This document describes a simple public-key certificate authentication +system for use by SSH. + +Background +---------- + +The SSH protocol currently supports a simple public key authentication +mechanism. Unlike other public key implementations, SSH eschews the use +of X.509 certificates and uses raw keys. This approach has some benefits +relating to simplicity of configuration and minimisation of attack +surface, but it does not support the important use-cases of centrally +managed, passwordless authentication and centrally certified host keys. + +These protocol extensions build on the simple public key authentication +system already in SSH to allow certificate-based authentication. The +certificates used are not traditional X.509 certificates, with numerous +options and complex encoding rules, but something rather more minimal: a +key, some identity information and usage options that have been signed +with some other trusted key. + +A sshd server may be configured to allow authentication via certified +keys, by extending the existing ~/.ssh/authorized_keys mechanism to +allow specification of certification authority keys in addition to +raw user keys. The ssh client will support automatic verification of +acceptance of certified host keys, by adding a similar ability to +specify CA keys in ~/.ssh/known_hosts. + +All certificate types include certification information along with the +public key that is used to sign challenges. In OpenSSH, ssh-keygen +performs the CA signing operation. + +Certified keys are represented using new key types: + + ssh-rsa-cert-v01@openssh.com + ssh-dss-cert-v01@openssh.com + ecdsa-sha2-nistp256-cert-v01@openssh.com + ecdsa-sha2-nistp384-cert-v01@openssh.com + ecdsa-sha2-nistp521-cert-v01@openssh.com + ssh-ed25519-cert-v01@openssh.com + +Two additional types exist for RSA certificates to force use of +SHA-2 signatures (SHA-256 and SHA-512 respectively): + + rsa-sha2-256-cert-v01@openssh.com + rsa-sha2-512-cert-v01@openssh.com + +These RSA/SHA-2 types should not appear in keys at rest or transmitted +on the wire, but do appear in a SSH_MSG_KEXINIT's host-key algorithms +field or in the "public key algorithm name" field of a "publickey" +SSH_USERAUTH_REQUEST to indicate that the signature will use the +specified algorithm. + +Protocol extensions +------------------- + +The SSH wire protocol includes several extensibility mechanisms. +These modifications shall take advantage of namespaced public key +algorithm names to add support for certificate authentication without +breaking the protocol - implementations that do not support the +extensions will simply ignore them. + +Authentication using the new key formats described below proceeds +using the existing SSH "publickey" authentication method described +in RFC4252 section 7. + +New public key formats +---------------------- + +The certificate key types take a similar high-level format (note: data +types and encoding are as per RFC4251 section 5). The serialised wire +encoding of these certificates is also used for storing them on disk. + +#define SSH_CERT_TYPE_USER 1 +#define SSH_CERT_TYPE_HOST 2 + +RSA certificate + + string "ssh-rsa-cert-v01@openssh.com" + string nonce + mpint e + mpint n + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +DSA certificate + + string "ssh-dss-cert-v01@openssh.com" + string nonce + mpint p + mpint q + mpint g + mpint y + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +ECDSA certificate + + string "ecdsa-sha2-nistp256-cert-v01@openssh.com" | + "ecdsa-sha2-nistp384-cert-v01@openssh.com" | + "ecdsa-sha2-nistp521-cert-v01@openssh.com" + string nonce + string curve + string public_key + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +ED25519 certificate + + string "ssh-ed25519-cert-v01@openssh.com" + string nonce + string pk + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +The nonce field is a CA-provided random bitstring of arbitrary length +(but typically 16 or 32 bytes) included to make attacks that depend on +inducing collisions in the signature hash infeasible. + +e and n are the RSA exponent and public modulus respectively. + +p, q, g, y are the DSA parameters as described in FIPS-186-2. + +curve and public key are respectively the ECDSA "[identifier]" and "Q" +defined in section 3.1 of RFC5656. + +pk is the encoded Ed25519 public key as defined by RFC8032. + +serial is an optional certificate serial number set by the CA to +provide an abbreviated way to refer to certificates from that CA. +If a CA does not wish to number its certificates, it must set this +field to zero. + +type specifies whether this certificate is for identification of a user +or a host using a SSH_CERT_TYPE_... value. + +key id is a free-form text field that is filled in by the CA at the time +of signing; the intention is that the contents of this field are used to +identify the identity principal in log messages. + +"valid principals" is a string containing zero or more principals as +strings packed inside it. These principals list the names for which this +certificate is valid; hostnames for SSH_CERT_TYPE_HOST certificates and +usernames for SSH_CERT_TYPE_USER certificates. As a special case, a +zero-length "valid principals" field means the certificate is valid for +any principal of the specified type. + +"valid after" and "valid before" specify a validity period for the +certificate. Each represents a time in seconds since 1970-01-01 +00:00:00. A certificate is considered valid if: + + valid after <= current time < valid before + +critical options is a set of zero or more key options encoded as +below. All such options are "critical" in the sense that an implementation +must refuse to authorise a key that has an unrecognised option. + +extensions is a set of zero or more optional extensions. These extensions +are not critical, and an implementation that encounters one that it does +not recognise may safely ignore it. + +Generally, critical options are used to control features that restrict +access where extensions are used to enable features that grant access. +This ensures that certificates containing unknown restrictions do not +inadvertently grant access while allowing new protocol features to be +enabled via extensions without breaking certificates' backwards +compatibility. + +The reserved field is currently unused and is ignored in this version of +the protocol. + +The signature key field contains the CA key used to sign the +certificate. The valid key types for CA keys are ssh-rsa, +ssh-dss, ssh-ed25519 and the ECDSA types ecdsa-sha2-nistp256, +ecdsa-sha2-nistp384, ecdsa-sha2-nistp521. "Chained" certificates, where +the signature key type is a certificate type itself are NOT supported. +Note that it is possible for a RSA certificate key to be signed by a +Ed25519 or ECDSA CA key and vice-versa. + +signature is computed over all preceding fields from the initial string +up to, and including the signature key. Signatures are computed and +encoded according to the rules defined for the CA's public key algorithm +(RFC4253 section 6.6 for ssh-rsa and ssh-dss, RFC5656 for the ECDSA +types, and RFC8032 for Ed25519). + +Critical options +---------------- + +The critical options section of the certificate specifies zero or more +options on the certificate's validity. The format of this field +is a sequence of zero or more tuples: + + string name + string data + +Options must be lexically ordered by "name" if they appear in the +sequence. Each named option may only appear once in a certificate. + +The name field identifies the option and the data field encodes +option-specific information (see below). All options are +"critical"; if an implementation does not recognise a option, +then the validating party should refuse to accept the certificate. + +Custom options should append the originating author or organisation's +domain name to the option name, e.g. "my-option@example.com". + +No critical options are defined for host certificates at present. The +supported user certificate options and the contents and structure of +their data fields are: + +Name Format Description +----------------------------------------------------------------------------- +force-command string Specifies a command that is executed + (replacing any the user specified on the + ssh command-line) whenever this key is + used for authentication. + +source-address string Comma-separated list of source addresses + from which this certificate is accepted + for authentication. Addresses are + specified in CIDR format (nn.nn.nn.nn/nn + or hhhh::hhhh/nn). + If this option is not present, then + certificates may be presented from any + source address. + +verify-required empty Flag indicating that signatures made + with this certificate must assert FIDO + user verification (e.g. PIN or + biometric). This option only makes sense + for the U2F/FIDO security key types that + support this feature in their signature + formats. + +Extensions +---------- + +The extensions section of the certificate specifies zero or more +non-critical certificate extensions. The encoding and ordering of +extensions in this field is identical to that of the critical options, +as is the requirement that each name appear only once. + +If an implementation does not recognise an extension, then it should +ignore it. + +Custom options should append the originating author or organisation's +domain name to the option name, e.g. "my-option@example.com". + +No extensions are defined for host certificates at present. The +supported user certificate extensions and the contents and structure of +their data fields are: + +Name Format Description +----------------------------------------------------------------------------- +no-touch-required empty Flag indicating that signatures made + with this certificate need not assert + FIDO user presence. This option only + makes sense for the U2F/FIDO security + key types that support this feature in + their signature formats. + +permit-X11-forwarding empty Flag indicating that X11 forwarding + should be permitted. X11 forwarding will + be refused if this option is absent. + +permit-agent-forwarding empty Flag indicating that agent forwarding + should be allowed. Agent forwarding + must not be permitted unless this + option is present. + +permit-port-forwarding empty Flag indicating that port-forwarding + should be allowed. If this option is + not present, then no port forwarding will + be allowed. + +permit-pty empty Flag indicating that PTY allocation + should be permitted. In the absence of + this option PTY allocation will be + disabled. + +permit-user-rc empty Flag indicating that execution of + ~/.ssh/rc should be permitted. Execution + of this script will not be permitted if + this option is not present. + +$OpenBSD: PROTOCOL.certkeys,v 1.19 2021/06/05 13:47:00 naddy Exp $ diff --git a/aosp/external/openssh/PROTOCOL.chacha20poly1305 b/aosp/external/openssh/PROTOCOL.chacha20poly1305 new file mode 100644 index 0000000000000000000000000000000000000000..0bfff28d70ef4ed06b6a70923f4564c4612ecc63 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.chacha20poly1305 @@ -0,0 +1,107 @@ +This document describes the chacha20-poly1305@openssh.com authenticated +encryption cipher supported by OpenSSH. + +Background +---------- + +ChaCha20 is a stream cipher designed by Daniel Bernstein and described +in [1]. It operates by permuting 128 fixed bits, 128 or 256 bits of key, +a 64 bit nonce and a 64 bit counter into 64 bytes of output. This output +is used as a keystream, with any unused bytes simply discarded. + +Poly1305[2], also by Daniel Bernstein, is a one-time Carter-Wegman MAC +that computes a 128 bit integrity tag given a message and a single-use +256 bit secret key. + +The chacha20-poly1305@openssh.com combines these two primitives into an +authenticated encryption mode. The construction used is based on that +proposed for TLS by Adam Langley in [3], but differs in the layout of +data passed to the MAC and in the addition of encryption of the packet +lengths. + +Negotiation +----------- + +The chacha20-poly1305@openssh.com offers both encryption and +authentication. As such, no separate MAC is required. If the +chacha20-poly1305@openssh.com cipher is selected in key exchange, +the offered MAC algorithms are ignored and no MAC is required to be +negotiated. + +Detailed Construction +--------------------- + +The chacha20-poly1305@openssh.com cipher requires 512 bits of key +material as output from the SSH key exchange. This forms two 256 bit +keys (K_1 and K_2), used by two separate instances of chacha20. +The first 256 bits constitute K_2 and the second 256 bits become +K_1. + +The instance keyed by K_1 is a stream cipher that is used only +to encrypt the 4 byte packet length field. The second instance, +keyed by K_2, is used in conjunction with poly1305 to build an AEAD +(Authenticated Encryption with Associated Data) that is used to encrypt +and authenticate the entire packet. + +Two separate cipher instances are used here so as to keep the packet +lengths confidential but not create an oracle for the packet payload +cipher by decrypting and using the packet length prior to checking +the MAC. By using an independently-keyed cipher instance to encrypt the +length, an active attacker seeking to exploit the packet input handling +as a decryption oracle can learn nothing about the payload contents or +its MAC (assuming key derivation, ChaCha20 and Poly1305 are secure). + +The AEAD is constructed as follows: for each packet, generate a Poly1305 +key by taking the first 256 bits of ChaCha20 stream output generated +using K_2, an IV consisting of the packet sequence number encoded as an +uint64 under the SSH wire encoding rules and a ChaCha20 block counter of +zero. The K_2 ChaCha20 block counter is then set to the little-endian +encoding of 1 (i.e. {1, 0, 0, 0, 0, 0, 0, 0}) and this instance is used +for encryption of the packet payload. + +Packet Handling +--------------- + +When receiving a packet, the length must be decrypted first. When 4 +bytes of ciphertext length have been received, they may be decrypted +using the K_1 key, a nonce consisting of the packet sequence number +encoded as a uint64 under the usual SSH wire encoding and a zero block +counter to obtain the plaintext length. + +Once the entire packet has been received, the MAC MUST be checked +before decryption. A per-packet Poly1305 key is generated as described +above and the MAC tag calculated using Poly1305 with this key over the +ciphertext of the packet length and the payload together. The calculated +MAC is then compared in constant time with the one appended to the +packet and the packet decrypted using ChaCha20 as described above (with +K_2, the packet sequence number as nonce and a starting block counter of +1). + +To send a packet, first encode the 4 byte length and encrypt it using +K_1. Encrypt the packet payload (using K_2) and append it to the +encrypted length. Finally, calculate a MAC tag and append it. + +Rekeying +-------- + +ChaCha20 must never reuse a {key, nonce} for encryption nor may it be +used to encrypt more than 2^70 bytes under the same {key, nonce}. The +SSH Transport protocol (RFC4253) recommends a far more conservative +rekeying every 1GB of data sent or received. If this recommendation +is followed, then chacha20-poly1305@openssh.com requires no special +handling in this area. + +References +---------- + +[1] "ChaCha, a variant of Salsa20", Daniel Bernstein + http://cr.yp.to/chacha/chacha-20080128.pdf + +[2] "The Poly1305-AES message-authentication code", Daniel Bernstein + http://cr.yp.to/mac/poly1305-20050329.pdf + +[3] "ChaCha20 and Poly1305 based Cipher Suites for TLS", Adam Langley + http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03 + +$OpenBSD: PROTOCOL.chacha20poly1305,v 1.5 2020/02/21 00:04:43 dtucker Exp $ + diff --git a/aosp/external/openssh/PROTOCOL.key b/aosp/external/openssh/PROTOCOL.key new file mode 100644 index 0000000000000000000000000000000000000000..38df268b6536f1d0e245d07f704953abf6242d90 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.key @@ -0,0 +1,71 @@ +This document describes the private key format for OpenSSH. + +1. Overall format + +The key consists of a header, a list of public keys, and +an encrypted list of matching private keys. + +#define AUTH_MAGIC "openssh-key-v1" + + byte[] AUTH_MAGIC + string ciphername + string kdfname + string kdfoptions + int number of keys N + string publickey1 + string publickey2 + ... + string publickeyN + string encrypted, padded list of private keys + +2. KDF options for kdfname "bcrypt" + +The options: + + string salt + uint32 rounds + +are concatenated and represented as a string. + +3. Unencrypted list of N private keys + +The list of privatekey/comment pairs is padded with the +bytes 1, 2, 3, ... until the total length is a multiple +of the cipher block size. + + uint32 checkint + uint32 checkint + byte[] privatekey1 + string comment1 + byte[] privatekey2 + string comment2 + ... + string privatekeyN + string commentN + char 1 + char 2 + char 3 + ... + char padlen % 255 + +where each private key is encoded using the same rules as used for +SSH agent. + +Before the key is encrypted, a random integer is assigned +to both checkint fields so successful decryption can be +quickly checked by verifying that both checkint fields +hold the same value. + +4. Encryption + +The KDF is used to derive a key, IV (and other values required by +the cipher) from the passphrase. These values are then used to +encrypt the unencrypted list of private keys. + +5. No encryption + +For unencrypted keys the cipher "none" and the KDF "none" +are used with empty passphrases. The options if the KDF "none" +are the empty string. + +$OpenBSD: PROTOCOL.key,v 1.2 2021/05/07 02:29:40 djm Exp $ diff --git a/aosp/external/openssh/PROTOCOL.krl b/aosp/external/openssh/PROTOCOL.krl new file mode 100644 index 0000000000000000000000000000000000000000..115f80e5d541f8e698f831d7033592d9a4b8dc75 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.krl @@ -0,0 +1,171 @@ +This describes the key/certificate revocation list format for OpenSSH. + +1. Overall format + +The KRL consists of a header and zero or more sections. The header is: + +#define KRL_MAGIC 0x5353484b524c0a00ULL /* "SSHKRL\n\0" */ +#define KRL_FORMAT_VERSION 1 + + uint64 KRL_MAGIC + uint32 KRL_FORMAT_VERSION + uint64 krl_version + uint64 generated_date + uint64 flags + string reserved + string comment + +Where "krl_version" is a version number that increases each time the KRL +is modified, "generated_date" is the time in seconds since 1970-01-01 +00:00:00 UTC that the KRL was generated, "comment" is an optional comment +and "reserved" an extension field whose contents are currently ignored. +No "flags" are currently defined. + +Following the header are zero or more sections, each consisting of: + + byte section_type + string section_data + +Where "section_type" indicates the type of the "section_data". An exception +to this is the KRL_SECTION_SIGNATURE section, that has a slightly different +format (see below). + +The available section types are: + +#define KRL_SECTION_CERTIFICATES 1 +#define KRL_SECTION_EXPLICIT_KEY 2 +#define KRL_SECTION_FINGERPRINT_SHA1 3 +#define KRL_SECTION_SIGNATURE 4 +#define KRL_SECTION_FINGERPRINT_SHA256 5 + +2. Certificate section + +These sections use type KRL_SECTION_CERTIFICATES to revoke certificates by +serial number or key ID. The consist of the CA key that issued the +certificates to be revoked and a reserved field whose contents is currently +ignored. + + string ca_key + string reserved + +Where "ca_key" is the standard SSH wire serialisation of the CA's +public key. Alternately, "ca_key" may be an empty string to indicate +the certificate section applies to all CAs (this is most useful when +revoking key IDs). + +Followed by one or more sections: + + byte cert_section_type + string cert_section_data + +The certificate section types are: + +#define KRL_SECTION_CERT_SERIAL_LIST 0x20 +#define KRL_SECTION_CERT_SERIAL_RANGE 0x21 +#define KRL_SECTION_CERT_SERIAL_BITMAP 0x22 +#define KRL_SECTION_CERT_KEY_ID 0x23 + +2.1 Certificate serial list section + +This section is identified as KRL_SECTION_CERT_SERIAL_LIST. It revokes +certificates by listing their serial numbers. The cert_section_data in this +case contains: + + uint64 revoked_cert_serial + uint64 ... + +This section may appear multiple times. + +2.2. Certificate serial range section + +These sections use type KRL_SECTION_CERT_SERIAL_RANGE and hold +a range of serial numbers of certificates: + + uint64 serial_min + uint64 serial_max + +All certificates in the range serial_min <= serial <= serial_max are +revoked. + +This section may appear multiple times. + +2.3. Certificate serial bitmap section + +Bitmap sections use type KRL_SECTION_CERT_SERIAL_BITMAP and revoke keys +by listing their serial number in a bitmap. + + uint64 serial_offset + mpint revoked_keys_bitmap + +A bit set at index N in the bitmap corresponds to revocation of a keys with +serial number (serial_offset + N). + +This section may appear multiple times. + +2.4. Revoked key ID sections + +KRL_SECTION_CERT_KEY_ID sections revoke particular certificate "key +ID" strings. This may be useful in revoking all certificates +associated with a particular identity, e.g. a host or a user. + + string key_id[0] + ... + +This section must contain at least one "key_id". This section may appear +multiple times. + +3. Explicit key sections + +These sections, identified as KRL_SECTION_EXPLICIT_KEY, revoke keys +(not certificates). They are less space efficient than serial numbers, +but are able to revoke plain keys. + + string public_key_blob[0] + .... + +This section must contain at least one "public_key_blob". The blob +must be a raw key (i.e. not a certificate). + +This section may appear multiple times. + +4. SHA1/SHA256 fingerprint sections + +These sections, identified as KRL_SECTION_FINGERPRINT_SHA1 and +KRL_SECTION_FINGERPRINT_SHA256, revoke plain keys (i.e. not +certificates) by listing their hashes: + + string public_key_hash[0] + .... + +This section must contain at least one "public_key_hash". The hash blob +is obtained by taking the SHA1 or SHA256 hash of the public key blob. +Hashes in this section must appear in numeric order, treating each hash +as a big-endian integer. + +This section may appear multiple times. + +5. KRL signature sections + +The KRL_SECTION_SIGNATURE section serves a different purpose to the +preceding ones: to provide cryptographic authentication of a KRL that +is retrieved over a channel that does not provide integrity protection. +Its format is slightly different to the previously-described sections: +in order to simplify the signature generation, it includes as a "body" +two string components instead of one. + + byte KRL_SECTION_SIGNATURE + string signature_key + string signature + +The signature is calculated over the entire KRL from the KRL_MAGIC +to this subsection's "signature_key", including both and using the +signature generation rules appropriate for the type of "signature_key". + +This section must appear last in the KRL. If multiple signature sections +appear, they must appear consecutively at the end of the KRL file. + +Implementations that retrieve KRLs over untrusted channels must verify +signatures. Signature sections are optional for KRLs distributed by +trusted means. + +$OpenBSD: PROTOCOL.krl,v 1.5 2018/09/12 01:21:34 djm Exp $ diff --git a/aosp/external/openssh/PROTOCOL.mux b/aosp/external/openssh/PROTOCOL.mux new file mode 100644 index 0000000000000000000000000000000000000000..5a3dd5fe04d743cc5335950b3797bab5537577ba --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.mux @@ -0,0 +1,298 @@ +This document describes the multiplexing protocol used by ssh(1)'s +ControlMaster connection-sharing. + +Multiplexing starts with a ssh(1) configured to act as a multiplexing +master. This will cause ssh(1) to listen on a Unix domain socket for +requests from clients. Clients communicate over this socket using a +simple packetised protocol, where each message is proceeded with +a length and message type in SSH uint32 wire format: + + uint32 packet length + uint32 packet type + ... packet body + +Most messages from the client to the server contain a "request id" +field. This field is returned in replies as "client request id" to +facilitate matching of responses to requests. + +Many multiplexing (mux) client requests yield immediate responses from +the mux process; requesting a forwarding, performing an alive check or +requesting the master terminate itself fall in to this category. + +The most common use of multiplexing however is to maintain multiple +concurrent sessions. These are supported via two separate modes: + +"Passenger" clients start by requesting a new session with a +MUX_C_NEW_SESSION message and passing stdio file descriptors over the +Unix domain control socket. The passenger client then waits until it is +signaled or the mux server closes the session. This mode is so named as +the client waits around while the mux server does all the driving. + +Stdio forwarding (requested using MUX_C_NEW_STDIO_FWD) is another +example of passenger mode; the client passes the stdio file descriptors +and passively waits for something to happen. + +"Proxy" clients, requested using MUX_C_PROXY, work quite differently. In +this mode, the mux client/server connection socket will stop speaking +the multiplexing protocol and start proxying SSH connection protocol +messages between the client and server. The client therefore must +speak a significant subset of the SSH protocol, but in return is able +to access basically the full suite of connection protocol features. +Moreover, as no file descriptor passing is required, the connection +supporting a proxy client may itself be forwarded or relayed to another +host if necessary. + +1. Connection setup + +When a multiplexing connection is made to a ssh(1) operating as a +ControlMaster from a client ssh(1), the first action of each is send +a hello messages to its peer: + + uint32 MUX_MSG_HELLO + uint32 protocol version + string extension name [optional] + string extension value [optional] + ... + +The current version of the mux protocol is 4. A client should refuse +to connect to a master that speaks an unsupported protocol version. + +Following the version identifier are zero or more extensions represented +as a name/value pair. No extensions are currently defined. + +2. Opening a passenger mode session + +To open a new multiplexed session in passenger mode, a client sends the +following request: + + uint32 MUX_C_NEW_SESSION + uint32 request id + string reserved + bool want tty flag + bool want X11 forwarding flag + bool want agent flag + bool subsystem flag + uint32 escape char + string terminal type + string command + string environment string 0 [optional] + ... + +To disable the use of an escape character, "escape char" may be set +to 0xffffffff. "terminal type" is generally set to the value of +$TERM. zero or more environment strings may follow the command. + +The client then sends its standard input, output and error file +descriptors (in that order) using Unix domain socket control messages. + +The contents of "reserved" are currently ignored. + +If successful, the server will reply with MUX_S_SESSION_OPENED + + uint32 MUX_S_SESSION_OPENED + uint32 client request id + uint32 session id + +Otherwise it will reply with an error: MUX_S_PERMISSION_DENIED or +MUX_S_FAILURE. + +Once the server has received the fds, it will respond with MUX_S_OK +indicating that the session is up. The client now waits for the +session to end. When it does, the server will send an exit status +message: + + uint32 MUX_S_EXIT_MESSAGE + uint32 session id + uint32 exit value + +The client should exit with this value to mimic the behaviour of a +non-multiplexed ssh(1) connection. Two additional cases that the +client must cope with are it receiving a signal itself and the +server disconnecting without sending an exit message. + +A master may also send a MUX_S_TTY_ALLOC_FAIL before MUX_S_EXIT_MESSAGE +if remote TTY allocation was unsuccessful. The client may use this to +return its local tty to "cooked" mode. + + uint32 MUX_S_TTY_ALLOC_FAIL + uint32 session id + +3. Requesting passenger-mode stdio forwarding + +A client may request the master to establish a stdio forwarding: + + uint32 MUX_C_NEW_STDIO_FWD + uint32 request id + string reserved + string connect host + string connect port + +The client then sends its standard input and output file descriptors +(in that order) using Unix domain socket control messages. + +The contents of "reserved" are currently ignored. + +A server may reply with a MUX_S_SESSION_OPENED, a MUX_S_PERMISSION_DENIED +or a MUX_S_FAILURE. + +4. Health checks + +The client may request a health check/PID report from a server: + + uint32 MUX_C_ALIVE_CHECK + uint32 request id + +The server replies with: + + uint32 MUX_S_ALIVE + uint32 client request id + uint32 server pid + +5. Remotely terminating a master + +A client may request that a master terminate immediately: + + uint32 MUX_C_TERMINATE + uint32 request id + +The server will reply with one of MUX_S_OK or MUX_S_PERMISSION_DENIED. + +6. Requesting establishment of port forwards + +A client may request the master to establish a port forward: + + uint32 MUX_C_OPEN_FWD + uint32 request id + uint32 forwarding type + string listen host + uint32 listen port + string connect host + uint32 connect port + +forwarding type may be MUX_FWD_LOCAL, MUX_FWD_REMOTE, MUX_FWD_DYNAMIC. + +If listen port is (unsigned int) -2, then the listen host is treated as +a unix socket path name. + +If connect port is (unsigned int) -2, then the connect host is treated +as a unix socket path name. + +A server may reply with a MUX_S_OK, a MUX_S_REMOTE_PORT, a +MUX_S_PERMISSION_DENIED or a MUX_S_FAILURE. + +For dynamically allocated listen port the server replies with + + uint32 MUX_S_REMOTE_PORT + uint32 client request id + uint32 allocated remote listen port + +7. Requesting closure of port forwards + +Note: currently unimplemented (server will always reply with MUX_S_FAILURE). + +A client may request the master to close a port forward: + + uint32 MUX_C_CLOSE_FWD + uint32 request id + uint32 forwarding type + string listen host + uint32 listen port + string connect host + uint32 connect port + +A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a +MUX_S_FAILURE. + +8. Requesting shutdown of mux listener + +A client may request the master to stop accepting new multiplexing requests +and remove its listener socket. + + uint32 MUX_C_STOP_LISTENING + uint32 request id + +A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a +MUX_S_FAILURE. + +9. Requesting proxy mode + +A client may request that the control connection be placed in proxy +mode: + + uint32 MUX_C_PROXY + uint32 request id + +When a mux master receives this message, it will reply with a +confirmation: + + uint32 MUX_S_PROXY + uint32 request id + +And go into proxy mode. All subsequent data over the connection will +be formatted as unencrypted, unpadded, SSH transport messages: + + uint32 packet length + byte 0 (padding length) + byte packet type + byte[packet length - 2] ... + +The mux master will accept most connection messages and global requests, +and will translate channel identifiers to ensure that the proxy client has +globally unique channel numbers (i.e. a proxy client need not worry about +collisions with other clients). + +10. Status messages + +The MUX_S_OK message is empty: + + uint32 MUX_S_OK + uint32 client request id + +The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason: + + uint32 MUX_S_PERMISSION_DENIED + uint32 client request id + string reason + + uint32 MUX_S_FAILURE + uint32 client request id + string reason + +11. Protocol numbers + +#define MUX_MSG_HELLO 0x00000001 +#define MUX_C_NEW_SESSION 0x10000002 +#define MUX_C_ALIVE_CHECK 0x10000004 +#define MUX_C_TERMINATE 0x10000005 +#define MUX_C_OPEN_FWD 0x10000006 +#define MUX_C_CLOSE_FWD 0x10000007 +#define MUX_C_NEW_STDIO_FWD 0x10000008 +#define MUX_C_STOP_LISTENING 0x10000009 +#define MUX_S_OK 0x80000001 +#define MUX_S_PERMISSION_DENIED 0x80000002 +#define MUX_S_FAILURE 0x80000003 +#define MUX_S_EXIT_MESSAGE 0x80000004 +#define MUX_S_ALIVE 0x80000005 +#define MUX_S_SESSION_OPENED 0x80000006 +#define MUX_S_REMOTE_PORT 0x80000007 +#define MUX_S_TTY_ALLOC_FAIL 0x80000008 + +#define MUX_FWD_LOCAL 1 +#define MUX_FWD_REMOTE 2 +#define MUX_FWD_DYNAMIC 3 + +XXX TODO +XXX extended status (e.g. report open channels / forwards) +XXX lock (maybe) +XXX watch in/out traffic (pre/post crypto) +XXX inject packet (what about replies) +XXX server->client error/warning notifications +XXX send signals via mux +XXX ^Z support in passengers +XXX extensions for multi-agent +XXX extensions for multi-X11 +XXX session inspection via master +XXX signals via mux request +XXX list active connections via mux + +$OpenBSD: PROTOCOL.mux,v 1.13 2022/01/01 01:55:30 jsg Exp $ diff --git a/aosp/external/openssh/PROTOCOL.sshsig b/aosp/external/openssh/PROTOCOL.sshsig new file mode 100644 index 0000000000000000000000000000000000000000..78457ddfc653519c056e36c79525712dafba4e6e --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.sshsig @@ -0,0 +1,100 @@ +This document describes a lightweight SSH Signature format +that is compatible with SSH keys and wire formats. + +At present, only detached and armored signatures are supported. + +1. Armored format + +The Armored SSH signatures consist of a header, a base64 +encoded blob, and a footer. + +The header is the string "-----BEGIN SSH SIGNATURE-----" +followed by a newline. The footer is the string +"-----END SSH SIGNATURE-----" immediately after a newline. + +The header MUST be present at the start of every signature. +Files containing the signature MUST start with the header. +Likewise, the footer MUST be present at the end of every +signature. + +The base64 encoded blob SHOULD be broken up by newlines +every 76 characters. + +Example: + +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgJKxoLBJBivUPNTUJUSslQTt2hD +jozKvHarKeN8uYFqgAAAADZm9vAAAAAAAAAFMAAAALc3NoLWVkMjU1MTkAAABAKNC4IEbt +Tq0Fb56xhtuE1/lK9H9RZJfON4o6hE9R4ZGFX98gy0+fFJ/1d2/RxnZky0Y7GojwrZkrHT +FgCqVWAQ== +-----END SSH SIGNATURE----- + +2. Blob format + +#define MAGIC_PREAMBLE "SSHSIG" +#define SIG_VERSION 0x01 + + byte[6] MAGIC_PREAMBLE + uint32 SIG_VERSION + string publickey + string namespace + string reserved + string hash_algorithm + string signature + +The publickey field MUST contain the serialisation of the +public key used to make the signature using the usual SSH +encoding rules, i.e RFC4253, RFC5656, +draft-ietf-curdle-ssh-ed25519-ed448, etc. + +Verifiers MUST reject signatures with versions greater than those +they support. + +The purpose of the namespace value is to specify a unambiguous +interpretation domain for the signature, e.g. file signing. +This prevents cross-protocol attacks caused by signatures +intended for one intended domain being accepted in another. +The namespace value MUST NOT be the empty string. + +The reserved value is present to encode future information +(e.g. tags) into the signature. Implementations should ignore +the reserved field if it is not empty. + +Data to be signed is first hashed with the specified hash_algorithm. +This is done to limit the amount of data presented to the signature +operation, which may be of concern if the signing key is held in limited +or slow hardware or on a remote ssh-agent. The supported hash algorithms +are "sha256" and "sha512". + +The signature itself is made using the SSH signature algorithm and +encoding rules for the chosen key type. For RSA signatures, the +signature algorithm must be "rsa-sha2-512" or "rsa-sha2-256" (i.e. +not the legacy RSA-SHA1 "ssh-rsa"). + +This blob is encoded as a string using the RFC4253 encoding +rules and base64 encoded to form the middle part of the +armored signature. + + +3. Signed Data, of which the signature goes into the blob above + +#define MAGIC_PREAMBLE "SSHSIG" + + byte[6] MAGIC_PREAMBLE + string namespace + string reserved + string hash_algorithm + string H(message) + +The preamble is the six-byte sequence "SSHSIG". It is included to +ensure that manual signatures can never be confused with any message +signed during SSH user or host authentication. + +The reserved value is present to encode future information +(e.g. tags) into the signature. Implementations should ignore +the reserved field if it is not empty. + +The data is concatenated and passed to the SSH signing +function. + +$OpenBSD: PROTOCOL.sshsig,v 1.4 2020/08/31 00:17:41 djm Exp $ diff --git a/aosp/external/openssh/PROTOCOL.u2f b/aosp/external/openssh/PROTOCOL.u2f new file mode 100644 index 0000000000000000000000000000000000000000..f8ca56b11c8cfc76adcedf813205e7d00171f913 --- /dev/null +++ b/aosp/external/openssh/PROTOCOL.u2f @@ -0,0 +1,309 @@ +This document describes OpenSSH's support for U2F/FIDO security keys. + +Background +---------- + +U2F is an open standard for two-factor authentication hardware, widely +used for user authentication to websites. U2F tokens are ubiquitous, +available from a number of manufacturers and are currently by far the +cheapest way for users to achieve hardware-backed credential storage. + +The U2F protocol however cannot be trivially used as an SSH protocol key +type as both the inputs to the signature operation and the resultant +signature differ from those specified for SSH. For similar reasons, +integration of U2F devices cannot be achieved via the PKCS#11 API. + +U2F also offers a number of features that are attractive in the context +of SSH authentication. They can be configured to require indication +of "user presence" for each signature operation (typically achieved +by requiring the user touch the key). They also offer an attestation +mechanism at key enrollment time that can be used to prove that a +given key is backed by hardware. Finally the signature format includes +a monotonic signature counter that can be used (at scale) to detect +concurrent use of a private key, should it be extracted from hardware. + +U2F private keys are generated through an enrollment operation, +which takes an application ID - a URL-like string, typically "ssh:" +in this case, but a HTTP origin for the case of web authentication, +and a challenge string (typically randomly generated). The enrollment +operation returns a public key, a key handle that must be used to invoke +the hardware-backed private key, some flags and signed attestation +information that may be used to verify that a private key is hosted on a +particular hardware instance. + +It is common for U2F hardware to derive private keys from the key handle +in conjunction with a small per-device secret that is unique to the +hardware, thus requiring little on-device storage for an effectively +unlimited number of supported keys. This drives the requirement that +the key handle be supplied for each signature operation. U2F tokens +primarily use ECDSA signatures in the NIST-P256 field, though the FIDO2 +standard specifies additional key types, including one based on Ed25519. + +Use of U2F security keys does not automatically imply multi-factor +authentication. From sshd's perspective, a security key constitutes a +single factor of authentication, even if protected by a PIN or biometric +authentication. To enable multi-factor authentication in ssh, please +refer to the AuthenticationMethods option in sshd_config(5). + + +SSH U2F Key formats +------------------- + +OpenSSH integrates U2F as new key and corresponding certificate types: + + sk-ecdsa-sha2-nistp256@openssh.com + sk-ecdsa-sha2-nistp256-cert-v01@openssh.com + sk-ssh-ed25519@openssh.com + sk-ssh-ed25519-cert-v01@openssh.com + +While each uses ecdsa-sha256-nistp256 as the underlying signature primitive, +keys require extra information in the public and private keys, and in +the signature object itself. As such they cannot be made compatible with +the existing ecdsa-sha2-nistp* key types. + +The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + string curve name + ec_point Q + string application (user-specified, but typically "ssh:") + +The corresponding private key contains: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + string curve name + ec_point Q + string application (user-specified, but typically "ssh:") + uint8 flags + string key_handle + string reserved + +The format of a sk-ssh-ed25519@openssh.com public key is: + + string "sk-ssh-ed25519@openssh.com" + string public key + string application (user-specified, but typically "ssh:") + +With a private half consisting of: + + string "sk-ssh-ed25519@openssh.com" + string public key + string application (user-specified, but typically "ssh:") + uint8 flags + string key_handle + string reserved + +The certificate form for SSH U2F keys appends the usual certificate +information to the public key: + + string "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" + string nonce + string curve name + ec_point Q + string application + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +and for security key ed25519 certificates: + + string "sk-ssh-ed25519-cert-v01@openssh.com" + string nonce + string public key + string application + uint64 serial + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string critical options + string extensions + string reserved + string signature key + string signature + +Both security key certificates use the following encoding for private keys: + + string type (e.g. "sk-ssh-ed25519-cert-v01@openssh.com") + string pubkey (the above key/cert structure) + string application + uint8 flags + string key_handle + string reserved + +During key generation, the hardware also returns attestation information +that may be used to cryptographically prove that a given key is +hardware-backed. Unfortunately, the protocol required for this proof is +not privacy-preserving and may be used to identify U2F tokens with at +least manufacturer and batch number granularity. For this reason, we +choose not to include this information in the public key or save it by +default. + +Attestation information is useful for out-of-band key and certificate +registration workflows, e.g. proving to a CA that a key is backed +by trusted hardware before it will issue a certificate. To support this +case, OpenSSH optionally allows retaining the attestation information +at the time of key generation. It will take the following format: + + string "ssh-sk-attest-v01" + string attestation certificate + string enrollment signature + string authenticator data (CBOR encoded) + uint32 reserved flags + string reserved string + +A previous version of this format, emitted prior to OpenSSH 8.4 omitted +the authenticator data. + + string "ssh-sk-attest-v00" + string attestation certificate + string enrollment signature + uint32 reserved flags + string reserved string + +OpenSSH treats the attestation certificate and enrollment signatures as +opaque objects and does no interpretation of them itself. + +SSH U2F signatures +------------------ + +In addition to the message to be signed, the U2F signature operation +requires the key handle and a few additional parameters. The signature +is signed over a blob that consists of: + + byte[32] SHA256(application) + byte flags (including "user present", extensions present) + uint32 counter + byte[] extensions + byte[32] SHA256(message) + +No extensions are yet defined for SSH use. If any are defined in the future, +it will be possible to infer their presence from the contents of the "flags" +value. + +The signature returned from U2F hardware takes the following format: + + byte flags (including "user present") + uint32 counter + byte[] ecdsa_signature (in X9.62 format). + +For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1 +format data in the pre-authentication attack surface. Therefore, the +signature format used on the wire in SSH2_USERAUTH_REQUEST packets will +be reformatted to better match the existing signature encoding: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + string ecdsa_signature + byte flags + uint32 counter + +Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature +encoding: + + mpint r + mpint s + +For Ed25519 keys the signature is encoded as: + + string "sk-ssh-ed25519@openssh.com" + string signature + byte flags + uint32 counter + +webauthn signatures +------------------- + +The W3C/FIDO webauthn[1] standard defines a mechanism for a web browser to +interact with FIDO authentication tokens. This standard builds upon the +FIDO standards, but requires different signature contents to raw FIDO +messages. OpenSSH supports ECDSA/p256 webauthn signatures through the +"webauthn-sk-ecdsa-sha2-nistp256@openssh.com" signature algorithm. + +The wire encoding for a webauthn-sk-ecdsa-sha2-nistp256@openssh.com +signature is similar to the sk-ecdsa-sha2-nistp256@openssh.com format: + + string "webauthn-sk-ecdsa-sha2-nistp256@openssh.com" + string ecdsa_signature + byte flags + uint32 counter + string origin + string clientData + string extensions + +Where "origin" is the HTTP origin making the signature, "clientData" is +the JSON-like structure signed by the browser and "extensions" are any +extensions used in making the signature. + +[1] https://www.w3.org/TR/webauthn-2/ + +ssh-agent protocol extensions +----------------------------- + +ssh-agent requires a protocol extension to support U2F keys. At +present the closest analogue to Security Keys in ssh-agent are PKCS#11 +tokens, insofar as they require a middleware library to communicate with +the device that holds the keys. Unfortunately, the protocol message used +to add PKCS#11 keys to ssh-agent does not include any way to send the +key handle to the agent as U2F keys require. + +To avoid this, without having to add wholly new messages to the agent +protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message +with a new key constraint extension to encode a path to the middleware +library for the key. The format of this constraint extension would be: + + byte SSH_AGENT_CONSTRAIN_EXTENSION + string sk-provider@openssh.com + string middleware path + +This constraint-based approach does not present any compatibility +problems. + +OpenSSH integration +------------------- + +U2F tokens may be attached via a number of means, including USB and NFC. +The USB interface is standardised around a HID protocol, but we want to +be able to support other transports as well as dummy implementations for +regress testing. For this reason, OpenSSH shall support a dynamically- +loaded middleware libraries to communicate with security keys, but offer +support for the common case of USB HID security keys internally. + +The middleware library need only expose a handful of functions and +numbers listed in sk-api.h. Included in the defined numbers is a +SSH_SK_VERSION_MAJOR that should be incremented for each incompatible +API change. + +miscellaneous options may be passed to the middleware as a NULL- +terminated array of pointers to struct sk_option. The middleware may +ignore unsupported or unknown options unless the "required" flag is set, +in which case it should return failure if an unsupported option is +requested. + +At present the following options names are supported: + + "device" + + Specifies a specific FIDO device on which to perform the + operation. The value in this field is interpreted by the + middleware but it would be typical to specify a path to + a /dev node for the device in question. + + "user" + + Specifies the FIDO2 username used when enrolling a key, + overriding OpenSSH's default of using an all-zero username. + +In OpenSSH, the middleware will be invoked by using a similar mechanism to +ssh-pkcs11-helper to provide address-space containment of the +middleware from ssh-agent. + +$OpenBSD: PROTOCOL.u2f,v 1.26 2020/09/09 03:08:01 djm Exp $ diff --git a/aosp/external/openssh/README b/aosp/external/openssh/README new file mode 100644 index 0000000000000000000000000000000000000000..70a8f814caa9c3d95bdc4afcf276260cbb831208 --- /dev/null +++ b/aosp/external/openssh/README @@ -0,0 +1,52 @@ +See https://www.openssh.com/releasenotes.html#9.0p1 for the release notes. + +Please read https://www.openssh.com/report.html for bug reporting +instructions and note that we do not use Github for bug reporting or +patch/pull-request management. + +This is the port of OpenBSD's excellent OpenSSH[0] to Linux and other +Unices. + +OpenSSH is based on the last free version of Tatu Ylonen's sample +implementation with all patent-encumbered algorithms removed (to +external libraries), all known security bugs fixed, new features +reintroduced and many other clean-ups. OpenSSH has been created by +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt, +and Dug Song. It has a homepage at https://www.openssh.com/ + +This port consists of the re-introduction of autoconf support, PAM +support, EGD/PRNGD support and replacements for OpenBSD library +functions that are (regrettably) absent from other unices. This port +has been best tested on AIX, Cygwin, HP-UX, Linux, MacOS/X, +FreeBSD, NetBSD, OpenBSD, OpenServer, Solaris and UnixWare. + +This version actively tracks changes in the OpenBSD CVS repository. + +The PAM support is now more functional than the popular packages of +commercial ssh-1.2.x. It checks "account" and "session" modules for +all logins, not just when using password authentication. + +There is now several mailing lists for this port of OpenSSH. Please +refer to https://www.openssh.com/list.html for details on how to join. + +Please send bug reports and patches to https://bugzilla.mindrot.org or +the mailing list openssh-unix-dev@mindrot.org. To mitigate spam, the +list only allows posting from subscribed addresses. Code contribution +are welcomed, but please follow the OpenBSD style guidelines[1]. + +Please refer to the INSTALL document for information on dependencies and +how to install OpenSSH on your system. + +Damien Miller + +Miscellania - + +This version of OpenSSH is based upon code retrieved from the OpenBSD CVS +repository which in turn was based on the last free sample implementation +released by Tatu Ylonen. + +References - + +[0] https://www.openssh.com/ +[1] https://man.openbsd.org/style.9 + diff --git a/aosp/external/openssh/README.dns b/aosp/external/openssh/README.dns new file mode 100644 index 0000000000000000000000000000000000000000..29ecaee8da39fb351c592e7d83501a6c1b145eee --- /dev/null +++ b/aosp/external/openssh/README.dns @@ -0,0 +1,47 @@ +How to verify host keys using OpenSSH and DNS +--------------------------------------------- + +OpenSSH contains support for verifying host keys using DNS as described +in https://tools.ietf.org/html/rfc4255. The document contains very brief +instructions on how to use this feature. Configuring DNS is out of the +scope of this document. + + +(1) Server: Generate and publish the DNS RR + +To create a DNS resource record (RR) containing a fingerprint of the +public host key, use the following command: + + ssh-keygen -r hostname -f keyfile -g + +where "hostname" is your fully qualified hostname and "keyfile" is the +file containing the public host key file. If you have multiple keys, +you should generate one RR for each key. + +In the example above, ssh-keygen will print the fingerprint in a +generic DNS RR format parsable by most modern name server +implementations. If your nameserver has support for the SSHFP RR +you can omit the -g flag and ssh-keygen will print a standard SSHFP RR. + +To publish the fingerprint using the DNS you must add the generated RR +to your DNS zone file and sign your zone. + + +(2) Client: Enable ssh to verify host keys using DNS + +To enable the ssh client to verify host keys using DNS, you have to +add the following option to the ssh configuration file +($HOME/.ssh/config or /etc/ssh/ssh_config): + + VerifyHostKeyDNS yes + +Upon connection the client will try to look up the fingerprint RR +using DNS. If the fingerprint received from the DNS server matches +the remote host key, the user will be notified. + + + Jakob Schlyter + Wesley Griffin + + +$OpenBSD: README.dns,v 1.2 2003/10/14 19:43:23 jakob Exp $ diff --git a/aosp/external/openssh/README.md b/aosp/external/openssh/README.md new file mode 100644 index 0000000000000000000000000000000000000000..de4717737eac6c6901b951029a75c17ba5245b31 --- /dev/null +++ b/aosp/external/openssh/README.md @@ -0,0 +1,84 @@ +# Portable OpenSSH + +[![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh) + +OpenSSH is a complete implementation of the SSH protocol (version 2) for secure remote login, command execution and file transfer. It includes a client ``ssh`` and server ``sshd``, file transfer utilities ``scp`` and ``sftp`` as well as tools for key generation (``ssh-keygen``), run-time key storage (``ssh-agent``) and a number of supporting programs. + +This is a port of OpenBSD's [OpenSSH](https://openssh.com) to most Unix-like operating systems, including Linux, OS X and Cygwin. Portable OpenSSH polyfills OpenBSD APIs that are not available elsewhere, adds sshd sandboxing for more operating systems and includes support for OS-native authentication and auditing (e.g. using PAM). + +## Documentation + +The official documentation for OpenSSH are the man pages for each tool: + +* [ssh(1)](https://man.openbsd.org/ssh.1) +* [sshd(8)](https://man.openbsd.org/sshd.8) +* [ssh-keygen(1)](https://man.openbsd.org/ssh-keygen.1) +* [ssh-agent(1)](https://man.openbsd.org/ssh-agent.1) +* [scp(1)](https://man.openbsd.org/scp.1) +* [sftp(1)](https://man.openbsd.org/sftp.1) +* [ssh-keyscan(8)](https://man.openbsd.org/ssh-keyscan.8) +* [sftp-server(8)](https://man.openbsd.org/sftp-server.8) + +## Stable Releases + +Stable release tarballs are available from a number of [download mirrors](https://www.openssh.com/portable.html#downloads). We recommend the use of a stable release for most users. Please read the [release notes](https://www.openssh.com/releasenotes.html) for details of recent changes and potential incompatibilities. + +## Building Portable OpenSSH + +### Dependencies + +Portable OpenSSH is built using autoconf and make. It requires a working C compiler, standard library and headers. + +``libcrypto`` from either [LibreSSL](https://www.libressl.org/) or [OpenSSL](https://www.openssl.org) may also be used, but OpenSSH may be built without it supporting a subset of crypto algorithms. + +[zlib](https://www.zlib.net/) is optional; without it transport compression is not supported. + +FIDO security token support needs [libfido2](https://github.com/Yubico/libfido2) and its dependencies. Also, certain platforms and build-time options may require additional dependencies; see README.platform for details. + +### Building a release + +Releases include a pre-built copy of the ``configure`` script and may be built using: + +``` +tar zxvf openssh-X.YpZ.tar.gz +cd openssh +./configure # [options] +make && make tests +``` + +See the [Build-time Customisation](#build-time-customisation) section below for configure options. If you plan on installing OpenSSH to your system, then you will usually want to specify destination paths. + +### Building from git + +If building from git, you'll need [autoconf](https://www.gnu.org/software/autoconf/) installed to build the ``configure`` script. The following commands will check out and build portable OpenSSH from git: + +``` +git clone https://github.com/openssh/openssh-portable # or https://anongit.mindrot.org/openssh.git +cd openssh-portable +autoreconf +./configure +make && make tests +``` + +### Build-time Customisation + +There are many build-time customisation options available. All Autoconf destination path flags (e.g. ``--prefix``) are supported (and are usually required if you want to install OpenSSH). + +For a full list of available flags, run ``configure --help`` but a few of the more frequently-used ones are described below. Some of these flags will require additional libraries and/or headers be installed. + +Flag | Meaning +--- | --- +``--with-pam`` | Enable [PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) support. [OpenPAM](https://www.openpam.org/), [Linux PAM](http://www.linux-pam.org/) and Solaris PAM are supported. +``--with-libedit`` | Enable [libedit](https://www.thrysoee.dk/editline/) support for sftp. +``--with-kerberos5`` | Enable Kerberos/GSSAPI support. Both [Heimdal](https://www.h5l.org/) and [MIT](https://web.mit.edu/kerberos/) Kerberos implementations are supported. +``--with-selinux`` | Enable [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux) support. +``--with-security-key-builtin`` | Include built-in support for U2F/FIDO2 security keys. This requires [libfido2](https://github.com/Yubico/libfido2) be installed. + +## Development + +Portable OpenSSH development is discussed on the [openssh-unix-dev mailing list](https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev) ([archive mirror](https://marc.info/?l=openssh-unix-dev)). Bugs and feature requests are tracked on our [Bugzilla](https://bugzilla.mindrot.org/). + +## Reporting bugs + +_Non-security_ bugs may be reported to the developers via [Bugzilla](https://bugzilla.mindrot.org/) or via the mailing list above. Security bugs should be reported to [openssh@openssh.com](mailto:openssh.openssh.com). diff --git a/aosp/external/openssh/README.platform b/aosp/external/openssh/README.platform new file mode 100644 index 0000000000000000000000000000000000000000..7b754ba42a102479f0e316455ae5304d4e48ebe9 --- /dev/null +++ b/aosp/external/openssh/README.platform @@ -0,0 +1,96 @@ +This file contains notes about OpenSSH on specific platforms. + +AIX + +Beginning with OpenSSH 3.8p1, sshd will honour an account's password +expiry settings, where prior to that it did not. Because of this, +it's possible for sites that have used OpenSSH's sshd exclusively to +have accounts which have passwords expired longer than the inactive time +(ie the "Weeks between password EXPIRATION and LOCKOUT" setting in SMIT +or the maxexpired chuser attribute). + +Accounts in this state must have their passwords reset manually by the +administrator. As a precaution, it is recommended that the administrative +passwords be reset before upgrading from OpenSSH <3.8. + +As of OpenSSH 4.0p1, configure will attempt to detect if your version +and maintenance level of AIX has a working getaddrinfo, and will use it +if found. This will enable IPv6 support. If for some reason configure +gets it wrong, or if you want to build binaries to work on earlier MLs +than the build host then you can add "-DBROKEN_GETADDRINFO" to CFLAGS +to force the previous IPv4-only behaviour. + +IPv6 known to work: 5.1ML7 5.2ML2 5.2ML5 +IPv6 known broken: 4.3.3ML11 5.1ML4 + +If you wish to use dynamic libraries that aren't in the normal system +locations (eg IBM's OpenSSL and zlib packages) then you will need to +define the environment variable blibpath before running configure, eg + +blibpath=/lib:/usr/lib:/opt/freeware/lib ./configure \ + --with-ssl-dir=/opt/freeware --with-zlib=/opt/freeware + +If sshd is built with the WITH_AIXAUTHENTICATE option (which is enabled +by default) then sshd checks that users are permitted via the +loginrestrictions() function, in particular that the user has the +"rlogin" attribute set. This check is not done for the root account, +instead the PermitRootLogin setting in sshd_config is used. + +If you are using the IBM compiler you probably want to use CC=xlc rather +than the default of cc. + + +Cygwin +------ +To build on Cygwin, OpenSSH requires the following packages: +gcc, gcc-mingw-core, mingw-runtime, binutils, make, openssl, +openssl-devel, zlib, minres, minires-devel. + + +Darwin and MacOS X +------------------ +Darwin does not provide a tun(4) driver required for OpenSSH-based +virtual private networks. The BSD manpage still exists, but the driver +has been removed in recent releases of Darwin and MacOS X. + +Nevertheless, tunnel support is known to work with Darwin 8 and +MacOS X 10.4 in Point-to-Point (Layer 3) and Ethernet (Layer 2) mode +using a third party driver. More information is available at: + http://www-user.rhrk.uni-kl.de/~nissler/tuntap/ + + +Linux +----- + +Some Linux distributions (including Red Hat/Fedora/CentOS) include +headers and library links in the -devel RPMs rather than the main +binary RPMs. If you get an error about headers, or complaining about a +missing prerequisite then you may need to install the equivalent +development packages. On Redhat based distros these may be openssl-devel, +zlib-devel and pam-devel, on Debian based distros these may be +libssl-dev, libz-dev and libpam-dev. + + +Solaris +------- +If you enable BSM auditing on Solaris, you need to update audit_event(4) +for praudit(1m) to give sensible output. The following line needs to be +added to /etc/security/audit_event: + + 32800:AUE_openssh:OpenSSH login:lo + +The BSM audit event range available for third party TCB applications is +32768 - 65535. Event number 32800 has been chosen for AUE_openssh. +There is no official registry of 3rd party event numbers, so if this +number is already in use on your system, you may change it at build time +by configure'ing --with-cflags=-DAUE_openssh=32801 then rebuilding. + + +Platforms using PAM +------------------- +As of OpenSSH 4.3p1, sshd will no longer check /etc/nologin itself when +PAM is enabled. To maintain existing behaviour, pam_nologin should be +added to sshd's session stack which will prevent users from starting shell +sessions. Alternatively, pam_nologin can be added to either the auth or +account stacks which will prevent authentication entirely, but will still +return the output from pam_nologin to the client. diff --git a/aosp/external/openssh/README.privsep b/aosp/external/openssh/README.privsep new file mode 100644 index 0000000000000000000000000000000000000000..d658c46db11e65dcb711c80018813b6a5d5c693b --- /dev/null +++ b/aosp/external/openssh/README.privsep @@ -0,0 +1,51 @@ +Privilege separation, or privsep, is method in OpenSSH by which +operations that require root privilege are performed by a separate +privileged monitor process. Its purpose is to prevent privilege +escalation by containing corruption to an unprivileged process. +More information is available at: + http://www.citi.umich.edu/u/provos/ssh/privsep.html + +Privilege separation is now mandatory. During the pre-authentication +phase sshd will chroot(2) to "/var/empty" and change its privileges to the +"sshd" user and its primary group. sshd is a pseudo-account that should +not be used by other daemons, and must be locked and should contain a +"nologin" or invalid shell. + +You should do something like the following to prepare the privsep +preauth environment: + + # mkdir /var/empty + # chown root:sys /var/empty + # chmod 755 /var/empty + # groupadd sshd + # useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd + +/var/empty should not contain any files. + +configure supports the following options to change the default +privsep user and chroot directory: + + --with-privsep-path=xxx Path for privilege separation chroot + --with-privsep-user=user Specify non-privileged user for privilege separation + +PAM-enabled OpenSSH is known to function with privsep on AIX, FreeBSD, +HP-UX (including Trusted Mode), Linux, NetBSD and Solaris. + +On Cygwin, Tru64 Unix and OpenServer only the pre-authentication part +of privsep is supported. Post-authentication privsep is disabled +automatically (so you won't see the additional process mentioned below). + +Note that for a normal interactive login with a shell, enabling privsep +will require 1 additional process per login session. + +Given the following process listing (from HP-UX): + + UID PID PPID C STIME TTY TIME COMMAND + root 1005 1 0 10:45:17 ? 0:08 /opt/openssh/sbin/sshd -u0 + root 6917 1005 0 15:19:16 ? 0:00 sshd: stevesk [priv] + stevesk 6919 6917 0 15:19:17 ? 0:03 sshd: stevesk@2 + stevesk 6921 6919 0 15:19:17 pts/2 0:00 -bash + +process 1005 is the sshd process listening for new connections. +process 6917 is the privileged monitor process, 6919 is the user owned +sshd process and 6921 is the shell process. diff --git a/aosp/external/openssh/README.tun b/aosp/external/openssh/README.tun new file mode 100644 index 0000000000000000000000000000000000000000..5e1cb074c2eef57d0dbb58af9787bbb94eea126b --- /dev/null +++ b/aosp/external/openssh/README.tun @@ -0,0 +1,132 @@ +How to use OpenSSH-based virtual private networks +------------------------------------------------- + +OpenSSH contains support for VPN tunneling using the tun(4) network +tunnel pseudo-device which is available on most platforms, either for +layer 2 or 3 traffic. + +The following brief instructions on how to use this feature use +a network configuration specific to the OpenBSD operating system. + +(1) Server: Enable support for SSH tunneling + +To enable the ssh server to accept tunnel requests from the client, you +have to add the following option to the ssh server configuration file +(/etc/ssh/sshd_config): + + PermitTunnel yes + +Restart the server or send the hangup signal (SIGHUP) to let the server +reread it's configuration. + +(2) Server: Restrict client access and assign the tunnel + +The OpenSSH server simply uses the file /root/.ssh/authorized_keys to +restrict the client to connect to a specified tunnel and to +automatically start the related interface configuration command. These +settings are optional but recommended: + + tunnel="1",command="sh /etc/netstart tun1" ssh-rsa ... reyk@openbsd.org + +(3) Client: Configure the local network tunnel interface + +Use the hostname.if(5) interface-specific configuration file to set up +the network tunnel configuration with OpenBSD. For example, use the +following configuration in /etc/hostname.tun0 to set up the layer 3 +tunnel on the client: + + inet 192.168.5.1 255.255.255.252 192.168.5.2 + +OpenBSD also supports layer 2 tunneling over the tun device by adding +the link0 flag: + + inet 192.168.1.78 255.255.255.0 192.168.1.255 link0 + +Layer 2 tunnels can be used in combination with an Ethernet bridge(4) +interface, like the following example for /etc/bridgename.bridge0: + + add tun0 + add sis0 + up + +(4) Client: Configure the OpenSSH client + +To establish tunnel forwarding for connections to a specified +remote host by default, use the following ssh client configuration for +the privileged user (in /root/.ssh/config): + + Host sshgateway + Tunnel yes + TunnelDevice 0:any + PermitLocalCommand yes + LocalCommand sh /etc/netstart tun0 + +A more complicated configuration is possible to establish a tunnel to +a remote host which is not directly accessible by the client. +The following example describes a client configuration to connect to +the remote host over two ssh hops in between. It uses the OpenSSH +ProxyCommand in combination with the nc(1) program to forward the final +ssh tunnel destination over multiple ssh sessions. + + Host access.somewhere.net + User puffy + Host dmzgw + User puffy + ProxyCommand ssh access.somewhere.net nc dmzgw 22 + Host sshgateway + Tunnel Ethernet + TunnelDevice 0:any + PermitLocalCommand yes + LocalCommand sh /etc/netstart tun0 + ProxyCommand ssh dmzgw nc sshgateway 22 + +The following network plan illustrates the previous configuration in +combination with layer 2 tunneling and Ethernet bridging. + ++--------+ ( ) +----------------------+ +| Client |------( Internet )-----| access.somewhere.net | ++--------+ ( ) +----------------------+ + : 192.168.1.78 | + :............................. +-------+ + Forwarded ssh connection : | dmzgw | + Layer 2 tunnel : +-------+ + : | + : | + : +------------+ + :......| sshgateway | + | +------------+ +--- real connection Bridge -> | +----------+ +... "virtual connection" [ X ]--------| somehost | +[X] switch +----------+ + 192.168.1.25 + +(5) Client: Connect to the server and establish the tunnel + +Finally connect to the OpenSSH server to establish the tunnel by using +the following command: + + ssh sshgateway + +It is also possible to tell the client to fork into the background after +the connection has been successfully established: + + ssh -f sshgateway true + +Without the ssh configuration done in step (4), it is also possible +to use the following command lines: + + ssh -fw 0:1 sshgateway true + ifconfig tun0 192.168.5.1 192.168.5.2 netmask 255.255.255.252 + +Using OpenSSH tunnel forwarding is a simple way to establish secure +and ad hoc virtual private networks. Possible fields of application +could be wireless networks or administrative VPN tunnels. + +Nevertheless, ssh tunneling requires some packet header overhead and +runs on top of TCP. It is still suggested to use the IP Security +Protocol (IPSec) for robust and permanent VPN connections and to +interconnect corporate networks. + + Reyk Floeter + +$OpenBSD: README.tun,v 1.4 2006/03/28 00:12:31 deraadt Exp $ diff --git a/aosp/external/openssh/README.version b/aosp/external/openssh/README.version new file mode 100644 index 0000000000000000000000000000000000000000..9b3b5ba28cad108d4518a329fdddb60b0110ed97 --- /dev/null +++ b/aosp/external/openssh/README.version @@ -0,0 +1,4 @@ +URL: https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.0p1.tar.gz +Version: 9.0p1 +BugComponent: 180238 +Owners: adelva, cloud-android-devs diff --git a/aosp/external/openssh/SECURITY.md b/aosp/external/openssh/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..ba436c4f176d3beb891a2f2dc0a11d5071ccd129 --- /dev/null +++ b/aosp/external/openssh/SECURITY.md @@ -0,0 +1,5 @@ +# Reporting OpenSSH Security Issues + +To report security issues in OpenSSH, please refer to our website +[OpenSSH Security](https://www.openssh.com/security.html). + diff --git a/aosp/external/openssh/TODO b/aosp/external/openssh/TODO new file mode 100644 index 0000000000000000000000000000000000000000..b76529c960a07a504b1791bf51c11a580684f1ef --- /dev/null +++ b/aosp/external/openssh/TODO @@ -0,0 +1,80 @@ +Documentation: + +- Update the docs + - Update README + - Update INSTALL + - Merge INSTALL & README.privsep + +- Install FAQ? + +- General FAQ on S/Key, TIS, RSA, RSA2, DSA, etc and suggestions on when it + would be best to use them. + +- Create a Documentation/ directory? + +Programming: + +- Grep for 'XXX' comments and fix + +- Link order is incorrect for some systems using Kerberos 4 and AFS. Result + is multiple inclusion of DES symbols. Holger Trapp + reports that changing the configure + generated link order from: + -lresolv -lkrb -lz -lnsl -lutil -lkafs -lkrb -ldes -lcrypto + to: + -lresolv -lkrb -lz -lnsl -lutil -lcrypto -lkafs -lkrb -ldes + fixing the problem. + +- Write a test program that calls stat() to search for EGD/PRNGd socket + rather than use the (non-portable) "test -S". + +- More platforms for for setproctitle() emulation (testing needed) + +- Improve PAM ChallengeResponseAuthentication + - Informational messages + - Use different PAM service name for kbdint vs regular auth (suggest from + Solar Designer) + - Ability to select which ChallengeResponseAuthentications may be used + and order to try them in e.g. "ChallengeResponseAuthentication pam" + +- Complete Tru64 SIA support + - It looks like we could merge it into the password auth code to cut down + on diff size. Maybe PAM password auth too? + +- Finish integrating kernel-level auditing code for IRIX and SOLARIS + (Gilbert.r.loomis@saic.com) + +- 64-bit builds on HP-UX 11.X (stevesk@pobox.com): + - utmp/wtmp get corrupted (something in loginrec?) + - can't build with PAM (no 64-bit libpam yet) + +Clean up configure/makefiles: +- Clean up configure.ac - There are a few double #defined variables + left to do. HAVE_LOGIN is one of them. Consider NOT looking for + information in wtmpx or utmpx or any of that stuff if it's not detected + from the start + +- Replace the whole u_intXX_t evilness in acconfig.h with something better??? + - Do it in configure.ac + +- Consider splitting the u_intXX_t test for sys/bitype.h into separate test + to allow people to (right/wrongfully) link against Bind directly. + +- Consider splitting configure.ac into separate files which do logically + similar tests. E.g move all the type detection stuff into one file, + entropy related stuff into another. + +Packaging: +- HP-UX: Provide DEPOT package scripts. + (gilbert.r.loomis@saic.com) + +PrivSep Issues: +- PAM + + See above PAM notes +- AIX + + usrinfo() does not set TTY, but only required for legacy systems. Works + with PrivSep. +- OSF + + SIA is broken +- Cygwin + + Privsep for Pre-auth only (no fd passing) diff --git a/aosp/external/openssh/addr.c b/aosp/external/openssh/addr.c new file mode 100644 index 0000000000000000000000000000000000000000..1ad10ae0fdf7d1e00c3b98671b386c15a127cb6c --- /dev/null +++ b/aosp/external/openssh/addr.c @@ -0,0 +1,435 @@ +/* $OpenBSD: addr.c,v 1.4 2021/10/22 10:51:57 dtucker Exp $ */ + +/* + * Copyright (c) 2004-2008 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "addr.h" + +#define _SA(x) ((struct sockaddr *)(x)) + +int +addr_unicast_masklen(int af) +{ + switch (af) { + case AF_INET: + return 32; + case AF_INET6: + return 128; + default: + return -1; + } +} + +static inline int +masklen_valid(int af, u_int masklen) +{ + switch (af) { + case AF_INET: + return masklen <= 32 ? 0 : -1; + case AF_INET6: + return masklen <= 128 ? 0 : -1; + default: + return -1; + } +} + +int +addr_xaddr_to_sa(const struct xaddr *xa, struct sockaddr *sa, socklen_t *len, + u_int16_t port) +{ + struct sockaddr_in *in4 = (struct sockaddr_in *)sa; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa; + + if (xa == NULL || sa == NULL || len == NULL) + return -1; + + switch (xa->af) { + case AF_INET: + if (*len < sizeof(*in4)) + return -1; + memset(sa, '\0', sizeof(*in4)); + *len = sizeof(*in4); +#ifdef SOCK_HAS_LEN + in4->sin_len = sizeof(*in4); +#endif + in4->sin_family = AF_INET; + in4->sin_port = htons(port); + memcpy(&in4->sin_addr, &xa->v4, sizeof(in4->sin_addr)); + break; + case AF_INET6: + if (*len < sizeof(*in6)) + return -1; + memset(sa, '\0', sizeof(*in6)); + *len = sizeof(*in6); +#ifdef SOCK_HAS_LEN + in6->sin6_len = sizeof(*in6); +#endif + in6->sin6_family = AF_INET6; + in6->sin6_port = htons(port); + memcpy(&in6->sin6_addr, &xa->v6, sizeof(in6->sin6_addr)); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + in6->sin6_scope_id = xa->scope_id; +#endif + break; + default: + return -1; + } + return 0; +} + +/* + * Convert struct sockaddr to struct xaddr + * Returns 0 on success, -1 on failure. + */ +int +addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa) +{ + struct sockaddr_in *in4 = (struct sockaddr_in *)sa; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa; + + memset(xa, '\0', sizeof(*xa)); + + switch (sa->sa_family) { + case AF_INET: + if (slen < (socklen_t)sizeof(*in4)) + return -1; + xa->af = AF_INET; + memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4)); + break; + case AF_INET6: + if (slen < (socklen_t)sizeof(*in6)) + return -1; + xa->af = AF_INET6; + memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6)); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + xa->scope_id = in6->sin6_scope_id; +#endif + break; + default: + return -1; + } + + return 0; +} + +int +addr_invert(struct xaddr *n) +{ + int i; + + if (n == NULL) + return -1; + + switch (n->af) { + case AF_INET: + n->v4.s_addr = ~n->v4.s_addr; + return 0; + case AF_INET6: + for (i = 0; i < 4; i++) + n->addr32[i] = ~n->addr32[i]; + return 0; + default: + return -1; + } +} + +/* + * Calculate a netmask of length 'l' for address family 'af' and + * store it in 'n'. + * Returns 0 on success, -1 on failure. + */ +int +addr_netmask(int af, u_int l, struct xaddr *n) +{ + int i; + + if (masklen_valid(af, l) != 0 || n == NULL) + return -1; + + memset(n, '\0', sizeof(*n)); + switch (af) { + case AF_INET: + n->af = AF_INET; + if (l == 0) + return 0; + n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); + return 0; + case AF_INET6: + n->af = AF_INET6; + for (i = 0; i < 4 && l >= 32; i++, l -= 32) + n->addr32[i] = 0xffffffffU; + if (i < 4 && l != 0) + n->addr32[i] = htonl((0xffffffff << (32 - l)) & + 0xffffffff); + return 0; + default: + return -1; + } +} + +int +addr_hostmask(int af, u_int l, struct xaddr *n) +{ + if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1) + return -1; + return 0; +} + +/* + * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'. + * Returns 0 on success, -1 on failure. + */ +int +addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b) +{ + int i; + + if (dst == NULL || a == NULL || b == NULL || a->af != b->af) + return -1; + + memcpy(dst, a, sizeof(*dst)); + switch (a->af) { + case AF_INET: + dst->v4.s_addr &= b->v4.s_addr; + return 0; + case AF_INET6: + dst->scope_id = a->scope_id; + for (i = 0; i < 4; i++) + dst->addr32[i] &= b->addr32[i]; + return 0; + default: + return -1; + } +} + +int +addr_cmp(const struct xaddr *a, const struct xaddr *b) +{ + int i; + + if (a->af != b->af) + return (a->af == AF_INET6 ? 1 : -1); + + switch (a->af) { + case AF_INET: + /* + * Can't just subtract here as 255.255.255.255 - 0.0.0.0 is + * too big to fit into a signed int + */ + if (a->v4.s_addr == b->v4.s_addr) + return 0; + return (ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1); + case AF_INET6: + /* + * Do this a byte at a time to avoid the above issue and + * any endian problems + */ + for (i = 0; i < 16; i++) + if (a->addr8[i] - b->addr8[i] != 0) + return (a->addr8[i] - b->addr8[i]); + if (a->scope_id == b->scope_id) + return (0); + return (a->scope_id > b->scope_id ? 1 : -1); + default: + return (-1); + } +} + +int +addr_is_all0s(const struct xaddr *a) +{ + int i; + + switch (a->af) { + case AF_INET: + return (a->v4.s_addr == 0 ? 0 : -1); + case AF_INET6: + for (i = 0; i < 4; i++) + if (a->addr32[i] != 0) + return -1; + return 0; + default: + return -1; + } +} + +/* + * Test whether host portion of address 'a', as determined by 'masklen' + * is all zeros. + * Returns 0 if host portion of address is all-zeros, + * -1 if not all zeros or on failure. + */ +int +addr_host_is_all0s(const struct xaddr *a, u_int masklen) +{ + struct xaddr tmp_addr, tmp_mask, tmp_result; + + memcpy(&tmp_addr, a, sizeof(tmp_addr)); + if (addr_hostmask(a->af, masklen, &tmp_mask) == -1) + return -1; + if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1) + return -1; + return addr_is_all0s(&tmp_result); +} + +/* + * Parse string address 'p' into 'n'. + * Returns 0 on success, -1 on failure. + */ +int +addr_pton(const char *p, struct xaddr *n) +{ + struct addrinfo hints, *ai; + + memset(&hints, '\0', sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + + if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0) + return -1; + + if (ai == NULL) + return -1; + + if (ai->ai_addr == NULL) { + freeaddrinfo(ai); + return -1; + } + + if (n != NULL && addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, + n) == -1) { + freeaddrinfo(ai); + return -1; + } + + freeaddrinfo(ai); + return 0; +} + +int +addr_sa_pton(const char *h, const char *s, struct sockaddr *sa, socklen_t slen) +{ + struct addrinfo hints, *ai; + + memset(&hints, '\0', sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + + if (h == NULL || getaddrinfo(h, s, &hints, &ai) != 0) + return -1; + + if (ai == NULL) + return -1; + + if (ai->ai_addr == NULL) { + freeaddrinfo(ai); + return -1; + } + + if (sa != NULL) { + if (slen < ai->ai_addrlen) { + freeaddrinfo(ai); + return -1; + } + memcpy(sa, &ai->ai_addr, ai->ai_addrlen); + } + + freeaddrinfo(ai); + return 0; +} + +int +addr_ntop(const struct xaddr *n, char *p, size_t len) +{ + struct sockaddr_storage ss; + socklen_t slen = sizeof(ss); + + if (addr_xaddr_to_sa(n, _SA(&ss), &slen, 0) == -1) + return -1; + if (p == NULL || len == 0) + return -1; + if (getnameinfo(_SA(&ss), slen, p, len, NULL, 0, + NI_NUMERICHOST) == -1) + return -1; + + return 0; +} + +/* + * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z). + * Return -1 on parse error, -2 on inconsistency or 0 on success. + */ +int +addr_pton_cidr(const char *p, struct xaddr *n, u_int *l) +{ + struct xaddr tmp; + long unsigned int masklen = 999; + char addrbuf[64], *mp, *cp; + + /* Don't modify argument */ + if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) >= sizeof(addrbuf)) + return -1; + + if ((mp = strchr(addrbuf, '/')) != NULL) { + *mp = '\0'; + mp++; + masklen = strtoul(mp, &cp, 10); + if (*mp == '\0' || *cp != '\0' || masklen > 128) + return -1; + } + + if (addr_pton(addrbuf, &tmp) == -1) + return -1; + + if (mp == NULL) + masklen = addr_unicast_masklen(tmp.af); + if (masklen_valid(tmp.af, masklen) == -1) + return -2; + if (addr_host_is_all0s(&tmp, masklen) != 0) + return -2; + + if (n != NULL) + memcpy(n, &tmp, sizeof(*n)); + if (l != NULL) + *l = masklen; + + return 0; +} + +int +addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen) +{ + struct xaddr tmp_mask, tmp_result; + + if (host->af != net->af) + return -1; + + if (addr_netmask(host->af, masklen, &tmp_mask) == -1) + return -1; + if (addr_and(&tmp_result, host, &tmp_mask) == -1) + return -1; + return addr_cmp(&tmp_result, net); +} diff --git a/aosp/external/openssh/addr.h b/aosp/external/openssh/addr.h new file mode 100644 index 0000000000000000000000000000000000000000..5eff026285923d86207c9a60a559630edf83fa79 --- /dev/null +++ b/aosp/external/openssh/addr.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004,2005 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Address handling routines */ + +#ifndef _ADDR_H +#define _ADDR_H + +#include +#include + +struct xaddr { + sa_family_t af; + union { + struct in_addr v4; + struct in6_addr v6; + u_int8_t addr8[16]; + u_int16_t addr16[8]; + u_int32_t addr32[4]; + } xa; /* 128-bit address */ + u_int32_t scope_id; /* iface scope id for v6 */ +#define v4 xa.v4 +#define v6 xa.v6 +#define addr8 xa.addr8 +#define addr16 xa.addr16 +#define addr32 xa.addr32 +}; + +int addr_unicast_masklen(int af); +int addr_xaddr_to_sa(const struct xaddr *xa, struct sockaddr *sa, + socklen_t *len, u_int16_t port); +int addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa); +int addr_netmask(int af, u_int l, struct xaddr *n); +int addr_hostmask(int af, u_int l, struct xaddr *n); +int addr_invert(struct xaddr *n); +int addr_pton(const char *p, struct xaddr *n); +int addr_sa_pton(const char *h, const char *s, struct sockaddr *sa, + socklen_t slen); +int addr_pton_cidr(const char *p, struct xaddr *n, u_int *l); +int addr_ntop(const struct xaddr *n, char *p, size_t len); +int addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b); +int addr_cmp(const struct xaddr *a, const struct xaddr *b); +int addr_is_all0s(const struct xaddr *n); +int addr_host_is_all0s(const struct xaddr *n, u_int masklen); +int addr_netmatch(const struct xaddr *host, const struct xaddr *net, + u_int masklen); +#endif /* _ADDR_H */ diff --git a/aosp/external/openssh/addrmatch.c b/aosp/external/openssh/addrmatch.c new file mode 100644 index 0000000000000000000000000000000000000000..b0dc096804db02315aa88f3d3275a9cfa9c61b31 --- /dev/null +++ b/aosp/external/openssh/addrmatch.c @@ -0,0 +1,169 @@ +/* $OpenBSD: addrmatch.c,v 1.17 2021/04/03 06:18:40 djm Exp $ */ + +/* + * Copyright (c) 2004-2008 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "addr.h" +#include "match.h" +#include "log.h" + +/* + * Match "addr" against list pattern list "_list", which may contain a + * mix of CIDR addresses and old-school wildcards. + * + * If addr is NULL, then no matching is performed, but _list is parsed + * and checked for well-formedness. + * + * Returns 1 on match found (never returned when addr == NULL). + * Returns 0 on if no match found, or no errors found when addr == NULL. + * Returns -1 on negated match found (never returned when addr == NULL). + * Returns -2 on invalid list entry. + */ +int +addr_match_list(const char *addr, const char *_list) +{ + char *list, *cp, *o; + struct xaddr try_addr, match_addr; + u_int masklen, neg; + int ret = 0, r; + + if (addr != NULL && addr_pton(addr, &try_addr) != 0) { + debug2_f("couldn't parse address %.100s", addr); + return 0; + } + if ((o = list = strdup(_list)) == NULL) + return -1; + while ((cp = strsep(&list, ",")) != NULL) { + neg = *cp == '!'; + if (neg) + cp++; + if (*cp == '\0') { + ret = -2; + break; + } + /* Prefer CIDR address matching */ + r = addr_pton_cidr(cp, &match_addr, &masklen); + if (r == -2) { + debug2_f("inconsistent mask length for " + "match network \"%.100s\"", cp); + ret = -2; + break; + } else if (r == 0) { + if (addr != NULL && addr_netmatch(&try_addr, + &match_addr, masklen) == 0) { + foundit: + if (neg) { + ret = -1; + break; + } + ret = 1; + } + continue; + } else { + /* If CIDR parse failed, try wildcard string match */ + if (addr != NULL && match_pattern(addr, cp) == 1) + goto foundit; + } + } + free(o); + + return ret; +} + +/* + * Match "addr" against list CIDR list "_list". Lexical wildcards and + * negation are not supported. If "addr" == NULL, will verify structure + * of "_list". + * + * Returns 1 on match found (never returned when addr == NULL). + * Returns 0 on if no match found, or no errors found when addr == NULL. + * Returns -1 on error + */ +int +addr_match_cidr_list(const char *addr, const char *_list) +{ + char *list, *cp, *o; + struct xaddr try_addr, match_addr; + u_int masklen; + int ret = 0, r; + + if (addr != NULL && addr_pton(addr, &try_addr) != 0) { + debug2_f("couldn't parse address %.100s", addr); + return 0; + } + if ((o = list = strdup(_list)) == NULL) + return -1; + while ((cp = strsep(&list, ",")) != NULL) { + if (*cp == '\0') { + error_f("empty entry in list \"%.100s\"", o); + ret = -1; + break; + } + + /* + * NB. This function is called in pre-auth with untrusted data, + * so be extra paranoid about junk reaching getaddrino (via + * addr_pton_cidr). + */ + + /* Stop junk from reaching getaddrinfo. +3 is for masklen */ + if (strlen(cp) > INET6_ADDRSTRLEN + 3) { + error_f("list entry \"%.100s\" too long", cp); + ret = -1; + break; + } +#define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" + if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { + error_f("list entry \"%.100s\" contains invalid " + "characters", cp); + ret = -1; + } + + /* Prefer CIDR address matching */ + r = addr_pton_cidr(cp, &match_addr, &masklen); + if (r == -1) { + error("Invalid network entry \"%.100s\"", cp); + ret = -1; + break; + } else if (r == -2) { + error("Inconsistent mask length for " + "network \"%.100s\"", cp); + ret = -1; + break; + } else if (r == 0 && addr != NULL) { + if (addr_netmatch(&try_addr, &match_addr, + masklen) == 0) + ret = 1; + continue; + } + } + free(o); + + return ret; +} diff --git a/aosp/external/openssh/atomicio.c b/aosp/external/openssh/atomicio.c new file mode 100644 index 0000000000000000000000000000000000000000..765073357f67342f8b5f21ac869629c0c9260c7a --- /dev/null +++ b/aosp/external/openssh/atomicio.c @@ -0,0 +1,179 @@ +/* $OpenBSD: atomicio.c,v 1.30 2019/01/24 02:42:23 dtucker Exp $ */ +/* + * Copyright (c) 2006 Damien Miller. All rights reserved. + * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif +#include +#include +#include + +#include "atomicio.h" + +/* + * ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t +atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n, + int (*cb)(void *, size_t), void *cb_arg) +{ + char *s = _s; + size_t pos = 0; + ssize_t res; + struct pollfd pfd; + + pfd.fd = fd; +#ifndef BROKEN_READ_COMPARISON + pfd.events = f == read ? POLLIN : POLLOUT; +#else + pfd.events = POLLIN|POLLOUT; +#endif + while (n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR) { + /* possible SIGALARM, update callback */ + if (cb != NULL && cb(cb_arg, 0) == -1) { + errno = EINTR; + return pos; + } + continue; + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + (void)poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + errno = EPIPE; + return pos; + default: + pos += (size_t)res; + if (cb != NULL && cb(cb_arg, (size_t)res) == -1) { + errno = EINTR; + return pos; + } + } + } + return pos; +} + +size_t +atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) +{ + return atomicio6(f, fd, _s, n, NULL, NULL); +} + +/* + * ensure all of data on socket comes through. f==readv || f==writev + */ +size_t +atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd, + const struct iovec *_iov, int iovcnt, + int (*cb)(void *, size_t), void *cb_arg) +{ + size_t pos = 0, rem; + ssize_t res; + struct iovec iov_array[IOV_MAX], *iov = iov_array; + struct pollfd pfd; + + if (iovcnt < 0 || iovcnt > IOV_MAX) { + errno = EINVAL; + return 0; + } + /* Make a copy of the iov array because we may modify it below */ + memcpy(iov, _iov, (size_t)iovcnt * sizeof(*_iov)); + + pfd.fd = fd; +#ifndef BROKEN_READV_COMPARISON + pfd.events = f == readv ? POLLIN : POLLOUT; +#else + pfd.events = POLLIN|POLLOUT; +#endif + for (; iovcnt > 0 && iov[0].iov_len > 0;) { + res = (f) (fd, iov, iovcnt); + switch (res) { + case -1: + if (errno == EINTR) { + /* possible SIGALARM, update callback */ + if (cb != NULL && cb(cb_arg, 0) == -1) { + errno = EINTR; + return pos; + } + continue; + } else if (errno == EAGAIN || errno == EWOULDBLOCK) { + (void)poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + errno = EPIPE; + return pos; + default: + rem = (size_t)res; + pos += rem; + /* skip completed iov entries */ + while (iovcnt > 0 && rem >= iov[0].iov_len) { + rem -= iov[0].iov_len; + iov++; + iovcnt--; + } + /* This shouldn't happen... */ + if (rem > 0 && (iovcnt <= 0 || rem > iov[0].iov_len)) { + errno = EFAULT; + return 0; + } + if (iovcnt == 0) + break; + /* update pointer in partially complete iov */ + iov[0].iov_base = ((char *)iov[0].iov_base) + rem; + iov[0].iov_len -= rem; + } + if (cb != NULL && cb(cb_arg, (size_t)res) == -1) { + errno = EINTR; + return pos; + } + } + return pos; +} + +size_t +atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd, + const struct iovec *_iov, int iovcnt) +{ + return atomiciov6(f, fd, _iov, iovcnt, NULL, NULL); +} diff --git a/aosp/external/openssh/atomicio.h b/aosp/external/openssh/atomicio.h new file mode 100644 index 0000000000000000000000000000000000000000..8b3cc6e211bd5fc78ed9e2736ea66081cb819385 --- /dev/null +++ b/aosp/external/openssh/atomicio.h @@ -0,0 +1,53 @@ +/* $OpenBSD: atomicio.h,v 1.12 2018/12/27 03:25:25 djm Exp $ */ + +/* + * Copyright (c) 2006 Damien Miller. All rights reserved. + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ATOMICIO_H +#define _ATOMICIO_H + +struct iovec; + +/* + * Ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t +atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n, + int (*cb)(void *, size_t), void *); +size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); + +#define vwrite (ssize_t (*)(int, void *, size_t))write + +/* + * ensure all of data on socket comes through. f==readv || f==writev + */ +size_t +atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd, + const struct iovec *_iov, int iovcnt, int (*cb)(void *, size_t), void *); +size_t atomiciov(ssize_t (*)(int, const struct iovec *, int), + int, const struct iovec *, int); + +#endif /* _ATOMICIO_H */ diff --git a/aosp/external/openssh/audit-bsm.c b/aosp/external/openssh/audit-bsm.c new file mode 100644 index 0000000000000000000000000000000000000000..ccfcf6f7fc6861d07b0df0d41d48104d4b73792c --- /dev/null +++ b/aosp/external/openssh/audit-bsm.c @@ -0,0 +1,455 @@ +/* + * TODO + * + * - deal with overlap between this and sys_auth_allowed_user + * sys_auth_record_login and record_failed_login. + */ + +/* + * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* #pragma ident "@(#)bsmaudit.c 1.1 01/09/17 SMI" */ + +#include "includes.h" +#if defined(USE_BSM_AUDIT) + +#include + +#include +#include +#include +#include +#include + +#ifdef BROKEN_BSM_API +#include +#endif + +#include "ssh.h" +#include "log.h" +#include "hostfile.h" +#include "auth.h" +#include "xmalloc.h" + +#ifndef AUE_openssh +# define AUE_openssh 32800 +#endif +#include +#include +#include +#include +#include + +#if defined(HAVE_GETAUDIT_ADDR) +#define AuditInfoStruct auditinfo_addr +#define AuditInfoTermID au_tid_addr_t +#define SetAuditFunc(a,b) setaudit_addr((a),(b)) +#define SetAuditFuncText "setaudit_addr" +#define AUToSubjectFunc au_to_subject_ex +#define AUToReturnFunc(a,b) au_to_return32((a), (int32_t)(b)) +#else +#define AuditInfoStruct auditinfo +#define AuditInfoTermID au_tid_t +#define SetAuditFunc(a,b) setaudit(a) +#define SetAuditFuncText "setaudit" +#define AUToSubjectFunc au_to_subject +#define AUToReturnFunc(a,b) au_to_return((a), (u_int)(b)) +#endif + +#ifndef cannot_audit +extern int cannot_audit(int); +#endif +extern void aug_init(void); +extern void aug_save_auid(au_id_t); +extern void aug_save_uid(uid_t); +extern void aug_save_euid(uid_t); +extern void aug_save_gid(gid_t); +extern void aug_save_egid(gid_t); +extern void aug_save_pid(pid_t); +extern void aug_save_asid(au_asid_t); +extern void aug_save_tid(dev_t, unsigned int); +extern void aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t); +extern int aug_save_me(void); +extern int aug_save_namask(void); +extern void aug_save_event(au_event_t); +extern void aug_save_sorf(int); +extern void aug_save_text(char *); +extern void aug_save_text1(char *); +extern void aug_save_text2(char *); +extern void aug_save_na(int); +extern void aug_save_user(char *); +extern void aug_save_path(char *); +extern int aug_save_policy(void); +extern void aug_save_afunc(int (*)(int)); +extern int aug_audit(void); +extern int aug_na_selected(void); +extern int aug_selected(void); +extern int aug_daemon_session(void); + +#ifndef HAVE_GETTEXT +# define gettext(a) (a) +#endif + +extern Authctxt *the_authctxt; +static AuditInfoTermID ssh_bsm_tid; + +#ifdef BROKEN_BSM_API +/* For some reason this constant is no longer defined + in Solaris 11. */ +#define BSM_TEXTBUFSZ 256 +#endif + +/* Below is the low-level BSM interface code */ + +/* + * aug_get_machine is only required on IPv6 capable machines, we use a + * different mechanism in audit_connection_from() for IPv4-only machines. + * getaudit_addr() is only present on IPv6 capable machines. + */ +#if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR) +extern int aug_get_machine(char *, u_int32_t *, u_int32_t *); +#else +static int +aug_get_machine(char *host, u_int32_t *addr, u_int32_t *type) +{ + struct addrinfo *ai; + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; + int ret = 0, r; + + if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) { + error("BSM audit: getaddrinfo failed for %.100s: %.100s", host, + r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); + return -1; + } + + switch (ai->ai_family) { + case AF_INET: + in4 = (struct sockaddr_in *)ai->ai_addr; + *type = AU_IPv4; + memcpy(addr, &in4->sin_addr, sizeof(struct in_addr)); + break; +#ifdef AU_IPv6 + case AF_INET6: + in6 = (struct sockaddr_in6 *)ai->ai_addr; + *type = AU_IPv6; + memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr)); + break; +#endif + default: + error("BSM audit: unknown address family for %.100s: %d", + host, ai->ai_family); + ret = -1; + } + freeaddrinfo(ai); + return ret; +} +#endif + +#ifdef BROKEN_BSM_API +/* + In Solaris 11 the audit daemon has been moved to SMF. In the process + they simply dropped getacna() from the API, since it read from a now + non-existent config file. This function re-implements getacna() to + read from the SMF repository instead. + */ +int +getacna(char *auditstring, int len) +{ + scf_handle_t *handle = NULL; + scf_property_t *property = NULL; + scf_value_t *value = NULL; + int ret = 0; + + /* + * The man page for getacna on Solaris 10 states we should return -2 + * in case of error and set errno to indicate the error. We don't + * bother with errno here, though, since the only use of this function + * below doesn't check for errors anyway. + */ + handle = scf_handle_create(SCF_VERSION); + if (handle == NULL) + return -2; + + ret = scf_handle_bind(handle); + if (ret == -1) + return -2; + + property = scf_property_create(handle); + if (property == NULL) + return -2; + + ret = scf_handle_decode_fmri(handle, + "svc:/system/auditd:default/:properties/preselection/naflags", + NULL, NULL, NULL, NULL, property, 0); + if (ret == -1) + return -2; + + value = scf_value_create(handle); + if (value == NULL) + return -2; + + ret = scf_property_get_value(property, value); + if (ret == -1) + return -2; + + ret = scf_value_get_astring(value, auditstring, len); + if (ret == -1) + return -2; + + scf_value_destroy(value); + scf_property_destroy(property); + scf_handle_destroy(handle); + + return 0; +} +#endif + +/* + * Check if the specified event is selected (enabled) for auditing. + * Returns 1 if the event is selected, 0 if not and -1 on failure. + */ +static int +selected(char *username, uid_t uid, au_event_t event, int sf) +{ + int rc, sorf; + char naflags[512]; + struct au_mask mask; + + mask.am_success = mask.am_failure = 0; + if (uid < 0) { + /* get flags for non-attributable (to a real user) events */ + rc = getacna(naflags, sizeof(naflags)); + if (rc == 0) + (void) getauditflagsbin(naflags, &mask); + } else + rc = au_user_mask(username, &mask); + + sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE; + return(au_preselect(event, &mask, sorf, AU_PRS_REREAD)); +} + +static void +bsm_audit_record(int typ, char *string, au_event_t event_no) +{ + int ad, rc, sel; + uid_t uid = -1; + gid_t gid = -1; + pid_t pid = getpid(); + AuditInfoTermID tid = ssh_bsm_tid; + + if (the_authctxt != NULL && the_authctxt->valid) { + uid = the_authctxt->pw->pw_uid; + gid = the_authctxt->pw->pw_gid; + } + + rc = (typ == 0) ? 0 : -1; + sel = selected(the_authctxt->user, uid, event_no, rc); + debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string); + if (!sel) + return; /* audit event does not match mask, do not write */ + + debug3("BSM audit: writing audit new record"); + ad = au_open(); + + (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid, + pid, pid, &tid)); + (void) au_write(ad, au_to_text(string)); + (void) au_write(ad, AUToReturnFunc(typ, rc)); + +#ifdef BROKEN_BSM_API + /* + * The last argument is the event modifier flags. For some seemingly + * undocumented reason it was added in Solaris 11. + */ + rc = au_close(ad, AU_TO_WRITE, event_no, 0); +#else + rc = au_close(ad, AU_TO_WRITE, event_no); +#endif + + if (rc < 0) + error("BSM audit: %s failed to write \"%s\" record: %s", + __func__, string, strerror(errno)); +} + +static void +bsm_audit_session_setup(void) +{ + int rc; + struct AuditInfoStruct info; + au_mask_t mask; + + if (the_authctxt == NULL) { + error("BSM audit: session setup internal error (NULL ctxt)"); + return; + } + + if (the_authctxt->valid) + info.ai_auid = the_authctxt->pw->pw_uid; + else + info.ai_auid = -1; + info.ai_asid = getpid(); + mask.am_success = 0; + mask.am_failure = 0; + + (void) au_user_mask(the_authctxt->user, &mask); + + info.ai_mask.am_success = mask.am_success; + info.ai_mask.am_failure = mask.am_failure; + + info.ai_termid = ssh_bsm_tid; + + rc = SetAuditFunc(&info, sizeof(info)); + if (rc < 0) + error("BSM audit: %s: %s failed: %s", __func__, + SetAuditFuncText, strerror(errno)); +} + +static void +bsm_audit_bad_login(const char *what) +{ + char textbuf[BSM_TEXTBUFSZ]; + + if (the_authctxt->valid) { + (void) snprintf(textbuf, sizeof (textbuf), + gettext("invalid %s for user %s"), + what, the_authctxt->user); + bsm_audit_record(4, textbuf, AUE_openssh); + } else { + (void) snprintf(textbuf, sizeof (textbuf), + gettext("invalid user name \"%s\""), + the_authctxt->user); + bsm_audit_record(3, textbuf, AUE_openssh); + } +} + +/* Below is the sshd audit API code */ + +void +audit_connection_from(const char *host, int port) +{ + AuditInfoTermID *tid = &ssh_bsm_tid; + char buf[1024]; + + if (cannot_audit(0)) + return; + debug3("BSM audit: connection from %.100s port %d", host, port); + + /* populate our terminal id structure */ +#if defined(HAVE_GETAUDIT_ADDR) + tid->at_port = (dev_t)port; + aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type)); + snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0], + tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]); + debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf); +#else + /* this is used on IPv4-only machines */ + tid->port = (dev_t)port; + tid->machine = inet_addr(host); + snprintf(buf, sizeof(buf), "%08x", tid->machine); + debug3("BSM audit: machine ID %s", buf); +#endif +} + +void +audit_run_command(const char *command) +{ + /* not implemented */ +} + +void +audit_session_open(struct logininfo *li) +{ + /* not implemented */ +} + +void +audit_session_close(struct logininfo *li) +{ + /* not implemented */ +} + +void +audit_event(struct ssh *ssh, ssh_audit_event_t event) +{ + char textbuf[BSM_TEXTBUFSZ]; + static int logged_in = 0; + const char *user = the_authctxt ? the_authctxt->user : "(unknown user)"; + + if (cannot_audit(0)) + return; + + switch(event) { + case SSH_AUTH_SUCCESS: + logged_in = 1; + bsm_audit_session_setup(); + snprintf(textbuf, sizeof(textbuf), + gettext("successful login %s"), user); + bsm_audit_record(0, textbuf, AUE_openssh); + break; + + case SSH_CONNECTION_CLOSE: + /* + * We can also get a close event if the user attempted auth + * but never succeeded. + */ + if (logged_in) { + snprintf(textbuf, sizeof(textbuf), + gettext("sshd logout %s"), the_authctxt->user); + bsm_audit_record(0, textbuf, AUE_logout); + } else { + debug("%s: connection closed without authentication", + __func__); + } + break; + + case SSH_NOLOGIN: + bsm_audit_record(1, + gettext("logins disabled by /etc/nologin"), AUE_openssh); + break; + + case SSH_LOGIN_EXCEED_MAXTRIES: + snprintf(textbuf, sizeof(textbuf), + gettext("too many tries for user %s"), the_authctxt->user); + bsm_audit_record(1, textbuf, AUE_openssh); + break; + + case SSH_LOGIN_ROOT_DENIED: + bsm_audit_record(2, gettext("not_console"), AUE_openssh); + break; + + case SSH_AUTH_FAIL_PASSWD: + bsm_audit_bad_login("password"); + break; + + case SSH_AUTH_FAIL_KBDINT: + bsm_audit_bad_login("interactive password entry"); + break; + + default: + debug("%s: unhandled event %d", __func__, event); + } +} +#endif /* BSM */ diff --git a/aosp/external/openssh/audit-linux.c b/aosp/external/openssh/audit-linux.c new file mode 100644 index 0000000000000000000000000000000000000000..3fcbe5c53ef991c16ec2fcc4b1544084b8a2a2a2 --- /dev/null +++ b/aosp/external/openssh/audit-linux.c @@ -0,0 +1,124 @@ +/* + * Copyright 2010 Red Hat, Inc. All rights reserved. + * Use is subject to license terms. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Red Hat author: Jan F. Chadima + */ + +#include "includes.h" +#if defined(USE_LINUX_AUDIT) +#include +#include +#include + +#include "log.h" +#include "audit.h" +#include "canohost.h" +#include "packet.h" + +const char *audit_username(void); + +int +linux_audit_record_event(int uid, const char *username, const char *hostname, + const char *ip, const char *ttyn, int success) +{ + int audit_fd, rc, saved_errno; + + if ((audit_fd = audit_open()) < 0) { + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) + return 1; /* No audit support in kernel */ + else + return 0; /* Must prevent login */ + } + rc = audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN, + NULL, "login", username ? username : "(unknown)", + username == NULL ? uid : -1, hostname, ip, ttyn, success); + saved_errno = errno; + close(audit_fd); + + /* + * Do not report error if the error is EPERM and sshd is run as non + * root user. + */ + if ((rc == -EPERM) && (geteuid() != 0)) + rc = 0; + errno = saved_errno; + + return rc >= 0; +} + +/* Below is the sshd audit API code */ + +void +audit_connection_from(const char *host, int port) +{ + /* not implemented */ +} + +void +audit_run_command(const char *command) +{ + /* not implemented */ +} + +void +audit_session_open(struct logininfo *li) +{ + if (linux_audit_record_event(li->uid, NULL, li->hostname, NULL, + li->line, 1) == 0) + fatal("linux_audit_write_entry failed: %s", strerror(errno)); +} + +void +audit_session_close(struct logininfo *li) +{ + /* not implemented */ +} + +void +audit_event(struct ssh *ssh, ssh_audit_event_t event) +{ + switch(event) { + case SSH_AUTH_SUCCESS: + case SSH_CONNECTION_CLOSE: + case SSH_NOLOGIN: + case SSH_LOGIN_EXCEED_MAXTRIES: + case SSH_LOGIN_ROOT_DENIED: + break; + case SSH_AUTH_FAIL_NONE: + case SSH_AUTH_FAIL_PASSWD: + case SSH_AUTH_FAIL_KBDINT: + case SSH_AUTH_FAIL_PUBKEY: + case SSH_AUTH_FAIL_HOSTBASED: + case SSH_AUTH_FAIL_GSSAPI: + case SSH_INVALID_USER: + linux_audit_record_event(-1, audit_username(), NULL, + ssh_remote_ipaddr(ssh), "sshd", 0); + break; + default: + debug("%s: unhandled event %d", __func__, event); + break; + } +} +#endif /* USE_LINUX_AUDIT */ diff --git a/aosp/external/openssh/audit.c b/aosp/external/openssh/audit.c new file mode 100644 index 0000000000000000000000000000000000000000..dd2f03558fe971c3b282a870c9c9f83275c66b5e --- /dev/null +++ b/aosp/external/openssh/audit.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2004, 2005 Darren Tucker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#ifdef SSH_AUDIT_EVENTS + +#include "audit.h" +#include "log.h" +#include "hostfile.h" +#include "auth.h" + +/* + * Care must be taken when using this since it WILL NOT be initialized when + * audit_connection_from() is called and MAY NOT be initialized when + * audit_event(CONNECTION_ABANDON) is called. Test for NULL before using. + */ +extern Authctxt *the_authctxt; + +/* Maybe add the audit class to struct Authmethod? */ +ssh_audit_event_t +audit_classify_auth(const char *method) +{ + if (strcmp(method, "none") == 0) + return SSH_AUTH_FAIL_NONE; + else if (strcmp(method, "password") == 0) + return SSH_AUTH_FAIL_PASSWD; + else if (strcmp(method, "publickey") == 0 || + strcmp(method, "rsa") == 0) + return SSH_AUTH_FAIL_PUBKEY; + else if (strncmp(method, "keyboard-interactive", 20) == 0 || + strcmp(method, "challenge-response") == 0) + return SSH_AUTH_FAIL_KBDINT; + else if (strcmp(method, "hostbased") == 0 || + strcmp(method, "rhosts-rsa") == 0) + return SSH_AUTH_FAIL_HOSTBASED; + else if (strcmp(method, "gssapi-with-mic") == 0) + return SSH_AUTH_FAIL_GSSAPI; + else + return SSH_AUDIT_UNKNOWN; +} + +/* helper to return supplied username */ +const char * +audit_username(void) +{ + static const char unknownuser[] = "(unknown user)"; + static const char invaliduser[] = "(invalid user)"; + + if (the_authctxt == NULL || the_authctxt->user == NULL) + return (unknownuser); + if (!the_authctxt->valid) + return (invaliduser); + return (the_authctxt->user); +} + +const char * +audit_event_lookup(ssh_audit_event_t ev) +{ + int i; + static struct event_lookup_struct { + ssh_audit_event_t event; + const char *name; + } event_lookup[] = { + {SSH_LOGIN_EXCEED_MAXTRIES, "LOGIN_EXCEED_MAXTRIES"}, + {SSH_LOGIN_ROOT_DENIED, "LOGIN_ROOT_DENIED"}, + {SSH_AUTH_SUCCESS, "AUTH_SUCCESS"}, + {SSH_AUTH_FAIL_NONE, "AUTH_FAIL_NONE"}, + {SSH_AUTH_FAIL_PASSWD, "AUTH_FAIL_PASSWD"}, + {SSH_AUTH_FAIL_KBDINT, "AUTH_FAIL_KBDINT"}, + {SSH_AUTH_FAIL_PUBKEY, "AUTH_FAIL_PUBKEY"}, + {SSH_AUTH_FAIL_HOSTBASED, "AUTH_FAIL_HOSTBASED"}, + {SSH_AUTH_FAIL_GSSAPI, "AUTH_FAIL_GSSAPI"}, + {SSH_INVALID_USER, "INVALID_USER"}, + {SSH_NOLOGIN, "NOLOGIN"}, + {SSH_CONNECTION_CLOSE, "CONNECTION_CLOSE"}, + {SSH_CONNECTION_ABANDON, "CONNECTION_ABANDON"}, + {SSH_AUDIT_UNKNOWN, "AUDIT_UNKNOWN"} + }; + + for (i = 0; event_lookup[i].event != SSH_AUDIT_UNKNOWN; i++) + if (event_lookup[i].event == ev) + break; + return(event_lookup[i].name); +} + +# ifndef CUSTOM_SSH_AUDIT_EVENTS +/* + * Null implementations of audit functions. + * These get used if SSH_AUDIT_EVENTS is defined but no audit module is enabled. + */ + +/* + * Called after a connection has been accepted but before any authentication + * has been attempted. + */ +void +audit_connection_from(const char *host, int port) +{ + debug("audit connection from %s port %d euid %d", host, port, + (int)geteuid()); +} + +/* + * Called when various events occur (see audit.h for a list of possible + * events and what they mean). + */ +void +audit_event(struct ssh *ssh, ssh_audit_event_t event) +{ + debug("audit event euid %d user %s event %d (%s)", geteuid(), + audit_username(), event, audit_event_lookup(event)); +} + +/* + * Called when a user session is started. Argument is the tty allocated to + * the session, or NULL if no tty was allocated. + * + * Note that this may be called multiple times if multiple sessions are used + * within a single connection. + */ +void +audit_session_open(struct logininfo *li) +{ + const char *t = li->line ? li->line : "(no tty)"; + + debug("audit session open euid %d user %s tty name %s", geteuid(), + audit_username(), t); +} + +/* + * Called when a user session is closed. Argument is the tty allocated to + * the session, or NULL if no tty was allocated. + * + * Note that this may be called multiple times if multiple sessions are used + * within a single connection. + */ +void +audit_session_close(struct logininfo *li) +{ + const char *t = li->line ? li->line : "(no tty)"; + + debug("audit session close euid %d user %s tty name %s", geteuid(), + audit_username(), t); +} + +/* + * This will be called when a user runs a non-interactive command. Note that + * it may be called multiple times for a single connection since SSH2 allows + * multiple sessions within a single connection. + */ +void +audit_run_command(const char *command) +{ + debug("audit run command euid %d user %s command '%.200s'", geteuid(), + audit_username(), command); +} +# endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ +#endif /* SSH_AUDIT_EVENTS */ diff --git a/aosp/external/openssh/audit.h b/aosp/external/openssh/audit.h new file mode 100644 index 0000000000000000000000000000000000000000..38cb5ad31d4a8bd7bb52da3eeb5d2f8822fcb6fb --- /dev/null +++ b/aosp/external/openssh/audit.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004, 2005 Darren Tucker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SSH_AUDIT_H +# define _SSH_AUDIT_H + +#include "loginrec.h" + +struct ssh; + +enum ssh_audit_event_type { + SSH_LOGIN_EXCEED_MAXTRIES, + SSH_LOGIN_ROOT_DENIED, + SSH_AUTH_SUCCESS, + SSH_AUTH_FAIL_NONE, + SSH_AUTH_FAIL_PASSWD, + SSH_AUTH_FAIL_KBDINT, /* keyboard-interactive or challenge-response */ + SSH_AUTH_FAIL_PUBKEY, /* ssh2 pubkey or ssh1 rsa */ + SSH_AUTH_FAIL_HOSTBASED, /* ssh2 hostbased or ssh1 rhostsrsa */ + SSH_AUTH_FAIL_GSSAPI, + SSH_INVALID_USER, + SSH_NOLOGIN, /* denied by /etc/nologin, not implemented */ + SSH_CONNECTION_CLOSE, /* closed after attempting auth or session */ + SSH_CONNECTION_ABANDON, /* closed without completing auth */ + SSH_AUDIT_UNKNOWN +}; +typedef enum ssh_audit_event_type ssh_audit_event_t; + +void audit_connection_from(const char *, int); +void audit_event(struct ssh *, ssh_audit_event_t); +void audit_session_open(struct logininfo *); +void audit_session_close(struct logininfo *); +void audit_run_command(const char *); +ssh_audit_event_t audit_classify_auth(const char *); + +#endif /* _SSH_AUDIT_H */ diff --git a/aosp/external/openssh/auth-bsdauth.c b/aosp/external/openssh/auth-bsdauth.c new file mode 100644 index 0000000000000000000000000000000000000000..d124e994e776d4907a8742e33bb74aff8a250eb8 --- /dev/null +++ b/aosp/external/openssh/auth-bsdauth.c @@ -0,0 +1,143 @@ +/* $OpenBSD: auth-bsdauth.c,v 1.15 2018/07/09 21:35:50 markus Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#ifdef BSD_AUTH +#include "xmalloc.h" +#include "sshkey.h" +#include "sshbuf.h" +#include "hostfile.h" +#include "auth.h" +#include "log.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" + +static void * +bsdauth_init_ctx(Authctxt *authctxt) +{ + return authctxt; +} + +int +bsdauth_query(void *ctx, char **name, char **infotxt, + u_int *numprompts, char ***prompts, u_int **echo_on) +{ + Authctxt *authctxt = ctx; + char *challenge = NULL; + + *infotxt = NULL; + *numprompts = 0; + *prompts = NULL; + *echo_on = NULL; + + if (authctxt->as != NULL) { + debug2("bsdauth_query: try reuse session"); + challenge = auth_getitem(authctxt->as, AUTHV_CHALLENGE); + if (challenge == NULL) { + auth_close(authctxt->as); + authctxt->as = NULL; + } + } + + if (challenge == NULL) { + debug2("bsdauth_query: new bsd auth session"); + debug3("bsdauth_query: style %s", + authctxt->style ? authctxt->style : ""); + authctxt->as = auth_userchallenge(authctxt->user, + authctxt->style, "auth-ssh", &challenge); + if (authctxt->as == NULL) + challenge = NULL; + debug2("bsdauth_query: <%s>", challenge ? challenge : "empty"); + } + + if (challenge == NULL) + return -1; + + *name = xstrdup(""); + *infotxt = xstrdup(""); + *numprompts = 1; + *prompts = xcalloc(*numprompts, sizeof(char *)); + *echo_on = xcalloc(*numprompts, sizeof(u_int)); + (*prompts)[0] = xstrdup(challenge); + + return 0; +} + +int +bsdauth_respond(void *ctx, u_int numresponses, char **responses) +{ + Authctxt *authctxt = ctx; + int authok; + + if (!authctxt->valid) + return -1; + + if (authctxt->as == NULL) + error("bsdauth_respond: no bsd auth session"); + + if (numresponses != 1) + return -1; + + authok = auth_userresponse(authctxt->as, responses[0], 0); + authctxt->as = NULL; + debug3("bsdauth_respond: <%s> = <%d>", responses[0], authok); + + return (authok == 0) ? -1 : 0; +} + +static void +bsdauth_free_ctx(void *ctx) +{ + Authctxt *authctxt = ctx; + + if (authctxt && authctxt->as) { + auth_close(authctxt->as); + authctxt->as = NULL; + } +} + +KbdintDevice bsdauth_device = { + "bsdauth", + bsdauth_init_ctx, + bsdauth_query, + bsdauth_respond, + bsdauth_free_ctx +}; + +KbdintDevice mm_bsdauth_device = { + "bsdauth", + bsdauth_init_ctx, + mm_bsdauth_query, + mm_bsdauth_respond, + bsdauth_free_ctx +}; +#endif diff --git a/aosp/external/openssh/auth-krb5.c b/aosp/external/openssh/auth-krb5.c new file mode 100644 index 0000000000000000000000000000000000000000..c99e4e430e739fad804d9c858eda40b6b6b65f47 --- /dev/null +++ b/aosp/external/openssh/auth-krb5.c @@ -0,0 +1,273 @@ +/* $OpenBSD: auth-krb5.c,v 1.24 2021/04/03 06:18:40 djm Exp $ */ +/* + * Kerberos v5 authentication and ticket-passing routines. + * + * From: FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar + */ +/* + * Copyright (c) 2002 Daniel Kouril. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include "xmalloc.h" +#include "ssh.h" +#include "packet.h" +#include "log.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "misc.h" +#include "servconf.h" +#include "uidswap.h" +#include "hostfile.h" +#include "auth.h" + +#ifdef KRB5 +#include +#include +#include +#include + +extern ServerOptions options; + +static int +krb5_init(void *context) +{ + Authctxt *authctxt = (Authctxt *)context; + krb5_error_code problem; + + if (authctxt->krb5_ctx == NULL) { + problem = krb5_init_context(&authctxt->krb5_ctx); + if (problem) + return (problem); + } + return (0); +} + +int +auth_krb5_password(Authctxt *authctxt, const char *password) +{ +#ifndef HEIMDAL + krb5_creds creds; + krb5_principal server; +#endif + krb5_error_code problem; + krb5_ccache ccache = NULL; + int len; + char *client, *platform_client; + const char *errmsg; + + /* get platform-specific kerberos client principal name (if it exists) */ + platform_client = platform_krb5_get_principal_name(authctxt->pw->pw_name); + client = platform_client ? platform_client : authctxt->pw->pw_name; + + temporarily_use_uid(authctxt->pw); + + problem = krb5_init(authctxt); + if (problem) + goto out; + + problem = krb5_parse_name(authctxt->krb5_ctx, client, + &authctxt->krb5_user); + if (problem) + goto out; + +#ifdef HEIMDAL +# ifdef HAVE_KRB5_CC_NEW_UNIQUE + problem = krb5_cc_new_unique(authctxt->krb5_ctx, + krb5_mcc_ops.prefix, NULL, &ccache); +# else + problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, &ccache); +# endif + if (problem) + goto out; + + problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, + authctxt->krb5_user); + if (problem) + goto out; + + restore_uid(); + + problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, + ccache, password, 1, NULL); + + temporarily_use_uid(authctxt->pw); + + if (problem) + goto out; + +# ifdef HAVE_KRB5_CC_NEW_UNIQUE + problem = krb5_cc_new_unique(authctxt->krb5_ctx, + krb5_fcc_ops.prefix, NULL, &authctxt->krb5_fwd_ccache); +# else + problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, + &authctxt->krb5_fwd_ccache); +# endif + if (problem) + goto out; + + problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache, + authctxt->krb5_fwd_ccache); + krb5_cc_destroy(authctxt->krb5_ctx, ccache); + ccache = NULL; + if (problem) + goto out; + +#else + problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds, + authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL); + if (problem) + goto out; + + problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, + KRB5_NT_SRV_HST, &server); + if (problem) + goto out; + + restore_uid(); + problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server, + NULL, NULL, NULL); + krb5_free_principal(authctxt->krb5_ctx, server); + temporarily_use_uid(authctxt->pw); + if (problem) + goto out; + + if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, + authctxt->pw->pw_name)) { + problem = -1; + goto out; + } + + problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, + &authctxt->krb5_fwd_ccache); + if (problem) + goto out; + + problem = krb5_cc_initialize(authctxt->krb5_ctx, + authctxt->krb5_fwd_ccache, authctxt->krb5_user); + if (problem) + goto out; + + problem = krb5_cc_store_cred(authctxt->krb5_ctx, + authctxt->krb5_fwd_ccache, &creds); + if (problem) + goto out; +#endif + + authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); + + len = strlen(authctxt->krb5_ticket_file) + 6; + authctxt->krb5_ccname = xmalloc(len); + snprintf(authctxt->krb5_ccname, len, "FILE:%s", + authctxt->krb5_ticket_file); + +#ifdef USE_PAM + if (options.use_pam) + do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname); +#endif + + out: + restore_uid(); + + free(platform_client); + + if (problem) { + if (ccache) + krb5_cc_destroy(authctxt->krb5_ctx, ccache); + + if (authctxt->krb5_ctx != NULL && problem!=-1) { + errmsg = krb5_get_error_message(authctxt->krb5_ctx, + problem); + debug("Kerberos password authentication failed: %s", + errmsg); + krb5_free_error_message(authctxt->krb5_ctx, errmsg); + } else + debug("Kerberos password authentication failed: %d", + problem); + + krb5_cleanup_proc(authctxt); + + if (options.kerberos_or_local_passwd) + return (-1); + else + return (0); + } + return (authctxt->valid ? 1 : 0); +} + +void +krb5_cleanup_proc(Authctxt *authctxt) +{ + debug("krb5_cleanup_proc called"); + if (authctxt->krb5_fwd_ccache) { + krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); + authctxt->krb5_fwd_ccache = NULL; + } + if (authctxt->krb5_user) { + krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); + authctxt->krb5_user = NULL; + } + if (authctxt->krb5_ctx) { + krb5_free_context(authctxt->krb5_ctx); + authctxt->krb5_ctx = NULL; + } +} + +#ifndef HEIMDAL +krb5_error_code +ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { + int tmpfd, ret, oerrno; + char ccname[40]; + mode_t old_umask; + + ret = snprintf(ccname, sizeof(ccname), + "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); + if (ret < 0 || (size_t)ret >= sizeof(ccname)) + return ENOMEM; + + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); + oerrno = errno; + umask(old_umask); + if (tmpfd == -1) { + logit("mkstemp(): %.100s", strerror(oerrno)); + return oerrno; + } + + if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { + oerrno = errno; + logit("fchmod(): %.100s", strerror(oerrno)); + close(tmpfd); + return oerrno; + } + close(tmpfd); + + return (krb5_cc_resolve(ctx, ccname, ccache)); +} +#endif /* !HEIMDAL */ +#endif /* KRB5 */ diff --git a/aosp/external/openssh/auth-options.c b/aosp/external/openssh/auth-options.c new file mode 100644 index 0000000000000000000000000000000000000000..7cb2a640a15b08c7c05a023ebccce70094643903 --- /dev/null +++ b/aosp/external/openssh/auth-options.c @@ -0,0 +1,909 @@ +/* $OpenBSD: auth-options.c,v 1.98 2022/02/08 08:59:12 dtucker Exp $ */ +/* + * Copyright (c) 2018 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" + +#include "xmalloc.h" +#include "ssherr.h" +#include "log.h" +#include "sshbuf.h" +#include "misc.h" +#include "sshkey.h" +#include "match.h" +#include "ssh2.h" +#include "auth-options.h" + +static int +dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc) +{ + char **dst; + size_t i, j; + + *dstp = NULL; + *ndstp = 0; + if (nsrc == 0) + return 0; + + if ((dst = calloc(nsrc, sizeof(*src))) == NULL) + return -1; + for (i = 0; i < nsrc; i++) { + if ((dst[i] = strdup(src[i])) == NULL) { + for (j = 0; j < i; j++) + free(dst[j]); + free(dst); + return -1; + } + } + /* success */ + *dstp = dst; + *ndstp = nsrc; + return 0; +} + +#define OPTIONS_CRITICAL 1 +#define OPTIONS_EXTENSIONS 2 +static int +cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob, + u_int which, int crit) +{ + char *command, *allowed; + char *name = NULL; + struct sshbuf *c = NULL, *data = NULL; + int r, ret = -1, found; + + if ((c = sshbuf_fromb(oblob)) == NULL) { + error_f("sshbuf_fromb failed"); + goto out; + } + + while (sshbuf_len(c) > 0) { + sshbuf_free(data); + data = NULL; + if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || + (r = sshbuf_froms(c, &data)) != 0) { + error_r(r, "Unable to parse certificate options"); + goto out; + } + debug3("found certificate option \"%.100s\" len %zu", + name, sshbuf_len(data)); + found = 0; + if ((which & OPTIONS_EXTENSIONS) != 0) { + if (strcmp(name, "no-touch-required") == 0) { + opts->no_require_user_presence = 1; + found = 1; + } else if (strcmp(name, "permit-X11-forwarding") == 0) { + opts->permit_x11_forwarding_flag = 1; + found = 1; + } else if (strcmp(name, + "permit-agent-forwarding") == 0) { + opts->permit_agent_forwarding_flag = 1; + found = 1; + } else if (strcmp(name, + "permit-port-forwarding") == 0) { + opts->permit_port_forwarding_flag = 1; + found = 1; + } else if (strcmp(name, "permit-pty") == 0) { + opts->permit_pty_flag = 1; + found = 1; + } else if (strcmp(name, "permit-user-rc") == 0) { + opts->permit_user_rc = 1; + found = 1; + } + } + if (!found && (which & OPTIONS_CRITICAL) != 0) { + if (strcmp(name, "verify-required") == 0) { + opts->require_verify = 1; + found = 1; + } else if (strcmp(name, "force-command") == 0) { + if ((r = sshbuf_get_cstring(data, &command, + NULL)) != 0) { + error_r(r, "Unable to parse \"%s\" " + "section", name); + goto out; + } + if (opts->force_command != NULL) { + error("Certificate has multiple " + "force-command options"); + free(command); + goto out; + } + opts->force_command = command; + found = 1; + } else if (strcmp(name, "source-address") == 0) { + if ((r = sshbuf_get_cstring(data, &allowed, + NULL)) != 0) { + error_r(r, "Unable to parse \"%s\" " + "section", name); + goto out; + } + if (opts->required_from_host_cert != NULL) { + error("Certificate has multiple " + "source-address options"); + free(allowed); + goto out; + } + /* Check syntax */ + if (addr_match_cidr_list(NULL, allowed) == -1) { + error("Certificate source-address " + "contents invalid"); + goto out; + } + opts->required_from_host_cert = allowed; + found = 1; + } + } + + if (!found) { + if (crit) { + error("Certificate critical option \"%s\" " + "is not supported", name); + goto out; + } else { + logit("Certificate extension \"%s\" " + "is not supported", name); + } + } else if (sshbuf_len(data) != 0) { + error("Certificate option \"%s\" corrupt " + "(extra data)", name); + goto out; + } + free(name); + name = NULL; + } + /* successfully parsed all options */ + ret = 0; + + out: + free(name); + sshbuf_free(data); + sshbuf_free(c); + return ret; +} + +struct sshauthopt * +sshauthopt_new(void) +{ + struct sshauthopt *ret; + + if ((ret = calloc(1, sizeof(*ret))) == NULL) + return NULL; + ret->force_tun_device = -1; + return ret; +} + +void +sshauthopt_free(struct sshauthopt *opts) +{ + size_t i; + + if (opts == NULL) + return; + + free(opts->cert_principals); + free(opts->force_command); + free(opts->required_from_host_cert); + free(opts->required_from_host_keys); + + for (i = 0; i < opts->nenv; i++) + free(opts->env[i]); + free(opts->env); + + for (i = 0; i < opts->npermitopen; i++) + free(opts->permitopen[i]); + free(opts->permitopen); + + for (i = 0; i < opts->npermitlisten; i++) + free(opts->permitlisten[i]); + free(opts->permitlisten); + + freezero(opts, sizeof(*opts)); +} + +struct sshauthopt * +sshauthopt_new_with_keys_defaults(void) +{ + struct sshauthopt *ret = NULL; + + if ((ret = sshauthopt_new()) == NULL) + return NULL; + + /* Defaults for authorized_keys flags */ + ret->permit_port_forwarding_flag = 1; + ret->permit_agent_forwarding_flag = 1; + ret->permit_x11_forwarding_flag = 1; + ret->permit_pty_flag = 1; + ret->permit_user_rc = 1; + return ret; +} + +/* + * Parse and record a permitopen/permitlisten directive. + * Return 0 on success. Return -1 on failure and sets *errstrp to error reason. + */ +static int +handle_permit(const char **optsp, int allow_bare_port, + char ***permitsp, size_t *npermitsp, const char **errstrp) +{ + char *opt, *tmp, *cp, *host, **permits = *permitsp; + size_t npermits = *npermitsp; + const char *errstr = "unknown error"; + + if (npermits > SSH_AUTHOPT_PERMIT_MAX) { + *errstrp = "too many permission directives"; + return -1; + } + if ((opt = opt_dequote(optsp, &errstr)) == NULL) { + return -1; + } + if (allow_bare_port && strchr(opt, ':') == NULL) { + /* + * Allow a bare port number in permitlisten to indicate a + * listen_host wildcard. + */ + if (asprintf(&tmp, "*:%s", opt) == -1) { + free(opt); + *errstrp = "memory allocation failed"; + return -1; + } + free(opt); + opt = tmp; + } + if ((tmp = strdup(opt)) == NULL) { + free(opt); + *errstrp = "memory allocation failed"; + return -1; + } + cp = tmp; + /* validate syntax before recording it. */ + host = hpdelim2(&cp, NULL); + if (host == NULL || strlen(host) >= NI_MAXHOST) { + free(tmp); + free(opt); + *errstrp = "invalid permission hostname"; + return -1; + } + /* + * don't want to use permitopen_port to avoid + * dependency on channels.[ch] here. + */ + if (cp == NULL || + (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) { + free(tmp); + free(opt); + *errstrp = "invalid permission port"; + return -1; + } + /* XXX - add streamlocal support */ + free(tmp); + /* Record it */ + if ((permits = recallocarray(permits, npermits, npermits + 1, + sizeof(*permits))) == NULL) { + free(opt); + /* NB. don't update *permitsp if alloc fails */ + *errstrp = "memory allocation failed"; + return -1; + } + permits[npermits++] = opt; + *permitsp = permits; + *npermitsp = npermits; + return 0; +} + +struct sshauthopt * +sshauthopt_parse(const char *opts, const char **errstrp) +{ + char **oarray, *opt, *cp, *tmp; + int r; + struct sshauthopt *ret = NULL; + const char *errstr = "unknown error"; + uint64_t valid_before; + size_t i, l; + + if (errstrp != NULL) + *errstrp = NULL; + if ((ret = sshauthopt_new_with_keys_defaults()) == NULL) + goto alloc_fail; + + if (opts == NULL) + return ret; + + while (*opts && *opts != ' ' && *opts != '\t') { + /* flag options */ + if ((r = opt_flag("restrict", 0, &opts)) != -1) { + ret->restricted = 1; + ret->permit_port_forwarding_flag = 0; + ret->permit_agent_forwarding_flag = 0; + ret->permit_x11_forwarding_flag = 0; + ret->permit_pty_flag = 0; + ret->permit_user_rc = 0; + } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { + ret->cert_authority = r; + } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) { + ret->permit_port_forwarding_flag = r == 1; + } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) { + ret->permit_agent_forwarding_flag = r == 1; + } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) { + ret->permit_x11_forwarding_flag = r == 1; + } else if ((r = opt_flag("touch-required", 1, &opts)) != -1) { + ret->no_require_user_presence = r != 1; /* NB. flip */ + } else if ((r = opt_flag("verify-required", 1, &opts)) != -1) { + ret->require_verify = r == 1; + } else if ((r = opt_flag("pty", 1, &opts)) != -1) { + ret->permit_pty_flag = r == 1; + } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) { + ret->permit_user_rc = r == 1; + } else if (opt_match(&opts, "command")) { + if (ret->force_command != NULL) { + errstr = "multiple \"command\" clauses"; + goto fail; + } + ret->force_command = opt_dequote(&opts, &errstr); + if (ret->force_command == NULL) + goto fail; + } else if (opt_match(&opts, "principals")) { + if (ret->cert_principals != NULL) { + errstr = "multiple \"principals\" clauses"; + goto fail; + } + ret->cert_principals = opt_dequote(&opts, &errstr); + if (ret->cert_principals == NULL) + goto fail; + } else if (opt_match(&opts, "from")) { + if (ret->required_from_host_keys != NULL) { + errstr = "multiple \"from\" clauses"; + goto fail; + } + ret->required_from_host_keys = opt_dequote(&opts, + &errstr); + if (ret->required_from_host_keys == NULL) + goto fail; + } else if (opt_match(&opts, "expiry-time")) { + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + if (parse_absolute_time(opt, &valid_before) != 0 || + valid_before == 0) { + free(opt); + errstr = "invalid expires time"; + goto fail; + } + free(opt); + if (ret->valid_before == 0 || + valid_before < ret->valid_before) + ret->valid_before = valid_before; + } else if (opt_match(&opts, "environment")) { + if (ret->nenv > SSH_AUTHOPT_ENV_MAX) { + errstr = "too many environment strings"; + goto fail; + } + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + /* env name must be alphanumeric and followed by '=' */ + if ((tmp = strchr(opt, '=')) == NULL) { + free(opt); + errstr = "invalid environment string"; + goto fail; + } + if ((cp = strdup(opt)) == NULL) { + free(opt); + goto alloc_fail; + } + l = (size_t)(tmp - opt); + cp[l] = '\0'; /* truncate at '=' */ + if (!valid_env_name(cp)) { + free(cp); + free(opt); + errstr = "invalid environment string"; + goto fail; + } + /* Check for duplicates; XXX O(n*log(n)) */ + for (i = 0; i < ret->nenv; i++) { + if (strncmp(ret->env[i], cp, l) == 0 && + ret->env[i][l] == '=') + break; + } + free(cp); + /* First match wins */ + if (i >= ret->nenv) { + /* Append it. */ + oarray = ret->env; + if ((ret->env = recallocarray(ret->env, + ret->nenv, ret->nenv + 1, + sizeof(*ret->env))) == NULL) { + free(opt); + /* put it back for cleanup */ + ret->env = oarray; + goto alloc_fail; + } + ret->env[ret->nenv++] = opt; + opt = NULL; /* transferred */ + } + free(opt); + } else if (opt_match(&opts, "permitopen")) { + if (handle_permit(&opts, 0, &ret->permitopen, + &ret->npermitopen, &errstr) != 0) + goto fail; + } else if (opt_match(&opts, "permitlisten")) { + if (handle_permit(&opts, 1, &ret->permitlisten, + &ret->npermitlisten, &errstr) != 0) + goto fail; + } else if (opt_match(&opts, "tunnel")) { + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + ret->force_tun_device = a2tun(opt, NULL); + free(opt); + if (ret->force_tun_device == SSH_TUNID_ERR) { + errstr = "invalid tun device"; + goto fail; + } + } + /* + * Skip the comma, and move to the next option + * (or break out if there are no more). + */ + if (*opts == '\0' || *opts == ' ' || *opts == '\t') + break; /* End of options. */ + /* Anything other than a comma is an unknown option */ + if (*opts != ',') { + errstr = "unknown key option"; + goto fail; + } + opts++; + if (*opts == '\0') { + errstr = "unexpected end-of-options"; + goto fail; + } + } + + /* success */ + if (errstrp != NULL) + *errstrp = NULL; + return ret; + +alloc_fail: + errstr = "memory allocation failed"; +fail: + sshauthopt_free(ret); + if (errstrp != NULL) + *errstrp = errstr; + return NULL; +} + +struct sshauthopt * +sshauthopt_from_cert(struct sshkey *k) +{ + struct sshauthopt *ret; + + if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL || + k->cert->type != SSH2_CERT_TYPE_USER) + return NULL; + + if ((ret = sshauthopt_new()) == NULL) + return NULL; + + /* Handle options and critical extensions separately */ + if (cert_option_list(ret, k->cert->critical, + OPTIONS_CRITICAL, 1) == -1) { + sshauthopt_free(ret); + return NULL; + } + if (cert_option_list(ret, k->cert->extensions, + OPTIONS_EXTENSIONS, 0) == -1) { + sshauthopt_free(ret); + return NULL; + } + /* success */ + return ret; +} + +/* + * Merges "additional" options to "primary" and returns the result. + * NB. Some options from primary have primacy. + */ +struct sshauthopt * +sshauthopt_merge(const struct sshauthopt *primary, + const struct sshauthopt *additional, const char **errstrp) +{ + struct sshauthopt *ret; + const char *errstr = "internal error"; + const char *tmp; + + if (errstrp != NULL) + *errstrp = NULL; + + if ((ret = sshauthopt_new()) == NULL) + goto alloc_fail; + + /* cert_authority and cert_principals are cleared in result */ + + /* Prefer access lists from primary. */ + /* XXX err is both set and mismatch? */ + tmp = primary->required_from_host_cert; + if (tmp == NULL) + tmp = additional->required_from_host_cert; + if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL) + goto alloc_fail; + tmp = primary->required_from_host_keys; + if (tmp == NULL) + tmp = additional->required_from_host_keys; + if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL) + goto alloc_fail; + + /* + * force_tun_device, permitopen/permitlisten and environment all + * prefer the primary. + */ + ret->force_tun_device = primary->force_tun_device; + if (ret->force_tun_device == -1) + ret->force_tun_device = additional->force_tun_device; + if (primary->nenv > 0) { + if (dup_strings(&ret->env, &ret->nenv, + primary->env, primary->nenv) != 0) + goto alloc_fail; + } else if (additional->nenv) { + if (dup_strings(&ret->env, &ret->nenv, + additional->env, additional->nenv) != 0) + goto alloc_fail; + } + if (primary->npermitopen > 0) { + if (dup_strings(&ret->permitopen, &ret->npermitopen, + primary->permitopen, primary->npermitopen) != 0) + goto alloc_fail; + } else if (additional->npermitopen > 0) { + if (dup_strings(&ret->permitopen, &ret->npermitopen, + additional->permitopen, additional->npermitopen) != 0) + goto alloc_fail; + } + + if (primary->npermitlisten > 0) { + if (dup_strings(&ret->permitlisten, &ret->npermitlisten, + primary->permitlisten, primary->npermitlisten) != 0) + goto alloc_fail; + } else if (additional->npermitlisten > 0) { + if (dup_strings(&ret->permitlisten, &ret->npermitlisten, + additional->permitlisten, additional->npermitlisten) != 0) + goto alloc_fail; + } + +#define OPTFLAG_AND(x) ret->x = (primary->x == 1) && (additional->x == 1) +#define OPTFLAG_OR(x) ret->x = (primary->x == 1) || (additional->x == 1) + /* Permissive flags are logical-AND (i.e. must be set in both) */ + OPTFLAG_AND(permit_port_forwarding_flag); + OPTFLAG_AND(permit_agent_forwarding_flag); + OPTFLAG_AND(permit_x11_forwarding_flag); + OPTFLAG_AND(permit_pty_flag); + OPTFLAG_AND(permit_user_rc); + OPTFLAG_AND(no_require_user_presence); + /* Restrictive flags are logical-OR (i.e. must be set in either) */ + OPTFLAG_OR(require_verify); +#undef OPTFLAG_AND + + /* Earliest expiry time should win */ + if (primary->valid_before != 0) + ret->valid_before = primary->valid_before; + if (additional->valid_before != 0 && + additional->valid_before < ret->valid_before) + ret->valid_before = additional->valid_before; + + /* + * When both multiple forced-command are specified, only + * proceed if they are identical, otherwise fail. + */ + if (primary->force_command != NULL && + additional->force_command != NULL) { + if (strcmp(primary->force_command, + additional->force_command) == 0) { + /* ok */ + ret->force_command = strdup(primary->force_command); + if (ret->force_command == NULL) + goto alloc_fail; + } else { + errstr = "forced command options do not match"; + goto fail; + } + } else if (primary->force_command != NULL) { + if ((ret->force_command = strdup( + primary->force_command)) == NULL) + goto alloc_fail; + } else if (additional->force_command != NULL) { + if ((ret->force_command = strdup( + additional->force_command)) == NULL) + goto alloc_fail; + } + /* success */ + if (errstrp != NULL) + *errstrp = NULL; + return ret; + + alloc_fail: + errstr = "memory allocation failed"; + fail: + if (errstrp != NULL) + *errstrp = errstr; + sshauthopt_free(ret); + return NULL; +} + +/* + * Copy options + */ +struct sshauthopt * +sshauthopt_copy(const struct sshauthopt *orig) +{ + struct sshauthopt *ret; + + if ((ret = sshauthopt_new()) == NULL) + return NULL; + +#define OPTSCALAR(x) ret->x = orig->x + OPTSCALAR(permit_port_forwarding_flag); + OPTSCALAR(permit_agent_forwarding_flag); + OPTSCALAR(permit_x11_forwarding_flag); + OPTSCALAR(permit_pty_flag); + OPTSCALAR(permit_user_rc); + OPTSCALAR(restricted); + OPTSCALAR(cert_authority); + OPTSCALAR(force_tun_device); + OPTSCALAR(valid_before); + OPTSCALAR(no_require_user_presence); + OPTSCALAR(require_verify); +#undef OPTSCALAR +#define OPTSTRING(x) \ + do { \ + if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \ + sshauthopt_free(ret); \ + return NULL; \ + } \ + } while (0) + OPTSTRING(cert_principals); + OPTSTRING(force_command); + OPTSTRING(required_from_host_cert); + OPTSTRING(required_from_host_keys); +#undef OPTSTRING + + if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 || + dup_strings(&ret->permitopen, &ret->npermitopen, + orig->permitopen, orig->npermitopen) != 0 || + dup_strings(&ret->permitlisten, &ret->npermitlisten, + orig->permitlisten, orig->npermitlisten) != 0) { + sshauthopt_free(ret); + return NULL; + } + return ret; +} + +static int +serialise_array(struct sshbuf *m, char **a, size_t n) +{ + struct sshbuf *b; + size_t i; + int r; + + if (n > INT_MAX) + return SSH_ERR_INTERNAL_ERROR; + + if ((b = sshbuf_new()) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } + for (i = 0; i < n; i++) { + if ((r = sshbuf_put_cstring(b, a[i])) != 0) { + sshbuf_free(b); + return r; + } + } + if ((r = sshbuf_put_u32(m, n)) != 0 || + (r = sshbuf_put_stringb(m, b)) != 0) { + sshbuf_free(b); + return r; + } + /* success */ + return 0; +} + +static int +deserialise_array(struct sshbuf *m, char ***ap, size_t *np) +{ + char **a = NULL; + size_t i, n = 0; + struct sshbuf *b = NULL; + u_int tmp; + int r = SSH_ERR_INTERNAL_ERROR; + + if ((r = sshbuf_get_u32(m, &tmp)) != 0 || + (r = sshbuf_froms(m, &b)) != 0) + goto out; + if (tmp > INT_MAX) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + n = tmp; + if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < n; i++) { + if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0) + goto out; + } + /* success */ + r = 0; + *ap = a; + a = NULL; + *np = n; + n = 0; + out: + if (a != NULL) { + for (i = 0; i < n; i++) + free(a[i]); + free(a); + } + sshbuf_free(b); + return r; +} + +static int +serialise_nullable_string(struct sshbuf *m, const char *s) +{ + int r; + + if ((r = sshbuf_put_u8(m, s == NULL)) != 0 || + (r = sshbuf_put_cstring(m, s)) != 0) + return r; + return 0; +} + +static int +deserialise_nullable_string(struct sshbuf *m, char **sp) +{ + int r; + u_char flag; + + *sp = NULL; + if ((r = sshbuf_get_u8(m, &flag)) != 0 || + (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0) + return r; + return 0; +} + +int +sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, + int untrusted) +{ + int r = SSH_ERR_INTERNAL_ERROR; + + /* Flag options */ + if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 || + (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 || + (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 || + (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 || + (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 || + (r = sshbuf_put_u8(m, opts->restricted)) != 0 || + (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 || + (r = sshbuf_put_u8(m, opts->no_require_user_presence)) != 0 || + (r = sshbuf_put_u8(m, opts->require_verify)) != 0) + return r; + + /* Simple integer options */ + if ((r = sshbuf_put_u64(m, opts->valid_before)) != 0) + return r; + + /* tunnel number can be negative to indicate "unset" */ + if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 || + (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ? + 0 : (u_int)opts->force_tun_device)) != 0) + return r; + + /* String options; these may be NULL */ + if ((r = serialise_nullable_string(m, + untrusted ? "yes" : opts->cert_principals)) != 0 || + (r = serialise_nullable_string(m, + untrusted ? "true" : opts->force_command)) != 0 || + (r = serialise_nullable_string(m, + untrusted ? NULL : opts->required_from_host_cert)) != 0 || + (r = serialise_nullable_string(m, + untrusted ? NULL : opts->required_from_host_keys)) != 0) + return r; + + /* Array options */ + if ((r = serialise_array(m, opts->env, + untrusted ? 0 : opts->nenv)) != 0 || + (r = serialise_array(m, opts->permitopen, + untrusted ? 0 : opts->npermitopen)) != 0 || + (r = serialise_array(m, opts->permitlisten, + untrusted ? 0 : opts->npermitlisten)) != 0) + return r; + + /* success */ + return 0; +} + +int +sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp) +{ + struct sshauthopt *opts = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + u_char f; + u_int tmp; + + if ((opts = calloc(1, sizeof(*opts))) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* Flag options */ +#define OPT_FLAG(x) \ + do { \ + if ((r = sshbuf_get_u8(m, &f)) != 0) \ + goto out; \ + opts->x = f; \ + } while (0) + OPT_FLAG(permit_port_forwarding_flag); + OPT_FLAG(permit_agent_forwarding_flag); + OPT_FLAG(permit_x11_forwarding_flag); + OPT_FLAG(permit_pty_flag); + OPT_FLAG(permit_user_rc); + OPT_FLAG(restricted); + OPT_FLAG(cert_authority); + OPT_FLAG(no_require_user_presence); + OPT_FLAG(require_verify); +#undef OPT_FLAG + + /* Simple integer options */ + if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0) + goto out; + + /* tunnel number can be negative to indicate "unset" */ + if ((r = sshbuf_get_u8(m, &f)) != 0 || + (r = sshbuf_get_u32(m, &tmp)) != 0) + goto out; + opts->force_tun_device = f ? -1 : (int)tmp; + + /* String options may be NULL */ + if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 || + (r = deserialise_nullable_string(m, &opts->force_command)) != 0 || + (r = deserialise_nullable_string(m, + &opts->required_from_host_cert)) != 0 || + (r = deserialise_nullable_string(m, + &opts->required_from_host_keys)) != 0) + goto out; + + /* Array options */ + if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 || + (r = deserialise_array(m, + &opts->permitopen, &opts->npermitopen)) != 0 || + (r = deserialise_array(m, + &opts->permitlisten, &opts->npermitlisten)) != 0) + goto out; + + /* success */ + r = 0; + *optsp = opts; + opts = NULL; + out: + sshauthopt_free(opts); + return r; +} diff --git a/aosp/external/openssh/auth-options.h b/aosp/external/openssh/auth-options.h new file mode 100644 index 0000000000000000000000000000000000000000..6e29b727c3d299a5a98420e57f1385e579e93d58 --- /dev/null +++ b/aosp/external/openssh/auth-options.h @@ -0,0 +1,106 @@ +/* $OpenBSD: auth-options.h,v 1.31 2021/07/23 03:57:20 djm Exp $ */ + +/* + * Copyright (c) 2018 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef AUTH_OPTIONS_H +#define AUTH_OPTIONS_H + +struct passwd; +struct sshkey; + +/* Maximum number of permitopen/permitlisten directives to accept */ +#define SSH_AUTHOPT_PERMIT_MAX 4096 + +/* Maximum number of environment directives to accept */ +#define SSH_AUTHOPT_ENV_MAX 1024 + +/* + * sshauthopt represents key options parsed from authorized_keys or + * from certificate extensions/options. + */ +struct sshauthopt { + /* Feature flags */ + int permit_port_forwarding_flag; + int permit_agent_forwarding_flag; + int permit_x11_forwarding_flag; + int permit_pty_flag; + int permit_user_rc; + + /* "restrict" keyword was invoked */ + int restricted; + + /* key/principal expiry date */ + uint64_t valid_before; + + /* Certificate-related options */ + int cert_authority; + char *cert_principals; + + int force_tun_device; + char *force_command; + + /* Custom environment */ + size_t nenv; + char **env; + + /* Permitted port forwardings */ + size_t npermitopen; + char **permitopen; + + /* Permitted listens (remote forwarding) */ + size_t npermitlisten; + char **permitlisten; + + /* + * Permitted host/addresses (comma-separated) + * Caller must check source address matches both lists (if present). + */ + char *required_from_host_cert; + char *required_from_host_keys; + + /* Key requires user presence asserted */ + int no_require_user_presence; + /* Key requires user verification (e.g. PIN) */ + int require_verify; +}; + +struct sshauthopt *sshauthopt_new(void); +struct sshauthopt *sshauthopt_new_with_keys_defaults(void); +void sshauthopt_free(struct sshauthopt *opts); +struct sshauthopt *sshauthopt_copy(const struct sshauthopt *orig); +int sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, int); +int sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **opts); + +/* + * Parse authorized_keys options. Returns an options structure on success + * or NULL on failure. Will set errstr on failure. + */ +struct sshauthopt *sshauthopt_parse(const char *s, const char **errstr); + +/* + * Parse certification options to a struct sshauthopt. + * Returns options on success or NULL on failure. + */ +struct sshauthopt *sshauthopt_from_cert(struct sshkey *k); + +/* + * Merge key options. + */ +struct sshauthopt *sshauthopt_merge(const struct sshauthopt *primary, + const struct sshauthopt *additional, const char **errstrp); + +#endif diff --git a/aosp/external/openssh/auth-pam.c b/aosp/external/openssh/auth-pam.c new file mode 100644 index 0000000000000000000000000000000000000000..29034e40d655e44f43f3f8cc243e66f679aa0e11 --- /dev/null +++ b/aosp/external/openssh/auth-pam.c @@ -0,0 +1,1397 @@ +/*- + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by ThinkSec AS and + * NAI Labs, the Security Research Division of Network Associates, Inc. + * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the + * DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 2003,2004 Damien Miller + * Copyright (c) 2003,2004 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef USE_PAM +#if defined(HAVE_SECURITY_PAM_APPL_H) +#include +#elif defined (HAVE_PAM_PAM_APPL_H) +#include +#endif + +#if !defined(SSHD_PAM_SERVICE) +extern char *__progname; +# define SSHD_PAM_SERVICE __progname +#endif + +/* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ +#ifdef PAM_SUN_CODEBASE +# define sshpam_const /* Solaris, HP-UX, SunOS */ +#else +# define sshpam_const const /* LinuxPAM, OpenPAM, AIX */ +#endif + +/* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ +#ifdef PAM_SUN_CODEBASE +# define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) +#else +# define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) +#endif + +#include "xmalloc.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-pam.h" +#include "canohost.h" +#include "log.h" +#include "msg.h" +#include "packet.h" +#include "misc.h" +#include "servconf.h" +#include "ssh2.h" +#include "auth-options.h" +#include "misc.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" + +extern ServerOptions options; +extern struct sshbuf *loginmsg; +extern u_int utmp_len; + +/* so we don't silently change behaviour */ +#ifdef USE_POSIX_THREADS +# error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK" +#endif + +/* + * Formerly known as USE_POSIX_THREADS, using this is completely unsupported + * and generally a bad idea. Use at own risk and do not expect support if + * this breaks. + */ +#ifdef UNSUPPORTED_POSIX_THREADS_HACK +#include +/* + * Avoid namespace clash when *not* using pthreads for systems *with* + * pthreads, which unconditionally define pthread_t via sys/types.h + * (e.g. Linux) + */ +typedef pthread_t sp_pthread_t; +#else +typedef pid_t sp_pthread_t; +#define pthread_exit fake_pthread_exit +#define pthread_create fake_pthread_create +#define pthread_cancel fake_pthread_cancel +#define pthread_join fake_pthread_join +#endif + +struct pam_ctxt { + sp_pthread_t pam_thread; + int pam_psock; + int pam_csock; + int pam_done; +}; + +static void sshpam_free_ctx(void *); +static struct pam_ctxt *cleanup_ctxt; + +#ifndef UNSUPPORTED_POSIX_THREADS_HACK +/* + * Simulate threads with processes. + */ + +static int sshpam_thread_status = -1; +static sshsig_t sshpam_oldsig; + +static void +sshpam_sigchld_handler(int sig) +{ + ssh_signal(SIGCHLD, SIG_DFL); + if (cleanup_ctxt == NULL) + return; /* handler called after PAM cleanup, shouldn't happen */ + if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) + <= 0) { + /* PAM thread has not exitted, privsep slave must have */ + kill(cleanup_ctxt->pam_thread, SIGTERM); + while (waitpid(cleanup_ctxt->pam_thread, + &sshpam_thread_status, 0) == -1) { + if (errno == EINTR) + continue; + return; + } + } + if (WIFSIGNALED(sshpam_thread_status) && + WTERMSIG(sshpam_thread_status) == SIGTERM) + return; /* terminated by pthread_cancel */ + if (!WIFEXITED(sshpam_thread_status)) + sigdie("PAM: authentication thread exited unexpectedly"); + if (WEXITSTATUS(sshpam_thread_status) != 0) + sigdie("PAM: authentication thread exited uncleanly"); +} + +/* ARGSUSED */ +static void +pthread_exit(void *value) +{ + _exit(0); +} + +/* ARGSUSED */ +static int +pthread_create(sp_pthread_t *thread, const void *attr, + void *(*thread_start)(void *), void *arg) +{ + pid_t pid; + struct pam_ctxt *ctx = arg; + + sshpam_thread_status = -1; + switch ((pid = fork())) { + case -1: + error("fork(): %s", strerror(errno)); + return errno; + case 0: + close(ctx->pam_psock); + ctx->pam_psock = -1; + thread_start(arg); + _exit(1); + default: + *thread = pid; + close(ctx->pam_csock); + ctx->pam_csock = -1; + sshpam_oldsig = ssh_signal(SIGCHLD, sshpam_sigchld_handler); + return (0); + } +} + +static int +pthread_cancel(sp_pthread_t thread) +{ + ssh_signal(SIGCHLD, sshpam_oldsig); + return (kill(thread, SIGTERM)); +} + +/* ARGSUSED */ +static int +pthread_join(sp_pthread_t thread, void **value) +{ + int status; + + if (sshpam_thread_status != -1) + return (sshpam_thread_status); + ssh_signal(SIGCHLD, sshpam_oldsig); + while (waitpid(thread, &status, 0) == -1) { + if (errno == EINTR) + continue; + fatal("%s: waitpid: %s", __func__, strerror(errno)); + } + return (status); +} +#endif + + +static pam_handle_t *sshpam_handle = NULL; +static int sshpam_err = 0; +static int sshpam_authenticated = 0; +static int sshpam_session_open = 0; +static int sshpam_cred_established = 0; +static int sshpam_account_status = -1; +static int sshpam_maxtries_reached = 0; +static char **sshpam_env = NULL; +static Authctxt *sshpam_authctxt = NULL; +static const char *sshpam_password = NULL; +static char *sshpam_rhost = NULL; +static char *sshpam_laddr = NULL; +static char *sshpam_conninfo = NULL; + +/* Some PAM implementations don't implement this */ +#ifndef HAVE_PAM_GETENVLIST +static char ** +pam_getenvlist(pam_handle_t *pamh) +{ + /* + * XXX - If necessary, we can still support environment passing + * for platforms without pam_getenvlist by searching for known + * env vars (e.g. KRB5CCNAME) from the PAM environment. + */ + return NULL; +} +#endif + +#ifndef HAVE_PAM_PUTENV +static int +pam_putenv(pam_handle_t *pamh, const char *name_value) +{ + return PAM_SUCCESS; +} +#endif /* HAVE_PAM_PUTENV */ + +/* + * Some platforms, notably Solaris, do not enforce password complexity + * rules during pam_chauthtok() if the real uid of the calling process + * is 0, on the assumption that it's being called by "passwd" run by root. + * This wraps pam_chauthtok and sets/restore the real uid so PAM will do + * the right thing. + */ +#ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID +static int +sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) +{ + int result; + + if (sshpam_authctxt == NULL) + fatal("PAM: sshpam_authctxt not initialized"); + if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) + fatal("%s: setreuid failed: %s", __func__, strerror(errno)); + result = pam_chauthtok(pamh, flags); + if (setreuid(0, -1) == -1) + fatal("%s: setreuid failed: %s", __func__, strerror(errno)); + return result; +} +# define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) +#endif + +static void +sshpam_password_change_required(int reqd) +{ + extern struct sshauthopt *auth_opts; + static int saved_port, saved_agent, saved_x11; + + debug3("%s %d", __func__, reqd); + if (sshpam_authctxt == NULL) + fatal("%s: PAM authctxt not initialized", __func__); + sshpam_authctxt->force_pwchange = reqd; + if (reqd) { + saved_port = auth_opts->permit_port_forwarding_flag; + saved_agent = auth_opts->permit_agent_forwarding_flag; + saved_x11 = auth_opts->permit_x11_forwarding_flag; + auth_opts->permit_port_forwarding_flag = 0; + auth_opts->permit_agent_forwarding_flag = 0; + auth_opts->permit_x11_forwarding_flag = 0; + } else { + if (saved_port) + auth_opts->permit_port_forwarding_flag = saved_port; + if (saved_agent) + auth_opts->permit_agent_forwarding_flag = saved_agent; + if (saved_x11) + auth_opts->permit_x11_forwarding_flag = saved_x11; + } +} + +/* Import regular and PAM environment from subprocess */ +static void +import_environments(struct sshbuf *b) +{ + char *env; + u_int n, i, num_env; + int r; + + debug3("PAM: %s entering", __func__); + +#ifndef UNSUPPORTED_POSIX_THREADS_HACK + /* Import variables set by do_pam_account */ + if ((r = sshbuf_get_u32(b, &n)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (n > INT_MAX) + fatal("%s: invalid PAM account status %u", __func__, n); + sshpam_account_status = (int)n; + if ((r = sshbuf_get_u32(b, &n)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + sshpam_password_change_required(n != 0); + + /* Import environment from subprocess */ + if ((r = sshbuf_get_u32(b, &num_env)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (num_env > 1024) + fatal("%s: received %u environment variables, expected <= 1024", + __func__, num_env); + sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); + debug3("PAM: num env strings %d", num_env); + for(i = 0; i < num_env; i++) { + if ((r = sshbuf_get_cstring(b, &(sshpam_env[i]), NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + sshpam_env[num_env] = NULL; + + /* Import PAM environment from subprocess */ + if ((r = sshbuf_get_u32(b, &num_env)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + debug("PAM: num PAM env strings %d", num_env); + for (i = 0; i < num_env; i++) { + if ((r = sshbuf_get_cstring(b, &env, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + /* Errors are not fatal here */ + if ((r = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { + error("PAM: pam_putenv: %s", + pam_strerror(sshpam_handle, r)); + } + /* + * XXX this possibly leaks env because it is not documented + * what pam_putenv() does with it. Does it copy it? Does it + * take ownweship? We don't know, so it's safest just to leak. + */ + } +#endif +} + +/* + * Conversation function for authentication thread. + */ +static int +sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + struct sshbuf *buffer; + struct pam_ctxt *ctxt; + struct pam_response *reply; + int r, i; + u_char status; + + debug3("PAM: %s entering, %d messages", __func__, n); + *resp = NULL; + + if (data == NULL) { + error("PAM: conversation function passed a null context"); + return (PAM_CONV_ERR); + } + ctxt = data; + if (n <= 0 || n > PAM_MAX_NUM_MSG) + return (PAM_CONV_ERR); + + if ((reply = calloc(n, sizeof(*reply))) == NULL) + return PAM_CONV_ERR; + if ((buffer = sshbuf_new()) == NULL) { + free(reply); + return PAM_CONV_ERR; + } + + for (i = 0; i < n; ++i) { + switch (PAM_MSG_MEMBER(msg, i, msg_style)) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + if ((r = sshbuf_put_cstring(buffer, + PAM_MSG_MEMBER(msg, i, msg))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + if (ssh_msg_send(ctxt->pam_csock, + PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) + goto fail; + + if (ssh_msg_recv(ctxt->pam_csock, buffer) == -1) + goto fail; + if ((r = sshbuf_get_u8(buffer, &status)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + if (status != PAM_AUTHTOK) + goto fail; + if ((r = sshbuf_get_cstring(buffer, + &reply[i].resp, NULL)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + if ((r = sshbuf_put_cstring(buffer, + PAM_MSG_MEMBER(msg, i, msg))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + if (ssh_msg_send(ctxt->pam_csock, + PAM_MSG_MEMBER(msg, i, msg_style), buffer) == -1) + goto fail; + break; + default: + goto fail; + } + sshbuf_reset(buffer); + } + sshbuf_free(buffer); + *resp = reply; + return (PAM_SUCCESS); + + fail: + for(i = 0; i < n; i++) { + free(reply[i].resp); + } + free(reply); + sshbuf_free(buffer); + return (PAM_CONV_ERR); +} + +/* + * Authentication thread. + */ +static void * +sshpam_thread(void *ctxtp) +{ + struct pam_ctxt *ctxt = ctxtp; + struct sshbuf *buffer = NULL; + struct pam_conv sshpam_conv; + int r, flags = (options.permit_empty_passwd == 0 ? + PAM_DISALLOW_NULL_AUTHTOK : 0); +#ifndef UNSUPPORTED_POSIX_THREADS_HACK + extern char **environ; + char **env_from_pam; + u_int i; + const char *pam_user; + const char **ptr_pam_user = &pam_user; + char *tz = getenv("TZ"); + + sshpam_err = pam_get_item(sshpam_handle, PAM_USER, + (sshpam_const void **)ptr_pam_user); + if (sshpam_err != PAM_SUCCESS) + goto auth_fail; + + environ[0] = NULL; + if (tz != NULL) + if (setenv("TZ", tz, 1) == -1) + error("PAM: could not set TZ environment: %s", + strerror(errno)); + + if (sshpam_authctxt != NULL) { + setproctitle("%s [pam]", + sshpam_authctxt->valid ? pam_user : "unknown"); + } +#endif + + sshpam_conv.conv = sshpam_thread_conv; + sshpam_conv.appdata_ptr = ctxt; + + if (sshpam_authctxt == NULL) + fatal("%s: PAM authctxt not initialized", __func__); + + if ((buffer = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + + sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, + (const void *)&sshpam_conv); + if (sshpam_err != PAM_SUCCESS) + goto auth_fail; + sshpam_err = pam_authenticate(sshpam_handle, flags); + if (sshpam_err == PAM_MAXTRIES) + sshpam_set_maxtries_reached(1); + if (sshpam_err != PAM_SUCCESS) + goto auth_fail; + + if (!do_pam_account()) { + sshpam_err = PAM_ACCT_EXPIRED; + goto auth_fail; + } + if (sshpam_authctxt->force_pwchange) { + sshpam_err = pam_chauthtok(sshpam_handle, + PAM_CHANGE_EXPIRED_AUTHTOK); + if (sshpam_err != PAM_SUCCESS) + goto auth_fail; + sshpam_password_change_required(0); + } + + if ((r = sshbuf_put_cstring(buffer, "OK")) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + +#ifndef UNSUPPORTED_POSIX_THREADS_HACK + /* Export variables set by do_pam_account */ + if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 || + (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + /* Export any environment strings set in child */ + for (i = 0; environ[i] != NULL; i++) { + /* Count */ + if (i > INT_MAX) + fatal("%s: too many environment strings", __func__); + } + if ((r = sshbuf_put_u32(buffer, i)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + for (i = 0; environ[i] != NULL; i++) { + if ((r = sshbuf_put_cstring(buffer, environ[i])) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + /* Export any environment strings set by PAM in child */ + env_from_pam = pam_getenvlist(sshpam_handle); + for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { + /* Count */ + if (i > INT_MAX) + fatal("%s: too many PAM environment strings", __func__); + } + if ((r = sshbuf_put_u32(buffer, i)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + for (i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) { + if ((r = sshbuf_put_cstring(buffer, env_from_pam[i])) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } +#endif /* UNSUPPORTED_POSIX_THREADS_HACK */ + + /* XXX - can't do much about an error here */ + ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer); + sshbuf_free(buffer); + pthread_exit(NULL); + + auth_fail: + if ((r = sshbuf_put_cstring(buffer, + pam_strerror(sshpam_handle, sshpam_err))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + /* XXX - can't do much about an error here */ + if (sshpam_err == PAM_ACCT_EXPIRED) + ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer); + else if (sshpam_maxtries_reached) + ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer); + else + ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, buffer); + sshbuf_free(buffer); + pthread_exit(NULL); + + return (NULL); /* Avoid warning for non-pthread case */ +} + +void +sshpam_thread_cleanup(void) +{ + struct pam_ctxt *ctxt = cleanup_ctxt; + + debug3("PAM: %s entering", __func__); + if (ctxt != NULL && ctxt->pam_thread != 0) { + pthread_cancel(ctxt->pam_thread); + pthread_join(ctxt->pam_thread, NULL); + close(ctxt->pam_psock); + close(ctxt->pam_csock); + memset(ctxt, 0, sizeof(*ctxt)); + cleanup_ctxt = NULL; + } +} + +static int +sshpam_null_conv(int n, sshpam_const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + debug3("PAM: %s entering, %d messages", __func__, n); + return (PAM_CONV_ERR); +} + +static struct pam_conv null_conv = { sshpam_null_conv, NULL }; + +static int +sshpam_store_conv(int n, sshpam_const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + struct pam_response *reply; + int r, i; + + debug3("PAM: %s called with %d messages", __func__, n); + *resp = NULL; + + if (n <= 0 || n > PAM_MAX_NUM_MSG) + return (PAM_CONV_ERR); + + if ((reply = calloc(n, sizeof(*reply))) == NULL) + return (PAM_CONV_ERR); + + for (i = 0; i < n; ++i) { + switch (PAM_MSG_MEMBER(msg, i, msg_style)) { + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + if ((r = sshbuf_putf(loginmsg, "%s\n", + PAM_MSG_MEMBER(msg, i, msg))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + reply[i].resp_retcode = PAM_SUCCESS; + break; + default: + goto fail; + } + } + *resp = reply; + return (PAM_SUCCESS); + + fail: + for(i = 0; i < n; i++) { + free(reply[i].resp); + } + free(reply); + return (PAM_CONV_ERR); +} + +static struct pam_conv store_conv = { sshpam_store_conv, NULL }; + +void +sshpam_cleanup(void) +{ + if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor())) + return; + debug("PAM: cleanup"); + pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); + if (sshpam_session_open) { + debug("PAM: closing session"); + pam_close_session(sshpam_handle, PAM_SILENT); + sshpam_session_open = 0; + } + if (sshpam_cred_established) { + debug("PAM: deleting credentials"); + pam_setcred(sshpam_handle, PAM_DELETE_CRED); + sshpam_cred_established = 0; + } + sshpam_authenticated = 0; + pam_end(sshpam_handle, sshpam_err); + sshpam_handle = NULL; +} + +static int +sshpam_init(struct ssh *ssh, Authctxt *authctxt) +{ + const char *pam_user, *user = authctxt->user; + const char **ptr_pam_user = &pam_user; + +#if defined(PAM_SUN_CODEBASE) && defined(PAM_MAX_RESP_SIZE) + /* Protect buggy PAM implementations from excessively long usernames */ + if (strlen(user) >= PAM_MAX_RESP_SIZE) + fatal("Username too long from %s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); +#endif + if (sshpam_handle == NULL) { + if (ssh == NULL) { + fatal("%s: called initially with no " + "packet context", __func__); + } + } if (sshpam_handle != NULL) { + /* We already have a PAM context; check if the user matches */ + sshpam_err = pam_get_item(sshpam_handle, + PAM_USER, (sshpam_const void **)ptr_pam_user); + if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) + return (0); + pam_end(sshpam_handle, sshpam_err); + sshpam_handle = NULL; + } + debug("PAM: initializing for \"%s\"", user); + sshpam_err = + pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); + sshpam_authctxt = authctxt; + + if (sshpam_err != PAM_SUCCESS) { + pam_end(sshpam_handle, sshpam_err); + sshpam_handle = NULL; + return (-1); + } + + if (ssh != NULL && sshpam_rhost == NULL) { + /* + * We need to cache these as we don't have packet context + * during the kbdint flow. + */ + sshpam_rhost = xstrdup(auth_get_canonical_hostname(ssh, + options.use_dns)); + sshpam_laddr = get_local_ipaddr( + ssh_packet_get_connection_in(ssh)); + xasprintf(&sshpam_conninfo, "SSH_CONNECTION=%.50s %d %.50s %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + sshpam_laddr, ssh_local_port(ssh)); + } + if (sshpam_rhost != NULL) { + debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost); + sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, + sshpam_rhost); + if (sshpam_err != PAM_SUCCESS) { + pam_end(sshpam_handle, sshpam_err); + sshpam_handle = NULL; + return (-1); + } + /* Put SSH_CONNECTION in the PAM environment too */ + pam_putenv(sshpam_handle, sshpam_conninfo); + } + +#ifdef PAM_TTY_KLUDGE + /* + * Some silly PAM modules (e.g. pam_time) require a TTY to operate. + * sshd doesn't set the tty until too late in the auth process and + * may not even set one (for tty-less connections) + */ + debug("PAM: setting PAM_TTY to \"ssh\""); + sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); + if (sshpam_err != PAM_SUCCESS) { + pam_end(sshpam_handle, sshpam_err); + sshpam_handle = NULL; + return (-1); + } +#endif + return (0); +} + +static void +expose_authinfo(const char *caller) +{ + char *auth_info; + + /* + * Expose authentication information to PAM. + * The environment variable is versioned. Please increment the + * version suffix if the format of session_info changes. + */ + if (sshpam_authctxt->session_info == NULL) + auth_info = xstrdup(""); + else if ((auth_info = sshbuf_dup_string( + sshpam_authctxt->session_info)) == NULL) + fatal("%s: sshbuf_dup_string failed", __func__); + + debug2("%s: auth information in SSH_AUTH_INFO_0", caller); + do_pam_putenv("SSH_AUTH_INFO_0", auth_info); + free(auth_info); +} + +static void * +sshpam_init_ctx(Authctxt *authctxt) +{ + struct pam_ctxt *ctxt; + int result, socks[2]; + + debug3("PAM: %s entering", __func__); + /* + * Refuse to start if we don't have PAM enabled or do_pam_account + * has previously failed. + */ + if (!options.use_pam || sshpam_account_status == 0) + return NULL; + + /* Initialize PAM */ + if (sshpam_init(NULL, authctxt) == -1) { + error("PAM: initialization failed"); + return (NULL); + } + + expose_authinfo(__func__); + ctxt = xcalloc(1, sizeof *ctxt); + + /* Start the authentication thread */ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { + error("PAM: failed create sockets: %s", strerror(errno)); + free(ctxt); + return (NULL); + } + ctxt->pam_psock = socks[0]; + ctxt->pam_csock = socks[1]; + result = pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt); + if (result != 0) { + error("PAM: failed to start authentication thread: %s", + strerror(result)); + close(socks[0]); + close(socks[1]); + free(ctxt); + return (NULL); + } + cleanup_ctxt = ctxt; + return (ctxt); +} + +static int +sshpam_query(void *ctx, char **name, char **info, + u_int *num, char ***prompts, u_int **echo_on) +{ + struct sshbuf *buffer; + struct pam_ctxt *ctxt = ctx; + size_t plen; + u_char type; + char *msg; + size_t len, mlen; + int r; + + debug3("PAM: %s entering", __func__); + if ((buffer = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + *name = xstrdup(""); + *info = xstrdup(""); + *prompts = xmalloc(sizeof(char *)); + **prompts = NULL; + plen = 0; + *echo_on = xmalloc(sizeof(u_int)); + while (ssh_msg_recv(ctxt->pam_psock, buffer) == 0) { + if ((r = sshbuf_get_u8(buffer, &type)) != 0 || + (r = sshbuf_get_cstring(buffer, &msg, &mlen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + switch (type) { + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: + *num = 1; + len = plen + mlen + 1; + **prompts = xreallocarray(**prompts, 1, len); + strlcpy(**prompts + plen, msg, len - plen); + plen += mlen; + **echo_on = (type == PAM_PROMPT_ECHO_ON); + free(msg); + sshbuf_free(buffer); + return (0); + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + /* accumulate messages */ + len = plen + mlen + 2; + **prompts = xreallocarray(**prompts, 1, len); + strlcpy(**prompts + plen, msg, len - plen); + plen += mlen; + strlcat(**prompts + plen, "\n", len - plen); + plen++; + free(msg); + break; + case PAM_ACCT_EXPIRED: + case PAM_MAXTRIES: + if (type == PAM_ACCT_EXPIRED) + sshpam_account_status = 0; + if (type == PAM_MAXTRIES) + sshpam_set_maxtries_reached(1); + /* FALLTHROUGH */ + case PAM_AUTH_ERR: + debug3("PAM: %s", pam_strerror(sshpam_handle, type)); + if (**prompts != NULL && strlen(**prompts) != 0) { + free(*info); + *info = **prompts; + **prompts = NULL; + *num = 0; + **echo_on = 0; + ctxt->pam_done = -1; + free(msg); + sshbuf_free(buffer); + return 0; + } + /* FALLTHROUGH */ + case PAM_SUCCESS: + if (**prompts != NULL) { + /* drain any accumulated messages */ + debug("PAM: %s", **prompts); + if ((r = sshbuf_put(loginmsg, **prompts, + strlen(**prompts))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + free(**prompts); + **prompts = NULL; + } + if (type == PAM_SUCCESS) { + if (!sshpam_authctxt->valid || + (sshpam_authctxt->pw->pw_uid == 0 && + options.permit_root_login != PERMIT_YES)) + fatal("Internal error: PAM auth " + "succeeded when it should have " + "failed"); + import_environments(buffer); + *num = 0; + **echo_on = 0; + ctxt->pam_done = 1; + free(msg); + sshbuf_free(buffer); + return (0); + } + error("PAM: %s for %s%.100s from %.100s", msg, + sshpam_authctxt->valid ? "" : "illegal user ", + sshpam_authctxt->user, sshpam_rhost); + /* FALLTHROUGH */ + default: + *num = 0; + **echo_on = 0; + free(msg); + ctxt->pam_done = -1; + sshbuf_free(buffer); + return (-1); + } + } + sshbuf_free(buffer); + return (-1); +} + +/* + * Returns a junk password of identical length to that the user supplied. + * Used to mitigate timing attacks against crypt(3)/PAM stacks that + * vary processing time in proportion to password length. + */ +static char * +fake_password(const char *wire_password) +{ + const char junk[] = "\b\n\r\177INCORRECT"; + char *ret = NULL; + size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; + + if (l >= INT_MAX) + fatal("%s: password length too long: %zu", __func__, l); + + ret = malloc(l + 1); + if (ret == NULL) + return NULL; + for (i = 0; i < l; i++) + ret[i] = junk[i % (sizeof(junk) - 1)]; + ret[i] = '\0'; + return ret; +} + +/* XXX - see also comment in auth-chall.c:verify_response */ +static int +sshpam_respond(void *ctx, u_int num, char **resp) +{ + struct sshbuf *buffer; + struct pam_ctxt *ctxt = ctx; + char *fake; + int r; + + debug2("PAM: %s entering, %u responses", __func__, num); + switch (ctxt->pam_done) { + case 1: + sshpam_authenticated = 1; + return (0); + case 0: + break; + default: + return (-1); + } + if (num != 1) { + error("PAM: expected one response, got %u", num); + return (-1); + } + if ((buffer = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if (sshpam_authctxt->valid && + (sshpam_authctxt->pw->pw_uid != 0 || + options.permit_root_login == PERMIT_YES)) { + if ((r = sshbuf_put_cstring(buffer, *resp)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } else { + fake = fake_password(*resp); + if ((r = sshbuf_put_cstring(buffer, fake)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + free(fake); + } + if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, buffer) == -1) { + sshbuf_free(buffer); + return (-1); + } + sshbuf_free(buffer); + return (1); +} + +static void +sshpam_free_ctx(void *ctxtp) +{ + struct pam_ctxt *ctxt = ctxtp; + + debug3("PAM: %s entering", __func__); + sshpam_thread_cleanup(); + free(ctxt); + /* + * We don't call sshpam_cleanup() here because we may need the PAM + * handle at a later stage, e.g. when setting up a session. It's + * still on the cleanup list, so pam_end() *will* be called before + * the server process terminates. + */ +} + +KbdintDevice sshpam_device = { + "pam", + sshpam_init_ctx, + sshpam_query, + sshpam_respond, + sshpam_free_ctx +}; + +KbdintDevice mm_sshpam_device = { + "pam", + mm_sshpam_init_ctx, + mm_sshpam_query, + mm_sshpam_respond, + mm_sshpam_free_ctx +}; + +/* + * This replaces auth-pam.c + */ +void +start_pam(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + + if (!options.use_pam) + fatal("PAM: initialisation requested when UsePAM=no"); + + if (sshpam_init(ssh, authctxt) == -1) + fatal("PAM: initialisation failed"); +} + +void +finish_pam(void) +{ + sshpam_cleanup(); +} + + +u_int +do_pam_account(void) +{ + debug("%s: called", __func__); + if (sshpam_account_status != -1) + return (sshpam_account_status); + + expose_authinfo(__func__); + + sshpam_err = pam_acct_mgmt(sshpam_handle, 0); + debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, + pam_strerror(sshpam_handle, sshpam_err)); + + if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { + sshpam_account_status = 0; + return (sshpam_account_status); + } + + if (sshpam_err == PAM_NEW_AUTHTOK_REQD) + sshpam_password_change_required(1); + + sshpam_account_status = 1; + return (sshpam_account_status); +} + +void +do_pam_setcred(int init) +{ + sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, + (const void *)&store_conv); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: failed to set PAM_CONV: %s", + pam_strerror(sshpam_handle, sshpam_err)); + if (init) { + debug("PAM: establishing credentials"); + sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); + } else { + debug("PAM: reinitializing credentials"); + sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); + } + if (sshpam_err == PAM_SUCCESS) { + sshpam_cred_established = 1; + return; + } + if (sshpam_authenticated) + fatal("PAM: pam_setcred(): %s", + pam_strerror(sshpam_handle, sshpam_err)); + else + debug("PAM: pam_setcred(): %s", + pam_strerror(sshpam_handle, sshpam_err)); +} + +static int +sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + char input[PAM_MAX_MSG_SIZE]; + struct pam_response *reply; + int i; + + debug3("PAM: %s called with %d messages", __func__, n); + + *resp = NULL; + + if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) + return (PAM_CONV_ERR); + + if ((reply = calloc(n, sizeof(*reply))) == NULL) + return (PAM_CONV_ERR); + + for (i = 0; i < n; ++i) { + switch (PAM_MSG_MEMBER(msg, i, msg_style)) { + case PAM_PROMPT_ECHO_OFF: + reply[i].resp = + read_passphrase(PAM_MSG_MEMBER(msg, i, msg), + RP_ALLOW_STDIN); + reply[i].resp_retcode = PAM_SUCCESS; + break; + case PAM_PROMPT_ECHO_ON: + fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); + if (fgets(input, sizeof input, stdin) == NULL) + input[0] = '\0'; + if ((reply[i].resp = strdup(input)) == NULL) + goto fail; + reply[i].resp_retcode = PAM_SUCCESS; + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); + reply[i].resp_retcode = PAM_SUCCESS; + break; + default: + goto fail; + } + } + *resp = reply; + return (PAM_SUCCESS); + + fail: + for(i = 0; i < n; i++) { + free(reply[i].resp); + } + free(reply); + return (PAM_CONV_ERR); +} + +static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; + +/* + * XXX this should be done in the authentication phase, but ssh1 doesn't + * support that + */ +void +do_pam_chauthtok(void) +{ + if (use_privsep) + fatal("Password expired (unable to change with privsep)"); + sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, + (const void *)&tty_conv); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: failed to set PAM_CONV: %s", + pam_strerror(sshpam_handle, sshpam_err)); + debug("PAM: changing password"); + sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: pam_chauthtok(): %s", + pam_strerror(sshpam_handle, sshpam_err)); +} + +void +do_pam_session(struct ssh *ssh) +{ + debug3("PAM: opening session"); + + expose_authinfo(__func__); + + sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, + (const void *)&store_conv); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: failed to set PAM_CONV: %s", + pam_strerror(sshpam_handle, sshpam_err)); + sshpam_err = pam_open_session(sshpam_handle, 0); + if (sshpam_err == PAM_SUCCESS) + sshpam_session_open = 1; + else { + sshpam_session_open = 0; + auth_restrict_session(ssh); + error("PAM: pam_open_session(): %s", + pam_strerror(sshpam_handle, sshpam_err)); + } + +} + +int +is_pam_session_open(void) +{ + return sshpam_session_open; +} + +/* + * Set a PAM environment string. We need to do this so that the session + * modules can handle things like Kerberos/GSI credentials that appear + * during the ssh authentication process. + */ +int +do_pam_putenv(char *name, char *value) +{ + int ret = 1; + char *compound; + size_t len; + + len = strlen(name) + strlen(value) + 2; + compound = xmalloc(len); + + snprintf(compound, len, "%s=%s", name, value); + ret = pam_putenv(sshpam_handle, compound); + free(compound); + + return (ret); +} + +char ** +fetch_pam_child_environment(void) +{ + return sshpam_env; +} + +char ** +fetch_pam_environment(void) +{ + return (pam_getenvlist(sshpam_handle)); +} + +void +free_pam_environment(char **env) +{ + char **envp; + + if (env == NULL) + return; + + for (envp = env; *envp; envp++) + free(*envp); + free(env); +} + +/* + * "Blind" conversation function for password authentication. Assumes that + * echo-off prompts are for the password and stores messages for later + * display. + */ +static int +sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + struct pam_response *reply; + int r, i; + size_t len; + + debug3("PAM: %s called with %d messages", __func__, n); + + *resp = NULL; + + if (n <= 0 || n > PAM_MAX_NUM_MSG) + return (PAM_CONV_ERR); + + if ((reply = calloc(n, sizeof(*reply))) == NULL) + return (PAM_CONV_ERR); + + for (i = 0; i < n; ++i) { + switch (PAM_MSG_MEMBER(msg, i, msg_style)) { + case PAM_PROMPT_ECHO_OFF: + if (sshpam_password == NULL) + goto fail; + if ((reply[i].resp = strdup(sshpam_password)) == NULL) + goto fail; + reply[i].resp_retcode = PAM_SUCCESS; + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + len = strlen(PAM_MSG_MEMBER(msg, i, msg)); + if (len > 0) { + if ((r = sshbuf_putf(loginmsg, "%s\n", + PAM_MSG_MEMBER(msg, i, msg))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + } + if ((reply[i].resp = strdup("")) == NULL) + goto fail; + reply[i].resp_retcode = PAM_SUCCESS; + break; + default: + goto fail; + } + } + *resp = reply; + return (PAM_SUCCESS); + + fail: + for(i = 0; i < n; i++) { + free(reply[i].resp); + } + free(reply); + return (PAM_CONV_ERR); +} + +static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; + +/* + * Attempt password authentication via PAM + */ +int +sshpam_auth_passwd(Authctxt *authctxt, const char *password) +{ + int flags = (options.permit_empty_passwd == 0 ? + PAM_DISALLOW_NULL_AUTHTOK : 0); + char *fake = NULL; + + if (!options.use_pam || sshpam_handle == NULL) + fatal("PAM: %s called when PAM disabled or failed to " + "initialise.", __func__); + + sshpam_password = password; + sshpam_authctxt = authctxt; + + /* + * If the user logging in is invalid, or is root but is not permitted + * by PermitRootLogin, use an invalid password to prevent leaking + * information via timing (eg if the PAM config has a delay on fail). + */ + if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && + options.permit_root_login != PERMIT_YES)) + sshpam_password = fake = fake_password(password); + + sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, + (const void *)&passwd_conv); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, + pam_strerror(sshpam_handle, sshpam_err)); + + sshpam_err = pam_authenticate(sshpam_handle, flags); + sshpam_password = NULL; + free(fake); + if (sshpam_err == PAM_MAXTRIES) + sshpam_set_maxtries_reached(1); + if (sshpam_err == PAM_SUCCESS && authctxt->valid) { + debug("PAM: password authentication accepted for %.100s", + authctxt->user); + return 1; + } else { + debug("PAM: password authentication failed for %.100s: %s", + authctxt->valid ? authctxt->user : "an illegal user", + pam_strerror(sshpam_handle, sshpam_err)); + return 0; + } +} + +int +sshpam_get_maxtries_reached(void) +{ + return sshpam_maxtries_reached; +} + +void +sshpam_set_maxtries_reached(int reached) +{ + if (reached == 0 || sshpam_maxtries_reached) + return; + sshpam_maxtries_reached = 1; + options.password_authentication = 0; + options.kbd_interactive_authentication = 0; +} +#endif /* USE_PAM */ diff --git a/aosp/external/openssh/auth-pam.h b/aosp/external/openssh/auth-pam.h new file mode 100644 index 0000000000000000000000000000000000000000..9fcea270faece3449f1f56e34d9cd5e55fcd0b4d --- /dev/null +++ b/aosp/external/openssh/auth-pam.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2000 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +#ifdef USE_PAM + +struct ssh; + +void start_pam(struct ssh *); +void finish_pam(void); +u_int do_pam_account(void); +void do_pam_session(struct ssh *); +void do_pam_setcred(int ); +void do_pam_chauthtok(void); +int do_pam_putenv(char *, char *); +char ** fetch_pam_environment(void); +char ** fetch_pam_child_environment(void); +void free_pam_environment(char **); +void sshpam_thread_cleanup(void); +void sshpam_cleanup(void); +int sshpam_auth_passwd(Authctxt *, const char *); +int sshpam_get_maxtries_reached(void); +void sshpam_set_maxtries_reached(int); +int is_pam_session_open(void); + +#endif /* USE_PAM */ diff --git a/aosp/external/openssh/auth-passwd.c b/aosp/external/openssh/auth-passwd.c new file mode 100644 index 0000000000000000000000000000000000000000..347d91e251926f31a1b72db2ac4574bc76121599 --- /dev/null +++ b/aosp/external/openssh/auth-passwd.c @@ -0,0 +1,223 @@ +/* $OpenBSD: auth-passwd.c,v 1.48 2020/10/18 11:32:01 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Password authentication. This file contains the functions to check whether + * the password is valid for the user. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 1999 Dug Song. All rights reserved. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#include "packet.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-options.h" + +extern struct sshbuf *loginmsg; +extern ServerOptions options; + +#ifdef HAVE_LOGIN_CAP +extern login_cap_t *lc; +#endif + + +#define DAY (24L * 60 * 60) /* 1 day in seconds */ +#define TWO_WEEKS (2L * 7 * DAY) /* 2 weeks in seconds */ + +#define MAX_PASSWORD_LEN 1024 + +/* + * Tries to authenticate the user using password. Returns true if + * authentication succeeds. + */ +int +auth_password(struct ssh *ssh, const char *password) +{ + Authctxt *authctxt = ssh->authctxt; + struct passwd *pw = authctxt->pw; + int result, ok = authctxt->valid; +#if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) + static int expire_checked = 0; +#endif + + if (strlen(password) > MAX_PASSWORD_LEN) + return 0; + +#ifndef HAVE_CYGWIN + if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES) + ok = 0; +#endif + if (*password == '\0' && options.permit_empty_passwd == 0) + return 0; + +#ifdef KRB5 + if (options.kerberos_authentication == 1) { + int ret = auth_krb5_password(authctxt, password); + if (ret == 1 || ret == 0) + return ret && ok; + /* Fall back to ordinary passwd authentication. */ + } +#endif +#ifdef HAVE_CYGWIN + { + HANDLE hToken = cygwin_logon_user(pw, password); + + if (hToken == INVALID_HANDLE_VALUE) + return 0; + cygwin_set_impersonation_token(hToken); + return ok; + } +#endif +#ifdef USE_PAM + if (options.use_pam) + return (sshpam_auth_passwd(authctxt, password) && ok); +#endif +#if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) + if (!expire_checked) { + expire_checked = 1; + if (auth_shadow_pwexpired(authctxt)) + authctxt->force_pwchange = 1; + } +#endif + result = sys_auth_passwd(ssh, password); + if (authctxt->force_pwchange) + auth_restrict_session(ssh); + return (result && ok); +} + +#ifdef BSD_AUTH +static void +warn_expiry(Authctxt *authctxt, auth_session_t *as) +{ + int r; + quad_t pwtimeleft, actimeleft, daysleft, pwwarntime, acwarntime; + + pwwarntime = acwarntime = TWO_WEEKS; + + pwtimeleft = auth_check_change(as); + actimeleft = auth_check_expire(as); +#ifdef HAVE_LOGIN_CAP + if (authctxt->valid) { + pwwarntime = login_getcaptime(lc, "password-warn", TWO_WEEKS, + TWO_WEEKS); + acwarntime = login_getcaptime(lc, "expire-warn", TWO_WEEKS, + TWO_WEEKS); + } +#endif + if (pwtimeleft != 0 && pwtimeleft < pwwarntime) { + daysleft = pwtimeleft / DAY + 1; + if ((r = sshbuf_putf(loginmsg, + "Your password will expire in %lld day%s.\n", + daysleft, daysleft == 1 ? "" : "s")) != 0) + fatal_fr(r, "buffer error"); + } + if (actimeleft != 0 && actimeleft < acwarntime) { + daysleft = actimeleft / DAY + 1; + if ((r = sshbuf_putf(loginmsg, + "Your account will expire in %lld day%s.\n", + daysleft, daysleft == 1 ? "" : "s")) != 0) + fatal_fr(r, "buffer error"); + } +} + +int +sys_auth_passwd(struct ssh *ssh, const char *password) +{ + Authctxt *authctxt = ssh->authctxt; + auth_session_t *as; + static int expire_checked = 0; + + as = auth_usercheck(authctxt->pw->pw_name, authctxt->style, "auth-ssh", + (char *)password); + if (as == NULL) + return (0); + if (auth_getstate(as) & AUTH_PWEXPIRED) { + auth_close(as); + auth_restrict_session(ssh); + authctxt->force_pwchange = 1; + return (1); + } else { + if (!expire_checked) { + expire_checked = 1; + warn_expiry(authctxt, as); + } + return (auth_close(as)); + } +} +#elif !defined(CUSTOM_SYS_AUTH_PASSWD) +int +sys_auth_passwd(struct ssh *ssh, const char *password) +{ + Authctxt *authctxt = ssh->authctxt; + struct passwd *pw = authctxt->pw; + char *encrypted_password, *salt = NULL; + + /* Just use the supplied fake password if authctxt is invalid */ + char *pw_password = authctxt->valid ? shadow_pw(pw) : pw->pw_passwd; + + if (pw_password == NULL) + return 0; + + /* Check for users with no password. */ + if (strcmp(pw_password, "") == 0 && strcmp(password, "") == 0) + return (1); + + /* + * Encrypt the candidate password using the proper salt, or pass a + * NULL and let xcrypt pick one. + */ + if (authctxt->valid && pw_password[0] && pw_password[1]) + salt = pw_password; + encrypted_password = xcrypt(password, salt); + + /* + * Authentication is accepted if the encrypted passwords + * are identical. + */ + return encrypted_password != NULL && + strcmp(encrypted_password, pw_password) == 0; +} +#endif diff --git a/aosp/external/openssh/auth-rhosts.c b/aosp/external/openssh/auth-rhosts.c new file mode 100644 index 0000000000000000000000000000000000000000..4fc9252a6b617b8f797218faf8dd3be5125911a1 --- /dev/null +++ b/aosp/external/openssh/auth-rhosts.c @@ -0,0 +1,336 @@ +/* $OpenBSD: auth-rhosts.c,v 1.56 2022/02/23 21:21:49 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Rhosts authentication. This file contains code to check whether to admit + * the login based on rhosts authentication. This file also processes + * /etc/hosts.equiv. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include + +#include +#ifdef HAVE_NETGROUP_H +# include +#endif +#include +#include +#include +#include +#include +#include + +#include "packet.h" +#include "uidswap.h" +#include "pathnames.h" +#include "log.h" +#include "misc.h" +#include "xmalloc.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "servconf.h" +#include "canohost.h" +#include "hostfile.h" +#include "auth.h" + +/* import */ +extern ServerOptions options; +extern int use_privsep; + +/* + * This function processes an rhosts-style file (.rhosts, .shosts, or + * /etc/hosts.equiv). This returns true if authentication can be granted + * based on the file, and returns zero otherwise. + */ + +static int +check_rhosts_file(const char *filename, const char *hostname, + const char *ipaddr, const char *client_user, + const char *server_user) +{ + FILE *f; +#define RBUFLN 1024 + char buf[RBUFLN];/* Must not be larger than host, user, dummy below. */ + int fd; + struct stat st; + + /* Open the .rhosts file, deny if unreadable */ + if ((fd = open(filename, O_RDONLY|O_NONBLOCK)) == -1) + return 0; + if (fstat(fd, &st) == -1) { + close(fd); + return 0; + } + if (!S_ISREG(st.st_mode)) { + logit("User %s hosts file %s is not a regular file", + server_user, filename); + close(fd); + return 0; + } + unset_nonblock(fd); + if ((f = fdopen(fd, "r")) == NULL) { + close(fd); + return 0; + } + while (fgets(buf, sizeof(buf), f)) { + /* All three must have length >= buf to avoid overflows. */ + char hostbuf[RBUFLN], userbuf[RBUFLN], dummy[RBUFLN]; + char *host, *user, *cp; + int negated; + + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) + ; + if (*cp == '#' || *cp == '\n' || !*cp) + continue; + + /* + * NO_PLUS is supported at least on OSF/1. We skip it (we + * don't ever support the plus syntax). + */ + if (strncmp(cp, "NO_PLUS", 7) == 0) + continue; + + /* + * This should be safe because each buffer is as big as the + * whole string, and thus cannot be overwritten. + */ + switch (sscanf(buf, "%1023s %1023s %1023s", hostbuf, userbuf, + dummy)) { + case 0: + auth_debug_add("Found empty line in %.100s.", filename); + continue; + case 1: + /* Host name only. */ + strlcpy(userbuf, server_user, sizeof(userbuf)); + break; + case 2: + /* Got both host and user name. */ + break; + case 3: + auth_debug_add("Found garbage in %.100s.", filename); + continue; + default: + /* Weird... */ + continue; + } + + host = hostbuf; + user = userbuf; + negated = 0; + + /* Process negated host names, or positive netgroups. */ + if (host[0] == '-') { + negated = 1; + host++; + } else if (host[0] == '+') + host++; + + if (user[0] == '-') { + negated = 1; + user++; + } else if (user[0] == '+') + user++; + + /* Check for empty host/user names (particularly '+'). */ + if (!host[0] || !user[0]) { + /* We come here if either was '+' or '-'. */ + auth_debug_add("Ignoring wild host/user names " + "in %.100s.", filename); + continue; + } + /* Verify that host name matches. */ + if (host[0] == '@') { + if (!innetgr(host + 1, hostname, NULL, NULL) && + !innetgr(host + 1, ipaddr, NULL, NULL)) + continue; + } else if (strcasecmp(host, hostname) && + strcmp(host, ipaddr) != 0) + continue; /* Different hostname. */ + + /* Verify that user name matches. */ + if (user[0] == '@') { + if (!innetgr(user + 1, NULL, client_user, NULL)) + continue; + } else if (strcmp(user, client_user) != 0) + continue; /* Different username. */ + + /* Found the user and host. */ + fclose(f); + + /* If the entry was negated, deny access. */ + if (negated) { + auth_debug_add("Matched negative entry in %.100s.", + filename); + return 0; + } + /* Accept authentication. */ + return 1; + } + + /* Authentication using this file denied. */ + fclose(f); + return 0; +} + +/* + * Tries to authenticate the user using the .shosts or .rhosts file. Returns + * true if authentication succeeds. If ignore_rhosts is true, only + * /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored). + */ +int +auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, + const char *ipaddr) +{ + char *path = NULL; + struct stat st; + static const char * const rhosts_files[] = {".shosts", ".rhosts", NULL}; + u_int rhosts_file_index; + int r; + + debug2_f("clientuser %s hostname %s ipaddr %s", + client_user, hostname, ipaddr); + + /* Switch to the user's uid. */ + temporarily_use_uid(pw); + /* + * Quick check: if the user has no .shosts or .rhosts files and + * no system hosts.equiv/shosts.equiv files exist then return + * failure immediately without doing costly lookups from name + * servers. + */ + for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; + rhosts_file_index++) { + /* Check users .rhosts or .shosts. */ + xasprintf(&path, "%s/%s", + pw->pw_dir, rhosts_files[rhosts_file_index]); + r = stat(path, &st); + free(path); + if (r >= 0) + break; + } + /* Switch back to privileged uid. */ + restore_uid(); + + /* + * Deny if The user has no .shosts or .rhosts file and there + * are no system-wide files. + */ + if (!rhosts_files[rhosts_file_index] && + stat(_PATH_RHOSTS_EQUIV, &st) == -1 && + stat(_PATH_SSH_HOSTS_EQUIV, &st) == -1) { + debug3_f("no hosts access files exist"); + return 0; + } + + /* + * If not logging in as superuser, try /etc/hosts.equiv and + * shosts.equiv. + */ + if (pw->pw_uid == 0) + debug3_f("root user, ignoring system hosts files"); + else { + if (check_rhosts_file(_PATH_RHOSTS_EQUIV, hostname, ipaddr, + client_user, pw->pw_name)) { + auth_debug_add("Accepted for %.100s [%.100s] by " + "/etc/hosts.equiv.", hostname, ipaddr); + return 1; + } + if (check_rhosts_file(_PATH_SSH_HOSTS_EQUIV, hostname, ipaddr, + client_user, pw->pw_name)) { + auth_debug_add("Accepted for %.100s [%.100s] by " + "%.100s.", hostname, ipaddr, _PATH_SSH_HOSTS_EQUIV); + return 1; + } + } + + /* + * Check that the home directory is owned by root or the user, and is + * not group or world writable. + */ + if (stat(pw->pw_dir, &st) == -1) { + logit("Rhosts authentication refused for %.100s: " + "no home directory %.200s", pw->pw_name, pw->pw_dir); + auth_debug_add("Rhosts authentication refused for %.100s: " + "no home directory %.200s", pw->pw_name, pw->pw_dir); + return 0; + } + if (options.strict_modes && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + logit("Rhosts authentication refused for %.100s: " + "bad ownership or modes for home directory.", pw->pw_name); + auth_debug_add("Rhosts authentication refused for %.100s: " + "bad ownership or modes for home directory.", pw->pw_name); + return 0; + } + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw); + + /* Check all .rhosts files (currently .shosts and .rhosts). */ + for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; + rhosts_file_index++) { + /* Check users .rhosts or .shosts. */ + xasprintf(&path, "%s/%s", + pw->pw_dir, rhosts_files[rhosts_file_index]); + if (stat(path, &st) == -1) { + free(path); + continue; + } + + /* + * Make sure that the file is either owned by the user or by + * root, and make sure it is not writable by anyone but the + * owner. This is to help avoid novices accidentally + * allowing access to their account by anyone. + */ + if (options.strict_modes && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + logit("Rhosts authentication refused for %.100s: " + "bad modes for %.200s", pw->pw_name, path); + auth_debug_add("Bad file modes for %.200s", path); + free(path); + continue; + } + /* + * Check if we have been configured to ignore .rhosts + * and .shosts files. + */ + if (options.ignore_rhosts == IGNORE_RHOSTS_YES || + (options.ignore_rhosts == IGNORE_RHOSTS_SHOSTS && + strcmp(rhosts_files[rhosts_file_index], ".shosts") != 0)) { + auth_debug_add("Server has been configured to " + "ignore %.100s.", rhosts_files[rhosts_file_index]); + free(path); + continue; + } + /* Check if authentication is permitted by the file. */ + if (check_rhosts_file(path, hostname, ipaddr, + client_user, pw->pw_name)) { + auth_debug_add("Accepted by %.100s.", + rhosts_files[rhosts_file_index]); + /* Restore the privileged uid. */ + restore_uid(); + auth_debug_add("Accepted host %s ip %s client_user " + "%s server_user %s", hostname, ipaddr, + client_user, pw->pw_name); + free(path); + return 1; + } + free(path); + } + + /* Restore the privileged uid. */ + restore_uid(); + return 0; +} diff --git a/aosp/external/openssh/auth-shadow.c b/aosp/external/openssh/auth-shadow.c new file mode 100644 index 0000000000000000000000000000000000000000..c77ee8da9b48ede93f6aa0df51d755281eb227aa --- /dev/null +++ b/aosp/external/openssh/auth-shadow.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2004 Darren Tucker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) +#include +#include +#include +#include + +#include "hostfile.h" +#include "auth.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "log.h" + +#ifdef DAY +# undef DAY +#endif +#define DAY (24L * 60 * 60) /* 1 day in seconds */ + +extern struct sshbuf *loginmsg; + +/* + * For the account and password expiration functions, we assume the expiry + * occurs the day after the day specified. + */ + +/* + * Check if specified account is expired. Returns 1 if account is expired, + * 0 otherwise. + */ +int +auth_shadow_acctexpired(struct spwd *spw) +{ + time_t today; + int daysleft; + int r; + + today = time(NULL) / DAY; + daysleft = spw->sp_expire - today; + debug3("%s: today %d sp_expire %d days left %d", __func__, (int)today, + (int)spw->sp_expire, daysleft); + + if (spw->sp_expire == -1) { + debug3("account expiration disabled"); + } else if (daysleft < 0) { + logit("Account %.100s has expired", spw->sp_namp); + return 1; + } else if (daysleft <= spw->sp_warn) { + debug3("account will expire in %d days", daysleft); + if ((r = sshbuf_putf(loginmsg, + "Your account will expire in %d day%s.\n", daysleft, + daysleft == 1 ? "" : "s")) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + + return 0; +} + +/* + * Checks password expiry for platforms that use shadow passwd files. + * Returns: 1 = password expired, 0 = password not expired + */ +int +auth_shadow_pwexpired(Authctxt *ctxt) +{ + struct spwd *spw = NULL; + const char *user = ctxt->pw->pw_name; + time_t today; + int r, daysleft, disabled = 0; + + if ((spw = getspnam((char *)user)) == NULL) { + error("Could not get shadow information for %.100s", user); + return 0; + } + + today = time(NULL) / DAY; + debug3("%s: today %d sp_lstchg %d sp_max %d", __func__, (int)today, + (int)spw->sp_lstchg, (int)spw->sp_max); + +#if defined(__hpux) && !defined(HAVE_SECUREWARE) + if (iscomsec()) { + struct pr_passwd *pr; + + pr = getprpwnam((char *)user); + + /* Test for Trusted Mode expiry disabled */ + if (pr != NULL && pr->ufld.fd_min == 0 && + pr->ufld.fd_lifetime == 0 && pr->ufld.fd_expire == 0 && + pr->ufld.fd_pw_expire_warning == 0 && + pr->ufld.fd_schange != 0) + disabled = 1; + } +#endif + + /* TODO: check sp_inact */ + daysleft = spw->sp_lstchg + spw->sp_max - today; + if (disabled) { + debug3("password expiration disabled"); + } else if (spw->sp_lstchg == 0) { + logit("User %.100s password has expired (root forced)", user); + return 1; + } else if (spw->sp_max == -1) { + debug3("password expiration disabled"); + } else if (daysleft < 0) { + logit("User %.100s password has expired (password aged)", user); + return 1; + } else if (daysleft <= spw->sp_warn) { + debug3("password will expire in %d days", daysleft); + if ((r = sshbuf_putf(loginmsg, + "Your password will expire in %d day%s.\n", daysleft, + daysleft == 1 ? "" : "s")) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + + return 0; +} +#endif /* USE_SHADOW && HAS_SHADOW_EXPIRE */ diff --git a/aosp/external/openssh/auth-sia.c b/aosp/external/openssh/auth-sia.c new file mode 100644 index 0000000000000000000000000000000000000000..ebe9d8d124c567f478f0c88a47eebd8a6dc3ad6a --- /dev/null +++ b/aosp/external/openssh/auth-sia.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2002 Chris Adams. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef HAVE_OSF_SIA +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssh.h" +#include "ssh_api.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-sia.h" +#include "log.h" +#include "servconf.h" +#include "canohost.h" +#include "uidswap.h" + +extern ServerOptions options; +extern int saved_argc; +extern char **saved_argv; + +int +sys_auth_passwd(struct ssh *ssh, const char *pass) +{ + int ret; + SIAENTITY *ent = NULL; + const char *host; + Authctxt *authctxt = ssh->authctxt; + + host = get_canonical_hostname(options.use_dns); + + if (!authctxt->user || pass == NULL || pass[0] == '\0') + return (0); + + if (sia_ses_init(&ent, saved_argc, saved_argv, host, authctxt->user, + NULL, 0, NULL) != SIASUCCESS) + return (0); + + if ((ret = sia_ses_authent(NULL, pass, ent)) != SIASUCCESS) { + error("Couldn't authenticate %s from %s", + authctxt->user, host); + if (ret & SIASTOP) + sia_ses_release(&ent); + + return (0); + } + + sia_ses_release(&ent); + + return (1); +} + +void +session_setup_sia(struct passwd *pw, char *tty) +{ + SIAENTITY *ent = NULL; + const char *host; + + host = get_canonical_hostname(options.use_dns); + + if (sia_ses_init(&ent, saved_argc, saved_argv, host, pw->pw_name, + tty, 0, NULL) != SIASUCCESS) + fatal("sia_ses_init failed"); + + if (sia_make_entity_pwd(pw, ent) != SIASUCCESS) { + sia_ses_release(&ent); + fatal("sia_make_entity_pwd failed"); + } + + ent->authtype = SIA_A_NONE; + if (sia_ses_estab(sia_collect_trm, ent) != SIASUCCESS) + fatal("Couldn't establish session for %s from %s", + pw->pw_name, host); + + if (sia_ses_launch(sia_collect_trm, ent) != SIASUCCESS) + fatal("Couldn't launch session for %s from %s", + pw->pw_name, host); + + sia_ses_release(&ent); + + setuid(0); + permanently_set_uid(pw); +} + +#endif /* HAVE_OSF_SIA */ diff --git a/aosp/external/openssh/auth-sia.h b/aosp/external/openssh/auth-sia.h new file mode 100644 index 0000000000000000000000000000000000000000..27cbb93f12da146753ed74d16ae24e76baed0aa9 --- /dev/null +++ b/aosp/external/openssh/auth-sia.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2002 Chris Adams. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef HAVE_OSF_SIA + +void session_setup_sia(struct passwd *, char *); + +#endif /* HAVE_OSF_SIA */ diff --git a/aosp/external/openssh/auth.c b/aosp/external/openssh/auth.c new file mode 100644 index 0000000000000000000000000000000000000000..f2ea845850296f8911d3e4120d848a38e49155ff --- /dev/null +++ b/aosp/external/openssh/auth.c @@ -0,0 +1,1015 @@ +/* $OpenBSD: auth.c,v 1.154 2022/02/23 11:17:10 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#ifdef HAVE_LOGIN_H +#include +#endif +#ifdef USE_SHADOW +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "match.h" +#include "groupaccess.h" +#include "log.h" +#include "sshbuf.h" +#include "misc.h" +#include "servconf.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-options.h" +#include "canohost.h" +#include "uidswap.h" +#include "packet.h" +#include "loginrec.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "authfile.h" +#include "monitor_wrap.h" +#include "ssherr.h" +#include "compat.h" +#include "channels.h" + +/* import */ +extern ServerOptions options; +extern struct include_list includes; +extern int use_privsep; +extern struct sshbuf *loginmsg; +extern struct passwd *privsep_pw; +extern struct sshauthopt *auth_opts; + +/* Debugging messages */ +static struct sshbuf *auth_debug; + +/* + * Check if the user is allowed to log in via ssh. If user is listed + * in DenyUsers or one of user's groups is listed in DenyGroups, false + * will be returned. If AllowUsers isn't empty and user isn't listed + * there, or if AllowGroups isn't empty and one of user's groups isn't + * listed there, false will be returned. + * If the user's shell is not executable, false will be returned. + * Otherwise true is returned. + */ +int +allowed_user(struct ssh *ssh, struct passwd * pw) +{ + struct stat st; + const char *hostname = NULL, *ipaddr = NULL; + u_int i; + int r; + + /* Shouldn't be called if pw is NULL, but better safe than sorry... */ + if (!pw || !pw->pw_name) + return 0; + + if (!options.use_pam && platform_locked_account(pw)) { + logit("User %.100s not allowed because account is locked", + pw->pw_name); + return 0; + } + + /* + * Deny if shell does not exist or is not executable unless we + * are chrooting. + */ + if (options.chroot_directory == NULL || + strcasecmp(options.chroot_directory, "none") == 0) { + char *shell = xstrdup((pw->pw_shell[0] == '\0') ? + _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ + + if (stat(shell, &st) == -1) { + logit("User %.100s not allowed because shell %.100s " + "does not exist", pw->pw_name, shell); + free(shell); + return 0; + } + if (S_ISREG(st.st_mode) == 0 || + (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { + logit("User %.100s not allowed because shell %.100s " + "is not executable", pw->pw_name, shell); + free(shell); + return 0; + } + free(shell); + } + + if (options.num_deny_users > 0 || options.num_allow_users > 0 || + options.num_deny_groups > 0 || options.num_allow_groups > 0) { + hostname = auth_get_canonical_hostname(ssh, options.use_dns); + ipaddr = ssh_remote_ipaddr(ssh); + } + + /* Return false if user is listed in DenyUsers */ + if (options.num_deny_users > 0) { + for (i = 0; i < options.num_deny_users; i++) { + r = match_user(pw->pw_name, hostname, ipaddr, + options.deny_users[i]); + if (r < 0) { + fatal("Invalid DenyUsers pattern \"%.100s\"", + options.deny_users[i]); + } else if (r != 0) { + logit("User %.100s from %.100s not allowed " + "because listed in DenyUsers", + pw->pw_name, hostname); + return 0; + } + } + } + /* Return false if AllowUsers isn't empty and user isn't listed there */ + if (options.num_allow_users > 0) { + for (i = 0; i < options.num_allow_users; i++) { + r = match_user(pw->pw_name, hostname, ipaddr, + options.allow_users[i]); + if (r < 0) { + fatal("Invalid AllowUsers pattern \"%.100s\"", + options.allow_users[i]); + } else if (r == 1) + break; + } + /* i < options.num_allow_users iff we break for loop */ + if (i >= options.num_allow_users) { + logit("User %.100s from %.100s not allowed because " + "not listed in AllowUsers", pw->pw_name, hostname); + return 0; + } + } + if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { + /* Get the user's group access list (primary and supplementary) */ + if (ga_init(pw->pw_name, pw->pw_gid) == 0) { + logit("User %.100s from %.100s not allowed because " + "not in any group", pw->pw_name, hostname); + return 0; + } + + /* Return false if one of user's groups is listed in DenyGroups */ + if (options.num_deny_groups > 0) + if (ga_match(options.deny_groups, + options.num_deny_groups)) { + ga_free(); + logit("User %.100s from %.100s not allowed " + "because a group is listed in DenyGroups", + pw->pw_name, hostname); + return 0; + } + /* + * Return false if AllowGroups isn't empty and one of user's groups + * isn't listed there + */ + if (options.num_allow_groups > 0) + if (!ga_match(options.allow_groups, + options.num_allow_groups)) { + ga_free(); + logit("User %.100s from %.100s not allowed " + "because none of user's groups are listed " + "in AllowGroups", pw->pw_name, hostname); + return 0; + } + ga_free(); + } + +#ifdef CUSTOM_SYS_AUTH_ALLOWED_USER + if (!sys_auth_allowed_user(pw, loginmsg)) + return 0; +#endif + + /* We found no reason not to let this user try to log on... */ + return 1; +} + +/* + * Formats any key left in authctxt->auth_method_key for inclusion in + * auth_log()'s message. Also includes authxtct->auth_method_info if present. + */ +static char * +format_method_key(Authctxt *authctxt) +{ + const struct sshkey *key = authctxt->auth_method_key; + const char *methinfo = authctxt->auth_method_info; + char *fp, *cafp, *ret = NULL; + + if (key == NULL) + return NULL; + + if (sshkey_is_cert(key)) { + fp = sshkey_fingerprint(key, + options.fingerprint_hash, SSH_FP_DEFAULT); + cafp = sshkey_fingerprint(key->cert->signature_key, + options.fingerprint_hash, SSH_FP_DEFAULT); + xasprintf(&ret, "%s %s ID %s (serial %llu) CA %s %s%s%s", + sshkey_type(key), fp == NULL ? "(null)" : fp, + key->cert->key_id, + (unsigned long long)key->cert->serial, + sshkey_type(key->cert->signature_key), + cafp == NULL ? "(null)" : cafp, + methinfo == NULL ? "" : ", ", + methinfo == NULL ? "" : methinfo); + free(fp); + free(cafp); + } else { + fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); + xasprintf(&ret, "%s %s%s%s", sshkey_type(key), + fp == NULL ? "(null)" : fp, + methinfo == NULL ? "" : ", ", + methinfo == NULL ? "" : methinfo); + free(fp); + } + return ret; +} + +void +auth_log(struct ssh *ssh, int authenticated, int partial, + const char *method, const char *submethod) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + int level = SYSLOG_LEVEL_VERBOSE; + const char *authmsg; + char *extra = NULL; + + if (use_privsep && !mm_is_monitor() && !authctxt->postponed) + return; + + /* Raise logging level */ + if (authenticated == 1 || + !authctxt->valid || + authctxt->failures >= options.max_authtries / 2 || + strcmp(method, "password") == 0) + level = SYSLOG_LEVEL_INFO; + + if (authctxt->postponed) + authmsg = "Postponed"; + else if (partial) + authmsg = "Partial"; + else + authmsg = authenticated ? "Accepted" : "Failed"; + + if ((extra = format_method_key(authctxt)) == NULL) { + if (authctxt->auth_method_info != NULL) + extra = xstrdup(authctxt->auth_method_info); + } + + do_log2(level, "%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", + authmsg, + method, + submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, + authctxt->valid ? "" : "invalid user ", + authctxt->user, + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), + extra != NULL ? ": " : "", + extra != NULL ? extra : ""); + + free(extra); + +#if defined(CUSTOM_FAILED_LOGIN) || defined(SSH_AUDIT_EVENTS) + if (authenticated == 0 && !(authctxt->postponed || partial)) { + /* Log failed login attempt */ +# ifdef CUSTOM_FAILED_LOGIN + if (strcmp(method, "password") == 0 || + strncmp(method, "keyboard-interactive", 20) == 0 || + strcmp(method, "challenge-response") == 0) + record_failed_login(ssh, authctxt->user, + auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); +# endif +# ifdef SSH_AUDIT_EVENTS + audit_event(ssh, audit_classify_auth(method)); +# endif + } +#endif +#if defined(CUSTOM_FAILED_LOGIN) && defined(WITH_AIXAUTHENTICATE) + if (authenticated) + sys_auth_record_login(authctxt->user, + auth_get_canonical_hostname(ssh, options.use_dns), "ssh", + loginmsg); +#endif +} + +void +auth_maxtries_exceeded(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + + error("maximum authentication attempts exceeded for " + "%s%.100s from %.200s port %d ssh2", + authctxt->valid ? "" : "invalid user ", + authctxt->user, + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh)); + ssh_packet_disconnect(ssh, "Too many authentication failures"); + /* NOTREACHED */ +} + +/* + * Check whether root logins are disallowed. + */ +int +auth_root_allowed(struct ssh *ssh, const char *method) +{ + switch (options.permit_root_login) { + case PERMIT_YES: + return 1; + case PERMIT_NO_PASSWD: + if (strcmp(method, "publickey") == 0 || + strcmp(method, "hostbased") == 0 || + strcmp(method, "gssapi-with-mic") == 0) + return 1; + break; + case PERMIT_FORCED_ONLY: + if (auth_opts->force_command != NULL) { + logit("Root login accepted for forced command."); + return 1; + } + break; + } + logit("ROOT LOGIN REFUSED FROM %.200s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + return 0; +} + + +/* + * Given a template and a passwd structure, build a filename + * by substituting % tokenised options. Currently, %% becomes '%', + * %h becomes the home directory and %u the username. + * + * This returns a buffer allocated by xmalloc. + */ +char * +expand_authorized_keys(const char *filename, struct passwd *pw) +{ + char *file, uidstr[32], ret[PATH_MAX]; + int i; + + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)pw->pw_uid); + file = percent_expand(filename, "h", pw->pw_dir, + "u", pw->pw_name, "U", uidstr, (char *)NULL); + + /* + * Ensure that filename starts anchored. If not, be backward + * compatible and prepend the '%h/' + */ + if (path_absolute(file)) + return (file); + + i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); + if (i < 0 || (size_t)i >= sizeof(ret)) + fatal("expand_authorized_keys: path too long"); + free(file); + return (xstrdup(ret)); +} + +char * +authorized_principals_file(struct passwd *pw) +{ + if (options.authorized_principals_file == NULL) + return NULL; + return expand_authorized_keys(options.authorized_principals_file, pw); +} + +/* return ok if key exists in sysfile or userfile */ +HostStatus +check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host, + const char *sysfile, const char *userfile) +{ + char *user_hostfile; + struct stat st; + HostStatus host_status; + struct hostkeys *hostkeys; + const struct hostkey_entry *found; + + hostkeys = init_hostkeys(); + load_hostkeys(hostkeys, host, sysfile, 0); + if (userfile != NULL) { + user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); + if (options.strict_modes && + (stat(user_hostfile, &st) == 0) && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + logit("Authentication refused for %.100s: " + "bad owner or modes for %.200s", + pw->pw_name, user_hostfile); + auth_debug_add("Ignored %.200s: bad ownership or modes", + user_hostfile); + } else { + temporarily_use_uid(pw); + load_hostkeys(hostkeys, host, user_hostfile, 0); + restore_uid(); + } + free(user_hostfile); + } + host_status = check_key_in_hostkeys(hostkeys, key, &found); + if (host_status == HOST_REVOKED) + error("WARNING: revoked key for %s attempted authentication", + host); + else if (host_status == HOST_OK) + debug_f("key for %s found at %s:%ld", + found->host, found->file, found->line); + else + debug_f("key for host %s not found", host); + + free_hostkeys(hostkeys); + + return host_status; +} + +static FILE * +auth_openfile(const char *file, struct passwd *pw, int strict_modes, + int log_missing, char *file_type) +{ + char line[1024]; + struct stat st; + int fd; + FILE *f; + + if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { + if (log_missing || errno != ENOENT) + debug("Could not open %s '%s': %s", file_type, file, + strerror(errno)); + return NULL; + } + + if (fstat(fd, &st) == -1) { + close(fd); + return NULL; + } + if (!S_ISREG(st.st_mode)) { + logit("User %s %s %s is not a regular file", + pw->pw_name, file_type, file); + close(fd); + return NULL; + } + unset_nonblock(fd); + if ((f = fdopen(fd, "r")) == NULL) { + close(fd); + return NULL; + } + if (strict_modes && + safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) { + fclose(f); + logit("Authentication refused: %s", line); + auth_debug_add("Ignored %s: %s", file_type, line); + return NULL; + } + + return f; +} + + +FILE * +auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) +{ + return auth_openfile(file, pw, strict_modes, 1, "authorized keys"); +} + +FILE * +auth_openprincipals(const char *file, struct passwd *pw, int strict_modes) +{ + return auth_openfile(file, pw, strict_modes, 0, + "authorized principals"); +} + +struct passwd * +getpwnamallow(struct ssh *ssh, const char *user) +{ +#ifdef HAVE_LOGIN_CAP + extern login_cap_t *lc; +#ifdef BSD_AUTH + auth_session_t *as; +#endif +#endif + struct passwd *pw; + struct connection_info *ci; + u_int i; + + ci = get_connection_info(ssh, 1, options.use_dns); + ci->user = user; + parse_server_match_config(&options, &includes, ci); + log_change_level(options.log_level); + log_verbose_reset(); + for (i = 0; i < options.num_log_verbose; i++) + log_verbose_add(options.log_verbose[i]); + process_permitopen(ssh, &options); + +#if defined(_AIX) && defined(HAVE_SETAUTHDB) + aix_setauthdb(user); +#endif + +#if defined(ANDROID) + // Android has a fixed set of users. Any incoming user that we can't + // identify should be authenticated as the shell user. + if (strcmp(user, "root") && strcmp(user, "shell")) { + logit("Login name %.100s forced to shell", user); + user = "shell"; + } +#endif + pw = getpwnam(user); + +#if defined(_AIX) && defined(HAVE_SETAUTHDB) + aix_restoreauthdb(); +#endif + if (pw == NULL) { + logit("Invalid user %.100s from %.100s port %d", + user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); +#ifdef CUSTOM_FAILED_LOGIN + record_failed_login(ssh, user, + auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); +#endif +#ifdef SSH_AUDIT_EVENTS + audit_event(ssh, SSH_INVALID_USER); +#endif /* SSH_AUDIT_EVENTS */ + return (NULL); + } + if (!allowed_user(ssh, pw)) + return (NULL); +#ifdef HAVE_LOGIN_CAP + if ((lc = login_getpwclass(pw)) == NULL) { + debug("unable to get login class: %s", user); + return (NULL); + } +#ifdef BSD_AUTH + if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || + auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { + debug("Approval failure for %s", user); + pw = NULL; + } + if (as != NULL) + auth_close(as); +#endif +#endif + if (pw != NULL) + return (pwcopy(pw)); + return (NULL); +} + +/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ +int +auth_key_is_revoked(struct sshkey *key) +{ + char *fp = NULL; + int r; + + if (options.revoked_keys_file == NULL) + return 0; + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + error_fr(r, "fingerprint key"); + goto out; + } + + r = sshkey_check_revoked(key, options.revoked_keys_file); + switch (r) { + case 0: + break; /* not revoked */ + case SSH_ERR_KEY_REVOKED: + error("Authentication key %s %s revoked by file %s", + sshkey_type(key), fp, options.revoked_keys_file); + goto out; + default: + error_r(r, "Error checking authentication key %s %s in " + "revoked keys file %s", sshkey_type(key), fp, + options.revoked_keys_file); + goto out; + } + + /* Success */ + r = 0; + + out: + free(fp); + return r == 0 ? 0 : 1; +} + +void +auth_debug_add(const char *fmt,...) +{ + char buf[1024]; + va_list args; + int r; + + if (auth_debug == NULL) + return; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + if ((r = sshbuf_put_cstring(auth_debug, buf)) != 0) + fatal_fr(r, "sshbuf_put_cstring"); +} + +void +auth_debug_send(struct ssh *ssh) +{ + char *msg; + int r; + + if (auth_debug == NULL) + return; + while (sshbuf_len(auth_debug) != 0) { + if ((r = sshbuf_get_cstring(auth_debug, &msg, NULL)) != 0) + fatal_fr(r, "sshbuf_get_cstring"); + ssh_packet_send_debug(ssh, "%s", msg); + free(msg); + } +} + +void +auth_debug_reset(void) +{ + if (auth_debug != NULL) + sshbuf_reset(auth_debug); + else if ((auth_debug = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); +} + +struct passwd * +fakepw(void) +{ + static int done = 0; + static struct passwd fake; + const char hashchars[] = "./ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789"; /* from bcrypt.c */ + char *cp; + + if (done) + return (&fake); + + memset(&fake, 0, sizeof(fake)); + fake.pw_name = "NOUSER"; + fake.pw_passwd = xstrdup("$2a$10$" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + for (cp = fake.pw_passwd + 7; *cp != '\0'; cp++) + *cp = hashchars[arc4random_uniform(sizeof(hashchars) - 1)]; +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + fake.pw_gecos = "NOUSER"; +#endif + fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid; + fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid; +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + fake.pw_class = ""; +#endif + fake.pw_dir = "/nonexist"; + fake.pw_shell = "/nonexist"; + done = 1; + + return (&fake); +} + +/* + * Returns the remote DNS hostname as a string. The returned string must not + * be freed. NB. this will usually trigger a DNS query the first time it is + * called. + * This function does additional checks on the hostname to mitigate some + * attacks on based on conflation of hostnames and IP addresses. + */ + +static char * +remote_hostname(struct ssh *ssh) +{ + struct sockaddr_storage from; + socklen_t fromlen; + struct addrinfo hints, *ai, *aitop; + char name[NI_MAXHOST], ntop2[NI_MAXHOST]; + const char *ntop = ssh_remote_ipaddr(ssh); + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(ssh_packet_get_connection_in(ssh), + (struct sockaddr *)&from, &fromlen) == -1) { + debug("getpeername failed: %.100s", strerror(errno)); + return xstrdup(ntop); + } + + ipv64_normalise_mapped(&from, &fromlen); + if (from.ss_family == AF_INET6) + fromlen = sizeof(struct sockaddr_in6); + + debug3("Trying to reverse map address %.100s.", ntop); + /* Map the IP address to a host name. */ + if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), + NULL, 0, NI_NAMEREQD) != 0) { + /* Host name not found. Use ip address. */ + return xstrdup(ntop); + } + + /* + * if reverse lookup result looks like a numeric hostname, + * someone is trying to trick us by PTR record like following: + * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(name, NULL, &hints, &ai) == 0) { + logit("Nasty PTR record \"%s\" is set up for %s, ignoring", + name, ntop); + freeaddrinfo(ai); + return xstrdup(ntop); + } + + /* Names are stored in lowercase. */ + lowercase(name); + + /* + * Map it back to an IP address and check that the given + * address actually is an address of this host. This is + * necessary because anyone with access to a name server can + * define arbitrary names for an IP address. Mapping from + * name to IP address can be trusted better (but can still be + * fooled if the intruder has access to the name server of + * the domain). + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = from.ss_family; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { + logit("reverse mapping checking getaddrinfo for %.700s " + "[%s] failed.", name, ntop); + return xstrdup(ntop); + } + /* Look for the address from the list of addresses. */ + for (ai = aitop; ai; ai = ai->ai_next) { + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, + sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && + (strcmp(ntop, ntop2) == 0)) + break; + } + freeaddrinfo(aitop); + /* If we reached the end of the list, the address was not there. */ + if (ai == NULL) { + /* Address not found for the host name. */ + logit("Address %.100s maps to %.600s, but this does not " + "map back to the address.", ntop, name); + return xstrdup(ntop); + } + return xstrdup(name); +} + +/* + * Return the canonical name of the host in the other side of the current + * connection. The host name is cached, so it is efficient to call this + * several times. + */ + +const char * +auth_get_canonical_hostname(struct ssh *ssh, int use_dns) +{ + static char *dnsname; + + if (!use_dns) + return ssh_remote_ipaddr(ssh); + else if (dnsname != NULL) + return dnsname; + else { + dnsname = remote_hostname(ssh); + return dnsname; + } +} + +/* These functions link key/cert options to the auth framework */ + +/* Log sshauthopt options locally and (optionally) for remote transmission */ +void +auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote) +{ + int do_env = options.permit_user_env && opts->nenv > 0; + int do_permitopen = opts->npermitopen > 0 && + (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0; + int do_permitlisten = opts->npermitlisten > 0 && + (options.allow_tcp_forwarding & FORWARD_REMOTE) != 0; + size_t i; + char msg[1024], buf[64]; + + snprintf(buf, sizeof(buf), "%d", opts->force_tun_device); + /* Try to keep this alphabetically sorted */ + snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + opts->permit_agent_forwarding_flag ? " agent-forwarding" : "", + opts->force_command == NULL ? "" : " command", + do_env ? " environment" : "", + opts->valid_before == 0 ? "" : "expires", + opts->no_require_user_presence ? " no-touch-required" : "", + do_permitopen ? " permitopen" : "", + do_permitlisten ? " permitlisten" : "", + opts->permit_port_forwarding_flag ? " port-forwarding" : "", + opts->cert_principals == NULL ? "" : " principals", + opts->permit_pty_flag ? " pty" : "", + opts->require_verify ? " uv" : "", + opts->force_tun_device == -1 ? "" : " tun=", + opts->force_tun_device == -1 ? "" : buf, + opts->permit_user_rc ? " user-rc" : "", + opts->permit_x11_forwarding_flag ? " x11-forwarding" : ""); + + debug("%s: %s", loc, msg); + if (do_remote) + auth_debug_add("%s: %s", loc, msg); + + if (options.permit_user_env) { + for (i = 0; i < opts->nenv; i++) { + debug("%s: environment: %s", loc, opts->env[i]); + if (do_remote) { + auth_debug_add("%s: environment: %s", + loc, opts->env[i]); + } + } + } + + /* Go into a little more details for the local logs. */ + if (opts->valid_before != 0) { + format_absolute_time(opts->valid_before, buf, sizeof(buf)); + debug("%s: expires at %s", loc, buf); + } + if (opts->cert_principals != NULL) { + debug("%s: authorized principals: \"%s\"", + loc, opts->cert_principals); + } + if (opts->force_command != NULL) + debug("%s: forced command: \"%s\"", loc, opts->force_command); + if (do_permitopen) { + for (i = 0; i < opts->npermitopen; i++) { + debug("%s: permitted open: %s", + loc, opts->permitopen[i]); + } + } + if (do_permitlisten) { + for (i = 0; i < opts->npermitlisten; i++) { + debug("%s: permitted listen: %s", + loc, opts->permitlisten[i]); + } + } +} + +/* Activate a new set of key/cert options; merging with what is there. */ +int +auth_activate_options(struct ssh *ssh, struct sshauthopt *opts) +{ + struct sshauthopt *old = auth_opts; + const char *emsg = NULL; + + debug_f("setting new authentication options"); + if ((auth_opts = sshauthopt_merge(old, opts, &emsg)) == NULL) { + error("Inconsistent authentication options: %s", emsg); + return -1; + } + return 0; +} + +/* Disable forwarding, etc for the session */ +void +auth_restrict_session(struct ssh *ssh) +{ + struct sshauthopt *restricted; + + debug_f("restricting session"); + + /* A blank sshauthopt defaults to permitting nothing */ + restricted = sshauthopt_new(); + restricted->permit_pty_flag = 1; + restricted->restricted = 1; + + if (auth_activate_options(ssh, restricted) != 0) + fatal_f("failed to restrict session"); + sshauthopt_free(restricted); +} + +int +auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw, + struct sshauthopt *opts, int allow_cert_authority, const char *loc) +{ + const char *remote_ip = ssh_remote_ipaddr(ssh); + const char *remote_host = auth_get_canonical_hostname(ssh, + options.use_dns); + time_t now = time(NULL); + char buf[64]; + + /* + * Check keys/principals file expiry time. + * NB. validity interval in certificate is handled elsewhere. + */ + if (opts->valid_before && now > 0 && + opts->valid_before < (uint64_t)now) { + format_absolute_time(opts->valid_before, buf, sizeof(buf)); + debug("%s: entry expired at %s", loc, buf); + auth_debug_add("%s: entry expired at %s", loc, buf); + return -1; + } + /* Consistency checks */ + if (opts->cert_principals != NULL && !opts->cert_authority) { + debug("%s: principals on non-CA key", loc); + auth_debug_add("%s: principals on non-CA key", loc); + /* deny access */ + return -1; + } + /* cert-authority flag isn't valid in authorized_principals files */ + if (!allow_cert_authority && opts->cert_authority) { + debug("%s: cert-authority flag invalid here", loc); + auth_debug_add("%s: cert-authority flag invalid here", loc); + /* deny access */ + return -1; + } + + /* Perform from= checks */ + if (opts->required_from_host_keys != NULL) { + switch (match_host_and_ip(remote_host, remote_ip, + opts->required_from_host_keys )) { + case 1: + /* Host name matches. */ + break; + case -1: + default: + debug("%s: invalid from criteria", loc); + auth_debug_add("%s: invalid from criteria", loc); + /* FALLTHROUGH */ + case 0: + logit("%s: Authentication tried for %.100s with " + "correct key but not from a permitted " + "host (host=%.200s, ip=%.200s, required=%.200s).", + loc, pw->pw_name, remote_host, remote_ip, + opts->required_from_host_keys); + auth_debug_add("%s: Your host '%.200s' is not " + "permitted to use this key for login.", + loc, remote_host); + /* deny access */ + return -1; + } + } + /* Check source-address restriction from certificate */ + if (opts->required_from_host_cert != NULL) { + switch (addr_match_cidr_list(remote_ip, + opts->required_from_host_cert)) { + case 1: + /* accepted */ + break; + case -1: + default: + /* invalid */ + error("%s: Certificate source-address invalid", loc); + /* FALLTHROUGH */ + case 0: + logit("%s: Authentication tried for %.100s with valid " + "certificate but not from a permitted source " + "address (%.200s).", loc, pw->pw_name, remote_ip); + auth_debug_add("%s: Your address '%.200s' is not " + "permitted to use this certificate for login.", + loc, remote_ip); + return -1; + } + } + /* + * + * XXX this is spammy. We should report remotely only for keys + * that are successful in actual auth attempts, and not PK_OK + * tests. + */ + auth_log_authopts(loc, opts, 1); + + return 0; +} diff --git a/aosp/external/openssh/auth.h b/aosp/external/openssh/auth.h new file mode 100644 index 0000000000000000000000000000000000000000..a65d8fd02d38934dcf1ecae887a9d4e1085afa58 --- /dev/null +++ b/aosp/external/openssh/auth.h @@ -0,0 +1,235 @@ +/* $OpenBSD: auth.h,v 1.102 2021/12/19 22:12:07 djm Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef AUTH_H +#define AUTH_H + +#include + +#ifdef HAVE_LOGIN_CAP +#include +#endif +#ifdef BSD_AUTH +#include +#endif +#ifdef KRB5 +#include +#endif + +struct passwd; +struct ssh; +struct sshbuf; +struct sshkey; +struct sshauthopt; + +typedef struct Authctxt Authctxt; +typedef struct Authmethod Authmethod; +typedef struct KbdintDevice KbdintDevice; + +struct Authctxt { + sig_atomic_t success; + int authenticated; /* authenticated and alarms cancelled */ + int postponed; /* authentication needs another step */ + int valid; /* user exists and is allowed to login */ + int attempt; + int failures; + int server_caused_failure; + int force_pwchange; + char *user; /* username sent by the client */ + char *service; + struct passwd *pw; /* set if 'valid' */ + char *style; + + /* Method lists for multiple authentication */ + char **auth_methods; /* modified from server config */ + u_int num_auth_methods; + + /* Authentication method-specific data */ + void *methoddata; + void *kbdintctxt; +#ifdef BSD_AUTH + auth_session_t *as; +#endif +#ifdef KRB5 + krb5_context krb5_ctx; + krb5_ccache krb5_fwd_ccache; + krb5_principal krb5_user; + char *krb5_ticket_file; + char *krb5_ccname; +#endif + struct sshbuf *loginmsg; + + /* Authentication keys already used; these will be refused henceforth */ + struct sshkey **prev_keys; + u_int nprev_keys; + + /* Last used key and ancillary information from active auth method */ + struct sshkey *auth_method_key; + char *auth_method_info; + + /* Information exposed to session */ + struct sshbuf *session_info; /* Auth info for environment */ +}; + +/* + * Every authentication method has to handle authentication requests for + * non-existing users, or for users that are not allowed to login. In this + * case 'valid' is set to 0, but 'user' points to the username requested by + * the client. + */ + +struct Authmethod { + char *name; + char *synonym; + int (*userauth)(struct ssh *, const char *); + int *enabled; +}; + +/* + * Keyboard interactive device: + * init_ctx returns: non NULL upon success + * query returns: 0 - success, otherwise failure + * respond returns: 0 - success, 1 - need further interaction, + * otherwise - failure + */ +struct KbdintDevice +{ + const char *name; + void* (*init_ctx)(Authctxt*); + int (*query)(void *ctx, char **name, char **infotxt, + u_int *numprompts, char ***prompts, u_int **echo_on); + int (*respond)(void *ctx, u_int numresp, char **responses); + void (*free_ctx)(void *ctx); +}; + +int +auth_rhosts2(struct passwd *, const char *, const char *, const char *); + +int auth_password(struct ssh *, const char *); + +int hostbased_key_allowed(struct ssh *, struct passwd *, + const char *, char *, struct sshkey *); +int user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int, + struct sshauthopt **); +int auth2_key_already_used(Authctxt *, const struct sshkey *); + +/* + * Handling auth method-specific information for logging and prevention + * of key reuse during multiple authentication. + */ +void auth2_authctxt_reset_info(Authctxt *); +void auth2_record_key(Authctxt *, int, const struct sshkey *); +void auth2_record_info(Authctxt *authctxt, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +void auth2_update_session_info(Authctxt *, const char *, const char *); + +#ifdef KRB5 +int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); +int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt); +int auth_krb5_password(Authctxt *authctxt, const char *password); +void krb5_cleanup_proc(Authctxt *authctxt); +#endif /* KRB5 */ + +#if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) +#include +int auth_shadow_acctexpired(struct spwd *); +int auth_shadow_pwexpired(Authctxt *); +#endif + +#include "auth-pam.h" +#include "audit.h" +void remove_kbdint_device(const char *); + +void do_authentication2(struct ssh *); + +void auth_log(struct ssh *, int, int, const char *, const char *); +void auth_maxtries_exceeded(struct ssh *) __attribute__((noreturn)); +void userauth_finish(struct ssh *, int, const char *, const char *); +int auth_root_allowed(struct ssh *, const char *); + +char *auth2_read_banner(void); +int auth2_methods_valid(const char *, int); +int auth2_update_methods_lists(Authctxt *, const char *, const char *); +int auth2_setup_methods_lists(Authctxt *); +int auth2_method_allowed(Authctxt *, const char *, const char *); + +void privsep_challenge_enable(void); + +int auth2_challenge(struct ssh *, char *); +void auth2_challenge_stop(struct ssh *); +int bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **); +int bsdauth_respond(void *, u_int, char **); + +int allowed_user(struct ssh *, struct passwd *); +struct passwd * getpwnamallow(struct ssh *, const char *user); + +char *expand_authorized_keys(const char *, struct passwd *pw); +char *authorized_principals_file(struct passwd *); + +FILE *auth_openkeyfile(const char *, struct passwd *, int); +FILE *auth_openprincipals(const char *, struct passwd *, int); +int auth_key_is_revoked(struct sshkey *); + +const char *auth_get_canonical_hostname(struct ssh *, int); + +HostStatus +check_key_in_hostfiles(struct passwd *, struct sshkey *, const char *, + const char *, const char *); + +/* hostkey handling */ +struct sshkey *get_hostkey_by_index(int); +struct sshkey *get_hostkey_public_by_index(int, struct ssh *); +struct sshkey *get_hostkey_public_by_type(int, int, struct ssh *); +struct sshkey *get_hostkey_private_by_type(int, int, struct ssh *); +int get_hostkey_index(struct sshkey *, int, struct ssh *); +int sshd_hostkey_sign(struct ssh *, struct sshkey *, struct sshkey *, + u_char **, size_t *, const u_char *, size_t, const char *); + +/* Key / cert options linkage to auth layer */ +const struct sshauthopt *auth_options(struct ssh *); +int auth_activate_options(struct ssh *, struct sshauthopt *); +void auth_restrict_session(struct ssh *); +int auth_authorise_keyopts(struct ssh *, struct passwd *pw, + struct sshauthopt *, int, const char *); +void auth_log_authopts(const char *, const struct sshauthopt *, int); + +/* debug messages during authentication */ +void auth_debug_add(const char *fmt,...) + __attribute__((format(printf, 1, 2))); +void auth_debug_send(struct ssh *); +void auth_debug_reset(void); + +struct passwd *fakepw(void); + +int sys_auth_passwd(struct ssh *, const char *); + +#if defined(KRB5) && !defined(HEIMDAL) +krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *); +#endif + +#endif /* AUTH_H */ diff --git a/aosp/external/openssh/auth2-chall.c b/aosp/external/openssh/auth2-chall.c new file mode 100644 index 0000000000000000000000000000000000000000..021df8291736e2441977ea62a8d20f927ae155ef --- /dev/null +++ b/aosp/external/openssh/auth2-chall.c @@ -0,0 +1,382 @@ +/* $OpenBSD: auth2-chall.c,v 1.54 2020/10/18 11:32:01 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Per Allansson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh2.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "sshbuf.h" +#include "packet.h" +#include "dispatch.h" +#include "ssherr.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" + +/* import */ +extern ServerOptions options; + +static int auth2_challenge_start(struct ssh *); +static int send_userauth_info_request(struct ssh *); +static int input_userauth_info_response(int, u_int32_t, struct ssh *); + +#ifdef BSD_AUTH +extern KbdintDevice bsdauth_device; +#else +#ifdef USE_PAM +extern KbdintDevice sshpam_device; +#endif +#endif + +KbdintDevice *devices[] = { +#ifdef BSD_AUTH + &bsdauth_device, +#else +#ifdef USE_PAM + &sshpam_device, +#endif +#endif + NULL +}; + +typedef struct KbdintAuthctxt KbdintAuthctxt; +struct KbdintAuthctxt +{ + char *devices; + void *ctxt; + KbdintDevice *device; + u_int nreq; + u_int devices_done; +}; + +#ifdef USE_PAM +void +remove_kbdint_device(const char *devname) +{ + int i, j; + + for (i = 0; devices[i] != NULL; i++) + if (strcmp(devices[i]->name, devname) == 0) { + for (j = i; devices[j] != NULL; j++) + devices[j] = devices[j+1]; + i--; + } +} +#endif + +static KbdintAuthctxt * +kbdint_alloc(const char *devs) +{ + KbdintAuthctxt *kbdintctxt; + struct sshbuf *b; + int i, r; + +#ifdef USE_PAM + if (!options.use_pam) + remove_kbdint_device("pam"); +#endif + + kbdintctxt = xcalloc(1, sizeof(KbdintAuthctxt)); + if (strcmp(devs, "") == 0) { + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + for (i = 0; devices[i]; i++) { + if ((r = sshbuf_putf(b, "%s%s", + sshbuf_len(b) ? "," : "", devices[i]->name)) != 0) + fatal_fr(r, "buffer error"); + } + if ((kbdintctxt->devices = sshbuf_dup_string(b)) == NULL) + fatal_f("sshbuf_dup_string failed"); + sshbuf_free(b); + } else { + kbdintctxt->devices = xstrdup(devs); + } + debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); + kbdintctxt->ctxt = NULL; + kbdintctxt->device = NULL; + kbdintctxt->nreq = 0; + + return kbdintctxt; +} +static void +kbdint_reset_device(KbdintAuthctxt *kbdintctxt) +{ + if (kbdintctxt->ctxt) { + kbdintctxt->device->free_ctx(kbdintctxt->ctxt); + kbdintctxt->ctxt = NULL; + } + kbdintctxt->device = NULL; +} +static void +kbdint_free(KbdintAuthctxt *kbdintctxt) +{ + if (kbdintctxt->device) + kbdint_reset_device(kbdintctxt); + free(kbdintctxt->devices); + freezero(kbdintctxt, sizeof(*kbdintctxt)); +} +/* get next device */ +static int +kbdint_next_device(Authctxt *authctxt, KbdintAuthctxt *kbdintctxt) +{ + size_t len; + char *t; + int i; + + if (kbdintctxt->device) + kbdint_reset_device(kbdintctxt); + do { + len = kbdintctxt->devices ? + strcspn(kbdintctxt->devices, ",") : 0; + + if (len == 0) + break; + for (i = 0; devices[i]; i++) { + if ((kbdintctxt->devices_done & (1 << i)) != 0 || + !auth2_method_allowed(authctxt, + "keyboard-interactive", devices[i]->name)) + continue; + if (strncmp(kbdintctxt->devices, devices[i]->name, + len) == 0) { + kbdintctxt->device = devices[i]; + kbdintctxt->devices_done |= 1 << i; + } + } + t = kbdintctxt->devices; + kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; + free(t); + debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? + kbdintctxt->devices : ""); + } while (kbdintctxt->devices && !kbdintctxt->device); + + return kbdintctxt->device ? 1 : 0; +} + +/* + * try challenge-response, set authctxt->postponed if we have to + * wait for the response. + */ +int +auth2_challenge(struct ssh *ssh, char *devs) +{ + Authctxt *authctxt = ssh->authctxt; + debug("auth2_challenge: user=%s devs=%s", + authctxt->user ? authctxt->user : "", + devs ? devs : ""); + + if (authctxt->user == NULL || !devs) + return 0; + if (authctxt->kbdintctxt == NULL) + authctxt->kbdintctxt = kbdint_alloc(devs); + return auth2_challenge_start(ssh); +} + +/* unregister kbd-int callbacks and context */ +void +auth2_challenge_stop(struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + /* unregister callback */ + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); + if (authctxt->kbdintctxt != NULL) { + kbdint_free(authctxt->kbdintctxt); + authctxt->kbdintctxt = NULL; + } +} + +/* side effect: sets authctxt->postponed if a reply was sent*/ +static int +auth2_challenge_start(struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt; + + debug2("auth2_challenge_start: devices %s", + kbdintctxt->devices ? kbdintctxt->devices : ""); + + if (kbdint_next_device(authctxt, kbdintctxt) == 0) { + auth2_challenge_stop(ssh); + return 0; + } + debug("auth2_challenge_start: trying authentication method '%s'", + kbdintctxt->device->name); + + if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { + auth2_challenge_stop(ssh); + return 0; + } + if (send_userauth_info_request(ssh) == 0) { + auth2_challenge_stop(ssh); + return 0; + } + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE, + &input_userauth_info_response); + + authctxt->postponed = 1; + return 0; +} + +static int +send_userauth_info_request(struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + KbdintAuthctxt *kbdintctxt; + char *name, *instr, **prompts; + u_int r, i, *echo_on; + + kbdintctxt = authctxt->kbdintctxt; + if (kbdintctxt->device->query(kbdintctxt->ctxt, + &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) + return 0; + + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, name)) != 0 || + (r = sshpkt_put_cstring(ssh, instr)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language not used */ + (r = sshpkt_put_u32(ssh, kbdintctxt->nreq)) != 0) + fatal_fr(r, "start packet"); + for (i = 0; i < kbdintctxt->nreq; i++) { + if ((r = sshpkt_put_cstring(ssh, prompts[i])) != 0 || + (r = sshpkt_put_u8(ssh, echo_on[i])) != 0) + fatal_fr(r, "assemble packet"); + } + if ((r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send packet"); + + for (i = 0; i < kbdintctxt->nreq; i++) + free(prompts[i]); + free(prompts); + free(echo_on); + free(name); + free(instr); + return 1; +} + +static int +input_userauth_info_response(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + KbdintAuthctxt *kbdintctxt; + int authenticated = 0, res; + int r; + u_int i, nresp; + const char *devicename = NULL; + char **response = NULL; + + if (authctxt == NULL) + fatal_f("no authctxt"); + kbdintctxt = authctxt->kbdintctxt; + if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) + fatal_f("no kbdintctxt"); + if (kbdintctxt->device == NULL) + fatal_f("no device"); + + authctxt->postponed = 0; /* reset */ + if ((r = sshpkt_get_u32(ssh, &nresp)) != 0) + fatal_fr(r, "parse packet"); + if (nresp != kbdintctxt->nreq) + fatal_f("wrong number of replies"); + if (nresp > 100) + fatal_f("too many replies"); + if (nresp > 0) { + response = xcalloc(nresp, sizeof(char *)); + for (i = 0; i < nresp; i++) { + if ((r = sshpkt_get_cstring(ssh, &response[i], NULL)) != 0) + fatal_fr(r, "parse response"); + } + } + if ((r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); + + for (i = 0; i < nresp; i++) { + explicit_bzero(response[i], strlen(response[i])); + free(response[i]); + } + free(response); + + switch (res) { + case 0: + /* Success! */ + authenticated = authctxt->valid ? 1 : 0; + break; + case 1: + /* Authentication needs further interaction */ + if (send_userauth_info_request(ssh) == 1) + authctxt->postponed = 1; + break; + default: + /* Failure! */ + break; + } + devicename = kbdintctxt->device->name; + if (!authctxt->postponed) { + if (authenticated) { + auth2_challenge_stop(ssh); + } else { + /* start next device */ + /* may set authctxt->postponed */ + auth2_challenge_start(ssh); + } + } + userauth_finish(ssh, authenticated, "keyboard-interactive", + devicename); + return 0; +} + +void +privsep_challenge_enable(void) +{ +#if defined(BSD_AUTH) || defined(USE_PAM) + int n = 0; +#endif +#ifdef BSD_AUTH + extern KbdintDevice mm_bsdauth_device; +#endif +#ifdef USE_PAM + extern KbdintDevice mm_sshpam_device; +#endif + +#ifdef BSD_AUTH + devices[n++] = &mm_bsdauth_device; +#else +#ifdef USE_PAM + devices[n++] = &mm_sshpam_device; +#endif +#endif +} diff --git a/aosp/external/openssh/auth2-gss.c b/aosp/external/openssh/auth2-gss.c new file mode 100644 index 0000000000000000000000000000000000000000..2062609d9308948d82f53c5ddcc89ca36119b52e --- /dev/null +++ b/aosp/external/openssh/auth2-gss.c @@ -0,0 +1,337 @@ +/* $OpenBSD: auth2-gss.c,v 1.33 2021/12/19 22:12:07 djm Exp $ */ + +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef GSSAPI + +#include + +#include + +#include "xmalloc.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "ssh2.h" +#include "log.h" +#include "dispatch.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "misc.h" +#include "servconf.h" +#include "packet.h" +#include "kex.h" +#include "ssh-gss.h" +#include "monitor_wrap.h" + +extern ServerOptions options; + +static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh); +static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh); +static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); +static int input_gssapi_errtok(int, u_int32_t, struct ssh *); + +/* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) + */ +static int +userauth_gssapi(struct ssh *ssh, const char *method) +{ + Authctxt *authctxt = ssh->authctxt; + gss_OID_desc goid = {0, NULL}; + Gssctxt *ctxt = NULL; + int r, present; + u_int mechs; + OM_uint32 ms; + size_t len; + u_char *doid = NULL; + + if ((r = sshpkt_get_u32(ssh, &mechs)) != 0) + fatal_fr(r, "parse packet"); + + if (mechs == 0) { + debug("Mechanism negotiation is not supported"); + return (0); + } + + do { + mechs--; + + free(doid); + + present = 0; + if ((r = sshpkt_get_string(ssh, &doid, &len)) != 0) + fatal_fr(r, "parse oid"); + + if (len > 2 && doid[0] == SSH_GSS_OIDTYPE && + doid[1] == len - 2) { + goid.elements = doid + 2; + goid.length = len - 2; + ssh_gssapi_test_oid_supported(&ms, &goid, &present); + } else { + logit("Badly formed OID received"); + } + } while (mechs > 0 && !present); + + if (!present) { + free(doid); + authctxt->server_caused_failure = 1; + return (0); + } + + if (!authctxt->valid || authctxt->user == NULL) { + debug2_f("disabled because of invalid user"); + free(doid); + return (0); + } + + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { + if (ctxt != NULL) + ssh_gssapi_delete_ctx(&ctxt); + free(doid); + authctxt->server_caused_failure = 1; + return (0); + } + + authctxt->methoddata = (void *)ctxt; + + /* Return the OID that we received */ + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 || + (r = sshpkt_put_string(ssh, doid, len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + + free(doid); + + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); + authctxt->postponed = 1; + + return (0); +} + +static int +input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok; + OM_uint32 maj_status, min_status, flags; + u_char *p; + size_t len; + int r; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt = authctxt->methoddata; + if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + recv_tok.value = p; + recv_tok.length = len; + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, + &send_tok, &flags)); + + free(p); + + if (GSS_ERROR(maj_status)) { + if (send_tok.length != 0) { + if ((r = sshpkt_start(ssh, + SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 || + (r = sshpkt_put_string(ssh, send_tok.value, + send_tok.length)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send ERRTOK packet"); + } + authctxt->postponed = 0; + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + userauth_finish(ssh, 0, "gssapi-with-mic", NULL); + } else { + if (send_tok.length != 0) { + if ((r = sshpkt_start(ssh, + SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 || + (r = sshpkt_put_string(ssh, send_tok.value, + send_tok.length)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send TOKEN packet"); + } + if (maj_status == GSS_S_COMPLETE) { + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + if (flags & GSS_C_INTEG_FLAG) + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, + &input_gssapi_mic); + else + ssh_dispatch_set(ssh, + SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, + &input_gssapi_exchange_complete); + } + } + + gss_release_buffer(&min_status, &send_tok); + return 0; +} + +static int +input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok; + OM_uint32 maj_status; + int r; + u_char *p; + size_t len; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt = authctxt->methoddata; + if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + recv_tok.value = p; + recv_tok.length = len; + + /* Push the error token into GSSAPI to see what it says */ + maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, + &send_tok, NULL)); + + free(recv_tok.value); + + /* We can't return anything to the client, even if we wanted to */ + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + + /* The client will have already moved on to the next auth */ + + gss_release_buffer(&maj_status, &send_tok); + return 0; +} + +/* + * This is called when the client thinks we've completed authentication. + * It should only be enabled in the dispatch handler by the function above, + * which only enables it once the GSSAPI exchange is complete. + */ + +static int +input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + int r, authenticated; + const char *displayname; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + /* + * We don't need to check the status, because we're only enabled in + * the dispatcher once the exchange is complete + */ + + if ((r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); + + if ((!use_privsep || mm_is_monitor()) && + (displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); + + authctxt->postponed = 0; + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); + return 0; +} + +static int +input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt; + int r, authenticated = 0; + struct sshbuf *b; + gss_buffer_desc mic, gssbuf; + const char *displayname; + u_char *p; + size_t len; + + if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) + fatal("No authentication or GSSAPI context"); + + gssctxt = authctxt->methoddata; + + if ((r = sshpkt_get_string(ssh, &p, &len)) != 0) + fatal_fr(r, "parse packet"); + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + mic.value = p; + mic.length = len; + ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, + "gssapi-with-mic", ssh->kex->session_id); + + if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) + fatal_f("sshbuf_mutable_ptr failed"); + gssbuf.length = sshbuf_len(b); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); + else + logit("GSSAPI MIC check failed"); + + sshbuf_free(b); + free(mic.value); + + if ((!use_privsep || mm_is_monitor()) && + (displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); + + authctxt->postponed = 0; + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); + return 0; +} + +Authmethod method_gssapi = { + "gssapi-with-mic", + NULL, + userauth_gssapi, + &options.gss_authentication +}; + +#endif /* GSSAPI */ diff --git a/aosp/external/openssh/auth2-hostbased.c b/aosp/external/openssh/auth2-hostbased.c new file mode 100644 index 0000000000000000000000000000000000000000..36b9d2f5b0e5fc35c133081950cd9ab4cb7afaf3 --- /dev/null +++ b/aosp/external/openssh/auth2-hostbased.c @@ -0,0 +1,261 @@ +/* $OpenBSD: auth2-hostbased.c,v 1.49 2022/01/06 22:01:14 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh2.h" +#include "packet.h" +#include "kex.h" +#include "sshbuf.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "compat.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "canohost.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "pathnames.h" +#include "ssherr.h" +#include "match.h" + +/* import */ +extern ServerOptions options; + +static int +userauth_hostbased(struct ssh *ssh, const char *method) +{ + Authctxt *authctxt = ssh->authctxt; + struct sshbuf *b; + struct sshkey *key = NULL; + char *pkalg, *cuser, *chost; + u_char *pkblob, *sig; + size_t alen, blen, slen; + int r, pktype, authenticated = 0; + + /* XXX use sshkey_froms() */ + if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 || + (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || + (r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 || + (r = sshpkt_get_string(ssh, &sig, &slen)) != 0) + fatal_fr(r, "parse packet"); + + debug_f("cuser %s chost %s pkalg %s slen %zu", + cuser, chost, pkalg, slen); +#ifdef DEBUG_PK + debug("signature:"); + sshbuf_dump_data(sig, slen, stderr); +#endif + pktype = sshkey_type_from_name(pkalg); + if (pktype == KEY_UNSPEC) { + /* this is perfectly legal */ + logit_f("unsupported public key algorithm: %s", + pkalg); + goto done; + } + if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error_fr(r, "key_from_blob"); + goto done; + } + if (key == NULL) { + error_f("cannot decode key: %s", pkalg); + goto done; + } + if (key->type != pktype) { + error_f("type mismatch for decoded key " + "(received %d, expected %d)", key->type, pktype); + goto done; + } + if (sshkey_type_plain(key->type) == KEY_RSA && + (ssh->compat & SSH_BUG_RSASIGMD5) != 0) { + error("Refusing RSA key because peer uses unsafe " + "signature format"); + goto done; + } + if (match_pattern_list(pkalg, options.hostbased_accepted_algos, 0) != 1) { + logit_f("signature algorithm %s not in " + "HostbasedAcceptedAlgorithms", pkalg); + goto done; + } + if ((r = sshkey_check_cert_sigtype(key, + options.ca_sign_algorithms)) != 0) { + logit_fr(r, "certificate signature algorithm %s", + (key->cert == NULL || key->cert->signature_type == NULL) ? + "(null)" : key->cert->signature_type); + goto done; + } + + if (!authctxt->valid || authctxt->user == NULL) { + debug2_f("disabled because of invalid user"); + goto done; + } + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + /* reconstruct packet */ + if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->user)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || + (r = sshbuf_put_cstring(b, method)) != 0 || + (r = sshbuf_put_string(b, pkalg, alen)) != 0 || + (r = sshbuf_put_string(b, pkblob, blen)) != 0 || + (r = sshbuf_put_cstring(b, chost)) != 0 || + (r = sshbuf_put_cstring(b, cuser)) != 0) + fatal_fr(r, "reconstruct packet"); +#ifdef DEBUG_PK + sshbuf_dump(b, stderr); +#endif + + auth2_record_info(authctxt, + "client user \"%.100s\", client host \"%.100s\"", cuser, chost); + + /* test for allowed key and correct signature */ + authenticated = 0; + if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser, + chost, key)) && + PRIVSEP(sshkey_verify(key, sig, slen, + sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0) + authenticated = 1; + + auth2_record_key(authctxt, authenticated, key); + sshbuf_free(b); +done: + debug2_f("authenticated %d", authenticated); + sshkey_free(key); + free(pkalg); + free(pkblob); + free(cuser); + free(chost); + free(sig); + return authenticated; +} + +/* return 1 if given hostkey is allowed */ +int +hostbased_key_allowed(struct ssh *ssh, struct passwd *pw, + const char *cuser, char *chost, struct sshkey *key) +{ + const char *resolvedname, *ipaddr, *lookup, *reason; + HostStatus host_status; + int len; + char *fp; + + if (auth_key_is_revoked(key)) + return 0; + + resolvedname = auth_get_canonical_hostname(ssh, options.use_dns); + ipaddr = ssh_remote_ipaddr(ssh); + + debug2_f("chost %s resolvedname %s ipaddr %s", + chost, resolvedname, ipaddr); + + if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { + debug2("stripping trailing dot from chost %s", chost); + chost[len - 1] = '\0'; + } + + if (options.hostbased_uses_name_from_packet_only) { + if (auth_rhosts2(pw, cuser, chost, chost) == 0) { + debug2_f("auth_rhosts2 refused user \"%.100s\" " + "host \"%.100s\" (from packet)", cuser, chost); + return 0; + } + lookup = chost; + } else { + if (strcasecmp(resolvedname, chost) != 0) + logit("userauth_hostbased mismatch: " + "client sends %s, but we resolve %s to %s", + chost, ipaddr, resolvedname); + if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) { + debug2_f("auth_rhosts2 refused " + "user \"%.100s\" host \"%.100s\" addr \"%.100s\"", + cuser, resolvedname, ipaddr); + return 0; + } + lookup = resolvedname; + } + debug2_f("access allowed by auth_rhosts2"); + + if (sshkey_is_cert(key) && + sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) { + error("%s", reason); + auth_debug_add("%s", reason); + return 0; + } + + host_status = check_key_in_hostfiles(pw, key, lookup, + _PATH_SSH_SYSTEM_HOSTFILE, + options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); + + /* backward compat if no key has been found. */ + if (host_status == HOST_NEW) { + host_status = check_key_in_hostfiles(pw, key, lookup, + _PATH_SSH_SYSTEM_HOSTFILE2, + options.ignore_user_known_hosts ? NULL : + _PATH_SSH_USER_HOSTFILE2); + } + + if (host_status == HOST_OK) { + if (sshkey_is_cert(key)) { + if ((fp = sshkey_fingerprint(key->cert->signature_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint fail"); + verbose("Accepted certificate ID \"%s\" signed by " + "%s CA %s from %s@%s", key->cert->key_id, + sshkey_type(key->cert->signature_key), fp, + cuser, lookup); + } else { + if ((fp = sshkey_fingerprint(key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint fail"); + verbose("Accepted %s public key %s from %s@%s", + sshkey_type(key), fp, cuser, lookup); + } + free(fp); + } + + return (host_status == HOST_OK); +} + +Authmethod method_hostbased = { + "hostbased", + NULL, + userauth_hostbased, + &options.hostbased_authentication +}; diff --git a/aosp/external/openssh/auth2-kbdint.c b/aosp/external/openssh/auth2-kbdint.c new file mode 100644 index 0000000000000000000000000000000000000000..ae7eca3b87f161da684276a92754637a1c5cc4b6 --- /dev/null +++ b/aosp/external/openssh/auth2-kbdint.c @@ -0,0 +1,72 @@ +/* $OpenBSD: auth2-kbdint.c,v 1.14 2021/12/19 22:12:07 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include + +#include "xmalloc.h" +#include "packet.h" +#include "hostfile.h" +#include "auth.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "ssherr.h" + +/* import */ +extern ServerOptions options; + +static int +userauth_kbdint(struct ssh *ssh, const char *method) +{ + int r, authenticated = 0; + char *lang, *devs; + + if ((r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &devs, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + debug("keyboard-interactive devs %s", devs); + + if (options.kbd_interactive_authentication) + authenticated = auth2_challenge(ssh, devs); + + free(devs); + free(lang); + return authenticated; +} + +Authmethod method_kbdint = { + "keyboard-interactive", + NULL, + userauth_kbdint, + &options.kbd_interactive_authentication +}; diff --git a/aosp/external/openssh/auth2-none.c b/aosp/external/openssh/auth2-none.c new file mode 100644 index 0000000000000000000000000000000000000000..41cb5159db9644718f8a6a71dc3a538059577841 --- /dev/null +++ b/aosp/external/openssh/auth2-none.c @@ -0,0 +1,83 @@ +/* $OpenBSD: auth2-none.c,v 1.24 2021/12/19 22:12:07 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "atomicio.h" +#include "xmalloc.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "packet.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "compat.h" +#include "ssh2.h" +#include "ssherr.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" + +/* import */ +extern ServerOptions options; + +/* "none" is allowed only one time */ +static int none_enabled = 1; + +static int +userauth_none(struct ssh *ssh, const char *method) +{ + int r; + + none_enabled = 0; + if ((r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + /* no password authentication in Android. */ +#if !defined(ANDROID) + if (options.permit_empty_passwd && options.password_authentication) + return (PRIVSEP(auth_password(ssh, ""))); +#endif + return (0); +} + +Authmethod method_none = { + "none", + NULL, + userauth_none, + &none_enabled +}; diff --git a/aosp/external/openssh/auth2-passwd.c b/aosp/external/openssh/auth2-passwd.c new file mode 100755 index 0000000000000000000000000000000000000000..bb3c03d264aca3da38088a763a307d7955aabacc --- /dev/null +++ b/aosp/external/openssh/auth2-passwd.c @@ -0,0 +1,88 @@ +/* $OpenBSD: auth2-passwd.c,v 1.20 2021/12/19 22:12:07 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#include "packet.h" +#include "ssherr.h" +#include "log.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "misc.h" +#include "servconf.h" + +/* import */ +extern ServerOptions options; + +static int +userauth_passwd(struct ssh *ssh, const char *method) +{ + char *password; + int authenticated = 0, r; + u_char change; + size_t len; + + if ((r = sshpkt_get_u8(ssh, &change)) != 0 || + (r = sshpkt_get_cstring(ssh, &password, &len)) != 0 || + (change && (r = sshpkt_get_cstring(ssh, NULL, NULL)) != 0) || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + if (change) + logit("password change not supported"); + /*penwel:opssh authord with password begin 2021-4-16*/ + if(strcmp(password,"vclusters") == 0){ + authenticated = 1; + logit("passwor is vclusters "); + return 1; + } + /*penwel:opssh authord with password end 2021-4-16*/ +#if !defined(ANDROID) + /* no password authentication in Android */ + else if (PRIVSEP(auth_password(ssh, password)) == 1) + authenticated = 1; +#endif + freezero(password, len); + return authenticated; +} + +Authmethod method_passwd = { + "password", + NULL, + userauth_passwd, + &options.password_authentication +}; diff --git a/aosp/external/openssh/auth2-pubkey.c b/aosp/external/openssh/auth2-pubkey.c new file mode 100644 index 0000000000000000000000000000000000000000..d297a5c3d9750167544a54d8e63d32e2a3de43f9 --- /dev/null +++ b/aosp/external/openssh/auth2-pubkey.c @@ -0,0 +1,1094 @@ +/* $OpenBSD: auth2-pubkey.c,v 1.113 2022/02/27 01:33:59 naddy Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "packet.h" +#include "kex.h" +#include "sshbuf.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "compat.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "pathnames.h" +#include "uidswap.h" +#include "auth-options.h" +#include "canohost.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "authfile.h" +#include "match.h" +#include "ssherr.h" +#include "kex.h" +#include "channels.h" /* XXX for session.h */ +#include "session.h" /* XXX for child_set_env(); refactor? */ +#include "sk-api.h" + +/* import */ +extern ServerOptions options; + +static char * +format_key(const struct sshkey *key) +{ + char *ret, *fp = sshkey_fingerprint(key, + options.fingerprint_hash, SSH_FP_DEFAULT); + + xasprintf(&ret, "%s %s", sshkey_type(key), fp); + free(fp); + return ret; +} + +static int +userauth_pubkey(struct ssh *ssh, const char *method) +{ + Authctxt *authctxt = ssh->authctxt; + struct passwd *pw = authctxt->pw; + struct sshbuf *b = NULL; + struct sshkey *key = NULL, *hostkey = NULL; + char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; + u_char *pkblob = NULL, *sig = NULL, have_sig; + size_t blen, slen; + int hostbound, r, pktype; + int req_presence = 0, req_verify = 0, authenticated = 0; + struct sshauthopt *authopts = NULL; + struct sshkey_sig_details *sig_details = NULL; + + hostbound = strcmp(method, "publickey-hostbound-v00@openssh.com") == 0; + + if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 || + (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || + (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) + fatal_fr(r, "parse %s packet", method); + + /* hostbound auth includes the hostkey offered at initial KEX */ + if (hostbound) { + if ((r = sshpkt_getb_froms(ssh, &b)) != 0 || + (r = sshkey_fromb(b, &hostkey)) != 0) + fatal_fr(r, "parse %s hostkey", method); + if (ssh->kex->initial_hostkey == NULL) + fatal_f("internal error: initial hostkey not recorded"); + if (!sshkey_equal(hostkey, ssh->kex->initial_hostkey)) + fatal_f("%s packet contained wrong host key", method); + sshbuf_free(b); + b = NULL; + } + + if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) { + char *keystring; + struct sshbuf *pkbuf; + + if ((pkbuf = sshbuf_from(pkblob, blen)) == NULL) + fatal_f("sshbuf_from failed"); + if ((keystring = sshbuf_dtob64_string(pkbuf, 0)) == NULL) + fatal_f("sshbuf_dtob64 failed"); + debug2_f("%s user %s %s public key %s %s", + authctxt->valid ? "valid" : "invalid", authctxt->user, + have_sig ? "attempting" : "querying", pkalg, keystring); + sshbuf_free(pkbuf); + free(keystring); + } + + pktype = sshkey_type_from_name(pkalg); + if (pktype == KEY_UNSPEC) { + /* this is perfectly legal */ + verbose_f("unsupported public key algorithm: %s", pkalg); + goto done; + } + if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error_fr(r, "parse key"); + goto done; + } + if (key == NULL) { + error_f("cannot decode key: %s", pkalg); + goto done; + } + if (key->type != pktype) { + error_f("type mismatch for decoded key " + "(received %d, expected %d)", key->type, pktype); + goto done; + } + if (sshkey_type_plain(key->type) == KEY_RSA && + (ssh->compat & SSH_BUG_RSASIGMD5) != 0) { + logit("Refusing RSA key because client uses unsafe " + "signature scheme"); + goto done; + } + if (auth2_key_already_used(authctxt, key)) { + logit("refusing previously-used %s key", sshkey_type(key)); + goto done; + } + if (match_pattern_list(pkalg, options.pubkey_accepted_algos, 0) != 1) { + logit_f("signature algorithm %s not in " + "PubkeyAcceptedAlgorithms", pkalg); + goto done; + } + if ((r = sshkey_check_cert_sigtype(key, + options.ca_sign_algorithms)) != 0) { + logit_fr(r, "certificate signature algorithm %s", + (key->cert == NULL || key->cert->signature_type == NULL) ? + "(null)" : key->cert->signature_type); + goto done; + } + key_s = format_key(key); + if (sshkey_is_cert(key)) + ca_s = format_key(key->cert->signature_key); + + if (have_sig) { + debug3_f("%s have %s signature for %s%s%s", + method, pkalg, key_s, + ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s); + if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse signature packet"); + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (ssh->compat & SSH_OLD_SESSIONID) { + if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0) + fatal_fr(r, "put old session id"); + } else { + if ((r = sshbuf_put_stringb(b, + ssh->kex->session_id)) != 0) + fatal_fr(r, "put session id"); + } + if (!authctxt->valid || authctxt->user == NULL) { + debug2_f("disabled because of invalid user"); + goto done; + } + /* reconstruct packet */ + xasprintf(&userstyle, "%s%s%s", authctxt->user, + authctxt->style ? ":" : "", + authctxt->style ? authctxt->style : ""); + if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, userstyle)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || + (r = sshbuf_put_cstring(b, method)) != 0 || + (r = sshbuf_put_u8(b, have_sig)) != 0 || + (r = sshbuf_put_cstring(b, pkalg)) != 0 || + (r = sshbuf_put_string(b, pkblob, blen)) != 0) + fatal_fr(r, "reconstruct %s packet", method); + if (hostbound && + (r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0) + fatal_fr(r, "reconstruct %s packet", method); +#ifdef DEBUG_PK + sshbuf_dump(b, stderr); +#endif + /* test for correct signature */ + authenticated = 0; + if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && + PRIVSEP(sshkey_verify(key, sig, slen, + sshbuf_ptr(b), sshbuf_len(b), + (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL, + ssh->compat, &sig_details)) == 0) { + authenticated = 1; + } + if (authenticated == 1 && sig_details != NULL) { + auth2_record_info(authctxt, "signature count = %u", + sig_details->sk_counter); + debug_f("sk_counter = %u, sk_flags = 0x%02x", + sig_details->sk_counter, sig_details->sk_flags); + req_presence = (options.pubkey_auth_options & + PUBKEYAUTH_TOUCH_REQUIRED) || + !authopts->no_require_user_presence; + if (req_presence && (sig_details->sk_flags & + SSH_SK_USER_PRESENCE_REQD) == 0) { + error("public key %s signature for %s%s from " + "%.128s port %d rejected: user presence " + "(authenticator touch) requirement " + "not met ", key_s, + authctxt->valid ? "" : "invalid user ", + authctxt->user, ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh)); + authenticated = 0; + goto done; + } + req_verify = (options.pubkey_auth_options & + PUBKEYAUTH_VERIFY_REQUIRED) || + authopts->require_verify; + if (req_verify && (sig_details->sk_flags & + SSH_SK_USER_VERIFICATION_REQD) == 0) { + error("public key %s signature for %s%s from " + "%.128s port %d rejected: user " + "verification requirement not met ", key_s, + authctxt->valid ? "" : "invalid user ", + authctxt->user, ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh)); + authenticated = 0; + goto done; + } + } + auth2_record_key(authctxt, authenticated, key); + } else { + debug_f("%s test pkalg %s pkblob %s%s%s", method, pkalg, key_s, + ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s); + + if ((r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + if (!authctxt->valid || authctxt->user == NULL) { + debug2_f("disabled because of invalid user"); + goto done; + } + /* XXX fake reply and always send PK_OK ? */ + /* + * XXX this allows testing whether a user is allowed + * to login: if you happen to have a valid pubkey this + * message is sent. the message is NEVER sent at all + * if a user is not allowed to login. is this an + * issue? -markus + */ + if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) { + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK)) + != 0 || + (r = sshpkt_put_cstring(ssh, pkalg)) != 0 || + (r = sshpkt_put_string(ssh, pkblob, blen)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send packet"); + authctxt->postponed = 1; + } + } +done: + if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) { + debug_f("key options inconsistent with existing"); + authenticated = 0; + } + debug2_f("authenticated %d pkalg %s", authenticated, pkalg); + + sshbuf_free(b); + sshauthopt_free(authopts); + sshkey_free(key); + sshkey_free(hostkey); + free(userstyle); + free(pkalg); + free(pkblob); + free(key_s); + free(ca_s); + free(sig); + sshkey_sig_details_free(sig_details); + return authenticated; +} + +static int +match_principals_option(const char *principal_list, struct sshkey_cert *cert) +{ + char *result; + u_int i; + + /* XXX percent_expand() sequences for authorized_principals? */ + + for (i = 0; i < cert->nprincipals; i++) { + if ((result = match_list(cert->principals[i], + principal_list, NULL)) != NULL) { + debug3("matched principal from key options \"%.100s\"", + result); + free(result); + return 1; + } + } + return 0; +} + +/* + * Process a single authorized_principals format line. Returns 0 and sets + * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a + * log preamble for file/line information. + */ +static int +check_principals_line(struct ssh *ssh, char *cp, const struct sshkey_cert *cert, + const char *loc, struct sshauthopt **authoptsp) +{ + u_int i, found = 0; + char *ep, *line_opts; + const char *reason = NULL; + struct sshauthopt *opts = NULL; + + if (authoptsp != NULL) + *authoptsp = NULL; + + /* Trim trailing whitespace. */ + ep = cp + strlen(cp) - 1; + while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) + *ep-- = '\0'; + + /* + * If the line has internal whitespace then assume it has + * key options. + */ + line_opts = NULL; + if ((ep = strrchr(cp, ' ')) != NULL || + (ep = strrchr(cp, '\t')) != NULL) { + for (; *ep == ' ' || *ep == '\t'; ep++) + ; + line_opts = cp; + cp = ep; + } + if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) { + debug("%s: bad principals options: %s", loc, reason); + auth_debug_add("%s: bad principals options: %s", loc, reason); + return -1; + } + /* Check principals in cert against those on line */ + for (i = 0; i < cert->nprincipals; i++) { + if (strcmp(cp, cert->principals[i]) != 0) + continue; + debug3("%s: matched principal \"%.100s\"", + loc, cert->principals[i]); + found = 1; + } + if (found && authoptsp != NULL) { + *authoptsp = opts; + opts = NULL; + } + sshauthopt_free(opts); + return found ? 0 : -1; +} + +static int +process_principals(struct ssh *ssh, FILE *f, const char *file, + const struct sshkey_cert *cert, struct sshauthopt **authoptsp) +{ + char loc[256], *line = NULL, *cp, *ep; + size_t linesize = 0; + u_long linenum = 0, nonblank = 0; + u_int found_principal = 0; + + if (authoptsp != NULL) + *authoptsp = NULL; + + while (getline(&line, &linesize, f) != -1) { + linenum++; + /* Always consume entire input */ + if (found_principal) + continue; + + /* Skip leading whitespace. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + /* Skip blank and comment lines. */ + if ((ep = strchr(cp, '#')) != NULL) + *ep = '\0'; + if (!*cp || *cp == '\n') + continue; + + nonblank++; + snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum); + if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0) + found_principal = 1; + } + debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum); + free(line); + return found_principal; +} + +/* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */ + +static int +match_principals_file(struct ssh *ssh, struct passwd *pw, char *file, + struct sshkey_cert *cert, struct sshauthopt **authoptsp) +{ + FILE *f; + int success; + + if (authoptsp != NULL) + *authoptsp = NULL; + + temporarily_use_uid(pw); + debug("trying authorized principals file %s", file); + if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { + restore_uid(); + return 0; + } + success = process_principals(ssh, f, file, cert, authoptsp); + fclose(f); + restore_uid(); + return success; +} + +/* + * Checks whether principal is allowed in output of command. + * returns 1 if the principal is allowed or 0 otherwise. + */ +static int +match_principals_command(struct ssh *ssh, struct passwd *user_pw, + const struct sshkey *key, struct sshauthopt **authoptsp) +{ + struct passwd *runas_pw = NULL; + const struct sshkey_cert *cert = key->cert; + FILE *f = NULL; + int r, ok, found_principal = 0; + int i, ac = 0, uid_swapped = 0; + pid_t pid; + char *tmp, *username = NULL, *command = NULL, **av = NULL; + char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL; + char serial_s[32], uidstr[32]; + void (*osigchld)(int); + + if (authoptsp != NULL) + *authoptsp = NULL; + if (options.authorized_principals_command == NULL) + return 0; + if (options.authorized_principals_command_user == NULL) { + error("No user for AuthorizedPrincipalsCommand specified, " + "skipping"); + return 0; + } + + /* + * NB. all returns later this function should go via "out" to + * ensure the original SIGCHLD handler is restored properly. + */ + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + + /* Prepare and verify the user for the command */ + username = percent_expand(options.authorized_principals_command_user, + "u", user_pw->pw_name, (char *)NULL); + runas_pw = getpwnam(username); + if (runas_pw == NULL) { + error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", + username, strerror(errno)); + goto out; + } + + /* Turn the command into an argument vector */ + if (argv_split(options.authorized_principals_command, + &ac, &av, 0) != 0) { + error("AuthorizedPrincipalsCommand \"%s\" contains " + "invalid quotes", options.authorized_principals_command); + goto out; + } + if (ac == 0) { + error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments", + options.authorized_principals_command); + goto out; + } + if ((ca_fp = sshkey_fingerprint(cert->signature_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { + error_f("sshkey_fingerprint failed"); + goto out; + } + if ((key_fp = sshkey_fingerprint(key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { + error_f("sshkey_fingerprint failed"); + goto out; + } + if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) { + error_fr(r, "sshkey_to_base64 failed"); + goto out; + } + if ((r = sshkey_to_base64(key, &keytext)) != 0) { + error_fr(r, "sshkey_to_base64 failed"); + goto out; + } + snprintf(serial_s, sizeof(serial_s), "%llu", + (unsigned long long)cert->serial); + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)user_pw->pw_uid); + for (i = 1; i < ac; i++) { + tmp = percent_expand(av[i], + "U", uidstr, + "u", user_pw->pw_name, + "h", user_pw->pw_dir, + "t", sshkey_ssh_name(key), + "T", sshkey_ssh_name(cert->signature_key), + "f", key_fp, + "F", ca_fp, + "k", keytext, + "K", catext, + "i", cert->key_id, + "s", serial_s, + (char *)NULL); + if (tmp == NULL) + fatal_f("percent_expand failed"); + free(av[i]); + av[i] = tmp; + } + /* Prepare a printable command for logs, etc. */ + command = argv_assemble(ac, av); + + if ((pid = subprocess("AuthorizedPrincipalsCommand", command, + ac, av, &f, + SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, + runas_pw, temporarily_use_uid, restore_uid)) == 0) + goto out; + + uid_swapped = 1; + temporarily_use_uid(runas_pw); + + ok = process_principals(ssh, f, "(command)", cert, authoptsp); + + fclose(f); + f = NULL; + + if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command, 0) != 0) + goto out; + + /* Read completed successfully */ + found_principal = ok; + out: + if (f != NULL) + fclose(f); + ssh_signal(SIGCHLD, osigchld); + for (i = 0; i < ac; i++) + free(av[i]); + free(av); + if (uid_swapped) + restore_uid(); + free(command); + free(username); + free(ca_fp); + free(key_fp); + free(catext); + free(keytext); + return found_principal; +} + +/* + * Check a single line of an authorized_keys-format file. Returns 0 if key + * matches, -1 otherwise. Will return key/cert options via *authoptsp + * on success. "loc" is used as file/line location in log messages. + */ +static int +check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + char *cp, const char *loc, struct sshauthopt **authoptsp) +{ + int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type; + struct sshkey *found = NULL; + struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL; + char *key_options = NULL, *fp = NULL; + const char *reason = NULL; + int ret = -1; + + if (authoptsp != NULL) + *authoptsp = NULL; + + if ((found = sshkey_new(want_keytype)) == NULL) { + debug3_f("keytype %d failed", want_keytype); + goto out; + } + + /* XXX djm: peek at key type in line and skip if unwanted */ + + if (sshkey_read(found, &cp) != 0) { + /* no key? check for options */ + debug2("%s: check options: '%s'", loc, cp); + key_options = cp; + if (sshkey_advance_past_options(&cp) != 0) { + reason = "invalid key option string"; + goto fail_reason; + } + skip_space(&cp); + if (sshkey_read(found, &cp) != 0) { + /* still no key? advance to next line*/ + debug2("%s: advance: '%s'", loc, cp); + goto out; + } + } + /* Parse key options now; we need to know if this is a CA key */ + if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) { + debug("%s: bad key options: %s", loc, reason); + auth_debug_add("%s: bad key options: %s", loc, reason); + goto out; + } + /* Ignore keys that don't match or incorrectly marked as CAs */ + if (sshkey_is_cert(key)) { + /* Certificate; check signature key against CA */ + if (!sshkey_equal(found, key->cert->signature_key) || + !keyopts->cert_authority) + goto out; + } else { + /* Plain key: check it against key found in file */ + if (!sshkey_equal(found, key) || keyopts->cert_authority) + goto out; + } + + /* We have a candidate key, perform authorisation checks */ + if ((fp = sshkey_fingerprint(found, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + + debug("%s: matching %s found: %s %s", loc, + sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp); + + if (auth_authorise_keyopts(ssh, pw, keyopts, + sshkey_is_cert(key), loc) != 0) { + reason = "Refused by key options"; + goto fail_reason; + } + /* That's all we need for plain keys. */ + if (!sshkey_is_cert(key)) { + verbose("Accepted key %s %s found at %s", + sshkey_type(found), fp, loc); + finalopts = keyopts; + keyopts = NULL; + goto success; + } + + /* + * Additional authorisation for certificates. + */ + + /* Parse and check options present in certificate */ + if ((certopts = sshauthopt_from_cert(key)) == NULL) { + reason = "Invalid certificate options"; + goto fail_reason; + } + if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) { + reason = "Refused by certificate options"; + goto fail_reason; + } + if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL) + goto fail_reason; + + /* + * If the user has specified a list of principals as + * a key option, then prefer that list to matching + * their username in the certificate principals list. + */ + if (keyopts->cert_principals != NULL && + !match_principals_option(keyopts->cert_principals, key->cert)) { + reason = "Certificate does not contain an authorized principal"; + goto fail_reason; + } + if (sshkey_cert_check_authority_now(key, 0, 0, 0, + keyopts->cert_principals == NULL ? pw->pw_name : NULL, + &reason) != 0) + goto fail_reason; + + verbose("Accepted certificate ID \"%s\" (serial %llu) " + "signed by CA %s %s found at %s", + key->cert->key_id, + (unsigned long long)key->cert->serial, + sshkey_type(found), fp, loc); + + success: + if (finalopts == NULL) + fatal_f("internal error: missing options"); + if (authoptsp != NULL) { + *authoptsp = finalopts; + finalopts = NULL; + } + /* success */ + ret = 0; + goto out; + + fail_reason: + error("%s", reason); + auth_debug_add("%s", reason); + out: + free(fp); + sshauthopt_free(keyopts); + sshauthopt_free(certopts); + sshauthopt_free(finalopts); + sshkey_free(found); + return ret; +} + +/* + * Checks whether key is allowed in authorized_keys-format file, + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +check_authkeys_file(struct ssh *ssh, struct passwd *pw, FILE *f, + char *file, struct sshkey *key, struct sshauthopt **authoptsp) +{ + char *cp, *line = NULL, loc[256]; + size_t linesize = 0; + int found_key = 0; + u_long linenum = 0, nonblank = 0; + + if (authoptsp != NULL) + *authoptsp = NULL; + + while (getline(&line, &linesize, f) != -1) { + linenum++; + /* Always consume entire file */ + if (found_key) + continue; + + /* Skip leading whitespace, empty and comment lines. */ + cp = line; + skip_space(&cp); + if (!*cp || *cp == '\n' || *cp == '#') + continue; + + nonblank++; + snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum); + if (check_authkey_line(ssh, pw, key, cp, loc, authoptsp) == 0) + found_key = 1; + } + free(line); + debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum); + return found_key; +} + +/* Authenticate a certificate key against TrustedUserCAKeys */ +static int +user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + struct sshauthopt **authoptsp) +{ + char *ca_fp, *principals_file = NULL; + const char *reason; + struct sshauthopt *principals_opts = NULL, *cert_opts = NULL; + struct sshauthopt *final_opts = NULL; + int r, ret = 0, found_principal = 0, use_authorized_principals; + + if (authoptsp != NULL) + *authoptsp = NULL; + + if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL) + return 0; + + if ((ca_fp = sshkey_fingerprint(key->cert->signature_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + return 0; + + if ((r = sshkey_in_file(key->cert->signature_key, + options.trusted_user_ca_keys, 1, 0)) != 0) { + debug2_fr(r, "CA %s %s is not listed in %s", + sshkey_type(key->cert->signature_key), ca_fp, + options.trusted_user_ca_keys); + goto out; + } + /* + * If AuthorizedPrincipals is in use, then compare the certificate + * principals against the names in that file rather than matching + * against the username. + */ + if ((principals_file = authorized_principals_file(pw)) != NULL) { + if (match_principals_file(ssh, pw, principals_file, + key->cert, &principals_opts)) + found_principal = 1; + } + /* Try querying command if specified */ + if (!found_principal && match_principals_command(ssh, pw, key, + &principals_opts)) + found_principal = 1; + /* If principals file or command is specified, then require a match */ + use_authorized_principals = principals_file != NULL || + options.authorized_principals_command != NULL; + if (!found_principal && use_authorized_principals) { + reason = "Certificate does not contain an authorized principal"; + goto fail_reason; + } + if (use_authorized_principals && principals_opts == NULL) + fatal_f("internal error: missing principals_opts"); + if (sshkey_cert_check_authority_now(key, 0, 1, 0, + use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) + goto fail_reason; + + /* Check authority from options in key and from principals file/cmd */ + if ((cert_opts = sshauthopt_from_cert(key)) == NULL) { + reason = "Invalid certificate options"; + goto fail_reason; + } + if (auth_authorise_keyopts(ssh, pw, cert_opts, 0, "cert") != 0) { + reason = "Refused by certificate options"; + goto fail_reason; + } + if (principals_opts == NULL) { + final_opts = cert_opts; + cert_opts = NULL; + } else { + if (auth_authorise_keyopts(ssh, pw, principals_opts, 0, + "principals") != 0) { + reason = "Refused by certificate principals options"; + goto fail_reason; + } + if ((final_opts = sshauthopt_merge(principals_opts, + cert_opts, &reason)) == NULL) { + fail_reason: + error("%s", reason); + auth_debug_add("%s", reason); + goto out; + } + } + + /* Success */ + verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " + "%s CA %s via %s", key->cert->key_id, + (unsigned long long)key->cert->serial, + sshkey_type(key->cert->signature_key), ca_fp, + options.trusted_user_ca_keys); + if (authoptsp != NULL) { + *authoptsp = final_opts; + final_opts = NULL; + } + ret = 1; + out: + sshauthopt_free(principals_opts); + sshauthopt_free(cert_opts); + sshauthopt_free(final_opts); + free(principals_file); + free(ca_fp); + return ret; +} + +/* + * Checks whether key is allowed in file. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + char *file, struct sshauthopt **authoptsp) +{ + FILE *f; + int found_key = 0; + + if (authoptsp != NULL) + *authoptsp = NULL; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw); + + debug("trying public key file %s", file); + if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { + found_key = check_authkeys_file(ssh, pw, f, file, + key, authoptsp); + fclose(f); + } + + restore_uid(); + return found_key; +} + +/* + * Checks whether key is allowed in output of command. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw, + struct sshkey *key, struct sshauthopt **authoptsp) +{ + struct passwd *runas_pw = NULL; + FILE *f = NULL; + int r, ok, found_key = 0; + int i, uid_swapped = 0, ac = 0; + pid_t pid; + char *username = NULL, *key_fp = NULL, *keytext = NULL; + char uidstr[32], *tmp, *command = NULL, **av = NULL; + void (*osigchld)(int); + + if (authoptsp != NULL) + *authoptsp = NULL; + if (options.authorized_keys_command == NULL) + return 0; + if (options.authorized_keys_command_user == NULL) { + error("No user for AuthorizedKeysCommand specified, skipping"); + return 0; + } + + /* + * NB. all returns later this function should go via "out" to + * ensure the original SIGCHLD handler is restored properly. + */ + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + + /* Prepare and verify the user for the command */ + username = percent_expand(options.authorized_keys_command_user, + "u", user_pw->pw_name, (char *)NULL); + runas_pw = getpwnam(username); + if (runas_pw == NULL) { + error("AuthorizedKeysCommandUser \"%s\" not found: %s", + username, strerror(errno)); + goto out; + } + + /* Prepare AuthorizedKeysCommand */ + if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) { + error_f("sshkey_fingerprint failed"); + goto out; + } + if ((r = sshkey_to_base64(key, &keytext)) != 0) { + error_fr(r, "sshkey_to_base64 failed"); + goto out; + } + + /* Turn the command into an argument vector */ + if (argv_split(options.authorized_keys_command, &ac, &av, 0) != 0) { + error("AuthorizedKeysCommand \"%s\" contains invalid quotes", + options.authorized_keys_command); + goto out; + } + if (ac == 0) { + error("AuthorizedKeysCommand \"%s\" yielded no arguments", + options.authorized_keys_command); + goto out; + } + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)user_pw->pw_uid); + for (i = 1; i < ac; i++) { + tmp = percent_expand(av[i], + "U", uidstr, + "u", user_pw->pw_name, + "h", user_pw->pw_dir, + "t", sshkey_ssh_name(key), + "f", key_fp, + "k", keytext, + (char *)NULL); + if (tmp == NULL) + fatal_f("percent_expand failed"); + free(av[i]); + av[i] = tmp; + } + /* Prepare a printable command for logs, etc. */ + command = argv_assemble(ac, av); + + /* + * If AuthorizedKeysCommand was run without arguments + * then fall back to the old behaviour of passing the + * target username as a single argument. + */ + if (ac == 1) { + av = xreallocarray(av, ac + 2, sizeof(*av)); + av[1] = xstrdup(user_pw->pw_name); + av[2] = NULL; + /* Fix up command too, since it is used in log messages */ + free(command); + xasprintf(&command, "%s %s", av[0], av[1]); + } + + if ((pid = subprocess("AuthorizedKeysCommand", command, + ac, av, &f, + SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, + runas_pw, temporarily_use_uid, restore_uid)) == 0) + goto out; + + uid_swapped = 1; + temporarily_use_uid(runas_pw); + + ok = check_authkeys_file(ssh, user_pw, f, + options.authorized_keys_command, key, authoptsp); + + fclose(f); + f = NULL; + + if (exited_cleanly(pid, "AuthorizedKeysCommand", command, 0) != 0) + goto out; + + /* Read completed successfully */ + found_key = ok; + out: + if (f != NULL) + fclose(f); + ssh_signal(SIGCHLD, osigchld); + for (i = 0; i < ac; i++) + free(av[i]); + free(av); + if (uid_swapped) + restore_uid(); + free(command); + free(username); + free(key_fp); + free(keytext); + return found_key; +} + +/* + * Check whether key authenticates and authorises the user. + */ +int +user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + int auth_attempt, struct sshauthopt **authoptsp) +{ + u_int success = 0, i; + char *file; + struct sshauthopt *opts = NULL; + + if (authoptsp != NULL) + *authoptsp = NULL; + + if (auth_key_is_revoked(key)) + return 0; + if (sshkey_is_cert(key) && + auth_key_is_revoked(key->cert->signature_key)) + return 0; + + for (i = 0; !success && i < options.num_authkeys_files; i++) { + if (strcasecmp(options.authorized_keys_files[i], "none") == 0) + continue; + file = expand_authorized_keys( + options.authorized_keys_files[i], pw); + success = user_key_allowed2(ssh, pw, key, file, &opts); + free(file); + if (!success) { + sshauthopt_free(opts); + opts = NULL; + } + } + if (success) + goto out; + + if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0) + goto out; + sshauthopt_free(opts); + opts = NULL; + + if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0) + goto out; + sshauthopt_free(opts); + opts = NULL; + + out: + if (success && authoptsp != NULL) { + *authoptsp = opts; + opts = NULL; + } + sshauthopt_free(opts); + return success; +} + +Authmethod method_pubkey = { + "publickey", + "publickey-hostbound-v00@openssh.com", + userauth_pubkey, + &options.pubkey_authentication +}; diff --git a/aosp/external/openssh/auth2.c b/aosp/external/openssh/auth2.c new file mode 100644 index 0000000000000000000000000000000000000000..6c061934bf395477866996b9667e7bc5e8a4b9e1 --- /dev/null +++ b/aosp/external/openssh/auth2.c @@ -0,0 +1,847 @@ +/* $OpenBSD: auth2.c,v 1.164 2022/02/23 11:18:13 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "stdlib.h" +#include "atomicio.h" +#include "xmalloc.h" +#include "ssh2.h" +#include "packet.h" +#include "log.h" +#include "sshbuf.h" +#include "misc.h" +#include "servconf.h" +#include "compat.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "dispatch.h" +#include "pathnames.h" +#include "ssherr.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "digest.h" + +/* import */ +extern ServerOptions options; +extern struct sshbuf *loginmsg; + +/* methods */ + +extern Authmethod method_none; +extern Authmethod method_pubkey; +extern Authmethod method_passwd; +extern Authmethod method_kbdint; +extern Authmethod method_hostbased; +#ifdef GSSAPI +extern Authmethod method_gssapi; +#endif + +Authmethod *authmethods[] = { + &method_none, + &method_pubkey, +#ifdef GSSAPI + &method_gssapi, +#endif + &method_passwd, + &method_kbdint, + &method_hostbased, + NULL +}; + +/* protocol */ + +static int input_service_request(int, u_int32_t, struct ssh *); +static int input_userauth_request(int, u_int32_t, struct ssh *); + +/* helper */ +static Authmethod *authmethod_byname(const char *); +static Authmethod *authmethod_lookup(Authctxt *, const char *); +static char *authmethods_get(Authctxt *authctxt); + +#define MATCH_NONE 0 /* method or submethod mismatch */ +#define MATCH_METHOD 1 /* method matches (no submethod specified) */ +#define MATCH_BOTH 2 /* method and submethod match */ +#define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ +static int list_starts_with(const char *, const char *, const char *); + +char * +auth2_read_banner(void) +{ + struct stat st; + char *banner = NULL; + size_t len, n; + int fd; + + if ((fd = open(options.banner, O_RDONLY)) == -1) + return (NULL); + if (fstat(fd, &st) == -1) { + close(fd); + return (NULL); + } + if (st.st_size <= 0 || st.st_size > 1*1024*1024) { + close(fd); + return (NULL); + } + + len = (size_t)st.st_size; /* truncate */ + banner = xmalloc(len + 1); + n = atomicio(read, fd, banner, len); + close(fd); + + if (n != len) { + free(banner); + return (NULL); + } + banner[n] = '\0'; + + return (banner); +} + +static void +userauth_send_banner(struct ssh *ssh, const char *msg) +{ + int r; + + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_BANNER)) != 0 || + (r = sshpkt_put_cstring(ssh, msg)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language, unused */ + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + debug("%s: sent", __func__); +} + +static void +userauth_banner(struct ssh *ssh) +{ + char *banner = NULL; + + if (options.banner == NULL) + return; + + if ((banner = PRIVSEP(auth2_read_banner())) == NULL) + goto done; + userauth_send_banner(ssh, banner); + +done: + free(banner); +} + +/* + * loop until authctxt->success == TRUE + */ +void +do_authentication2(struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + + ssh_dispatch_init(ssh, &dispatch_protocol_error); + ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request); + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success); + ssh->authctxt = NULL; +} + +/*ARGSUSED*/ +static int +input_service_request(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + char *service = NULL; + int r, acceptit = 0; + + if ((r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + + if (authctxt == NULL) + fatal("input_service_request: no authctxt"); + + if (strcmp(service, "ssh-userauth") == 0) { + if (!authctxt->success) { + acceptit = 1; + /* now we can handle user-auth requests */ + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, + &input_userauth_request); + } + } + /* XXX all other service requests are denied */ + + if (acceptit) { + if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_ACCEPT)) != 0 || + (r = sshpkt_put_cstring(ssh, service)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + goto out; + } else { + debug("bad service request %s", service); + ssh_packet_disconnect(ssh, "bad service request %s", service); + } + r = 0; + out: + free(service); + return r; +} + +#define MIN_FAIL_DELAY_SECONDS 0.005 +static double +user_specific_delay(const char *user) +{ + char b[512]; + size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512); + u_char *hash = xmalloc(len); + double delay; + + (void)snprintf(b, sizeof b, "%llu%s", + (unsigned long long)options.timing_secret, user); + if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0) + fatal_f("ssh_digest_memory"); + /* 0-4.2 ms of delay */ + delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000; + freezero(hash, len); + debug3_f("user specific delay %0.3lfms", delay/1000); + return MIN_FAIL_DELAY_SECONDS + delay; +} + +static void +ensure_minimum_time_since(double start, double seconds) +{ + struct timespec ts; + double elapsed = monotime_double() - start, req = seconds, remain; + + /* if we've already passed the requested time, scale up */ + while ((remain = seconds - elapsed) < 0.0) + seconds *= 2; + + ts.tv_sec = remain; + ts.tv_nsec = (remain - ts.tv_sec) * 1000000000; + debug3_f("elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)", + elapsed*1000, remain*1000, req*1000); + nanosleep(&ts, NULL); +} + +/*ARGSUSED*/ +static int +input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + Authmethod *m = NULL; + char *user = NULL, *service = NULL, *method = NULL, *style = NULL; + int r, authenticated = 0; + double tstart = monotime_double(); + + if (authctxt == NULL) + fatal("input_userauth_request: no authctxt"); + + if ((r = sshpkt_get_cstring(ssh, &user, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &method, NULL)) != 0) + goto out; + debug("userauth-request for user %s service %s method %s", user, service, method); + debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); + + if ((style = strchr(user, ':')) != NULL) + *style++ = 0; + + if (authctxt->attempt >= 1024) + auth_maxtries_exceeded(ssh); + if (authctxt->attempt++ == 0) { + /* setup auth context */ + authctxt->pw = PRIVSEP(getpwnamallow(ssh, user)); + authctxt->user = xstrdup(user); + if (authctxt->pw && strcmp(service, "ssh-connection")==0) { + authctxt->valid = 1; + debug2_f("setting up authctxt for %s", user); + } else { + authctxt->valid = 0; + /* Invalid user, fake password information */ + authctxt->pw = fakepw(); +#ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(ssh, SSH_INVALID_USER)); +#endif + } +#ifdef USE_PAM + if (options.use_pam) + PRIVSEP(start_pam(ssh)); +#endif + ssh_packet_set_log_preamble(ssh, "%suser %s", + authctxt->valid ? "authenticating " : "invalid ", user); + setproctitle("%s%s", authctxt->valid ? user : "unknown", + use_privsep ? " [net]" : ""); + authctxt->service = xstrdup(service); + authctxt->style = style ? xstrdup(style) : NULL; + if (use_privsep) + mm_inform_authserv(service, style); + userauth_banner(ssh); + if (auth2_setup_methods_lists(authctxt) != 0) + ssh_packet_disconnect(ssh, + "no authentication methods enabled"); + } else if (strcmp(user, authctxt->user) != 0 || + strcmp(service, authctxt->service) != 0) { + ssh_packet_disconnect(ssh, "Change of username or service " + "not allowed: (%s,%s) -> (%s,%s)", + authctxt->user, authctxt->service, user, service); + } + /* reset state */ + auth2_challenge_stop(ssh); + +#ifdef GSSAPI + /* XXX move to auth2_gssapi_stop() */ + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); +#endif + + auth2_authctxt_reset_info(authctxt); + authctxt->postponed = 0; + authctxt->server_caused_failure = 0; + + /* try to authenticate user */ + m = authmethod_lookup(authctxt, method); + if (m != NULL && authctxt->failures < options.max_authtries) { + debug2("input_userauth_request: try method %s", method); + authenticated = m->userauth(ssh, method); + } + if (!authctxt->authenticated) + ensure_minimum_time_since(tstart, + user_specific_delay(authctxt->user)); + userauth_finish(ssh, authenticated, method, NULL); + r = 0; + out: + free(service); + free(user); + free(method); + return r; +} + +void +userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method, + const char *submethod) +{ + Authctxt *authctxt = ssh->authctxt; + Authmethod *m = NULL; + const char *method = packet_method; + char *methods; + int r, partial = 0; + + if (authenticated) { + if (!authctxt->valid) { + fatal("INTERNAL ERROR: authenticated invalid user %s", + authctxt->user); + } + if (authctxt->postponed) + fatal("INTERNAL ERROR: authenticated and postponed"); + /* prefer primary authmethod name to possible synonym */ + if ((m = authmethod_byname(method)) == NULL) + fatal("INTERNAL ERROR: bad method %s", method); + method = m->name; + } + + /* Special handling for root */ + if (authenticated && authctxt->pw->pw_uid == 0 && + !auth_root_allowed(ssh, method)) { + authenticated = 0; +#ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(ssh, SSH_LOGIN_ROOT_DENIED)); +#endif + } + + if (authenticated && options.num_auth_methods != 0) { + if (!auth2_update_methods_lists(authctxt, method, submethod)) { + authenticated = 0; + partial = 1; + } + } + + /* Log before sending the reply */ + auth_log(ssh, authenticated, partial, method, submethod); + + /* Update information exposed to session */ + if (authenticated || partial) + auth2_update_session_info(authctxt, method, submethod); + + if (authctxt->postponed) + return; + +#ifdef USE_PAM + if (options.use_pam && authenticated) { + int r, success = PRIVSEP(do_pam_account()); + + /* If PAM returned a message, send it to the user. */ + if (sshbuf_len(loginmsg) > 0) { + if ((r = sshbuf_put(loginmsg, "\0", 1)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + userauth_send_banner(ssh, sshbuf_ptr(loginmsg)); + if ((r = ssh_packet_write_wait(ssh)) != 0) { + sshpkt_fatal(ssh, r, + "%s: send PAM banner", __func__); + } + } + if (!success) { + fatal("Access denied for user %s by PAM account " + "configuration", authctxt->user); + } + } +#endif + + if (authenticated == 1) { + /* turn off userauth */ + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, + &dispatch_protocol_ignore); + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_SUCCESS)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send success packet"); + /* now we can break out */ + authctxt->success = 1; + ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); + } else { + /* Allow initial try of "none" auth without failure penalty */ + if (!partial && !authctxt->server_caused_failure && + (authctxt->attempt > 1 || strcmp(method, "none") != 0)) + authctxt->failures++; + if (authctxt->failures >= options.max_authtries) { +#ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(ssh, SSH_LOGIN_EXCEED_MAXTRIES)); +#endif + auth_maxtries_exceeded(ssh); + } + methods = authmethods_get(authctxt); + debug3_f("failure partial=%d next methods=\"%s\"", + partial, methods); + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_FAILURE)) != 0 || + (r = sshpkt_put_cstring(ssh, methods)) != 0 || + (r = sshpkt_put_u8(ssh, partial)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send failure packet"); + free(methods); + } +} + +/* + * Checks whether method is allowed by at least one AuthenticationMethods + * methods list. Returns 1 if allowed, or no methods lists configured. + * 0 otherwise. + */ +int +auth2_method_allowed(Authctxt *authctxt, const char *method, + const char *submethod) +{ + u_int i; + + /* + * NB. authctxt->num_auth_methods might be zero as a result of + * auth2_setup_methods_lists(), so check the configuration. + */ + if (options.num_auth_methods == 0) + return 1; + for (i = 0; i < authctxt->num_auth_methods; i++) { + if (list_starts_with(authctxt->auth_methods[i], method, + submethod) != MATCH_NONE) + return 1; + } + return 0; +} + +static char * +authmethods_get(Authctxt *authctxt) +{ + struct sshbuf *b; + char *list; + int i, r; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + for (i = 0; authmethods[i] != NULL; i++) { + if (strcmp(authmethods[i]->name, "none") == 0) + continue; + if (authmethods[i]->enabled == NULL || + *(authmethods[i]->enabled) == 0) + continue; + if (!auth2_method_allowed(authctxt, authmethods[i]->name, + NULL)) + continue; + if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "", + authmethods[i]->name)) != 0) + fatal_fr(r, "buffer error"); + } + if ((list = sshbuf_dup_string(b)) == NULL) + fatal_f("sshbuf_dup_string failed"); + sshbuf_free(b); + return list; +} + +static Authmethod * +authmethod_byname(const char *name) +{ + int i; + + if (name == NULL) + fatal_f("NULL authentication method name"); + for (i = 0; authmethods[i] != NULL; i++) { + if (strcmp(name, authmethods[i]->name) == 0 || + (authmethods[i]->synonym != NULL && + strcmp(name, authmethods[i]->synonym) == 0)) + return authmethods[i]; + } + debug_f("unrecognized authentication method name: %s", name); + return NULL; +} + +static Authmethod * +authmethod_lookup(Authctxt *authctxt, const char *name) +{ + Authmethod *method; + + if ((method = authmethod_byname(name)) == NULL) + return NULL; + + if (method->enabled == NULL || *(method->enabled) == 0) { + debug3_f("method %s not enabled", name); + return NULL; + } + if (!auth2_method_allowed(authctxt, method->name, NULL)) { + debug3_f("method %s not allowed " + "by AuthenticationMethods", name); + return NULL; + } + return method; +} + +/* + * Check a comma-separated list of methods for validity. Is need_enable is + * non-zero, then also require that the methods are enabled. + * Returns 0 on success or -1 if the methods list is invalid. + */ +int +auth2_methods_valid(const char *_methods, int need_enable) +{ + char *methods, *omethods, *method, *p; + u_int i, found; + int ret = -1; + + if (*_methods == '\0') { + error("empty authentication method list"); + return -1; + } + omethods = methods = xstrdup(_methods); + while ((method = strsep(&methods, ",")) != NULL) { + for (found = i = 0; !found && authmethods[i] != NULL; i++) { + if ((p = strchr(method, ':')) != NULL) + *p = '\0'; + if (strcmp(method, authmethods[i]->name) != 0) + continue; + if (need_enable) { + if (authmethods[i]->enabled == NULL || + *(authmethods[i]->enabled) == 0) { + error("Disabled method \"%s\" in " + "AuthenticationMethods list \"%s\"", + method, _methods); + goto out; + } + } + found = 1; + break; + } + if (!found) { + error("Unknown authentication method \"%s\" in list", + method); + goto out; + } + } + ret = 0; + out: + free(omethods); + return ret; +} + +/* + * Prune the AuthenticationMethods supplied in the configuration, removing + * any methods lists that include disabled methods. Note that this might + * leave authctxt->num_auth_methods == 0, even when multiple required auth + * has been requested. For this reason, all tests for whether multiple is + * enabled should consult options.num_auth_methods directly. + */ +int +auth2_setup_methods_lists(Authctxt *authctxt) +{ + u_int i; + + /* First, normalise away the "any" pseudo-method */ + if (options.num_auth_methods == 1 && + strcmp(options.auth_methods[0], "any") == 0) { + free(options.auth_methods[0]); + options.auth_methods[0] = NULL; + options.num_auth_methods = 0; + } + + if (options.num_auth_methods == 0) + return 0; + debug3_f("checking methods"); + authctxt->auth_methods = xcalloc(options.num_auth_methods, + sizeof(*authctxt->auth_methods)); + authctxt->num_auth_methods = 0; + for (i = 0; i < options.num_auth_methods; i++) { + if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { + logit("Authentication methods list \"%s\" contains " + "disabled method, skipping", + options.auth_methods[i]); + continue; + } + debug("authentication methods list %d: %s", + authctxt->num_auth_methods, options.auth_methods[i]); + authctxt->auth_methods[authctxt->num_auth_methods++] = + xstrdup(options.auth_methods[i]); + } + if (authctxt->num_auth_methods == 0) { + error("No AuthenticationMethods left after eliminating " + "disabled methods"); + return -1; + } + return 0; +} + +static int +list_starts_with(const char *methods, const char *method, + const char *submethod) +{ + size_t l = strlen(method); + int match; + const char *p; + + if (strncmp(methods, method, l) != 0) + return MATCH_NONE; + p = methods + l; + match = MATCH_METHOD; + if (*p == ':') { + if (!submethod) + return MATCH_PARTIAL; + l = strlen(submethod); + p += 1; + if (strncmp(submethod, p, l)) + return MATCH_NONE; + p += l; + match = MATCH_BOTH; + } + if (*p != ',' && *p != '\0') + return MATCH_NONE; + return match; +} + +/* + * Remove method from the start of a comma-separated list of methods. + * Returns 0 if the list of methods did not start with that method or 1 + * if it did. + */ +static int +remove_method(char **methods, const char *method, const char *submethod) +{ + char *omethods = *methods, *p; + size_t l = strlen(method); + int match; + + match = list_starts_with(omethods, method, submethod); + if (match != MATCH_METHOD && match != MATCH_BOTH) + return 0; + p = omethods + l; + if (submethod && match == MATCH_BOTH) + p += 1 + strlen(submethod); /* include colon */ + if (*p == ',') + p++; + *methods = xstrdup(p); + free(omethods); + return 1; +} + +/* + * Called after successful authentication. Will remove the successful method + * from the start of each list in which it occurs. If it was the last method + * in any list, then authentication is deemed successful. + * Returns 1 if the method completed any authentication list or 0 otherwise. + */ +int +auth2_update_methods_lists(Authctxt *authctxt, const char *method, + const char *submethod) +{ + u_int i, found = 0; + + debug3_f("updating methods list after \"%s\"", method); + for (i = 0; i < authctxt->num_auth_methods; i++) { + if (!remove_method(&(authctxt->auth_methods[i]), method, + submethod)) + continue; + found = 1; + if (*authctxt->auth_methods[i] == '\0') { + debug2("authentication methods list %d complete", i); + return 1; + } + debug3("authentication methods list %d remaining: \"%s\"", + i, authctxt->auth_methods[i]); + } + /* This should not happen, but would be bad if it did */ + if (!found) + fatal_f("method not in AuthenticationMethods"); + return 0; +} + +/* Reset method-specific information */ +void auth2_authctxt_reset_info(Authctxt *authctxt) +{ + sshkey_free(authctxt->auth_method_key); + free(authctxt->auth_method_info); + authctxt->auth_method_key = NULL; + authctxt->auth_method_info = NULL; +} + +/* Record auth method-specific information for logs */ +void +auth2_record_info(Authctxt *authctxt, const char *fmt, ...) +{ + va_list ap; + int i; + + free(authctxt->auth_method_info); + authctxt->auth_method_info = NULL; + + va_start(ap, fmt); + i = vasprintf(&authctxt->auth_method_info, fmt, ap); + va_end(ap); + + if (i == -1) + fatal_f("vasprintf failed"); +} + +/* + * Records a public key used in authentication. This is used for logging + * and to ensure that the same key is not subsequently accepted again for + * multiple authentication. + */ +void +auth2_record_key(Authctxt *authctxt, int authenticated, + const struct sshkey *key) +{ + struct sshkey **tmp, *dup; + int r; + + if ((r = sshkey_from_private(key, &dup)) != 0) + fatal_fr(r, "copy key"); + sshkey_free(authctxt->auth_method_key); + authctxt->auth_method_key = dup; + + if (!authenticated) + return; + + /* If authenticated, make sure we don't accept this key again */ + if ((r = sshkey_from_private(key, &dup)) != 0) + fatal_fr(r, "copy key"); + if (authctxt->nprev_keys >= INT_MAX || + (tmp = recallocarray(authctxt->prev_keys, authctxt->nprev_keys, + authctxt->nprev_keys + 1, sizeof(*authctxt->prev_keys))) == NULL) + fatal_f("reallocarray failed"); + authctxt->prev_keys = tmp; + authctxt->prev_keys[authctxt->nprev_keys] = dup; + authctxt->nprev_keys++; + +} + +/* Checks whether a key has already been previously used for authentication */ +int +auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) +{ + u_int i; + char *fp; + + for (i = 0; i < authctxt->nprev_keys; i++) { + if (sshkey_equal_public(key, authctxt->prev_keys[i])) { + fp = sshkey_fingerprint(authctxt->prev_keys[i], + options.fingerprint_hash, SSH_FP_DEFAULT); + debug3_f("key already used: %s %s", + sshkey_type(authctxt->prev_keys[i]), + fp == NULL ? "UNKNOWN" : fp); + free(fp); + return 1; + } + } + return 0; +} + +/* + * Updates authctxt->session_info with details of authentication. Should be + * whenever an authentication method succeeds. + */ +void +auth2_update_session_info(Authctxt *authctxt, const char *method, + const char *submethod) +{ + int r; + + if (authctxt->session_info == NULL) { + if ((authctxt->session_info = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + } + + /* Append method[/submethod] */ + if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s", + method, submethod == NULL ? "" : "/", + submethod == NULL ? "" : submethod)) != 0) + fatal_fr(r, "append method"); + + /* Append key if present */ + if (authctxt->auth_method_key != NULL) { + if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || + (r = sshkey_format_text(authctxt->auth_method_key, + authctxt->session_info)) != 0) + fatal_fr(r, "append key"); + } + + if (authctxt->auth_method_info != NULL) { + /* Ensure no ambiguity here */ + if (strchr(authctxt->auth_method_info, '\n') != NULL) + fatal_f("auth_method_info contains \\n"); + if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || + (r = sshbuf_putf(authctxt->session_info, "%s", + authctxt->auth_method_info)) != 0) { + fatal_fr(r, "append method info"); + } + } + if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0) + fatal_fr(r, "append"); +} + diff --git a/aosp/external/openssh/authfd.c b/aosp/external/openssh/authfd.c new file mode 100644 index 0000000000000000000000000000000000000000..76e48aab779ed8d0eb3bd42c65dc50498601359a --- /dev/null +++ b/aosp/external/openssh/authfd.c @@ -0,0 +1,754 @@ +/* $OpenBSD: authfd.c,v 1.129 2021/12/19 22:10:24 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for connecting the local authentication agent. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation, + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "authfd.h" +#include "cipher.h" +#include "compat.h" +#include "log.h" +#include "atomicio.h" +#include "misc.h" +#include "ssherr.h" + +#define MAX_AGENT_IDENTITIES 2048 /* Max keys in agent reply */ +#define MAX_AGENT_REPLY_LEN (256 * 1024) /* Max bytes in agent reply */ + +/* macro to check for "agent failure" message */ +#define agent_failed(x) \ + ((x == SSH_AGENT_FAILURE) || \ + (x == SSH_COM_AGENT2_FAILURE) || \ + (x == SSH2_AGENT_FAILURE)) + +/* Convert success/failure response from agent to a err.h status */ +static int +decode_reply(u_char type) +{ + if (agent_failed(type)) + return SSH_ERR_AGENT_FAILURE; + else if (type == SSH_AGENT_SUCCESS) + return 0; + else + return SSH_ERR_INVALID_FORMAT; +} + +/* + * Opens an authentication socket at the provided path and stores the file + * descriptor in fdp. Returns 0 on success and an error on failure. + */ +int +ssh_get_authentication_socket_path(const char *authsocket, int *fdp) +{ + int sock, oerrno; + struct sockaddr_un sunaddr; + + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); + + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + return SSH_ERR_SYSTEM_ERROR; + + /* close on exec */ + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 || + connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { + oerrno = errno; + close(sock); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + if (fdp != NULL) + *fdp = sock; + else + close(sock); + return 0; +} + +/* + * Opens the default authentication socket and stores the file descriptor in + * fdp. Returns 0 on success and an error on failure. + */ +int +ssh_get_authentication_socket(int *fdp) +{ + const char *authsocket; + + if (fdp != NULL) + *fdp = -1; + + authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); + if (authsocket == NULL || *authsocket == '\0') + return SSH_ERR_AGENT_NOT_PRESENT; + + return ssh_get_authentication_socket_path(authsocket, fdp); +} + +/* Communicate with agent: send request and read reply */ +static int +ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) +{ + int r; + size_t l, len; + char buf[1024]; + + /* Get the length of the message, and format it in the buffer. */ + len = sshbuf_len(request); + POKE_U32(buf, len); + + /* Send the length and then the packet to the agent. */ + if (atomicio(vwrite, sock, buf, 4) != 4 || + atomicio(vwrite, sock, sshbuf_mutable_ptr(request), + sshbuf_len(request)) != sshbuf_len(request)) + return SSH_ERR_AGENT_COMMUNICATION; + /* + * Wait for response from the agent. First read the length of the + * response packet. + */ + if (atomicio(read, sock, buf, 4) != 4) + return SSH_ERR_AGENT_COMMUNICATION; + + /* Extract the length, and check it for sanity. */ + len = PEEK_U32(buf); + if (len > MAX_AGENT_REPLY_LEN) + return SSH_ERR_INVALID_FORMAT; + + /* Read the rest of the response in to the buffer. */ + sshbuf_reset(reply); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + if (atomicio(read, sock, buf, l) != l) + return SSH_ERR_AGENT_COMMUNICATION; + if ((r = sshbuf_put(reply, buf, l)) != 0) + return r; + len -= l; + } + return 0; +} + +/* Communicate with agent: sent request, read and decode status reply */ +static int +ssh_request_reply_decode(int sock, struct sshbuf *request) +{ + struct sshbuf *reply; + int r; + u_char type; + + if ((reply = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = ssh_request_reply(sock, request, reply)) != 0 || + (r = sshbuf_get_u8(reply, &type)) != 0 || + (r = decode_reply(type)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(reply); + return r; +} + +/* + * Closes the agent socket if it should be closed (depends on how it was + * obtained). The argument must have been returned by + * ssh_get_authentication_socket(). + */ +void +ssh_close_authentication_socket(int sock) +{ + if (getenv(SSH_AUTHSOCKET_ENV_NAME)) + close(sock); +} + +/* Lock/unlock agent */ +int +ssh_lock_agent(int sock, int lock, const char *password) +{ + int r; + u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK; + struct sshbuf *msg; + + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_u8(msg, type)) != 0 || + (r = sshbuf_put_cstring(msg, password)) != 0 || + (r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(msg); + return r; +} + + +static int +deserialise_identity2(struct sshbuf *ids, struct sshkey **keyp, char **commentp) +{ + int r; + char *comment = NULL; + const u_char *blob; + size_t blen; + + if ((r = sshbuf_get_string_direct(ids, &blob, &blen)) != 0 || + (r = sshbuf_get_cstring(ids, &comment, NULL)) != 0) + goto out; + if ((r = sshkey_from_blob(blob, blen, keyp)) != 0) + goto out; + if (commentp != NULL) { + *commentp = comment; + comment = NULL; + } + r = 0; + out: + free(comment); + return r; +} + +/* + * Fetch list of identities held by the agent. + */ +int +ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp) +{ + u_char type; + u_int32_t num, i; + struct sshbuf *msg; + struct ssh_identitylist *idl = NULL; + int r; + + /* + * Send a message to the agent requesting for a list of the + * identities it can represent. + */ + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REQUEST_IDENTITIES)) != 0) + goto out; + + if ((r = ssh_request_reply(sock, msg, msg)) != 0) + goto out; + + /* Get message type, and verify that we got a proper answer. */ + if ((r = sshbuf_get_u8(msg, &type)) != 0) + goto out; + if (agent_failed(type)) { + r = SSH_ERR_AGENT_FAILURE; + goto out; + } else if (type != SSH2_AGENT_IDENTITIES_ANSWER) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* Get the number of entries in the response and check it for sanity. */ + if ((r = sshbuf_get_u32(msg, &num)) != 0) + goto out; + if (num > MAX_AGENT_IDENTITIES) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (num == 0) { + r = SSH_ERR_AGENT_NO_IDENTITIES; + goto out; + } + + /* Deserialise the response into a list of keys/comments */ + if ((idl = calloc(1, sizeof(*idl))) == NULL || + (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL || + (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < num;) { + if ((r = deserialise_identity2(msg, &(idl->keys[i]), + &(idl->comments[i]))) != 0) { + if (r == SSH_ERR_KEY_TYPE_UNKNOWN) { + /* Gracefully skip unknown key types */ + num--; + continue; + } else + goto out; + } + i++; + } + idl->nkeys = num; + *idlp = idl; + idl = NULL; + r = 0; + out: + sshbuf_free(msg); + if (idl != NULL) + ssh_free_identitylist(idl); + return r; +} + +void +ssh_free_identitylist(struct ssh_identitylist *idl) +{ + size_t i; + + if (idl == NULL) + return; + for (i = 0; i < idl->nkeys; i++) { + if (idl->keys != NULL) + sshkey_free(idl->keys[i]); + if (idl->comments != NULL) + free(idl->comments[i]); + } + free(idl->keys); + free(idl->comments); + free(idl); +} + +/* + * Check if the ssh agent has a given key. + * Returns 0 if found, or a negative SSH_ERR_* error code on failure. + */ +int +ssh_agent_has_key(int sock, const struct sshkey *key) +{ + int r, ret = SSH_ERR_KEY_NOT_FOUND; + size_t i; + struct ssh_identitylist *idlist = NULL; + + if ((r = ssh_fetch_identitylist(sock, &idlist)) != 0) { + return r; + } + + for (i = 0; i < idlist->nkeys; i++) { + if (sshkey_equal_public(idlist->keys[i], key)) { + ret = 0; + break; + } + } + + ssh_free_identitylist(idlist); + return ret; +} + +/* + * Sends a challenge (typically from a server via ssh(1)) to the agent, + * and waits for a response from the agent. + * Returns true (non-zero) if the agent gave the correct answer, zero + * otherwise. + */ + + +/* encode signature algorithm in flag bits, so we can keep the msg format */ +static u_int +agent_encode_alg(const struct sshkey *key, const char *alg) +{ + if (alg != NULL && sshkey_type_plain(key->type) == KEY_RSA) { + if (strcmp(alg, "rsa-sha2-256") == 0 || + strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0) + return SSH_AGENT_RSA_SHA2_256; + if (strcmp(alg, "rsa-sha2-512") == 0 || + strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0) + return SSH_AGENT_RSA_SHA2_512; + } + return 0; +} + +/* ask agent to sign data, returns err.h code on error, 0 on success */ +int +ssh_agent_sign(int sock, const struct sshkey *key, + u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, const char *alg, u_int compat) +{ + struct sshbuf *msg; + u_char *sig = NULL, type = 0; + size_t len = 0; + u_int flags = 0; + int r = SSH_ERR_INTERNAL_ERROR; + + *sigp = NULL; + *lenp = 0; + + if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) + return SSH_ERR_INVALID_ARGUMENT; + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + flags |= agent_encode_alg(key, alg); + if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || + (r = sshkey_puts(key, msg)) != 0 || + (r = sshbuf_put_string(msg, data, datalen)) != 0 || + (r = sshbuf_put_u32(msg, flags)) != 0) + goto out; + if ((r = ssh_request_reply(sock, msg, msg)) != 0) + goto out; + if ((r = sshbuf_get_u8(msg, &type)) != 0) + goto out; + if (agent_failed(type)) { + r = SSH_ERR_AGENT_FAILURE; + goto out; + } else if (type != SSH2_AGENT_SIGN_RESPONSE) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_string(msg, &sig, &len)) != 0) + goto out; + /* Check what we actually got back from the agent. */ + if ((r = sshkey_check_sigtype(sig, len, alg)) != 0) + goto out; + /* success */ + *sigp = sig; + *lenp = len; + sig = NULL; + len = 0; + r = 0; + out: + freezero(sig, len); + sshbuf_free(msg); + return r; +} + +/* Encode key for a message to the agent. */ + +static int +encode_dest_constraint_hop(struct sshbuf *m, + const struct dest_constraint_hop *dch) +{ + struct sshbuf *b; + u_int i; + int r; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, dch->user)) != 0 || + (r = sshbuf_put_cstring(b, dch->hostname)) != 0 || + (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */ + goto out; + for (i = 0; i < dch->nkeys; i++) { + if ((r = sshkey_puts(dch->keys[i], b)) != 0 || + (r = sshbuf_put_u8(b, dch->key_is_ca[i] != 0)) != 0) + goto out; + } + if ((r = sshbuf_put_stringb(m, b)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +static int +encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc) +{ + struct sshbuf *b; + int r; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = encode_dest_constraint_hop(b, &dc->from) != 0) || + (r = encode_dest_constraint_hop(b, &dc->to) != 0) || + (r = sshbuf_put_string(b, NULL, 0)) != 0) /* reserved */ + goto out; + if ((r = sshbuf_put_stringb(m, b)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +static int +encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, + const char *provider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) +{ + int r; + struct sshbuf *b = NULL; + size_t i; + + if (life != 0) { + if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_LIFETIME)) != 0 || + (r = sshbuf_put_u32(m, life)) != 0) + goto out; + } + if (confirm != 0) { + if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0) + goto out; + } + if (maxsign != 0) { + if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_MAXSIGN)) != 0 || + (r = sshbuf_put_u32(m, maxsign)) != 0) + goto out; + } + if (provider != NULL) { + if ((r = sshbuf_put_u8(m, + SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 || + (r = sshbuf_put_cstring(m, + "sk-provider@openssh.com")) != 0 || + (r = sshbuf_put_cstring(m, provider)) != 0) + goto out; + } + if (dest_constraints != NULL && ndest_constraints > 0) { + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < ndest_constraints; i++) { + if ((r = encode_dest_constraint(b, + dest_constraints[i])) != 0) + goto out; + } + if ((r = sshbuf_put_u8(m, + SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 || + (r = sshbuf_put_cstring(m, + "restrict-destination-v00@openssh.com")) != 0 || + (r = sshbuf_put_stringb(m, b)) != 0) + goto out; + } + r = 0; + out: + sshbuf_free(b); + return r; +} + +/* + * Adds an identity to the authentication server. + * This call is intended only for use by ssh-add(1) and like applications. + */ +int +ssh_add_identity_constrained(int sock, struct sshkey *key, + const char *comment, u_int life, u_int confirm, u_int maxsign, + const char *provider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) +{ + struct sshbuf *msg; + int r, constrained = (life || confirm || maxsign || + provider || dest_constraints); + u_char type; + + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: + case KEY_DSA: + case KEY_DSA_CERT: + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: +#endif + case KEY_ED25519: + case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: + type = constrained ? + SSH2_AGENTC_ADD_ID_CONSTRAINED : + SSH2_AGENTC_ADD_IDENTITY; + if ((r = sshbuf_put_u8(msg, type)) != 0 || + (r = sshkey_private_serialize_maxsign(key, msg, maxsign, + 0)) != 0 || + (r = sshbuf_put_cstring(msg, comment)) != 0) + goto out; + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (constrained && + (r = encode_constraints(msg, life, confirm, maxsign, + provider, dest_constraints, ndest_constraints)) != 0) + goto out; + if ((r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(msg); + return r; +} + +/* + * Removes an identity from the authentication server. + * This call is intended only for use by ssh-add(1) and like applications. + */ +int +ssh_remove_identity(int sock, const struct sshkey *key) +{ + struct sshbuf *msg; + int r; + u_char *blob = NULL; + size_t blen; + + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + if (key->type != KEY_UNSPEC) { + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) + goto out; + if ((r = sshbuf_put_u8(msg, + SSH2_AGENTC_REMOVE_IDENTITY)) != 0 || + (r = sshbuf_put_string(msg, blob, blen)) != 0) + goto out; + } else { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + if (blob != NULL) + freezero(blob, blen); + sshbuf_free(msg); + return r; +} + +/* + * Add/remove an token-based identity from the authentication server. + * This call is intended only for use by ssh-add(1) and like applications. + */ +int +ssh_update_card(int sock, int add, const char *reader_id, const char *pin, + u_int life, u_int confirm, + struct dest_constraint **dest_constraints, size_t ndest_constraints) +{ + struct sshbuf *msg; + int r, constrained = (life || confirm); + u_char type; + + if (add) { + type = constrained ? + SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : + SSH_AGENTC_ADD_SMARTCARD_KEY; + } else + type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; + + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_u8(msg, type)) != 0 || + (r = sshbuf_put_cstring(msg, reader_id)) != 0 || + (r = sshbuf_put_cstring(msg, pin)) != 0) + goto out; + if (constrained && + (r = encode_constraints(msg, life, confirm, 0, NULL, + dest_constraints, ndest_constraints)) != 0) + goto out; + if ((r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(msg); + return r; +} + +/* + * Removes all identities from the agent. + * This call is intended only for use by ssh-add(1) and like applications. + * + * This supports the SSH protocol 1 message to because, when clearing all + * keys from an agent, we generally want to clear both protocol v1 and v2 + * keys. + */ +int +ssh_remove_all_identities(int sock, int version) +{ + struct sshbuf *msg; + u_char type = (version == 1) ? + SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : + SSH2_AGENTC_REMOVE_ALL_IDENTITIES; + int r; + + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_u8(msg, type)) != 0) + goto out; + if ((r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(msg); + return r; +} + +/* Binds a session ID to a hostkey via the initial KEX signature. */ +int +ssh_agent_bind_hostkey(int sock, const struct sshkey *key, + const struct sshbuf *session_id, const struct sshbuf *signature, + int forwarding) +{ + struct sshbuf *msg; + int r; + + if (key == NULL || session_id == NULL || signature == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_u8(msg, SSH_AGENTC_EXTENSION)) != 0 || + (r = sshbuf_put_cstring(msg, "session-bind@openssh.com")) != 0 || + (r = sshkey_puts(key, msg)) != 0 || + (r = sshbuf_put_stringb(msg, session_id)) != 0 || + (r = sshbuf_put_stringb(msg, signature)) != 0 || + (r = sshbuf_put_u8(msg, forwarding ? 1 : 0)) != 0) + goto out; + if ((r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(msg); + return r; +} diff --git a/aosp/external/openssh/authfd.h b/aosp/external/openssh/authfd.h new file mode 100644 index 0000000000000000000000000000000000000000..7a1c0ddff980e55c97b0e59b0d6cb3f4eb45d035 --- /dev/null +++ b/aosp/external/openssh/authfd.h @@ -0,0 +1,121 @@ +/* $OpenBSD: authfd.h,v 1.51 2021/12/19 22:10:24 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions to interface with the SSH_AUTHENTICATION_FD socket. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef AUTHFD_H +#define AUTHFD_H + +struct sshbuf; +struct sshkey; + +/* List of identities returned by ssh_fetch_identitylist() */ +struct ssh_identitylist { + size_t nkeys; + struct sshkey **keys; + char **comments; +}; + +/* Key destination restrictions */ +struct dest_constraint_hop { + char *user; /* wildcards allowed */ + char *hostname; /* used to matching cert principals and for display */ + int is_ca; + u_int nkeys; /* number of entries in *both* 'keys' and 'key_is_ca' */ + struct sshkey **keys; + int *key_is_ca; +}; +struct dest_constraint { + struct dest_constraint_hop from; + struct dest_constraint_hop to; +}; + +int ssh_get_authentication_socket(int *fdp); +int ssh_get_authentication_socket_path(const char *authsocket, int *fdp); +void ssh_close_authentication_socket(int sock); + +int ssh_lock_agent(int sock, int lock, const char *password); +int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); +void ssh_free_identitylist(struct ssh_identitylist *idl); +int ssh_add_identity_constrained(int sock, struct sshkey *key, + const char *comment, u_int life, u_int confirm, u_int maxsign, + const char *provider, struct dest_constraint **dest_constraints, + size_t ndest_constraints); +int ssh_agent_has_key(int sock, const struct sshkey *key); +int ssh_remove_identity(int sock, const struct sshkey *key); +int ssh_update_card(int sock, int add, const char *reader_id, + const char *pin, u_int life, u_int confirm, + struct dest_constraint **dest_constraints, + size_t ndest_constraints); +int ssh_remove_all_identities(int sock, int version); + +int ssh_agent_sign(int sock, const struct sshkey *key, + u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, const char *alg, u_int compat); + +int ssh_agent_bind_hostkey(int sock, const struct sshkey *key, + const struct sshbuf *session_id, const struct sshbuf *signature, + int forwarding); + +/* Messages for the authentication agent connection. */ +#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 +#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 +#define SSH_AGENTC_RSA_CHALLENGE 3 +#define SSH_AGENT_RSA_RESPONSE 4 +#define SSH_AGENT_FAILURE 5 +#define SSH_AGENT_SUCCESS 6 +#define SSH_AGENTC_ADD_RSA_IDENTITY 7 +#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 +#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 + +/* private OpenSSH extensions for SSH2 */ +#define SSH2_AGENTC_REQUEST_IDENTITIES 11 +#define SSH2_AGENT_IDENTITIES_ANSWER 12 +#define SSH2_AGENTC_SIGN_REQUEST 13 +#define SSH2_AGENT_SIGN_RESPONSE 14 +#define SSH2_AGENTC_ADD_IDENTITY 17 +#define SSH2_AGENTC_REMOVE_IDENTITY 18 +#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 + +/* smartcard */ +#define SSH_AGENTC_ADD_SMARTCARD_KEY 20 +#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 + +/* lock/unlock the agent */ +#define SSH_AGENTC_LOCK 22 +#define SSH_AGENTC_UNLOCK 23 + +/* add key with constraints */ +#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 +#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 +#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 + +/* generic extension mechanism */ +#define SSH_AGENTC_EXTENSION 27 + +#define SSH_AGENT_CONSTRAIN_LIFETIME 1 +#define SSH_AGENT_CONSTRAIN_CONFIRM 2 +#define SSH_AGENT_CONSTRAIN_MAXSIGN 3 +#define SSH_AGENT_CONSTRAIN_EXTENSION 255 + +/* extended failure messages */ +#define SSH2_AGENT_FAILURE 30 + +/* additional error code for ssh.com's ssh-agent2 */ +#define SSH_COM_AGENT2_FAILURE 102 + +#define SSH_AGENT_OLD_SIGNATURE 0x01 +#define SSH_AGENT_RSA_SHA2_256 0x02 +#define SSH_AGENT_RSA_SHA2_512 0x04 + +#endif /* AUTHFD_H */ diff --git a/aosp/external/openssh/authfile.c b/aosp/external/openssh/authfile.c new file mode 100644 index 0000000000000000000000000000000000000000..a399efc3e7382f42a1067f073d2521021e51df7a --- /dev/null +++ b/aosp/external/openssh/authfile.c @@ -0,0 +1,521 @@ +/* $OpenBSD: authfile.c,v 1.142 2022/01/01 01:55:30 jsg Exp $ */ +/* + * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cipher.h" +#include "ssh.h" +#include "log.h" +#include "authfile.h" +#include "misc.h" +#include "atomicio.h" +#include "sshkey.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "krl.h" + +#define MAX_KEY_FILE_SIZE (1024 * 1024) + +/* Save a key blob to a file */ +static int +sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) +{ + int r; + mode_t omask; + + omask = umask(077); + r = sshbuf_write_file(filename, keybuf); + umask(omask); + return r; +} + +int +sshkey_save_private(struct sshkey *key, const char *filename, + const char *passphrase, const char *comment, + int format, const char *openssh_format_cipher, int openssh_format_rounds) +{ + struct sshbuf *keyblob = NULL; + int r; + + if ((keyblob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, + format, openssh_format_cipher, openssh_format_rounds)) != 0) + goto out; + if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) + goto out; + r = 0; + out: + sshbuf_free(keyblob); + return r; +} + +/* XXX remove error() calls from here? */ +int +sshkey_perm_ok(int fd, const char *filename) +{ + struct stat st; + + if (fstat(fd, &st) == -1) + return SSH_ERR_SYSTEM_ERROR; + /* + * if a key owned by the user is accessed, then we check the + * permissions of the file. if the key owned by a different user, + * then we don't care. + */ +#ifdef HAVE_CYGWIN + if (check_ntsec(filename)) +#endif + if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("Permissions 0%3.3o for '%s' are too open.", + (u_int)st.st_mode & 0777, filename); + error("It is required that your private key files are NOT accessible by others."); + error("This private key will be ignored."); + return SSH_ERR_KEY_BAD_PERMISSIONS; + } + return 0; +} + +int +sshkey_load_private_type(int type, const char *filename, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + int fd, r; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + if ((fd = open(filename, O_RDONLY)) == -1) + return SSH_ERR_SYSTEM_ERROR; + + r = sshkey_perm_ok(fd, filename); + if (r != 0) + goto out; + + r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); + if (r == 0 && keyp && *keyp) + r = sshkey_set_filename(*keyp, filename); + out: + close(fd); + return r; +} + +int +sshkey_load_private(const char *filename, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase, + keyp, commentp); +} + +int +sshkey_load_private_type_fd(int fd, int type, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + struct sshbuf *buffer = NULL; + int r; + + if (keyp != NULL) + *keyp = NULL; + if ((r = sshbuf_load_fd(fd, &buffer)) != 0 || + (r = sshkey_parse_private_fileblob_type(buffer, type, + passphrase, keyp, commentp)) != 0) + goto out; + + /* success */ + r = 0; + out: + sshbuf_free(buffer); + return r; +} + +/* Load a pubkey from the unencrypted envelope of a new-format private key */ +static int +sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp) +{ + struct sshbuf *buffer = NULL; + struct sshkey *pubkey = NULL; + int r, fd; + + if (pubkeyp != NULL) + *pubkeyp = NULL; + + if ((fd = open(filename, O_RDONLY)) == -1) + return SSH_ERR_SYSTEM_ERROR; + if ((r = sshbuf_load_fd(fd, &buffer)) != 0 || + (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer, + KEY_UNSPEC, &pubkey)) != 0) + goto out; + if ((r = sshkey_set_filename(pubkey, filename)) != 0) + goto out; + /* success */ + if (pubkeyp != NULL) { + *pubkeyp = pubkey; + pubkey = NULL; + } + r = 0; + out: + close(fd); + sshbuf_free(buffer); + sshkey_free(pubkey); + return r; +} + +static int +sshkey_try_load_public(struct sshkey **kp, const char *filename, + char **commentp) +{ + FILE *f; + char *line = NULL, *cp; + size_t linesize = 0; + int r; + struct sshkey *k = NULL; + + *kp = NULL; + if (commentp != NULL) + *commentp = NULL; + if ((f = fopen(filename, "r")) == NULL) + return SSH_ERR_SYSTEM_ERROR; + if ((k = sshkey_new(KEY_UNSPEC)) == NULL) { + fclose(f); + return SSH_ERR_ALLOC_FAIL; + } + while (getline(&line, &linesize, f) != -1) { + cp = line; + switch (*cp) { + case '#': + case '\n': + case '\0': + continue; + } + /* Abort loading if this looks like a private key */ + if (strncmp(cp, "-----BEGIN", 10) == 0 || + strcmp(cp, "SSH PRIVATE KEY FILE") == 0) + break; + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + if (*cp) { + if ((r = sshkey_read(k, &cp)) == 0) { + cp[strcspn(cp, "\r\n")] = '\0'; + if (commentp) { + *commentp = strdup(*cp ? + cp : filename); + if (*commentp == NULL) + r = SSH_ERR_ALLOC_FAIL; + } + /* success */ + *kp = k; + free(line); + fclose(f); + return r; + } + } + } + free(k); + free(line); + fclose(f); + return SSH_ERR_INVALID_FORMAT; +} + +/* load public key from any pubkey file */ +int +sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) +{ + char *pubfile = NULL; + int r, oerrno; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0) + goto out; + + /* try .pub suffix */ + if (asprintf(&pubfile, "%s.pub", filename) == -1) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0) + goto out; + + /* finally, try to extract public key from private key file */ + if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0) + goto out; + + /* Pretend we couldn't find the key */ + r = SSH_ERR_SYSTEM_ERROR; + errno = ENOENT; + + out: + oerrno = errno; + free(pubfile); + errno = oerrno; + return r; +} + +/* Load the certificate associated with the named private key */ +int +sshkey_load_cert(const char *filename, struct sshkey **keyp) +{ + struct sshkey *pub = NULL; + char *file = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if (keyp != NULL) + *keyp = NULL; + + if (asprintf(&file, "%s-cert.pub", filename) == -1) + return SSH_ERR_ALLOC_FAIL; + + r = sshkey_try_load_public(keyp, file, NULL); + free(file); + sshkey_free(pub); + return r; +} + +/* Load private key and certificate */ +int +sshkey_load_private_cert(int type, const char *filename, const char *passphrase, + struct sshkey **keyp) +{ + struct sshkey *key = NULL, *cert = NULL; + int r; + + if (keyp != NULL) + *keyp = NULL; + + switch (type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_XMSS: + case KEY_UNSPEC: + break; + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + + if ((r = sshkey_load_private_type(type, filename, + passphrase, &key, NULL)) != 0 || + (r = sshkey_load_cert(filename, &cert)) != 0) + goto out; + + /* Make sure the private key matches the certificate */ + if (sshkey_equal_public(key, cert) == 0) { + r = SSH_ERR_KEY_CERT_MISMATCH; + goto out; + } + + if ((r = sshkey_to_certified(key)) != 0 || + (r = sshkey_cert_copy(cert, key)) != 0) + goto out; + r = 0; + if (keyp != NULL) { + *keyp = key; + key = NULL; + } + out: + sshkey_free(key); + sshkey_free(cert); + return r; +} + +/* + * Returns success if the specified "key" is listed in the file "filename", + * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error. + * If "strict_type" is set then the key type must match exactly, + * otherwise a comparison that ignores certificate data is performed. + * If "check_ca" is set and "key" is a certificate, then its CA key is + * also checked and sshkey_in_file() will return success if either is found. + */ +int +sshkey_in_file(struct sshkey *key, const char *filename, int strict_type, + int check_ca) +{ + FILE *f; + char *line = NULL, *cp; + size_t linesize = 0; + int r = 0; + struct sshkey *pub = NULL; + + int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) = + strict_type ? sshkey_equal : sshkey_equal_public; + + if ((f = fopen(filename, "r")) == NULL) + return SSH_ERR_SYSTEM_ERROR; + + while (getline(&line, &linesize, f) != -1) { + sshkey_free(pub); + pub = NULL; + cp = line; + + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + + /* Skip comments and empty lines */ + switch (*cp) { + case '#': + case '\n': + case '\0': + continue; + } + + if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + switch (r = sshkey_read(pub, &cp)) { + case 0: + break; + case SSH_ERR_KEY_LENGTH: + continue; + default: + goto out; + } + if (sshkey_compare(key, pub) || + (check_ca && sshkey_is_cert(key) && + sshkey_compare(key->cert->signature_key, pub))) { + r = 0; + goto out; + } + } + r = SSH_ERR_KEY_NOT_FOUND; + out: + free(line); + sshkey_free(pub); + fclose(f); + return r; +} + +/* + * Checks whether the specified key is revoked, returning 0 if not, + * SSH_ERR_KEY_REVOKED if it is or another error code if something + * unexpected happened. + * This will check both the key and, if it is a certificate, its CA key too. + * "revoked_keys_file" may be a KRL or a one-per-line list of public keys. + */ +int +sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file) +{ + int r; + + r = ssh_krl_file_contains_key(revoked_keys_file, key); + /* If this was not a KRL to begin with then continue below */ + if (r != SSH_ERR_KRL_BAD_MAGIC) + return r; + + /* + * If the file is not a KRL or we can't handle KRLs then attempt to + * parse the file as a flat list of keys. + */ + switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) { + case 0: + /* Key found => revoked */ + return SSH_ERR_KEY_REVOKED; + case SSH_ERR_KEY_NOT_FOUND: + /* Key not found => not revoked */ + return 0; + default: + /* Some other error occurred */ + return r; + } +} + +/* + * Advanced *cpp past the end of key options, defined as the first unquoted + * whitespace character. Returns 0 on success or -1 on failure (e.g. + * unterminated quotes). + */ +int +sshkey_advance_past_options(char **cpp) +{ + char *cp = *cpp; + int quoted = 0; + + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + *cpp = cp; + /* return failure for unterminated quotes */ + return (*cp == '\0' && quoted) ? -1 : 0; +} + +/* Save a public key */ +int +sshkey_save_public(const struct sshkey *key, const char *path, + const char *comment) +{ + int fd, oerrno; + FILE *f = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) + return SSH_ERR_SYSTEM_ERROR; + if ((f = fdopen(fd, "w")) == NULL) { + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if ((r = sshkey_write(key, f)) != 0) + goto fail; + fprintf(f, " %s\n", comment); + if (ferror(f) || fclose(f) != 0) { + r = SSH_ERR_SYSTEM_ERROR; + fail: + oerrno = errno; + if (f != NULL) + fclose(f); + else + close(fd); + errno = oerrno; + return r; + } + return 0; +} diff --git a/aosp/external/openssh/authfile.h b/aosp/external/openssh/authfile.h new file mode 100644 index 0000000000000000000000000000000000000000..1db067a813a1de3dbb3fefde0010da5153a4fc4b --- /dev/null +++ b/aosp/external/openssh/authfile.h @@ -0,0 +1,54 @@ +/* $OpenBSD: authfile.h,v 1.25 2020/01/25 23:02:13 djm Exp $ */ + +/* + * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AUTHFILE_H +#define AUTHFILE_H + +struct sshbuf; +struct sshkey; + +/* XXX document these */ +/* XXX some of these could probably be merged/retired */ + +int sshkey_save_private(struct sshkey *, const char *, + const char *, const char *, int, const char *, int); +int sshkey_load_cert(const char *, struct sshkey **); +int sshkey_load_public(const char *, struct sshkey **, char **); +int sshkey_load_private(const char *, const char *, struct sshkey **, char **); +int sshkey_load_private_cert(int, const char *, const char *, + struct sshkey **); +int sshkey_load_private_type(int, const char *, const char *, + struct sshkey **, char **); +int sshkey_load_private_type_fd(int fd, int type, const char *passphrase, + struct sshkey **keyp, char **commentp); +int sshkey_perm_ok(int, const char *); +int sshkey_in_file(struct sshkey *, const char *, int, int); +int sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file); +int sshkey_advance_past_options(char **cpp); +int sshkey_save_public(const struct sshkey *key, const char *path, + const char *comment); + +#endif diff --git a/aosp/external/openssh/bitmap.c b/aosp/external/openssh/bitmap.c new file mode 100644 index 0000000000000000000000000000000000000000..5ecfe68b89bdcdf195aba3ee302fb0ce79def811 --- /dev/null +++ b/aosp/external/openssh/bitmap.c @@ -0,0 +1,214 @@ +/* $OpenBSD: bitmap.c,v 1.9 2017/10/20 01:56:39 djm Exp $ */ +/* + * Copyright (c) 2015 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include + +#include "bitmap.h" + +#define BITMAP_WTYPE u_int +#define BITMAP_MAX (1<<24) +#define BITMAP_BYTES (sizeof(BITMAP_WTYPE)) +#define BITMAP_BITS (sizeof(BITMAP_WTYPE) * 8) +#define BITMAP_WMASK ((BITMAP_WTYPE)BITMAP_BITS - 1) +struct bitmap { + BITMAP_WTYPE *d; + size_t len; /* number of words allocated */ + size_t top; /* index of top word allocated */ +}; + +struct bitmap * +bitmap_new(void) +{ + struct bitmap *ret; + + if ((ret = calloc(1, sizeof(*ret))) == NULL) + return NULL; + if ((ret->d = calloc(1, BITMAP_BYTES)) == NULL) { + free(ret); + return NULL; + } + ret->len = 1; + ret->top = 0; + return ret; +} + +void +bitmap_free(struct bitmap *b) +{ + if (b != NULL && b->d != NULL) { + bitmap_zero(b); + free(b->d); + b->d = NULL; + } + free(b); +} + +void +bitmap_zero(struct bitmap *b) +{ + memset(b->d, 0, b->len * BITMAP_BYTES); + b->top = 0; +} + +int +bitmap_test_bit(struct bitmap *b, u_int n) +{ + if (b->top >= b->len) + return 0; /* invalid */ + if (b->len == 0 || (n / BITMAP_BITS) > b->top) + return 0; + return (b->d[n / BITMAP_BITS] >> (n & BITMAP_WMASK)) & 1; +} + +static int +reserve(struct bitmap *b, u_int n) +{ + BITMAP_WTYPE *tmp; + size_t nlen; + + if (b->top >= b->len || n > BITMAP_MAX) + return -1; /* invalid */ + nlen = (n / BITMAP_BITS) + 1; + if (b->len < nlen) { + if ((tmp = recallocarray(b->d, b->len, + nlen, BITMAP_BYTES)) == NULL) + return -1; + b->d = tmp; + b->len = nlen; + } + return 0; +} + +int +bitmap_set_bit(struct bitmap *b, u_int n) +{ + int r; + size_t offset; + + if ((r = reserve(b, n)) != 0) + return r; + offset = n / BITMAP_BITS; + if (offset > b->top) + b->top = offset; + b->d[offset] |= (BITMAP_WTYPE)1 << (n & BITMAP_WMASK); + return 0; +} + +/* Resets b->top to point to the most significant bit set in b->d */ +static void +retop(struct bitmap *b) +{ + if (b->top >= b->len) + return; + while (b->top > 0 && b->d[b->top] == 0) + b->top--; +} + +void +bitmap_clear_bit(struct bitmap *b, u_int n) +{ + size_t offset; + + if (b->top >= b->len || n > BITMAP_MAX) + return; /* invalid */ + offset = n / BITMAP_BITS; + if (offset > b->top) + return; + b->d[offset] &= ~((BITMAP_WTYPE)1 << (n & BITMAP_WMASK)); + /* The top may have changed as a result of the clear */ + retop(b); +} + +size_t +bitmap_nbits(struct bitmap *b) +{ + size_t bits; + BITMAP_WTYPE w; + + retop(b); + if (b->top >= b->len) + return 0; /* invalid */ + if (b->len == 0 || (b->top == 0 && b->d[0] == 0)) + return 0; + /* Find MSB set */ + w = b->d[b->top]; + bits = (b->top + 1) * BITMAP_BITS; + while (!(w & ((BITMAP_WTYPE)1 << (BITMAP_BITS - 1)))) { + w <<= 1; + bits--; + } + return bits; +} + +size_t +bitmap_nbytes(struct bitmap *b) +{ + return (bitmap_nbits(b) + 7) / 8; +} + +int +bitmap_to_string(struct bitmap *b, void *p, size_t l) +{ + u_char *s = (u_char *)p; + size_t i, j, k, need = bitmap_nbytes(b); + + if (l < need || b->top >= b->len) + return -1; + if (l > need) + l = need; + /* Put the bytes from LSB backwards */ + for (i = k = 0; i < b->top + 1; i++) { + for (j = 0; j < BITMAP_BYTES; j++) { + if (k >= l) + break; + s[need - 1 - k++] = (b->d[i] >> (j * 8)) & 0xff; + } + } + return 0; +} + +int +bitmap_from_string(struct bitmap *b, const void *p, size_t l) +{ + int r; + size_t i, offset, shift; + const u_char *s = (const u_char *)p; + + if (l > BITMAP_MAX / 8) + return -1; + if ((r = reserve(b, l * 8)) != 0) + return r; + bitmap_zero(b); + if (l == 0) + return 0; + b->top = offset = ((l + (BITMAP_BYTES - 1)) / BITMAP_BYTES) - 1; + shift = ((l + (BITMAP_BYTES - 1)) % BITMAP_BYTES) * 8; + for (i = 0; i < l; i++) { + b->d[offset] |= (BITMAP_WTYPE)s[i] << shift; + if (shift == 0) { + offset--; + shift = BITMAP_BITS - 8; + } else + shift -= 8; + } + retop(b); + return 0; +} diff --git a/aosp/external/openssh/bitmap.h b/aosp/external/openssh/bitmap.h new file mode 100644 index 0000000000000000000000000000000000000000..336e90b06cce25af13cc1067eb9edb940d7104fe --- /dev/null +++ b/aosp/external/openssh/bitmap.h @@ -0,0 +1,57 @@ +/* $OpenBSD: bitmap.h,v 1.2 2017/10/20 01:56:39 djm Exp $ */ +/* + * Copyright (c) 2015 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BITMAP_H +#define _BITMAP_H + +#include + +/* Simple bit vector routines */ + +struct bitmap; + +/* Allocate a new bitmap. Returns NULL on allocation failure. */ +struct bitmap *bitmap_new(void); + +/* Free a bitmap */ +void bitmap_free(struct bitmap *b); + +/* Zero an existing bitmap */ +void bitmap_zero(struct bitmap *b); + +/* Test whether a bit is set in a bitmap. */ +int bitmap_test_bit(struct bitmap *b, u_int n); + +/* Set a bit in a bitmap. Returns 0 on success or -1 on error */ +int bitmap_set_bit(struct bitmap *b, u_int n); + +/* Clear a bit in a bitmap */ +void bitmap_clear_bit(struct bitmap *b, u_int n); + +/* Return the number of bits in a bitmap (i.e. the position of the MSB) */ +size_t bitmap_nbits(struct bitmap *b); + +/* Return the number of bytes needed to represent a bitmap */ +size_t bitmap_nbytes(struct bitmap *b); + +/* Convert a bitmap to a big endian byte string */ +int bitmap_to_string(struct bitmap *b, void *p, size_t l); + +/* Convert a big endian byte string to a bitmap */ +int bitmap_from_string(struct bitmap *b, const void *p, size_t l); + +#endif /* _BITMAP_H */ diff --git a/aosp/external/openssh/buildpkg.sh.in b/aosp/external/openssh/buildpkg.sh.in new file mode 100644 index 0000000000000000000000000000000000000000..15555cd7ed7e793613ae1089ceac5994ef8db124 --- /dev/null +++ b/aosp/external/openssh/buildpkg.sh.in @@ -0,0 +1,677 @@ +#!/bin/sh +# +# Fake Root Solaris/SVR4/SVR5 Build System - Prototype +# +# The following code has been provide under Public Domain License. I really +# don't care what you use it for. Just as long as you don't complain to me +# nor my employer if you break it. - Ben Lindstrom (mouring@eviladmin.org) +# +umask 022 +# +# Options for building the package +# You can create a openssh-config.local with your customized options +# +REMOVE_FAKE_ROOT_WHEN_DONE=yes +# +# uncommenting TEST_DIR and using +# configure --prefix=/var/tmp --with-privsep-path=/var/tmp/empty +# and +# PKGNAME=tOpenSSH should allow testing a package without interfering +# with a real OpenSSH package on a system. This is not needed on systems +# that support the -R option to pkgadd. +#TEST_DIR=/var/tmp # leave commented out for production build +PKGNAME=OpenSSH +# revisions within the same version (REV=a) +#REV= +SYSVINIT_NAME=opensshd +AWK=${AWK:="nawk"} +MAKE=${MAKE:="make"} +SSHDUID=67 # Default privsep uid +SSHDGID=67 # Default privsep gid +# uncomment these next three as needed +#PERMIT_ROOT_LOGIN=no +#X11_FORWARDING=yes +#USR_LOCAL_IS_SYMLINK=yes +# System V init run levels +SYSVINITSTART=S98 +SYSVINITSTOPT=K30 +# We will source these if they exist +POST_MAKE_INSTALL_FIXES=./pkg-post-make-install-fixes.sh +POST_PROTOTYPE_EDITS=./pkg-post-prototype-edit.sh +# We'll be one level deeper looking for these +PKG_PREINSTALL_LOCAL=../pkg-preinstall.local +PKG_POSTINSTALL_LOCAL=../pkg-postinstall.local +PKG_PREREMOVE_LOCAL=../pkg-preremove.local +PKG_POSTREMOVE_LOCAL=../pkg-postremove.local +PKG_REQUEST_LOCAL=../pkg-request.local +# end of sourced files +# +OPENSSHD=opensshd.init +OPENSSH_MANIFEST=openssh.xml +OPENSSH_FMRI=svc:/site/${SYSVINIT_NAME}:default +SMF_METHOD_DIR=/lib/svc/method/site +SMF_MANIFEST_DIR=/var/svc/manifest/site + +PATH_GROUPADD_PROG=@PATH_GROUPADD_PROG@ +PATH_USERADD_PROG=@PATH_USERADD_PROG@ +PATH_PASSWD_PROG=@PATH_PASSWD_PROG@ +# +# list of system directories we do NOT want to change owner/group/perms +# when installing our package +SYSTEM_DIR="/etc \ +/etc/init.d \ +/etc/rcS.d \ +/etc/rc0.d \ +/etc/rc1.d \ +/etc/rc2.d \ +/etc/opt \ +/lib \ +/lib/svc \ +/lib/svc/method \ +/lib/svc/method/site \ +/opt \ +/opt/bin \ +/usr \ +/usr/bin \ +/usr/lib \ +/usr/sbin \ +/usr/share \ +/usr/share/man \ +/usr/share/man/man1 \ +/usr/share/man/man8 \ +/usr/local \ +/usr/local/bin \ +/usr/local/etc \ +/usr/local/libexec \ +/usr/local/man \ +/usr/local/man/man1 \ +/usr/local/man/man8 \ +/usr/local/sbin \ +/usr/local/share \ +/var \ +/var/opt \ +/var/run \ +/var/svc \ +/var/svc/manifest \ +/var/svc/manifest/site \ +/var/tmp \ +/tmp" + +# We may need to build as root so we make sure PATH is set up +# only set the path if it's not set already +[ -d /opt/bin ] && { + echo $PATH | grep ":/opt/bin" > /dev/null 2>&1 + [ $? -ne 0 ] && PATH=$PATH:/opt/bin +} +[ -d /usr/local/bin ] && { + echo $PATH | grep ":/usr/local/bin" > /dev/null 2>&1 + [ $? -ne 0 ] && PATH=$PATH:/usr/local/bin +} +[ -d /usr/ccs/bin ] && { + echo $PATH | grep ":/usr/ccs/bin" > /dev/null 2>&1 + [ $? -ne 0 ] && PATH=$PATH:/usr/ccs/bin +} +export PATH +# + +[ -f Makefile ] || { + echo "Please run this script from your build directory" + exit 1 +} + +# we will look for openssh-config.local to override the above options +[ -s ./openssh-config.local ] && . ./openssh-config.local + +START=`pwd` +FAKE_ROOT=$START/pkg + +## Fill in some details, like prefix and sysconfdir +for confvar in prefix exec_prefix bindir sbindir libexecdir datadir mandir sysconfdir piddir srcdir +do + eval $confvar=`grep "^$confvar=" Makefile | cut -d = -f 2` +done + +## Are we using Solaris' SMF? +DO_SMF=0 +if egrep "^#define USE_SOLARIS_PROCESS_CONTRACTS" config.h > /dev/null 2>&1 +then + DO_SMF=1 +fi + +## Collect value of privsep user +for confvar in SSH_PRIVSEP_USER +do + eval $confvar=`awk '/#define[ \t]'$confvar'/{print $3}' config.h` +done + +## Set privsep defaults if not defined +if [ -z "$SSH_PRIVSEP_USER" ] +then + SSH_PRIVSEP_USER=sshd +fi + +## Extract common info requires for the 'info' part of the package. +VERSION=`./ssh -V 2>&1 | sed -e 's/,.*//'` + +ARCH=`uname -m` +DEF_MSG="\n" +OS_VER=`uname -v` +SCRIPT_SHELL=/sbin/sh +UNAME_R=`uname -r` +UNAME_S=`uname -s` +case ${UNAME_S} in + SunOS) UNAME_S=Solaris + OS_VER=${UNAME_R} + ARCH=`uname -p` + RCS_D=yes + DEF_MSG="(default: n)" + ;; + SCO_SV) case ${UNAME_R} in + 3.2) UNAME_S=OpenServer5 + OS_VER=`uname -X | grep Release | sed -e 's/^Rel.*3.2v//'` + ;; + 5) UNAME_S=OpenServer6 + ;; + esac + SCRIPT_SHELL=/bin/sh + RC1_D=no + DEF_MSG="(default: n)" + ;; +esac + +case `basename $0` in + buildpkg.sh) +## Start by faking root install +echo "Faking root install..." +[ -d $FAKE_ROOT ] && rm -fr $FAKE_ROOT +mkdir $FAKE_ROOT +${MAKE} install-nokeys DESTDIR=$FAKE_ROOT +if [ $? -gt 0 ] +then + echo "Fake root install failed, stopping." + exit 1 +fi + +## Setup our run level stuff while we are at it. +if [ $DO_SMF -eq 1 ] +then + # For Solaris' SMF, /lib/svc/method/site is the preferred place + # for start/stop scripts that aren't supplied with the OS, and + # similarly /var/svc/manifest/site for manifests. + mkdir -p $FAKE_ROOT${TEST_DIR}${SMF_METHOD_DIR} + mkdir -p $FAKE_ROOT${TEST_DIR}${SMF_MANIFEST_DIR} + + cp ${OPENSSHD} $FAKE_ROOT${TEST_DIR}${SMF_METHOD_DIR}/${SYSVINIT_NAME} + chmod 744 $FAKE_ROOT${TEST_DIR}${SMF_METHOD_DIR}/${SYSVINIT_NAME} + + cat ${OPENSSH_MANIFEST} | \ + sed -e "s|__SYSVINIT_NAME__|${SYSVINIT_NAME}|" \ + -e "s|__SMF_METHOD_DIR__|${SMF_METHOD_DIR}|" \ + > $FAKE_ROOT${TEST_DIR}${SMF_MANIFEST_DIR}/${SYSVINIT_NAME}.xml + chmod 644 $FAKE_ROOT${TEST_DIR}${SMF_MANIFEST_DIR}/${SYSVINIT_NAME}.xml +else + mkdir -p $FAKE_ROOT${TEST_DIR}/etc/init.d + + cp ${OPENSSHD} $FAKE_ROOT${TEST_DIR}/etc/init.d/${SYSVINIT_NAME} + chmod 744 $FAKE_ROOT${TEST_DIR}/etc/init.d/${SYSVINIT_NAME} +fi + +[ "${PERMIT_ROOT_LOGIN}" = no ] && \ + perl -p -i -e "s/#PermitRootLogin yes/PermitRootLogin no/" \ + $FAKE_ROOT${sysconfdir}/sshd_config +[ "${X11_FORWARDING}" = yes ] && \ + perl -p -i -e "s/#X11Forwarding no/X11Forwarding yes/" \ + $FAKE_ROOT${sysconfdir}/sshd_config +# fix PrintMotd +perl -p -i -e "s/#PrintMotd yes/PrintMotd no/" \ + $FAKE_ROOT${sysconfdir}/sshd_config + +# We don't want to overwrite config files on multiple installs +mv $FAKE_ROOT${sysconfdir}/ssh_config $FAKE_ROOT${sysconfdir}/ssh_config.default +mv $FAKE_ROOT${sysconfdir}/sshd_config $FAKE_ROOT${sysconfdir}/sshd_config.default + +# local tweeks here +[ -s "${POST_MAKE_INSTALL_FIXES}" ] && . ${POST_MAKE_INSTALL_FIXES} + +cd $FAKE_ROOT + +## Ok, this is outright wrong, but it will work. I'm tired of pkgmk +## whining. +for i in *; do + PROTO_ARGS="$PROTO_ARGS $i=/$i"; +done + +## Build info file +echo "Building pkginfo file..." +cat > pkginfo << _EOF +PKG=$PKGNAME +NAME="OpenSSH Portable for ${UNAME_S}" +DESC="Secure Shell remote access utility; replaces telnet and rlogin/rsh." +VENDOR="OpenSSH Portable Team - https://www.openssh.com/portable.html" +ARCH=$ARCH +VERSION=$VERSION$REV +CATEGORY="Security,application" +BASEDIR=/ +CLASSES="none" +PSTAMP="${UNAME_S} ${OS_VER} ${ARCH} `date '+%d%b%Y %H:%M'`" +_EOF + +## Build empty depend file that may get updated by $POST_PROTOTYPE_EDITS +echo "Building depend file..." +touch depend + +## Build space file +echo "Building space file..." +if [ $DO_SMF -eq 1 ] +then + # XXX Is this necessary? If not, remove space line from mk-proto.awk. + touch space +else + cat > space << _EOF +# extra space required by start/stop links added by installf +# in postinstall +$TEST_DIR/etc/rc0.d/${SYSVINITSTOPT}${SYSVINIT_NAME} 0 1 +$TEST_DIR/etc/rc2.d/${SYSVINITSTART}${SYSVINIT_NAME} 0 1 +_EOF + [ "$RC1_D" = no ] || \ + echo "$TEST_DIR/etc/rc1.d/${SYSVINITSTOPT}${SYSVINIT_NAME} 0 1" >> space + [ "$RCS_D" = yes ] && \ + echo "$TEST_DIR/etc/rcS.d/${SYSVINITSTOPT}${SYSVINIT_NAME} 0 1" >> space +fi + +## Build preinstall file +echo "Building preinstall file..." +cat > preinstall << _EOF +#! ${SCRIPT_SHELL} +# +_EOF + +# local preinstall changes here +[ -s "${PKG_PREINSTALL_LOCAL}" ] && . ${PKG_PREINSTALL_LOCAL} + +cat >> preinstall << _EOF +# +if [ "\${PRE_INS_STOP}" = "yes" ] +then + if [ $DO_SMF -eq 1 ] + then + svcadm disable $OPENSSH_FMRI + else + ${TEST_DIR}/etc/init.d/${SYSVINIT_NAME} stop + fi +fi + +exit 0 +_EOF + +## Build postinstall file +echo "Building postinstall file..." +cat > postinstall << _EOF +#! ${SCRIPT_SHELL} +# +[ -f \${PKG_INSTALL_ROOT}${sysconfdir}/ssh_config ] || \\ + cp -p \${PKG_INSTALL_ROOT}${sysconfdir}/ssh_config.default \\ + \${PKG_INSTALL_ROOT}${sysconfdir}/ssh_config +[ -f \${PKG_INSTALL_ROOT}${sysconfdir}/sshd_config ] || \\ + cp -p \${PKG_INSTALL_ROOT}${sysconfdir}/sshd_config.default \\ + \${PKG_INSTALL_ROOT}${sysconfdir}/sshd_config + +# make rc?.d dirs only if we are doing a test install +[ -n "${TEST_DIR}" ] && [ $DO_SMF -ne 1 ] && { + [ "$RCS_D" = yes ] && mkdir -p ${TEST_DIR}/etc/rcS.d + mkdir -p ${TEST_DIR}/etc/rc0.d + [ "$RC1_D" = no ] || mkdir -p ${TEST_DIR}/etc/rc1.d + mkdir -p ${TEST_DIR}/etc/rc2.d +} + +if [ $DO_SMF -eq 1 ] +then + # Delete the existing service, if it exists, then import the + # new one. + if svcs $OPENSSH_FMRI > /dev/null 2>&1 + then + svccfg delete -f $OPENSSH_FMRI + fi + # NOTE, The manifest disables sshd by default. + svccfg import ${TEST_DIR}${SMF_MANIFEST_DIR}/${SYSVINIT_NAME}.xml +else + if [ "\${USE_SYM_LINKS}" = yes ] + then + [ "$RCS_D" = yes ] && \\ + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rcS.d/${SYSVINITSTOPT}${SYSVINIT_NAME}=../init.d/${SYSVINIT_NAME} s + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rc0.d/${SYSVINITSTOPT}${SYSVINIT_NAME}=../init.d/${SYSVINIT_NAME} s + [ "$RC1_D" = no ] || \\ + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rc1.d/${SYSVINITSTOPT}${SYSVINIT_NAME}=../init.d/${SYSVINIT_NAME} s + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rc2.d/${SYSVINITSTART}${SYSVINIT_NAME}=../init.d/${SYSVINIT_NAME} s + else + [ "$RCS_D" = yes ] && \\ + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rcS.d/${SYSVINITSTOPT}${SYSVINIT_NAME}=\${PKG_INSTALL_ROOT}$TEST_DIR/etc/init.d/${SYSVINIT_NAME} l + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rc0.d/${SYSVINITSTOPT}${SYSVINIT_NAME}=\${PKG_INSTALL_ROOT}$TEST_DIR/etc/init.d/${SYSVINIT_NAME} l + [ "$RC1_D" = no ] || \\ + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rc1.d/${SYSVINITSTOPT}${SYSVINIT_NAME}=\${PKG_INSTALL_ROOT}$TEST_DIR/etc/init.d/${SYSVINIT_NAME} l + installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR/etc/rc2.d/${SYSVINITSTART}${SYSVINIT_NAME}=\${PKG_INSTALL_ROOT}$TEST_DIR/etc/init.d/${SYSVINIT_NAME} l + fi +fi + +# If piddir doesn't exist we add it. (Ie. --with-pid-dir=/var/opt/ssh) +[ -d $piddir ] || installf ${PKGNAME} \${PKG_INSTALL_ROOT}$TEST_DIR$piddir d 0755 root sys + +_EOF + +# local postinstall changes here +[ -s "${PKG_POSTINSTALL_LOCAL}" ] && . ${PKG_POSTINSTALL_LOCAL} + +cat >> postinstall << _EOF +installf -f ${PKGNAME} + +# Use chroot to handle PKG_INSTALL_ROOT +if [ ! -z "\${PKG_INSTALL_ROOT}" ] +then + chroot="chroot \${PKG_INSTALL_ROOT}" +fi +# If this is a test build, we will skip the groupadd/useradd/passwd commands +if [ ! -z "${TEST_DIR}" ] +then + chroot=echo +fi + + echo "PrivilegeSeparation user always required." + if cut -f1 -d: \${PKG_INSTALL_ROOT}/etc/passwd | egrep '^'$SSH_PRIVSEP_USER'\$' >/dev/null + then + echo "PrivSep user $SSH_PRIVSEP_USER already exists." + SSH_PRIVSEP_GROUP=\`grep "^$SSH_PRIVSEP_USER:" \${PKG_INSTALL_ROOT}/etc/passwd | awk -F: '{print \$4}'\` + SSH_PRIVSEP_GROUP=\`grep ":\$SSH_PRIVSEP_GROUP:" \${PKG_INSTALL_ROOT}/etc/group | awk -F: '{print \$1}'\` + else + DO_PASSWD=yes + fi + [ -z "\$SSH_PRIVSEP_GROUP" ] && SSH_PRIVSEP_GROUP=$SSH_PRIVSEP_USER + + # group required? + if cut -f1 -d: \${PKG_INSTALL_ROOT}/etc/group | egrep '^'\$SSH_PRIVSEP_GROUP'\$' >/dev/null + then + echo "PrivSep group \$SSH_PRIVSEP_GROUP already exists." + else + DO_GROUP=yes + fi + + # create group if required + [ "\$DO_GROUP" = yes ] && { + # Use gid of 67 if possible + if cut -f3 -d: \${PKG_INSTALL_ROOT}/etc/group | egrep '^'$SSHDGID'\$' >/dev/null + then + : + else + sshdgid="-g $SSHDGID" + fi + echo "Creating PrivSep group \$SSH_PRIVSEP_GROUP." + \$chroot ${PATH_GROUPADD_PROG} \$sshdgid \$SSH_PRIVSEP_GROUP + } + + # Create user if required + [ "\$DO_PASSWD" = yes ] && { + # Use uid of 67 if possible + if cut -f3 -d: \${PKG_INSTALL_ROOT}/etc/passwd | egrep '^'$SSHDUID'\$' >/dev/null + then + : + else + sshduid="-u $SSHDUID" + fi + echo "Creating PrivSep user $SSH_PRIVSEP_USER." + \$chroot ${PATH_USERADD_PROG} -c 'SSHD PrivSep User' -s /bin/false -g $SSH_PRIVSEP_USER \$sshduid $SSH_PRIVSEP_USER + \$chroot ${PATH_PASSWD_PROG} -l $SSH_PRIVSEP_USER + } + +if [ "\${POST_INS_START}" = "yes" ] +then + if [ $DO_SMF -eq 1 ] + then + svcadm enable $OPENSSH_FMRI + else + ${TEST_DIR}/etc/init.d/${SYSVINIT_NAME} start + fi +fi +exit 0 +_EOF + +## Build preremove file +echo "Building preremove file..." +cat > preremove << _EOF +#! ${SCRIPT_SHELL} +# +if [ $DO_SMF -eq 1 ] +then + svcadm disable $OPENSSH_FMRI +else + ${TEST_DIR}/etc/init.d/${SYSVINIT_NAME} stop +fi +_EOF + +# local preremove changes here +[ -s "${PKG_PREREMOVE_LOCAL}" ] && . ${PKG_PREREMOVE_LOCAL} + +cat >> preremove << _EOF +exit 0 +_EOF + +## Build postremove file +echo "Building postremove file..." +cat > postremove << _EOF +#! ${SCRIPT_SHELL} +# +if [ $DO_SMF -eq 1 ] +then + if svcs $OPENSSH_FMRI > /dev/null 2>&1 + then + svccfg delete -f $OPENSSH_FMRI + fi +fi +_EOF + +# local postremove changes here +[ -s "${PKG_POSTREMOVE_LOCAL}" ] && . ${PKG_POSTREMOVE_LOCAL} + +cat >> postremove << _EOF +exit 0 +_EOF + +## Build request file +echo "Building request file..." +cat > request << _EOF +trap 'exit 3' 15 + +_EOF + +[ -x /usr/bin/ckyorn ] || cat >> request << _EOF + +ckyorn() { +# for some strange reason OpenServer5 has no ckyorn +# We build a striped down version here + +DEFAULT=n +PROMPT="Yes or No [yes,no,?,quit]" +HELP_PROMPT=" Enter y or yes if your answer is yes; n or no if your answer is no." +USAGE="usage: ckyorn [options] +where options may include: + -d default + -h help + -p prompt +" + +if [ \$# != 0 ] +then + while getopts d:p:h: c + do + case \$c in + h) HELP_PROMPT="\$OPTARG" ;; + d) DEFAULT=\$OPTARG ;; + p) PROMPT=\$OPTARG ;; + \\?) echo "\$USAGE" 1>&2 + exit 1 ;; + esac + done + shift \`expr \$OPTIND - 1\` +fi + +while true +do + echo "\${PROMPT}\\c " 1>&2 + read key + [ -z "\$key" ] && key=\$DEFAULT + case \$key in + [n,N]|[n,N][o,O]|[y,Y]|[y,Y][e,E][s,S]) echo "\${key}\\c" + exit 0 ;; + \\?) echo \$HELP_PROMPT 1>&2 ;; + q|quit) echo "q\\c" 1>&2 + exit 3 ;; + esac +done + +} + +_EOF + +if [ $DO_SMF -eq 1 ] +then + # This could get hairy, as the running sshd may not be under SMF. + # We'll assume an earlier version of OpenSSH started via SMF. + cat >> request << _EOF +PRE_INS_STOP=no +POST_INS_START=no +# determine if should restart the daemon +if [ -s ${piddir}/sshd.pid ] && \\ + /usr/bin/svcs -H $OPENSSH_FMRI 2>&1 | egrep "^online" > /dev/null 2>&1 +then + ans=\`ckyorn -d n \\ +-p "Should the running sshd daemon be restarted? ${DEF_MSG}"\` || exit \$? + case \$ans in + [y,Y]*) PRE_INS_STOP=yes + POST_INS_START=yes + ;; + esac + +else + +# determine if we should start sshd + ans=\`ckyorn -d n \\ +-p "Start the sshd daemon after installing this package? ${DEF_MSG}"\` || exit \$? + case \$ans in + [y,Y]*) POST_INS_START=yes ;; + esac +fi + +# make parameters available to installation service, +# and so to any other packaging scripts +cat >\$1 <> request << _EOF +USE_SYM_LINKS=no +PRE_INS_STOP=no +POST_INS_START=no +# Use symbolic links? +ans=\`ckyorn -d n \\ +-p "Do you want symbolic links for the start/stop scripts? ${DEF_MSG}"\` || exit \$? +case \$ans in + [y,Y]*) USE_SYM_LINKS=yes ;; +esac + +# determine if should restart the daemon +if [ -s ${piddir}/sshd.pid -a -f ${TEST_DIR}/etc/init.d/${SYSVINIT_NAME} ] +then + ans=\`ckyorn -d n \\ +-p "Should the running sshd daemon be restarted? ${DEF_MSG}"\` || exit \$? + case \$ans in + [y,Y]*) PRE_INS_STOP=yes + POST_INS_START=yes + ;; + esac + +else + +# determine if we should start sshd + ans=\`ckyorn -d n \\ +-p "Start the sshd daemon after installing this package? ${DEF_MSG}"\` || exit \$? + case \$ans in + [y,Y]*) POST_INS_START=yes ;; + esac +fi + +# make parameters available to installation service, +# and so to any other packaging scripts +cat >\$1 <> request << _EOF +exit 0 + +_EOF + +## Next Build our prototype +echo "Building prototype file..." +cat >mk-proto.awk << _EOF + BEGIN { print "i pkginfo"; print "i depend"; \\ + print "i preinstall"; print "i postinstall"; \\ + print "i preremove"; print "i postremove"; \\ + print "i request"; print "i space"; \\ + split("$SYSTEM_DIR",sys_files); } + { + for (dir in sys_files) { if ( \$3 != sys_files[dir] ) + { if ( \$1 == "s" ) + { \$5=""; \$6=""; } + else + { \$5="root"; \$6="sys"; } + } + else + { \$4="?"; \$5="?"; \$6="?"; break;} + } } + { print; } +_EOF + +find . | egrep -v "prototype|pkginfo|mk-proto.awk" | sort | \ + pkgproto $PROTO_ARGS | ${AWK} -f mk-proto.awk > prototype + +# /usr/local is a symlink on some systems +[ "${USR_LOCAL_IS_SYMLINK}" = yes ] && { + grep -v "^d none /usr/local ? ? ?$" prototype > prototype.new + mv prototype.new prototype +} + +## Step back a directory and now build the package. +cd .. +# local prototype tweeks here +[ -s "${POST_PROTOTYPE_EDITS}" ] && . ${POST_PROTOTYPE_EDITS} + +echo "Building package.." +pkgmk -d ${FAKE_ROOT} -f $FAKE_ROOT/prototype -o +echo | pkgtrans -os ${FAKE_ROOT} ${START}/$PKGNAME-$VERSION$REV-$UNAME_S-$ARCH.pkg + ;; + + justpkg.sh) +rm -fr ${FAKE_ROOT}/${PKGNAME} +grep -v "^PSTAMP=" $FAKE_ROOT/pkginfo > $$tmp +mv $$tmp $FAKE_ROOT/pkginfo +cat >> $FAKE_ROOT/pkginfo << _EOF +PSTAMP="${UNAME_S} ${OS_VER} ${ARCH} `date '+%d%b%Y %H:%M'`" +_EOF +pkgmk -d ${FAKE_ROOT} -f $FAKE_ROOT/prototype -o +echo | pkgtrans -os ${FAKE_ROOT} ${START}/$PKGNAME-$VERSION$REV-$UNAME_S-$ARCH.pkg + ;; + +esac + +[ "${REMOVE_FAKE_ROOT_WHEN_DONE}" = yes ] && rm -rf $FAKE_ROOT +exit 0 + diff --git a/aosp/external/openssh/canohost.c b/aosp/external/openssh/canohost.c new file mode 100644 index 0000000000000000000000000000000000000000..a810da0eeb736b374b36fe6e89a527cc80759e99 --- /dev/null +++ b/aosp/external/openssh/canohost.c @@ -0,0 +1,204 @@ +/* $OpenBSD: canohost.c,v 1.75 2020/10/18 11:32:01 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for returning the canonical host name of the remote site. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "packet.h" +#include "log.h" +#include "canohost.h" +#include "misc.h" + +void +ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) +{ + struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr; + struct sockaddr_in *a4 = (struct sockaddr_in *)addr; + struct in_addr inaddr; + u_int16_t port; + + if (addr->ss_family != AF_INET6 || + !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) + return; + + debug3("Normalising mapped IPv4 in IPv6 address"); + + memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr)); + port = a6->sin6_port; + + memset(a4, 0, sizeof(*a4)); + + a4->sin_family = AF_INET; + *len = sizeof(*a4); + memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr)); + a4->sin_port = port; +} + +/* + * Returns the local/remote IP-address/hostname of socket as a string. + * The returned string must be freed. + */ +static char * +get_socket_address(int sock, int remote, int flags) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + char ntop[NI_MAXHOST]; + int r; + + /* Get IP address of client. */ + addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + + if (remote) { + if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) != 0) + return NULL; + } else { + if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) != 0) + return NULL; + } + + /* Work around Linux IPv6 weirdness */ + if (addr.ss_family == AF_INET6) { + addrlen = sizeof(struct sockaddr_in6); + ipv64_normalise_mapped(&addr, &addrlen); + } + + switch (addr.ss_family) { + case AF_INET: + case AF_INET6: + /* Get the address in ascii. */ + if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ntop, + sizeof(ntop), NULL, 0, flags)) != 0) { + error_f("getnameinfo %d failed: %s", + flags, ssh_gai_strerror(r)); + return NULL; + } + return xstrdup(ntop); + case AF_UNIX: + /* Get the Unix domain socket path. */ + return xstrdup(((struct sockaddr_un *)&addr)->sun_path); + default: + /* We can't look up remote Unix domain sockets. */ + return NULL; + } +} + +char * +get_peer_ipaddr(int sock) +{ + char *p; + + if ((p = get_socket_address(sock, 1, NI_NUMERICHOST)) != NULL) + return p; + return xstrdup("UNKNOWN"); +} + +char * +get_local_ipaddr(int sock) +{ + char *p; + + if ((p = get_socket_address(sock, 0, NI_NUMERICHOST)) != NULL) + return p; + return xstrdup("UNKNOWN"); +} + +char * +get_local_name(int fd) +{ + char *host, myname[NI_MAXHOST]; + + /* Assume we were passed a socket */ + if ((host = get_socket_address(fd, 0, NI_NAMEREQD)) != NULL) + return host; + + /* Handle the case where we were passed a pipe */ + if (gethostname(myname, sizeof(myname)) == -1) { + verbose_f("gethostname: %s", strerror(errno)); + host = xstrdup("UNKNOWN"); + } else { + host = xstrdup(myname); + } + + return host; +} + +/* Returns the local/remote port for the socket. */ + +static int +get_sock_port(int sock, int local) +{ + struct sockaddr_storage from; + socklen_t fromlen; + char strport[NI_MAXSERV]; + int r; + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (local) { + if (getsockname(sock, (struct sockaddr *)&from, &fromlen) == -1) { + error("getsockname failed: %.100s", strerror(errno)); + return 0; + } + } else { + if (getpeername(sock, (struct sockaddr *)&from, &fromlen) == -1) { + debug("getpeername failed: %.100s", strerror(errno)); + return -1; + } + } + + /* Work around Linux IPv6 weirdness */ + if (from.ss_family == AF_INET6) + fromlen = sizeof(struct sockaddr_in6); + + /* Non-inet sockets don't have a port number. */ + if (from.ss_family != AF_INET && from.ss_family != AF_INET6) + return 0; + + /* Return port number. */ + if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, + strport, sizeof(strport), NI_NUMERICSERV)) != 0) + fatal_f("getnameinfo NI_NUMERICSERV failed: %s", + ssh_gai_strerror(r)); + return atoi(strport); +} + +int +get_peer_port(int sock) +{ + return get_sock_port(sock, 0); +} + +int +get_local_port(int sock) +{ + return get_sock_port(sock, 1); +} diff --git a/aosp/external/openssh/canohost.h b/aosp/external/openssh/canohost.h new file mode 100644 index 0000000000000000000000000000000000000000..26d62855a935443741836dd958ce808e0b5c5ff6 --- /dev/null +++ b/aosp/external/openssh/canohost.h @@ -0,0 +1,26 @@ +/* $OpenBSD: canohost.h,v 1.12 2016/03/07 19:02:43 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef _CANOHOST_H +#define _CANOHOST_H + +char *get_peer_ipaddr(int); +int get_peer_port(int); +char *get_local_ipaddr(int); +char *get_local_name(int); +int get_local_port(int); + +#endif /* _CANOHOST_H */ + +void ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *); diff --git a/aosp/external/openssh/chacha.c b/aosp/external/openssh/chacha.c new file mode 100644 index 0000000000000000000000000000000000000000..a84c25ea88cea89c6abf879f9597a53246e7403d --- /dev/null +++ b/aosp/external/openssh/chacha.c @@ -0,0 +1,219 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include "includes.h" + +#include "chacha.h" + +/* $OpenBSD: chacha.c,v 1.1 2013/11/21 00:45:44 djm Exp $ */ + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct chacha_ctx chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +void +chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits) +{ + const char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +void +chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter) +{ + x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0); + x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4); + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +void +chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + x0 = XOR(x0,U8TO32_LITTLE(m + 0)); + x1 = XOR(x1,U8TO32_LITTLE(m + 4)); + x2 = XOR(x2,U8TO32_LITTLE(m + 8)); + x3 = XOR(x3,U8TO32_LITTLE(m + 12)); + x4 = XOR(x4,U8TO32_LITTLE(m + 16)); + x5 = XOR(x5,U8TO32_LITTLE(m + 20)); + x6 = XOR(x6,U8TO32_LITTLE(m + 24)); + x7 = XOR(x7,U8TO32_LITTLE(m + 28)); + x8 = XOR(x8,U8TO32_LITTLE(m + 32)); + x9 = XOR(x9,U8TO32_LITTLE(m + 36)); + x10 = XOR(x10,U8TO32_LITTLE(m + 40)); + x11 = XOR(x11,U8TO32_LITTLE(m + 44)); + x12 = XOR(x12,U8TO32_LITTLE(m + 48)); + x13 = XOR(x13,U8TO32_LITTLE(m + 52)); + x14 = XOR(x14,U8TO32_LITTLE(m + 56)); + x15 = XOR(x15,U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} diff --git a/aosp/external/openssh/chacha.h b/aosp/external/openssh/chacha.h new file mode 100644 index 0000000000000000000000000000000000000000..19a61e29423018b308542edab51dd21ac1dcaac9 --- /dev/null +++ b/aosp/external/openssh/chacha.h @@ -0,0 +1,36 @@ +/* $OpenBSD: chacha.h,v 1.5 2021/04/03 05:54:14 djm Exp $ */ + +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#ifndef CHACHA_H +#define CHACHA_H + +#include +#include + +struct chacha_ctx { + u_int input[16]; +}; + +#define CHACHA_MINKEYLEN 16 +#define CHACHA_NONCELEN 8 +#define CHACHA_CTRLEN 8 +#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN) +#define CHACHA_BLOCKLEN 64 + +void chacha_keysetup(struct chacha_ctx *x, const u_char *k, u_int kbits) + __attribute__((__bounded__(__minbytes__, 2, CHACHA_MINKEYLEN))); +void chacha_ivsetup(struct chacha_ctx *x, const u_char *iv, const u_char *ctr) + __attribute__((__bounded__(__minbytes__, 2, CHACHA_NONCELEN))) + __attribute__((__bounded__(__minbytes__, 3, CHACHA_CTRLEN))); +void chacha_encrypt_bytes(struct chacha_ctx *x, const u_char *m, + u_char *c, u_int bytes) + __attribute__((__bounded__(__buffer__, 2, 4))) + __attribute__((__bounded__(__buffer__, 3, 4))); + +#endif /* CHACHA_H */ + diff --git a/aosp/external/openssh/channels.c b/aosp/external/openssh/channels.c new file mode 100644 index 0000000000000000000000000000000000000000..ee3c787922fbcf391122b6a508cd15acf4fb545a --- /dev/null +++ b/aosp/external/openssh/channels.c @@ -0,0 +1,5133 @@ +/* $OpenBSD: channels.c,v 1.415 2022/03/30 21:10:25 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains functions for generic socket connection forwarding. + * There is also code for initiating connection forwarding for X11 connections, + * arbitrary tcp/ip connections, and the authentication agent connection. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 support added by Markus Friedl. + * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. + * Copyright (c) 1999 Dug Song. All rights reserved. + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "packet.h" +#include "log.h" +#include "misc.h" +#include "channels.h" +#include "compat.h" +#include "canohost.h" +#include "sshkey.h" +#include "authfd.h" +#include "pathnames.h" +#include "match.h" + +/* XXX remove once we're satisfied there's no lurking bugs */ +/* #define DEBUG_CHANNEL_POLL 1 */ + +/* -- agent forwarding */ +#define NUM_SOCKS 10 + +/* -- tcp forwarding */ +/* special-case port number meaning allow any port */ +#define FWD_PERMIT_ANY_PORT 0 + +/* special-case wildcard meaning allow any host */ +#define FWD_PERMIT_ANY_HOST "*" + +/* -- X11 forwarding */ +/* Maximum number of fake X11 displays to try. */ +#define MAX_DISPLAYS 1000 + +/* Per-channel callback for pre/post IO actions */ +typedef void chan_fn(struct ssh *, Channel *c); + +/* + * Data structure for storing which hosts are permitted for forward requests. + * The local sides of any remote forwards are stored in this array to prevent + * a corrupt remote server from accessing arbitrary TCP/IP ports on our local + * network (which might be behind a firewall). + */ +/* XXX: streamlocal wants a path instead of host:port */ +/* Overload host_to_connect; we could just make this match Forward */ +/* XXX - can we use listen_host instead of listen_path? */ +struct permission { + char *host_to_connect; /* Connect to 'host'. */ + int port_to_connect; /* Connect to 'port'. */ + char *listen_host; /* Remote side should listen address. */ + char *listen_path; /* Remote side should listen path. */ + int listen_port; /* Remote side should listen port. */ + Channel *downstream; /* Downstream mux*/ +}; + +/* + * Stores the forwarding permission state for a single direction (local or + * remote). + */ +struct permission_set { + /* + * List of all local permitted host/port pairs to allow for the + * user. + */ + u_int num_permitted_user; + struct permission *permitted_user; + + /* + * List of all permitted host/port pairs to allow for the admin. + */ + u_int num_permitted_admin; + struct permission *permitted_admin; + + /* + * If this is true, all opens/listens are permitted. This is the + * case on the server on which we have to trust the client anyway, + * and the user could do anything after logging in. + */ + int all_permitted; +}; + +/* Master structure for channels state */ +struct ssh_channels { + /* + * Pointer to an array containing all allocated channels. The array + * is dynamically extended as needed. + */ + Channel **channels; + + /* + * Size of the channel array. All slots of the array must always be + * initialized (at least the type field); unused slots set to NULL + */ + u_int channels_alloc; + + /* + * 'channel_pre*' are called just before IO to add any bits + * relevant to channels in the c->io_want bitmasks. + * + * 'channel_post*': perform any appropriate operations for + * channels which have c->io_ready events pending. + */ + chan_fn **channel_pre; + chan_fn **channel_post; + + /* -- tcp forwarding */ + struct permission_set local_perms; + struct permission_set remote_perms; + + /* -- X11 forwarding */ + + /* Saved X11 local (client) display. */ + char *x11_saved_display; + + /* Saved X11 authentication protocol name. */ + char *x11_saved_proto; + + /* Saved X11 authentication data. This is the real data. */ + char *x11_saved_data; + u_int x11_saved_data_len; + + /* Deadline after which all X11 connections are refused */ + u_int x11_refuse_time; + + /* + * Fake X11 authentication data. This is what the server will be + * sending us; we should replace any occurrences of this by the + * real data. + */ + u_char *x11_fake_data; + u_int x11_fake_data_len; + + /* AF_UNSPEC or AF_INET or AF_INET6 */ + int IPv4or6; +}; + +/* helper */ +static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype); +static const char *channel_rfwd_bind_host(const char *listen_host); + +/* non-blocking connect helpers */ +static int connect_next(struct channel_connect *); +static void channel_connect_ctx_free(struct channel_connect *); +static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); +static int rdynamic_connect_finish(struct ssh *, Channel *); + +/* Setup helper */ +static void channel_handler_init(struct ssh_channels *sc); + +/* -- channel core */ + +void +channel_init_channels(struct ssh *ssh) +{ + struct ssh_channels *sc; + + if ((sc = calloc(1, sizeof(*sc))) == NULL) + fatal_f("allocation failed"); + sc->channels_alloc = 10; + sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); + sc->IPv4or6 = AF_UNSPEC; + channel_handler_init(sc); + + ssh->chanctxt = sc; +} + +Channel * +channel_by_id(struct ssh *ssh, int id) +{ + Channel *c; + + if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { + logit_f("%d: bad id", id); + return NULL; + } + c = ssh->chanctxt->channels[id]; + if (c == NULL) { + logit_f("%d: bad id: channel free", id); + return NULL; + } + return c; +} + +Channel * +channel_by_remote_id(struct ssh *ssh, u_int remote_id) +{ + Channel *c; + u_int i; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c != NULL && c->have_remote_id && c->remote_id == remote_id) + return c; + } + return NULL; +} + +/* + * Returns the channel if it is allowed to receive protocol messages. + * Private channels, like listening sockets, may not receive messages. + */ +Channel * +channel_lookup(struct ssh *ssh, int id) +{ + Channel *c; + + if ((c = channel_by_id(ssh, id)) == NULL) + return NULL; + + switch (c->type) { + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_MUX_PROXY: + return c; + } + logit("Non-public channel %d, type %d.", id, c->type); + return NULL; +} + +/* + * Register filedescriptors for a channel, used when allocating a channel or + * when the channel consumer/producer is ready, e.g. shell exec'd + */ +static void +channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, + int extusage, int nonblock, int is_tty) +{ + if (rfd != -1) + fcntl(rfd, F_SETFD, FD_CLOEXEC); + if (wfd != -1 && wfd != rfd) + fcntl(wfd, F_SETFD, FD_CLOEXEC); + if (efd != -1 && efd != rfd && efd != wfd) + fcntl(efd, F_SETFD, FD_CLOEXEC); + + c->rfd = rfd; + c->wfd = wfd; + c->sock = (rfd == wfd) ? rfd : -1; + c->efd = efd; + c->extended_usage = extusage; + + if ((c->isatty = is_tty) != 0) + debug2("channel %d: rfd %d isatty", c->self, c->rfd); +#ifdef _AIX + /* XXX: Later AIX versions can't push as much data to tty */ + c->wfd_isatty = is_tty || isatty(c->wfd); +#endif + + /* enable nonblocking mode */ + c->restore_block = 0; + if (nonblock == CHANNEL_NONBLOCK_STDIO) { + /* + * Special handling for stdio file descriptors: do not set + * non-blocking mode if they are TTYs. Otherwise prepare to + * restore their blocking state on exit to avoid interfering + * with other programs that follow. + */ + if (rfd != -1 && !isatty(rfd) && fcntl(rfd, F_GETFL) == 0) { + c->restore_block |= CHANNEL_RESTORE_RFD; + set_nonblock(rfd); + } + if (wfd != -1 && !isatty(wfd) && fcntl(wfd, F_GETFL) == 0) { + c->restore_block |= CHANNEL_RESTORE_WFD; + set_nonblock(wfd); + } + if (efd != -1 && !isatty(efd) && fcntl(efd, F_GETFL) == 0) { + c->restore_block |= CHANNEL_RESTORE_EFD; + set_nonblock(efd); + } + } else if (nonblock) { + if (rfd != -1) + set_nonblock(rfd); + if (wfd != -1) + set_nonblock(wfd); + if (efd != -1) + set_nonblock(efd); + } +} + +/* + * Allocate a new channel object and set its type and socket. This will cause + * remote_name to be freed. + */ +Channel * +channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, + u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) +{ + struct ssh_channels *sc = ssh->chanctxt; + u_int i, found; + Channel *c; + int r; + + /* Try to find a free slot where to put the new channel. */ + for (i = 0; i < sc->channels_alloc; i++) { + if (sc->channels[i] == NULL) { + /* Found a free slot. */ + found = i; + break; + } + } + if (i >= sc->channels_alloc) { + /* + * There are no free slots. Take last+1 slot and expand + * the array. + */ + found = sc->channels_alloc; + if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) + fatal_f("internal error: channels_alloc %d too big", + sc->channels_alloc); + sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, + sc->channels_alloc + 10, sizeof(*sc->channels)); + sc->channels_alloc += 10; + debug2("channel: expanding %d", sc->channels_alloc); + } + /* Initialize and return new channel. */ + c = sc->channels[found] = xcalloc(1, sizeof(Channel)); + if ((c->input = sshbuf_new()) == NULL || + (c->output = sshbuf_new()) == NULL || + (c->extended = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0) + fatal_fr(r, "sshbuf_set_max_size"); + c->ostate = CHAN_OUTPUT_OPEN; + c->istate = CHAN_INPUT_OPEN; + channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); + c->self = found; + c->type = type; + c->ctype = ctype; + c->local_window = window; + c->local_window_max = window; + c->local_maxpacket = maxpack; + c->remote_name = xstrdup(remote_name); + c->ctl_chan = -1; + c->delayed = 1; /* prevent call to channel_post handler */ + TAILQ_INIT(&c->status_confirms); + debug("channel %d: new [%s]", found, remote_name); + return c; +} + +int +channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) +{ + int ret, fd = *fdp; + + if (fd == -1) + return 0; + + if ((*fdp == c->rfd && (c->restore_block & CHANNEL_RESTORE_RFD) != 0) || + (*fdp == c->wfd && (c->restore_block & CHANNEL_RESTORE_WFD) != 0) || + (*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0)) + (void)fcntl(*fdp, F_SETFL, 0); /* restore blocking */ + + if (*fdp == c->rfd) { + c->io_want &= ~SSH_CHAN_IO_RFD; + c->io_ready &= ~SSH_CHAN_IO_RFD; + c->rfd = -1; + c->pfds[0] = -1; + } + if (*fdp == c->wfd) { + c->io_want &= ~SSH_CHAN_IO_WFD; + c->io_ready &= ~SSH_CHAN_IO_WFD; + c->wfd = -1; + c->pfds[1] = -1; + } + if (*fdp == c->efd) { + c->io_want &= ~SSH_CHAN_IO_EFD; + c->io_ready &= ~SSH_CHAN_IO_EFD; + c->efd = -1; + c->pfds[2] = -1; + } + if (*fdp == c->sock) { + c->io_want &= ~SSH_CHAN_IO_SOCK; + c->io_ready &= ~SSH_CHAN_IO_SOCK; + c->sock = -1; + c->pfds[3] = -1; + } + + ret = close(fd); + *fdp = -1; /* probably redundant */ + return ret; +} + +/* Close all channel fd/socket. */ +static void +channel_close_fds(struct ssh *ssh, Channel *c) +{ + int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; + + channel_close_fd(ssh, c, &c->sock); + if (rfd != sock) + channel_close_fd(ssh, c, &c->rfd); + if (wfd != sock && wfd != rfd) + channel_close_fd(ssh, c, &c->wfd); + if (efd != sock && efd != rfd && efd != wfd) + channel_close_fd(ssh, c, &c->efd); +} + +static void +fwd_perm_clear(struct permission *perm) +{ + free(perm->host_to_connect); + free(perm->listen_host); + free(perm->listen_path); + memset(perm, 0, sizeof(*perm)); +} + +/* Returns an printable name for the specified forwarding permission list */ +static const char * +fwd_ident(int who, int where) +{ + if (who == FORWARD_ADM) { + if (where == FORWARD_LOCAL) + return "admin local"; + else if (where == FORWARD_REMOTE) + return "admin remote"; + } else if (who == FORWARD_USER) { + if (where == FORWARD_LOCAL) + return "user local"; + else if (where == FORWARD_REMOTE) + return "user remote"; + } + fatal("Unknown forward permission list %d/%d", who, where); +} + +/* Returns the forwarding permission list for the specified direction */ +static struct permission_set * +permission_set_get(struct ssh *ssh, int where) +{ + struct ssh_channels *sc = ssh->chanctxt; + + switch (where) { + case FORWARD_LOCAL: + return &sc->local_perms; + break; + case FORWARD_REMOTE: + return &sc->remote_perms; + break; + default: + fatal_f("invalid forwarding direction %d", where); + } +} + +/* Returns pointers to the specified forwarding list and its element count */ +static void +permission_set_get_array(struct ssh *ssh, int who, int where, + struct permission ***permpp, u_int **npermpp) +{ + struct permission_set *pset = permission_set_get(ssh, where); + + switch (who) { + case FORWARD_USER: + *permpp = &pset->permitted_user; + *npermpp = &pset->num_permitted_user; + break; + case FORWARD_ADM: + *permpp = &pset->permitted_admin; + *npermpp = &pset->num_permitted_admin; + break; + default: + fatal_f("invalid forwarding client %d", who); + } +} + +/* Adds an entry to the specified forwarding list */ +static int +permission_set_add(struct ssh *ssh, int who, int where, + const char *host_to_connect, int port_to_connect, + const char *listen_host, const char *listen_path, int listen_port, + Channel *downstream) +{ + struct permission **permp; + u_int n, *npermp; + + permission_set_get_array(ssh, who, where, &permp, &npermp); + + if (*npermp >= INT_MAX) + fatal_f("%s overflow", fwd_ident(who, where)); + + *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); + n = (*npermp)++; +#define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) + (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); + (*permp)[n].port_to_connect = port_to_connect; + (*permp)[n].listen_host = MAYBE_DUP(listen_host); + (*permp)[n].listen_path = MAYBE_DUP(listen_path); + (*permp)[n].listen_port = listen_port; + (*permp)[n].downstream = downstream; +#undef MAYBE_DUP + return (int)n; +} + +static void +mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + struct permission *perm; + int r; + u_int i; + + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (perm->downstream != c) + continue; + + /* cancel on the server, since mux client is gone */ + debug("channel %d: cleanup remote forward for %s:%u", + c->self, perm->listen_host, perm->listen_port); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "cancel-tcpip-forward")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || + (r = sshpkt_put_cstring(ssh, + channel_rfwd_bind_host(perm->listen_host))) != 0 || + (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal_fr(r, "channel %i", c->self); + } + fwd_perm_clear(perm); /* unregister */ + } +} + +/* Free the channel and close its fd/socket. */ +void +channel_free(struct ssh *ssh, Channel *c) +{ + struct ssh_channels *sc = ssh->chanctxt; + char *s; + u_int i, n; + Channel *other; + struct channel_confirm *cc; + + for (n = 0, i = 0; i < sc->channels_alloc; i++) { + if ((other = sc->channels[i]) == NULL) + continue; + n++; + /* detach from mux client and prepare for closing */ + if (c->type == SSH_CHANNEL_MUX_CLIENT && + other->type == SSH_CHANNEL_MUX_PROXY && + other->mux_ctx == c) { + other->mux_ctx = NULL; + other->type = SSH_CHANNEL_OPEN; + other->istate = CHAN_INPUT_CLOSED; + other->ostate = CHAN_OUTPUT_CLOSED; + } + } + debug("channel %d: free: %s, nchannels %u", c->self, + c->remote_name ? c->remote_name : "???", n); + + if (c->type == SSH_CHANNEL_MUX_CLIENT) { + mux_remove_remote_forwardings(ssh, c); + free(c->mux_ctx); + c->mux_ctx = NULL; + } else if (c->type == SSH_CHANNEL_MUX_LISTENER) { + free(c->mux_ctx); + c->mux_ctx = NULL; + } + + if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) { + s = channel_open_message(ssh); + debug3("channel %d: status: %s", c->self, s); + free(s); + } + + channel_close_fds(ssh, c); + sshbuf_free(c->input); + sshbuf_free(c->output); + sshbuf_free(c->extended); + c->input = c->output = c->extended = NULL; + free(c->remote_name); + c->remote_name = NULL; + free(c->path); + c->path = NULL; + free(c->listening_addr); + c->listening_addr = NULL; + while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { + if (cc->abandon_cb != NULL) + cc->abandon_cb(ssh, c, cc->ctx); + TAILQ_REMOVE(&c->status_confirms, cc, entry); + freezero(cc, sizeof(*cc)); + } + if (c->filter_cleanup != NULL && c->filter_ctx != NULL) + c->filter_cleanup(ssh, c->self, c->filter_ctx); + sc->channels[c->self] = NULL; + freezero(c, sizeof(*c)); +} + +void +channel_free_all(struct ssh *ssh) +{ + u_int i; + struct ssh_channels *sc = ssh->chanctxt; + + for (i = 0; i < sc->channels_alloc; i++) + if (sc->channels[i] != NULL) + channel_free(ssh, sc->channels[i]); + + free(sc->channels); + sc->channels = NULL; + sc->channels_alloc = 0; + + free(sc->x11_saved_display); + sc->x11_saved_display = NULL; + + free(sc->x11_saved_proto); + sc->x11_saved_proto = NULL; + + free(sc->x11_saved_data); + sc->x11_saved_data = NULL; + sc->x11_saved_data_len = 0; + + free(sc->x11_fake_data); + sc->x11_fake_data = NULL; + sc->x11_fake_data_len = 0; +} + +/* + * Closes the sockets/fds of all channels. This is used to close extra file + * descriptors after a fork. + */ +void +channel_close_all(struct ssh *ssh) +{ + u_int i; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) + if (ssh->chanctxt->channels[i] != NULL) + channel_close_fds(ssh, ssh->chanctxt->channels[i]); +} + +/* + * Stop listening to channels. + */ +void +channel_stop_listening(struct ssh *ssh) +{ + u_int i; + Channel *c; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c != NULL) { + switch (c->type) { + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + channel_close_fd(ssh, c, &c->sock); + channel_free(ssh, c); + break; + } + } + } +} + +/* + * Returns true if no channel has too much buffered data, and false if one or + * more channel is overfull. + */ +int +channel_not_very_much_buffered_data(struct ssh *ssh) +{ + u_int i; + u_int maxsize = ssh_packet_get_maxsize(ssh); + Channel *c; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_OPEN) + continue; + if (sshbuf_len(c->output) > maxsize) { + debug2("channel %d: big output buffer %zu > %u", + c->self, sshbuf_len(c->output), maxsize); + return 0; + } + } + return 1; +} + +/* Returns true if any channel is still open. */ +int +channel_still_open(struct ssh *ssh) +{ + u_int i; + Channel *c; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c == NULL) + continue; + switch (c->type) { + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + continue; + case SSH_CHANNEL_LARVAL: + continue; + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_MUX_CLIENT: + case SSH_CHANNEL_MUX_PROXY: + return 1; + default: + fatal_f("bad channel type %d", c->type); + /* NOTREACHED */ + } + } + return 0; +} + +/* Returns the id of an open channel suitable for keepaliving */ +int +channel_find_open(struct ssh *ssh) +{ + u_int i; + Channel *c; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c == NULL || !c->have_remote_id) + continue; + switch (c->type) { + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_MUX_CLIENT: + case SSH_CHANNEL_MUX_PROXY: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + return i; + default: + fatal_f("bad channel type %d", c->type); + /* NOTREACHED */ + } + } + return -1; +} + +/* Returns the state of the channel's extended usage flag */ +const char * +channel_format_extended_usage(const Channel *c) +{ + if (c->efd == -1) + return "closed"; + + switch (c->extended_usage) { + case CHAN_EXTENDED_WRITE: + return "write"; + case CHAN_EXTENDED_READ: + return "read"; + case CHAN_EXTENDED_IGNORE: + return "ignore"; + default: + return "UNKNOWN"; + } +} + +static char * +channel_format_status(const Channel *c) +{ + char *ret = NULL; + + xasprintf(&ret, "t%d %s%u i%u/%zu o%u/%zu e[%s]/%zu " + "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x", + c->type, + c->have_remote_id ? "r" : "nr", c->remote_id, + c->istate, sshbuf_len(c->input), + c->ostate, sshbuf_len(c->output), + channel_format_extended_usage(c), sshbuf_len(c->extended), + c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, + c->io_want, c->io_ready); + return ret; +} + +/* + * Returns a message describing the currently open forwarded connections, + * suitable for sending to the client. The message contains crlf pairs for + * newlines. + */ +char * +channel_open_message(struct ssh *ssh) +{ + struct sshbuf *buf; + Channel *c; + u_int i; + int r; + char *cp, *ret; + + if ((buf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_putf(buf, + "The following connections are open:\r\n")) != 0) + fatal_fr(r, "sshbuf_putf"); + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + c = ssh->chanctxt->channels[i]; + if (c == NULL) + continue; + switch (c->type) { + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_ABANDONED: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_RDYNAMIC_OPEN: + case SSH_CHANNEL_RDYNAMIC_FINISH: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_MUX_PROXY: + case SSH_CHANNEL_MUX_CLIENT: + cp = channel_format_status(c); + if ((r = sshbuf_putf(buf, " #%d %.300s (%s)\r\n", + c->self, c->remote_name, cp)) != 0) { + free(cp); + fatal_fr(r, "sshbuf_putf"); + } + free(cp); + continue; + default: + fatal_f("bad channel type %d", c->type); + /* NOTREACHED */ + } + } + if ((ret = sshbuf_dup_string(buf)) == NULL) + fatal_f("sshbuf_dup_string"); + sshbuf_free(buf); + return ret; +} + +static void +open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) +{ + int r; + + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || + (r = sshpkt_put_cstring(ssh, type)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { + fatal_r(r, "%s: channel %i: open", where, c->self); + } +} + +void +channel_send_open(struct ssh *ssh, int id) +{ + Channel *c = channel_lookup(ssh, id); + int r; + + if (c == NULL) { + logit("channel_send_open: %d: bad id", id); + return; + } + debug2("channel %d: send open", id); + open_preamble(ssh, __func__, c, c->ctype); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i", c->self); +} + +void +channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) +{ + Channel *c = channel_lookup(ssh, id); + int r; + + if (c == NULL) { + logit_f("%d: unknown channel id", id); + return; + } + if (!c->have_remote_id) + fatal_f("channel %d: no remote id", c->self); + + debug2("channel %d: request %s confirm %d", id, service, wantconfirm); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_cstring(ssh, service)) != 0 || + (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { + fatal_fr(r, "channel %i", c->self); + } +} + +void +channel_register_status_confirm(struct ssh *ssh, int id, + channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) +{ + struct channel_confirm *cc; + Channel *c; + + if ((c = channel_lookup(ssh, id)) == NULL) + fatal_f("%d: bad id", id); + + cc = xcalloc(1, sizeof(*cc)); + cc->cb = cb; + cc->abandon_cb = abandon_cb; + cc->ctx = ctx; + TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); +} + +void +channel_register_open_confirm(struct ssh *ssh, int id, + channel_open_fn *fn, void *ctx) +{ + Channel *c = channel_lookup(ssh, id); + + if (c == NULL) { + logit_f("%d: bad id", id); + return; + } + c->open_confirm = fn; + c->open_confirm_ctx = ctx; +} + +void +channel_register_cleanup(struct ssh *ssh, int id, + channel_callback_fn *fn, int do_close) +{ + Channel *c = channel_by_id(ssh, id); + + if (c == NULL) { + logit_f("%d: bad id", id); + return; + } + c->detach_user = fn; + c->detach_close = do_close; +} + +void +channel_cancel_cleanup(struct ssh *ssh, int id) +{ + Channel *c = channel_by_id(ssh, id); + + if (c == NULL) { + logit_f("%d: bad id", id); + return; + } + c->detach_user = NULL; + c->detach_close = 0; +} + +void +channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, + channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) +{ + Channel *c = channel_lookup(ssh, id); + + if (c == NULL) { + logit_f("%d: bad id", id); + return; + } + c->input_filter = ifn; + c->output_filter = ofn; + c->filter_ctx = ctx; + c->filter_cleanup = cfn; +} + +void +channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, + int extusage, int nonblock, int is_tty, u_int window_max) +{ + Channel *c = channel_lookup(ssh, id); + int r; + + if (c == NULL || c->type != SSH_CHANNEL_LARVAL) + fatal("channel_activate for non-larval channel %d.", id); + if (!c->have_remote_id) + fatal_f("channel %d: no remote id", c->self); + + channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); + c->type = SSH_CHANNEL_OPEN; + c->local_window = c->local_window_max = window_max; + + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i", c->self); +} + +static void +channel_pre_listener(struct ssh *ssh, Channel *c) +{ + c->io_want = SSH_CHAN_IO_SOCK_R; +} + +static void +channel_pre_connecting(struct ssh *ssh, Channel *c) +{ + debug3("channel %d: waiting for connection", c->self); + c->io_want = SSH_CHAN_IO_SOCK_W; +} + +static void +channel_pre_open(struct ssh *ssh, Channel *c) +{ + c->io_want = 0; + if (c->istate == CHAN_INPUT_OPEN && + c->remote_window > 0 && + sshbuf_len(c->input) < c->remote_window && + sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) + c->io_want |= SSH_CHAN_IO_RFD; + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (sshbuf_len(c->output) > 0) { + c->io_want |= SSH_CHAN_IO_WFD; + } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) + debug2("channel %d: " + "obuf_empty delayed efd %d/(%zu)", c->self, + c->efd, sshbuf_len(c->extended)); + else + chan_obuf_empty(ssh, c); + } + } + /** XXX check close conditions, too */ + if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && + c->ostate == CHAN_OUTPUT_CLOSED)) { + if (c->extended_usage == CHAN_EXTENDED_WRITE && + sshbuf_len(c->extended) > 0) + c->io_want |= SSH_CHAN_IO_EFD_W; + else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && + (c->extended_usage == CHAN_EXTENDED_READ || + c->extended_usage == CHAN_EXTENDED_IGNORE) && + sshbuf_len(c->extended) < c->remote_window) + c->io_want |= SSH_CHAN_IO_EFD_R; + } + /* XXX: What about efd? races? */ +} + +/* + * This is a special state for X11 authentication spoofing. An opened X11 + * connection (when authentication spoofing is being done) remains in this + * state until the first packet has been completely read. The authentication + * data in that packet is then substituted by the real data if it matches the + * fake data, and the channel is put into normal mode. + * XXX All this happens at the client side. + * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok + */ +static int +x11_open_helper(struct ssh *ssh, struct sshbuf *b) +{ + struct ssh_channels *sc = ssh->chanctxt; + u_char *ucp; + u_int proto_len, data_len; + + /* Is this being called after the refusal deadline? */ + if (sc->x11_refuse_time != 0 && + (u_int)monotime() >= sc->x11_refuse_time) { + verbose("Rejected X11 connection after ForwardX11Timeout " + "expired"); + return -1; + } + + /* Check if the fixed size part of the packet is in buffer. */ + if (sshbuf_len(b) < 12) + return 0; + + /* Parse the lengths of variable-length fields. */ + ucp = sshbuf_mutable_ptr(b); + if (ucp[0] == 0x42) { /* Byte order MSB first. */ + proto_len = 256 * ucp[6] + ucp[7]; + data_len = 256 * ucp[8] + ucp[9]; + } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ + proto_len = ucp[6] + 256 * ucp[7]; + data_len = ucp[8] + 256 * ucp[9]; + } else { + debug2("Initial X11 packet contains bad byte order byte: 0x%x", + ucp[0]); + return -1; + } + + /* Check if the whole packet is in buffer. */ + if (sshbuf_len(b) < + 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) + return 0; + + /* Check if authentication protocol matches. */ + if (proto_len != strlen(sc->x11_saved_proto) || + memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { + debug2("X11 connection uses different authentication protocol."); + return -1; + } + /* Check if authentication data matches our fake data. */ + if (data_len != sc->x11_fake_data_len || + timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), + sc->x11_fake_data, sc->x11_fake_data_len) != 0) { + debug2("X11 auth data does not match fake data."); + return -1; + } + /* Check fake data length */ + if (sc->x11_fake_data_len != sc->x11_saved_data_len) { + error("X11 fake_data_len %d != saved_data_len %d", + sc->x11_fake_data_len, sc->x11_saved_data_len); + return -1; + } + /* + * Received authentication protocol and data match + * our fake data. Substitute the fake data with real + * data. + */ + memcpy(ucp + 12 + ((proto_len + 3) & ~3), + sc->x11_saved_data, sc->x11_saved_data_len); + return 1; +} + +static void +channel_pre_x11_open(struct ssh *ssh, Channel *c) +{ + int ret = x11_open_helper(ssh, c->output); + + /* c->force_drain = 1; */ + + if (ret == 1) { + c->type = SSH_CHANNEL_OPEN; + channel_pre_open(ssh, c); + } else if (ret == -1) { + logit("X11 connection rejected because of wrong authentication."); + debug2("X11 rejected %d i%d/o%d", + c->self, c->istate, c->ostate); + chan_read_failed(ssh, c); + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); + sshbuf_reset(c->output); + chan_write_failed(ssh, c); + debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); + } +} + +static void +channel_pre_mux_client(struct ssh *ssh, Channel *c) +{ + c->io_want = 0; + if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && + sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) + c->io_want |= SSH_CHAN_IO_RFD; + if (c->istate == CHAN_INPUT_WAIT_DRAIN) { + /* clear buffer immediately (discard any partial packet) */ + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); + /* Start output drain. XXX just kill chan? */ + chan_rcvd_oclose(ssh, c); + } + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (sshbuf_len(c->output) > 0) + c->io_want |= SSH_CHAN_IO_WFD; + else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) + chan_obuf_empty(ssh, c); + } +} + +/* try to decode a socks4 header */ +static int +channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) +{ + const u_char *p; + char *host; + u_int len, have, i, found, need; + char username[256]; + struct { + u_int8_t version; + u_int8_t command; + u_int16_t dest_port; + struct in_addr dest_addr; + } s4_req, s4_rsp; + int r; + + debug2("channel %d: decode socks4", c->self); + + have = sshbuf_len(input); + len = sizeof(s4_req); + if (have < len) + return 0; + p = sshbuf_ptr(input); + + need = 1; + /* SOCKS4A uses an invalid IP address 0.0.0.x */ + if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { + debug2("channel %d: socks4a request", c->self); + /* ... and needs an extra string (the hostname) */ + need = 2; + } + /* Check for terminating NUL on the string(s) */ + for (found = 0, i = len; i < have; i++) { + if (p[i] == '\0') { + found++; + if (found == need) + break; + } + if (i > 1024) { + /* the peer is probably sending garbage */ + debug("channel %d: decode socks4: too long", + c->self); + return -1; + } + } + if (found < need) + return 0; + if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 || + (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || + (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || + (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { + debug_r(r, "channels %d: decode socks4", c->self); + return -1; + } + have = sshbuf_len(input); + p = sshbuf_ptr(input); + if (memchr(p, '\0', have) == NULL) { + error("channel %d: decode socks4: unterminated user", c->self); + return -1; + } + len = strlen(p); + debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); + len++; /* trailing '\0' */ + strlcpy(username, p, sizeof(username)); + if ((r = sshbuf_consume(input, len)) != 0) + fatal_fr(r, "channel %d: consume", c->self); + free(c->path); + c->path = NULL; + if (need == 1) { /* SOCKS4: one string */ + host = inet_ntoa(s4_req.dest_addr); + c->path = xstrdup(host); + } else { /* SOCKS4A: two strings */ + have = sshbuf_len(input); + p = sshbuf_ptr(input); + if (memchr(p, '\0', have) == NULL) { + error("channel %d: decode socks4a: host not nul " + "terminated", c->self); + return -1; + } + len = strlen(p); + debug2("channel %d: decode socks4a: host %s/%d", + c->self, p, len); + len++; /* trailing '\0' */ + if (len > NI_MAXHOST) { + error("channel %d: hostname \"%.100s\" too long", + c->self, p); + return -1; + } + c->path = xstrdup(p); + if ((r = sshbuf_consume(input, len)) != 0) + fatal_fr(r, "channel %d: consume", c->self); + } + c->host_port = ntohs(s4_req.dest_port); + + debug2("channel %d: dynamic request: socks4 host %s port %u command %u", + c->self, c->path, c->host_port, s4_req.command); + + if (s4_req.command != 1) { + debug("channel %d: cannot handle: %s cn %d", + c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); + return -1; + } + s4_rsp.version = 0; /* vn: 0 for reply */ + s4_rsp.command = 90; /* cd: req granted */ + s4_rsp.dest_port = 0; /* ignored */ + s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ + if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) + fatal_fr(r, "channel %d: append reply", c->self); + return 1; +} + +/* try to decode a socks5 header */ +#define SSH_SOCKS5_AUTHDONE 0x1000 +#define SSH_SOCKS5_NOAUTH 0x00 +#define SSH_SOCKS5_IPV4 0x01 +#define SSH_SOCKS5_DOMAIN 0x03 +#define SSH_SOCKS5_IPV6 0x04 +#define SSH_SOCKS5_CONNECT 0x01 +#define SSH_SOCKS5_SUCCESS 0x00 + +static int +channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output) +{ + /* XXX use get/put_u8 instead of trusting struct padding */ + struct { + u_int8_t version; + u_int8_t command; + u_int8_t reserved; + u_int8_t atyp; + } s5_req, s5_rsp; + u_int16_t dest_port; + char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; + const u_char *p; + u_int have, need, i, found, nmethods, addrlen, af; + int r; + + debug2("channel %d: decode socks5", c->self); + p = sshbuf_ptr(input); + if (p[0] != 0x05) + return -1; + have = sshbuf_len(input); + if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { + /* format: ver | nmethods | methods */ + if (have < 2) + return 0; + nmethods = p[1]; + if (have < nmethods + 2) + return 0; + /* look for method: "NO AUTHENTICATION REQUIRED" */ + for (found = 0, i = 2; i < nmethods + 2; i++) { + if (p[i] == SSH_SOCKS5_NOAUTH) { + found = 1; + break; + } + } + if (!found) { + debug("channel %d: method SSH_SOCKS5_NOAUTH not found", + c->self); + return -1; + } + if ((r = sshbuf_consume(input, nmethods + 2)) != 0) + fatal_fr(r, "channel %d: consume", c->self); + /* version, method */ + if ((r = sshbuf_put_u8(output, 0x05)) != 0 || + (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) + fatal_fr(r, "channel %d: append reply", c->self); + c->flags |= SSH_SOCKS5_AUTHDONE; + debug2("channel %d: socks5 auth done", c->self); + return 0; /* need more */ + } + debug2("channel %d: socks5 post auth", c->self); + if (have < sizeof(s5_req)+1) + return 0; /* need more */ + memcpy(&s5_req, p, sizeof(s5_req)); + if (s5_req.version != 0x05 || + s5_req.command != SSH_SOCKS5_CONNECT || + s5_req.reserved != 0x00) { + debug2("channel %d: only socks5 connect supported", c->self); + return -1; + } + switch (s5_req.atyp){ + case SSH_SOCKS5_IPV4: + addrlen = 4; + af = AF_INET; + break; + case SSH_SOCKS5_DOMAIN: + addrlen = p[sizeof(s5_req)]; + af = -1; + break; + case SSH_SOCKS5_IPV6: + addrlen = 16; + af = AF_INET6; + break; + default: + debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); + return -1; + } + need = sizeof(s5_req) + addrlen + 2; + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) + need++; + if (have < need) + return 0; + if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) + fatal_fr(r, "channel %d: consume", c->self); + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { + /* host string length */ + if ((r = sshbuf_consume(input, 1)) != 0) + fatal_fr(r, "channel %d: consume", c->self); + } + if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || + (r = sshbuf_get(input, &dest_port, 2)) != 0) { + debug_r(r, "channel %d: parse addr/port", c->self); + return -1; + } + dest_addr[addrlen] = '\0'; + free(c->path); + c->path = NULL; + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { + if (addrlen >= NI_MAXHOST) { + error("channel %d: dynamic request: socks5 hostname " + "\"%.100s\" too long", c->self, dest_addr); + return -1; + } + c->path = xstrdup(dest_addr); + } else { + if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) + return -1; + c->path = xstrdup(ntop); + } + c->host_port = ntohs(dest_port); + + debug2("channel %d: dynamic request: socks5 host %s port %u command %u", + c->self, c->path, c->host_port, s5_req.command); + + s5_rsp.version = 0x05; + s5_rsp.command = SSH_SOCKS5_SUCCESS; + s5_rsp.reserved = 0; /* ignored */ + s5_rsp.atyp = SSH_SOCKS5_IPV4; + dest_port = 0; /* ignored */ + + if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 || + (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || + (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0) + fatal_fr(r, "channel %d: append reply", c->self); + return 1; +} + +Channel * +channel_connect_stdio_fwd(struct ssh *ssh, + const char *host_to_connect, u_short port_to_connect, + int in, int out, int nonblock) +{ + Channel *c; + + debug_f("%s:%d", host_to_connect, port_to_connect); + + c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, + -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "stdio-forward", nonblock); + + c->path = xstrdup(host_to_connect); + c->host_port = port_to_connect; + c->listening_port = 0; + c->force_drain = 1; + + channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); + port_open_helper(ssh, c, "direct-tcpip"); + + return c; +} + +/* dynamic port forwarding */ +static void +channel_pre_dynamic(struct ssh *ssh, Channel *c) +{ + const u_char *p; + u_int have; + int ret; + + c->io_want = 0; + have = sshbuf_len(c->input); + debug2("channel %d: pre_dynamic: have %d", c->self, have); + /* sshbuf_dump(c->input, stderr); */ + /* check if the fixed size part of the packet is in buffer. */ + if (have < 3) { + /* need more */ + c->io_want |= SSH_CHAN_IO_RFD; + return; + } + /* try to guess the protocol */ + p = sshbuf_ptr(c->input); + /* XXX sshbuf_peek_u8? */ + switch (p[0]) { + case 0x04: + ret = channel_decode_socks4(c, c->input, c->output); + break; + case 0x05: + ret = channel_decode_socks5(c, c->input, c->output); + break; + default: + ret = -1; + break; + } + if (ret < 0) { + chan_mark_dead(ssh, c); + } else if (ret == 0) { + debug2("channel %d: pre_dynamic: need more", c->self); + /* need more */ + c->io_want |= SSH_CHAN_IO_RFD; + if (sshbuf_len(c->output)) + c->io_want |= SSH_CHAN_IO_WFD; + } else { + /* switch to the next state */ + c->type = SSH_CHANNEL_OPENING; + port_open_helper(ssh, c, "direct-tcpip"); + } +} + +/* simulate read-error */ +static void +rdynamic_close(struct ssh *ssh, Channel *c) +{ + c->type = SSH_CHANNEL_OPEN; + chan_read_failed(ssh, c); + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); + sshbuf_reset(c->output); + chan_write_failed(ssh, c); +} + +/* reverse dynamic port forwarding */ +static void +channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c) +{ + const u_char *p; + u_int have, len; + int r, ret; + + have = sshbuf_len(c->output); + debug2("channel %d: pre_rdynamic: have %d", c->self, have); + /* sshbuf_dump(c->output, stderr); */ + /* EOF received */ + if (c->flags & CHAN_EOF_RCVD) { + if ((r = sshbuf_consume(c->output, have)) != 0) + fatal_fr(r, "channel %d: consume", c->self); + rdynamic_close(ssh, c); + return; + } + /* check if the fixed size part of the packet is in buffer. */ + if (have < 3) + return; + /* try to guess the protocol */ + p = sshbuf_ptr(c->output); + switch (p[0]) { + case 0x04: + /* switch input/output for reverse forwarding */ + ret = channel_decode_socks4(c, c->output, c->input); + break; + case 0x05: + ret = channel_decode_socks5(c, c->output, c->input); + break; + default: + ret = -1; + break; + } + if (ret < 0) { + rdynamic_close(ssh, c); + } else if (ret == 0) { + debug2("channel %d: pre_rdynamic: need more", c->self); + /* send socks request to peer */ + len = sshbuf_len(c->input); + if (len > 0 && len < c->remote_window) { + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_stringb(ssh, c->input)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal_fr(r, "channel %i: rdynamic", c->self); + } + if ((r = sshbuf_consume(c->input, len)) != 0) + fatal_fr(r, "channel %d: consume", c->self); + c->remote_window -= len; + } + } else if (rdynamic_connect_finish(ssh, c) < 0) { + /* the connect failed */ + rdynamic_close(ssh, c); + } +} + +/* This is our fake X11 server socket. */ +static void +channel_post_x11_listener(struct ssh *ssh, Channel *c) +{ + Channel *nc; + struct sockaddr_storage addr; + int r, newsock, oerrno, remote_port; + socklen_t addrlen; + char buf[16384], *remote_ipaddr; + + if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) + return; + + debug("X11 connection requested."); + addrlen = sizeof(addr); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); + if (c->single_connection) { + oerrno = errno; + debug2("single_connection: closing X11 listener."); + channel_close_fd(ssh, c, &c->sock); + chan_mark_dead(ssh, c); + errno = oerrno; + } + if (newsock == -1) { + if (errno != EINTR && errno != EWOULDBLOCK && + errno != ECONNABORTED) + error("accept: %.100s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; + } + set_nodelay(newsock); + remote_ipaddr = get_peer_ipaddr(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", + remote_ipaddr, remote_port); + + nc = channel_new(ssh, "accepted x11 socket", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, 0, buf, 1); + open_preamble(ssh, __func__, nc, "x11"); + if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || + (r = sshpkt_put_u32(ssh, remote_port)) != 0) { + fatal_fr(r, "channel %i: reply", c->self); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: send", c->self); + free(remote_ipaddr); +} + +static void +port_open_helper(struct ssh *ssh, Channel *c, char *rtype) +{ + char *local_ipaddr = get_local_ipaddr(c->sock); + int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); + char *remote_ipaddr = get_peer_ipaddr(c->sock); + int remote_port = get_peer_port(c->sock); + int r; + + if (remote_port == -1) { + /* Fake addr/port to appease peers that validate it (Tectia) */ + free(remote_ipaddr); + remote_ipaddr = xstrdup("127.0.0.1"); + remote_port = 65535; + } + + free(c->remote_name); + xasprintf(&c->remote_name, + "%s: listening port %d for %.100s port %d, " + "connect from %.200s port %d to %.100s port %d", + rtype, c->listening_port, c->path, c->host_port, + remote_ipaddr, remote_port, local_ipaddr, local_port); + + open_preamble(ssh, __func__, c, rtype); + if (strcmp(rtype, "direct-tcpip") == 0) { + /* target host, port */ + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || + (r = sshpkt_put_u32(ssh, c->host_port)) != 0) + fatal_fr(r, "channel %i: reply", c->self); + } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { + /* target path */ + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) + fatal_fr(r, "channel %i: reply", c->self); + } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* listen path */ + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) + fatal_fr(r, "channel %i: reply", c->self); + } else { + /* listen address, port */ + if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || + (r = sshpkt_put_u32(ssh, local_port)) != 0) + fatal_fr(r, "channel %i: reply", c->self); + } + if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { + /* reserved for future owner/mode info */ + if ((r = sshpkt_put_cstring(ssh, "")) != 0) + fatal_fr(r, "channel %i: reply", c->self); + } else { + /* originator host and port */ + if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) + fatal_fr(r, "channel %i: reply", c->self); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: send", c->self); + free(remote_ipaddr); + free(local_ipaddr); +} + +void +channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time) +{ + ssh->chanctxt->x11_refuse_time = refuse_time; +} + +/* + * This socket is listening for connections to a forwarded TCP/IP port. + */ +static void +channel_post_port_listener(struct ssh *ssh, Channel *c) +{ + Channel *nc; + struct sockaddr_storage addr; + int newsock, nextstate; + socklen_t addrlen; + char *rtype; + + if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) + return; + + debug("Connection to port %d forwarding to %.100s port %d requested.", + c->listening_port, c->path, c->host_port); + + if (c->type == SSH_CHANNEL_RPORT_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-tcpip"; + } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "forwarded-streamlocal@openssh.com"; + } else if (c->host_port == PORT_STREAMLOCAL) { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-streamlocal@openssh.com"; + } else if (c->host_port == 0) { + nextstate = SSH_CHANNEL_DYNAMIC; + rtype = "dynamic-tcpip"; + } else { + nextstate = SSH_CHANNEL_OPENING; + rtype = "direct-tcpip"; + } + + addrlen = sizeof(addr); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); + if (newsock == -1) { + if (errno != EINTR && errno != EWOULDBLOCK && + errno != ECONNABORTED) + error("accept: %.100s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; + } + if (c->host_port != PORT_STREAMLOCAL) + set_nodelay(newsock); + nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, 0, rtype, 1); + nc->listening_port = c->listening_port; + nc->host_port = c->host_port; + if (c->path != NULL) + nc->path = xstrdup(c->path); + + if (nextstate != SSH_CHANNEL_DYNAMIC) + port_open_helper(ssh, nc, rtype); +} + +/* + * This is the authentication agent socket listening for connections from + * clients. + */ +static void +channel_post_auth_listener(struct ssh *ssh, Channel *c) +{ + Channel *nc; + int r, newsock; + struct sockaddr_storage addr; + socklen_t addrlen; + + if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) + return; + + addrlen = sizeof(addr); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); + if (newsock == -1) { + error("accept from auth socket: %.100s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; + } + nc = channel_new(ssh, "accepted auth socket", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, "accepted auth socket", 1); + open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i", c->self); +} + +static void +channel_post_connecting(struct ssh *ssh, Channel *c) +{ + int err = 0, sock, isopen, r; + socklen_t sz = sizeof(err); + + if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0) + return; + if (!c->have_remote_id) + fatal_f("channel %d: no remote id", c->self); + /* for rdynamic the OPEN_CONFIRMATION has been sent already */ + isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); + if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) { + err = errno; + error("getsockopt SO_ERROR failed"); + } + if (err == 0) { + debug("channel %d: connected to %s port %d", + c->self, c->connect_ctx.host, c->connect_ctx.port); + channel_connect_ctx_free(&c->connect_ctx); + c->type = SSH_CHANNEL_OPEN; + if (isopen) { + /* no message necessary */ + } else { + if ((r = sshpkt_start(ssh, + SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i open confirm", c->self); + } + } else { + debug("channel %d: connection failed: %s", + c->self, strerror(err)); + /* Try next address, if any */ + if ((sock = connect_next(&c->connect_ctx)) > 0) { + close(c->sock); + c->sock = c->rfd = c->wfd = sock; + return; + } + /* Exhausted all addresses */ + error("connect_to %.100s port %d: failed.", + c->connect_ctx.host, c->connect_ctx.port); + channel_connect_ctx_free(&c->connect_ctx); + if (isopen) { + rdynamic_close(ssh, c); + } else { + if ((r = sshpkt_start(ssh, + SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, + SSH2_OPEN_CONNECT_FAILED)) != 0 || + (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: failure", c->self); + chan_mark_dead(ssh, c); + } + } +} + +static int +channel_handle_rfd(struct ssh *ssh, Channel *c) +{ + char buf[CHAN_RBUF]; + ssize_t len; + int r, force; + size_t have, avail, maxlen = CHANNEL_MAX_READ; + int pty_zeroread = 0; + +#ifdef PTY_ZEROREAD + /* Bug on AIX: read(1) can return 0 for a non-closed fd */ + pty_zeroread = c->isatty; +#endif + + force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; + + if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0) + return 1; + if ((avail = sshbuf_avail(c->input)) == 0) + return 1; /* Shouldn't happen */ + + /* + * For "simple" channels (i.e. not datagram or filtered), we can + * read directly to the channel buffer. + */ + if (!pty_zeroread && c->input_filter == NULL && !c->datagram) { + /* Only OPEN channels have valid rwin */ + if (c->type == SSH_CHANNEL_OPEN) { + if ((have = sshbuf_len(c->input)) >= c->remote_window) + return 1; /* shouldn't happen */ + if (maxlen > c->remote_window - have) + maxlen = c->remote_window - have; + } + if (maxlen > avail) + maxlen = avail; + if ((r = sshbuf_read(c->rfd, c->input, maxlen, NULL)) != 0) { + if (errno == EINTR || (!force && + (errno == EAGAIN || errno == EWOULDBLOCK))) + return 1; + debug2("channel %d: read failed rfd %d maxlen %zu: %s", + c->self, c->rfd, maxlen, ssh_err(r)); + goto rfail; + } + return 1; + } + + errno = 0; + len = read(c->rfd, buf, sizeof(buf)); + /* fixup AIX zero-length read with errno set to look more like errors */ + if (pty_zeroread && len == 0 && errno != 0) + len = -1; + if (len == -1 && (errno == EINTR || + ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) + return 1; + if (len < 0 || (!pty_zeroread && len == 0)) { + debug2("channel %d: read<=0 rfd %d len %zd: %s", + c->self, c->rfd, len, + len == 0 ? "closed" : strerror(errno)); + rfail: + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(ssh, c); + return -1; + } else { + chan_read_failed(ssh, c); + } + return -1; + } + if (c->input_filter != NULL) { + if (c->input_filter(ssh, c, buf, len) == -1) { + debug2("channel %d: filter stops", c->self); + chan_read_failed(ssh, c); + } + } else if (c->datagram) { + if ((r = sshbuf_put_string(c->input, buf, len)) != 0) + fatal_fr(r, "channel %i: put datagram", c->self); + } else if ((r = sshbuf_put(c->input, buf, len)) != 0) + fatal_fr(r, "channel %i: put data", c->self); + + return 1; +} + +static int +channel_handle_wfd(struct ssh *ssh, Channel *c) +{ + struct termios tio; + u_char *data = NULL, *buf; /* XXX const; need filter API change */ + size_t dlen, olen = 0; + int r, len; + + if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) + return 1; + if (sshbuf_len(c->output) == 0) + return 1; + + /* Send buffered output data to the socket. */ + olen = sshbuf_len(c->output); + if (c->output_filter != NULL) { + if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { + debug2("channel %d: filter stops", c->self); + if (c->type != SSH_CHANNEL_OPEN) + chan_mark_dead(ssh, c); + else + chan_write_failed(ssh, c); + return -1; + } + } else if (c->datagram) { + if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) + fatal_fr(r, "channel %i: get datagram", c->self); + buf = data; + } else { + buf = data = sshbuf_mutable_ptr(c->output); + dlen = sshbuf_len(c->output); + } + + if (c->datagram) { + /* ignore truncated writes, datagrams might get lost */ + len = write(c->wfd, buf, dlen); + free(data); + if (len == -1 && (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK)) + return 1; + if (len <= 0) + goto write_fail; + goto out; + } + +#ifdef _AIX + /* XXX: Later AIX versions can't push as much data to tty */ + if (c->wfd_isatty) + dlen = MINIMUM(dlen, 8*1024); +#endif + + len = write(c->wfd, buf, dlen); + if (len == -1 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) + return 1; + if (len <= 0) { + write_fail: + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(ssh, c); + return -1; + } else { + chan_write_failed(ssh, c); + } + return -1; + } +#ifndef BROKEN_TCGETATTR_ICANON + if (c->isatty && dlen >= 1 && buf[0] != '\r') { + if (tcgetattr(c->wfd, &tio) == 0 && + !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { + /* + * Simulate echo to reduce the impact of + * traffic analysis. We need to match the + * size of a SSH2_MSG_CHANNEL_DATA message + * (4 byte channel id + buf) + */ + if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: ignore", c->self); + } + } +#endif /* BROKEN_TCGETATTR_ICANON */ + if ((r = sshbuf_consume(c->output, len)) != 0) + fatal_fr(r, "channel %i: consume", c->self); + out: + c->local_consumed += olen - sshbuf_len(c->output); + + return 1; +} + +static int +channel_handle_efd_write(struct ssh *ssh, Channel *c) +{ + int r; + ssize_t len; + + if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0) + return 1; + if (sshbuf_len(c->extended) == 0) + return 1; + + len = write(c->efd, sshbuf_ptr(c->extended), + sshbuf_len(c->extended)); + debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); + if (len == -1 && (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK)) + return 1; + if (len <= 0) { + debug2("channel %d: closing write-efd %d", c->self, c->efd); + channel_close_fd(ssh, c, &c->efd); + } else { + if ((r = sshbuf_consume(c->extended, len)) != 0) + fatal_fr(r, "channel %i: consume", c->self); + c->local_consumed += len; + } + return 1; +} + +static int +channel_handle_efd_read(struct ssh *ssh, Channel *c) +{ + char buf[CHAN_RBUF]; + ssize_t len; + int r, force; + + force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; + + if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0) + return 1; + + len = read(c->efd, buf, sizeof(buf)); + debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); + if (len == -1 && (errno == EINTR || ((errno == EAGAIN || + errno == EWOULDBLOCK) && !force))) + return 1; + if (len <= 0) { + debug2("channel %d: closing read-efd %d", c->self, c->efd); + channel_close_fd(ssh, c, &c->efd); + } else if (c->extended_usage == CHAN_EXTENDED_IGNORE) + debug3("channel %d: discard efd", c->self); + else if ((r = sshbuf_put(c->extended, buf, len)) != 0) + fatal_fr(r, "channel %i: append", c->self); + return 1; +} + +static int +channel_handle_efd(struct ssh *ssh, Channel *c) +{ + if (c->efd == -1) + return 1; + + /** XXX handle drain efd, too */ + + if (c->extended_usage == CHAN_EXTENDED_WRITE) + return channel_handle_efd_write(ssh, c); + else if (c->extended_usage == CHAN_EXTENDED_READ || + c->extended_usage == CHAN_EXTENDED_IGNORE) + return channel_handle_efd_read(ssh, c); + + return 1; +} + +static int +channel_check_window(struct ssh *ssh, Channel *c) +{ + int r; + + if (c->type == SSH_CHANNEL_OPEN && + !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && + ((c->local_window_max - c->local_window > + c->local_maxpacket*3) || + c->local_window < c->local_window_max/2) && + c->local_consumed > 0) { + if (!c->have_remote_id) + fatal_f("channel %d: no remote id", c->self); + if ((r = sshpkt_start(ssh, + SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal_fr(r, "channel %i", c->self); + } + debug2("channel %d: window %d sent adjust %d", c->self, + c->local_window, c->local_consumed); + c->local_window += c->local_consumed; + c->local_consumed = 0; + } + return 1; +} + +static void +channel_post_open(struct ssh *ssh, Channel *c) +{ + channel_handle_rfd(ssh, c); + channel_handle_wfd(ssh, c); + channel_handle_efd(ssh, c); + channel_check_window(ssh, c); +} + +static u_int +read_mux(struct ssh *ssh, Channel *c, u_int need) +{ + char buf[CHAN_RBUF]; + ssize_t len; + u_int rlen; + int r; + + if (sshbuf_len(c->input) < need) { + rlen = need - sshbuf_len(c->input); + len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); + if (len == -1 && (errno == EINTR || errno == EAGAIN)) + return sshbuf_len(c->input); + if (len <= 0) { + debug2("channel %d: ctl read<=0 rfd %d len %zd", + c->self, c->rfd, len); + chan_read_failed(ssh, c); + return 0; + } else if ((r = sshbuf_put(c->input, buf, len)) != 0) + fatal_fr(r, "channel %i: append", c->self); + } + return sshbuf_len(c->input); +} + +static void +channel_post_mux_client_read(struct ssh *ssh, Channel *c) +{ + u_int need; + + if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) + return; + if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) + return; + if (c->mux_pause) + return; + + /* + * Don't not read past the precise end of packets to + * avoid disrupting fd passing. + */ + if (read_mux(ssh, c, 4) < 4) /* read header */ + return; + /* XXX sshbuf_peek_u32 */ + need = PEEK_U32(sshbuf_ptr(c->input)); +#define CHANNEL_MUX_MAX_PACKET (256 * 1024) + if (need > CHANNEL_MUX_MAX_PACKET) { + debug2("channel %d: packet too big %u > %u", + c->self, CHANNEL_MUX_MAX_PACKET, need); + chan_rcvd_oclose(ssh, c); + return; + } + if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ + return; + if (c->mux_rcb(ssh, c) != 0) { + debug("channel %d: mux_rcb failed", c->self); + chan_mark_dead(ssh, c); + return; + } +} + +static void +channel_post_mux_client_write(struct ssh *ssh, Channel *c) +{ + ssize_t len; + int r; + + if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) + return; + if (sshbuf_len(c->output) == 0) + return; + + len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); + if (len == -1 && (errno == EINTR || errno == EAGAIN)) + return; + if (len <= 0) { + chan_mark_dead(ssh, c); + return; + } + if ((r = sshbuf_consume(c->output, len)) != 0) + fatal_fr(r, "channel %i: consume", c->self); +} + +static void +channel_post_mux_client(struct ssh *ssh, Channel *c) +{ + channel_post_mux_client_read(ssh, c); + channel_post_mux_client_write(ssh, c); +} + +static void +channel_post_mux_listener(struct ssh *ssh, Channel *c) +{ + Channel *nc; + struct sockaddr_storage addr; + socklen_t addrlen; + int newsock; + uid_t euid; + gid_t egid; + + if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) + return; + + debug("multiplexing control connection"); + + /* + * Accept connection on control socket + */ + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + if ((newsock = accept(c->sock, (struct sockaddr*)&addr, + &addrlen)) == -1) { + error_f("accept: %s", strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + c->notbefore = monotime() + 1; + return; + } + + if (getpeereid(newsock, &euid, &egid) == -1) { + error_f("getpeereid failed: %s", strerror(errno)); + close(newsock); + return; + } + if ((euid != 0) && (getuid() != euid)) { + error("multiplex uid mismatch: peer euid %u != uid %u", + (u_int)euid, (u_int)getuid()); + close(newsock); + return; + } + nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT, + newsock, newsock, -1, c->local_window_max, + c->local_maxpacket, 0, "mux-control", 1); + nc->mux_rcb = c->mux_rcb; + debug3_f("new mux channel %d fd %d", nc->self, nc->sock); + /* establish state */ + nc->mux_rcb(ssh, nc); + /* mux state transitions must not elicit protocol messages */ + nc->flags |= CHAN_LOCAL; +} + +static void +channel_handler_init(struct ssh_channels *sc) +{ + chan_fn **pre, **post; + + if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || + (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) + fatal_f("allocation failed"); + + pre[SSH_CHANNEL_OPEN] = &channel_pre_open; + pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; + pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; + pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; + pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting; + pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; + pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; + + post[SSH_CHANNEL_OPEN] = &channel_post_open; + post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; + post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; + post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; + post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting; + post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; + post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; + + sc->channel_pre = pre; + sc->channel_post = post; +} + +/* gc dead channels */ +static void +channel_garbage_collect(struct ssh *ssh, Channel *c) +{ + if (c == NULL) + return; + if (c->detach_user != NULL) { + if (!chan_is_dead(ssh, c, c->detach_close)) + return; + + debug2("channel %d: gc: notify user", c->self); + c->detach_user(ssh, c->self, NULL); + /* if we still have a callback */ + if (c->detach_user != NULL) + return; + debug2("channel %d: gc: user detached", c->self); + } + if (!chan_is_dead(ssh, c, 1)) + return; + debug2("channel %d: garbage collecting", c->self); + channel_free(ssh, c); +} + +enum channel_table { CHAN_PRE, CHAN_POST }; + +static void +channel_handler(struct ssh *ssh, int table, time_t *unpause_secs) +{ + struct ssh_channels *sc = ssh->chanctxt; + chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; + u_int i, oalloc; + Channel *c; + time_t now; + + now = monotime(); + if (unpause_secs != NULL) + *unpause_secs = 0; + for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { + c = sc->channels[i]; + if (c == NULL) + continue; + if (c->delayed) { + if (table == CHAN_PRE) + c->delayed = 0; + else + continue; + } + if (ftab[c->type] != NULL) { + /* + * Run handlers that are not paused. + */ + if (c->notbefore <= now) + (*ftab[c->type])(ssh, c); + else if (unpause_secs != NULL) { + /* + * Collect the time that the earliest + * channel comes off pause. + */ + debug3_f("chan %d: skip for %d more " + "seconds", c->self, + (int)(c->notbefore - now)); + if (*unpause_secs == 0 || + (c->notbefore - now) < *unpause_secs) + *unpause_secs = c->notbefore - now; + } + } + channel_garbage_collect(ssh, c); + } + if (unpause_secs != NULL && *unpause_secs != 0) + debug3_f("first channel unpauses in %d seconds", + (int)*unpause_secs); +} + +/* + * Create sockets before preparing IO. + * This is necessary for things that need to happen after reading + * the network-input but need to be completed before IO event setup, e.g. + * because they may create new channels. + */ +static void +channel_before_prepare_io(struct ssh *ssh) +{ + struct ssh_channels *sc = ssh->chanctxt; + Channel *c; + u_int i, oalloc; + + for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { + c = sc->channels[i]; + if (c == NULL) + continue; + if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) + channel_before_prepare_io_rdynamic(ssh, c); + } +} + +static void +dump_channel_poll(const char *func, const char *what, Channel *c, + u_int pollfd_offset, struct pollfd *pfd) +{ +#ifdef DEBUG_CHANNEL_POLL + debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] " + "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d " + "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what, + c->rfd, c->wfd, c->efd, c->sock, + c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3], + c->io_want, c->io_ready, + pollfd_offset, pfd->fd, pfd->events, pfd->revents); +#endif +} + +/* Prepare pollfd entries for a single channel */ +static void +channel_prepare_pollfd(Channel *c, u_int *next_pollfd, + struct pollfd *pfd, u_int npfd) +{ + u_int ev, p = *next_pollfd; + + if (c == NULL) + return; + if (p + 4 > npfd) { + /* Shouldn't happen */ + fatal_f("channel %d: bad pfd offset %u (max %u)", + c->self, p, npfd); + } + c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1; + /* + * prepare c->rfd + * + * This is a special case, since c->rfd might be the same as + * c->wfd, c->efd and/or c->sock. Handle those here if they want + * IO too. + */ + if (c->rfd != -1) { + ev = 0; + if ((c->io_want & SSH_CHAN_IO_RFD) != 0) + ev |= POLLIN; + /* rfd == wfd */ + if (c->wfd == c->rfd) { + if ((c->io_want & SSH_CHAN_IO_WFD) != 0) + ev |= POLLOUT; + } + /* rfd == efd */ + if (c->efd == c->rfd) { + if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) + ev |= POLLIN; + if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) + ev |= POLLOUT; + } + /* rfd == sock */ + if (c->sock == c->rfd) { + if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) + ev |= POLLIN; + if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) + ev |= POLLOUT; + } + /* Pack a pfd entry if any event armed for this fd */ + if (ev != 0) { + c->pfds[0] = p; + pfd[p].fd = c->rfd; + pfd[p].events = ev; + dump_channel_poll(__func__, "rfd", c, p, &pfd[p]); + p++; + } + } + /* prepare c->wfd if wanting IO and not already handled above */ + if (c->wfd != -1 && c->rfd != c->wfd) { + ev = 0; + if ((c->io_want & SSH_CHAN_IO_WFD)) + ev |= POLLOUT; + /* Pack a pfd entry if any event armed for this fd */ + if (ev != 0) { + c->pfds[1] = p; + pfd[p].fd = c->wfd; + pfd[p].events = ev; + dump_channel_poll(__func__, "wfd", c, p, &pfd[p]); + p++; + } + } + /* prepare c->efd if wanting IO and not already handled above */ + if (c->efd != -1 && c->rfd != c->efd) { + ev = 0; + if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) + ev |= POLLIN; + if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) + ev |= POLLOUT; + /* Pack a pfd entry if any event armed for this fd */ + if (ev != 0) { + c->pfds[2] = p; + pfd[p].fd = c->efd; + pfd[p].events = ev; + dump_channel_poll(__func__, "efd", c, p, &pfd[p]); + p++; + } + } + /* prepare c->sock if wanting IO and not already handled above */ + if (c->sock != -1 && c->rfd != c->sock) { + ev = 0; + if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) + ev |= POLLIN; + if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) + ev |= POLLOUT; + /* Pack a pfd entry if any event armed for this fd */ + if (ev != 0) { + c->pfds[3] = p; + pfd[p].fd = c->sock; + pfd[p].events = 0; + dump_channel_poll(__func__, "sock", c, p, &pfd[p]); + p++; + } + } + *next_pollfd = p; +} + +/* * Allocate/prepare poll structure */ +void +channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp, + u_int *npfd_activep, u_int npfd_reserved, time_t *minwait_secs) +{ + struct ssh_channels *sc = ssh->chanctxt; + u_int i, oalloc, p, npfd = npfd_reserved; + + channel_before_prepare_io(ssh); /* might create a new channel */ + + /* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */ + if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved) + fatal_f("too many channels"); /* shouldn't happen */ + if (!ssh_packet_is_rekeying(ssh)) + npfd += sc->channels_alloc * 4; + if (npfd > *npfd_allocp) { + *pfdp = xrecallocarray(*pfdp, *npfd_allocp, + npfd, sizeof(**pfdp)); + *npfd_allocp = npfd; + } + *npfd_activep = npfd_reserved; + if (ssh_packet_is_rekeying(ssh)) + return; + + oalloc = sc->channels_alloc; + + channel_handler(ssh, CHAN_PRE, minwait_secs); + + if (oalloc != sc->channels_alloc) { + /* shouldn't happen */ + fatal_f("channels_alloc changed during CHAN_PRE " + "(was %u, now %u)", oalloc, sc->channels_alloc); + } + + /* Prepare pollfd */ + p = npfd_reserved; + for (i = 0; i < sc->channels_alloc; i++) + channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd); + *npfd_activep = p; +} + +static void +fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd, + const char *what, u_int revents_mask, u_int ready) +{ + struct pollfd *pfd = &pfds[p]; + + if (fd == -1) + return; + if (p == -1 || (u_int)p >= npfd) + fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd); + dump_channel_poll(__func__, what, c, p, pfd); + if (pfd->fd != fd) { + fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d " + "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd, + c->rfd, c->wfd, c->efd, c->sock); + } + if ((pfd->revents & POLLNVAL) != 0) { + fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d", + c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); + } + if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0) + c->io_ready |= ready & c->io_want; +} + +/* + * After poll, perform any appropriate operations for channels which have + * events pending. + */ +void +channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd) +{ + struct ssh_channels *sc = ssh->chanctxt; + u_int i; + int p; + Channel *c; + +#ifdef DEBUG_CHANNEL_POLL + for (p = 0; p < (int)npfd; p++) { + if (pfd[p].revents == 0) + continue; + debug_f("pfd[%u].fd %d rev 0x%04x", + p, pfd[p].fd, pfd[p].revents); + } +#endif + + /* Convert pollfd into c->io_ready */ + for (i = 0; i < sc->channels_alloc; i++) { + c = sc->channels[i]; + if (c == NULL) + continue; + /* if rfd is shared with efd/sock then wfd should be too */ + if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd && + (c->rfd == c->efd || c->rfd == c->sock)) { + /* Shouldn't happen */ + fatal_f("channel %d: unexpected fds r%d w%d e%d s%d", + c->self, c->rfd, c->wfd, c->efd, c->sock); + } + c->io_ready = 0; + /* rfd, potentially shared with wfd, efd and sock */ + if (c->rfd != -1 && (p = c->pfds[0]) != -1) { + fd_ready(c, p, pfd, npfd, c->rfd, + "rfd", POLLIN, SSH_CHAN_IO_RFD); + if (c->rfd == c->wfd) { + fd_ready(c, p, pfd, npfd, c->wfd, + "wfd/r", POLLOUT, SSH_CHAN_IO_WFD); + } + if (c->rfd == c->efd) { + fd_ready(c, p, pfd, npfd, c->efd, + "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R); + fd_ready(c, p, pfd, npfd, c->efd, + "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W); + } + if (c->rfd == c->sock) { + fd_ready(c, p, pfd, npfd, c->sock, + "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R); + fd_ready(c, p, pfd, npfd, c->sock, + "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W); + } + dump_channel_poll(__func__, "rfd", c, p, pfd); + } + /* wfd */ + if (c->wfd != -1 && c->wfd != c->rfd && + (p = c->pfds[1]) != -1) { + fd_ready(c, p, pfd, npfd, c->wfd, + "wfd", POLLOUT, SSH_CHAN_IO_WFD); + dump_channel_poll(__func__, "wfd", c, p, pfd); + } + /* efd */ + if (c->efd != -1 && c->efd != c->rfd && + (p = c->pfds[2]) != -1) { + fd_ready(c, p, pfd, npfd, c->efd, + "efdr", POLLIN, SSH_CHAN_IO_EFD_R); + fd_ready(c, p, pfd, npfd, c->efd, + "efdw", POLLOUT, SSH_CHAN_IO_EFD_W); + dump_channel_poll(__func__, "efd", c, p, pfd); + } + /* sock */ + if (c->sock != -1 && c->sock != c->rfd && + (p = c->pfds[3]) != -1) { + fd_ready(c, p, pfd, npfd, c->sock, + "sockr", POLLIN, SSH_CHAN_IO_SOCK_R); + fd_ready(c, p, pfd, npfd, c->sock, + "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W); + dump_channel_poll(__func__, "sock", c, p, pfd); + } + } + channel_handler(ssh, CHAN_POST, NULL); +} + +/* + * Enqueue data for channels with open or draining c->input. + */ +static void +channel_output_poll_input_open(struct ssh *ssh, Channel *c) +{ + size_t len, plen; + const u_char *pkt; + int r; + + if ((len = sshbuf_len(c->input)) == 0) { + if (c->istate == CHAN_INPUT_WAIT_DRAIN) { + /* + * input-buffer is empty and read-socket shutdown: + * tell peer, that we will not send more data: + * send IEOF. + * hack for extended data: delay EOF if EFD still + * in use. + */ + if (CHANNEL_EFD_INPUT_ACTIVE(c)) + debug2("channel %d: " + "ibuf_empty delayed efd %d/(%zu)", + c->self, c->efd, sshbuf_len(c->extended)); + else + chan_ibuf_empty(ssh, c); + } + return; + } + + if (!c->have_remote_id) + fatal_f("channel %d: no remote id", c->self); + + if (c->datagram) { + /* Check datagram will fit; drop if not */ + if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) + fatal_fr(r, "channel %i: get datagram", c->self); + /* + * XXX this does tail-drop on the datagram queue which is + * usually suboptimal compared to head-drop. Better to have + * backpressure at read time? (i.e. read + discard) + */ + if (plen > c->remote_window || plen > c->remote_maxpacket) { + debug("channel %d: datagram too big", c->self); + return; + } + /* Enqueue it */ + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_string(ssh, pkt, plen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: send datagram", c->self); + c->remote_window -= plen; + return; + } + + /* Enqueue packet for buffered data. */ + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + if (len == 0) + return; + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: send data", c->self); + if ((r = sshbuf_consume(c->input, len)) != 0) + fatal_fr(r, "channel %i: consume", c->self); + c->remote_window -= len; +} + +/* + * Enqueue data for channels with open c->extended in read mode. + */ +static void +channel_output_poll_extended_read(struct ssh *ssh, Channel *c) +{ + size_t len; + int r; + + if ((len = sshbuf_len(c->extended)) == 0) + return; + + debug2("channel %d: rwin %u elen %zu euse %d", c->self, + c->remote_window, sshbuf_len(c->extended), c->extended_usage); + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + if (len == 0) + return; + if (!c->have_remote_id) + fatal_f("channel %d: no remote id", c->self); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || + (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %i: data", c->self); + if ((r = sshbuf_consume(c->extended, len)) != 0) + fatal_fr(r, "channel %i: consume", c->self); + c->remote_window -= len; + debug2("channel %d: sent ext data %zu", c->self, len); +} + +/* If there is data to send to the connection, enqueue some of it now. */ +void +channel_output_poll(struct ssh *ssh) +{ + struct ssh_channels *sc = ssh->chanctxt; + Channel *c; + u_int i; + + for (i = 0; i < sc->channels_alloc; i++) { + c = sc->channels[i]; + if (c == NULL) + continue; + + /* + * We are only interested in channels that can have buffered + * incoming data. + */ + if (c->type != SSH_CHANNEL_OPEN) + continue; + if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { + /* XXX is this true? */ + debug3("channel %d: will not send data after close", + c->self); + continue; + } + + /* Get the amount of buffered data for this channel. */ + if (c->istate == CHAN_INPUT_OPEN || + c->istate == CHAN_INPUT_WAIT_DRAIN) + channel_output_poll_input_open(ssh, c); + /* Send extended data, i.e. stderr */ + if (!(c->flags & CHAN_EOF_SENT) && + c->extended_usage == CHAN_EXTENDED_READ) + channel_output_poll_extended_read(ssh, c); + } +} + +/* -- mux proxy support */ + +/* + * When multiplexing channel messages for mux clients we have to deal + * with downstream messages from the mux client and upstream messages + * from the ssh server: + * 1) Handling downstream messages is straightforward and happens + * in channel_proxy_downstream(): + * - We forward all messages (mostly) unmodified to the server. + * - However, in order to route messages from upstream to the correct + * downstream client, we have to replace the channel IDs used by the + * mux clients with a unique channel ID because the mux clients might + * use conflicting channel IDs. + * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and + * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local + * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID + * with the newly allocated channel ID. + * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY + * channels and processed by channel_proxy_upstream(). The local channel ID + * is then translated back to the original mux client ID. + * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE + * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. + * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the + * downstream mux client are removed. + * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server + * requires more work, because they are not addressed to a specific + * channel. E.g. client_request_forwarded_tcpip() needs to figure + * out whether the request is addressed to the local client or a + * specific downstream client based on the listen-address/port. + * 6) Agent and X11-Forwarding have a similar problem and are currently + * not supported as the matching session/channel cannot be identified + * easily. + */ + +/* + * receive packets from downstream mux clients: + * channel callback fired on read from mux client, creates + * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs + * on channel creation. + */ +int +channel_proxy_downstream(struct ssh *ssh, Channel *downstream) +{ + Channel *c = NULL; + struct sshbuf *original = NULL, *modified = NULL; + const u_char *cp; + char *ctype = NULL, *listen_host = NULL; + u_char type; + size_t have; + int ret = -1, r; + u_int id, remote_id, listen_port; + + /* sshbuf_dump(downstream->input, stderr); */ + if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) + != 0) { + error_fr(r, "parse"); + return -1; + } + if (have < 2) { + error_f("short message"); + return -1; + } + type = cp[1]; + /* skip padlen + type */ + cp += 2; + have -= 2; + if (ssh_packet_log_type(type)) + debug3_f("channel %u: down->up: type %u", + downstream->self, type); + + switch (type) { + case SSH2_MSG_CHANNEL_OPEN: + if ((original = sshbuf_from(cp, have)) == NULL || + (modified = sshbuf_new()) == NULL) { + error_f("alloc"); + goto out; + } + if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || + (r = sshbuf_get_u32(original, &id)) != 0) { + error_fr(r, "parse"); + goto out; + } + c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, + -1, -1, -1, 0, 0, 0, ctype, 1); + c->mux_ctx = downstream; /* point to mux client */ + c->mux_downstream_id = id; /* original downstream id */ + if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || + (r = sshbuf_put_u32(modified, c->self)) != 0 || + (r = sshbuf_putb(modified, original)) != 0) { + error_fr(r, "compose"); + channel_free(ssh, c); + goto out; + } + break; + case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + /* + * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we + * need to parse 'remote_id' instead of 'ctype'. + */ + if ((original = sshbuf_from(cp, have)) == NULL || + (modified = sshbuf_new()) == NULL) { + error_f("alloc"); + goto out; + } + if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || + (r = sshbuf_get_u32(original, &id)) != 0) { + error_fr(r, "parse"); + goto out; + } + c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY, + -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); + c->mux_ctx = downstream; /* point to mux client */ + c->mux_downstream_id = id; + c->remote_id = remote_id; + c->have_remote_id = 1; + if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || + (r = sshbuf_put_u32(modified, c->self)) != 0 || + (r = sshbuf_putb(modified, original)) != 0) { + error_fr(r, "compose"); + channel_free(ssh, c); + goto out; + } + break; + case SSH2_MSG_GLOBAL_REQUEST: + if ((original = sshbuf_from(cp, have)) == NULL) { + error_f("alloc"); + goto out; + } + if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { + error_fr(r, "parse"); + goto out; + } + if (strcmp(ctype, "tcpip-forward") != 0) { + error_f("unsupported request %s", ctype); + goto out; + } + if ((r = sshbuf_get_u8(original, NULL)) != 0 || + (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || + (r = sshbuf_get_u32(original, &listen_port)) != 0) { + error_fr(r, "parse"); + goto out; + } + if (listen_port > 65535) { + error_f("tcpip-forward for %s: bad port %u", + listen_host, listen_port); + goto out; + } + /* Record that connection to this host/port is permitted. */ + permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "", -1, + listen_host, NULL, (int)listen_port, downstream); + listen_host = NULL; + break; + case SSH2_MSG_CHANNEL_CLOSE: + if (have < 4) + break; + remote_id = PEEK_U32(cp); + if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { + if (c->flags & CHAN_CLOSE_RCVD) + channel_free(ssh, c); + else + c->flags |= CHAN_CLOSE_SENT; + } + break; + } + if (modified) { + if ((r = sshpkt_start(ssh, type)) != 0 || + (r = sshpkt_putb(ssh, modified)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_fr(r, "send"); + goto out; + } + } else { + if ((r = sshpkt_start(ssh, type)) != 0 || + (r = sshpkt_put(ssh, cp, have)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_fr(r, "send"); + goto out; + } + } + ret = 0; + out: + free(ctype); + free(listen_host); + sshbuf_free(original); + sshbuf_free(modified); + return ret; +} + +/* + * receive packets from upstream server and de-multiplex packets + * to correct downstream: + * implemented as a helper for channel input handlers, + * replaces local (proxy) channel ID with downstream channel ID. + */ +int +channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) +{ + struct sshbuf *b = NULL; + Channel *downstream; + const u_char *cp = NULL; + size_t len; + int r; + + /* + * When receiving packets from the peer we need to check whether we + * need to forward the packets to the mux client. In this case we + * restore the original channel id and keep track of CLOSE messages, + * so we can cleanup the channel. + */ + if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) + return 0; + if ((downstream = c->mux_ctx) == NULL) + return 0; + switch (type) { + case SSH2_MSG_CHANNEL_CLOSE: + case SSH2_MSG_CHANNEL_DATA: + case SSH2_MSG_CHANNEL_EOF: + case SSH2_MSG_CHANNEL_EXTENDED_DATA: + case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + case SSH2_MSG_CHANNEL_OPEN_FAILURE: + case SSH2_MSG_CHANNEL_WINDOW_ADJUST: + case SSH2_MSG_CHANNEL_SUCCESS: + case SSH2_MSG_CHANNEL_FAILURE: + case SSH2_MSG_CHANNEL_REQUEST: + break; + default: + debug2_f("channel %u: unsupported type %u", c->self, type); + return 0; + } + if ((b = sshbuf_new()) == NULL) { + error_f("alloc reply"); + goto out; + } + /* get remaining payload (after id) */ + cp = sshpkt_ptr(ssh, &len); + if (cp == NULL) { + error_f("no packet"); + goto out; + } + /* translate id and send to muxclient */ + if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ + (r = sshbuf_put_u8(b, type)) != 0 || + (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || + (r = sshbuf_put(b, cp, len)) != 0 || + (r = sshbuf_put_stringb(downstream->output, b)) != 0) { + error_fr(r, "compose muxclient"); + goto out; + } + /* sshbuf_dump(b, stderr); */ + if (ssh_packet_log_type(type)) + debug3_f("channel %u: up->down: type %u", c->self, type); + out: + /* update state */ + switch (type) { + case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ + if (cp && len > 4) { + c->remote_id = PEEK_U32(cp); + c->have_remote_id = 1; + } + break; + case SSH2_MSG_CHANNEL_CLOSE: + if (c->flags & CHAN_CLOSE_SENT) + channel_free(ssh, c); + else + c->flags |= CHAN_CLOSE_RCVD; + break; + } + sshbuf_free(b); + return 1; +} + +/* -- protocol input */ + +/* Parse a channel ID from the current packet */ +static int +channel_parse_id(struct ssh *ssh, const char *where, const char *what) +{ + u_int32_t id; + int r; + + if ((r = sshpkt_get_u32(ssh, &id)) != 0) { + error_r(r, "%s: parse id", where); + ssh_packet_disconnect(ssh, "Invalid %s message", what); + } + if (id > INT_MAX) { + error_r(r, "%s: bad channel id %u", where, id); + ssh_packet_disconnect(ssh, "Invalid %s channel id", what); + } + return (int)id; +} + +/* Lookup a channel from an ID in the current packet */ +static Channel * +channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) +{ + int id = channel_parse_id(ssh, where, what); + Channel *c; + + if ((c = channel_lookup(ssh, id)) == NULL) { + ssh_packet_disconnect(ssh, + "%s packet referred to nonexistent channel %d", what, id); + } + return c; +} + +int +channel_input_data(int type, u_int32_t seq, struct ssh *ssh) +{ + const u_char *data; + size_t data_len, win_len; + Channel *c = channel_from_packet_id(ssh, __func__, "data"); + int r; + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + + /* Ignore any data for non-open channels (might happen on close) */ + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_RDYNAMIC_OPEN && + c->type != SSH_CHANNEL_RDYNAMIC_FINISH && + c->type != SSH_CHANNEL_X11_OPEN) + return 0; + + /* Get the data. */ + if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "channel %i: get data", c->self); + + win_len = data_len; + if (c->datagram) + win_len += 4; /* string length header */ + + /* + * The sending side reduces its window as it sends data, so we + * must 'fake' consumption of the data in order to ensure that window + * updates are sent back. Otherwise the connection might deadlock. + */ + if (c->ostate != CHAN_OUTPUT_OPEN) { + c->local_window -= win_len; + c->local_consumed += win_len; + return 0; + } + + if (win_len > c->local_maxpacket) { + logit("channel %d: rcvd big packet %zu, maxpack %u", + c->self, win_len, c->local_maxpacket); + return 0; + } + if (win_len > c->local_window) { + logit("channel %d: rcvd too much data %zu, win %u", + c->self, win_len, c->local_window); + return 0; + } + c->local_window -= win_len; + + if (c->datagram) { + if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) + fatal_fr(r, "channel %i: append datagram", c->self); + } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) + fatal_fr(r, "channel %i: append data", c->self); + + return 0; +} + +int +channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) +{ + const u_char *data; + size_t data_len; + u_int32_t tcode; + Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); + int r; + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if (c->type != SSH_CHANNEL_OPEN) { + logit("channel %d: ext data for non open", c->self); + return 0; + } + if (c->flags & CHAN_EOF_RCVD) { + if (ssh->compat & SSH_BUG_EXTEOF) + debug("channel %d: accepting ext data after eof", + c->self); + else + ssh_packet_disconnect(ssh, "Received extended_data " + "after EOF on channel %d.", c->self); + } + + if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { + error_fr(r, "parse tcode"); + ssh_packet_disconnect(ssh, "Invalid extended_data message"); + } + if (c->efd == -1 || + c->extended_usage != CHAN_EXTENDED_WRITE || + tcode != SSH2_EXTENDED_DATA_STDERR) { + logit("channel %d: bad ext data", c->self); + return 0; + } + if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "parse data"); + ssh_packet_disconnect(ssh, "Invalid extended_data message"); + } + + if (data_len > c->local_window) { + logit("channel %d: rcvd too much extended_data %zu, win %u", + c->self, data_len, c->local_window); + return 0; + } + debug2("channel %d: rcvd ext data %zu", c->self, data_len); + /* XXX sshpkt_getb? */ + if ((r = sshbuf_put(c->extended, data, data_len)) != 0) + error_fr(r, "append"); + c->local_window -= data_len; + return 0; +} + +int +channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); + int r; + + if ((r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "parse data"); + ssh_packet_disconnect(ssh, "Invalid ieof message"); + } + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + chan_rcvd_ieof(ssh, c); + + /* XXX force input close */ + if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { + debug("channel %d: FORCE input drain", c->self); + c->istate = CHAN_INPUT_WAIT_DRAIN; + if (sshbuf_len(c->input) == 0) + chan_ibuf_empty(ssh, c); + } + return 0; +} + +int +channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); + int r; + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if ((r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "parse data"); + ssh_packet_disconnect(ssh, "Invalid oclose message"); + } + chan_rcvd_oclose(ssh, c); + return 0; +} + +int +channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); + u_int32_t remote_window, remote_maxpacket; + int r; + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if (c->type != SSH_CHANNEL_OPENING) + ssh_packet_disconnect(ssh, "Received open confirmation for " + "non-opening channel %d.", c->self); + /* + * Record the remote channel number and mark that the channel + * is now open. + */ + if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || + (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || + (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "window/maxpacket"); + ssh_packet_disconnect(ssh, "Invalid open confirmation message"); + } + + c->have_remote_id = 1; + c->remote_window = remote_window; + c->remote_maxpacket = remote_maxpacket; + c->type = SSH_CHANNEL_OPEN; + if (c->open_confirm) { + debug2_f("channel %d: callback start", c->self); + c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); + debug2_f("channel %d: callback done", c->self); + } + debug2("channel %d: open confirm rwindow %u rmax %u", c->self, + c->remote_window, c->remote_maxpacket); + return 0; +} + +static char * +reason2txt(int reason) +{ + switch (reason) { + case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: + return "administratively prohibited"; + case SSH2_OPEN_CONNECT_FAILED: + return "connect failed"; + case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: + return "unknown channel type"; + case SSH2_OPEN_RESOURCE_SHORTAGE: + return "resource shortage"; + } + return "unknown reason"; +} + +int +channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); + u_int32_t reason; + char *msg = NULL; + int r; + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if (c->type != SSH_CHANNEL_OPENING) + ssh_packet_disconnect(ssh, "Received open failure for " + "non-opening channel %d.", c->self); + if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { + error_fr(r, "parse reason"); + ssh_packet_disconnect(ssh, "Invalid open failure message"); + } + /* skip language */ + if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || + (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "parse msg/lang"); + ssh_packet_disconnect(ssh, "Invalid open failure message"); + } + logit("channel %d: open failed: %s%s%s", c->self, + reason2txt(reason), msg ? ": ": "", msg ? msg : ""); + free(msg); + if (c->open_confirm) { + debug2_f("channel %d: callback start", c->self); + c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); + debug2_f("channel %d: callback done", c->self); + } + /* Schedule the channel for cleanup/deletion. */ + chan_mark_dead(ssh, c); + return 0; +} + +int +channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) +{ + int id = channel_parse_id(ssh, __func__, "window adjust"); + Channel *c; + u_int32_t adjust; + u_int new_rwin; + int r; + + if ((c = channel_lookup(ssh, id)) == NULL) { + logit("Received window adjust for non-open channel %d.", id); + return 0; + } + + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "parse adjust"); + ssh_packet_disconnect(ssh, "Invalid window adjust message"); + } + debug2("channel %d: rcvd adjust %u", c->self, adjust); + if ((new_rwin = c->remote_window + adjust) < c->remote_window) { + fatal("channel %d: adjust %u overflows remote window %u", + c->self, adjust, c->remote_window); + } + c->remote_window = new_rwin; + return 0; +} + +int +channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) +{ + int id = channel_parse_id(ssh, __func__, "status confirm"); + Channel *c; + struct channel_confirm *cc; + + /* Reset keepalive timeout */ + ssh_packet_set_alive_timeouts(ssh, 0); + + debug2_f("type %d id %d", type, id); + + if ((c = channel_lookup(ssh, id)) == NULL) { + logit_f("%d: unknown", id); + return 0; + } + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if (sshpkt_get_end(ssh) != 0) + ssh_packet_disconnect(ssh, "Invalid status confirm message"); + if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) + return 0; + cc->cb(ssh, type, c, cc->ctx); + TAILQ_REMOVE(&c->status_confirms, cc, entry); + freezero(cc, sizeof(*cc)); + return 0; +} + +/* -- tcp forwarding */ + +void +channel_set_af(struct ssh *ssh, int af) +{ + ssh->chanctxt->IPv4or6 = af; +} + + +/* + * Determine whether or not a port forward listens to loopback, the + * specified address or wildcard. On the client, a specified bind + * address will always override gateway_ports. On the server, a + * gateway_ports of 1 (``yes'') will override the client's specification + * and force a wildcard bind, whereas a value of 2 (``clientspecified'') + * will bind to whatever address the client asked for. + * + * Special-case listen_addrs are: + * + * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR + * "" (empty string), "*" -> wildcard v4/v6 + * "localhost" -> loopback v4/v6 + * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set + */ +static const char * +channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp, + int is_client, struct ForwardOptions *fwd_opts) +{ + const char *addr = NULL; + int wildcard = 0; + + if (listen_addr == NULL) { + /* No address specified: default to gateway_ports setting */ + if (fwd_opts->gateway_ports) + wildcard = 1; + } else if (fwd_opts->gateway_ports || is_client) { + if (((ssh->compat & SSH_OLD_FORWARD_ADDR) && + strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || + *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || + (!is_client && fwd_opts->gateway_ports == 1)) { + wildcard = 1; + /* + * Notify client if they requested a specific listen + * address and it was overridden. + */ + if (*listen_addr != '\0' && + strcmp(listen_addr, "0.0.0.0") != 0 && + strcmp(listen_addr, "*") != 0) { + ssh_packet_send_debug(ssh, + "Forwarding listen address " + "\"%s\" overridden by server " + "GatewayPorts", listen_addr); + } + } else if (strcmp(listen_addr, "localhost") != 0 || + strcmp(listen_addr, "127.0.0.1") == 0 || + strcmp(listen_addr, "::1") == 0) { + /* + * Accept explicit localhost address when + * GatewayPorts=yes. The "localhost" hostname is + * deliberately skipped here so it will listen on all + * available local address families. + */ + addr = listen_addr; + } + } else if (strcmp(listen_addr, "127.0.0.1") == 0 || + strcmp(listen_addr, "::1") == 0) { + /* + * If a specific IPv4/IPv6 localhost address has been + * requested then accept it even if gateway_ports is in + * effect. This allows the client to prefer IPv4 or IPv6. + */ + addr = listen_addr; + } + if (wildcardp != NULL) + *wildcardp = wildcard; + return addr; +} + +static int +channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, + struct Forward *fwd, int *allocated_listen_port, + struct ForwardOptions *fwd_opts) +{ + Channel *c; + int sock, r, success = 0, wildcard = 0, is_client; + struct addrinfo hints, *ai, *aitop; + const char *host, *addr; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + in_port_t *lport_p; + + is_client = (type == SSH_CHANNEL_PORT_LISTENER); + + if (is_client && fwd->connect_path != NULL) { + host = fwd->connect_path; + } else { + host = (type == SSH_CHANNEL_RPORT_LISTENER) ? + fwd->listen_host : fwd->connect_host; + if (host == NULL) { + error("No forward host name."); + return 0; + } + if (strlen(host) >= NI_MAXHOST) { + error("Forward host name too long."); + return 0; + } + } + + /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ + addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard, + is_client, fwd_opts); + debug3_f("type %d wildcard %d addr %s", type, wildcard, + (addr == NULL) ? "NULL" : addr); + + /* + * getaddrinfo returns a loopback address if the hostname is + * set to NULL and hints.ai_flags is not AI_PASSIVE + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_flags = wildcard ? AI_PASSIVE : 0; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", fwd->listen_port); + if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { + if (addr == NULL) { + /* This really shouldn't happen */ + ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s", + ssh_gai_strerror(r)); + } else { + error_f("getaddrinfo(%.64s): %s", addr, + ssh_gai_strerror(r)); + } + return 0; + } + if (allocated_listen_port != NULL) + *allocated_listen_port = 0; + for (ai = aitop; ai; ai = ai->ai_next) { + switch (ai->ai_family) { + case AF_INET: + lport_p = &((struct sockaddr_in *)ai->ai_addr)-> + sin_port; + break; + case AF_INET6: + lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> + sin6_port; + break; + default: + continue; + } + /* + * If allocating a port for -R forwards, then use the + * same port for all address families. + */ + if (type == SSH_CHANNEL_RPORT_LISTENER && + fwd->listen_port == 0 && allocated_listen_port != NULL && + *allocated_listen_port > 0) + *lport_p = htons(*allocated_listen_port); + + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), + strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error_f("getnameinfo failed"); + continue; + } + /* Create a port to listen for the host. */ + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock == -1) { + /* this is no error since kernel may not support ipv6 */ + verbose("socket [%s]:%s: %.100s", ntop, strport, + strerror(errno)); + continue; + } + + set_reuseaddr(sock); + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); + + debug("Local forwarding listening on %s port %s.", + ntop, strport); + + /* Bind the socket to the address. */ + if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { + /* + * address can be in if use ipv6 address is + * already bound + */ + if (!ai->ai_next) + error("bind [%s]:%s: %.100s", + ntop, strport, strerror(errno)); + else + verbose("bind [%s]:%s: %.100s", + ntop, strport, strerror(errno)); + + close(sock); + continue; + } + /* Start listening for connections on the socket. */ + if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { + error("listen [%s]:%s: %.100s", ntop, strport, + strerror(errno)); + close(sock); + continue; + } + + /* + * fwd->listen_port == 0 requests a dynamically allocated port - + * record what we got. + */ + if (type == SSH_CHANNEL_RPORT_LISTENER && + fwd->listen_port == 0 && + allocated_listen_port != NULL && + *allocated_listen_port == 0) { + *allocated_listen_port = get_local_port(sock); + debug("Allocated listen port %d", + *allocated_listen_port); + } + + /* Allocate a channel number for the socket. */ + c = channel_new(ssh, "port listener", type, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "port listener", 1); + c->path = xstrdup(host); + c->host_port = fwd->connect_port; + c->listening_addr = addr == NULL ? NULL : xstrdup(addr); + if (fwd->listen_port == 0 && allocated_listen_port != NULL && + !(ssh->compat & SSH_BUG_DYNAMIC_RPORT)) + c->listening_port = *allocated_listen_port; + else + c->listening_port = fwd->listen_port; + success = 1; + } + if (success == 0) + error_f("cannot listen to port: %d", fwd->listen_port); + freeaddrinfo(aitop); + return success; +} + +static int +channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, + struct Forward *fwd, struct ForwardOptions *fwd_opts) +{ + struct sockaddr_un sunaddr; + const char *path; + Channel *c; + int port, sock; + mode_t omask; + + switch (type) { + case SSH_CHANNEL_UNIX_LISTENER: + if (fwd->connect_path != NULL) { + if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { + error("Local connecting path too long: %s", + fwd->connect_path); + return 0; + } + path = fwd->connect_path; + port = PORT_STREAMLOCAL; + } else { + if (fwd->connect_host == NULL) { + error("No forward host name."); + return 0; + } + if (strlen(fwd->connect_host) >= NI_MAXHOST) { + error("Forward host name too long."); + return 0; + } + path = fwd->connect_host; + port = fwd->connect_port; + } + break; + case SSH_CHANNEL_RUNIX_LISTENER: + path = fwd->listen_path; + port = PORT_STREAMLOCAL; + break; + default: + error_f("unexpected channel type %d", type); + return 0; + } + + if (fwd->listen_path == NULL) { + error("No forward path name."); + return 0; + } + if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { + error("Local listening path too long: %s", fwd->listen_path); + return 0; + } + + debug3_f("type %d path %s", type, fwd->listen_path); + + /* Start a Unix domain listener. */ + omask = umask(fwd_opts->streamlocal_bind_mask); + sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, + fwd_opts->streamlocal_bind_unlink); + umask(omask); + if (sock < 0) + return 0; + + debug("Local forwarding listening on path %s.", fwd->listen_path); + + /* Allocate a channel number for the socket. */ + c = channel_new(ssh, "unix listener", type, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "unix listener", 1); + c->path = xstrdup(path); + c->host_port = port; + c->listening_port = PORT_STREAMLOCAL; + c->listening_addr = xstrdup(fwd->listen_path); + return 1; +} + +static int +channel_cancel_rport_listener_tcpip(struct ssh *ssh, + const char *host, u_short port) +{ + u_int i; + int found = 0; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) + continue; + if (strcmp(c->path, host) == 0 && c->listening_port == port) { + debug2_f("close channel %d", i); + channel_free(ssh, c); + found = 1; + } + } + + return found; +} + +static int +channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) +{ + u_int i; + int found = 0; + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) + continue; + if (c->path == NULL) + continue; + if (strcmp(c->path, path) == 0) { + debug2_f("close channel %d", i); + channel_free(ssh, c); + found = 1; + } + } + + return found; +} + +int +channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) +{ + if (fwd->listen_path != NULL) { + return channel_cancel_rport_listener_streamlocal(ssh, + fwd->listen_path); + } else { + return channel_cancel_rport_listener_tcpip(ssh, + fwd->listen_host, fwd->listen_port); + } +} + +static int +channel_cancel_lport_listener_tcpip(struct ssh *ssh, + const char *lhost, u_short lport, int cport, + struct ForwardOptions *fwd_opts) +{ + u_int i; + int found = 0; + const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts); + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) + continue; + if (c->listening_port != lport) + continue; + if (cport == CHANNEL_CANCEL_PORT_STATIC) { + /* skip dynamic forwardings */ + if (c->host_port == 0) + continue; + } else { + if (c->host_port != cport) + continue; + } + if ((c->listening_addr == NULL && addr != NULL) || + (c->listening_addr != NULL && addr == NULL)) + continue; + if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { + debug2_f("close channel %d", i); + channel_free(ssh, c); + found = 1; + } + } + + return found; +} + +static int +channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) +{ + u_int i; + int found = 0; + + if (path == NULL) { + error_f("no path specified."); + return 0; + } + + for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { + Channel *c = ssh->chanctxt->channels[i]; + if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) + continue; + if (c->listening_addr == NULL) + continue; + if (strcmp(c->listening_addr, path) == 0) { + debug2_f("close channel %d", i); + channel_free(ssh, c); + found = 1; + } + } + + return found; +} + +int +channel_cancel_lport_listener(struct ssh *ssh, + struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) +{ + if (fwd->listen_path != NULL) { + return channel_cancel_lport_listener_streamlocal(ssh, + fwd->listen_path); + } else { + return channel_cancel_lport_listener_tcpip(ssh, + fwd->listen_host, fwd->listen_port, cport, fwd_opts); + } +} + +/* protocol local port fwd, used by ssh */ +int +channel_setup_local_fwd_listener(struct ssh *ssh, + struct Forward *fwd, struct ForwardOptions *fwd_opts) +{ + if (fwd->listen_path != NULL) { + return channel_setup_fwd_listener_streamlocal(ssh, + SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); + } else { + return channel_setup_fwd_listener_tcpip(ssh, + SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); + } +} + +/* Matches a remote forwarding permission against a requested forwarding */ +static int +remote_open_match(struct permission *allowed_open, struct Forward *fwd) +{ + int ret; + char *lhost; + + /* XXX add ACLs for streamlocal */ + if (fwd->listen_path != NULL) + return 1; + + if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) + return 0; + + if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && + allowed_open->listen_port != fwd->listen_port) + return 0; + + /* Match hostnames case-insensitively */ + lhost = xstrdup(fwd->listen_host); + lowercase(lhost); + ret = match_pattern(lhost, allowed_open->listen_host); + free(lhost); + + return ret; +} + +/* Checks whether a requested remote forwarding is permitted */ +static int +check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->remote_perms; + u_int i, permit, permit_adm = 1; + struct permission *perm; + + /* XXX apply GatewayPorts override before checking? */ + + permit = pset->all_permitted; + if (!permit) { + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (remote_open_match(perm, fwd)) { + permit = 1; + break; + } + } + } + + if (pset->num_permitted_admin > 0) { + permit_adm = 0; + for (i = 0; i < pset->num_permitted_admin; i++) { + perm = &pset->permitted_admin[i]; + if (remote_open_match(perm, fwd)) { + permit_adm = 1; + break; + } + } + } + + return permit && permit_adm; +} + +/* protocol v2 remote port fwd, used by sshd */ +int +channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, + int *allocated_listen_port, struct ForwardOptions *fwd_opts) +{ + if (!check_rfwd_permission(ssh, fwd)) { + ssh_packet_send_debug(ssh, "port forwarding refused"); + if (fwd->listen_path != NULL) + /* XXX always allowed, see remote_open_match() */ + logit("Received request from %.100s port %d to " + "remote forward to path \"%.100s\", " + "but the request was denied.", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + fwd->listen_path); + else if(fwd->listen_host != NULL) + logit("Received request from %.100s port %d to " + "remote forward to host %.100s port %d, " + "but the request was denied.", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + fwd->listen_host, fwd->listen_port ); + else + logit("Received request from %.100s port %d to remote " + "forward, but the request was denied.", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + return 0; + } + if (fwd->listen_path != NULL) { + return channel_setup_fwd_listener_streamlocal(ssh, + SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); + } else { + return channel_setup_fwd_listener_tcpip(ssh, + SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, + fwd_opts); + } +} + +/* + * Translate the requested rfwd listen host to something usable for + * this server. + */ +static const char * +channel_rfwd_bind_host(const char *listen_host) +{ + if (listen_host == NULL) { + return "localhost"; + } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { + return ""; + } else + return listen_host; +} + +/* + * Initiate forwarding of connections to port "port" on remote host through + * the secure channel to host:port from local side. + * Returns handle (index) for updating the dynamic listen port with + * channel_update_permission(). + */ +int +channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) +{ + int r, success = 0, idx = -1; + char *host_to_connect, *listen_host, *listen_path; + int port_to_connect, listen_port; + + /* Send the forward request to the remote side. */ + if (fwd->listen_path != NULL) { + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "streamlocal-forward@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "request streamlocal"); + } else { + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, + channel_rfwd_bind_host(fwd->listen_host))) != 0 || + (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "request tcpip-forward"); + } + /* Assume that server accepts the request */ + success = 1; + if (success) { + /* Record that connection to this host/port is permitted. */ + host_to_connect = listen_host = listen_path = NULL; + port_to_connect = listen_port = 0; + if (fwd->connect_path != NULL) { + host_to_connect = xstrdup(fwd->connect_path); + port_to_connect = PORT_STREAMLOCAL; + } else { + host_to_connect = xstrdup(fwd->connect_host); + port_to_connect = fwd->connect_port; + } + if (fwd->listen_path != NULL) { + listen_path = xstrdup(fwd->listen_path); + listen_port = PORT_STREAMLOCAL; + } else { + if (fwd->listen_host != NULL) + listen_host = xstrdup(fwd->listen_host); + listen_port = fwd->listen_port; + } + idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, + host_to_connect, port_to_connect, + listen_host, listen_path, listen_port, NULL); + } + return idx; +} + +static int +open_match(struct permission *allowed_open, const char *requestedhost, + int requestedport) +{ + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && + allowed_open->port_to_connect != requestedport) + return 0; + if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && + strcmp(allowed_open->host_to_connect, requestedhost) != 0) + return 0; + return 1; +} + +/* + * Note that in the listen host/port case + * we don't support FWD_PERMIT_ANY_PORT and + * need to translate between the configured-host (listen_host) + * and what we've sent to the remote server (channel_rfwd_bind_host) + */ +static int +open_listen_match_tcpip(struct permission *allowed_open, + const char *requestedhost, u_short requestedport, int translate) +{ + const char *allowed_host; + + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->listen_port != requestedport) + return 0; + if (!translate && allowed_open->listen_host == NULL && + requestedhost == NULL) + return 1; + allowed_host = translate ? + channel_rfwd_bind_host(allowed_open->listen_host) : + allowed_open->listen_host; + if (allowed_host == NULL || requestedhost == NULL || + strcmp(allowed_host, requestedhost) != 0) + return 0; + return 1; +} + +static int +open_listen_match_streamlocal(struct permission *allowed_open, + const char *requestedpath) +{ + if (allowed_open->host_to_connect == NULL) + return 0; + if (allowed_open->listen_port != PORT_STREAMLOCAL) + return 0; + if (allowed_open->listen_path == NULL || + strcmp(allowed_open->listen_path, requestedpath) != 0) + return 0; + return 1; +} + +/* + * Request cancellation of remote forwarding of connection host:port from + * local side. + */ +static int +channel_request_rforward_cancel_tcpip(struct ssh *ssh, + const char *host, u_short port) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + int r; + u_int i; + struct permission *perm = NULL; + + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (open_listen_match_tcpip(perm, host, port, 0)) + break; + perm = NULL; + } + if (perm == NULL) { + debug_f("requested forward not found"); + return -1; + } + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || + (r = sshpkt_put_u32(ssh, port)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send cancel"); + + fwd_perm_clear(perm); /* unregister */ + + return 0; +} + +/* + * Request cancellation of remote forwarding of Unix domain socket + * path from local side. + */ +static int +channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + int r; + u_int i; + struct permission *perm = NULL; + + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (open_listen_match_streamlocal(perm, path)) + break; + perm = NULL; + } + if (perm == NULL) { + debug_f("requested forward not found"); + return -1; + } + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "cancel-streamlocal-forward@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ + (r = sshpkt_put_cstring(ssh, path)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send cancel"); + + fwd_perm_clear(perm); /* unregister */ + + return 0; +} + +/* + * Request cancellation of remote forwarding of a connection from local side. + */ +int +channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) +{ + if (fwd->listen_path != NULL) { + return channel_request_rforward_cancel_streamlocal(ssh, + fwd->listen_path); + } else { + return channel_request_rforward_cancel_tcpip(ssh, + fwd->listen_host, + fwd->listen_port ? fwd->listen_port : fwd->allocated_port); + } +} + +/* + * Permits opening to any host/port if permitted_user[] is empty. This is + * usually called by the server, because the user could connect to any port + * anyway, and the server has no way to know but to trust the client anyway. + */ +void +channel_permit_all(struct ssh *ssh, int where) +{ + struct permission_set *pset = permission_set_get(ssh, where); + + if (pset->num_permitted_user == 0) + pset->all_permitted = 1; +} + +/* + * Permit the specified host/port for forwarding. + */ +void +channel_add_permission(struct ssh *ssh, int who, int where, + char *host, int port) +{ + int local = where == FORWARD_LOCAL; + struct permission_set *pset = permission_set_get(ssh, where); + + debug("allow %s forwarding to host %s port %d", + fwd_ident(who, where), host, port); + /* + * Remote forwards set listen_host/port, local forwards set + * host/port_to_connect. + */ + permission_set_add(ssh, who, where, + local ? host : 0, local ? port : 0, + local ? NULL : host, NULL, local ? 0 : port, NULL); + pset->all_permitted = 0; +} + +/* + * Administratively disable forwarding. + */ +void +channel_disable_admin(struct ssh *ssh, int where) +{ + channel_clear_permission(ssh, FORWARD_ADM, where); + permission_set_add(ssh, FORWARD_ADM, where, + NULL, 0, NULL, NULL, 0, NULL); +} + +/* + * Clear a list of permitted opens. + */ +void +channel_clear_permission(struct ssh *ssh, int who, int where) +{ + struct permission **permp; + u_int *npermp; + + permission_set_get_array(ssh, who, where, &permp, &npermp); + *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); + *npermp = 0; +} + +/* + * Update the listen port for a dynamic remote forward, after + * the actual 'newport' has been allocated. If 'newport' < 0 is + * passed then they entry will be invalidated. + */ +void +channel_update_permission(struct ssh *ssh, int idx, int newport) +{ + struct permission_set *pset = &ssh->chanctxt->local_perms; + + if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { + debug_f("index out of range: %d num_permitted_user %d", + idx, pset->num_permitted_user); + return; + } + debug("%s allowed port %d for forwarding to host %s port %d", + newport > 0 ? "Updating" : "Removing", + newport, + pset->permitted_user[idx].host_to_connect, + pset->permitted_user[idx].port_to_connect); + if (newport <= 0) + fwd_perm_clear(&pset->permitted_user[idx]); + else { + pset->permitted_user[idx].listen_port = + (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; + } +} + +/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ +int +permitopen_port(const char *p) +{ + int port; + + if (strcmp(p, "*") == 0) + return FWD_PERMIT_ANY_PORT; + if ((port = a2port(p)) > 0) + return port; + return -1; +} + +/* Try to start non-blocking connect to next host in cctx list */ +static int +connect_next(struct channel_connect *cctx) +{ + int sock, saved_errno; + struct sockaddr_un *sunaddr; + char ntop[NI_MAXHOST]; + char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; + + for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { + switch (cctx->ai->ai_family) { + case AF_UNIX: + /* unix:pathname instead of host:port */ + sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; + strlcpy(ntop, "unix", sizeof(ntop)); + strlcpy(strport, sunaddr->sun_path, sizeof(strport)); + break; + case AF_INET: + case AF_INET6: + if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("connect_next: getnameinfo failed"); + continue; + } + break; + default: + continue; + } + if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, + cctx->ai->ai_protocol)) == -1) { + if (cctx->ai->ai_next == NULL) + error("socket: %.100s", strerror(errno)); + else + verbose("socket: %.100s", strerror(errno)); + continue; + } + if (set_nonblock(sock) == -1) + fatal_f("set_nonblock(%d)", sock); + if (connect(sock, cctx->ai->ai_addr, + cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { + debug("connect_next: host %.100s ([%.100s]:%s): " + "%.100s", cctx->host, ntop, strport, + strerror(errno)); + saved_errno = errno; + close(sock); + errno = saved_errno; + continue; /* fail -- try next */ + } + if (cctx->ai->ai_family != AF_UNIX) + set_nodelay(sock); + debug("connect_next: host %.100s ([%.100s]:%s) " + "in progress, fd=%d", cctx->host, ntop, strport, sock); + cctx->ai = cctx->ai->ai_next; + return sock; + } + return -1; +} + +static void +channel_connect_ctx_free(struct channel_connect *cctx) +{ + free(cctx->host); + if (cctx->aitop) { + if (cctx->aitop->ai_family == AF_UNIX) + free(cctx->aitop); + else + freeaddrinfo(cctx->aitop); + } + memset(cctx, 0, sizeof(*cctx)); +} + +/* + * Return connecting socket to remote host:port or local socket path, + * passing back the failure reason if appropriate. + */ +static int +connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, + char *ctype, char *rname, struct channel_connect *cctx, + int *reason, const char **errmsg) +{ + struct addrinfo hints; + int gaierr; + int sock = -1; + char strport[NI_MAXSERV]; + + if (port == PORT_STREAMLOCAL) { + struct sockaddr_un *sunaddr; + struct addrinfo *ai; + + if (strlen(name) > sizeof(sunaddr->sun_path)) { + error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); + return -1; + } + + /* + * Fake up a struct addrinfo for AF_UNIX connections. + * channel_connect_ctx_free() must check ai_family + * and use free() not freeaddirinfo() for AF_UNIX. + */ + ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); + memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); + ai->ai_addr = (struct sockaddr *)(ai + 1); + ai->ai_addrlen = sizeof(*sunaddr); + ai->ai_family = AF_UNIX; + ai->ai_socktype = socktype; + ai->ai_protocol = PF_UNSPEC; + sunaddr = (struct sockaddr_un *)ai->ai_addr; + sunaddr->sun_family = AF_UNIX; + strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); + cctx->aitop = ai; + } else { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_socktype = socktype; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) + != 0) { + if (errmsg != NULL) + *errmsg = ssh_gai_strerror(gaierr); + if (reason != NULL) + *reason = SSH2_OPEN_CONNECT_FAILED; + error("connect_to %.100s: unknown host (%s)", name, + ssh_gai_strerror(gaierr)); + return -1; + } + } + + cctx->host = xstrdup(name); + cctx->port = port; + cctx->ai = cctx->aitop; + + if ((sock = connect_next(cctx)) == -1) { + error("connect to %.100s port %d failed: %s", + name, port, strerror(errno)); + return -1; + } + + return sock; +} + +/* Return CONNECTING channel to remote host:port or local socket path */ +static Channel * +connect_to(struct ssh *ssh, const char *host, int port, + char *ctype, char *rname) +{ + struct channel_connect cctx; + Channel *c; + int sock; + + memset(&cctx, 0, sizeof(cctx)); + sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, + &cctx, NULL, NULL); + if (sock == -1) { + channel_connect_ctx_free(&cctx); + return NULL; + } + c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); + c->host_port = port; + c->path = xstrdup(host); + c->connect_ctx = cctx; + + return c; +} + +/* + * returns either the newly connected channel or the downstream channel + * that needs to deal with this connection. + */ +Channel * +channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, + u_short listen_port, char *ctype, char *rname) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + u_int i; + struct permission *perm; + + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (open_listen_match_tcpip(perm, + listen_host, listen_port, 1)) { + if (perm->downstream) + return perm->downstream; + if (perm->port_to_connect == 0) + return rdynamic_connect_prepare(ssh, + ctype, rname); + return connect_to(ssh, + perm->host_to_connect, perm->port_to_connect, + ctype, rname); + } + } + error("WARNING: Server requests forwarding for unknown listen_port %d", + listen_port); + return NULL; +} + +Channel * +channel_connect_by_listen_path(struct ssh *ssh, const char *path, + char *ctype, char *rname) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + u_int i; + struct permission *perm; + + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (open_listen_match_streamlocal(perm, path)) { + return connect_to(ssh, + perm->host_to_connect, perm->port_to_connect, + ctype, rname); + } + } + error("WARNING: Server requests forwarding for unknown path %.100s", + path); + return NULL; +} + +/* Check if connecting to that port is permitted and connect. */ +Channel * +channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, + char *ctype, char *rname, int *reason, const char **errmsg) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + struct channel_connect cctx; + Channel *c; + u_int i, permit, permit_adm = 1; + int sock; + struct permission *perm; + + permit = pset->all_permitted; + if (!permit) { + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (open_match(perm, host, port)) { + permit = 1; + break; + } + } + } + + if (pset->num_permitted_admin > 0) { + permit_adm = 0; + for (i = 0; i < pset->num_permitted_admin; i++) { + perm = &pset->permitted_admin[i]; + if (open_match(perm, host, port)) { + permit_adm = 1; + break; + } + } + } + + if (!permit || !permit_adm) { + logit("Received request from %.100s port %d to connect to " + "host %.100s port %d, but the request was denied.", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port); + if (reason != NULL) + *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; + return NULL; + } + + memset(&cctx, 0, sizeof(cctx)); + sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, + &cctx, reason, errmsg); + if (sock == -1) { + channel_connect_ctx_free(&cctx); + return NULL; + } + + c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); + c->host_port = port; + c->path = xstrdup(host); + c->connect_ctx = cctx; + + return c; +} + +/* Check if connecting to that path is permitted and connect. */ +Channel * +channel_connect_to_path(struct ssh *ssh, const char *path, + char *ctype, char *rname) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + u_int i, permit, permit_adm = 1; + struct permission *perm; + + permit = pset->all_permitted; + if (!permit) { + for (i = 0; i < pset->num_permitted_user; i++) { + perm = &pset->permitted_user[i]; + if (open_match(perm, path, PORT_STREAMLOCAL)) { + permit = 1; + break; + } + } + } + + if (pset->num_permitted_admin > 0) { + permit_adm = 0; + for (i = 0; i < pset->num_permitted_admin; i++) { + perm = &pset->permitted_admin[i]; + if (open_match(perm, path, PORT_STREAMLOCAL)) { + permit_adm = 1; + break; + } + } + } + + if (!permit || !permit_adm) { + logit("Received request to connect to path %.100s, " + "but the request was denied.", path); + return NULL; + } + return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); +} + +void +channel_send_window_changes(struct ssh *ssh) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct winsize ws; + int r; + u_int i; + + for (i = 0; i < sc->channels_alloc; i++) { + if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || + sc->channels[i]->type != SSH_CHANNEL_OPEN) + continue; + if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1) + continue; + channel_request_start(ssh, i, "window-change", 0); + if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "channel %u; send window-change", i); + } +} + +/* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ +static Channel * +rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) +{ + Channel *c; + int r; + + c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); + c->host_port = 0; + c->path = NULL; + + /* + * We need to open the channel before we have a FD, + * so that we can get SOCKS header from peer. + */ + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) + fatal_fr(r, "channel %i; confirm", c->self); + return c; +} + +/* Return CONNECTING socket to remote host:port or local socket path */ +static int +rdynamic_connect_finish(struct ssh *ssh, Channel *c) +{ + struct ssh_channels *sc = ssh->chanctxt; + struct permission_set *pset = &sc->local_perms; + struct permission *perm; + struct channel_connect cctx; + u_int i, permit_adm = 1; + int sock; + + if (pset->num_permitted_admin > 0) { + permit_adm = 0; + for (i = 0; i < pset->num_permitted_admin; i++) { + perm = &pset->permitted_admin[i]; + if (open_match(perm, c->path, c->host_port)) { + permit_adm = 1; + break; + } + } + } + if (!permit_adm) { + debug_f("requested forward not permitted"); + return -1; + } + + memset(&cctx, 0, sizeof(cctx)); + sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, + NULL, &cctx, NULL, NULL); + if (sock == -1) + channel_connect_ctx_free(&cctx); + else { + /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ + c->type = SSH_CHANNEL_RDYNAMIC_FINISH; + c->connect_ctx = cctx; + channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); + } + return sock; +} + +/* -- X11 forwarding */ + +/* + * Creates an internet domain socket for listening for X11 connections. + * Returns 0 and a suitable display number for the DISPLAY variable + * stored in display_numberp , or -1 if an error occurs. + */ +int +x11_create_display_inet(struct ssh *ssh, int x11_display_offset, + int x11_use_localhost, int single_connection, + u_int *display_numberp, int **chanids) +{ + Channel *nc = NULL; + int display_number, sock; + u_short port; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; + + if (chanids == NULL) + return -1; + + for (display_number = x11_display_offset; + display_number < MAX_DISPLAYS; + display_number++) { + port = 6000 + display_number; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(NULL, strport, + &hints, &aitop)) != 0) { + error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); + return -1; + } + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && + ai->ai_family != AF_INET6) + continue; + sock = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (sock == -1) { + if ((errno != EINVAL) && (errno != EAFNOSUPPORT) +#ifdef EPFNOSUPPORT + && (errno != EPFNOSUPPORT) +#endif + ) { + error("socket: %.100s", strerror(errno)); + freeaddrinfo(aitop); + return -1; + } else { + debug("x11_create_display_inet: Socket family %d not supported", + ai->ai_family); + continue; + } + } + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); + if (x11_use_localhost) + set_reuseaddr(sock); + if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { + debug2_f("bind port %d: %.100s", port, + strerror(errno)); + close(sock); + for (n = 0; n < num_socks; n++) + close(socks[n]); + num_socks = 0; + break; + } + socks[num_socks++] = sock; + if (num_socks == NUM_SOCKS) + break; + } + freeaddrinfo(aitop); + if (num_socks > 0) + break; + } + if (display_number >= MAX_DISPLAYS) { + error("Failed to allocate internet-domain X11 display socket."); + return -1; + } + /* Start listening for connections on the socket. */ + for (n = 0; n < num_socks; n++) { + sock = socks[n]; + if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { + error("listen: %.100s", strerror(errno)); + close(sock); + return -1; + } + } + + /* Allocate a channel for each socket. */ + *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); + for (n = 0; n < num_socks; n++) { + sock = socks[n]; + nc = channel_new(ssh, "x11 listener", + SSH_CHANNEL_X11_LISTENER, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, + 0, "X11 inet listener", 1); + nc->single_connection = single_connection; + (*chanids)[n] = nc->self; + } + (*chanids)[n] = -1; + + /* Return the display number for the DISPLAY environment variable. */ + *display_numberp = display_number; + return 0; +} + +static int +connect_local_xsocket_path(const char *pathname) +{ + int sock; + struct sockaddr_un addr; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) + error("socket: %.100s", strerror(errno)); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) + return sock; + close(sock); + error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); + return -1; +} + +static int +connect_local_xsocket(u_int dnr) +{ + char buf[1024]; + snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); + return connect_local_xsocket_path(buf); +} + +#ifdef __APPLE__ +static int +is_path_to_xsocket(const char *display, char *path, size_t pathlen) +{ + struct stat sbuf; + + if (strlcpy(path, display, pathlen) >= pathlen) { + error("%s: display path too long", __func__); + return 0; + } + if (display[0] != '/') + return 0; + if (stat(path, &sbuf) == 0) { + return 1; + } else { + char *dot = strrchr(path, '.'); + if (dot != NULL) { + *dot = '\0'; + if (stat(path, &sbuf) == 0) { + return 1; + } + } + } + return 0; +} +#endif + +int +x11_connect_display(struct ssh *ssh) +{ + u_int display_number; + const char *display; + char buf[1024], *cp; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr, sock = 0; + + /* Try to open a socket for the local X server. */ + display = getenv("DISPLAY"); + if (!display) { + error("DISPLAY not set."); + return -1; + } + /* + * Now we decode the value of the DISPLAY variable and make a + * connection to the real X server. + */ + +#ifdef __APPLE__ + /* Check if display is a path to a socket (as set by launchd). */ + { + char path[PATH_MAX]; + + if (is_path_to_xsocket(display, path, sizeof(path))) { + debug("x11_connect_display: $DISPLAY is launchd"); + + /* Create a socket. */ + sock = connect_local_xsocket_path(path); + if (sock < 0) + return -1; + + /* OK, we now have a connection to the display. */ + return sock; + } + } +#endif + /* + * Check if it is a unix domain socket. Unix domain displays are in + * one of the following formats: unix:d[.s], :d[.s], ::d[.s] + */ + if (strncmp(display, "unix:", 5) == 0 || + display[0] == ':') { + /* Connect to the unix domain socket. */ + if (sscanf(strrchr(display, ':') + 1, "%u", + &display_number) != 1) { + error("Could not parse display number from DISPLAY: " + "%.100s", display); + return -1; + } + /* Create a socket. */ + sock = connect_local_xsocket(display_number); + if (sock < 0) + return -1; + + /* OK, we now have a connection to the display. */ + return sock; + } + /* + * Connect to an inet socket. The DISPLAY value is supposedly + * hostname:d[.s], where hostname may also be numeric IP address. + */ + strlcpy(buf, display, sizeof(buf)); + cp = strchr(buf, ':'); + if (!cp) { + error("Could not find ':' in DISPLAY: %.100s", display); + return -1; + } + *cp = 0; + /* + * buf now contains the host name. But first we parse the + * display number. + */ + if (sscanf(cp + 1, "%u", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + return -1; + } + + /* Look up the host address */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%u", 6000 + display_number); + if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host. (%s)", buf, + ssh_gai_strerror(gaierr)); + return -1; + } + for (ai = aitop; ai; ai = ai->ai_next) { + /* Create a socket. */ + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock == -1) { + debug2("socket: %.100s", strerror(errno)); + continue; + } + /* Connect it to the display. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { + debug2("connect %.100s port %u: %.100s", buf, + 6000 + display_number, strerror(errno)); + close(sock); + continue; + } + /* Success */ + break; + } + freeaddrinfo(aitop); + if (!ai) { + error("connect %.100s port %u: %.100s", buf, + 6000 + display_number, strerror(errno)); + return -1; + } + set_nodelay(sock); + return sock; +} + +/* + * Requests forwarding of X11 connections, generates fake authentication + * data, and enables authentication spoofing. + * This should be called in the client only. + */ +void +x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, + const char *disp, const char *proto, const char *data, int want_reply) +{ + struct ssh_channels *sc = ssh->chanctxt; + u_int data_len = (u_int) strlen(data) / 2; + u_int i, value; + const char *cp; + char *new_data; + int r, screen_number; + + if (sc->x11_saved_display == NULL) + sc->x11_saved_display = xstrdup(disp); + else if (strcmp(disp, sc->x11_saved_display) != 0) { + error("x11_request_forwarding_with_spoofing: different " + "$DISPLAY already forwarded"); + return; + } + + cp = strchr(disp, ':'); + if (cp) + cp = strchr(cp, '.'); + if (cp) + screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); + else + screen_number = 0; + + if (sc->x11_saved_proto == NULL) { + /* Save protocol name. */ + sc->x11_saved_proto = xstrdup(proto); + + /* Extract real authentication data. */ + sc->x11_saved_data = xmalloc(data_len); + for (i = 0; i < data_len; i++) { + if (sscanf(data + 2 * i, "%2x", &value) != 1) { + fatal("x11_request_forwarding: bad " + "authentication data: %.100s", data); + } + sc->x11_saved_data[i] = value; + } + sc->x11_saved_data_len = data_len; + + /* Generate fake data of the same length. */ + sc->x11_fake_data = xmalloc(data_len); + arc4random_buf(sc->x11_fake_data, data_len); + sc->x11_fake_data_len = data_len; + } + + /* Convert the fake data into hex. */ + new_data = tohex(sc->x11_fake_data, data_len); + + /* Send the request packet. */ + channel_request_start(ssh, client_session_id, "x11-req", want_reply); + if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ + (r = sshpkt_put_cstring(ssh, proto)) != 0 || + (r = sshpkt_put_cstring(ssh, new_data)) != 0 || + (r = sshpkt_put_u32(ssh, screen_number)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send x11-req"); + free(new_data); +} diff --git a/aosp/external/openssh/channels.h b/aosp/external/openssh/channels.h new file mode 100644 index 0000000000000000000000000000000000000000..dfb82f8ce2627f23854ff8227537b72c6f9cf648 --- /dev/null +++ b/aosp/external/openssh/channels.h @@ -0,0 +1,383 @@ +/* $OpenBSD: channels.h,v 1.142 2022/03/30 21:10:25 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CHANNEL_H +#define CHANNEL_H + +/* Definitions for channel types. */ +#define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ +#define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ +#define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ +#define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ +#define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ +#define SSH_CHANNEL_AUTH_SOCKET 6 /* authentication socket */ +#define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ +#define SSH_CHANNEL_LARVAL 10 /* larval session */ +#define SSH_CHANNEL_RPORT_LISTENER 11 /* Listening to a R-style port */ +#define SSH_CHANNEL_CONNECTING 12 +#define SSH_CHANNEL_DYNAMIC 13 +#define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */ +#define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */ +#define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux client */ +#define SSH_CHANNEL_ABANDONED 17 /* Abandoned session, eg mux */ +#define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */ +#define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */ +#define SSH_CHANNEL_MUX_PROXY 20 /* proxy channel for mux-client */ +#define SSH_CHANNEL_RDYNAMIC_OPEN 21 /* reverse SOCKS, parsing request */ +#define SSH_CHANNEL_RDYNAMIC_FINISH 22 /* reverse SOCKS, finishing connect */ +#define SSH_CHANNEL_MAX_TYPE 23 + +#define CHANNEL_CANCEL_PORT_STATIC -1 + +/* nonblocking flags for channel_new */ +#define CHANNEL_NONBLOCK_LEAVE 0 /* don't modify non-blocking state */ +#define CHANNEL_NONBLOCK_SET 1 /* set non-blocking state */ +#define CHANNEL_NONBLOCK_STDIO 2 /* set non-blocking and restore on close */ + +/* c->restore_block mask flags */ +#define CHANNEL_RESTORE_RFD 0x01 +#define CHANNEL_RESTORE_WFD 0x02 +#define CHANNEL_RESTORE_EFD 0x04 + +/* TCP forwarding */ +#define FORWARD_DENY 0 +#define FORWARD_REMOTE (1) +#define FORWARD_LOCAL (1<<1) +#define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL) + +#define FORWARD_ADM 0x100 +#define FORWARD_USER 0x101 + +struct ssh; +struct Channel; +typedef struct Channel Channel; +struct fwd_perm_list; + +typedef void channel_open_fn(struct ssh *, int, int, void *); +typedef void channel_callback_fn(struct ssh *, int, void *); +typedef int channel_infilter_fn(struct ssh *, struct Channel *, char *, int); +typedef void channel_filter_cleanup_fn(struct ssh *, int, void *); +typedef u_char *channel_outfilter_fn(struct ssh *, struct Channel *, + u_char **, size_t *); + +/* Channel success/failure callbacks */ +typedef void channel_confirm_cb(struct ssh *, int, struct Channel *, void *); +typedef void channel_confirm_abandon_cb(struct ssh *, struct Channel *, void *); +struct channel_confirm { + TAILQ_ENTRY(channel_confirm) entry; + channel_confirm_cb *cb; + channel_confirm_abandon_cb *abandon_cb; + void *ctx; +}; +TAILQ_HEAD(channel_confirms, channel_confirm); + +/* Context for non-blocking connects */ +struct channel_connect { + char *host; + int port; + struct addrinfo *ai, *aitop; +}; + +/* Callbacks for mux channels back into client-specific code */ +typedef int mux_callback_fn(struct ssh *, struct Channel *); + +/* + * NB. channel IDs on the wire and in c->remote_id are uint32, but local + * channel IDs (e.g. c->self) only ever use the int32 subset of this range, + * because we use local channel ID -1 for housekeeping. Remote channels have + * a dedicated "have_remote_id" flag to indicate their validity. + */ + +struct Channel { + int type; /* channel type/state */ + + int self; /* my own channel identifier */ + uint32_t remote_id; /* channel identifier for remote peer */ + int have_remote_id; /* non-zero if remote_id is valid */ + + u_int istate; /* input from channel (state of receive half) */ + u_int ostate; /* output to channel (state of transmit half) */ + int flags; /* close sent/rcvd */ + int rfd; /* read fd */ + int wfd; /* write fd */ + int efd; /* extended fd */ + int sock; /* sock fd */ + u_int io_want; /* bitmask of SSH_CHAN_IO_* */ + u_int io_ready; /* bitmask of SSH_CHAN_IO_* */ + int pfds[4]; /* pollfd entries for rfd/wfd/efd/sock */ + int ctl_chan; /* control channel (multiplexed connections) */ + int isatty; /* rfd is a tty */ +#ifdef _AIX + int wfd_isatty; /* wfd is a tty */ +#endif + int client_tty; /* (client) TTY has been requested */ + int force_drain; /* force close on iEOF */ + time_t notbefore; /* Pause IO until deadline (time_t) */ + int delayed; /* post-IO handlers for newly created + * channels are delayed until the first call + * to a matching pre-IO handler. + * this way post-IO handlers are not + * accidentally called if a FD gets reused */ + int restore_block; /* fd mask to restore blocking status */ + struct sshbuf *input; /* data read from socket, to be sent over + * encrypted connection */ + struct sshbuf *output; /* data received over encrypted connection for + * send on socket */ + struct sshbuf *extended; + + char *path; + /* path for unix domain sockets, or host name for forwards */ + int listening_port; /* port being listened for forwards */ + char *listening_addr; /* addr being listened for forwards */ + int host_port; /* remote port to connect for forwards */ + char *remote_name; /* remote hostname */ + + u_int remote_window; + u_int remote_maxpacket; + u_int local_window; + u_int local_window_max; + u_int local_consumed; + u_int local_maxpacket; + int extended_usage; + int single_connection; + + char *ctype; /* type */ + + /* callback */ + channel_open_fn *open_confirm; + void *open_confirm_ctx; + channel_callback_fn *detach_user; + int detach_close; + struct channel_confirms status_confirms; + + /* filter */ + channel_infilter_fn *input_filter; + channel_outfilter_fn *output_filter; + void *filter_ctx; + channel_filter_cleanup_fn *filter_cleanup; + + /* keep boundaries */ + int datagram; + + /* non-blocking connect */ + /* XXX make this a pointer so the structure can be opaque */ + struct channel_connect connect_ctx; + + /* multiplexing protocol hook, called for each packet received */ + mux_callback_fn *mux_rcb; + void *mux_ctx; + int mux_pause; + int mux_downstream_id; +}; + +#define CHAN_EXTENDED_IGNORE 0 +#define CHAN_EXTENDED_READ 1 +#define CHAN_EXTENDED_WRITE 2 + +/* default window/packet sizes for tcp/x11-fwd-channel */ +#define CHAN_SES_PACKET_DEFAULT (32*1024) +#define CHAN_SES_WINDOW_DEFAULT (64*CHAN_SES_PACKET_DEFAULT) +#define CHAN_TCP_PACKET_DEFAULT (32*1024) +#define CHAN_TCP_WINDOW_DEFAULT (64*CHAN_TCP_PACKET_DEFAULT) +#define CHAN_X11_PACKET_DEFAULT (16*1024) +#define CHAN_X11_WINDOW_DEFAULT (4*CHAN_X11_PACKET_DEFAULT) + +/* possible input states */ +#define CHAN_INPUT_OPEN 0 +#define CHAN_INPUT_WAIT_DRAIN 1 +#define CHAN_INPUT_WAIT_OCLOSE 2 +#define CHAN_INPUT_CLOSED 3 + +/* possible output states */ +#define CHAN_OUTPUT_OPEN 0 +#define CHAN_OUTPUT_WAIT_DRAIN 1 +#define CHAN_OUTPUT_WAIT_IEOF 2 +#define CHAN_OUTPUT_CLOSED 3 + +#define CHAN_CLOSE_SENT 0x01 +#define CHAN_CLOSE_RCVD 0x02 +#define CHAN_EOF_SENT 0x04 +#define CHAN_EOF_RCVD 0x08 +#define CHAN_LOCAL 0x10 + +/* file descriptor events */ +#define SSH_CHAN_IO_RFD 0x01 +#define SSH_CHAN_IO_WFD 0x02 +#define SSH_CHAN_IO_EFD_R 0x04 +#define SSH_CHAN_IO_EFD_W 0x08 +#define SSH_CHAN_IO_EFD (SSH_CHAN_IO_EFD_R|SSH_CHAN_IO_EFD_W) +#define SSH_CHAN_IO_SOCK_R 0x10 +#define SSH_CHAN_IO_SOCK_W 0x20 +#define SSH_CHAN_IO_SOCK (SSH_CHAN_IO_SOCK_R|SSH_CHAN_IO_SOCK_W) + +/* Read buffer size */ +#define CHAN_RBUF (16*1024) + +/* Maximum size for direct reads to buffers */ +#define CHANNEL_MAX_READ CHAN_SES_PACKET_DEFAULT + +/* Maximum channel input buffer size */ +#define CHAN_INPUT_MAX (16*1024*1024) + +/* Hard limit on number of channels */ +#define CHANNELS_MAX_CHANNELS (16*1024) + +/* check whether 'efd' is still in use */ +#define CHANNEL_EFD_INPUT_ACTIVE(c) \ + (c->extended_usage == CHAN_EXTENDED_READ && \ + (c->efd != -1 || \ + sshbuf_len(c->extended) > 0)) +#define CHANNEL_EFD_OUTPUT_ACTIVE(c) \ + (c->extended_usage == CHAN_EXTENDED_WRITE && \ + c->efd != -1 && (!(c->flags & (CHAN_EOF_RCVD|CHAN_CLOSE_RCVD)) || \ + sshbuf_len(c->extended) > 0)) + +/* Add channel management structures to SSH transport instance */ +void channel_init_channels(struct ssh *ssh); + +/* channel management */ + +Channel *channel_by_id(struct ssh *, int); +Channel *channel_by_remote_id(struct ssh *, u_int); +Channel *channel_lookup(struct ssh *, int); +Channel *channel_new(struct ssh *, char *, int, int, int, int, + u_int, u_int, int, char *, int); +void channel_set_fds(struct ssh *, int, int, int, int, int, + int, int, u_int); +void channel_free(struct ssh *, Channel *); +void channel_free_all(struct ssh *); +void channel_stop_listening(struct ssh *); + +void channel_send_open(struct ssh *, int); +void channel_request_start(struct ssh *, int, char *, int); +void channel_register_cleanup(struct ssh *, int, + channel_callback_fn *, int); +void channel_register_open_confirm(struct ssh *, int, + channel_open_fn *, void *); +void channel_register_filter(struct ssh *, int, channel_infilter_fn *, + channel_outfilter_fn *, channel_filter_cleanup_fn *, void *); +void channel_register_status_confirm(struct ssh *, int, + channel_confirm_cb *, channel_confirm_abandon_cb *, void *); +void channel_cancel_cleanup(struct ssh *, int); +int channel_close_fd(struct ssh *, Channel *, int *); +void channel_send_window_changes(struct ssh *); + +/* mux proxy support */ + +int channel_proxy_downstream(struct ssh *, Channel *mc); +int channel_proxy_upstream(Channel *, int, u_int32_t, struct ssh *); + +/* protocol handler */ + +int channel_input_data(int, u_int32_t, struct ssh *); +int channel_input_extended_data(int, u_int32_t, struct ssh *); +int channel_input_ieof(int, u_int32_t, struct ssh *); +int channel_input_oclose(int, u_int32_t, struct ssh *); +int channel_input_open_confirmation(int, u_int32_t, struct ssh *); +int channel_input_open_failure(int, u_int32_t, struct ssh *); +int channel_input_port_open(int, u_int32_t, struct ssh *); +int channel_input_window_adjust(int, u_int32_t, struct ssh *); +int channel_input_status_confirm(int, u_int32_t, struct ssh *); + +/* file descriptor handling (read/write) */ +struct pollfd; + +void channel_prepare_poll(struct ssh *, struct pollfd **, + u_int *, u_int *, u_int, time_t *); +void channel_after_poll(struct ssh *, struct pollfd *, u_int); +void channel_output_poll(struct ssh *); + +int channel_not_very_much_buffered_data(struct ssh *); +void channel_close_all(struct ssh *); +int channel_still_open(struct ssh *); +const char *channel_format_extended_usage(const Channel *); +char *channel_open_message(struct ssh *); +int channel_find_open(struct ssh *); + +/* tcp forwarding */ +struct Forward; +struct ForwardOptions; +void channel_set_af(struct ssh *, int af); +void channel_permit_all(struct ssh *, int); +void channel_add_permission(struct ssh *, int, int, char *, int); +void channel_clear_permission(struct ssh *, int, int); +void channel_disable_admin(struct ssh *, int); +void channel_update_permission(struct ssh *, int, int); +Channel *channel_connect_to_port(struct ssh *, const char *, u_short, + char *, char *, int *, const char **); +Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *); +Channel *channel_connect_stdio_fwd(struct ssh *, const char*, + u_short, int, int, int); +Channel *channel_connect_by_listen_address(struct ssh *, const char *, + u_short, char *, char *); +Channel *channel_connect_by_listen_path(struct ssh *, const char *, + char *, char *); +int channel_request_remote_forwarding(struct ssh *, struct Forward *); +int channel_setup_local_fwd_listener(struct ssh *, struct Forward *, + struct ForwardOptions *); +int channel_request_rforward_cancel(struct ssh *, struct Forward *); +int channel_setup_remote_fwd_listener(struct ssh *, struct Forward *, + int *, struct ForwardOptions *); +int channel_cancel_rport_listener(struct ssh *, struct Forward *); +int channel_cancel_lport_listener(struct ssh *, struct Forward *, + int, struct ForwardOptions *); +int permitopen_port(const char *); + +/* x11 forwarding */ + +void channel_set_x11_refuse_time(struct ssh *, u_int); +int x11_connect_display(struct ssh *); +int x11_create_display_inet(struct ssh *, int, int, int, u_int *, int **); +void x11_request_forwarding_with_spoofing(struct ssh *, int, + const char *, const char *, const char *, int); + +/* channel close */ + +int chan_is_dead(struct ssh *, Channel *, int); +void chan_mark_dead(struct ssh *, Channel *); + +/* channel events */ + +void chan_rcvd_oclose(struct ssh *, Channel *); +void chan_rcvd_eow(struct ssh *, Channel *); +void chan_read_failed(struct ssh *, Channel *); +void chan_ibuf_empty(struct ssh *, Channel *); +void chan_rcvd_ieof(struct ssh *, Channel *); +void chan_write_failed(struct ssh *, Channel *); +void chan_obuf_empty(struct ssh *, Channel *); + +#endif diff --git a/aosp/external/openssh/cipher-aes.c b/aosp/external/openssh/cipher-aes.c new file mode 100644 index 0000000000000000000000000000000000000000..8b101727284302278cd87aee49d1fb847a1414c4 --- /dev/null +++ b/aosp/external/openssh/cipher-aes.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2003 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +/* compatibility with old or broken OpenSSL versions */ +#include "openbsd-compat/openssl-compat.h" + +#ifdef USE_BUILTIN_RIJNDAEL +#include + +#include + +#include +#include + +#include "rijndael.h" +#include "xmalloc.h" +#include "log.h" + +#define RIJNDAEL_BLOCKSIZE 16 +struct ssh_rijndael_ctx +{ + rijndael_ctx r_ctx; + u_char r_iv[RIJNDAEL_BLOCKSIZE]; +}; + +static int +ssh_rijndael_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, + int enc) +{ + struct ssh_rijndael_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { + c = xmalloc(sizeof(*c)); + EVP_CIPHER_CTX_set_app_data(ctx, c); + } + if (key != NULL) { + if (enc == -1) + enc = ctx->encrypt; + rijndael_set_key(&c->r_ctx, (u_char *)key, + 8*EVP_CIPHER_CTX_key_length(ctx), enc); + } + if (iv != NULL) + memcpy(c->r_iv, iv, RIJNDAEL_BLOCKSIZE); + return (1); +} + +static int +ssh_rijndael_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, + LIBCRYPTO_EVP_INL_TYPE len) +{ + struct ssh_rijndael_ctx *c; + u_char buf[RIJNDAEL_BLOCKSIZE]; + u_char *cprev, *cnow, *plain, *ivp; + int i, j, blocks = len / RIJNDAEL_BLOCKSIZE; + + if (len == 0) + return (1); + if (len % RIJNDAEL_BLOCKSIZE) + fatal("ssh_rijndael_cbc: bad len %d", len); + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { + error("ssh_rijndael_cbc: no context"); + return (0); + } + if (ctx->encrypt) { + cnow = dest; + plain = (u_char *)src; + cprev = c->r_iv; + for (i = 0; i < blocks; i++, plain+=RIJNDAEL_BLOCKSIZE, + cnow+=RIJNDAEL_BLOCKSIZE) { + for (j = 0; j < RIJNDAEL_BLOCKSIZE; j++) + buf[j] = plain[j] ^ cprev[j]; + rijndael_encrypt(&c->r_ctx, buf, cnow); + cprev = cnow; + } + memcpy(c->r_iv, cprev, RIJNDAEL_BLOCKSIZE); + } else { + cnow = (u_char *) (src+len-RIJNDAEL_BLOCKSIZE); + plain = dest+len-RIJNDAEL_BLOCKSIZE; + + memcpy(buf, cnow, RIJNDAEL_BLOCKSIZE); + for (i = blocks; i > 0; i--, cnow-=RIJNDAEL_BLOCKSIZE, + plain-=RIJNDAEL_BLOCKSIZE) { + rijndael_decrypt(&c->r_ctx, cnow, plain); + ivp = (i == 1) ? c->r_iv : cnow-RIJNDAEL_BLOCKSIZE; + for (j = 0; j < RIJNDAEL_BLOCKSIZE; j++) + plain[j] ^= ivp[j]; + } + memcpy(c->r_iv, buf, RIJNDAEL_BLOCKSIZE); + } + return (1); +} + +static int +ssh_rijndael_cleanup(EVP_CIPHER_CTX *ctx) +{ + struct ssh_rijndael_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) { + memset(c, 0, sizeof(*c)); + free(c); + EVP_CIPHER_CTX_set_app_data(ctx, NULL); + } + return (1); +} + +void +ssh_rijndael_iv(EVP_CIPHER_CTX *evp, int doset, u_char * iv, u_int len) +{ + struct ssh_rijndael_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(evp)) == NULL) + fatal("ssh_rijndael_iv: no context"); + if (doset) + memcpy(c->r_iv, iv, len); + else + memcpy(iv, c->r_iv, len); +} + +const EVP_CIPHER * +evp_rijndael(void) +{ + static EVP_CIPHER rijndal_cbc; + + memset(&rijndal_cbc, 0, sizeof(EVP_CIPHER)); + rijndal_cbc.nid = NID_undef; + rijndal_cbc.block_size = RIJNDAEL_BLOCKSIZE; + rijndal_cbc.iv_len = RIJNDAEL_BLOCKSIZE; + rijndal_cbc.key_len = 16; + rijndal_cbc.init = ssh_rijndael_init; + rijndal_cbc.cleanup = ssh_rijndael_cleanup; + rijndal_cbc.do_cipher = ssh_rijndael_cbc; +#ifndef SSH_OLD_EVP + rijndal_cbc.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | + EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; +#endif + return (&rijndal_cbc); +} +#endif /* USE_BUILTIN_RIJNDAEL */ diff --git a/aosp/external/openssh/cipher-aesctr.c b/aosp/external/openssh/cipher-aesctr.c new file mode 100644 index 0000000000000000000000000000000000000000..eed95c3e6e3c22fa37c10e9ac5f24821581b0283 --- /dev/null +++ b/aosp/external/openssh/cipher-aesctr.c @@ -0,0 +1,83 @@ +/* $OpenBSD: cipher-aesctr.c,v 1.2 2015/01/14 10:24:42 markus Exp $ */ +/* + * Copyright (c) 2003 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#ifndef WITH_OPENSSL + +#include "cipher-aesctr.h" + +/* + * increment counter 'ctr', + * the counter is of size 'len' bytes and stored in network-byte-order. + * (LSB at ctr[len-1], MSB at ctr[0]) + */ +static inline void +aesctr_inc(u8 *ctr, u32 len) +{ + ssize_t i; + +#ifndef CONSTANT_TIME_INCREMENT + for (i = len - 1; i >= 0; i--) + if (++ctr[i]) /* continue on overflow */ + return; +#else + u8 x, add = 1; + + for (i = len - 1; i >= 0; i--) { + ctr[i] += add; + /* constant time for: x = ctr[i] ? 1 : 0 */ + x = ctr[i]; + x = (x | (x >> 4)) & 0xf; + x = (x | (x >> 2)) & 0x3; + x = (x | (x >> 1)) & 0x1; + add *= (x^1); + } +#endif +} + +void +aesctr_keysetup(aesctr_ctx *x,const u8 *k,u32 kbits,u32 ivbits) +{ + x->rounds = rijndaelKeySetupEnc(x->ek, k, kbits); +} + +void +aesctr_ivsetup(aesctr_ctx *x,const u8 *iv) +{ + memcpy(x->ctr, iv, AES_BLOCK_SIZE); +} + +void +aesctr_encrypt_bytes(aesctr_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 n = 0; + u8 buf[AES_BLOCK_SIZE]; + + while ((bytes--) > 0) { + if (n == 0) { + rijndaelEncrypt(x->ek, x->rounds, x->ctr, buf); + aesctr_inc(x->ctr, AES_BLOCK_SIZE); + } + *(c++) = *(m++) ^ buf[n]; + n = (n + 1) % AES_BLOCK_SIZE; + } +} +#endif /* !WITH_OPENSSL */ diff --git a/aosp/external/openssh/cipher-aesctr.h b/aosp/external/openssh/cipher-aesctr.h new file mode 100644 index 0000000000000000000000000000000000000000..85d55bba291e18368cce27009e29dd43123fee6d --- /dev/null +++ b/aosp/external/openssh/cipher-aesctr.h @@ -0,0 +1,35 @@ +/* $OpenBSD: cipher-aesctr.h,v 1.1 2014/04/29 15:39:33 markus Exp $ */ +/* + * Copyright (c) 2014 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef OPENSSH_AESCTR_H +#define OPENSSH_AESCTR_H + +#include "rijndael.h" + +#define AES_BLOCK_SIZE 16 + +typedef struct aesctr_ctx { + int rounds; /* keylen-dependent #rounds */ + u32 ek[4*(AES_MAXROUNDS + 1)]; /* encrypt key schedule */ + u8 ctr[AES_BLOCK_SIZE]; /* counter */ +} aesctr_ctx; + +void aesctr_keysetup(aesctr_ctx *x,const u8 *k,u32 kbits,u32 ivbits); +void aesctr_ivsetup(aesctr_ctx *x,const u8 *iv); +void aesctr_encrypt_bytes(aesctr_ctx *x,const u8 *m,u8 *c,u32 bytes); + +#endif diff --git a/aosp/external/openssh/cipher-chachapoly-libcrypto.c b/aosp/external/openssh/cipher-chachapoly-libcrypto.c new file mode 100644 index 0000000000000000000000000000000000000000..719f9c843d63f007d08cfdd744d08a7ed5007d17 --- /dev/null +++ b/aosp/external/openssh/cipher-chachapoly-libcrypto.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2013 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: cipher-chachapoly-libcrypto.c,v 1.1 2020/04/03 04:32:21 djm Exp $ */ + +#include "includes.h" +#ifdef WITH_OPENSSL +#include "openbsd-compat/openssl-compat.h" +#endif + +#if defined(HAVE_EVP_CHACHA20) && !defined(HAVE_BROKEN_CHACHA20) + +#include +#include /* needed for log.h */ +#include +#include /* needed for misc.h */ + +#include + +#include "log.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "cipher-chachapoly.h" + +struct chachapoly_ctx { + EVP_CIPHER_CTX *main_evp, *header_evp; +}; + +struct chachapoly_ctx * +chachapoly_new(const u_char *key, u_int keylen) +{ + struct chachapoly_ctx *ctx; + + if (keylen != (32 + 32)) /* 2 x 256 bit keys */ + return NULL; + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) + return NULL; + if ((ctx->main_evp = EVP_CIPHER_CTX_new()) == NULL || + (ctx->header_evp = EVP_CIPHER_CTX_new()) == NULL) + goto fail; + if (!EVP_CipherInit(ctx->main_evp, EVP_chacha20(), key, NULL, 1)) + goto fail; + if (!EVP_CipherInit(ctx->header_evp, EVP_chacha20(), key + 32, NULL, 1)) + goto fail; + if (EVP_CIPHER_CTX_iv_length(ctx->header_evp) != 16) + goto fail; + return ctx; + fail: + chachapoly_free(ctx); + return NULL; +} + +void +chachapoly_free(struct chachapoly_ctx *cpctx) +{ + if (cpctx == NULL) + return; + EVP_CIPHER_CTX_free(cpctx->main_evp); + EVP_CIPHER_CTX_free(cpctx->header_evp); + freezero(cpctx, sizeof(*cpctx)); +} + +/* + * chachapoly_crypt() operates as following: + * En/decrypt with header key 'aadlen' bytes from 'src', storing result + * to 'dest'. The ciphertext here is treated as additional authenticated + * data for MAC calculation. + * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use + * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication + * tag. This tag is written on encryption and verified on decryption. + */ +int +chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, + const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt) +{ + u_char seqbuf[16]; /* layout: u64 counter || u64 seqno */ + int r = SSH_ERR_INTERNAL_ERROR; + u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; + + /* + * Run ChaCha20 once to generate the Poly1305 key. The IV is the + * packet sequence number. + */ + memset(seqbuf, 0, sizeof(seqbuf)); + POKE_U64(seqbuf + 8, seqnr); + memset(poly_key, 0, sizeof(poly_key)); + if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) || + EVP_Cipher(ctx->main_evp, poly_key, + poly_key, sizeof(poly_key)) < 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + /* If decrypting, check tag before anything else */ + if (!do_encrypt) { + const u_char *tag = src + aadlen + len; + + poly1305_auth(expected_tag, src, aadlen + len, poly_key); + if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { + r = SSH_ERR_MAC_INVALID; + goto out; + } + } + + /* Crypt additional data */ + if (aadlen) { + if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 1) || + EVP_Cipher(ctx->header_evp, dest, src, aadlen) < 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + } + + /* Set Chacha's block counter to 1 */ + seqbuf[0] = 1; + if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, 1) || + EVP_Cipher(ctx->main_evp, dest + aadlen, src + aadlen, len) < 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + /* If encrypting, calculate and append tag */ + if (do_encrypt) { + poly1305_auth(dest + aadlen + len, dest, aadlen + len, + poly_key); + } + r = 0; + out: + explicit_bzero(expected_tag, sizeof(expected_tag)); + explicit_bzero(seqbuf, sizeof(seqbuf)); + explicit_bzero(poly_key, sizeof(poly_key)); + return r; +} + +/* Decrypt and extract the encrypted packet length */ +int +chachapoly_get_length(struct chachapoly_ctx *ctx, + u_int *plenp, u_int seqnr, const u_char *cp, u_int len) +{ + u_char buf[4], seqbuf[16]; + + if (len < 4) + return SSH_ERR_MESSAGE_INCOMPLETE; + memset(seqbuf, 0, sizeof(seqbuf)); + POKE_U64(seqbuf + 8, seqnr); + if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 0)) + return SSH_ERR_LIBCRYPTO_ERROR; + if (EVP_Cipher(ctx->header_evp, buf, (u_char *)cp, sizeof(buf)) < 0) + return SSH_ERR_LIBCRYPTO_ERROR; + *plenp = PEEK_U32(buf); + return 0; +} +#endif /* defined(HAVE_EVP_CHACHA20) && !defined(HAVE_BROKEN_CHACHA20) */ diff --git a/aosp/external/openssh/cipher-chachapoly.c b/aosp/external/openssh/cipher-chachapoly.c new file mode 100644 index 0000000000000000000000000000000000000000..716f8d426a068a1ae0598cec6022fa54a84e78c9 --- /dev/null +++ b/aosp/external/openssh/cipher-chachapoly.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: cipher-chachapoly.c,v 1.9 2020/04/03 04:27:03 djm Exp $ */ + +#include "includes.h" +#ifdef WITH_OPENSSL +#include "openbsd-compat/openssl-compat.h" +#endif + +#if !defined(HAVE_EVP_CHACHA20) || defined(HAVE_BROKEN_CHACHA20) + +#include +#include /* needed for log.h */ +#include +#include /* needed for misc.h */ + +#include "log.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "cipher-chachapoly.h" + +struct chachapoly_ctx { + struct chacha_ctx main_ctx, header_ctx; +}; + +struct chachapoly_ctx * +chachapoly_new(const u_char *key, u_int keylen) +{ + struct chachapoly_ctx *ctx; + + if (keylen != (32 + 32)) /* 2 x 256 bit keys */ + return NULL; + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) + return NULL; + chacha_keysetup(&ctx->main_ctx, key, 256); + chacha_keysetup(&ctx->header_ctx, key + 32, 256); + return ctx; +} + +void +chachapoly_free(struct chachapoly_ctx *cpctx) +{ + freezero(cpctx, sizeof(*cpctx)); +} + +/* + * chachapoly_crypt() operates as following: + * En/decrypt with header key 'aadlen' bytes from 'src', storing result + * to 'dest'. The ciphertext here is treated as additional authenticated + * data for MAC calculation. + * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use + * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication + * tag. This tag is written on encryption and verified on decryption. + */ +int +chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest, + const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt) +{ + u_char seqbuf[8]; + const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ + u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; + int r = SSH_ERR_INTERNAL_ERROR; + + /* + * Run ChaCha20 once to generate the Poly1305 key. The IV is the + * packet sequence number. + */ + memset(poly_key, 0, sizeof(poly_key)); + POKE_U64(seqbuf, seqnr); + chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); + chacha_encrypt_bytes(&ctx->main_ctx, + poly_key, poly_key, sizeof(poly_key)); + + /* If decrypting, check tag before anything else */ + if (!do_encrypt) { + const u_char *tag = src + aadlen + len; + + poly1305_auth(expected_tag, src, aadlen + len, poly_key); + if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) { + r = SSH_ERR_MAC_INVALID; + goto out; + } + } + + /* Crypt additional data */ + if (aadlen) { + chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL); + chacha_encrypt_bytes(&ctx->header_ctx, src, dest, aadlen); + } + + /* Set Chacha's block counter to 1 */ + chacha_ivsetup(&ctx->main_ctx, seqbuf, one); + chacha_encrypt_bytes(&ctx->main_ctx, src + aadlen, + dest + aadlen, len); + + /* If encrypting, calculate and append tag */ + if (do_encrypt) { + poly1305_auth(dest + aadlen + len, dest, aadlen + len, + poly_key); + } + r = 0; + out: + explicit_bzero(expected_tag, sizeof(expected_tag)); + explicit_bzero(seqbuf, sizeof(seqbuf)); + explicit_bzero(poly_key, sizeof(poly_key)); + return r; +} + +/* Decrypt and extract the encrypted packet length */ +int +chachapoly_get_length(struct chachapoly_ctx *ctx, + u_int *plenp, u_int seqnr, const u_char *cp, u_int len) +{ + u_char buf[4], seqbuf[8]; + + if (len < 4) + return SSH_ERR_MESSAGE_INCOMPLETE; + POKE_U64(seqbuf, seqnr); + chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL); + chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4); + *plenp = PEEK_U32(buf); + return 0; +} + +#endif /* !defined(HAVE_EVP_CHACHA20) || defined(HAVE_BROKEN_CHACHA20) */ diff --git a/aosp/external/openssh/cipher-chachapoly.h b/aosp/external/openssh/cipher-chachapoly.h new file mode 100644 index 0000000000000000000000000000000000000000..026d2de93a3a82aab95730ba94546f39516c6fb2 --- /dev/null +++ b/aosp/external/openssh/cipher-chachapoly.h @@ -0,0 +1,40 @@ +/* $OpenBSD: cipher-chachapoly.h,v 1.5 2020/04/03 04:27:03 djm Exp $ */ + +/* + * Copyright (c) Damien Miller 2013 + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef CHACHA_POLY_AEAD_H +#define CHACHA_POLY_AEAD_H + +#include +#include "chacha.h" +#include "poly1305.h" + +#define CHACHA_KEYLEN 32 /* Only 256 bit keys used here */ + +struct chachapoly_ctx; + +struct chachapoly_ctx *chachapoly_new(const u_char *key, u_int keylen) + __attribute__((__bounded__(__buffer__, 1, 2))); +void chachapoly_free(struct chachapoly_ctx *cpctx); + +int chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr, + u_char *dest, const u_char *src, u_int len, u_int aadlen, u_int authlen, + int do_encrypt); +int chachapoly_get_length(struct chachapoly_ctx *cpctx, + u_int *plenp, u_int seqnr, const u_char *cp, u_int len) + __attribute__((__bounded__(__buffer__, 4, 5))); + +#endif /* CHACHA_POLY_AEAD_H */ diff --git a/aosp/external/openssh/cipher-ctr.c b/aosp/external/openssh/cipher-ctr.c new file mode 100644 index 0000000000000000000000000000000000000000..32771f28743be2b853566224aeeee814ca26df7f --- /dev/null +++ b/aosp/external/openssh/cipher-ctr.c @@ -0,0 +1,146 @@ +/* $OpenBSD: cipher-ctr.c,v 1.11 2010/10/01 23:05:32 djm Exp $ */ +/* + * Copyright (c) 2003 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "includes.h" + +#if defined(WITH_OPENSSL) && !defined(OPENSSL_HAVE_EVPCTR) +#include + +#include +#include + +#include + +#include "xmalloc.h" +#include "log.h" + +/* compatibility with old or broken OpenSSL versions */ +#include "openbsd-compat/openssl-compat.h" + +#ifndef USE_BUILTIN_RIJNDAEL +#include +#endif + +struct ssh_aes_ctr_ctx +{ + AES_KEY aes_ctx; + u_char aes_counter[AES_BLOCK_SIZE]; +}; + +/* + * increment counter 'ctr', + * the counter is of size 'len' bytes and stored in network-byte-order. + * (LSB at ctr[len-1], MSB at ctr[0]) + */ +static void +ssh_ctr_inc(u_char *ctr, size_t len) +{ + int i; + + for (i = len - 1; i >= 0; i--) + if (++ctr[i]) /* continue on overflow */ + return; +} + +static int +ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, + LIBCRYPTO_EVP_INL_TYPE len) +{ + struct ssh_aes_ctr_ctx *c; + size_t n = 0; + u_char buf[AES_BLOCK_SIZE]; + + if (len == 0) + return (1); + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) + return (0); + + while ((len--) > 0) { + if (n == 0) { + AES_encrypt(c->aes_counter, buf, &c->aes_ctx); + ssh_ctr_inc(c->aes_counter, AES_BLOCK_SIZE); + } + *(dest++) = *(src++) ^ buf[n]; + n = (n + 1) % AES_BLOCK_SIZE; + } + return (1); +} + +static int +ssh_aes_ctr_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, + int enc) +{ + struct ssh_aes_ctr_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { + c = xmalloc(sizeof(*c)); + EVP_CIPHER_CTX_set_app_data(ctx, c); + } + if (key != NULL) + AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8, + &c->aes_ctx); + if (iv != NULL) + memcpy(c->aes_counter, iv, AES_BLOCK_SIZE); + return (1); +} + +static int +ssh_aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) +{ + struct ssh_aes_ctr_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) { + memset(c, 0, sizeof(*c)); + free(c); + EVP_CIPHER_CTX_set_app_data(ctx, NULL); + } + return (1); +} + +void +ssh_aes_ctr_iv(EVP_CIPHER_CTX *evp, int doset, u_char * iv, size_t len) +{ + struct ssh_aes_ctr_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(evp)) == NULL) + fatal("ssh_aes_ctr_iv: no context"); + if (doset) + memcpy(c->aes_counter, iv, len); + else + memcpy(iv, c->aes_counter, len); +} + +const EVP_CIPHER * +evp_aes_128_ctr(void) +{ + static EVP_CIPHER aes_ctr; + + memset(&aes_ctr, 0, sizeof(EVP_CIPHER)); + aes_ctr.nid = NID_undef; + aes_ctr.block_size = AES_BLOCK_SIZE; + aes_ctr.iv_len = AES_BLOCK_SIZE; + aes_ctr.key_len = 16; + aes_ctr.init = ssh_aes_ctr_init; + aes_ctr.cleanup = ssh_aes_ctr_cleanup; + aes_ctr.do_cipher = ssh_aes_ctr; +#ifndef SSH_OLD_EVP + aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | + EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; +#endif + return (&aes_ctr); +} + +#endif /* defined(WITH_OPENSSL) && !defined(OPENSSL_HAVE_EVPCTR) */ diff --git a/aosp/external/openssh/cipher.c b/aosp/external/openssh/cipher.c new file mode 100644 index 0000000000000000000000000000000000000000..5b3a86d69219df807a5e78c576624d39788d18f8 --- /dev/null +++ b/aosp/external/openssh/cipher.c @@ -0,0 +1,539 @@ +/* $OpenBSD: cipher.c,v 1.119 2021/04/03 06:18:40 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999 Niels Provos. All rights reserved. + * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include + +#include "cipher.h" +#include "misc.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "digest.h" + +#include "openbsd-compat/openssl-compat.h" + +#ifndef WITH_OPENSSL +#define EVP_CIPHER_CTX void +#endif + +struct sshcipher_ctx { + int plaintext; + int encrypt; + EVP_CIPHER_CTX *evp; + struct chachapoly_ctx *cp_ctx; + struct aesctr_ctx ac_ctx; /* XXX union with evp? */ + const struct sshcipher *cipher; +}; + +struct sshcipher { + char *name; + u_int block_size; + u_int key_len; + u_int iv_len; /* defaults to block_size */ + u_int auth_len; + u_int flags; +#define CFLAG_CBC (1<<0) +#define CFLAG_CHACHAPOLY (1<<1) +#define CFLAG_AESCTR (1<<2) +#define CFLAG_NONE (1<<3) +#define CFLAG_INTERNAL CFLAG_NONE /* Don't use "none" for packets */ +#ifdef WITH_OPENSSL + const EVP_CIPHER *(*evptype)(void); +#else + void *ignored; +#endif +}; + +static const struct sshcipher ciphers[] = { +#ifdef WITH_OPENSSL +#ifndef OPENSSL_NO_DES + { "3des-cbc", 8, 24, 0, 0, CFLAG_CBC, EVP_des_ede3_cbc }, +#endif + { "aes128-cbc", 16, 16, 0, 0, CFLAG_CBC, EVP_aes_128_cbc }, + { "aes192-cbc", 16, 24, 0, 0, CFLAG_CBC, EVP_aes_192_cbc }, + { "aes256-cbc", 16, 32, 0, 0, CFLAG_CBC, EVP_aes_256_cbc }, + { "aes128-ctr", 16, 16, 0, 0, 0, EVP_aes_128_ctr }, + { "aes192-ctr", 16, 24, 0, 0, 0, EVP_aes_192_ctr }, + { "aes256-ctr", 16, 32, 0, 0, 0, EVP_aes_256_ctr }, +# ifdef OPENSSL_HAVE_EVPGCM + { "aes128-gcm@openssh.com", + 16, 16, 12, 16, 0, EVP_aes_128_gcm }, + { "aes256-gcm@openssh.com", + 16, 32, 12, 16, 0, EVP_aes_256_gcm }, +# endif /* OPENSSL_HAVE_EVPGCM */ +#else + { "aes128-ctr", 16, 16, 0, 0, CFLAG_AESCTR, NULL }, + { "aes192-ctr", 16, 24, 0, 0, CFLAG_AESCTR, NULL }, + { "aes256-ctr", 16, 32, 0, 0, CFLAG_AESCTR, NULL }, +#endif + { "chacha20-poly1305@openssh.com", + 8, 64, 0, 16, CFLAG_CHACHAPOLY, NULL }, + { "none", 8, 0, 0, 0, CFLAG_NONE, NULL }, + + { NULL, 0, 0, 0, 0, 0, NULL } +}; + +/*--*/ + +/* Returns a comma-separated list of supported ciphers. */ +char * +cipher_alg_list(char sep, int auth_only) +{ + char *tmp, *ret = NULL; + size_t nlen, rlen = 0; + const struct sshcipher *c; + + for (c = ciphers; c->name != NULL; c++) { + if ((c->flags & CFLAG_INTERNAL) != 0) + continue; + if (auth_only && c->auth_len == 0) + continue; + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(c->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; + memcpy(ret + rlen, c->name, nlen + 1); + rlen += nlen; + } + return ret; +} + +const char * +compression_alg_list(int compression) +{ +#ifdef WITH_ZLIB + return compression ? "zlib@openssh.com,zlib,none" : + "none,zlib@openssh.com,zlib"; +#else + return "none"; +#endif +} + +u_int +cipher_blocksize(const struct sshcipher *c) +{ + return (c->block_size); +} + +u_int +cipher_keylen(const struct sshcipher *c) +{ + return (c->key_len); +} + +u_int +cipher_seclen(const struct sshcipher *c) +{ + if (strcmp("3des-cbc", c->name) == 0) + return 14; + return cipher_keylen(c); +} + +u_int +cipher_authlen(const struct sshcipher *c) +{ + return (c->auth_len); +} + +u_int +cipher_ivlen(const struct sshcipher *c) +{ + /* + * Default is cipher block size, except for chacha20+poly1305 that + * needs no IV. XXX make iv_len == -1 default? + */ + return (c->iv_len != 0 || (c->flags & CFLAG_CHACHAPOLY) != 0) ? + c->iv_len : c->block_size; +} + +u_int +cipher_is_cbc(const struct sshcipher *c) +{ + return (c->flags & CFLAG_CBC) != 0; +} + +u_int +cipher_ctx_is_plaintext(struct sshcipher_ctx *cc) +{ + return cc->plaintext; +} + +const struct sshcipher * +cipher_by_name(const char *name) +{ + const struct sshcipher *c; + for (c = ciphers; c->name != NULL; c++) + if (strcmp(c->name, name) == 0) + return c; + return NULL; +} + +#define CIPHER_SEP "," +int +ciphers_valid(const char *names) +{ + const struct sshcipher *c; + char *cipher_list, *cp; + char *p; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + if ((cipher_list = cp = strdup(names)) == NULL) + return 0; + for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0'; + (p = strsep(&cp, CIPHER_SEP))) { + c = cipher_by_name(p); + if (c == NULL || (c->flags & CFLAG_INTERNAL) != 0) { + free(cipher_list); + return 0; + } + } + free(cipher_list); + return 1; +} + +const char * +cipher_warning_message(const struct sshcipher_ctx *cc) +{ + if (cc == NULL || cc->cipher == NULL) + return NULL; + /* XXX repurpose for CBC warning */ + return NULL; +} + +int +cipher_init(struct sshcipher_ctx **ccp, const struct sshcipher *cipher, + const u_char *key, u_int keylen, const u_char *iv, u_int ivlen, + int do_encrypt) +{ + struct sshcipher_ctx *cc = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; +#ifdef WITH_OPENSSL + const EVP_CIPHER *type; + int klen; +#endif + + *ccp = NULL; + if ((cc = calloc(sizeof(*cc), 1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + + cc->plaintext = (cipher->flags & CFLAG_NONE) != 0; + cc->encrypt = do_encrypt; + + if (keylen < cipher->key_len || + (iv != NULL && ivlen < cipher_ivlen(cipher))) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + + cc->cipher = cipher; + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { + cc->cp_ctx = chachapoly_new(key, keylen); + ret = cc->cp_ctx != NULL ? 0 : SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((cc->cipher->flags & CFLAG_NONE) != 0) { + ret = 0; + goto out; + } +#ifndef WITH_OPENSSL + if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { + aesctr_keysetup(&cc->ac_ctx, key, 8 * keylen, 8 * ivlen); + aesctr_ivsetup(&cc->ac_ctx, iv); + ret = 0; + goto out; + } + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; +#else /* WITH_OPENSSL */ + type = (*cipher->evptype)(); + if ((cc->evp = EVP_CIPHER_CTX_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EVP_CipherInit(cc->evp, type, NULL, (u_char *)iv, + (do_encrypt == CIPHER_ENCRYPT)) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (cipher_authlen(cipher) && + !EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_SET_IV_FIXED, + -1, (u_char *)iv)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + klen = EVP_CIPHER_CTX_key_length(cc->evp); + if (klen > 0 && keylen != (u_int)klen) { + if (EVP_CIPHER_CTX_set_key_length(cc->evp, keylen) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + } + if (EVP_CipherInit(cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + ret = 0; +#endif /* WITH_OPENSSL */ + out: + if (ret == 0) { + /* success */ + *ccp = cc; + } else { + if (cc != NULL) { +#ifdef WITH_OPENSSL + EVP_CIPHER_CTX_free(cc->evp); +#endif /* WITH_OPENSSL */ + freezero(cc, sizeof(*cc)); + } + } + return ret; +} + +/* + * cipher_crypt() operates as following: + * Copy 'aadlen' bytes (without en/decryption) from 'src' to 'dest'. + * These bytes are treated as additional authenticated data for + * authenticated encryption modes. + * En/Decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. + * Use 'authlen' bytes at offset 'len'+'aadlen' as the authentication tag. + * This tag is written on encryption and verified on decryption. + * Both 'aadlen' and 'authlen' can be set to 0. + */ +int +cipher_crypt(struct sshcipher_ctx *cc, u_int seqnr, u_char *dest, + const u_char *src, u_int len, u_int aadlen, u_int authlen) +{ + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { + return chachapoly_crypt(cc->cp_ctx, seqnr, dest, src, + len, aadlen, authlen, cc->encrypt); + } + if ((cc->cipher->flags & CFLAG_NONE) != 0) { + memcpy(dest, src, aadlen + len); + return 0; + } +#ifndef WITH_OPENSSL + if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { + if (aadlen) + memcpy(dest, src, aadlen); + aesctr_encrypt_bytes(&cc->ac_ctx, src + aadlen, + dest + aadlen, len); + return 0; + } + return SSH_ERR_INVALID_ARGUMENT; +#else + if (authlen) { + u_char lastiv[1]; + + if (authlen != cipher_authlen(cc->cipher)) + return SSH_ERR_INVALID_ARGUMENT; + /* increment IV */ + if (!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_IV_GEN, + 1, lastiv)) + return SSH_ERR_LIBCRYPTO_ERROR; + /* set tag on decyption */ + if (!cc->encrypt && + !EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_SET_TAG, + authlen, (u_char *)src + aadlen + len)) + return SSH_ERR_LIBCRYPTO_ERROR; + } + if (aadlen) { + if (authlen && + EVP_Cipher(cc->evp, NULL, (u_char *)src, aadlen) < 0) + return SSH_ERR_LIBCRYPTO_ERROR; + memcpy(dest, src, aadlen); + } + if (len % cc->cipher->block_size) + return SSH_ERR_INVALID_ARGUMENT; + if (EVP_Cipher(cc->evp, dest + aadlen, (u_char *)src + aadlen, + len) < 0) + return SSH_ERR_LIBCRYPTO_ERROR; + if (authlen) { + /* compute tag (on encrypt) or verify tag (on decrypt) */ + if (EVP_Cipher(cc->evp, NULL, NULL, 0) < 0) + return cc->encrypt ? + SSH_ERR_LIBCRYPTO_ERROR : SSH_ERR_MAC_INVALID; + if (cc->encrypt && + !EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_GET_TAG, + authlen, dest + aadlen + len)) + return SSH_ERR_LIBCRYPTO_ERROR; + } + return 0; +#endif +} + +/* Extract the packet length, including any decryption necessary beforehand */ +int +cipher_get_length(struct sshcipher_ctx *cc, u_int *plenp, u_int seqnr, + const u_char *cp, u_int len) +{ + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) + return chachapoly_get_length(cc->cp_ctx, plenp, seqnr, + cp, len); + if (len < 4) + return SSH_ERR_MESSAGE_INCOMPLETE; + *plenp = PEEK_U32(cp); + return 0; +} + +void +cipher_free(struct sshcipher_ctx *cc) +{ + if (cc == NULL) + return; + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { + chachapoly_free(cc->cp_ctx); + cc->cp_ctx = NULL; + } else if ((cc->cipher->flags & CFLAG_AESCTR) != 0) + explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx)); +#ifdef WITH_OPENSSL + EVP_CIPHER_CTX_free(cc->evp); + cc->evp = NULL; +#endif + freezero(cc, sizeof(*cc)); +} + +/* + * Exports an IV from the sshcipher_ctx required to export the key + * state back from the unprivileged child to the privileged parent + * process. + */ +int +cipher_get_keyiv_len(const struct sshcipher_ctx *cc) +{ + const struct sshcipher *c = cc->cipher; + + if ((c->flags & CFLAG_CHACHAPOLY) != 0) + return 0; + else if ((c->flags & CFLAG_AESCTR) != 0) + return sizeof(cc->ac_ctx.ctr); +#ifdef WITH_OPENSSL + return EVP_CIPHER_CTX_iv_length(cc->evp); +#else + return 0; +#endif +} + +int +cipher_get_keyiv(struct sshcipher_ctx *cc, u_char *iv, size_t len) +{ +#ifdef WITH_OPENSSL + const struct sshcipher *c = cc->cipher; + int evplen; +#endif + + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { + if (len != 0) + return SSH_ERR_INVALID_ARGUMENT; + return 0; + } + if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { + if (len != sizeof(cc->ac_ctx.ctr)) + return SSH_ERR_INVALID_ARGUMENT; + memcpy(iv, cc->ac_ctx.ctr, len); + return 0; + } + if ((cc->cipher->flags & CFLAG_NONE) != 0) + return 0; + +#ifdef WITH_OPENSSL + evplen = EVP_CIPHER_CTX_iv_length(cc->evp); + if (evplen == 0) + return 0; + else if (evplen < 0) + return SSH_ERR_LIBCRYPTO_ERROR; + if ((size_t)evplen != len) + return SSH_ERR_INVALID_ARGUMENT; +#ifndef OPENSSL_HAVE_EVPCTR + if (c->evptype == evp_aes_128_ctr) + ssh_aes_ctr_iv(cc->evp, 0, iv, len); + else +#endif + if (cipher_authlen(c)) { + if (!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_IV_GEN, + len, iv)) + return SSH_ERR_LIBCRYPTO_ERROR; + } else if (!EVP_CIPHER_CTX_get_iv(cc->evp, iv, len)) + return SSH_ERR_LIBCRYPTO_ERROR; +#endif + return 0; +} + +int +cipher_set_keyiv(struct sshcipher_ctx *cc, const u_char *iv, size_t len) +{ +#ifdef WITH_OPENSSL + const struct sshcipher *c = cc->cipher; + int evplen = 0; +#endif + + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) + return 0; + if ((cc->cipher->flags & CFLAG_NONE) != 0) + return 0; + +#ifdef WITH_OPENSSL + evplen = EVP_CIPHER_CTX_iv_length(cc->evp); + if (evplen <= 0) + return SSH_ERR_LIBCRYPTO_ERROR; + if ((size_t)evplen != len) + return SSH_ERR_INVALID_ARGUMENT; +#ifndef OPENSSL_HAVE_EVPCTR + /* XXX iv arg is const, but ssh_aes_ctr_iv isn't */ + if (c->evptype == evp_aes_128_ctr) + ssh_aes_ctr_iv(cc->evp, 1, (u_char *)iv, evplen); + else +#endif + if (cipher_authlen(c)) { + /* XXX iv arg is const, but EVP_CIPHER_CTX_ctrl isn't */ + if (!EVP_CIPHER_CTX_ctrl(cc->evp, + EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) + return SSH_ERR_LIBCRYPTO_ERROR; + } else if (!EVP_CIPHER_CTX_set_iv(cc->evp, iv, evplen)) + return SSH_ERR_LIBCRYPTO_ERROR; +#endif + return 0; +} diff --git a/aosp/external/openssh/cipher.h b/aosp/external/openssh/cipher.h new file mode 100644 index 0000000000000000000000000000000000000000..1a591cd7fd46deccad4a776582a3bff7b68435df --- /dev/null +++ b/aosp/external/openssh/cipher.h @@ -0,0 +1,78 @@ +/* $OpenBSD: cipher.h,v 1.55 2020/01/23 10:24:29 dtucker Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CIPHER_H +#define CIPHER_H + +#include +#ifdef WITH_OPENSSL +#include +#endif +#include "cipher-chachapoly.h" +#include "cipher-aesctr.h" + +#define CIPHER_ENCRYPT 1 +#define CIPHER_DECRYPT 0 + +struct sshcipher; +struct sshcipher_ctx; + +const struct sshcipher *cipher_by_name(const char *); +const char *cipher_warning_message(const struct sshcipher_ctx *); +int ciphers_valid(const char *); +char *cipher_alg_list(char, int); +const char *compression_alg_list(int); +int cipher_init(struct sshcipher_ctx **, const struct sshcipher *, + const u_char *, u_int, const u_char *, u_int, int); +int cipher_crypt(struct sshcipher_ctx *, u_int, u_char *, const u_char *, + u_int, u_int, u_int); +int cipher_get_length(struct sshcipher_ctx *, u_int *, u_int, + const u_char *, u_int); +void cipher_free(struct sshcipher_ctx *); +u_int cipher_blocksize(const struct sshcipher *); +u_int cipher_keylen(const struct sshcipher *); +u_int cipher_seclen(const struct sshcipher *); +u_int cipher_authlen(const struct sshcipher *); +u_int cipher_ivlen(const struct sshcipher *); +u_int cipher_is_cbc(const struct sshcipher *); + +u_int cipher_ctx_is_plaintext(struct sshcipher_ctx *); + +int cipher_get_keyiv(struct sshcipher_ctx *, u_char *, size_t); +int cipher_set_keyiv(struct sshcipher_ctx *, const u_char *, size_t); +int cipher_get_keyiv_len(const struct sshcipher_ctx *); + +#endif /* CIPHER_H */ diff --git a/aosp/external/openssh/cleanup.c b/aosp/external/openssh/cleanup.c new file mode 100644 index 0000000000000000000000000000000000000000..238f965e64461e9d6199f39c43d7349064f77b63 --- /dev/null +++ b/aosp/external/openssh/cleanup.c @@ -0,0 +1,32 @@ +/* $OpenBSD: cleanup.c,v 1.5 2006/08/03 03:34:42 deraadt Exp $ */ +/* + * Copyright (c) 2003 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include +#include + +#include "log.h" + +/* default implementation */ +void +cleanup_exit(int i) +{ + _exit(i); +} diff --git a/aosp/external/openssh/clientloop.c b/aosp/external/openssh/clientloop.c new file mode 100644 index 0000000000000000000000000000000000000000..f8350e67224bfe14bbd8ab179910e8bc84d35f8b --- /dev/null +++ b/aosp/external/openssh/clientloop.c @@ -0,0 +1,2625 @@ +/* $OpenBSD: clientloop.c,v 1.378 2022/01/22 00:49:34 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * The main loop for the interactive session (client side). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SSH2 support added by Markus Friedl. + * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "packet.h" +#include "sshbuf.h" +#include "compat.h" +#include "channels.h" +#include "dispatch.h" +#include "sshkey.h" +#include "cipher.h" +#include "kex.h" +#include "myproposal.h" +#include "log.h" +#include "misc.h" +#include "readconf.h" +#include "clientloop.h" +#include "sshconnect.h" +#include "authfd.h" +#include "atomicio.h" +#include "sshpty.h" +#include "match.h" +#include "msg.h" +#include "ssherr.h" +#include "hostfile.h" + +/* Permitted RSA signature algorithms for UpdateHostkeys proofs */ +#define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" + +/* import options */ +extern Options options; + +/* Control socket */ +extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ + +/* + * Name of the host we are connecting to. This is the name given on the + * command line, or the Hostname specified for the user-supplied name in a + * configuration file. + */ +extern char *host; + +/* + * If this field is not NULL, the ForwardAgent socket is this path and different + * instead of SSH_AUTH_SOCK. + */ +extern char *forward_agent_sock_path; + +/* + * Flag to indicate that we have received a window change signal which has + * not yet been processed. This will cause a message indicating the new + * window size to be sent to the server a little later. This is volatile + * because this is updated in a signal handler. + */ +static volatile sig_atomic_t received_window_change_signal = 0; +static volatile sig_atomic_t received_signal = 0; + +/* Time when backgrounded control master using ControlPersist should exit */ +static time_t control_persist_exit_time = 0; + +/* Common data for the client loop code. */ +volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ +static int last_was_cr; /* Last character was a newline. */ +static int exit_status; /* Used to store the command exit status. */ +static struct sshbuf *stderr_buffer; /* Used for final exit message. */ +static int connection_in; /* Connection to server (input). */ +static int connection_out; /* Connection to server (output). */ +static int need_rekeying; /* Set to non-zero if rekeying is requested. */ +static int session_closed; /* In SSH2: login session closed. */ +static u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ +static time_t server_alive_time; /* Time to do server_alive_check */ + +static void client_init_dispatch(struct ssh *ssh); +int session_ident = -1; + +/* Track escape per proto2 channel */ +struct escape_filter_ctx { + int escape_pending; + int escape_char; +}; + +/* Context for channel confirmation replies */ +struct channel_reply_ctx { + const char *request_type; + int id; + enum confirm_action action; +}; + +/* Global request success/failure callbacks */ +/* XXX move to struct ssh? */ +struct global_confirm { + TAILQ_ENTRY(global_confirm) entry; + global_confirm_cb *cb; + void *ctx; + int ref_count; +}; +TAILQ_HEAD(global_confirms, global_confirm); +static struct global_confirms global_confirms = + TAILQ_HEAD_INITIALIZER(global_confirms); + +void ssh_process_session2_setup(int, int, int, struct sshbuf *); +static void quit_message(const char *fmt, ...) + __attribute__((__format__ (printf, 1, 2))); + +static void +quit_message(const char *fmt, ...) +{ + char *msg; + va_list args; + int r; + + va_start(args, fmt); + xvasprintf(&msg, fmt, args); + va_end(args); + + if ((r = sshbuf_putf(stderr_buffer, "%s\r\n", msg)) != 0) + fatal_fr(r, "sshbuf_putf"); + quit_pending = 1; +} + +/* + * Signal handler for the window change signal (SIGWINCH). This just sets a + * flag indicating that the window has changed. + */ +/*ARGSUSED */ +static void +window_change_handler(int sig) +{ + received_window_change_signal = 1; +} + +/* + * Signal handler for signals that cause the program to terminate. These + * signals must be trapped to restore terminal modes. + */ +/*ARGSUSED */ +static void +signal_handler(int sig) +{ + received_signal = sig; + quit_pending = 1; +} + +/* + * Sets control_persist_exit_time to the absolute time when the + * backgrounded control master should exit due to expiry of the + * ControlPersist timeout. Sets it to 0 if we are not a backgrounded + * control master process, or if there is no ControlPersist timeout. + */ +static void +set_control_persist_exit_time(struct ssh *ssh) +{ + if (muxserver_sock == -1 || !options.control_persist + || options.control_persist_timeout == 0) { + /* not using a ControlPersist timeout */ + control_persist_exit_time = 0; + } else if (channel_still_open(ssh)) { + /* some client connections are still open */ + if (control_persist_exit_time > 0) + debug2_f("cancel scheduled exit"); + control_persist_exit_time = 0; + } else if (control_persist_exit_time <= 0) { + /* a client connection has recently closed */ + control_persist_exit_time = monotime() + + (time_t)options.control_persist_timeout; + debug2_f("schedule exit in %d seconds", + options.control_persist_timeout); + } + /* else we are already counting down to the timeout */ +} + +#define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" +static int +client_x11_display_valid(const char *display) +{ + size_t i, dlen; + + if (display == NULL) + return 0; + + dlen = strlen(display); + for (i = 0; i < dlen; i++) { + if (!isalnum((u_char)display[i]) && + strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { + debug("Invalid character '%c' in DISPLAY", display[i]); + return 0; + } + } + return 1; +} + +#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" +#define X11_TIMEOUT_SLACK 60 +int +client_x11_get_proto(struct ssh *ssh, const char *display, + const char *xauth_path, u_int trusted, u_int timeout, + char **_proto, char **_data) +{ + char *cmd, line[512], xdisplay[512]; + char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; + static char proto[512], data[512]; + FILE *f; + int got_data = 0, generated = 0, do_unlink = 0, r; + struct stat st; + u_int now, x11_timeout_real; + + *_proto = proto; + *_data = data; + proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; + + if (!client_x11_display_valid(display)) { + if (display != NULL) + logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", + display); + return -1; + } + if (xauth_path != NULL && stat(xauth_path, &st) == -1) { + debug("No xauth program."); + xauth_path = NULL; + } + + if (xauth_path != NULL) { + /* + * Handle FamilyLocal case where $DISPLAY does + * not match an authorization entry. For this we + * just try "xauth list unix:displaynum.screennum". + * XXX: "localhost" match to determine FamilyLocal + * is not perfect. + */ + if (strncmp(display, "localhost:", 10) == 0) { + if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", + display + 10)) < 0 || + (size_t)r >= sizeof(xdisplay)) { + error_f("display name too long"); + return -1; + } + display = xdisplay; + } + if (trusted == 0) { + /* + * Generate an untrusted X11 auth cookie. + * + * The authentication cookie should briefly outlive + * ssh's willingness to forward X11 connections to + * avoid nasty fail-open behaviour in the X server. + */ + mktemp_proto(xauthdir, sizeof(xauthdir)); + if (mkdtemp(xauthdir) == NULL) { + error_f("mkdtemp: %s", strerror(errno)); + return -1; + } + do_unlink = 1; + if ((r = snprintf(xauthfile, sizeof(xauthfile), + "%s/xauthfile", xauthdir)) < 0 || + (size_t)r >= sizeof(xauthfile)) { + error_f("xauthfile path too long"); + rmdir(xauthdir); + return -1; + } + + if (timeout == 0) { + /* auth doesn't time out */ + xasprintf(&cmd, "%s -f %s generate %s %s " + "untrusted 2>%s", + xauth_path, xauthfile, display, + SSH_X11_PROTO, _PATH_DEVNULL); + } else { + /* Add some slack to requested expiry */ + if (timeout < UINT_MAX - X11_TIMEOUT_SLACK) + x11_timeout_real = timeout + + X11_TIMEOUT_SLACK; + else { + /* Don't overflow on long timeouts */ + x11_timeout_real = UINT_MAX; + } + xasprintf(&cmd, "%s -f %s generate %s %s " + "untrusted timeout %u 2>%s", + xauth_path, xauthfile, display, + SSH_X11_PROTO, x11_timeout_real, + _PATH_DEVNULL); + } + debug2_f("xauth command: %s", cmd); + + if (timeout != 0 && x11_refuse_time == 0) { + now = monotime() + 1; + if (UINT_MAX - timeout < now) + x11_refuse_time = UINT_MAX; + else + x11_refuse_time = now + timeout; + channel_set_x11_refuse_time(ssh, + x11_refuse_time); + } + if (system(cmd) == 0) + generated = 1; + free(cmd); + } + + /* + * When in untrusted mode, we read the cookie only if it was + * successfully generated as an untrusted one in the step + * above. + */ + if (trusted || generated) { + xasprintf(&cmd, + "%s %s%s list %s 2>" _PATH_DEVNULL, + xauth_path, + generated ? "-f " : "" , + generated ? xauthfile : "", + display); + debug2("x11_get_proto: %s", cmd); + f = popen(cmd, "r"); + if (f && fgets(line, sizeof(line), f) && + sscanf(line, "%*s %511s %511s", proto, data) == 2) + got_data = 1; + if (f) + pclose(f); + free(cmd); + } + } + + if (do_unlink) { + unlink(xauthfile); + rmdir(xauthdir); + } + + /* Don't fall back to fake X11 data for untrusted forwarding */ + if (!trusted && !got_data) { + error("Warning: untrusted X11 forwarding setup failed: " + "xauth key data not generated"); + return -1; + } + + /* + * If we didn't get authentication data, just make up some + * data. The forwarding code will check the validity of the + * response anyway, and substitute this data. The X11 + * server, however, will ignore this fake data and use + * whatever authentication mechanisms it was using otherwise + * for the local connection. + */ + if (!got_data) { + u_int8_t rnd[16]; + u_int i; + + logit("Warning: No xauth data; " + "using fake authentication data for X11 forwarding."); + strlcpy(proto, SSH_X11_PROTO, sizeof proto); + arc4random_buf(rnd, sizeof(rnd)); + for (i = 0; i < sizeof(rnd); i++) { + snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", + rnd[i]); + } + } + + return 0; +} + +/* + * Checks if the client window has changed, and sends a packet about it to + * the server if so. The actual change is detected elsewhere (by a software + * interrupt on Unix); this just checks the flag and sends a message if + * appropriate. + */ + +static void +client_check_window_change(struct ssh *ssh) +{ + if (!received_window_change_signal) + return; + received_window_change_signal = 0; + debug2_f("changed"); + channel_send_window_changes(ssh); +} + +static int +client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) +{ + struct global_confirm *gc; + + if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) + return 0; + if (gc->cb != NULL) + gc->cb(ssh, type, seq, gc->ctx); + if (--gc->ref_count <= 0) { + TAILQ_REMOVE(&global_confirms, gc, entry); + freezero(gc, sizeof(*gc)); + } + + ssh_packet_set_alive_timeouts(ssh, 0); + return 0; +} + +static void +schedule_server_alive_check(void) +{ + if (options.server_alive_interval > 0) + server_alive_time = monotime() + options.server_alive_interval; +} + +static void +server_alive_check(struct ssh *ssh) +{ + int r; + + if (ssh_packet_inc_alive_timeouts(ssh) > options.server_alive_count_max) { + logit("Timeout, server %s not responding.", host); + cleanup_exit(255); + } + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0 || /* boolean: want reply */ + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + /* Insert an empty placeholder to maintain ordering */ + client_register_global_confirm(NULL, NULL); + schedule_server_alive_check(); +} + +/* + * Waits until the client can do something (some data becomes available on + * one of the file descriptors). + */ +static void +client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp, + u_int *npfd_allocp, u_int *npfd_activep, int rekeying, + int *conn_in_readyp, int *conn_out_readyp) +{ + int timeout_secs, pollwait; + time_t minwait_secs = 0, now = monotime(); + int ret; + u_int p; + + *conn_in_readyp = *conn_out_readyp = 0; + + /* Prepare channel poll. First two pollfd entries are reserved */ + channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, + &minwait_secs); + if (*npfd_activep < 2) + fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ + + /* channel_prepare_poll could have closed the last channel */ + if (session_closed && !channel_still_open(ssh) && + !ssh_packet_have_data_to_write(ssh)) { + /* clear events since we did not call poll() */ + for (p = 0; p < *npfd_activep; p++) + (*pfdp)[p].revents = 0; + return; + } + + /* Monitor server connection on reserved pollfd entries */ + (*pfdp)[0].fd = connection_in; + (*pfdp)[0].events = POLLIN; + (*pfdp)[1].fd = connection_out; + (*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; + + /* + * Wait for something to happen. This will suspend the process until + * some polled descriptor can be read, written, or has some other + * event pending, or a timeout expires. + */ + + timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ + if (options.server_alive_interval > 0) + timeout_secs = MAXIMUM(server_alive_time - now, 0); + if (options.rekey_interval > 0 && !rekeying) + timeout_secs = MINIMUM(timeout_secs, + ssh_packet_get_rekey_timeout(ssh)); + set_control_persist_exit_time(ssh); + if (control_persist_exit_time > 0) { + timeout_secs = MINIMUM(timeout_secs, + control_persist_exit_time - now); + if (timeout_secs < 0) + timeout_secs = 0; + } + if (minwait_secs != 0) + timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); + if (timeout_secs == INT_MAX) + pollwait = -1; + else if (timeout_secs >= INT_MAX / 1000) + pollwait = INT_MAX; + else + pollwait = timeout_secs * 1000; + + ret = poll(*pfdp, *npfd_activep, pollwait); + + if (ret == -1) { + /* + * We have to clear the events because we return. + * We have to return, because the mainloop checks for the flags + * set by the signal handlers. + */ + for (p = 0; p < *npfd_activep; p++) + (*pfdp)[p].revents = 0; + if (errno == EINTR) + return; + /* Note: we might still have data in the buffers. */ + quit_message("poll: %s", strerror(errno)); + return; + } + + *conn_in_readyp = (*pfdp)[0].revents != 0; + *conn_out_readyp = (*pfdp)[1].revents != 0; + + if (options.server_alive_interval > 0 && !*conn_in_readyp && + monotime() >= server_alive_time) { + /* + * ServerAlive check is needed. We can't rely on the poll + * timing out since traffic on the client side such as port + * forwards can keep waking it up. + */ + server_alive_check(ssh); + } +} + +static void +client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) +{ + /* Flush stdout and stderr buffers. */ + if (sshbuf_len(bout) > 0) + atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), + sshbuf_len(bout)); + if (sshbuf_len(berr) > 0) + atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), + sshbuf_len(berr)); + + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + + sshbuf_reset(bin); + sshbuf_reset(bout); + sshbuf_reset(berr); + + /* Send the suspend signal to the program itself. */ + kill(getpid(), SIGTSTP); + + /* Reset window sizes in case they have changed */ + received_window_change_signal = 1; + + enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); +} + +static void +client_process_net_input(struct ssh *ssh) +{ + int r; + + /* + * Read input from the server, and add any such data to the buffer of + * the packet subsystem. + */ + schedule_server_alive_check(); + if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) + return; /* success */ + if (r == SSH_ERR_SYSTEM_ERROR) { + if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) + return; + if (errno == EPIPE) { + quit_message("Connection to %s closed by remote host.", + host); + return; + } + } + quit_message("Read from remote host %s: %s", host, ssh_err(r)); +} + +static void +client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) +{ + struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; + char errmsg[256]; + int r, tochan; + + /* + * If a TTY was explicitly requested, then a failure to allocate + * one is fatal. + */ + if (cr->action == CONFIRM_TTY && + (options.request_tty == REQUEST_TTY_FORCE || + options.request_tty == REQUEST_TTY_YES)) + cr->action = CONFIRM_CLOSE; + + /* XXX suppress on mux _client_ quietmode */ + tochan = options.log_level >= SYSLOG_LEVEL_ERROR && + c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; + + if (type == SSH2_MSG_CHANNEL_SUCCESS) { + debug2("%s request accepted on channel %d", + cr->request_type, c->self); + } else if (type == SSH2_MSG_CHANNEL_FAILURE) { + if (tochan) { + snprintf(errmsg, sizeof(errmsg), + "%s request failed\r\n", cr->request_type); + } else { + snprintf(errmsg, sizeof(errmsg), + "%s request failed on channel %d", + cr->request_type, c->self); + } + /* If error occurred on primary session channel, then exit */ + if (cr->action == CONFIRM_CLOSE && c->self == session_ident) + fatal("%s", errmsg); + /* + * If error occurred on mux client, append to + * their stderr. + */ + if (tochan) { + debug3_f("channel %d: mux request: %s", c->self, + cr->request_type); + if ((r = sshbuf_put(c->extended, errmsg, + strlen(errmsg))) != 0) + fatal_fr(r, "sshbuf_put"); + } else + error("%s", errmsg); + if (cr->action == CONFIRM_TTY) { + /* + * If a TTY allocation error occurred, then arrange + * for the correct TTY to leave raw mode. + */ + if (c->self == session_ident) + leave_raw_mode(0); + else + mux_tty_alloc_failed(ssh, c); + } else if (cr->action == CONFIRM_CLOSE) { + chan_read_failed(ssh, c); + chan_write_failed(ssh, c); + } + } + free(cr); +} + +static void +client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) +{ + free(ctx); +} + +void +client_expect_confirm(struct ssh *ssh, int id, const char *request, + enum confirm_action action) +{ + struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); + + cr->request_type = request; + cr->action = action; + + channel_register_status_confirm(ssh, id, client_status_confirm, + client_abandon_status_confirm, cr); +} + +void +client_register_global_confirm(global_confirm_cb *cb, void *ctx) +{ + struct global_confirm *gc, *last_gc; + + /* Coalesce identical callbacks */ + last_gc = TAILQ_LAST(&global_confirms, global_confirms); + if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { + if (++last_gc->ref_count >= INT_MAX) + fatal_f("last_gc->ref_count = %d", + last_gc->ref_count); + return; + } + + gc = xcalloc(1, sizeof(*gc)); + gc->cb = cb; + gc->ctx = ctx; + gc->ref_count = 1; + TAILQ_INSERT_TAIL(&global_confirms, gc, entry); +} + +static void +process_cmdline(struct ssh *ssh) +{ + void (*handler)(int); + char *s, *cmd; + int ok, delete = 0, local = 0, remote = 0, dynamic = 0; + struct Forward fwd; + + memset(&fwd, 0, sizeof(fwd)); + + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + handler = ssh_signal(SIGINT, SIG_IGN); + cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); + if (s == NULL) + goto out; + while (isspace((u_char)*s)) + s++; + if (*s == '-') + s++; /* Skip cmdline '-', if any */ + if (*s == '\0') + goto out; + + if (*s == 'h' || *s == 'H' || *s == '?') { + logit("Commands:"); + logit(" -L[bind_address:]port:host:hostport " + "Request local forward"); + logit(" -R[bind_address:]port:host:hostport " + "Request remote forward"); + logit(" -D[bind_address:]port " + "Request dynamic forward"); + logit(" -KL[bind_address:]port " + "Cancel local forward"); + logit(" -KR[bind_address:]port " + "Cancel remote forward"); + logit(" -KD[bind_address:]port " + "Cancel dynamic forward"); + if (!options.permit_local_command) + goto out; + logit(" !args " + "Execute local command"); + goto out; + } + + if (*s == '!' && options.permit_local_command) { + s++; + ssh_local_cmd(s); + goto out; + } + + if (*s == 'K') { + delete = 1; + s++; + } + if (*s == 'L') + local = 1; + else if (*s == 'R') + remote = 1; + else if (*s == 'D') + dynamic = 1; + else { + logit("Invalid command."); + goto out; + } + + while (isspace((u_char)*++s)) + ; + + /* XXX update list of forwards in options */ + if (delete) { + /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ + if (!parse_forward(&fwd, s, 1, 0)) { + logit("Bad forwarding close specification."); + goto out; + } + if (remote) + ok = channel_request_rforward_cancel(ssh, &fwd) == 0; + else if (dynamic) + ok = channel_cancel_lport_listener(ssh, &fwd, + 0, &options.fwd_opts) > 0; + else + ok = channel_cancel_lport_listener(ssh, &fwd, + CHANNEL_CANCEL_PORT_STATIC, + &options.fwd_opts) > 0; + if (!ok) { + logit("Unknown port forwarding."); + goto out; + } + logit("Canceled forwarding."); + } else { + if (!parse_forward(&fwd, s, dynamic, remote)) { + logit("Bad forwarding specification."); + goto out; + } + if (local || dynamic) { + if (!channel_setup_local_fwd_listener(ssh, &fwd, + &options.fwd_opts)) { + logit("Port forwarding failed."); + goto out; + } + } else { + if (channel_request_remote_forwarding(ssh, &fwd) < 0) { + logit("Port forwarding failed."); + goto out; + } + } + logit("Forwarding port."); + } + +out: + ssh_signal(SIGINT, handler); + enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + free(cmd); + free(fwd.listen_host); + free(fwd.listen_path); + free(fwd.connect_host); + free(fwd.connect_path); +} + +/* reasons to suppress output of an escape command in help output */ +#define SUPPRESS_NEVER 0 /* never suppress, always show */ +#define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ +#define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ +#define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ +struct escape_help_text { + const char *cmd; + const char *text; + unsigned int flags; +}; +static struct escape_help_text esc_txt[] = { + {".", "terminate session", SUPPRESS_MUXMASTER}, + {".", "terminate connection (and any multiplexed sessions)", + SUPPRESS_MUXCLIENT}, + {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, + {"C", "open a command line", SUPPRESS_MUXCLIENT}, + {"R", "request rekey", SUPPRESS_NEVER}, + {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, + {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, + {"#", "list forwarded connections", SUPPRESS_NEVER}, + {"&", "background ssh (when waiting for connections to terminate)", + SUPPRESS_MUXCLIENT}, + {"?", "this message", SUPPRESS_NEVER}, +}; + +static void +print_escape_help(struct sshbuf *b, int escape_char, int mux_client, + int using_stderr) +{ + unsigned int i, suppress_flags; + int r; + + if ((r = sshbuf_putf(b, + "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); + + suppress_flags = + (mux_client ? SUPPRESS_MUXCLIENT : 0) | + (mux_client ? 0 : SUPPRESS_MUXMASTER) | + (using_stderr ? 0 : SUPPRESS_SYSLOG); + + for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { + if (esc_txt[i].flags & suppress_flags) + continue; + if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", + escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) + fatal_fr(r, "sshbuf_putf"); + } + + if ((r = sshbuf_putf(b, + " %c%c - send the escape character by typing it twice\r\n" + "(Note that escapes are only recognized immediately after " + "newline.)\r\n", escape_char, escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); +} + +/* + * Process the characters one by one. + */ +static int +process_escapes(struct ssh *ssh, Channel *c, + struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, + char *buf, int len) +{ + pid_t pid; + int r, bytes = 0; + u_int i; + u_char ch; + char *s; + struct escape_filter_ctx *efc = c->filter_ctx == NULL ? + NULL : (struct escape_filter_ctx *)c->filter_ctx; + + if (c->filter_ctx == NULL) + return 0; + + if (len <= 0) + return (0); + + for (i = 0; i < (u_int)len; i++) { + /* Get one character at a time. */ + ch = buf[i]; + + if (efc->escape_pending) { + /* We have previously seen an escape character. */ + /* Clear the flag now. */ + efc->escape_pending = 0; + + /* Process the escaped character. */ + switch (ch) { + case '.': + /* Terminate the connection. */ + if ((r = sshbuf_putf(berr, "%c.\r\n", + efc->escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); + if (c && c->ctl_chan != -1) { + chan_read_failed(ssh, c); + chan_write_failed(ssh, c); + if (c->detach_user) { + c->detach_user(ssh, + c->self, NULL); + } + c->type = SSH_CHANNEL_ABANDONED; + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); + return 0; + } else + quit_pending = 1; + return -1; + + case 'Z' - 64: + /* XXX support this for mux clients */ + if (c && c->ctl_chan != -1) { + char b[16]; + noescape: + if (ch == 'Z' - 64) + snprintf(b, sizeof b, "^Z"); + else + snprintf(b, sizeof b, "%c", ch); + if ((r = sshbuf_putf(berr, + "%c%s escape not available to " + "multiplexed sessions\r\n", + efc->escape_char, b)) != 0) + fatal_fr(r, "sshbuf_putf"); + continue; + } + /* Suspend the program. Inform the user */ + if ((r = sshbuf_putf(berr, + "%c^Z [suspend ssh]\r\n", + efc->escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); + + /* Restore terminal modes and suspend. */ + client_suspend_self(bin, bout, berr); + + /* We have been continued. */ + continue; + + case 'B': + if ((r = sshbuf_putf(berr, + "%cB\r\n", efc->escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); + channel_request_start(ssh, c->self, "break", 0); + if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + continue; + + case 'R': + if (ssh->compat & SSH_BUG_NOREKEY) + logit("Server does not " + "support re-keying"); + else + need_rekeying = 1; + continue; + + case 'V': + /* FALLTHROUGH */ + case 'v': + if (c && c->ctl_chan != -1) + goto noescape; + if (!log_is_on_stderr()) { + if ((r = sshbuf_putf(berr, + "%c%c [Logging to syslog]\r\n", + efc->escape_char, ch)) != 0) + fatal_fr(r, "sshbuf_putf"); + continue; + } + if (ch == 'V' && options.log_level > + SYSLOG_LEVEL_QUIET) + log_change_level(--options.log_level); + if (ch == 'v' && options.log_level < + SYSLOG_LEVEL_DEBUG3) + log_change_level(++options.log_level); + if ((r = sshbuf_putf(berr, + "%c%c [LogLevel %s]\r\n", + efc->escape_char, ch, + log_level_name(options.log_level))) != 0) + fatal_fr(r, "sshbuf_putf"); + continue; + + case '&': + if (c && c->ctl_chan != -1) + goto noescape; + /* + * Detach the program (continue to serve + * connections, but put in background and no + * more new connections). + */ + /* Restore tty modes. */ + leave_raw_mode( + options.request_tty == REQUEST_TTY_FORCE); + + /* Stop listening for new connections. */ + channel_stop_listening(ssh); + + if ((r = sshbuf_putf(berr, "%c& " + "[backgrounded]\n", efc->escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); + + /* Fork into background. */ + pid = fork(); + if (pid == -1) { + error("fork: %.100s", strerror(errno)); + continue; + } + if (pid != 0) { /* This is the parent. */ + /* The parent just exits. */ + exit(0); + } + /* The child continues serving connections. */ + /* fake EOF on stdin */ + if ((r = sshbuf_put_u8(bin, 4)) != 0) + fatal_fr(r, "sshbuf_put_u8"); + return -1; + case '?': + print_escape_help(berr, efc->escape_char, + (c && c->ctl_chan != -1), + log_is_on_stderr()); + continue; + + case '#': + if ((r = sshbuf_putf(berr, "%c#\r\n", + efc->escape_char)) != 0) + fatal_fr(r, "sshbuf_putf"); + s = channel_open_message(ssh); + if ((r = sshbuf_put(berr, s, strlen(s))) != 0) + fatal_fr(r, "sshbuf_put"); + free(s); + continue; + + case 'C': + if (c && c->ctl_chan != -1) + goto noescape; + process_cmdline(ssh); + continue; + + default: + if (ch != efc->escape_char) { + if ((r = sshbuf_put_u8(bin, + efc->escape_char)) != 0) + fatal_fr(r, "sshbuf_put_u8"); + bytes++; + } + /* Escaped characters fall through here */ + break; + } + } else { + /* + * The previous character was not an escape char. + * Check if this is an escape. + */ + if (last_was_cr && ch == efc->escape_char) { + /* + * It is. Set the flag and continue to + * next character. + */ + efc->escape_pending = 1; + continue; + } + } + + /* + * Normal character. Record whether it was a newline, + * and append it to the buffer. + */ + last_was_cr = (ch == '\r' || ch == '\n'); + if ((r = sshbuf_put_u8(bin, ch)) != 0) + fatal_fr(r, "sshbuf_put_u8"); + bytes++; + } + return bytes; +} + +/* + * Get packets from the connection input buffer, and process them as long as + * there are packets available. + * + * Any unknown packets received during the actual + * session cause the session to terminate. This is + * intended to make debugging easier since no + * confirmations are sent. Any compatible protocol + * extensions must be negotiated during the + * preparatory phase. + */ + +static void +client_process_buffered_input_packets(struct ssh *ssh) +{ + ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, &quit_pending); +} + +/* scan buf[] for '~' before sending data to the peer */ + +/* Helper: allocate a new escape_filter_ctx and fill in its escape char */ +void * +client_new_escape_filter_ctx(int escape_char) +{ + struct escape_filter_ctx *ret; + + ret = xcalloc(1, sizeof(*ret)); + ret->escape_pending = 0; + ret->escape_char = escape_char; + return (void *)ret; +} + +/* Free the escape filter context on channel free */ +void +client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) +{ + free(ctx); +} + +int +client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) +{ + if (c->extended_usage != CHAN_EXTENDED_WRITE) + return 0; + + return process_escapes(ssh, c, c->input, c->output, c->extended, + buf, len); +} + +static void +client_channel_closed(struct ssh *ssh, int id, void *arg) +{ + channel_cancel_cleanup(ssh, id); + session_closed = 1; + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); +} + +/* + * Implements the interactive session with the server. This is called after + * the user has been authenticated, and a command has been started on the + * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character + * used as an escape character for terminating or suspending the session. + */ +int +client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + int ssh2_chan_id) +{ + struct pollfd *pfd = NULL; + u_int npfd_alloc = 0, npfd_active = 0; + double start_time, total_time; + int r, len; + u_int64_t ibytes, obytes; + int conn_in_ready, conn_out_ready; + + debug("Entering interactive session."); + + if (options.control_master && + !option_clear_or_none(options.control_path)) { + debug("pledge: id"); + if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty", + NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + + } else if (options.forward_x11 || options.permit_local_command) { + debug("pledge: exec"); + if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", + NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + + } else if (options.update_hostkeys) { + debug("pledge: filesystem"); + if (pledge("stdio rpath wpath cpath unix inet dns proc tty", + NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + + } else if (!option_clear_or_none(options.proxy_command) || + options.fork_after_authentication) { + debug("pledge: proc"); + if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + + } else { + debug("pledge: network"); + if (pledge("stdio unix inet dns proc tty", NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + } + + start_time = monotime_double(); + + /* Initialize variables. */ + last_was_cr = 1; + exit_status = -1; + connection_in = ssh_packet_get_connection_in(ssh); + connection_out = ssh_packet_get_connection_out(ssh); + + quit_pending = 0; + + /* Initialize buffer. */ + if ((stderr_buffer = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + client_init_dispatch(ssh); + + /* + * Set signal handlers, (e.g. to restore non-blocking mode) + * but don't overwrite SIG_IGN, matches behaviour from rsh(1) + */ + if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) + ssh_signal(SIGHUP, signal_handler); + if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) + ssh_signal(SIGINT, signal_handler); + if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) + ssh_signal(SIGQUIT, signal_handler); + if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) + ssh_signal(SIGTERM, signal_handler); + ssh_signal(SIGWINCH, window_change_handler); + + if (have_pty) + enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + + session_ident = ssh2_chan_id; + if (session_ident != -1) { + if (escape_char_arg != SSH_ESCAPECHAR_NONE) { + channel_register_filter(ssh, session_ident, + client_simple_escape_filter, NULL, + client_filter_cleanup, + client_new_escape_filter_ctx( + escape_char_arg)); + } + channel_register_cleanup(ssh, session_ident, + client_channel_closed, 0); + } + + schedule_server_alive_check(); + + /* Main loop of the client for the interactive session mode. */ + while (!quit_pending) { + + /* Process buffered packets sent by the server. */ + client_process_buffered_input_packets(ssh); + + if (session_closed && !channel_still_open(ssh)) + break; + + if (ssh_packet_is_rekeying(ssh)) { + debug("rekeying in progress"); + } else if (need_rekeying) { + /* manual rekey request */ + debug("need rekeying"); + if ((r = kex_start_rekex(ssh)) != 0) + fatal_fr(r, "kex_start_rekex"); + need_rekeying = 0; + } else { + /* + * Make packets from buffered channel data, and + * enqueue them for sending to the server. + */ + if (ssh_packet_not_very_much_data_to_write(ssh)) + channel_output_poll(ssh); + + /* + * Check if the window size has changed, and buffer a + * message about it to the server if so. + */ + client_check_window_change(ssh); + + if (quit_pending) + break; + } + /* + * Wait until we have something to do (something becomes + * available on one of the descriptors). + */ + client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, + &npfd_active, ssh_packet_is_rekeying(ssh), + &conn_in_ready, &conn_out_ready); + + if (quit_pending) + break; + + /* Do channel operations unless rekeying in progress. */ + if (!ssh_packet_is_rekeying(ssh)) + channel_after_poll(ssh, pfd, npfd_active); + + /* Buffer input from the connection. */ + if (conn_in_ready) + client_process_net_input(ssh); + + if (quit_pending) + break; + + /* A timeout may have triggered rekeying */ + if ((r = ssh_packet_check_rekey(ssh)) != 0) + fatal_fr(r, "cannot start rekeying"); + + /* + * Send as much buffered packet data as possible to the + * sender. + */ + if (conn_out_ready) { + if ((r = ssh_packet_write_poll(ssh)) != 0) { + sshpkt_fatal(ssh, r, + "%s: ssh_packet_write_poll", __func__); + } + } + + /* + * If we are a backgrounded control master, and the + * timeout has expired without any active client + * connections, then quit. + */ + if (control_persist_exit_time > 0) { + if (monotime() >= control_persist_exit_time) { + debug("ControlPersist timeout expired"); + break; + } + } + } + free(pfd); + + /* Terminate the session. */ + + /* Stop watching for window change. */ + ssh_signal(SIGWINCH, SIG_DFL); + + if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || + (r = sshpkt_put_cstring(ssh, "disconnected by user")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */ + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send disconnect"); + + channel_free_all(ssh); + + if (have_pty) + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + + /* + * If there was no shell or command requested, there will be no remote + * exit status to be returned. In that case, clear error code if the + * connection was deliberately terminated at this end. + */ + if (options.session_type == SESSION_TYPE_NONE && + received_signal == SIGTERM) { + received_signal = 0; + exit_status = 0; + } + + if (received_signal) { + verbose("Killed by signal %d.", (int) received_signal); + cleanup_exit(255); + } + + /* + * In interactive mode (with pseudo tty) display a message indicating + * that the connection has been closed. + */ + if (have_pty && options.log_level >= SYSLOG_LEVEL_INFO) + quit_message("Connection to %s closed.", host); + + /* Output any buffered data for stderr. */ + if (sshbuf_len(stderr_buffer) > 0) { + len = atomicio(vwrite, fileno(stderr), + (u_char *)sshbuf_ptr(stderr_buffer), + sshbuf_len(stderr_buffer)); + if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) + error("Write failed flushing stderr buffer."); + else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) + fatal_fr(r, "sshbuf_consume"); + } + + /* Clear and free any buffers. */ + sshbuf_free(stderr_buffer); + + /* Report bytes transferred, and transfer rates. */ + total_time = monotime_double() - start_time; + ssh_packet_get_bytes(ssh, &ibytes, &obytes); + verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", + (unsigned long long)obytes, (unsigned long long)ibytes, total_time); + if (total_time > 0) + verbose("Bytes per second: sent %.1f, received %.1f", + obytes / total_time, ibytes / total_time); + /* Return the exit status of the program. */ + debug("Exit status %d", exit_status); + return exit_status; +} + +/*********/ + +static Channel * +client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, + int rchan, u_int rwindow, u_int rmaxpack) +{ + Channel *c = NULL; + struct sshbuf *b = NULL; + char *listen_address, *originator_address; + u_int listen_port, originator_port; + int r; + + /* Get rest of the packet */ + if ((r = sshpkt_get_cstring(ssh, &listen_address, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &listen_port)) != 0 || + (r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + debug_f("listen %s port %d, originator %s port %d", + listen_address, listen_port, originator_address, originator_port); + + if (listen_port > 0xffff) + error_f("invalid listen port"); + else if (originator_port > 0xffff) + error_f("invalid originator port"); + else { + c = channel_connect_by_listen_address(ssh, + listen_address, listen_port, "forwarded-tcpip", + originator_address); + } + + if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { + if ((b = sshbuf_new()) == NULL) { + error_f("alloc reply"); + goto out; + } + /* reconstruct and send to muxclient */ + if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ + (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || + (r = sshbuf_put_cstring(b, request_type)) != 0 || + (r = sshbuf_put_u32(b, rchan)) != 0 || + (r = sshbuf_put_u32(b, rwindow)) != 0 || + (r = sshbuf_put_u32(b, rmaxpack)) != 0 || + (r = sshbuf_put_cstring(b, listen_address)) != 0 || + (r = sshbuf_put_u32(b, listen_port)) != 0 || + (r = sshbuf_put_cstring(b, originator_address)) != 0 || + (r = sshbuf_put_u32(b, originator_port)) != 0 || + (r = sshbuf_put_stringb(c->output, b)) != 0) { + error_fr(r, "compose for muxclient"); + goto out; + } + } + + out: + sshbuf_free(b); + free(originator_address); + free(listen_address); + return c; +} + +static Channel * +client_request_forwarded_streamlocal(struct ssh *ssh, + const char *request_type, int rchan) +{ + Channel *c = NULL; + char *listen_path; + int r; + + /* Get the remote path. */ + if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 || + (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */ + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + + debug_f("request: %s", listen_path); + + c = channel_connect_by_listen_path(ssh, listen_path, + "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); + free(listen_path); + return c; +} + +static Channel * +client_request_x11(struct ssh *ssh, const char *request_type, int rchan) +{ + Channel *c = NULL; + char *originator; + u_int originator_port; + int r, sock; + + if (!options.forward_x11) { + error("Warning: ssh server tried X11 forwarding."); + error("Warning: this is probably a break-in attempt by a " + "malicious server."); + return NULL; + } + if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { + verbose("Rejected X11 connection after ForwardX11Timeout " + "expired"); + return NULL; + } + if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal_fr(r, "parse packet"); + /* XXX check permission */ + /* XXX range check originator port? */ + debug("client_request_x11: request from %s %u", originator, + originator_port); + free(originator); + sock = x11_connect_display(ssh); + if (sock < 0) + return NULL; + c = channel_new(ssh, "x11", + SSH_CHANNEL_X11_OPEN, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); + c->force_drain = 1; + return c; +} + +static Channel * +client_request_agent(struct ssh *ssh, const char *request_type, int rchan) +{ + Channel *c = NULL; + int r, sock; + + if (!options.forward_agent) { + error("Warning: ssh server tried agent forwarding."); + error("Warning: this is probably a break-in attempt by a " + "malicious server."); + return NULL; + } + if (forward_agent_sock_path == NULL) { + r = ssh_get_authentication_socket(&sock); + } else { + r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); + } + if (r != 0) { + if (r != SSH_ERR_AGENT_NOT_PRESENT) + debug_fr(r, "ssh_get_authentication_socket"); + return NULL; + } + if ((r = ssh_agent_bind_hostkey(sock, ssh->kex->initial_hostkey, + ssh->kex->session_id, ssh->kex->initial_sig, 1)) == 0) + debug_f("bound agent to hostkey"); + else + debug2_fr(r, "ssh_agent_bind_hostkey"); + + c = channel_new(ssh, "authentication agent connection", + SSH_CHANNEL_OPEN, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, + "authentication agent connection", 1); + c->force_drain = 1; + return c; +} + +char * +client_request_tun_fwd(struct ssh *ssh, int tun_mode, + int local_tun, int remote_tun, channel_open_fn *cb, void *cbctx) +{ + Channel *c; + int r, fd; + char *ifname = NULL; + + if (tun_mode == SSH_TUNMODE_NO) + return 0; + + debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); + + /* Open local tunnel device */ + if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { + error("Tunnel device open failed."); + return NULL; + } + debug("Tunnel forwarding using interface %s", ifname); + + c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + c->datagram = 1; + +#if defined(SSH_TUN_FILTER) + if (options.tun_open == SSH_TUNMODE_POINTOPOINT) + channel_register_filter(ssh, c->self, sys_tun_infilter, + sys_tun_outfilter, NULL, NULL); +#endif + + if (cb != NULL) + channel_register_open_confirm(ssh, c->self, cb, cbctx); + + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || + (r = sshpkt_put_cstring(ssh, "tun@openssh.com")) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window_max)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || + (r = sshpkt_put_u32(ssh, tun_mode)) != 0 || + (r = sshpkt_put_u32(ssh, remote_tun)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send reply", __func__); + + return ifname; +} + +/* XXXX move to generic input handler */ +static int +client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = NULL; + char *ctype = NULL; + int r; + u_int rchan; + size_t len; + u_int rmaxpack, rwindow; + + if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || + (r = sshpkt_get_u32(ssh, &rchan)) != 0 || + (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || + (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) + goto out; + + debug("client_input_channel_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "forwarded-tcpip") == 0) { + c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, + rmaxpack); + } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { + c = client_request_forwarded_streamlocal(ssh, ctype, rchan); + } else if (strcmp(ctype, "x11") == 0) { + c = client_request_x11(ssh, ctype, rchan); + } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { + c = client_request_agent(ssh, ctype, rchan); + } + if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { + debug3("proxied to downstream: %s", ctype); + } else if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->have_remote_id = 1; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + if (c->type != SSH_CHANNEL_CONNECTING) { + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send reply", __func__); + } + } else { + debug("failure %s", ctype); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, rchan)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || + (r = sshpkt_put_cstring(ssh, "open failed")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send failure", __func__); + } + r = 0; + out: + free(ctype); + return r; +} + +static int +client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = NULL; + char *rtype = NULL; + u_char reply; + u_int id, exitval; + int r, success = 0; + + if ((r = sshpkt_get_u32(ssh, &id)) != 0) + return r; + if (id <= INT_MAX) + c = channel_lookup(ssh, id); + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &reply)) != 0) + goto out; + + debug("client_input_channel_req: channel %u rtype %s reply %d", + id, rtype, reply); + + if (c == NULL) { + error("client_input_channel_req: channel %d: " + "unknown channel", id); + } else if (strcmp(rtype, "eow@openssh.com") == 0) { + if ((r = sshpkt_get_end(ssh)) != 0) + goto out; + chan_rcvd_eow(ssh, c); + } else if (strcmp(rtype, "exit-status") == 0) { + if ((r = sshpkt_get_u32(ssh, &exitval)) != 0) + goto out; + if (c->ctl_chan != -1) { + mux_exit_message(ssh, c, exitval); + success = 1; + } else if ((int)id == session_ident) { + /* Record exit value of local session */ + success = 1; + exit_status = exitval; + } else { + /* Probably for a mux channel that has already closed */ + debug_f("no sink for exit-status on channel %d", + id); + } + if ((r = sshpkt_get_end(ssh)) != 0) + goto out; + } + if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { + if (!c->have_remote_id) + fatal_f("channel %d: no remote_id", c->self); + if ((r = sshpkt_start(ssh, success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send failure", __func__); + } + r = 0; + out: + free(rtype); + return r; +} + +struct hostkeys_update_ctx { + /* The hostname and (optionally) IP address string for the server */ + char *host_str, *ip_str; + + /* + * Keys received from the server and a flag for each indicating + * whether they already exist in known_hosts. + * keys_match is filled in by hostkeys_find() and later (for new + * keys) by client_global_hostkeys_private_confirm(). + */ + struct sshkey **keys; + u_int *keys_match; /* mask of HKF_MATCH_* from hostfile.h */ + int *keys_verified; /* flag for new keys verified by server */ + size_t nkeys, nnew, nincomplete; /* total, new keys, incomplete match */ + + /* + * Keys that are in known_hosts, but were not present in the update + * from the server (i.e. scheduled to be deleted). + * Filled in by hostkeys_find(). + */ + struct sshkey **old_keys; + size_t nold; + + /* Various special cases. */ + int complex_hostspec; /* wildcard or manual pattern-list host name */ + int ca_available; /* saw CA key for this host */ + int old_key_seen; /* saw old key with other name/addr */ + int other_name_seen; /* saw key with other name/addr */ +}; + +static void +hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) +{ + size_t i; + + if (ctx == NULL) + return; + for (i = 0; i < ctx->nkeys; i++) + sshkey_free(ctx->keys[i]); + free(ctx->keys); + free(ctx->keys_match); + free(ctx->keys_verified); + for (i = 0; i < ctx->nold; i++) + sshkey_free(ctx->old_keys[i]); + free(ctx->old_keys); + free(ctx->host_str); + free(ctx->ip_str); + free(ctx); +} + +/* + * Returns non-zero if a known_hosts hostname list is not of a form that + * can be handled by UpdateHostkeys. These include wildcard hostnames and + * hostnames lists that do not follow the form host[,ip]. + */ +static int +hostspec_is_complex(const char *hosts) +{ + char *cp; + + /* wildcard */ + if (strchr(hosts, '*') != NULL || strchr(hosts, '?') != NULL) + return 1; + /* single host/ip = ok */ + if ((cp = strchr(hosts, ',')) == NULL) + return 0; + /* more than two entries on the line */ + if (strchr(cp + 1, ',') != NULL) + return 1; + /* XXX maybe parse cp+1 and ensure it is an IP? */ + return 0; +} + +/* callback to search for ctx->keys in known_hosts */ +static int +hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) +{ + struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; + size_t i; + struct sshkey **tmp; + + if (l->key == NULL) + return 0; + if (l->status != HKF_STATUS_MATCHED) { + /* Record if one of the keys appears on a non-matching line */ + for (i = 0; i < ctx->nkeys; i++) { + if (sshkey_equal(l->key, ctx->keys[i])) { + ctx->other_name_seen = 1; + debug3_f("found %s key under different " + "name/addr at %s:%ld", + sshkey_ssh_name(ctx->keys[i]), + l->path, l->linenum); + return 0; + } + } + return 0; + } + /* Don't proceed if revocation or CA markers are present */ + /* XXX relax this */ + if (l->marker != MRK_NONE) { + debug3_f("hostkeys file %s:%ld has CA/revocation marker", + l->path, l->linenum); + ctx->complex_hostspec = 1; + return 0; + } + + /* If CheckHostIP is enabled, then check for mismatched hostname/addr */ + if (ctx->ip_str != NULL && strchr(l->hosts, ',') != NULL) { + if ((l->match & HKF_MATCH_HOST) == 0) { + /* Record if address matched a different hostname. */ + ctx->other_name_seen = 1; + debug3_f("found address %s against different hostname " + "at %s:%ld", ctx->ip_str, l->path, l->linenum); + return 0; + } else if ((l->match & HKF_MATCH_IP) == 0) { + /* Record if hostname matched a different address. */ + ctx->other_name_seen = 1; + debug3_f("found hostname %s against different address " + "at %s:%ld", ctx->host_str, l->path, l->linenum); + } + } + + /* + * UpdateHostkeys is skipped for wildcard host names and hostnames + * that contain more than two entries (ssh never writes these). + */ + if (hostspec_is_complex(l->hosts)) { + debug3_f("hostkeys file %s:%ld complex host specification", + l->path, l->linenum); + ctx->complex_hostspec = 1; + return 0; + } + + /* Mark off keys we've already seen for this host */ + for (i = 0; i < ctx->nkeys; i++) { + if (!sshkey_equal(l->key, ctx->keys[i])) + continue; + debug3_f("found %s key at %s:%ld", + sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); + ctx->keys_match[i] |= l->match; + return 0; + } + /* This line contained a key that not offered by the server */ + debug3_f("deprecated %s key at %s:%ld", sshkey_ssh_name(l->key), + l->path, l->linenum); + if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, + sizeof(*ctx->old_keys))) == NULL) + fatal_f("recallocarray failed nold = %zu", ctx->nold); + ctx->old_keys = tmp; + ctx->old_keys[ctx->nold++] = l->key; + l->key = NULL; + + return 0; +} + +/* callback to search for ctx->old_keys in known_hosts under other names */ +static int +hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx) +{ + struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; + size_t i; + int hashed; + + /* only care about lines that *don't* match the active host spec */ + if (l->status == HKF_STATUS_MATCHED || l->key == NULL) + return 0; + + hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED); + for (i = 0; i < ctx->nold; i++) { + if (!sshkey_equal(l->key, ctx->old_keys[i])) + continue; + debug3_f("found deprecated %s key at %s:%ld as %s", + sshkey_ssh_name(ctx->old_keys[i]), l->path, l->linenum, + hashed ? "[HASHED]" : l->hosts); + ctx->old_key_seen = 1; + break; + } + return 0; +} + +/* + * Check known_hosts files for deprecated keys under other names. Returns 0 + * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys + * exist under names other than the active hostname/IP. + */ +static int +check_old_keys_othernames(struct hostkeys_update_ctx *ctx) +{ + size_t i; + int r; + + debug2_f("checking for %zu deprecated keys", ctx->nold); + for (i = 0; i < options.num_user_hostfiles; i++) { + debug3_f("searching %s for %s / %s", + options.user_hostfiles[i], ctx->host_str, + ctx->ip_str ? ctx->ip_str : "(none)"); + if ((r = hostkeys_foreach(options.user_hostfiles[i], + hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str, + HKF_WANT_PARSE_KEY, 0)) != 0) { + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { + debug_f("hostkeys file %s does not exist", + options.user_hostfiles[i]); + continue; + } + error_fr(r, "hostkeys_foreach failed for %s", + options.user_hostfiles[i]); + return -1; + } + } + return 0; +} + +static void +hostkey_change_preamble(LogLevel loglevel) +{ + do_log2(loglevel, "The server has updated its host keys."); + do_log2(loglevel, "These changes were verified by the server's " + "existing trusted key."); +} + +static void +update_known_hosts(struct hostkeys_update_ctx *ctx) +{ + int r, was_raw = 0, first = 1; + int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; + LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; + char *fp, *response; + size_t i; + struct stat sb; + + for (i = 0; i < ctx->nkeys; i++) { + if (!ctx->keys_verified[i]) + continue; + if ((fp = sshkey_fingerprint(ctx->keys[i], + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + if (first && asking) + hostkey_change_preamble(loglevel); + do_log2(loglevel, "Learned new hostkey: %s %s", + sshkey_type(ctx->keys[i]), fp); + first = 0; + free(fp); + } + for (i = 0; i < ctx->nold; i++) { + if ((fp = sshkey_fingerprint(ctx->old_keys[i], + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + if (first && asking) + hostkey_change_preamble(loglevel); + do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", + sshkey_type(ctx->old_keys[i]), fp); + first = 0; + free(fp); + } + if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { + if (get_saved_tio() != NULL) { + leave_raw_mode(1); + was_raw = 1; + } + response = NULL; + for (i = 0; !quit_pending && i < 3; i++) { + free(response); + response = read_passphrase("Accept updated hostkeys? " + "(yes/no): ", RP_ECHO); + if (strcasecmp(response, "yes") == 0) + break; + else if (quit_pending || response == NULL || + strcasecmp(response, "no") == 0) { + options.update_hostkeys = 0; + break; + } else { + do_log2(loglevel, "Please enter " + "\"yes\" or \"no\""); + } + } + if (quit_pending || i >= 3 || response == NULL) + options.update_hostkeys = 0; + free(response); + if (was_raw) + enter_raw_mode(1); + } + if (options.update_hostkeys == 0) + return; + /* + * Now that all the keys are verified, we can go ahead and replace + * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't + * cancel the operation). + */ + for (i = 0; i < options.num_user_hostfiles; i++) { + /* + * NB. keys are only added to hostfiles[0], for the rest we + * just delete the hostname entries. + */ + if (stat(options.user_hostfiles[i], &sb) != 0) { + if (errno == ENOENT) { + debug_f("known hosts file %s does not " + "exist", options.user_hostfiles[i]); + } else { + error_f("known hosts file %s " + "inaccessible: %s", + options.user_hostfiles[i], strerror(errno)); + } + continue; + } + if ((r = hostfile_replace_entries(options.user_hostfiles[i], + ctx->host_str, ctx->ip_str, + i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, + options.hash_known_hosts, 0, + options.fingerprint_hash)) != 0) { + error_fr(r, "hostfile_replace_entries failed for %s", + options.user_hostfiles[i]); + } + } +} + +static void +client_global_hostkeys_private_confirm(struct ssh *ssh, int type, + u_int32_t seq, void *_ctx) +{ + struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; + size_t i, ndone; + struct sshbuf *signdata; + int r, plaintype; + const u_char *sig; + const char *rsa_kexalg = NULL; + char *alg = NULL; + size_t siglen; + + if (ctx->nnew == 0) + fatal_f("ctx->nnew == 0"); /* sanity */ + if (type != SSH2_MSG_REQUEST_SUCCESS) { + error("Server failed to confirm ownership of " + "private host keys"); + hostkeys_update_ctx_free(ctx); + return; + } + if (sshkey_type_plain(sshkey_type_from_name( + ssh->kex->hostkey_alg)) == KEY_RSA) + rsa_kexalg = ssh->kex->hostkey_alg; + if ((signdata = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + /* + * Expect a signature for each of the ctx->nnew private keys we + * haven't seen before. They will be in the same order as the + * ctx->keys where the corresponding ctx->keys_match[i] == 0. + */ + for (ndone = i = 0; i < ctx->nkeys; i++) { + if (ctx->keys_match[i]) + continue; + plaintype = sshkey_type_plain(ctx->keys[i]->type); + /* Prepare data to be signed: session ID, unique string, key */ + sshbuf_reset(signdata); + if ( (r = sshbuf_put_cstring(signdata, + "hostkeys-prove-00@openssh.com")) != 0 || + (r = sshbuf_put_stringb(signdata, + ssh->kex->session_id)) != 0 || + (r = sshkey_puts(ctx->keys[i], signdata)) != 0) + fatal_fr(r, "compose signdata"); + /* Extract and verify signature */ + if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { + error_fr(r, "parse sig"); + goto out; + } + if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) { + error_fr(r, "server gave unintelligible signature " + "for %s key %zu", sshkey_type(ctx->keys[i]), i); + goto out; + } + /* + * Special case for RSA keys: if a RSA hostkey was negotiated, + * then use its signature type for verification of RSA hostkey + * proofs. Otherwise, accept only RSA-SHA256/512 signatures. + */ + if (plaintype == KEY_RSA && rsa_kexalg == NULL && + match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) { + debug_f("server used untrusted RSA signature algorithm " + "%s for key %zu, disregarding", alg, i); + free(alg); + /* zap the key from the list */ + sshkey_free(ctx->keys[i]); + ctx->keys[i] = NULL; + ndone++; + continue; + } + debug3_f("verify %s key %zu using sigalg %s", + sshkey_type(ctx->keys[i]), i, alg); + free(alg); + if ((r = sshkey_verify(ctx->keys[i], sig, siglen, + sshbuf_ptr(signdata), sshbuf_len(signdata), + plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) { + error_fr(r, "server gave bad signature for %s key %zu", + sshkey_type(ctx->keys[i]), i); + goto out; + } + /* Key is good. Mark it as 'seen' */ + ctx->keys_verified[i] = 1; + ndone++; + } + /* Shouldn't happen */ + if (ndone != ctx->nnew) + fatal_f("ndone != ctx->nnew (%zu / %zu)", ndone, ctx->nnew); + if ((r = sshpkt_get_end(ssh)) != 0) { + error_f("protocol error"); + goto out; + } + + /* Make the edits to known_hosts */ + update_known_hosts(ctx); + out: + hostkeys_update_ctx_free(ctx); +} + +/* + * Returns non-zero if the key is accepted by HostkeyAlgorithms. + * Made slightly less trivial by the multiple RSA signature algorithm names. + */ +static int +key_accepted_by_hostkeyalgs(const struct sshkey *key) +{ + const char *ktype = sshkey_ssh_name(key); + const char *hostkeyalgs = options.hostkeyalgorithms; + + if (key == NULL || key->type == KEY_UNSPEC) + return 0; + if (key->type == KEY_RSA && + (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || + match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) + return 1; + return match_pattern_list(ktype, hostkeyalgs, 0) == 1; +} + +/* + * Handle hostkeys-00@openssh.com global request to inform the client of all + * the server's hostkeys. The keys are checked against the user's + * HostkeyAlgorithms preference before they are accepted. + */ +static int +client_input_hostkeys(struct ssh *ssh) +{ + const u_char *blob = NULL; + size_t i, len = 0; + struct sshbuf *buf = NULL; + struct sshkey *key = NULL, **tmp; + int r; + char *fp; + static int hostkeys_seen = 0; /* XXX use struct ssh */ + extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ + struct hostkeys_update_ctx *ctx = NULL; + u_int want; + + if (hostkeys_seen) + fatal_f("server already sent hostkeys"); + if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && + options.batch_mode) + return 1; /* won't ask in batchmode, so don't even try */ + if (!options.update_hostkeys || options.num_user_hostfiles <= 0) + return 1; + + ctx = xcalloc(1, sizeof(*ctx)); + while (ssh_packet_remaining(ssh) > 0) { + sshkey_free(key); + key = NULL; + if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { + error_fr(r, "parse key"); + goto out; + } + if ((r = sshkey_from_blob(blob, len, &key)) != 0) { + do_log2_fr(r, r == SSH_ERR_KEY_TYPE_UNKNOWN ? + SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, + "convert key"); + continue; + } + fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); + debug3_f("received %s key %s", sshkey_type(key), fp); + free(fp); + + if (!key_accepted_by_hostkeyalgs(key)) { + debug3_f("%s key not permitted by " + "HostkeyAlgorithms", sshkey_ssh_name(key)); + continue; + } + /* Skip certs */ + if (sshkey_is_cert(key)) { + debug3_f("%s key is a certificate; skipping", + sshkey_ssh_name(key)); + continue; + } + /* Ensure keys are unique */ + for (i = 0; i < ctx->nkeys; i++) { + if (sshkey_equal(key, ctx->keys[i])) { + error_f("received duplicated %s host key", + sshkey_ssh_name(key)); + goto out; + } + } + /* Key is good, record it */ + if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, + sizeof(*ctx->keys))) == NULL) + fatal_f("recallocarray failed nkeys = %zu", + ctx->nkeys); + ctx->keys = tmp; + ctx->keys[ctx->nkeys++] = key; + key = NULL; + } + + if (ctx->nkeys == 0) { + debug_f("server sent no hostkeys"); + goto out; + } + + if ((ctx->keys_match = calloc(ctx->nkeys, + sizeof(*ctx->keys_match))) == NULL || + (ctx->keys_verified = calloc(ctx->nkeys, + sizeof(*ctx->keys_verified))) == NULL) + fatal_f("calloc failed"); + + get_hostfile_hostname_ipaddr(host, + options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, + options.port, &ctx->host_str, + options.check_host_ip ? &ctx->ip_str : NULL); + + /* Find which keys we already know about. */ + for (i = 0; i < options.num_user_hostfiles; i++) { + debug_f("searching %s for %s / %s", + options.user_hostfiles[i], ctx->host_str, + ctx->ip_str ? ctx->ip_str : "(none)"); + if ((r = hostkeys_foreach(options.user_hostfiles[i], + hostkeys_find, ctx, ctx->host_str, ctx->ip_str, + HKF_WANT_PARSE_KEY, 0)) != 0) { + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { + debug_f("hostkeys file %s does not exist", + options.user_hostfiles[i]); + continue; + } + error_fr(r, "hostkeys_foreach failed for %s", + options.user_hostfiles[i]); + goto out; + } + } + + /* Figure out if we have any new keys to add */ + ctx->nnew = ctx->nincomplete = 0; + want = HKF_MATCH_HOST | ( options.check_host_ip ? HKF_MATCH_IP : 0); + for (i = 0; i < ctx->nkeys; i++) { + if (ctx->keys_match[i] == 0) + ctx->nnew++; + if ((ctx->keys_match[i] & want) != want) + ctx->nincomplete++; + } + + debug3_f("%zu server keys: %zu new, %zu retained, " + "%zu incomplete match. %zu to remove", ctx->nkeys, ctx->nnew, + ctx->nkeys - ctx->nnew - ctx->nincomplete, + ctx->nincomplete, ctx->nold); + + if (ctx->nnew == 0 && ctx->nold == 0) { + debug_f("no new or deprecated keys from server"); + goto out; + } + + /* Various reasons why we cannot proceed with the update */ + if (ctx->complex_hostspec) { + debug_f("CA/revocation marker, manual host list or wildcard " + "host pattern found, skipping UserKnownHostsFile update"); + goto out; + } + if (ctx->other_name_seen) { + debug_f("host key found matching a different name/address, " + "skipping UserKnownHostsFile update"); + goto out; + } + /* + * If removing keys, check whether they appear under different + * names/addresses and refuse to proceed if they do. This avoids + * cases such as hosts with multiple names becoming inconsistent + * with regards to CheckHostIP entries. + * XXX UpdateHostkeys=force to override this (and other) checks? + */ + if (ctx->nold != 0) { + if (check_old_keys_othernames(ctx) != 0) + goto out; /* error already logged */ + if (ctx->old_key_seen) { + debug_f("key(s) for %s%s%s exist under other names; " + "skipping UserKnownHostsFile update", + ctx->host_str, ctx->ip_str == NULL ? "" : ",", + ctx->ip_str == NULL ? "" : ctx->ip_str); + goto out; + } + } + + if (ctx->nnew == 0) { + /* + * We have some keys to remove or fix matching for. + * We can proceed to do this without requiring a fresh proof + * from the server. + */ + update_known_hosts(ctx); + goto out; + } + /* + * We have received previously-unseen keys from the server. + * Ask the server to confirm ownership of the private halves. + */ + debug3_f("asking server to prove ownership for %zu keys", ctx->nnew); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "hostkeys-prove-00@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ + fatal_fr(r, "prepare hostkeys-prove"); + if ((buf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + for (i = 0; i < ctx->nkeys; i++) { + if (ctx->keys_match[i]) + continue; + sshbuf_reset(buf); + if ((r = sshkey_putb(ctx->keys[i], buf)) != 0 || + (r = sshpkt_put_stringb(ssh, buf)) != 0) + fatal_fr(r, "assemble hostkeys-prove"); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send hostkeys-prove"); + client_register_global_confirm( + client_global_hostkeys_private_confirm, ctx); + ctx = NULL; /* will be freed in callback */ + + /* Success */ + out: + hostkeys_update_ctx_free(ctx); + sshkey_free(key); + sshbuf_free(buf); + /* + * NB. Return success for all cases. The server doesn't need to know + * what the client does with its hosts file. + */ + return 1; +} + +static int +client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) +{ + char *rtype; + u_char want_reply; + int r, success = 0; + + if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &want_reply)) != 0) + goto out; + debug("client_input_global_request: rtype %s want_reply %d", + rtype, want_reply); + if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) + success = client_input_hostkeys(ssh); + if (want_reply) { + if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : + SSH2_MSG_REQUEST_FAILURE)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + goto out; + } + r = 0; + out: + free(rtype); + return r; +} + +static void +client_send_env(struct ssh *ssh, int id, const char *name, const char *val) +{ + int r; + + debug("channel %d: setting env %s = \"%s\"", id, name, val); + channel_request_start(ssh, id, "env", 0); + if ((r = sshpkt_put_cstring(ssh, name)) != 0 || + (r = sshpkt_put_cstring(ssh, val)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send setenv"); +} + +void +client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, + const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, + char **env) +{ + int i, j, matched, len, r; + char *name, *val; + Channel *c = NULL; + + debug2_f("id %d", id); + + if ((c = channel_lookup(ssh, id)) == NULL) + fatal_f("channel %d: unknown channel", id); + + ssh_packet_set_interactive(ssh, want_tty, + options.ip_qos_interactive, options.ip_qos_bulk); + + if (want_tty) { + struct winsize ws; + + /* Store window size in the packet. */ + if (ioctl(in_fd, TIOCGWINSZ, &ws) == -1) + memset(&ws, 0, sizeof(ws)); + + channel_request_start(ssh, id, "pty-req", 1); + client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); + if ((r = sshpkt_put_cstring(ssh, term != NULL ? term : "")) + != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0) + fatal_fr(r, "build pty-req"); + if (tiop == NULL) + tiop = get_saved_tio(); + ssh_tty_make_modes(ssh, -1, tiop); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send pty-req"); + /* XXX wait for reply */ + c->client_tty = 1; + } + + /* Transfer any environment variables from client to server */ + if (options.num_send_env != 0 && env != NULL) { + debug("Sending environment."); + for (i = 0; env[i] != NULL; i++) { + /* Split */ + name = xstrdup(env[i]); + if ((val = strchr(name, '=')) == NULL) { + free(name); + continue; + } + *val++ = '\0'; + + matched = 0; + for (j = 0; j < options.num_send_env; j++) { + if (match_pattern(name, options.send_env[j])) { + matched = 1; + break; + } + } + if (!matched) { + debug3("Ignored env %s", name); + free(name); + continue; + } + client_send_env(ssh, id, name, val); + free(name); + } + } + for (i = 0; i < options.num_setenv; i++) { + /* Split */ + name = xstrdup(options.setenv[i]); + if ((val = strchr(name, '=')) == NULL) { + free(name); + continue; + } + *val++ = '\0'; + client_send_env(ssh, id, name, val); + free(name); + } + + len = sshbuf_len(cmd); + if (len > 0) { + if (len > 900) + len = 900; + if (want_subsystem) { + debug("Sending subsystem: %.*s", + len, (const u_char*)sshbuf_ptr(cmd)); + channel_request_start(ssh, id, "subsystem", 1); + client_expect_confirm(ssh, id, "subsystem", + CONFIRM_CLOSE); + } else { + debug("Sending command: %.*s", + len, (const u_char*)sshbuf_ptr(cmd)); + channel_request_start(ssh, id, "exec", 1); + client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); + } + if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send command"); + } else { + channel_request_start(ssh, id, "shell", 1); + client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send shell"); + } +} + +static void +client_init_dispatch(struct ssh *ssh) +{ + ssh_dispatch_init(ssh, &dispatch_protocol_error); + + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); + ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); + + /* rekeying */ + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); + + /* global request reply messages */ + ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); + ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); +} + +void +client_stop_mux(void) +{ + if (options.control_path != NULL && muxserver_sock != -1) + unlink(options.control_path); + /* + * If we are in persist mode, or don't have a shell, signal that we + * should close when all active channels are closed. + */ + if (options.control_persist || options.session_type == SESSION_TYPE_NONE) { + session_closed = 1; + setproctitle("[stopped mux]"); + } +} + +/* client specific fatal cleanup */ +void +cleanup_exit(int i) +{ + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + if (options.control_path != NULL && muxserver_sock != -1) + unlink(options.control_path); + ssh_kill_proxy_command(); + _exit(i); +} diff --git a/aosp/external/openssh/clientloop.h b/aosp/external/openssh/clientloop.h new file mode 100644 index 0000000000000000000000000000000000000000..31630551b23cba11ddf59a3e7c0031ccb8ac6458 --- /dev/null +++ b/aosp/external/openssh/clientloop.h @@ -0,0 +1,84 @@ +/* $OpenBSD: clientloop.h,v 1.37 2020/04/03 02:40:32 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +struct ssh; + +/* Client side main loop for the interactive session. */ +int client_loop(struct ssh *, int, int, int); +int client_x11_get_proto(struct ssh *, const char *, const char *, + u_int, u_int, char **, char **); +void client_global_request_reply_fwd(int, u_int32_t, void *); +void client_session2_setup(struct ssh *, int, int, int, + const char *, struct termios *, int, struct sshbuf *, char **); +char *client_request_tun_fwd(struct ssh *, int, int, int, + channel_open_fn *, void *); +void client_stop_mux(void); + +/* Escape filter for protocol 2 sessions */ +void *client_new_escape_filter_ctx(int); +void client_filter_cleanup(struct ssh *, int, void *); +int client_simple_escape_filter(struct ssh *, Channel *, char *, int); + +/* Global request confirmation callbacks */ +typedef void global_confirm_cb(struct ssh *, int, u_int32_t, void *); +void client_register_global_confirm(global_confirm_cb *, void *); + +/* Channel request confirmation callbacks */ +enum confirm_action { CONFIRM_WARN = 0, CONFIRM_CLOSE, CONFIRM_TTY }; +void client_expect_confirm(struct ssh *, int, const char *, + enum confirm_action); + +/* Multiplexing protocol version */ +#define SSHMUX_VER 4 + +/* Multiplexing control protocol flags */ +#define SSHMUX_COMMAND_OPEN 1 /* Open new connection */ +#define SSHMUX_COMMAND_ALIVE_CHECK 2 /* Check master is alive */ +#define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */ +#define SSHMUX_COMMAND_STDIO_FWD 4 /* Open stdio fwd (ssh -W) */ +#define SSHMUX_COMMAND_FORWARD 5 /* Forward only, no command */ +#define SSHMUX_COMMAND_STOP 6 /* Disable mux but not conn */ +#define SSHMUX_COMMAND_CANCEL_FWD 7 /* Cancel forwarding(s) */ +#define SSHMUX_COMMAND_PROXY 8 /* Open new connection */ + +void muxserver_listen(struct ssh *); +int muxclient(const char *); +void mux_exit_message(struct ssh *, Channel *, int); +void mux_tty_alloc_failed(struct ssh *ssh, Channel *); + diff --git a/aosp/external/openssh/compat.c b/aosp/external/openssh/compat.c new file mode 100644 index 0000000000000000000000000000000000000000..0dbea68c625f0bc131640bd5deb31ae72268551b --- /dev/null +++ b/aosp/external/openssh/compat.c @@ -0,0 +1,208 @@ +/* $OpenBSD: compat.c,v 1.119 2021/09/10 05:46:09 djm Exp $ */ +/* + * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include + +#include "xmalloc.h" +#include "packet.h" +#include "compat.h" +#include "log.h" +#include "match.h" +#include "kex.h" + +/* determine bug flags from SSH protocol banner */ +void +compat_banner(struct ssh *ssh, const char *version) +{ + int i; + static struct { + char *pat; + int bugs; + } check[] = { + { "OpenSSH_2.*," + "OpenSSH_3.0*," + "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR| + SSH_BUG_SIGTYPE}, + { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR|SSH_BUG_SIGTYPE }, + { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| + SSH_BUG_SIGTYPE}, + { "OpenSSH_2*," + "OpenSSH_3*," + "OpenSSH_4*", SSH_BUG_SIGTYPE }, + { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT| + SSH_BUG_SIGTYPE}, + { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH|SSH_BUG_SIGTYPE}, + { "OpenSSH_6.5*," + "OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD| + SSH_BUG_SIGTYPE}, + { "OpenSSH_7.4*", SSH_NEW_OPENSSH|SSH_BUG_SIGTYPE| + SSH_BUG_SIGTYPE74}, + { "OpenSSH_7.0*," + "OpenSSH_7.1*," + "OpenSSH_7.2*," + "OpenSSH_7.3*," + "OpenSSH_7.5*," + "OpenSSH_7.6*," + "OpenSSH_7.7*", SSH_NEW_OPENSSH|SSH_BUG_SIGTYPE}, + { "OpenSSH*", SSH_NEW_OPENSSH }, + { "*MindTerm*", 0 }, + { "3.0.*", SSH_BUG_DEBUG }, + { "3.0 SecureCRT*", SSH_OLD_SESSIONID }, + { "1.7 SecureFX*", SSH_OLD_SESSIONID }, + { "1.2.18*," + "1.2.19*," + "1.2.20*," + "1.2.21*," + "1.2.22*", SSH_BUG_IGNOREMSG }, + { "1.3.2*", /* F-Secure */ + SSH_BUG_IGNOREMSG }, + { "Cisco-1.*", SSH_BUG_DHGEX_LARGE| + SSH_BUG_HOSTKEYS }, + { "*SSH Compatible Server*", /* Netscreen */ + SSH_BUG_PASSWORDPAD }, + { "*OSU_0*," + "OSU_1.0*," + "OSU_1.1*," + "OSU_1.2*," + "OSU_1.3*," + "OSU_1.4*," + "OSU_1.5alpha1*," + "OSU_1.5alpha2*," + "OSU_1.5alpha3*", SSH_BUG_PASSWORDPAD }, + { "*SSH_Version_Mapper*", + SSH_BUG_SCANNER }, + { "PuTTY_Local:*," /* dev versions < Sep 2014 */ + "PuTTY-Release-0.5*," /* 0.50-0.57, DH-GEX in >=0.52 */ + "PuTTY_Release_0.5*," /* 0.58-0.59 */ + "PuTTY_Release_0.60*," + "PuTTY_Release_0.61*," + "PuTTY_Release_0.62*," + "PuTTY_Release_0.63*," + "PuTTY_Release_0.64*", + SSH_OLD_DHGEX }, + { "FuTTY*", SSH_OLD_DHGEX }, /* Putty Fork */ + { "Probe-*", + SSH_BUG_PROBE }, + { "TeraTerm SSH*," + "TTSSH/1.5.*," + "TTSSH/2.1*," + "TTSSH/2.2*," + "TTSSH/2.3*," + "TTSSH/2.4*," + "TTSSH/2.5*," + "TTSSH/2.6*," + "TTSSH/2.70*," + "TTSSH/2.71*," + "TTSSH/2.72*", SSH_BUG_HOSTKEYS }, + { "WinSCP_release_4*," + "WinSCP_release_5.0*," + "WinSCP_release_5.1," + "WinSCP_release_5.1.*," + "WinSCP_release_5.5," + "WinSCP_release_5.5.*," + "WinSCP_release_5.6," + "WinSCP_release_5.6.*," + "WinSCP_release_5.7," + "WinSCP_release_5.7.1," + "WinSCP_release_5.7.2," + "WinSCP_release_5.7.3," + "WinSCP_release_5.7.4", + SSH_OLD_DHGEX }, + { "ConfD-*", + SSH_BUG_UTF8TTYMODE }, + { "Twisted_*", 0 }, + { "Twisted*", SSH_BUG_DEBUG }, + { NULL, 0 } + }; + + /* process table, return first match */ + ssh->compat = 0; + for (i = 0; check[i].pat; i++) { + if (match_pattern_list(version, check[i].pat, 0) == 1) { + debug_f("match: %s pat %s compat 0x%08x", + version, check[i].pat, check[i].bugs); + ssh->compat = check[i].bugs; + return; + } + } + debug_f("no match: %s", version); +} + +char * +compat_cipher_proposal(struct ssh *ssh, char *cipher_prop) +{ + if (!(ssh->compat & SSH_BUG_BIGENDIANAES)) + return cipher_prop; + debug2_f("original cipher proposal: %s", cipher_prop); + if ((cipher_prop = match_filter_denylist(cipher_prop, "aes*")) == NULL) + fatal("match_filter_denylist failed"); + debug2_f("compat cipher proposal: %s", cipher_prop); + if (*cipher_prop == '\0') + fatal("No supported ciphers found"); + return cipher_prop; +} + +char * +compat_pkalg_proposal(struct ssh *ssh, char *pkalg_prop) +{ + if (!(ssh->compat & SSH_BUG_RSASIGMD5)) + return pkalg_prop; + debug2_f("original public key proposal: %s", pkalg_prop); + if ((pkalg_prop = match_filter_denylist(pkalg_prop, "ssh-rsa")) == NULL) + fatal("match_filter_denylist failed"); + debug2_f("compat public key proposal: %s", pkalg_prop); + if (*pkalg_prop == '\0') + fatal("No supported PK algorithms found"); + return pkalg_prop; +} + +char * +compat_kex_proposal(struct ssh *ssh, char *p) +{ + if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0) + return p; + debug2_f("original KEX proposal: %s", p); + if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0) + if ((p = match_filter_denylist(p, + "curve25519-sha256@libssh.org")) == NULL) + fatal("match_filter_denylist failed"); + if ((ssh->compat & SSH_OLD_DHGEX) != 0) { + if ((p = match_filter_denylist(p, + "diffie-hellman-group-exchange-sha256," + "diffie-hellman-group-exchange-sha1")) == NULL) + fatal("match_filter_denylist failed"); + } + debug2_f("compat KEX proposal: %s", p); + if (*p == '\0') + fatal("No supported key exchange algorithms found"); + return p; +} + diff --git a/aosp/external/openssh/compat.h b/aosp/external/openssh/compat.h new file mode 100644 index 0000000000000000000000000000000000000000..167409b2bd3326ca74c579e9836dcaa059ef13ff --- /dev/null +++ b/aosp/external/openssh/compat.h @@ -0,0 +1,67 @@ +/* $OpenBSD: compat.h,v 1.57 2021/06/06 03:40:39 djm Exp $ */ + +/* + * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef COMPAT_H +#define COMPAT_H + +#define SSH_BUG_UTF8TTYMODE 0x00000001 +#define SSH_BUG_SIGTYPE 0x00000002 +#define SSH_BUG_SIGTYPE74 0x00000004 +/* #define unused 0x00000008 */ +#define SSH_OLD_SESSIONID 0x00000010 +/* #define unused 0x00000020 */ +#define SSH_BUG_DEBUG 0x00000040 +/* #define unused 0x00000080 */ +#define SSH_BUG_IGNOREMSG 0x00000100 +/* #define unused 0x00000200 */ +#define SSH_BUG_PASSWORDPAD 0x00000400 +#define SSH_BUG_SCANNER 0x00000800 +#define SSH_BUG_BIGENDIANAES 0x00001000 +#define SSH_BUG_RSASIGMD5 0x00002000 +#define SSH_OLD_DHGEX 0x00004000 +#define SSH_BUG_NOREKEY 0x00008000 +/* #define unused 0x00010000 */ +/* #define unused 0x00020000 */ +/* #define unused 0x00040000 */ +/* #define unused 0x00100000 */ +#define SSH_BUG_EXTEOF 0x00200000 +#define SSH_BUG_PROBE 0x00400000 +/* #define unused 0x00800000 */ +#define SSH_OLD_FORWARD_ADDR 0x01000000 +/* #define unused 0x02000000 */ +#define SSH_NEW_OPENSSH 0x04000000 +#define SSH_BUG_DYNAMIC_RPORT 0x08000000 +#define SSH_BUG_CURVE25519PAD 0x10000000 +#define SSH_BUG_HOSTKEYS 0x20000000 +#define SSH_BUG_DHGEX_LARGE 0x40000000 + +struct ssh; + +void compat_banner(struct ssh *, const char *); +char *compat_cipher_proposal(struct ssh *, char *); +char *compat_pkalg_proposal(struct ssh *, char *); +char *compat_kex_proposal(struct ssh *, char *); +#endif diff --git a/aosp/external/openssh/config.guess b/aosp/external/openssh/config.guess new file mode 100644 index 0000000000000000000000000000000000000000..11fda528bc7b15f6a8a963a20c06cd9a6dbe5d6b --- /dev/null +++ b/aosp/external/openssh/config.guess @@ -0,0 +1,1674 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2020 Free Software Foundation, Inc. + +timestamp='2020-04-26' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2020 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$driver" + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "$UNAME_SYSTEM" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + + # If ldd exists, use it to detect musl libc. + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl + then + LIBC=musl + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ + echo unknown)` + case "$UNAME_MACHINE_ARCH" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine="${arch}${endian}"-unknown + ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "$UNAME_MACHINE_ARCH" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "$UNAME_MACHINE_ARCH" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "$UNAME_VERSION" in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "$machine-${os}${release}${abi-}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" + exit ;; + *:ekkoBSD:*:*) + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" + exit ;; + *:SolidBSD:*:*) + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" + exit ;; + *:OS108:*:*) + echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:MirBSD:*:*) + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:Sortix:*:*) + echo "$UNAME_MACHINE"-unknown-sortix + exit ;; + *:Twizzler:*:*) + echo "$UNAME_MACHINE"-unknown-twizzler + exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox + exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix"$UNAME_RELEASE" + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux"$UNAME_RELEASE" + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos"$UNAME_RELEASE" + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos"$UNAME_RELEASE" + ;; + sun4) + echo sparc-sun-sunos"$UNAME_RELEASE" + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos"$UNAME_RELEASE" + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint"$UNAME_RELEASE" + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint"$UNAME_RELEASE" + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint"$UNAME_RELEASE" + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten"$UNAME_RELEASE" + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten"$UNAME_RELEASE" + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix"$UNAME_RELEASE" + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix"$UNAME_RELEASE" + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix"$UNAME_RELEASE" + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos"$UNAME_RELEASE" + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] + then + if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ + [ "$TARGET_BINARY_INTERFACE"x = x ] + then + echo m88k-dg-dgux"$UNAME_RELEASE" + else + echo m88k-dg-dguxbcs"$UNAME_RELEASE" + fi + else + echo i586-dg-dgux"$UNAME_RELEASE" + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "$sc_cpu_version" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "$sc_kernel_bits" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "$HP_ARCH" = "" ]; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ "$HP_ARCH" = hppa2.0w ] + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux"$HPUX_REV" + exit ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo "$UNAME_MACHINE"-unknown-osf1mk + else + echo "$UNAME_MACHINE"-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi"$UNAME_RELEASE" + exit ;; + *:BSD/OS:*:*) + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" + exit ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi + else + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf + fi + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case "$UNAME_PROCESSOR" in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + i*:CYGWIN*:*) + echo "$UNAME_MACHINE"-pc-cygwin + exit ;; + *:MINGW64*:*) + echo "$UNAME_MACHINE"-pc-mingw64 + exit ;; + *:MINGW*:*) + echo "$UNAME_MACHINE"-pc-mingw32 + exit ;; + *:MSYS*:*) + echo "$UNAME_MACHINE"-pc-msys + exit ;; + i*:PW*:*) + echo "$UNAME_MACHINE"-pc-pw32 + exit ;; + *:Interix*:*) + case "$UNAME_MACHINE" in + x86) + echo i586-pc-interix"$UNAME_RELEASE" + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix"$UNAME_RELEASE" + exit ;; + IA64) + echo ia64-unknown-interix"$UNAME_RELEASE" + exit ;; + esac ;; + i*:UWIN*:*) + echo "$UNAME_MACHINE"-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-pc-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + *:GNU:*:*) + # the GNU system + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" + exit ;; + *:Minix:*:*) + echo "$UNAME_MACHINE"-unknown-minix + exit ;; + aarch64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi + else + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + cris:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + crisv32:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + e2k:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + frv:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + hexagon:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:Linux:*:*) + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + exit ;; + ia64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + k1om:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m32r*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m68*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-"$LIBC" + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-"$LIBC" + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-"$LIBC" + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-"$LIBC" + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-"$LIBC" + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-"$LIBC" + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-"$LIBC" + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" + exit ;; + sh64*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sh*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + tile*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + vax:Linux:*:*) + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" + exit ;; + x86_64:Linux:*:*) + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + exit ;; + xtensa*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo "$UNAME_MACHINE"-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo "$UNAME_MACHINE"-unknown-stop + exit ;; + i*86:atheos:*:*) + echo "$UNAME_MACHINE"-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo "$UNAME_MACHINE"-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos"$UNAME_RELEASE" + exit ;; + i*86:*DOS:*:*) + echo "$UNAME_MACHINE"-pc-msdosdjgpp + exit ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos"$UNAME_RELEASE" + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos"$UNAME_RELEASE" + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv"$UNAME_RELEASE" + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo "$UNAME_MACHINE"-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo "$UNAME_MACHINE"-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux"$UNAME_RELEASE" + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv"$UNAME_RELEASE" + else + echo mips-unknown-sysv"$UNAME_RELEASE" + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux"$UNAME_RELEASE" + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux"$UNAME_RELEASE" + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux"$UNAME_RELEASE" + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux"$UNAME_RELEASE" + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux"$UNAME_RELEASE" + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Rhapsody:*:*) + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + # shellcheck disable=SC2154 + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo "$UNAME_MACHINE"-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux"$UNAME_RELEASE" + exit ;; + *:DragonFly:*:*) + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "$UNAME_MACHINE" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" + exit ;; + i*86:rdos:*:*) + echo "$UNAME_MACHINE"-pc-rdos + exit ;; + i*86:AROS:*:*) + echo "$UNAME_MACHINE"-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo "$UNAME_MACHINE"-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; + *:Unleashed:*:*) + echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" + exit ;; +esac + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF +fi + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/aosp/external/openssh/config.sub b/aosp/external/openssh/config.sub new file mode 100644 index 0000000000000000000000000000000000000000..973a2980ac3ad53d126199f5b7446e86344347a9 --- /dev/null +++ b/aosp/external/openssh/config.sub @@ -0,0 +1,1793 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2020 Free Software Foundation, Inc. + +timestamp='2020-05-04' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2020 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +# shellcheck disable=SC2162 +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ + | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + os=linux-android + ;; + *) + basic_machine=$field1-$field2 + os=$field3 + ;; + esac + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + os= + ;; + *) + basic_machine=$field1 + os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + os=bsd + ;; + a29khif) + basic_machine=a29k-amd + os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=scout + ;; + alliant) + basic_machine=fx80-alliant + os= + ;; + altos | altos3068) + basic_machine=m68k-altos + os= + ;; + am29k) + basic_machine=a29k-none + os=bsd + ;; + amdahl) + basic_machine=580-amdahl + os=sysv + ;; + amiga) + basic_machine=m68k-unknown + os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=bsd + ;; + aros) + basic_machine=i386-pc + os=aros + ;; + aux) + basic_machine=m68k-apple + os=aux + ;; + balance) + basic_machine=ns32k-sequent + os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=linux + ;; + cegcc) + basic_machine=arm-unknown + os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=bsd + ;; + convex-c2) + basic_machine=c2-convex + os=bsd + ;; + convex-c32) + basic_machine=c32-convex + os=bsd + ;; + convex-c34) + basic_machine=c34-convex + os=bsd + ;; + convex-c38) + basic_machine=c38-convex + os=bsd + ;; + cray) + basic_machine=j90-cray + os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + os= + ;; + da30) + basic_machine=m68k-da30 + os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + os= + ;; + delta88) + basic_machine=m88k-motorola + os=sysv3 + ;; + dicos) + basic_machine=i686-pc + os=dicos + ;; + djgpp) + basic_machine=i586-pc + os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=ose + ;; + gmicro) + basic_machine=tron-gmicro + os=sysv + ;; + go32) + basic_machine=i386-pc + os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=hms + ;; + harris) + basic_machine=m88k-harris + os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=proelf + ;; + i386mach) + basic_machine=i386-mach + os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=sysv + ;; + merlin) + basic_machine=ns32k-utek + os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + os=coff + ;; + morphos) + basic_machine=powerpc-unknown + os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=moxiebox + ;; + msdos) + basic_machine=i386-pc + os=msdos + ;; + msys) + basic_machine=i686-pc + os=msys + ;; + mvs) + basic_machine=i370-ibm + os=mvs + ;; + nacl) + basic_machine=le32-unknown + os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=newsos + ;; + news1000) + basic_machine=m68030-sony + os=newsos + ;; + necv70) + basic_machine=v70-nec + os=sysv + ;; + nh3000) + basic_machine=m68k-harris + os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=cxux + ;; + nindy960) + basic_machine=i960-intel + os=nindy + ;; + mon960) + basic_machine=i960-intel + os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=ose + ;; + os68k) + basic_machine=m68k-none + os=os68k + ;; + paragon) + basic_machine=i860-intel + os=osf + ;; + parisc) + basic_machine=hppa-unknown + os=linux + ;; + pw32) + basic_machine=i586-unknown + os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=rdos + ;; + rdos32) + basic_machine=i386-pc + os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=coff + ;; + sa29200) + basic_machine=a29k-amd + os=udi + ;; + sei) + basic_machine=mips-sei + os=seiux + ;; + sequent) + basic_machine=i386-sequent + os= + ;; + sps7) + basic_machine=m68k-bull + os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + os= + ;; + stratus) + basic_machine=i860-stratus + os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + os= + ;; + sun2os3) + basic_machine=m68000-sun + os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + os= + ;; + sun3os3) + basic_machine=m68k-sun + os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + os= + ;; + sun4os3) + basic_machine=sparc-sun + os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + os= + ;; + sv1) + basic_machine=sv1-cray + os=unicos + ;; + symmetry) + basic_machine=i386-sequent + os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=unicos + ;; + t90) + basic_machine=t90-cray + os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + os=tpf + ;; + udi29k) + basic_machine=a29k-amd + os=udi + ;; + ultra3) + basic_machine=a29k-nyu + os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=none + ;; + vaxv) + basic_machine=vax-dec + os=sysv + ;; + vms) + basic_machine=vax-dec + os=vms + ;; + vsta) + basic_machine=i386-pc + os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=vxworks + ;; + xbox) + basic_machine=i686-pc + os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + os=unicos + ;; + *) + basic_machine=$1 + os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + os=${os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + os=${os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $os in + irix*) + ;; + *) + os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + os=nextstep2 + ;; + *) + os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + os=${os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + # shellcheck disable=SC2162 + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x$os != x ] +then +case $os in + # First match some system type aliases that might get confused + # with valid system types. + # solaris* is a basic system type, with this one exception. + auroraux) + os=auroraux + ;; + bluegene*) + os=cnk + ;; + solaris1 | solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + solaris) + os=solaris2 + ;; + unixware*) + os=sysv4.2uw + ;; + gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # es1800 is here to avoid being matched by es* (a different OS) + es1800*) + os=ose + ;; + # Some version numbers need modification + chorusos*) + os=chorusos + ;; + isc) + os=isc2.2 + ;; + sco6) + os=sco5v6 + ;; + sco5) + os=sco3.2v5 + ;; + sco4) + os=sco3.2v4 + ;; + sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + ;; + sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + scout) + # Don't match below + ;; + sco*) + os=sco3.2v2 + ;; + psos*) + os=psos + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + # sysv* is not here because it comes later, after sysvr4. + gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | kopensolaris* | plan9* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | knetbsd* | mirbsd* | netbsd* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ + | linux-newlib* | linux-musl* | linux-uclibc* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* \ + | morphos* | superux* | rtmk* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + qnx*) + case $cpu in + x86 | i*86) + ;; + *) + os=nto-$os + ;; + esac + ;; + hiux*) + os=hiuxwe2 + ;; + nto-qnx*) + ;; + nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + sim | xray | os68k* | v88r* \ + | windows* | osx | abug | netware* | os9* \ + | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) + ;; + linux-dietlibc) + os=linux-dietlibc + ;; + linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + lynx*178) + os=lynxos178 + ;; + lynx*5) + os=lynxos5 + ;; + lynx*) + os=lynxos + ;; + mac*) + os=`echo "$os" | sed -e 's|mac|macos|'` + ;; + opened*) + os=openedition + ;; + os400*) + os=os400 + ;; + sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` + ;; + sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` + ;; + wince*) + os=wince + ;; + utek*) + os=bsd + ;; + dynix*) + os=bsd + ;; + acis*) + os=aos + ;; + atheos*) + os=atheos + ;; + syllable*) + os=syllable + ;; + 386bsd) + os=bsd + ;; + ctix* | uts*) + os=sysv + ;; + nova*) + os=rtmk-nova + ;; + ns2) + os=nextstep2 + ;; + # Preserve the version number of sinix5. + sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + sinix*) + os=sysv4 + ;; + tpf*) + os=tpf + ;; + triton*) + os=sysv3 + ;; + oss*) + os=sysv3 + ;; + svr4*) + os=sysv4 + ;; + svr3) + os=sysv3 + ;; + sysvr4) + os=sysv4 + ;; + # This must come after sysvr4. + sysv*) + ;; + ose*) + os=ose + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint + ;; + zvmoe) + os=zvmoe + ;; + dicos*) + os=dicos + ;; + pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $cpu in + arm*) + os=eabi + ;; + *) + os=elf + ;; + esac + ;; + nacl*) + ;; + ios) + ;; + none) + ;; + *-eabi) + ;; + *) + echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $cpu-$vendor in + score-*) + os=elf + ;; + spu-*) + os=elf + ;; + *-acorn) + os=riscix1.2 + ;; + arm*-rebel) + os=linux + ;; + arm*-semi) + os=aout + ;; + c4x-* | tic4x-*) + os=coff + ;; + c8051-*) + os=elf + ;; + clipper-intergraph) + os=clix + ;; + hexagon-*) + os=elf + ;; + tic54x-*) + os=coff + ;; + tic55x-*) + os=coff + ;; + tic6x-*) + os=coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=tops20 + ;; + pdp11-*) + os=none + ;; + *-dec | vax-*) + os=ultrix4.2 + ;; + m68*-apollo) + os=domain + ;; + i386-sun) + os=sunos4.0.2 + ;; + m68000-sun) + os=sunos3 + ;; + m68*-cisco) + os=aout + ;; + mep-*) + os=elf + ;; + mips*-cisco) + os=elf + ;; + mips*-*) + os=elf + ;; + or32-*) + os=coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=sysv3 + ;; + sparc-* | *-sun) + os=sunos4.1.1 + ;; + pru-*) + os=elf + ;; + *-be) + os=beos + ;; + *-ibm) + os=aix + ;; + *-knuth) + os=mmixware + ;; + *-wec) + os=proelf + ;; + *-winbond) + os=proelf + ;; + *-oki) + os=proelf + ;; + *-hp) + os=hpux + ;; + *-hitachi) + os=hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=sysv + ;; + *-cbm) + os=amigaos + ;; + *-dg) + os=dgux + ;; + *-dolphin) + os=sysv3 + ;; + m68k-ccur) + os=rtu + ;; + m88k-omron*) + os=luna + ;; + *-next) + os=nextstep + ;; + *-sequent) + os=ptx + ;; + *-crds) + os=unos + ;; + *-ns) + os=genix + ;; + i370-*) + os=mvs + ;; + *-gould) + os=sysv + ;; + *-highlevel) + os=bsd + ;; + *-encore) + os=bsd + ;; + *-sgi) + os=irix + ;; + *-siemens) + os=sysv4 + ;; + *-masscomp) + os=rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=uxpv + ;; + *-rom68k) + os=coff + ;; + *-*bug) + os=coff + ;; + *-apple) + os=macos + ;; + *-atari*) + os=mint + ;; + *-wrs) + os=vxworks + ;; + *) + os=none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $os in + riscix*) + vendor=acorn + ;; + sunos*) + vendor=sun + ;; + cnk*|-aix*) + vendor=ibm + ;; + beos*) + vendor=be + ;; + hpux*) + vendor=hp + ;; + mpeix*) + vendor=hp + ;; + hiux*) + vendor=hitachi + ;; + unos*) + vendor=crds + ;; + dgux*) + vendor=dg + ;; + luna*) + vendor=omron + ;; + genix*) + vendor=ns + ;; + clix*) + vendor=intergraph + ;; + mvs* | opened*) + vendor=ibm + ;; + os400*) + vendor=ibm + ;; + ptx*) + vendor=sequent + ;; + tpf*) + vendor=ibm + ;; + vxsim* | vxworks* | windiss*) + vendor=wrs + ;; + aux*) + vendor=apple + ;; + hms*) + vendor=hitachi + ;; + mpw* | macos*) + vendor=apple + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + vendor=atari + ;; + vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor-$os" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/aosp/external/openssh/configure.ac b/aosp/external/openssh/configure.ac new file mode 100755 index 0000000000000000000000000000000000000000..2b3d2638617a4b0354f5e6ef3397580441c0591d --- /dev/null +++ b/aosp/external/openssh/configure.ac @@ -0,0 +1,5663 @@ +# +# Copyright (c) 1999-2004 Damien Miller +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +AC_INIT([OpenSSH], [Portable], [openssh-unix-dev@mindrot.org]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_SRCDIR([ssh.c]) +AC_LANG([C]) + +AC_CONFIG_HEADERS([config.h]) +AC_PROG_CC([cc gcc clang]) + +# XXX relax this after reimplementing logit() etc. +AC_MSG_CHECKING([if $CC supports C99-style variadic macros]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +int f(int a, int b, int c) { return a + b + c; } +#define F(a, ...) f(a, __VA_ARGS__) +]], [[return F(1, 2, -3);]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_ERROR([*** OpenSSH requires support for C99-style variadic macros]) ] +) + +AC_CANONICAL_HOST +AC_C_BIGENDIAN + +# Checks for programs. +AC_PROG_AWK +AC_PROG_CPP +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PROG_EGREP +AC_PROG_MKDIR_P +AC_CHECK_TOOLS([AR], [ar]) +AC_PATH_PROG([CAT], [cat]) +AC_PATH_PROG([KILL], [kill]) +AC_PATH_PROG([SED], [sed]) +AC_PATH_PROG([TEST_MINUS_S_SH], [bash]) +AC_PATH_PROG([TEST_MINUS_S_SH], [ksh]) +AC_PATH_PROG([TEST_MINUS_S_SH], [sh]) +AC_PATH_PROG([SH], [bash]) +AC_PATH_PROG([SH], [ksh]) +AC_PATH_PROG([SH], [sh]) +AC_PATH_PROG([GROFF], [groff]) +AC_PATH_PROG([NROFF], [nroff awf]) +AC_PATH_PROG([MANDOC], [mandoc]) +AC_SUBST([TEST_SHELL], [sh]) + +dnl select manpage formatter to be used to build "cat" format pages. +if test "x$MANDOC" != "x" ; then + MANFMT="$MANDOC" +elif test "x$NROFF" != "x" ; then + MANFMT="$NROFF -mandoc" +elif test "x$GROFF" != "x" ; then + MANFMT="$GROFF -mandoc -Tascii" +else + AC_MSG_WARN([no manpage formatter found]) + MANFMT="false" +fi +AC_SUBST([MANFMT]) + +dnl for buildpkg.sh +AC_PATH_PROG([PATH_GROUPADD_PROG], [groupadd], [groupadd], + [/usr/sbin${PATH_SEPARATOR}/etc]) +AC_PATH_PROG([PATH_USERADD_PROG], [useradd], [useradd], + [/usr/sbin${PATH_SEPARATOR}/etc]) +AC_CHECK_PROG([MAKE_PACKAGE_SUPPORTED], [pkgmk], [yes], [no]) +if test -x /sbin/sh; then + AC_SUBST([STARTUP_SCRIPT_SHELL], [/sbin/sh]) +else + AC_SUBST([STARTUP_SCRIPT_SHELL], [/bin/sh]) +fi + +# System features +AC_SYS_LARGEFILE + +if test -z "$AR" ; then + AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***]) +fi + +AC_PATH_PROG([PATH_PASSWD_PROG], [passwd]) +if test ! -z "$PATH_PASSWD_PROG" ; then + AC_DEFINE_UNQUOTED([_PATH_PASSWD_PROG], ["$PATH_PASSWD_PROG"], + [Full path of your "passwd" program]) +fi + +dnl Since autoconf doesn't support it very well, we no longer allow users to +dnl override LD, however keeping the hook here for now in case there's a use +dnl use case we overlooked and someone needs to re-enable it. Unless a good +dnl reason is found we'll be removing this in future. +LD="$CC" +AC_SUBST([LD]) + +AC_C_INLINE + +AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include ]) +AC_CHECK_DECL([LONG_LONG_MAX], [have_long_long_max=1], , [#include ]) +AC_CHECK_DECL([SYSTR_POLICY_KILL], [have_systr_policy_kill=1], , [ + #include + #include + #include +]) +AC_CHECK_DECL([RLIMIT_NPROC], + [AC_DEFINE([HAVE_RLIMIT_NPROC], [], [sys/resource.h has RLIMIT_NPROC])], , [ + #include + #include +]) +AC_CHECK_DECL([PR_SET_NO_NEW_PRIVS], [have_linux_no_new_privs=1], , [ + #include + #include +]) + +openssl=yes +AC_ARG_WITH([openssl], + [ --without-openssl Disable use of OpenSSL; use only limited internal crypto **EXPERIMENTAL** ], + [ if test "x$withval" = "xno" ; then + openssl=no + fi + ] +) +AC_MSG_CHECKING([whether OpenSSL will be used for cryptography]) +if test "x$openssl" = "xyes" ; then + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED([WITH_OPENSSL], [1], [use libcrypto for cryptography]) +else + AC_MSG_RESULT([no]) +fi + +use_stack_protector=1 +use_toolchain_hardening=1 +AC_ARG_WITH([stackprotect], + [ --without-stackprotect Don't use compiler's stack protection], [ + if test "x$withval" = "xno"; then + use_stack_protector=0 + fi ]) +AC_ARG_WITH([hardening], + [ --without-hardening Don't use toolchain hardening flags], [ + if test "x$withval" = "xno"; then + use_toolchain_hardening=0 + fi ]) + +# We use -Werror for the tests only so that we catch warnings like "this is +# on by default" for things like -fPIE. +AC_MSG_CHECKING([if $CC supports -Werror]) +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -Werror" +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int main(void) { return 0; }]])], + [ AC_MSG_RESULT([yes]) + WERROR="-Werror"], + [ AC_MSG_RESULT([no]) + WERROR="" ] +) +CFLAGS="$saved_CFLAGS" + +if test "$GCC" = "yes" || test "$GCC" = "egcs"; then + OSSH_CHECK_CFLAG_COMPILE([-pipe]) + OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option]) + OSSH_CHECK_CFLAG_COMPILE([-Wno-error=format-truncation]) + OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments]) + OSSH_CHECK_CFLAG_COMPILE([-Wall]) + OSSH_CHECK_CFLAG_COMPILE([-Wextra]) + OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith]) + OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized]) + OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare]) + OSSH_CHECK_CFLAG_COMPILE([-Wformat-security]) + OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess]) + OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) + OSSH_CHECK_CFLAG_COMPILE([-Wunused-parameter], [-Wno-unused-parameter]) + OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) + OSSH_CHECK_CFLAG_COMPILE([-Wimplicit-fallthrough]) + OSSH_CHECK_CFLAG_COMPILE([-Wmisleading-indentation]) + OSSH_CHECK_CFLAG_COMPILE([-Wbitwise-instead-of-logical]) + OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) + if test "x$use_toolchain_hardening" = "x1"; then + OSSH_CHECK_CFLAG_COMPILE([-mretpoline]) # clang + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,retpolineplt]) + OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro]) + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now]) + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack]) + # NB. -ftrapv expects certain support functions to be present in + # the compiler library (libgcc or similar) to detect integer operations + # that can overflow. We must check that the result of enabling it + # actually links. The test program compiled/linked includes a number + # of integer operations that should exercise this. + OSSH_CHECK_CFLAG_LINK([-ftrapv]) + OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=all]) + OSSH_CHECK_CFLAG_COMPILE([-ftrivial-auto-var-init=zero]) + fi + AC_MSG_CHECKING([gcc version]) + GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` + case $GCC_VER in + 1.*) no_attrib_nonnull=1 ;; + 2.8* | 2.9*) + no_attrib_nonnull=1 + ;; + 2.*) no_attrib_nonnull=1 ;; + *) ;; + esac + AC_MSG_RESULT([$GCC_VER]) + + AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-builtin-memset" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ char b[10]; memset(b, 0, sizeof(b)); ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" ] + ) + + # -fstack-protector-all doesn't always work for some GCC versions + # and/or platforms, so we test if we can. If it's not supported + # on a given platform gcc will emit a warning so we use -Werror. + if test "x$use_stack_protector" = "x1"; then + for t in -fstack-protector-strong -fstack-protector-all \ + -fstack-protector; do + AC_MSG_CHECKING([if $CC supports $t]) + saved_CFLAGS="$CFLAGS" + saved_LDFLAGS="$LDFLAGS" + CFLAGS="$CFLAGS $t -Werror" + LDFLAGS="$LDFLAGS $t -Werror" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + int func (int t) {char b[100]; snprintf(b,sizeof b,"%d",t); return t;} + ]], + [[ + char x[256]; + snprintf(x, sizeof(x), "XXX%d", func(1)); + ]])], + [ AC_MSG_RESULT([yes]) + CFLAGS="$saved_CFLAGS $t" + LDFLAGS="$saved_LDFLAGS $t" + AC_MSG_CHECKING([if $t works]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + int func (int t) {char b[100]; snprintf(b,sizeof b,"%d",t); return t;} + ]], + [[ + char x[256]; + snprintf(x, sizeof(x), "XXX%d", func(1)); + ]])], + [ AC_MSG_RESULT([yes]) + break ], + [ AC_MSG_RESULT([no]) ], + [ AC_MSG_WARN([cross compiling: cannot test]) + break ] + ) + ], + [ AC_MSG_RESULT([no]) ] + ) + CFLAGS="$saved_CFLAGS" + LDFLAGS="$saved_LDFLAGS" + done + fi + + if test -z "$have_llong_max"; then + # retry LLONG_MAX with -std=gnu99, needed on some Linuxes + unset ac_cv_have_decl_LLONG_MAX + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -std=gnu99" + AC_CHECK_DECL([LLONG_MAX], + [have_llong_max=1], + [CFLAGS="$saved_CFLAGS"], + [#include ] + ) + fi +fi + +AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include +__attribute__((__unused__)) static void foo(void){return;}]], + [[ exit(0); ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1, + [compiler does not accept __attribute__ on return types]) ] +) + +AC_MSG_CHECKING([if compiler allows __attribute__ prototype args]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include +typedef void foo(const char *, ...) __attribute__((format(printf, 1, 2)));]], + [[ exit(0); ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + AC_DEFINE(NO_ATTRIBUTE_ON_PROTOTYPE_ARGS, 1, + [compiler does not accept __attribute__ on prototype args]) ] +) + +AC_MSG_CHECKING([if compiler supports variable length arrays]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[ int i; for (i=0; i<3; i++){int a[i]; a[i-1]=0;} exit(0); ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE(VARIABLE_LENGTH_ARRAYS, [1], + [compiler supports variable length arrays]) ], + [ AC_MSG_RESULT([no]) ] +) + +AC_MSG_CHECKING([if compiler accepts variable declarations after code]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[ int a; a = 1; int b = 1; exit(a-b); ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE(VARIABLE_DECLARATION_AFTER_CODE, [1], + [compiler variable declarations after code]) ], + [ AC_MSG_RESULT([no]) ] +) + +if test "x$no_attrib_nonnull" != "x1" ; then + AC_DEFINE([HAVE_ATTRIBUTE__NONNULL__], [1], [Have attribute nonnull]) +fi + +AC_ARG_WITH([rpath], + [ --without-rpath Disable auto-added -R linker paths], + [ + if test "x$withval" = "xno" ; then + rpath_opt="" + elif test "x$withval" = "xyes" ; then + rpath_opt="-R" + else + rpath_opt="$withval" + fi + ] +) + +# Allow user to specify flags +AC_ARG_WITH([cflags], + [ --with-cflags Specify additional flags to pass to compiler], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + CFLAGS="$CFLAGS $withval" + fi + ] +) + +AC_ARG_WITH([cflags-after], + [ --with-cflags-after Specify additional flags to pass to compiler after configure], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + CFLAGS_AFTER="$withval" + fi + ] +) +AC_ARG_WITH([cppflags], + [ --with-cppflags Specify additional flags to pass to preprocessor] , + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + CPPFLAGS="$CPPFLAGS $withval" + fi + ] +) +AC_ARG_WITH([ldflags], + [ --with-ldflags Specify additional flags to pass to linker], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + LDFLAGS="$LDFLAGS $withval" + fi + ] +) +AC_ARG_WITH([ldflags-after], + [ --with-ldflags-after Specify additional flags to pass to linker after configure], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + LDFLAGS_AFTER="$withval" + fi + ] +) +AC_ARG_WITH([libs], + [ --with-libs Specify additional libraries to link with], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + LIBS="$LIBS $withval" + fi + ] +) +AC_ARG_WITH([Werror], + [ --with-Werror Build main code with -Werror], + [ + if test -n "$withval" && test "x$withval" != "xno"; then + werror_flags="-Werror" + if test "x${withval}" != "xyes"; then + werror_flags="$withval" + fi + fi + ] +) + +AC_CHECK_HEADERS([ \ + blf.h \ + bstring.h \ + crypt.h \ + crypto/sha2.h \ + dirent.h \ + endian.h \ + elf.h \ + err.h \ + features.h \ + fcntl.h \ + floatingpoint.h \ + fnmatch.h \ + getopt.h \ + glob.h \ + ia.h \ + iaf.h \ + ifaddrs.h \ + inttypes.h \ + langinfo.h \ + limits.h \ + locale.h \ + login.h \ + maillock.h \ + ndir.h \ + net/if_tun.h \ + netdb.h \ + netgroup.h \ + pam/pam_appl.h \ + paths.h \ + poll.h \ + pty.h \ + readpassphrase.h \ + rpc/types.h \ + security/pam_appl.h \ + sha2.h \ + shadow.h \ + stddef.h \ + stdint.h \ + string.h \ + strings.h \ + sys/bitypes.h \ + sys/byteorder.h \ + sys/bsdtty.h \ + sys/cdefs.h \ + sys/dir.h \ + sys/file.h \ + sys/mman.h \ + sys/label.h \ + sys/ndir.h \ + sys/param.h \ + sys/poll.h \ + sys/prctl.h \ + sys/procctl.h \ + sys/pstat.h \ + sys/ptrace.h \ + sys/random.h \ + sys/select.h \ + sys/stat.h \ + sys/stream.h \ + sys/stropts.h \ + sys/strtio.h \ + sys/statvfs.h \ + sys/sysmacros.h \ + sys/time.h \ + sys/timers.h \ + sys/vfs.h \ + time.h \ + tmpdir.h \ + ttyent.h \ + ucred.h \ + unistd.h \ + usersec.h \ + util.h \ + utime.h \ + utmp.h \ + utmpx.h \ + vis.h \ + wchar.h \ +]) + +# On some platforms (eg SunOS4) sys/audit.h requires sys/[time|types|label.h] +# to be included first. +AC_CHECK_HEADERS([sys/audit.h], [], [], [ +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_LABEL_H +# include +#endif +]) + +# sys/capsicum.h requires sys/types.h +AC_CHECK_HEADERS([sys/capsicum.h], [], [], [ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +]) + +# net/route.h requires sys/socket.h and sys/types.h. +# sys/sysctl.h also requires sys/param.h +AC_CHECK_HEADERS([net/route.h sys/sysctl.h], [], [], [ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +#include +]) + +# lastlog.h requires sys/time.h to be included first on Solaris +AC_CHECK_HEADERS([lastlog.h], [], [], [ +#ifdef HAVE_SYS_TIME_H +# include +#endif +]) + +# sys/ptms.h requires sys/stream.h to be included first on Solaris +AC_CHECK_HEADERS([sys/ptms.h], [], [], [ +#ifdef HAVE_SYS_STREAM_H +# include +#endif +]) + +# login_cap.h requires sys/types.h on NetBSD +AC_CHECK_HEADERS([login_cap.h], [], [], [ +#include +]) + +# older BSDs need sys/param.h before sys/mount.h +AC_CHECK_HEADERS([sys/mount.h], [], [], [ +#include +]) + +# Android requires sys/socket.h to be included before sys/un.h +AC_CHECK_HEADERS([sys/un.h], [], [], [ +#include +#include +]) + +# Messages for features tested for in target-specific section +SIA_MSG="no" +SPC_MSG="no" +SP_MSG="no" +SPP_MSG="no" + +# Support for Solaris/Illumos privileges (this test is used by both +# the --with-solaris-privs option and --with-sandbox=solaris). +SOLARIS_PRIVS="no" + +# Check for some target-specific stuff +case "$host" in +*-*-aix*) + # Some versions of VAC won't allow macro redefinitions at + # -qlanglevel=ansi, and autoconf 2.60 sometimes insists on using that + # particularly with older versions of vac or xlc. + # It also throws errors about null macro arguments, but these are + # not fatal. + AC_MSG_CHECKING([if compiler allows macro redefinitions]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#define testmacro foo +#define testmacro bar]], + [[ exit(0); ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CC="`echo $CC | sed 's/-qlanglvl\=ansi//g'`" + CFLAGS="`echo $CFLAGS | sed 's/-qlanglvl\=ansi//g'`" + CPPFLAGS="`echo $CPPFLAGS | sed 's/-qlanglvl\=ansi//g'`" + ] + ) + + AC_MSG_CHECKING([how to specify blibpath for linker ($LD)]) + if (test -z "$blibpath"); then + blibpath="/usr/lib:/lib" + fi + saved_LDFLAGS="$LDFLAGS" + if test "$GCC" = "yes"; then + flags="-Wl,-blibpath: -Wl,-rpath, -blibpath:" + else + flags="-blibpath: -Wl,-blibpath: -Wl,-rpath," + fi + for tryflags in $flags ;do + if (test -z "$blibflags"); then + LDFLAGS="$saved_LDFLAGS $tryflags$blibpath" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [blibflags=$tryflags], []) + fi + done + if (test -z "$blibflags"); then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([*** must be able to specify blibpath on AIX - check config.log]) + else + AC_MSG_RESULT([$blibflags]) + fi + LDFLAGS="$saved_LDFLAGS" + dnl Check for authenticate. Might be in libs.a on older AIXes + AC_CHECK_FUNC([authenticate], [AC_DEFINE([WITH_AIXAUTHENTICATE], [1], + [Define if you want to enable AIX4's authenticate function])], + [AC_CHECK_LIB([s], [authenticate], + [ AC_DEFINE([WITH_AIXAUTHENTICATE]) + LIBS="$LIBS -ls" + ]) + ]) + dnl Check for various auth function declarations in headers. + AC_CHECK_DECLS([authenticate, loginrestrictions, loginsuccess, + passwdexpired, setauthdb], , , [#include ]) + dnl Check if loginfailed is declared and takes 4 arguments (AIX >= 5.2) + AC_CHECK_DECLS([loginfailed], + [AC_MSG_CHECKING([if loginfailed takes 4 arguments]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ (void)loginfailed("user","host","tty",0); ]])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([AIX_LOGINFAILED_4ARG], [1], + [Define if your AIX loginfailed() function + takes 4 arguments (AIX >= 5.2)])], [AC_MSG_RESULT([no]) + ])], + [], + [#include ] + ) + AC_CHECK_FUNCS([getgrset setauthdb]) + AC_CHECK_DECL([F_CLOSEM], + AC_DEFINE([HAVE_FCNTL_CLOSEM], [1], [Use F_CLOSEM fcntl for closefrom]), + [], + [ #include + #include ] + ) + check_for_aix_broken_getaddrinfo=1 + AC_DEFINE([SETEUID_BREAKS_SETUID], [1], + [Define if your platform breaks doing a seteuid before a setuid]) + AC_DEFINE([BROKEN_SETREUID], [1], [Define if your setreuid() is broken]) + AC_DEFINE([BROKEN_SETREGID], [1], [Define if your setregid() is broken]) + dnl AIX handles lastlog as part of its login message + AC_DEFINE([DISABLE_LASTLOG], [1], [Define if you don't want to use lastlog]) + AC_DEFINE([LOGIN_NEEDS_UTMPX], [1], + [Some systems need a utmpx entry for /bin/login to work]) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], + [Define to a Set Process Title type if your system is + supported by bsd-setproctitle.c]) + AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], + [AIX 5.2 and 5.3 (and presumably newer) require this]) + AC_DEFINE([PTY_ZEROREAD], [1], [read(1) can return 0 for a non-closed fd]) + AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) + AC_DEFINE([BROKEN_STRNDUP], 1, [strndup broken, see APAR IY61211]) + AC_DEFINE([BROKEN_STRNLEN], 1, [strnlen broken, see APAR IY62551]) + ;; +*-*-IGNOREandroid*) + AC_DEFINE([DISABLE_UTMP], [1], [Define if you don't want to use utmp]) + AC_DEFINE([DISABLE_WTMP], [1], [Define if you don't want to use wtmp]) + ;; +*-*-cygwin*) + check_for_libcrypt_later=1 + LIBS="$LIBS /usr/lib/textreadmode.o" + AC_DEFINE([HAVE_CYGWIN], [1], [Define if you are on Cygwin]) + AC_DEFINE([USE_PIPES], [1], [Use PIPES instead of a socketpair()]) + AC_DEFINE([NO_UID_RESTORATION_TEST], [1], + [Define to disable UID restoration test]) + AC_DEFINE([DISABLE_SHADOW], [1], + [Define if you want to disable shadow passwords]) + AC_DEFINE([NO_X11_UNIX_SOCKETS], [1], + [Define if X11 doesn't support AF_UNIX sockets on that system]) + AC_DEFINE([DISABLE_FD_PASSING], [1], + [Define if your platform needs to skip post auth + file descriptor passing]) + AC_DEFINE([SSH_IOBUFSZ], [65535], [Windows is sensitive to read buffer size]) + AC_DEFINE([FILESYSTEM_NO_BACKSLASH], [1], [File names may not contain backslash characters]) + # Cygwin defines optargs, optargs as declspec(dllimport) for historical + # reasons which cause compile warnings, so we disable those warnings. + OSSH_CHECK_CFLAG_COMPILE([-Wno-attributes]) + ;; +*-*-dgux*) + AC_DEFINE([IP_TOS_IS_BROKEN], [1], + [Define if your system choked on IP TOS setting]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + ;; +*-*-darwin*) + use_pie=auto + AC_MSG_CHECKING([if we have working getaddrinfo]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) + exit(0); + else + exit(1); +} + ]])], + [AC_MSG_RESULT([working])], + [AC_MSG_RESULT([buggy]) + AC_DEFINE([BROKEN_GETADDRINFO], [1], + [getaddrinfo is broken (if present)]) + ], + [AC_MSG_RESULT([assume it is working])]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect]) + AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1], + [Define if your resolver libs need this for getrrsetbyname]) + AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) + AC_DEFINE([SSH_TUN_COMPAT_AF], [1], + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) + m4_pattern_allow([AU_IPv]) + AC_CHECK_DECL([AU_IPv4], [], + AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) + [#include ] + AC_DEFINE([LASTLOG_WRITE_PUTUTXLINE], [1], + [Define if pututxline updates lastlog too]) + ) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], + [Define to a Set Process Title type if your system is + supported by bsd-setproctitle.c]) + AC_CHECK_FUNCS([sandbox_init]) + AC_CHECK_HEADERS([sandbox.h]) + AC_CHECK_LIB([sandbox], [sandbox_apply], [ + SSHDLIBS="$SSHDLIBS -lsandbox" + ]) + # proc_pidinfo()-based closefrom() replacement. + AC_CHECK_HEADERS([libproc.h]) + AC_CHECK_FUNCS([proc_pidinfo]) + # poll(2) is broken for character-special devices (at least). + # cf. Apple bug 3710161 (not public, but searchable) + AC_DEFINE([BROKEN_POLL], [1], + [System poll(2) implementation is broken]) + ;; +*-*-dragonfly*) + SSHDLIBS="$SSHDLIBS -lcrypt" + TEST_MALLOC_OPTIONS="AFGJPRX" + ;; +*-*-haiku*) + LIBS="$LIBS -lbsd " + CFLAGS="$CFLAGS -D_BSD_SOURCE" + AC_CHECK_LIB([network], [socket]) + AC_DEFINE([HAVE_U_INT64_T]) + AC_DEFINE([DISABLE_UTMPX], [1], [no utmpx]) + MANTYPE=man + ;; +*-*-hpux*) + # first we define all of the options common to all HP-UX releases + CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" + IPADDR_IN_DISPLAY=yes + AC_DEFINE([USE_PIPES]) + AC_DEFINE([LOGIN_NEEDS_UTMPX]) + AC_DEFINE([LOCKED_PASSWD_STRING], ["*"], + [String used in /etc/passwd to denote locked account]) + AC_DEFINE([SPT_TYPE], [SPT_PSTAT]) + AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) + maildir="/var/mail" + LIBS="$LIBS -lsec" + AC_CHECK_LIB([xnet], [t_error], , + [AC_MSG_ERROR([*** -lxnet needed on HP-UX - check config.log ***])]) + + # next, we define all of the options specific to major releases + case "$host" in + *-*-hpux10*) + if test -z "$GCC"; then + CFLAGS="$CFLAGS -Ae" + fi + AC_DEFINE([BROKEN_GETLINE], [1], [getline is not what we expect]) + ;; + *-*-hpux11*) + AC_DEFINE([PAM_SUN_CODEBASE], [1], + [Define if you are using Solaris-derived PAM which + passes pam_messages to the conversation function + with an extra level of indirection]) + AC_DEFINE([DISABLE_UTMP], [1], + [Define if you don't want to use utmp]) + AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) + check_for_hpux_broken_getaddrinfo=1 + check_for_conflicting_getspnam=1 + ;; + esac + + # lastly, we define options specific to minor releases + case "$host" in + *-*-hpux10.26) + AC_DEFINE([HAVE_SECUREWARE], [1], + [Define if you have SecureWare-based + protected password database]) + disable_ptmx_check=yes + LIBS="$LIBS -lsecpw" + ;; + esac + ;; +*-*-irix5*) + PATH="$PATH:/usr/etc" + AC_DEFINE([BROKEN_INET_NTOA], [1], + [Define if you system's inet_ntoa is busted + (e.g. Irix gcc issue)]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([WITH_ABBREV_NO_TTY], [1], + [Define if you shouldn't strip 'tty' from your + ttyname in [uw]tmp]) + AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) + ;; +*-*-irix6*) + PATH="$PATH:/usr/etc" + AC_DEFINE([WITH_IRIX_ARRAY], [1], + [Define if you have/want arrays + (cluster-wide session management, not C arrays)]) + AC_DEFINE([WITH_IRIX_PROJECT], [1], + [Define if you want IRIX project management]) + AC_DEFINE([WITH_IRIX_AUDIT], [1], + [Define if you want IRIX audit trails]) + AC_CHECK_FUNC([jlimit_startjob], [AC_DEFINE([WITH_IRIX_JOBS], [1], + [Define if you want IRIX kernel jobs])]) + AC_DEFINE([BROKEN_INET_NTOA]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([BROKEN_UPDWTMPX], [1], [updwtmpx is broken (if present)]) + AC_DEFINE([WITH_ABBREV_NO_TTY]) + AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) + ;; +*-*-k*bsd*-gnu | *-*-kopensolaris*-gnu) + check_for_libcrypt_later=1 + AC_DEFINE([PAM_TTY_KLUDGE]) + AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"]) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) + AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) + AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) + ;; +*-*-linux*) + no_dev_ptmx=1 + use_pie=auto + check_for_libcrypt_later=1 + check_for_openpty_ctty_bug=1 + dnl Target SUSv3/POSIX.1-2001 plus BSD specifics. + dnl _DEFAULT_SOURCE is the new name for _BSD_SOURCE + CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_DEFAULT_SOURCE" + false && + AC_DEFINE([BROKEN_CLOSEFROM], [1], [broken in chroots on older kernels]) + AC_DEFINE([PAM_TTY_KLUDGE], [1], + [Work around problematic Linux PAM modules handling of PAM_TTY]) + AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"], + [String used in /etc/passwd to denote locked account]) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) + AC_DEFINE([LINK_OPNOTSUPP_ERRNO], [EPERM], + [Define to whatever link() returns for "not supported" + if it doesn't return EOPNOTSUPP.]) + AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) + false && + AC_DEFINE([USE_BTMP]) + AC_DEFINE([LINUX_OOM_ADJUST], [1], [Adjust Linux out-of-memory killer]) + inet6_default_4in6=yes + case `uname -r` in + 1.*|2.0.*) + AC_DEFINE([BROKEN_CMSG_TYPE], [1], + [Define if cmsg_type is not passed correctly]) + ;; + esac + # tun(4) forwarding compat code + AC_CHECK_HEADERS([linux/if_tun.h]) + if test "x$ac_cv_header_linux_if_tun_h" = "xyes" ; then + AC_DEFINE([SSH_TUN_LINUX], [1], + [Open tunnel devices the Linux tun/tap way]) + AC_DEFINE([SSH_TUN_COMPAT_AF], [1], + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) + fi + AC_CHECK_HEADER([linux/if.h], + AC_DEFINE([SYS_RDOMAIN_LINUX], [1], + [Support routing domains using Linux VRF]), [], [ +#ifdef HAVE_SYS_TYPES_H +# include +#endif + ]) + AC_CHECK_HEADERS([linux/seccomp.h linux/filter.h linux/audit.h], [], + [], [#include ]) + # Obtain MIPS ABI + case "$host" in + mips*) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if _MIPS_SIM != _ABIO32 +#error +#endif + ]])],[mips_abi="o32"],[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if _MIPS_SIM != _ABIN32 +#error +#endif + ]])],[mips_abi="n32"],[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#if _MIPS_SIM != _ABI64 +#error +#endif + ]])],[mips_abi="n64"],[AC_MSG_ERROR([unknown MIPS ABI]) + ]) + ]) + ]) + ;; + esac + AC_MSG_CHECKING([for seccomp architecture]) + seccomp_audit_arch= + case "$host" in + x86_64-*) + seccomp_audit_arch=AUDIT_ARCH_X86_64 + ;; + i*86-*) + seccomp_audit_arch=AUDIT_ARCH_I386 + ;; + arm*-*) + seccomp_audit_arch=AUDIT_ARCH_ARM + ;; + aarch64*-*) + seccomp_audit_arch=AUDIT_ARCH_AARCH64 + ;; + s390x-*) + seccomp_audit_arch=AUDIT_ARCH_S390X + ;; + s390-*) + seccomp_audit_arch=AUDIT_ARCH_S390 + ;; + powerpc64-*) + seccomp_audit_arch=AUDIT_ARCH_PPC64 + ;; + powerpc64le-*) + seccomp_audit_arch=AUDIT_ARCH_PPC64LE + ;; + mips-*) + seccomp_audit_arch=AUDIT_ARCH_MIPS + ;; + mipsel-*) + seccomp_audit_arch=AUDIT_ARCH_MIPSEL + ;; + mips64-*) + case "$mips_abi" in + "n32") + seccomp_audit_arch=AUDIT_ARCH_MIPS64N32 + ;; + "n64") + seccomp_audit_arch=AUDIT_ARCH_MIPS64 + ;; + esac + ;; + mips64el-*) + case "$mips_abi" in + "n32") + seccomp_audit_arch=AUDIT_ARCH_MIPSEL64N32 + ;; + "n64") + seccomp_audit_arch=AUDIT_ARCH_MIPSEL64 + ;; + esac + ;; + riscv64-*) + seccomp_audit_arch=AUDIT_ARCH_RISCV64 + ;; + esac + if test "x$seccomp_audit_arch" != "x" ; then + AC_MSG_RESULT(["$seccomp_audit_arch"]) + AC_DEFINE_UNQUOTED([SECCOMP_AUDIT_ARCH], [$seccomp_audit_arch], + [Specify the system call convention in use]) + else + AC_MSG_RESULT([architecture not supported]) + fi + ;; +*-*-minix) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + # poll(2) seems to choke on /dev/null; "Bad file descriptor" + AC_DEFINE([BROKEN_POLL], [1], + [System poll(2) implementation is broken]) + ;; +mips-sony-bsd|mips-sony-newsos4) + AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) + SONY=1 + ;; +*-*-netbsd*) + check_for_libcrypt_before=1 + if test "x$withval" != "xno" ; then + rpath_opt="-R" + fi + CPPFLAGS="$CPPFLAGS -D_OPENBSD_SOURCE" + AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) + AC_CHECK_HEADER([net/if_tap.h], , + AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) + TEST_MALLOC_OPTIONS="AJRX" + AC_DEFINE([BROKEN_READ_COMPARISON], [1], + [NetBSD read function is sometimes redirected, breaking atomicio comparisons against it]) + ;; +*-*-freebsd*) + check_for_libcrypt_later=1 + AC_DEFINE([LOCKED_PASSWD_PREFIX], ["*LOCKED*"], [Account locked with pw(1)]) + AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) + AC_CHECK_HEADER([net/if_tap.h], , + AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) + AC_DEFINE([BROKEN_GLOB], [1], [FreeBSD glob does not do what we need]) + TEST_MALLOC_OPTIONS="AJRX" + # Preauth crypto occasionally uses file descriptors for crypto offload + # and will crash if they cannot be opened. + AC_DEFINE([SANDBOX_SKIP_RLIMIT_NOFILE], [1], + [define if setrlimit RLIMIT_NOFILE breaks things]) + case "$host" in + *-*-freebsd9.*|*-*-freebsd10.*) + # Capsicum on 9 and 10 do not allow ppoll() so don't auto-enable. + disable_capsicum=yes + esac + ;; +*-*-bsdi*) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + ;; +*-next-*) + conf_lastlog_location="/usr/adm/lastlog" + conf_utmp_location=/etc/utmp + conf_wtmp_location=/usr/adm/wtmp + maildir=/usr/spool/mail + AC_DEFINE([HAVE_NEXT], [1], [Define if you are on NeXT]) + AC_DEFINE([USE_PIPES]) + AC_DEFINE([BROKEN_SAVED_UIDS], [1], [Needed for NeXT]) + ;; +*-*-openbsd*) + use_pie=auto + AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel]) + AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded]) + AC_DEFINE([SSH_TUN_OPENBSD], [1], [Open tunnel devices the OpenBSD way]) + AC_DEFINE([SYSLOG_R_SAFE_IN_SIGHAND], [1], + [syslog_r function is safe to use in in a signal handler]) + TEST_MALLOC_OPTIONS="AFGJPRX" + ;; +*-*-solaris*) + if test "x$withval" != "xno" ; then + rpath_opt="-R" + fi + AC_DEFINE([PAM_SUN_CODEBASE]) + AC_DEFINE([LOGIN_NEEDS_UTMPX]) + AC_DEFINE([PAM_TTY_KLUDGE]) + AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], + [Define if pam_chauthtok wants real uid set + to the unpriv'ed user]) + AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) + # Pushing STREAMS modules will cause sshd to acquire a controlling tty. + AC_DEFINE([SSHD_ACQUIRES_CTTY], [1], + [Define if sshd somehow reacquires a controlling TTY + after setsid()]) + AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd + in case the name is longer than 8 chars]) + AC_DEFINE([BROKEN_TCGETATTR_ICANON], [1], [tcgetattr with ICANON may hang]) + external_path_file=/etc/default/login + # hardwire lastlog location (can't detect it on some versions) + conf_lastlog_location="/var/adm/lastlog" + AC_MSG_CHECKING([for obsolete utmp and wtmp in solaris2.x]) + sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'` + if test "$sol2ver" -ge 8; then + AC_MSG_RESULT([yes]) + AC_DEFINE([DISABLE_UTMP]) + AC_DEFINE([DISABLE_WTMP], [1], + [Define if you don't want to use wtmp]) + else + AC_MSG_RESULT([no]) + fi + AC_CHECK_FUNCS([setpflags]) + AC_CHECK_FUNCS([setppriv]) + AC_CHECK_FUNCS([priv_basicset]) + AC_CHECK_HEADERS([priv.h]) + AC_ARG_WITH([solaris-contracts], + [ --with-solaris-contracts Enable Solaris process contracts (experimental)], + [ + AC_CHECK_LIB([contract], [ct_tmpl_activate], + [ AC_DEFINE([USE_SOLARIS_PROCESS_CONTRACTS], [1], + [Define if you have Solaris process contracts]) + LIBS="$LIBS -lcontract" + SPC_MSG="yes" ], ) + ], + ) + AC_ARG_WITH([solaris-projects], + [ --with-solaris-projects Enable Solaris projects (experimental)], + [ + AC_CHECK_LIB([project], [setproject], + [ AC_DEFINE([USE_SOLARIS_PROJECTS], [1], + [Define if you have Solaris projects]) + LIBS="$LIBS -lproject" + SP_MSG="yes" ], ) + ], + ) + AC_ARG_WITH([solaris-privs], + [ --with-solaris-privs Enable Solaris/Illumos privileges (experimental)], + [ + AC_MSG_CHECKING([for Solaris/Illumos privilege support]) + if test "x$ac_cv_func_setppriv" = "xyes" -a \ + "x$ac_cv_header_priv_h" = "xyes" ; then + SOLARIS_PRIVS=yes + AC_MSG_RESULT([found]) + AC_DEFINE([NO_UID_RESTORATION_TEST], [1], + [Define to disable UID restoration test]) + AC_DEFINE([USE_SOLARIS_PRIVS], [1], + [Define if you have Solaris privileges]) + SPP_MSG="yes" + else + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([*** must have support for Solaris privileges to use --with-solaris-privs]) + fi + ], + ) + TEST_SHELL=$SHELL # let configure find us a capable shell + ;; +*-*-sunos4*) + CPPFLAGS="$CPPFLAGS -DSUNOS4" + AC_CHECK_FUNCS([getpwanam]) + AC_DEFINE([PAM_SUN_CODEBASE]) + conf_utmp_location=/etc/utmp + conf_wtmp_location=/var/adm/wtmp + conf_lastlog_location=/var/adm/lastlog + AC_DEFINE([USE_PIPES]) + AC_DEFINE([DISABLE_UTMPX], [1], [no utmpx]) + ;; +*-ncr-sysv*) + LIBS="$LIBS -lc89" + AC_DEFINE([USE_PIPES]) + AC_DEFINE([SSHD_ACQUIRES_CTTY]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + ;; +*-sni-sysv*) + # /usr/ucblib MUST NOT be searched on ReliantUNIX + AC_CHECK_LIB([dl], [dlsym], ,) + # -lresolv needs to be at the end of LIBS or DNS lookups break + AC_CHECK_LIB([resolv], [res_query], [ LIBS="$LIBS -lresolv" ]) + IPADDR_IN_DISPLAY=yes + AC_DEFINE([USE_PIPES]) + AC_DEFINE([IP_TOS_IS_BROKEN]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([SSHD_ACQUIRES_CTTY]) + external_path_file=/etc/default/login + # /usr/ucblib/libucb.a no longer needed on ReliantUNIX + # Attention: always take care to bind libsocket and libnsl before libc, + # otherwise you will find lots of "SIOCGPGRP errno 22" on syslog + ;; +# UnixWare 1.x, UnixWare 2.x, and others based on code from Univel. +*-*-sysv4.2*) + AC_DEFINE([USE_PIPES]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd]) + AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) + TEST_SHELL=$SHELL # let configure find us a capable shell + ;; +# UnixWare 7.x, OpenUNIX 8 +*-*-sysv5*) + CPPFLAGS="$CPPFLAGS -Dvsnprintf=_xvsnprintf -Dsnprintf=_xsnprintf" + AC_DEFINE([UNIXWARE_LONG_PASSWORDS], [1], [Support passwords > 8 chars]) + AC_DEFINE([USE_PIPES]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_GETADDRINFO]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([PASSWD_NEEDS_USERNAME]) + AC_DEFINE([BROKEN_TCGETATTR_ICANON]) + TEST_SHELL=$SHELL # let configure find us a capable shell + check_for_libcrypt_later=1 + case "$host" in + *-*-sysv5SCO_SV*) # SCO OpenServer 6.x + maildir=/var/spool/mail + AC_DEFINE([BROKEN_UPDWTMPX]) + AC_CHECK_LIB([prot], [getluid], [ LIBS="$LIBS -lprot" + AC_CHECK_FUNCS([getluid setluid], , , [-lprot]) + ], , ) + ;; + *) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) + ;; + esac + ;; +*-*-sysv*) + ;; +# SCO UNIX and OEM versions of SCO UNIX +*-*-sco3.2v4*) + AC_MSG_ERROR("This Platform is no longer supported.") + ;; +# SCO OpenServer 5.x +*-*-sco3.2v5*) + if test -z "$GCC"; then + CFLAGS="$CFLAGS -belf" + fi + LIBS="$LIBS -lprot -lx -ltinfo -lm" + no_dev_ptmx=1 + AC_DEFINE([USE_PIPES]) + AC_DEFINE([HAVE_SECUREWARE]) + AC_DEFINE([DISABLE_SHADOW]) + AC_DEFINE([DISABLE_FD_PASSING]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_GETADDRINFO]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([WITH_ABBREV_NO_TTY]) + AC_DEFINE([BROKEN_UPDWTMPX]) + AC_DEFINE([PASSWD_NEEDS_USERNAME]) + AC_CHECK_FUNCS([getluid setluid]) + MANTYPE=man + TEST_SHELL=$SHELL # let configure find us a capable shell + SKIP_DISABLE_LASTLOG_DEFINE=yes + ;; +*-dec-osf*) + AC_MSG_CHECKING([for Digital Unix SIA]) + no_osfsia="" + AC_ARG_WITH([osfsia], + [ --with-osfsia Enable Digital Unix SIA], + [ + if test "x$withval" = "xno" ; then + AC_MSG_RESULT([disabled]) + no_osfsia=1 + fi + ], + ) + if test -z "$no_osfsia" ; then + if test -f /etc/sia/matrix.conf; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_OSF_SIA], [1], + [Define if you have Digital Unix Security + Integration Architecture]) + AC_DEFINE([DISABLE_LOGIN], [1], + [Define if you don't want to use your + system's login() call]) + AC_DEFINE([DISABLE_FD_PASSING]) + LIBS="$LIBS -lsecurity -ldb -lm -laud" + SIA_MSG="yes" + else + AC_MSG_RESULT([no]) + AC_DEFINE([LOCKED_PASSWD_SUBSTR], ["Nologin"], + [String used in /etc/passwd to denote locked account]) + fi + fi + AC_DEFINE([BROKEN_GETADDRINFO]) + AC_DEFINE([SETEUID_BREAKS_SETUID]) + AC_DEFINE([BROKEN_SETREUID]) + AC_DEFINE([BROKEN_SETREGID]) + AC_DEFINE([BROKEN_READV_COMPARISON], [1], [Can't do comparisons on readv]) + ;; + +*-*-nto-qnx*) + AC_DEFINE([USE_PIPES]) + AC_DEFINE([NO_X11_UNIX_SOCKETS]) + AC_DEFINE([DISABLE_LASTLOG]) + AC_DEFINE([SSHD_ACQUIRES_CTTY]) + AC_DEFINE([BROKEN_SHADOW_EXPIRE], [1], [QNX shadow support is broken]) + enable_etc_default_login=no # has incompatible /etc/default/login + case "$host" in + *-*-nto-qnx6*) + AC_DEFINE([DISABLE_FD_PASSING]) + ;; + esac + ;; + +*-*-ultrix*) + AC_DEFINE([BROKEN_GETGROUPS], [1], [getgroups(0,NULL) will return -1]) + AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to for controlling tty]) + AC_DEFINE([HAVE_SYS_SYSLOG_H], [1], [Force use of sys/syslog.h on Ultrix]) + AC_DEFINE([DISABLE_UTMPX], [1], [Disable utmpx]) + # DISABLE_FD_PASSING so that we call setpgrp as root, otherwise we + # don't get a controlling tty. + AC_DEFINE([DISABLE_FD_PASSING], [1], [Need to call setpgrp as root]) + # On Ultrix some headers are not protected against multiple includes, + # so we create wrappers and put it where the compiler will find it. + AC_MSG_WARN([creating compat wrappers for headers]) + mkdir -p netinet + for header in netinet/ip.h netdb.h resolv.h; do + name=`echo $header | tr 'a-z/.' 'A-Z__'` + cat >$header < ]], [[ exit(0); ]])], + [ AC_MSG_RESULT([yes]) ], + [ + AC_MSG_RESULT([no]) + AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***]) + ], + [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ] +) + +dnl Checks for header files. +# Checks for libraries. +AC_CHECK_FUNC([setsockopt], , [AC_CHECK_LIB([socket], [setsockopt])]) + +dnl IRIX and Solaris 2.5.1 have dirname() in libgen +AC_CHECK_FUNCS([dirname], [AC_CHECK_HEADERS([libgen.h])] , [ + AC_CHECK_LIB([gen], [dirname], [ + AC_CACHE_CHECK([for broken dirname], + ac_cv_have_broken_dirname, [ + save_LIBS="$LIBS" + LIBS="$LIBS -lgen" + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +#include + +int main(int argc, char **argv) { + char *s, buf[32]; + + strncpy(buf,"/etc", 32); + s = dirname(buf); + if (!s || strncmp(s, "/", 32) != 0) { + exit(1); + } else { + exit(0); + } +} + ]])], + [ ac_cv_have_broken_dirname="no" ], + [ ac_cv_have_broken_dirname="yes" ], + [ ac_cv_have_broken_dirname="no" ], + ) + LIBS="$save_LIBS" + ]) + if test "x$ac_cv_have_broken_dirname" = "xno" ; then + LIBS="$LIBS -lgen" + AC_DEFINE([HAVE_DIRNAME]) + AC_CHECK_HEADERS([libgen.h]) + fi + ]) +]) + +AC_CHECK_FUNC([getspnam], , + [AC_CHECK_LIB([gen], [getspnam], [LIBS="$LIBS -lgen"])]) +AC_SEARCH_LIBS([basename], [gen], [AC_DEFINE([HAVE_BASENAME], [1], + [Define if you have the basename function.])]) + +dnl zlib defaults to enabled +zlib=yes +AC_ARG_WITH([zlib], + [ --with-zlib=PATH Use zlib in PATH], + [ if test "x$withval" = "xno" ; then + zlib=no + elif test "x$withval" != "xyes"; then + if test -d "$withval/lib"; then + if test -n "${rpath_opt}"; then + LDFLAGS="-L${withval}/lib ${rpath_opt}${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + else + if test -n "${rpath_opt}"; then + LDFLAGS="-L${withval} ${rpath_opt}${withval} ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + fi ] +) + +AC_MSG_CHECKING([for zlib]) +if test "x${zlib}" = "xno"; then + AC_MSG_RESULT([no]) +else + AC_MSG_RESULT([yes]) + AC_DEFINE([WITH_ZLIB], [1], [Enable zlib]) + AC_CHECK_HEADER([zlib.h], ,[AC_MSG_ERROR([*** zlib.h missing - please install first or check config.log ***])]) + AC_CHECK_LIB([z], [deflate], , + [ + saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + save_LIBS="$LIBS" + dnl Check default zlib install dir + if test -n "${rpath_opt}"; then + LDFLAGS="-L/usr/local/lib ${rpath_opt}/usr/local/lib ${saved_LDFLAGS}" + else + LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}" + fi + CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}" + LIBS="$LIBS -lz" + AC_TRY_LINK_FUNC([deflate], [AC_DEFINE([HAVE_LIBZ])], + [ + AC_MSG_ERROR([*** zlib missing - please install first or check config.log ***]) + ] + ) + ] + ) + + AC_ARG_WITH([zlib-version-check], + [ --without-zlib-version-check Disable zlib version check], + [ if test "x$withval" = "xno" ; then + zlib_check_nonfatal=1 + fi + ] + ) + + AC_MSG_CHECKING([for possibly buggy zlib]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include + ]], + [[ + int a=0, b=0, c=0, d=0, n, v; + n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); + if (n != 3 && n != 4) + exit(1); + v = a*1000000 + b*10000 + c*100 + d; + fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); + + /* 1.1.4 is OK */ + if (a == 1 && b == 1 && c >= 4) + exit(0); + + /* 1.2.3 and up are OK */ + if (v >= 1020300) + exit(0); + + exit(2); + ]])], + AC_MSG_RESULT([no]), + [ AC_MSG_RESULT([yes]) + if test -z "$zlib_check_nonfatal" ; then + AC_MSG_ERROR([*** zlib too old - check config.log *** +Your reported zlib version has known security problems. It's possible your +vendor has fixed these problems without changing the version number. If you +are sure this is the case, you can disable the check by running +"./configure --without-zlib-version-check". +If you are in doubt, upgrade zlib to version 1.2.3 or greater. +See http://www.gzip.org/zlib/ for details.]) + else + AC_MSG_WARN([zlib version may have security problems]) + fi + ], + [ AC_MSG_WARN([cross compiling: not checking zlib version]) ] + ) +fi + +dnl UnixWare 2.x +AC_CHECK_FUNC([strcasecmp], + [], [ AC_CHECK_LIB([resolv], [strcasecmp], [LIBS="$LIBS -lresolv"]) ] +) +AC_CHECK_FUNCS([utimes], + [], [ AC_CHECK_LIB([c89], [utimes], [AC_DEFINE([HAVE_UTIMES]) + LIBS="$LIBS -lc89"]) ] +) + +dnl Checks for libutil functions +AC_CHECK_HEADERS([bsd/libutil.h libutil.h]) +AC_SEARCH_LIBS([fmt_scaled], [util bsd]) +AC_SEARCH_LIBS([scan_scaled], [util bsd]) +AC_SEARCH_LIBS([login], [util bsd]) +AC_SEARCH_LIBS([logout], [util bsd]) +AC_SEARCH_LIBS([logwtmp], [util bsd]) +AC_SEARCH_LIBS([openpty], [util bsd]) +AC_SEARCH_LIBS([updwtmp], [util bsd]) +AC_CHECK_FUNCS([fmt_scaled scan_scaled login logout openpty updwtmp logwtmp]) + +# On some platforms, inet_ntop and gethostbyname may be found in libresolv +# or libnsl. +AC_SEARCH_LIBS([inet_ntop], [resolv nsl]) +AC_SEARCH_LIBS([gethostbyname], [resolv nsl]) + +# Some Linux distribtions ship the BSD libc hashing functions in +# separate libraries. +AC_SEARCH_LIBS([SHA256Update], [md bsd]) + +# "Particular Function Checks" +# see https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Particular-Functions.html +AC_FUNC_STRFTIME +AC_FUNC_MALLOC +AC_FUNC_REALLOC +# autoconf doesn't have AC_FUNC_CALLOC so fake it if malloc returns NULL; +AC_MSG_CHECKING([if calloc(0, N) returns non-null]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[ #include ]], + [[ void *p = calloc(0, 1); exit(p == NULL); ]] + )], + [ func_calloc_0_nonnull=yes ], + [ func_calloc_0_nonnull=no ], + [ AC_MSG_WARN([cross compiling: assuming same as malloc]) + func_calloc_0_nonnull="$ac_cv_func_malloc_0_nonnull"] +) +AC_MSG_RESULT([$func_calloc_0_nonnull]) + +if test "x$func_calloc_0_nonnull" = "xyes"; then + AC_DEFINE(HAVE_CALLOC, 1, [calloc(0, x) returns non-null]) +else + AC_DEFINE(HAVE_CALLOC, 0, [calloc(0, x) returns NULL]) + AC_DEFINE(calloc, rpl_calloc, + [Define to rpl_calloc if the replacement function should be used.]) +fi + +# Check for ALTDIRFUNC glob() extension +AC_MSG_CHECKING([for GLOB_ALTDIRFUNC support]) +AC_EGREP_CPP([FOUNDIT], + [ + #include + #ifdef GLOB_ALTDIRFUNC + FOUNDIT + #endif + ], + [ + AC_DEFINE([GLOB_HAS_ALTDIRFUNC], [1], + [Define if your system glob() function has + the GLOB_ALTDIRFUNC extension]) + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + ] +) + +# Check for g.gl_matchc glob() extension +AC_MSG_CHECKING([for gl_matchc field in glob_t]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ glob_t g; g.gl_matchc = 1; ]])], + [ + AC_DEFINE([GLOB_HAS_GL_MATCHC], [1], + [Define if your system glob() function has + gl_matchc options in glob_t]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) +]) + +# Check for g.gl_statv glob() extension +AC_MSG_CHECKING([for gl_statv and GLOB_KEEPSTAT extensions for glob]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ +#ifndef GLOB_KEEPSTAT +#error "glob does not support GLOB_KEEPSTAT extension" +#endif +glob_t g; +g.gl_statv = NULL; +]])], + [ + AC_DEFINE([GLOB_HAS_GL_STATV], [1], + [Define if your system glob() function has + gl_statv options in glob_t]) + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + +]) + +AC_CHECK_DECLS([GLOB_NOMATCH], , , [#include ]) + +AC_CHECK_DECL([VIS_ALL], , + AC_DEFINE(BROKEN_STRNVIS, 1, [missing VIS_ALL]), [#include ]) + +AC_MSG_CHECKING([whether struct dirent allocates space for d_name]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include + ]], + [[ + struct dirent d; + exit(sizeof(d.d_name)<=sizeof(char)); + ]])], + [AC_MSG_RESULT([yes])], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME], [1], + [Define if your struct dirent expects you to + allocate extra space for d_name]) + ], + [ + AC_MSG_WARN([cross compiling: assuming not BROKEN_ONE_BYTE_DIRENT_D_NAME]) + ] +) + +AC_MSG_CHECKING([for /proc/pid/fd directory]) +if test -d "/proc/$$/fd" ; then + AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + +# Check whether user wants to use ldns +LDNS_MSG="no" +AC_ARG_WITH(ldns, + [ --with-ldns[[=PATH]] Use ldns for DNSSEC support (optionally in PATH)], + [ + ldns="" + if test "x$withval" = "xyes" ; then + AC_PATH_TOOL([LDNSCONFIG], [ldns-config], [no]) + if test "x$LDNSCONFIG" = "xno"; then + LIBS="-lldns $LIBS" + ldns=yes + else + LIBS="$LIBS `$LDNSCONFIG --libs`" + CPPFLAGS="$CPPFLAGS `$LDNSCONFIG --cflags`" + ldns=yes + fi + elif test "x$withval" != "xno" ; then + CPPFLAGS="$CPPFLAGS -I${withval}/include" + LDFLAGS="$LDFLAGS -L${withval}/lib" + LIBS="-lldns $LIBS" + ldns=yes + fi + + # Verify that it works. + if test "x$ldns" = "xyes" ; then + AC_DEFINE(HAVE_LDNS, 1, [Define if you want ldns support]) + LDNS_MSG="yes" + AC_MSG_CHECKING([for ldns support]) + AC_LINK_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +int main() { ldns_status status = ldns_verify_trusted(NULL, NULL, NULL, NULL); status=LDNS_STATUS_OK; exit(0); } + ]]) + ], + [AC_MSG_RESULT(yes)], + [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([** Incomplete or missing ldns libraries.]) + ]) + fi +]) + +# Check whether user wants libedit support +LIBEDIT_MSG="no" +AC_ARG_WITH([libedit], + [ --with-libedit[[=PATH]] Enable libedit support for sftp], + [ if test "x$withval" != "xno" ; then + if test "x$withval" = "xyes" ; then + AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) + if test "x$PKGCONFIG" != "xno"; then + AC_MSG_CHECKING([if $PKGCONFIG knows about libedit]) + if "$PKGCONFIG" libedit; then + AC_MSG_RESULT([yes]) + use_pkgconfig_for_libedit=yes + else + AC_MSG_RESULT([no]) + fi + fi + else + CPPFLAGS="$CPPFLAGS -I${withval}/include" + if test -n "${rpath_opt}"; then + LDFLAGS="-L${withval}/lib ${rpath_opt}${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + fi + if test "x$use_pkgconfig_for_libedit" = "xyes"; then + LIBEDIT=`$PKGCONFIG --libs libedit` + CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libedit`" + else + LIBEDIT="-ledit -lcurses" + fi + OTHERLIBS=`echo $LIBEDIT | sed 's/-ledit//'` + AC_CHECK_LIB([edit], [el_init], + [ AC_DEFINE([USE_LIBEDIT], [1], [Use libedit for sftp]) + LIBEDIT_MSG="yes" + AC_SUBST([LIBEDIT]) + ], + [ AC_MSG_ERROR([libedit not found]) ], + [ $OTHERLIBS ] + ) + AC_MSG_CHECKING([if libedit version is compatible]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include + ]], + [[ + int i = H_SETSIZE; + el_init("", NULL, NULL, NULL); + exit(0); + ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([libedit version is not compatible]) ] + ) + fi ] +) + +AUDIT_MODULE=none +AC_ARG_WITH([audit], + [ --with-audit=module Enable audit support (modules=debug,bsm,linux)], + [ + AC_MSG_CHECKING([for supported audit module]) + case "$withval" in + bsm) + AC_MSG_RESULT([bsm]) + AUDIT_MODULE=bsm + dnl Checks for headers, libs and functions + AC_CHECK_HEADERS([bsm/audit.h], [], + [AC_MSG_ERROR([BSM enabled and bsm/audit.h not found])], + [ +#ifdef HAVE_TIME_H +# include +#endif + ] +) + AC_CHECK_LIB([bsm], [getaudit], [], + [AC_MSG_ERROR([BSM enabled and required library not found])]) + AC_CHECK_FUNCS([getaudit], [], + [AC_MSG_ERROR([BSM enabled and required function not found])]) + # These are optional + AC_CHECK_FUNCS([getaudit_addr aug_get_machine]) + AC_DEFINE([USE_BSM_AUDIT], [1], [Use BSM audit module]) + if test "$sol2ver" -ge 11; then + SSHDLIBS="$SSHDLIBS -lscf" + AC_DEFINE([BROKEN_BSM_API], [1], + [The system has incomplete BSM API]) + fi + ;; + linux) + AC_MSG_RESULT([linux]) + AUDIT_MODULE=linux + dnl Checks for headers, libs and functions + AC_CHECK_HEADERS([libaudit.h]) + SSHDLIBS="$SSHDLIBS -laudit" + AC_DEFINE([USE_LINUX_AUDIT], [1], [Use Linux audit module]) + ;; + debug) + AUDIT_MODULE=debug + AC_MSG_RESULT([debug]) + AC_DEFINE([SSH_AUDIT_EVENTS], [1], [Use audit debugging module]) + ;; + no) + AC_MSG_RESULT([no]) + ;; + *) + AC_MSG_ERROR([Unknown audit module $withval]) + ;; + esac ] +) + +AC_ARG_WITH([pie], + [ --with-pie Build Position Independent Executables if possible], [ + if test "x$withval" = "xno"; then + use_pie=no + fi + if test "x$withval" = "xyes"; then + use_pie=yes + fi + ] +) +if test "x$use_pie" = "x"; then + use_pie=no +fi +if test "x$use_toolchain_hardening" != "x1" && test "x$use_pie" = "xauto"; then + # Turn off automatic PIE when toolchain hardening is off. + use_pie=no +fi +if test "x$use_pie" = "xauto"; then + # Automatic PIE requires gcc >= 4.x + AC_MSG_CHECKING([for gcc >= 4.x]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#if !defined(__GNUC__) || __GNUC__ < 4 +#error gcc is too old +#endif +]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + use_pie=no ] +) +fi +if test "x$use_pie" != "xno"; then + SAVED_CFLAGS="$CFLAGS" + SAVED_LDFLAGS="$LDFLAGS" + OSSH_CHECK_CFLAG_COMPILE([-fPIE]) + OSSH_CHECK_LDFLAG_LINK([-pie]) + # We use both -fPIE and -pie or neither. + AC_MSG_CHECKING([whether both -fPIE and -pie are supported]) + if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ + echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + CFLAGS="$SAVED_CFLAGS" + LDFLAGS="$SAVED_LDFLAGS" + fi +fi + +AC_MSG_CHECKING([whether -fPIC is accepted]) +SAVED_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fPIC" +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( [[ #include ]], [[ exit(0); ]] )], + [AC_MSG_RESULT([yes]) + PICFLAG="-fPIC"; ], + [AC_MSG_RESULT([no]) + PICFLAG=""; ]) +CFLAGS="$SAVED_CFLAGS" +AC_SUBST([PICFLAG]) + +dnl Checks for library functions. Please keep in alphabetical order +AC_CHECK_FUNCS([ \ + Blowfish_initstate \ + Blowfish_expandstate \ + Blowfish_expand0state \ + Blowfish_stream2word \ + SHA256Update \ + SHA384Update \ + SHA512Update \ + asprintf \ + b64_ntop \ + __b64_ntop \ + b64_pton \ + __b64_pton \ + bcopy \ + bcrypt_pbkdf \ + bindresvport_sa \ + blf_enc \ + bzero \ + cap_rights_limit \ + clock \ + closefrom \ + close_range \ + dirfd \ + endgrent \ + err \ + errx \ + explicit_bzero \ + explicit_memset \ + fchmod \ + fchmodat \ + fchown \ + fchownat \ + flock \ + fnmatch \ + freeaddrinfo \ + freezero \ + fstatfs \ + fstatvfs \ + futimes \ + getaddrinfo \ + getcwd \ + getgrouplist \ + getline \ + getnameinfo \ + getopt \ + getpagesize \ + getpeereid \ + getpeerucred \ + getpgid \ + _getpty \ + getrlimit \ + getrandom \ + getsid \ + getttyent \ + glob \ + group_from_gid \ + inet_aton \ + inet_ntoa \ + inet_ntop \ + innetgr \ + killpg \ + llabs \ + localtime_r \ + login_getcapbool \ + login_getpwclass \ + memmem \ + memmove \ + memset_s \ + mkdtemp \ + ngetaddrinfo \ + nsleep \ + ogetaddrinfo \ + openlog_r \ + pledge \ + poll \ + ppoll \ + prctl \ + procctl \ + pselect \ + pstat \ + raise \ + readpassphrase \ + reallocarray \ + realpath \ + recvmsg \ + recallocarray \ + rresvport_af \ + sendmsg \ + setdtablesize \ + setegid \ + setenv \ + seteuid \ + setgroupent \ + setgroups \ + setlinebuf \ + setlogin \ + setpassent\ + setpcred \ + setproctitle \ + setregid \ + setreuid \ + setrlimit \ + setsid \ + setvbuf \ + sigaction \ + sigvec \ + snprintf \ + socketpair \ + statfs \ + statvfs \ + strcasestr \ + strdup \ + strerror \ + strlcat \ + strlcpy \ + strmode \ + strndup \ + strnlen \ + strnvis \ + strptime \ + strsignal \ + strtonum \ + strtoll \ + strtoul \ + strtoull \ + swap32 \ + sysconf \ + tcgetpgrp \ + timingsafe_bcmp \ + truncate \ + unsetenv \ + updwtmpx \ + utimensat \ + user_from_uid \ + usleep \ + vasprintf \ + vsnprintf \ + waitpid \ + warn \ +]) + +AC_CHECK_DECLS([bzero, memmem]) + +dnl Wide character support. +AC_CHECK_FUNCS([mblen mbtowc nl_langinfo wcwidth]) + +TEST_SSH_UTF8=${TEST_SSH_UTF8:=yes} +AC_MSG_CHECKING([for utf8 locale support]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include + ]], [[ + char *loc = setlocale(LC_CTYPE, "en_US.UTF-8"); + if (loc != NULL) + exit(0); + exit(1); + ]])], + AC_MSG_RESULT(yes), + [AC_MSG_RESULT(no) + TEST_SSH_UTF8=no], + AC_MSG_WARN([cross compiling: assuming yes]) +) + +AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ #include ]], + [[ return (isblank('a')); ]])], + [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) +]) + +disable_pkcs11= +AC_ARG_ENABLE([pkcs11], + [ --disable-pkcs11 disable PKCS#11 support code [no]], + [ + if test "x$enableval" = "xno" ; then + disable_pkcs11=1 + fi + ] +) + +disable_sk= +AC_ARG_ENABLE([security-key], + [ --disable-security-key disable U2F/FIDO support code [no]], + [ + if test "x$enableval" = "xno" ; then + disable_sk=1 + fi + ] +) +enable_sk_internal= +AC_ARG_WITH([security-key-builtin], + [ --with-security-key-builtin include builtin U2F/FIDO support], + [ + if test "x$withval" != "xno" ; then + enable_sk_internal=yes + fi + ] +) +test "x$disable_sk" != "x" && enable_sk_internal="" + +AC_SEARCH_LIBS([dlopen], [dl]) +AC_CHECK_FUNCS([dlopen]) +AC_CHECK_DECL([RTLD_NOW], [], [], [#include ]) + +# IRIX has a const char return value for gai_strerror() +AC_CHECK_FUNCS([gai_strerror], [ + AC_DEFINE([HAVE_GAI_STRERROR]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include + +const char *gai_strerror(int); + ]], [[ + char *str; + str = gai_strerror(0); + ]])], [ + AC_DEFINE([HAVE_CONST_GAI_STRERROR_PROTO], [1], + [Define if gai_strerror() returns const char *])], [])]) + +AC_SEARCH_LIBS([nanosleep], [rt posix4], [AC_DEFINE([HAVE_NANOSLEEP], [1], + [Some systems put nanosleep outside of libc])]) + +AC_SEARCH_LIBS([clock_gettime], [rt], + [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Have clock_gettime])]) + +dnl check if we need -D_REENTRANT for localtime_r declaration. +AC_CHECK_DECL([localtime_r], [], + [ saved_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS -D_REENTRANT" + unset ac_cv_have_decl_localtime_r + AC_CHECK_DECL([localtime_r], [], + [ CPPFLAGS="$saved_CPPFLAGS" ], + [ #include ] + ) + ], + [ #include ] +) + +dnl Make sure prototypes are defined for these before using them. +AC_CHECK_DECL([strsep], + [AC_CHECK_FUNCS([strsep])], + [], + [ +#ifdef HAVE_STRING_H +# include +#endif + ]) + +dnl tcsendbreak might be a macro +AC_CHECK_DECL([tcsendbreak], + [AC_DEFINE([HAVE_TCSENDBREAK])], + [AC_CHECK_FUNCS([tcsendbreak])], + [#include ] +) + +AC_CHECK_DECLS([h_errno], , ,[#include ]) + +AC_CHECK_DECLS([SHUT_RD, getpeereid], , , + [ +#include +#include +#include + ]) + +AC_CHECK_DECLS([O_NONBLOCK], , , + [ +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif + ]) + +AC_CHECK_DECLS([ftruncate], , , + [ +#include +#include + ]) + +AC_CHECK_DECLS([readv, writev], , , [ +#include +#include +#include + ]) + +AC_CHECK_DECLS([MAXSYMLINKS], , , [ +#include + ]) + +AC_CHECK_DECLS([offsetof], , , [ +#include + ]) + +# extra bits for select(2) +AC_CHECK_DECLS([howmany, NFDBITS], [], [], [[ +#include +#include +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + ]]) +AC_CHECK_TYPES([fd_mask], [], [], [[ +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + ]]) + +AC_CHECK_FUNCS([setresuid], [ + dnl Some platorms have setresuid that isn't implemented, test for this + AC_MSG_CHECKING([if setresuid seems to work]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include + ]], [[ + errno=0; + setresuid(0,0,0); + if (errno==ENOSYS) + exit(1); + else + exit(0); + ]])], + [AC_MSG_RESULT([yes])], + [AC_DEFINE([BROKEN_SETRESUID], [1], + [Define if your setresuid() is broken]) + AC_MSG_RESULT([not implemented])], + [AC_MSG_WARN([cross compiling: not checking setresuid])] + ) +]) + +AC_CHECK_FUNCS([setresgid], [ + dnl Some platorms have setresgid that isn't implemented, test for this + AC_MSG_CHECKING([if setresgid seems to work]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include + ]], [[ + errno=0; + setresgid(0,0,0); + if (errno==ENOSYS) + exit(1); + else + exit(0); + ]])], + [AC_MSG_RESULT([yes])], + [AC_DEFINE([BROKEN_SETRESGID], [1], + [Define if your setresgid() is broken]) + AC_MSG_RESULT([not implemented])], + [AC_MSG_WARN([cross compiling: not checking setresuid])] + ) +]) + +AC_MSG_CHECKING([for working fflush(NULL)]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include + ]], + [[fflush(NULL); exit(0);]])], + AC_MSG_RESULT([yes]), + [AC_MSG_RESULT([no]) + AC_DEFINE([FFLUSH_NULL_BUG], [1], + [define if fflush(NULL) does not work])], + AC_MSG_WARN([cross compiling: assuming working]) +) + +dnl Checks for time functions +AC_CHECK_FUNCS([gettimeofday time]) +dnl Checks for utmp functions +AC_CHECK_FUNCS([endutent getutent getutid getutline pututline setutent]) +AC_CHECK_FUNCS([utmpname]) +dnl Checks for utmpx functions +AC_CHECK_FUNCS([endutxent getutxent getutxid getutxline getutxuser pututxline]) +AC_CHECK_FUNCS([setutxdb setutxent utmpxname]) +dnl Checks for lastlog functions +AC_CHECK_FUNCS([getlastlogxbyname]) + +AC_CHECK_FUNC([daemon], + [AC_DEFINE([HAVE_DAEMON], [1], [Define if your libraries define daemon()])], + [AC_CHECK_LIB([bsd], [daemon], + [LIBS="$LIBS -lbsd"; AC_DEFINE([HAVE_DAEMON])])] +) + +AC_CHECK_FUNC([getpagesize], + [AC_DEFINE([HAVE_GETPAGESIZE], [1], + [Define if your libraries define getpagesize()])], + [AC_CHECK_LIB([ucb], [getpagesize], + [LIBS="$LIBS -lucb"; AC_DEFINE([HAVE_GETPAGESIZE])])] +) + +# Check for broken snprintf +if test "x$ac_cv_func_snprintf" = "xyes" ; then + AC_MSG_CHECKING([whether snprintf correctly terminates long strings]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include + ]], + [[ + char b[5]; + snprintf(b,5,"123456789"); + exit(b[4]!='\0'); + ]])], + [AC_MSG_RESULT([yes])], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_SNPRINTF], [1], + [Define if your snprintf is busted]) + AC_MSG_WARN([****** Your snprintf() function is broken, complain to your vendor]) + ], + [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] + ) +fi + +if test "x$ac_cv_func_snprintf" = "xyes" ; then + AC_MSG_CHECKING([whether snprintf understands %zu]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +#include + ]], + [[ + size_t a = 1, b = 2; + char z[128]; + snprintf(z, sizeof z, "%zu%zu", a, b); + exit(strcmp(z, "12")); + ]])], + [AC_MSG_RESULT([yes])], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_SNPRINTF], [1], + [snprintf does not understand %zu]) + ], + [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] + ) +fi + +# We depend on vsnprintf returning the right thing on overflow: the +# number of characters it tried to create (as per SUSv3) +if test "x$ac_cv_func_vsnprintf" = "xyes" ; then + AC_MSG_CHECKING([whether vsnprintf returns correct values on overflow]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include + +int x_snprintf(char *str, size_t count, const char *fmt, ...) +{ + size_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vsnprintf(str, count, fmt, ap); + va_end(ap); + return ret; +} + ]], [[ +char x[1]; +if (x_snprintf(x, 1, "%s %d", "hello", 12345) != 11) + return 1; +if (x_snprintf(NULL, 0, "%s %d", "hello", 12345) != 11) + return 1; +return 0; + ]])], + [AC_MSG_RESULT([yes])], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_SNPRINTF], [1], + [Define if your snprintf is busted]) + AC_MSG_WARN([****** Your vsnprintf() function is broken, complain to your vendor]) + ], + [ AC_MSG_WARN([cross compiling: Assuming working vsnprintf()]) ] + ) +fi + +# On systems where [v]snprintf is broken, but is declared in stdio, +# check that the fmt argument is const char * or just char *. +# This is only useful for when BROKEN_SNPRINTF +AC_MSG_CHECKING([whether snprintf can declare const char *fmt]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +int snprintf(char *a, size_t b, const char *c, ...) { return 0; } + ]], [[ + snprintf(0, 0, 0); + ]])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([SNPRINTF_CONST], [const], + [Define as const if snprintf() can declare const char *fmt])], + [AC_MSG_RESULT([no]) + AC_DEFINE([SNPRINTF_CONST], [/* not const */])]) + +# Check for missing getpeereid (or equiv) support +NO_PEERCHECK="" +if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then + AC_MSG_CHECKING([whether system supports SO_PEERCRED getsockopt]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include ]], [[int i = SO_PEERCRED;]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option]) + ], [AC_MSG_RESULT([no]) + NO_PEERCHECK=1 + ]) +fi + +dnl make sure that openpty does not reacquire controlling terminal +if test ! -z "$check_for_openpty_ctty_bug"; then + AC_MSG_CHECKING([if openpty correctly handles controlling tty]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +#include +#include +#include + ]], [[ + pid_t pid; + int fd, ptyfd, ttyfd, status; + + pid = fork(); + if (pid < 0) { /* failed */ + exit(1); + } else if (pid > 0) { /* parent */ + waitpid(pid, &status, 0); + if (WIFEXITED(status)) + exit(WEXITSTATUS(status)); + else + exit(2); + } else { /* child */ + close(0); close(1); close(2); + setsid(); + openpty(&ptyfd, &ttyfd, NULL, NULL, NULL); + fd = open("/dev/tty", O_RDWR | O_NOCTTY); + if (fd >= 0) + exit(3); /* Acquired ctty: broken */ + else + exit(0); /* Did not acquire ctty: OK */ + } + ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([SSHD_ACQUIRES_CTTY]) + ], + [ + AC_MSG_RESULT([cross-compiling, assuming yes]) + ] + ) +fi + +if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ + test "x$check_for_hpux_broken_getaddrinfo" = "x1"; then + AC_MSG_CHECKING([if getaddrinfo seems to work]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +#include +#include +#include + +#define TEST_PORT "2222" + ]], [[ + int err, sock; + struct addrinfo *gai_ai, *ai, hints; + char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); + if (err != 0) { + fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); + exit(1); + } + + for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family != AF_INET6) + continue; + + err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, + sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV); + + if (err != 0) { + if (err == EAI_SYSTEM) + perror("getnameinfo EAI_SYSTEM"); + else + fprintf(stderr, "getnameinfo failed: %s\n", + gai_strerror(err)); + exit(2); + } + + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) + perror("socket"); + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + if (errno == EBADF) + exit(3); + } + } + exit(0); + ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_GETADDRINFO]) + ], + [ + AC_MSG_RESULT([cross-compiling, assuming yes]) + ] + ) +fi + +if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ + test "x$check_for_aix_broken_getaddrinfo" = "x1"; then + AC_MSG_CHECKING([if getaddrinfo seems to work]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +#include +#include +#include + +#define TEST_PORT "2222" + ]], [[ + int err, sock; + struct addrinfo *gai_ai, *ai, hints; + char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); + if (err != 0) { + fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); + exit(1); + } + + for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + + err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, + sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV); + + if (ai->ai_family == AF_INET && err != 0) { + perror("getnameinfo"); + exit(2); + } + } + exit(0); + ]])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([AIX_GETNAMEINFO_HACK], [1], + [Define if you have a getaddrinfo that fails + for the all-zeros IPv6 address]) + ], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_GETADDRINFO]) + ], + [ + AC_MSG_RESULT([cross-compiling, assuming no]) + ] + ) +fi + +if test "x$ac_cv_func_getaddrinfo" = "xyes"; then + AC_CHECK_DECLS(AI_NUMERICSERV, , , + [#include + #include + #include ]) +fi + +if test "x$check_for_conflicting_getspnam" = "x1"; then + AC_MSG_CHECKING([for conflicting getspnam in shadow.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], + [[ exit(0); ]])], + [ + AC_MSG_RESULT([no]) + ], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([GETSPNAM_CONFLICTING_DEFS], [1], + [Conflicting defs for getspnam]) + ] + ) +fi + +dnl NetBSD added an strnvis and unfortunately made it incompatible with the +dnl existing one in OpenBSD and Linux's libbsd (the former having existed +dnl for over ten years). Despite this incompatibility being reported during +dnl development (see http://gnats.netbsd.org/44977) they still shipped it. +dnl Even more unfortunately FreeBSD and later MacOS picked up this incompatible +dnl implementation. Try to detect this mess, and assume the only safe option +dnl if we're cross compiling. +dnl +dnl OpenBSD, 2001: strnvis(char *dst, const char *src, size_t dlen, int flag); +dnl NetBSD: 2012, strnvis(char *dst, size_t dlen, const char *src, int flag); +if test "x$ac_cv_func_strnvis" = "xyes"; then + AC_MSG_CHECKING([for working strnvis]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +#include +#include +static void sighandler(int sig) { _exit(1); } + ]], [[ + char dst[16]; + + signal(SIGSEGV, sighandler); + if (strnvis(dst, "src", 4, 0) && strcmp(dst, "src") == 0) + exit(0); + exit(1) + ]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_DEFINE([BROKEN_STRNVIS], [1], [strnvis detected broken])], + [AC_MSG_WARN([cross compiling: assuming broken]) + AC_DEFINE([BROKEN_STRNVIS], [1], [strnvis assumed broken])] + ) +fi + +AC_MSG_CHECKING([if SA_RESTARTed signals interrupt select()]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#ifdef HAVE_SYS_SELECT +# include +#endif +#include +#include +#include +#include +#include +static void sighandler(int sig) { } + ]], [[ + int r; + pid_t pid; + struct sigaction sa; + + sa.sa_handler = sighandler; + sa.sa_flags = SA_RESTART; + (void)sigaction(SIGTERM, &sa, NULL); + if ((pid = fork()) == 0) { /* child */ + pid = getppid(); + sleep(1); + kill(pid, SIGTERM); + sleep(1); + if (getppid() == pid) /* if parent did not exit, shoot it */ + kill(pid, SIGKILL); + exit(0); + } else { /* parent */ + r = select(0, NULL, NULL, NULL, NULL); + } + exit(r == -1 ? 0 : 1); + ]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_DEFINE([NO_SA_RESTART], [1], + [SA_RESTARTed signals do no interrupt select])], + [AC_MSG_WARN([cross compiling: assuming yes])] +) + +AC_CHECK_FUNCS([getpgrp],[ + AC_MSG_CHECKING([if getpgrp accepts zero args]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[$ac_includes_default]], [[ getpgrp(); ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([GETPGRP_VOID], [1], [getpgrp takes zero args])], + [ AC_MSG_RESULT([no]) + AC_DEFINE([GETPGRP_VOID], [0], [getpgrp takes one arg])] + ) +]) + +# Search for OpenSSL +saved_CPPFLAGS="$CPPFLAGS" +saved_LDFLAGS="$LDFLAGS" +AC_ARG_WITH([ssl-dir], + [ --with-ssl-dir=PATH Specify path to OpenSSL installation ], + [ + if test "x$openssl" = "xno" ; then + AC_MSG_ERROR([cannot use --with-ssl-dir when OpenSSL disabled]) + fi + if test "x$withval" != "xno" ; then + case "$withval" in + # Relative paths + ./*|../*) withval="`pwd`/$withval" + esac + if test -d "$withval/lib"; then + libcrypto_path="${withval}/lib" + elif test -d "$withval/lib64"; then + libcrypto_path="$withval/lib64" + else + # Built but not installed + libcrypto_path="${withval}" + fi + if test -n "${rpath_opt}"; then + LDFLAGS="-L${libcrypto_path} ${rpath_opt}${libcrypto_path} ${LDFLAGS}" + else + LDFLAGS="-L${libcrypto_path} ${LDFLAGS}" + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + fi + ] +) + +AC_ARG_WITH([openssl-header-check], + [ --without-openssl-header-check Disable OpenSSL version consistency check], + [ + if test "x$withval" = "xno" ; then + openssl_check_nonfatal=1 + fi + ] +) + +openssl_engine=no +AC_ARG_WITH([ssl-engine], + [ --with-ssl-engine Enable OpenSSL (hardware) ENGINE support ], + [ + if test "x$withval" != "xno" ; then + if test "x$openssl" = "xno" ; then + AC_MSG_ERROR([cannot use --with-ssl-engine when OpenSSL disabled]) + fi + openssl_engine=yes + fi + ] +) + +if test "x$openssl" = "xyes" ; then + LIBS="-lcrypto $LIBS" + AC_TRY_LINK_FUNC([RAND_add], , + [AC_MSG_ERROR([*** working libcrypto not found, check config.log])]) + AC_CHECK_HEADER([openssl/opensslv.h], , + [AC_MSG_ERROR([*** OpenSSL headers missing - please install first or check config.log ***])]) + + # Determine OpenSSL header version + AC_MSG_CHECKING([OpenSSL header version]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #define DATA "conftest.sslincver" + ]], [[ + FILE *fd; + int rc; + + fd = fopen(DATA,"w"); + if(fd == NULL) + exit(1); + + if ((rc = fprintf(fd, "%08lx (%s)\n", + (unsigned long)OPENSSL_VERSION_NUMBER, + OPENSSL_VERSION_TEXT)) < 0) + exit(1); + + exit(0); + ]])], + [ + ssl_header_ver=`cat conftest.sslincver` + AC_MSG_RESULT([$ssl_header_ver]) + ], + [ + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([OpenSSL version header not found.]) + ], + [ + AC_MSG_WARN([cross compiling: not checking]) + ] + ) + + # Determining OpenSSL library version is version dependent. + AC_CHECK_FUNCS([OpenSSL_version OpenSSL_version_num]) + + # Determine OpenSSL library version + AC_MSG_CHECKING([OpenSSL library version]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #include + #define DATA "conftest.ssllibver" + ]], [[ + FILE *fd; + int rc; + + fd = fopen(DATA,"w"); + if(fd == NULL) + exit(1); +#ifndef OPENSSL_VERSION +# define OPENSSL_VERSION SSLEAY_VERSION +#endif +#ifndef HAVE_OPENSSL_VERSION +# define OpenSSL_version SSLeay_version +#endif +#ifndef HAVE_OPENSSL_VERSION_NUM +# define OpenSSL_version_num SSLeay +#endif + if ((rc = fprintf(fd, "%08lx (%s)\n", + (unsigned long)OpenSSL_version_num(), + OpenSSL_version(OPENSSL_VERSION))) < 0) + exit(1); + + exit(0); + ]])], + [ + ssl_library_ver=`cat conftest.ssllibver` + # Check version is supported. + case "$ssl_library_ver" in + 10000*|0*) + AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) + ;; + 100*) ;; # 1.0.x + 101000[[0123456]]*) + # https://github.com/openssl/openssl/pull/4613 + AC_MSG_ERROR([OpenSSL 1.1.x versions prior to 1.1.0g have a bug that breaks their use with OpenSSH (have "$ssl_library_ver")]) + ;; + 101*) ;; # 1.1.x + 200*) ;; # LibreSSL + 300*) ;; # OpenSSL 3 + 301*) ;; # OpenSSL development branch. + *) + AC_MSG_ERROR([Unknown/unsupported OpenSSL version ("$ssl_library_ver")]) + ;; + esac + AC_MSG_RESULT([$ssl_library_ver]) + ], + [ + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([OpenSSL library not found.]) + ], + [ + AC_MSG_WARN([cross compiling: not checking]) + ] + ) + + # Sanity check OpenSSL headers + AC_MSG_CHECKING([whether OpenSSL's headers match the library]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + ]], [[ +#ifndef HAVE_OPENSSL_VERSION_NUM +# define OpenSSL_version_num SSLeay +#endif + exit(OpenSSL_version_num() == OPENSSL_VERSION_NUMBER ? 0 : 1); + ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + if test "x$openssl_check_nonfatal" = "x"; then + AC_MSG_ERROR([Your OpenSSL headers do not match your + library. Check config.log for details. + If you are sure your installation is consistent, you can disable the check + by running "./configure --without-openssl-header-check". + Also see contrib/findssl.sh for help identifying header/library mismatches. + ]) + else + AC_MSG_WARN([Your OpenSSL headers do not match your + library. Check config.log for details. + Also see contrib/findssl.sh for help identifying header/library mismatches.]) + fi + ], + [ + AC_MSG_WARN([cross compiling: not checking]) + ] + ) + + AC_MSG_CHECKING([if programs using OpenSSL functions will link]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ #include ]], + [[ ERR_load_crypto_strings(); ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + saved_LIBS="$LIBS" + LIBS="$LIBS -ldl" + AC_MSG_CHECKING([if programs using OpenSSL need -ldl]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ #include ]], + [[ ERR_load_crypto_strings(); ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + LIBS="$saved_LIBS" + ] + ) + ] + ) + + AC_CHECK_FUNCS([ \ + BN_is_prime_ex \ + DSA_generate_parameters_ex \ + EVP_CIPHER_CTX_ctrl \ + EVP_DigestFinal_ex \ + EVP_DigestInit_ex \ + EVP_MD_CTX_cleanup \ + EVP_MD_CTX_copy_ex \ + EVP_MD_CTX_init \ + HMAC_CTX_init \ + RSA_generate_key_ex \ + RSA_get_default_method \ + ]) + + # OpenSSL_add_all_algorithms may be a macro. + AC_CHECK_FUNC(OpenSSL_add_all_algorithms, + AC_DEFINE(HAVE_OPENSSL_ADD_ALL_ALGORITHMS, 1, [as a function]), + AC_CHECK_DECL(OpenSSL_add_all_algorithms, + AC_DEFINE(HAVE_OPENSSL_ADD_ALL_ALGORITHMS, 1, [as a macro]), , + [[#include ]] + ) + ) + + # LibreSSL/OpenSSL 1.1x API + AC_CHECK_FUNCS([ \ + OPENSSL_init_crypto \ + DH_get0_key \ + DH_get0_pqg \ + DH_set0_key \ + DH_set_length1 \ + DH_set0_pqg \ + DSA_get0_key \ + DSA_get0_pqg \ + DSA_set0_key \ + DSA_set0_pqg \ + DSA_SIG_get0 \ + DSA_SIG_set0 \ + ECDSA_SIG_get0 \ + ECDSA_SIG_set0 \ + EVP_CIPHER_CTX_iv \ + EVP_CIPHER_CTX_iv_noconst \ + EVP_CIPHER_CTX_get_iv \ + EVP_CIPHER_CTX_get_updated_iv \ + EVP_CIPHER_CTX_set_iv \ + RSA_get0_crt_params \ + RSA_get0_factors \ + RSA_get0_key \ + RSA_set0_crt_params \ + RSA_set0_factors \ + RSA_set0_key \ + RSA_meth_free \ + RSA_meth_dup \ + RSA_meth_set1_name \ + RSA_meth_get_finish \ + RSA_meth_set_priv_enc \ + RSA_meth_set_priv_dec \ + RSA_meth_set_finish \ + EVP_PKEY_get0_RSA \ + EVP_MD_CTX_new \ + EVP_MD_CTX_free \ + EVP_chacha20 \ + ]) + + if test "x$openssl_engine" = "xyes" ; then + AC_MSG_CHECKING([for OpenSSL ENGINE support]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[ + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([USE_OPENSSL_ENGINE], [1], + [Enable OpenSSL engine support]) + ], [ AC_MSG_ERROR([OpenSSL ENGINE support not found]) + ]) + fi + + # Check for OpenSSL without EVP_aes_{192,256}_cbc + AC_MSG_CHECKING([whether OpenSSL has crippled AES support]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + ]], [[ + exit(EVP_aes_192_cbc() == NULL || EVP_aes_256_cbc() == NULL); + ]])], + [ + AC_MSG_RESULT([no]) + ], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([OPENSSL_LOBOTOMISED_AES], [1], + [libcrypto is missing AES 192 and 256 bit functions]) + ] + ) + + # Check for OpenSSL with EVP_aes_*ctr + AC_MSG_CHECKING([whether OpenSSL has AES CTR via EVP]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + ]], [[ + exit(EVP_aes_128_ctr() == NULL || + EVP_aes_192_cbc() == NULL || + EVP_aes_256_cbc() == NULL); + ]])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([OPENSSL_HAVE_EVPCTR], [1], + [libcrypto has EVP AES CTR]) + ], + [ + AC_MSG_RESULT([no]) + ] + ) + + # Check for OpenSSL with EVP_aes_*gcm + AC_MSG_CHECKING([whether OpenSSL has AES GCM via EVP]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + ]], [[ + exit(EVP_aes_128_gcm() == NULL || + EVP_aes_256_gcm() == NULL || + EVP_CTRL_GCM_SET_IV_FIXED == 0 || + EVP_CTRL_GCM_IV_GEN == 0 || + EVP_CTRL_GCM_SET_TAG == 0 || + EVP_CTRL_GCM_GET_TAG == 0 || + EVP_CIPHER_CTX_ctrl(NULL, 0, 0, NULL) == 0); + ]])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE([OPENSSL_HAVE_EVPGCM], [1], + [libcrypto has EVP AES GCM]) + ], + [ + AC_MSG_RESULT([no]) + unsupported_algorithms="$unsupported_cipers \ + aes128-gcm@openssh.com \ + aes256-gcm@openssh.com" + ] + ) + + AC_MSG_CHECKING([if EVP_DigestUpdate returns an int]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + ]], [[ + if(EVP_DigestUpdate(NULL, NULL,0)) + exit(0); + ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + AC_DEFINE([OPENSSL_EVP_DIGESTUPDATE_VOID], [1], + [Define if EVP_DigestUpdate returns void]) + ] + ) + + # Some systems want crypt() from libcrypt, *not* the version in OpenSSL, + # because the system crypt() is more featureful. + if test "x$check_for_libcrypt_before" = "x1"; then + AC_CHECK_LIB([crypt], [crypt]) + fi + + # Some Linux systems (Slackware) need crypt() from libcrypt, *not* the + # version in OpenSSL. + if test "x$check_for_libcrypt_later" = "x1"; then + AC_CHECK_LIB([crypt], [crypt], [LIBS="$LIBS -lcrypt"]) + fi + AC_CHECK_FUNCS([crypt DES_crypt]) + + # Check for SHA256, SHA384 and SHA512 support in OpenSSL + AC_CHECK_FUNCS([EVP_sha256 EVP_sha384 EVP_sha512]) + + # Check complete ECC support in OpenSSL + AC_MSG_CHECKING([whether OpenSSL has NID_X9_62_prime256v1]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #include + #include + ]], [[ + EC_KEY *e = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + const EVP_MD *m = EVP_sha256(); /* We need this too */ + ]])], + [ AC_MSG_RESULT([yes]) + enable_nistp256=1 ], + [ AC_MSG_RESULT([no]) ] + ) + + AC_MSG_CHECKING([whether OpenSSL has NID_secp384r1]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #include + #include + ]], [[ + EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp384r1); + const EVP_MD *m = EVP_sha384(); /* We need this too */ + ]])], + [ AC_MSG_RESULT([yes]) + enable_nistp384=1 ], + [ AC_MSG_RESULT([no]) ] + ) + + AC_MSG_CHECKING([whether OpenSSL has NID_secp521r1]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #include + #include + ]], [[ + EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); + const EVP_MD *m = EVP_sha512(); /* We need this too */ + ]])], + [ AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([if OpenSSL's NID_secp521r1 is functional]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #include + #include + #include + ]],[[ + EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); + const EVP_MD *m = EVP_sha512(); /* We need this too */ + exit(e == NULL || m == NULL); + ]])], + [ AC_MSG_RESULT([yes]) + enable_nistp521=1 ], + [ AC_MSG_RESULT([no]) ], + [ AC_MSG_WARN([cross-compiling: assuming yes]) + enable_nistp521=1 ] + )], + AC_MSG_RESULT([no]) + ) + + if test x$enable_nistp256 = x1 || test x$enable_nistp384 = x1 || \ + test x$enable_nistp521 = x1; then + AC_DEFINE(OPENSSL_HAS_ECC, [1], [OpenSSL has ECC]) + AC_CHECK_FUNCS([EC_KEY_METHOD_new]) + openssl_ecc=yes + else + openssl_ecc=no + fi + if test x$enable_nistp256 = x1; then + AC_DEFINE([OPENSSL_HAS_NISTP256], [1], + [libcrypto has NID_X9_62_prime256v1]) + else + unsupported_algorithms="$unsupported_algorithms \ + ecdsa-sha2-nistp256 \ + ecdh-sha2-nistp256 \ + ecdsa-sha2-nistp256-cert-v01@openssh.com" + fi + if test x$enable_nistp384 = x1; then + AC_DEFINE([OPENSSL_HAS_NISTP384], [1], [libcrypto has NID_secp384r1]) + else + unsupported_algorithms="$unsupported_algorithms \ + ecdsa-sha2-nistp384 \ + ecdh-sha2-nistp384 \ + ecdsa-sha2-nistp384-cert-v01@openssh.com" + fi + if test x$enable_nistp521 = x1; then + AC_DEFINE([OPENSSL_HAS_NISTP521], [1], [libcrypto has NID_secp521r1]) + else + unsupported_algorithms="$unsupported_algorithms \ + ecdh-sha2-nistp521 \ + ecdsa-sha2-nistp521 \ + ecdsa-sha2-nistp521-cert-v01@openssh.com" + fi + +else + AC_CHECK_LIB([crypt], [crypt], [LIBS="$LIBS -lcrypt"]) + AC_CHECK_FUNCS([crypt]) +fi + +# PKCS11/U2F depend on OpenSSL and dlopen(). +enable_pkcs11=yes +enable_sk=yes +if test "x$openssl" != "xyes" ; then + enable_pkcs11="disabled; missing libcrypto" +fi +if test "x$ac_cv_func_dlopen" != "xyes" ; then + enable_pkcs11="disabled; missing dlopen(3)" + enable_sk="disabled; missing dlopen(3)" +fi +if test "x$ac_cv_have_decl_RTLD_NOW" != "xyes" ; then + enable_pkcs11="disabled; missing RTLD_NOW" + enable_sk="disabled; missing RTLD_NOW" +fi +if test ! -z "$disable_pkcs11" ; then + enable_pkcs11="disabled by user" +fi +if test ! -z "$disable_sk" ; then + enable_sk="disabled by user" +fi + +AC_MSG_CHECKING([whether to enable PKCS11]) +if test "x$enable_pkcs11" = "xyes" ; then + AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support]) +fi +AC_MSG_RESULT([$enable_pkcs11]) + +AC_MSG_CHECKING([whether to enable U2F]) +if test "x$enable_sk" = "xyes" ; then + AC_DEFINE([ENABLE_SK], [], [Enable for U2F/FIDO support]) + AC_SUBST(SK_DUMMY_LIBRARY, [regress/misc/sk-dummy/sk-dummy.so]) +else + # Do not try to build sk-dummy library. + AC_SUBST(SK_DUMMY_LIBRARY, [""]) +fi +AC_MSG_RESULT([$enable_sk]) + +# Now check for built-in security key support. +if test "x$enable_sk" = "xyes" -a "x$enable_sk_internal" = "xyes" ; then + AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) + use_pkgconfig_for_libfido2= + if test "x$PKGCONFIG" != "xno"; then + AC_MSG_CHECKING([if $PKGCONFIG knows about libfido2]) + if "$PKGCONFIG" libfido2; then + AC_MSG_RESULT([yes]) + use_pkgconfig_for_libfido2=yes + else + AC_MSG_RESULT([no]) + fi + fi + if test "x$use_pkgconfig_for_libfido2" = "xyes"; then + LIBFIDO2=`$PKGCONFIG --libs libfido2` + CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libfido2`" + else + LIBFIDO2="-lfido2 -lcbor" + fi + OTHERLIBS=`echo $LIBFIDO2 | sed 's/-lfido2//'` + AC_CHECK_LIB([fido2], [fido_init], + [ + AC_SUBST([LIBFIDO2]) + AC_DEFINE([ENABLE_SK_INTERNAL], [], + [Enable for built-in U2F/FIDO support]) + enable_sk="built-in" + ], [ AC_MSG_ERROR([no usable libfido2 found]) ], + [ $OTHERLIBS ] + ) + saved_LIBS="$LIBS" + LIBS="$LIBS $LIBFIDO2" + AC_CHECK_FUNCS([ \ + fido_assert_set_clientdata \ + fido_cred_prot \ + fido_cred_set_prot \ + fido_cred_set_clientdata \ + fido_dev_get_touch_begin \ + fido_dev_get_touch_status \ + fido_dev_supports_cred_prot \ + ]) + LIBS="$saved_LIBS" + AC_CHECK_HEADER([fido.h], [], + AC_MSG_ERROR([missing fido.h from libfido2])) + AC_CHECK_HEADER([fido/credman.h], [], + AC_MSG_ERROR([missing fido/credman.h from libfido2]), + [#include ] + ) +fi + +AC_CHECK_FUNCS([ \ + arc4random \ + arc4random_buf \ + arc4random_stir \ + arc4random_uniform \ +]) + +saved_LIBS="$LIBS" +AC_CHECK_LIB([iaf], [ia_openinfo], [ + LIBS="$LIBS -liaf" + AC_CHECK_FUNCS([set_id], [SSHDLIBS="$SSHDLIBS -liaf" + AC_DEFINE([HAVE_LIBIAF], [1], + [Define if system has libiaf that supports set_id]) + ]) +]) +LIBS="$saved_LIBS" + +### Configure cryptographic random number support + +# Check whether OpenSSL seeds itself +if test "x$openssl" = "xyes" ; then + AC_MSG_CHECKING([whether OpenSSL's PRNG is internally seeded]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + #include + ]], [[ + exit(RAND_status() == 1 ? 0 : 1); + ]])], + [ + OPENSSL_SEEDS_ITSELF=yes + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + ], + [ + AC_MSG_WARN([cross compiling: assuming yes]) + # This is safe, since we will fatal() at runtime if + # OpenSSL is not seeded correctly. + OPENSSL_SEEDS_ITSELF=yes + ] + ) +fi + +# PRNGD TCP socket +AC_ARG_WITH([prngd-port], + [ --with-prngd-port=PORT read entropy from PRNGD/EGD TCP localhost:PORT], + [ + case "$withval" in + no) + withval="" + ;; + [[0-9]]*) + ;; + *) + AC_MSG_ERROR([You must specify a numeric port number for --with-prngd-port]) + ;; + esac + if test ! -z "$withval" ; then + PRNGD_PORT="$withval" + AC_DEFINE_UNQUOTED([PRNGD_PORT], [$PRNGD_PORT], + [Port number of PRNGD/EGD random number socket]) + fi + ] +) + +# PRNGD Unix domain socket +AC_ARG_WITH([prngd-socket], + [ --with-prngd-socket=FILE read entropy from PRNGD/EGD socket FILE (default=/var/run/egd-pool)], + [ + case "$withval" in + yes) + withval="/var/run/egd-pool" + ;; + no) + withval="" + ;; + /*) + ;; + *) + AC_MSG_ERROR([You must specify an absolute path to the entropy socket]) + ;; + esac + + if test ! -z "$withval" ; then + if test ! -z "$PRNGD_PORT" ; then + AC_MSG_ERROR([You may not specify both a PRNGD/EGD port and socket]) + fi + if test ! -r "$withval" ; then + AC_MSG_WARN([Entropy socket is not readable]) + fi + PRNGD_SOCKET="$withval" + AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"], + [Location of PRNGD/EGD random number socket]) + fi + ], + [ + # Check for existing socket only if we don't have a random device already + if test "x$OPENSSL_SEEDS_ITSELF" != "xyes" ; then + AC_MSG_CHECKING([for PRNGD/EGD socket]) + # Insert other locations here + for sock in /var/run/egd-pool /dev/egd-pool /etc/entropy; do + if test -r $sock && $TEST_MINUS_S_SH -c "test -S $sock -o -p $sock" ; then + PRNGD_SOCKET="$sock" + AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"]) + break; + fi + done + if test ! -z "$PRNGD_SOCKET" ; then + AC_MSG_RESULT([$PRNGD_SOCKET]) + else + AC_MSG_RESULT([not found]) + fi + fi + ] +) + +# Which randomness source do we use? +if test ! -z "$PRNGD_PORT" ; then + RAND_MSG="PRNGd port $PRNGD_PORT" +elif test ! -z "$PRNGD_SOCKET" ; then + RAND_MSG="PRNGd socket $PRNGD_SOCKET" +elif test ! -z "$OPENSSL_SEEDS_ITSELF" ; then + AC_DEFINE([OPENSSL_PRNG_ONLY], [1], + [Define if you want the OpenSSL internally seeded PRNG only]) + RAND_MSG="OpenSSL internal ONLY" +elif test "x$openssl" = "xno" ; then + AC_MSG_WARN([OpenSSH will use /dev/urandom as a source of random numbers. It will fail if this device is not supported or accessible]) +else + AC_MSG_ERROR([OpenSSH has no source of random numbers. Please configure OpenSSL with an entropy source or re-run configure using one of the --with-prngd-port or --with-prngd-socket options]) +fi + +# Check for PAM libs +PAM_MSG="no" +AC_ARG_WITH([pam], + [ --with-pam Enable PAM support ], + [ + if test "x$withval" != "xno" ; then + if test "x$ac_cv_header_security_pam_appl_h" != "xyes" && \ + test "x$ac_cv_header_pam_pam_appl_h" != "xyes" ; then + AC_MSG_ERROR([PAM headers not found]) + fi + + saved_LIBS="$LIBS" + AC_CHECK_LIB([dl], [dlopen], , ) + AC_CHECK_LIB([pam], [pam_set_item], , [AC_MSG_ERROR([*** libpam missing])]) + AC_CHECK_FUNCS([pam_getenvlist]) + AC_CHECK_FUNCS([pam_putenv]) + LIBS="$saved_LIBS" + + PAM_MSG="yes" + + SSHDLIBS="$SSHDLIBS -lpam" + AC_DEFINE([USE_PAM], [1], + [Define if you want to enable PAM support]) + + if test $ac_cv_lib_dl_dlopen = yes; then + case "$LIBS" in + *-ldl*) + # libdl already in LIBS + ;; + *) + SSHDLIBS="$SSHDLIBS -ldl" + ;; + esac + fi + fi + ] +) + +AC_ARG_WITH([pam-service], + [ --with-pam-service=name Specify PAM service name ], + [ + if test "x$withval" != "xno" && \ + test "x$withval" != "xyes" ; then + AC_DEFINE_UNQUOTED([SSHD_PAM_SERVICE], + ["$withval"], [sshd PAM service name]) + fi + ] +) + +# Check for older PAM +if test "x$PAM_MSG" = "xyes" ; then + # Check PAM strerror arguments (old PAM) + AC_MSG_CHECKING([whether pam_strerror takes only one argument]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#if defined(HAVE_SECURITY_PAM_APPL_H) +#include +#elif defined (HAVE_PAM_PAM_APPL_H) +#include +#endif + ]], [[ +(void)pam_strerror((pam_handle_t *)NULL, -1); + ]])], [AC_MSG_RESULT([no])], [ + AC_DEFINE([HAVE_OLD_PAM], [1], + [Define if you have an old version of PAM + which takes only one argument to pam_strerror]) + AC_MSG_RESULT([yes]) + PAM_MSG="yes (old library)" + + ]) +fi + +case "$host" in +*-*-cygwin*) + SSH_PRIVSEP_USER=CYGWIN_SSH_PRIVSEP_USER + ;; +*) + SSH_PRIVSEP_USER=sshd + ;; +esac +AC_ARG_WITH([privsep-user], + [ --with-privsep-user=user Specify non-privileged user for privilege separation], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + SSH_PRIVSEP_USER=$withval + fi + ] +) +if test "x$SSH_PRIVSEP_USER" = "xCYGWIN_SSH_PRIVSEP_USER" ; then + AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], [CYGWIN_SSH_PRIVSEP_USER], + [Cygwin function to fetch non-privileged user for privilege separation]) +else + AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], ["$SSH_PRIVSEP_USER"], + [non-privileged user for privilege separation]) +fi +AC_SUBST([SSH_PRIVSEP_USER]) + +if test "x$have_linux_no_new_privs" = "x1" ; then +AC_CHECK_DECL([SECCOMP_MODE_FILTER], [have_seccomp_filter=1], , [ + #include + #include +]) +fi +if test "x$have_seccomp_filter" = "x1" ; then +AC_MSG_CHECKING([kernel for seccomp_filter support]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #include + #include + ]], + [[ int i = $seccomp_audit_arch; + errno = 0; + prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); + exit(errno == EFAULT ? 0 : 1); ]])], + [ AC_MSG_RESULT([yes]) ], [ + AC_MSG_RESULT([no]) + # Disable seccomp filter as a target + have_seccomp_filter=0 + ] +) +fi + +# Decide which sandbox style to use +sandbox_arg="" +AC_ARG_WITH([sandbox], + [ --with-sandbox=style Specify privilege separation sandbox (no, capsicum, darwin, rlimit, seccomp_filter, systrace, pledge)], + [ + if test "x$withval" = "xyes" ; then + sandbox_arg="" + else + sandbox_arg="$withval" + fi + ] +) + +# POSIX specifies that poll() "shall fail with EINVAL if the nfds argument +# is greater than OPEN_MAX". On some platforms that includes implementions +# ofselect in userspace on top of poll() so check both work with rlimit NOFILES +# so check that both work before enabling the rlimit sandbox. +AC_MSG_CHECKING([if select and/or poll works with descriptor rlimit]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_POLL_H +# include +#elif HAVE_SYS_POLL_H +# include +#endif +#include +#include +#include + ]],[[ + struct rlimit rl_zero; + int fd, r; + fd_set fds; + struct timeval tv; +#ifdef HAVE_POLL + struct pollfd pfd; +#endif + + fd = open("/dev/null", O_RDONLY); + FD_ZERO(&fds); + FD_SET(fd, &fds); + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + setrlimit(RLIMIT_FSIZE, &rl_zero); + setrlimit(RLIMIT_NOFILE, &rl_zero); + tv.tv_sec = 1; + tv.tv_usec = 0; + r = select(fd+1, &fds, NULL, NULL, &tv); + if (r == -1) + exit(1); +#ifdef HAVE_POLL + pfd.fd = fd; + pfd.events = POLLIN; + r = poll(&pfd, 1, 1); + if (r == -1) + exit(2); +#endif + exit(0); + ]])], + [AC_MSG_RESULT([yes]) + select_works_with_rlimit=yes], + [AC_MSG_RESULT([no]) + select_works_with_rlimit=no], + [AC_MSG_WARN([cross compiling: assuming yes]) + select_works_with_rlimit=yes] +) + +AC_CHECK_MEMBERS([struct pollfd.fd], [], [], [[ +#include +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#endif +]]) + +AC_CHECK_TYPES([nfds_t], , , [ +#include +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#endif +]) + +AC_MSG_CHECKING([if setrlimit(RLIMIT_NOFILE,{0,0}) works]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include +#include +#include + ]],[[ + struct rlimit rl_zero; + int r; + + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + r = setrlimit(RLIMIT_NOFILE, &rl_zero); + exit (r == -1 ? 1 : 0); + ]])], + [AC_MSG_RESULT([yes]) + rlimit_nofile_zero_works=yes], + [AC_MSG_RESULT([no]) + rlimit_nofile_zero_works=no], + [AC_MSG_WARN([cross compiling: assuming yes]) + rlimit_nofile_zero_works=yes] +) + +AC_MSG_CHECKING([if setrlimit RLIMIT_FSIZE works]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include + ]],[[ + struct rlimit rl_zero; + + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + exit(setrlimit(RLIMIT_FSIZE, &rl_zero) != 0); + ]])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AC_DEFINE(SANDBOX_SKIP_RLIMIT_FSIZE, 1, + [setrlimit RLIMIT_FSIZE works])], + [AC_MSG_WARN([cross compiling: assuming yes])] +) + +if test "x$sandbox_arg" = "xpledge" || \ + ( test -z "$sandbox_arg" && test "x$ac_cv_func_pledge" = "xyes" ) ; then + test "x$ac_cv_func_pledge" != "xyes" && \ + AC_MSG_ERROR([pledge sandbox requires pledge(2) support]) + SANDBOX_STYLE="pledge" + AC_DEFINE([SANDBOX_PLEDGE], [1], [Sandbox using pledge(2)]) +elif test "x$sandbox_arg" = "xsystrace" || \ + ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then + test "x$have_systr_policy_kill" != "x1" && \ + AC_MSG_ERROR([systrace sandbox requires systrace headers and SYSTR_POLICY_KILL support]) + SANDBOX_STYLE="systrace" + AC_DEFINE([SANDBOX_SYSTRACE], [1], [Sandbox using systrace(4)]) +elif test "x$sandbox_arg" = "xdarwin" || \ + ( test -z "$sandbox_arg" && test "x$ac_cv_func_sandbox_init" = "xyes" && \ + test "x$ac_cv_header_sandbox_h" = "xyes") ; then + test "x$ac_cv_func_sandbox_init" != "xyes" -o \ + "x$ac_cv_header_sandbox_h" != "xyes" && \ + AC_MSG_ERROR([Darwin seatbelt sandbox requires sandbox.h and sandbox_init function]) + SANDBOX_STYLE="darwin" + AC_DEFINE([SANDBOX_DARWIN], [1], [Sandbox using Darwin sandbox_init(3)]) +elif test "x$sandbox_arg" = "xseccomp_filter" || \ + ( test -z "$sandbox_arg" && \ + test "x$have_seccomp_filter" = "x1" && \ + test "x$ac_cv_header_elf_h" = "xyes" && \ + test "x$ac_cv_header_linux_audit_h" = "xyes" && \ + test "x$ac_cv_header_linux_filter_h" = "xyes" && \ + test "x$seccomp_audit_arch" != "x" && \ + test "x$have_linux_no_new_privs" = "x1" && \ + test "x$ac_cv_func_prctl" = "xyes" ) ; then + test "x$seccomp_audit_arch" = "x" && \ + AC_MSG_ERROR([seccomp_filter sandbox not supported on $host]) + test "x$have_linux_no_new_privs" != "x1" && \ + AC_MSG_ERROR([seccomp_filter sandbox requires PR_SET_NO_NEW_PRIVS]) + test "x$have_seccomp_filter" != "x1" && \ + AC_MSG_ERROR([seccomp_filter sandbox requires seccomp headers]) + test "x$ac_cv_func_prctl" != "xyes" && \ + AC_MSG_ERROR([seccomp_filter sandbox requires prctl function]) + SANDBOX_STYLE="seccomp_filter" + AC_DEFINE([SANDBOX_SECCOMP_FILTER], [1], [Sandbox using seccomp filter]) +elif test "x$sandbox_arg" = "xcapsicum" || \ + ( test -z "$sandbox_arg" && \ + test "x$disable_capsicum" != "xyes" && \ + test "x$ac_cv_header_sys_capsicum_h" = "xyes" && \ + test "x$ac_cv_func_cap_rights_limit" = "xyes") ; then + test "x$ac_cv_header_sys_capsicum_h" != "xyes" && \ + AC_MSG_ERROR([capsicum sandbox requires sys/capsicum.h header]) + test "x$ac_cv_func_cap_rights_limit" != "xyes" && \ + AC_MSG_ERROR([capsicum sandbox requires cap_rights_limit function]) + SANDBOX_STYLE="capsicum" + AC_DEFINE([SANDBOX_CAPSICUM], [1], [Sandbox using capsicum]) +elif test "x$sandbox_arg" = "xrlimit" || \ + ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" && \ + test "x$select_works_with_rlimit" = "xyes" && \ + test "x$rlimit_nofile_zero_works" = "xyes" ) ; then + test "x$ac_cv_func_setrlimit" != "xyes" && \ + AC_MSG_ERROR([rlimit sandbox requires setrlimit function]) + test "x$select_works_with_rlimit" != "xyes" && \ + AC_MSG_ERROR([rlimit sandbox requires select to work with rlimit]) + SANDBOX_STYLE="rlimit" + AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)]) +elif test "x$sandbox_arg" = "xsolaris" || \ + ( test -z "$sandbox_arg" && test "x$SOLARIS_PRIVS" = "xyes" ) ; then + SANDBOX_STYLE="solaris" + AC_DEFINE([SANDBOX_SOLARIS], [1], [Sandbox using Solaris/Illumos privileges]) +elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \ + test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then + SANDBOX_STYLE="none" + AC_DEFINE([SANDBOX_NULL], [1], [no privsep sandboxing]) +else + AC_MSG_ERROR([unsupported --with-sandbox]) +fi + +# Cheap hack to ensure NEWS-OS libraries are arranged right. +if test ! -z "$SONY" ; then + LIBS="$LIBS -liberty"; +fi + +# Check for long long datatypes +AC_CHECK_TYPES([long long, unsigned long long, long double]) + +# Check datatype sizes +AC_CHECK_SIZEOF([short int]) +AC_CHECK_SIZEOF([int]) +AC_CHECK_SIZEOF([long int]) +AC_CHECK_SIZEOF([long long int]) +AC_CHECK_SIZEOF([time_t], [], [[ + #include + #ifdef HAVE_SYS_TIME_H + # include + #endif + #ifdef HAVE_TIME_H + # include + #endif + ]] +) + +# Sanity check long long for some platforms (AIX) +if test "x$ac_cv_sizeof_long_long_int" = "x4" ; then + ac_cv_sizeof_long_long_int=0 +fi + +# compute LLONG_MIN and LLONG_MAX if we don't know them. +if test -z "$have_llong_max" && test -z "$have_long_long_max"; then + AC_MSG_CHECKING([for max value of long long]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +/* Why is this so damn hard? */ +#ifdef __GNUC__ +# undef __GNUC__ +#endif +#define __USE_ISOC99 +#include +#define DATA "conftest.llminmax" +#define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) + +/* + * printf in libc on some platforms (eg old Tru64) does not understand %lld so + * we do this the hard way. + */ +static int +fprint_ll(FILE *f, long long n) +{ + unsigned int i; + int l[sizeof(long long) * 8]; + + if (n < 0) + if (fprintf(f, "-") < 0) + return -1; + for (i = 0; n != 0; i++) { + l[i] = my_abs(n % 10); + n /= 10; + } + do { + if (fprintf(f, "%d", l[--i]) < 0) + return -1; + } while (i != 0); + if (fprintf(f, " ") < 0) + return -1; + return 0; +} + ]], [[ + FILE *f; + long long i, llmin, llmax = 0; + + if((f = fopen(DATA,"w")) == NULL) + exit(1); + +#if defined(LLONG_MIN) && defined(LLONG_MAX) + fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); + llmin = LLONG_MIN; + llmax = LLONG_MAX; +#else + fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); + /* This will work on one's complement and two's complement */ + for (i = 1; i > llmax; i <<= 1, i++) + llmax = i; + llmin = llmax + 1LL; /* wrap */ +#endif + + /* Sanity check */ + if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax + || llmax - 1 > llmax || llmin == llmax || llmin == 0 + || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { + fprintf(f, "unknown unknown\n"); + exit(2); + } + + if (fprint_ll(f, llmin) < 0) + exit(3); + if (fprint_ll(f, llmax) < 0) + exit(4); + if (fclose(f) < 0) + exit(5); + exit(0); + ]])], + [ + llong_min=`$AWK '{print $1}' conftest.llminmax` + llong_max=`$AWK '{print $2}' conftest.llminmax` + + AC_MSG_RESULT([$llong_max]) + AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL], + [max value of long long calculated by configure]) + AC_MSG_CHECKING([for min value of long long]) + AC_MSG_RESULT([$llong_min]) + AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL], + [min value of long long calculated by configure]) + ], + [ + AC_MSG_RESULT([not found]) + ], + [ + AC_MSG_WARN([cross compiling: not checking]) + ] + ) +fi + +AC_CHECK_DECLS([UINT32_MAX], , , [[ +#ifdef HAVE_SYS_LIMITS_H +# include +#endif +#ifdef HAVE_LIMITS_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +]]) + +# More checks for data types +AC_CACHE_CHECK([for u_int type], ac_cv_have_u_int, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ u_int a; a = 1;]])], + [ ac_cv_have_u_int="yes" ], [ ac_cv_have_u_int="no" + ]) +]) +if test "x$ac_cv_have_u_int" = "xyes" ; then + AC_DEFINE([HAVE_U_INT], [1], [define if you have u_int data type]) + have_u_int=1 +fi + +AC_CACHE_CHECK([for intXX_t types], ac_cv_have_intxx_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], + [ ac_cv_have_intxx_t="yes" ], [ ac_cv_have_intxx_t="no" + ]) +]) +if test "x$ac_cv_have_intxx_t" = "xyes" ; then + AC_DEFINE([HAVE_INTXX_T], [1], [define if you have intxx_t data type]) + have_intxx_t=1 +fi + +if (test -z "$have_intxx_t" && \ + test "x$ac_cv_header_stdint_h" = "xyes") +then + AC_MSG_CHECKING([for intXX_t types in stdint.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], + [ + AC_DEFINE([HAVE_INTXX_T]) + AC_MSG_RESULT([yes]) + ], [ AC_MSG_RESULT([no]) + ]) +fi + +AC_CACHE_CHECK([for int64_t type], ac_cv_have_int64_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#ifdef HAVE_SYS_BITYPES_H +# include +#endif + ]], [[ +int64_t a; a = 1; + ]])], + [ ac_cv_have_int64_t="yes" ], [ ac_cv_have_int64_t="no" + ]) +]) +if test "x$ac_cv_have_int64_t" = "xyes" ; then + AC_DEFINE([HAVE_INT64_T], [1], [define if you have int64_t data type]) +fi + +AC_CACHE_CHECK([for u_intXX_t types], ac_cv_have_u_intxx_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], + [ ac_cv_have_u_intxx_t="yes" ], [ ac_cv_have_u_intxx_t="no" + ]) +]) +if test "x$ac_cv_have_u_intxx_t" = "xyes" ; then + AC_DEFINE([HAVE_U_INTXX_T], [1], [define if you have u_intxx_t data type]) + have_u_intxx_t=1 +fi + +if test -z "$have_u_intxx_t" ; then + AC_MSG_CHECKING([for u_intXX_t types in sys/socket.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], + [ + AC_DEFINE([HAVE_U_INTXX_T]) + AC_MSG_RESULT([yes]) + ], [ AC_MSG_RESULT([no]) + ]) +fi + +AC_CACHE_CHECK([for u_int64_t types], ac_cv_have_u_int64_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ u_int64_t a; a = 1;]])], + [ ac_cv_have_u_int64_t="yes" ], [ ac_cv_have_u_int64_t="no" + ]) +]) +if test "x$ac_cv_have_u_int64_t" = "xyes" ; then + AC_DEFINE([HAVE_U_INT64_T], [1], [define if you have u_int64_t data type]) + have_u_int64_t=1 +fi + +if (test -z "$have_u_int64_t" && \ + test "x$ac_cv_header_sys_bitypes_h" = "xyes") +then + AC_MSG_CHECKING([for u_int64_t type in sys/bitypes.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ u_int64_t a; a = 1]])], + [ + AC_DEFINE([HAVE_U_INT64_T]) + AC_MSG_RESULT([yes]) + ], [ AC_MSG_RESULT([no]) + ]) +fi + +if test -z "$have_u_intxx_t" ; then + AC_CACHE_CHECK([for uintXX_t types], ac_cv_have_uintxx_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include + ]], [[ + uint8_t a; + uint16_t b; + uint32_t c; + a = b = c = 1; + ]])], + [ ac_cv_have_uintxx_t="yes" ], [ ac_cv_have_uintxx_t="no" + ]) + ]) + if test "x$ac_cv_have_uintxx_t" = "xyes" ; then + AC_DEFINE([HAVE_UINTXX_T], [1], + [define if you have uintxx_t data type]) + fi +fi + +if (test -z "$have_uintxx_t" && \ + test "x$ac_cv_header_stdint_h" = "xyes") +then + AC_MSG_CHECKING([for uintXX_t types in stdint.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], + [ + AC_DEFINE([HAVE_UINTXX_T]) + AC_MSG_RESULT([yes]) + ], [ AC_MSG_RESULT([no]) + ]) +fi + +if (test -z "$have_uintxx_t" && \ + test "x$ac_cv_header_inttypes_h" = "xyes") +then + AC_MSG_CHECKING([for uintXX_t types in inttypes.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], + [ + AC_DEFINE([HAVE_UINTXX_T]) + AC_MSG_RESULT([yes]) + ], [ AC_MSG_RESULT([no]) + ]) +fi + +if (test -z "$have_u_intxx_t" || test -z "$have_intxx_t" && \ + test "x$ac_cv_header_sys_bitypes_h" = "xyes") +then + AC_MSG_CHECKING([for intXX_t and u_intXX_t types in sys/bitypes.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include + ]], [[ + int8_t a; int16_t b; int32_t c; + u_int8_t e; u_int16_t f; u_int32_t g; + a = b = c = e = f = g = 1; + ]])], + [ + AC_DEFINE([HAVE_U_INTXX_T]) + AC_DEFINE([HAVE_INTXX_T]) + AC_MSG_RESULT([yes]) + ], [AC_MSG_RESULT([no]) + ]) +fi + + +AC_CACHE_CHECK([for u_char], ac_cv_have_u_char, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ u_char foo; foo = 125; ]])], + [ ac_cv_have_u_char="yes" ], [ ac_cv_have_u_char="no" + ]) +]) +if test "x$ac_cv_have_u_char" = "xyes" ; then + AC_DEFINE([HAVE_U_CHAR], [1], [define if you have u_char data type]) +fi + +AC_CHECK_TYPES([intmax_t, uintmax_t], , , [ +#include +#ifdef HAVE_STDINT_H +# include +#endif +]) + +TYPE_SOCKLEN_T + +AC_CHECK_TYPES([sig_atomic_t, sighandler_t], , , [#include ]) +AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], , , [ +#include +#ifdef HAVE_SYS_BITYPES_H +#include +#endif +#ifdef HAVE_SYS_STATFS_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +]) + +AC_CHECK_MEMBERS([struct statfs.f_files, struct statfs.f_flags], [], [], [[ +#include +#include +#ifdef HAVE_SYS_BITYPES_H +#include +#endif +#ifdef HAVE_SYS_STATFS_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +]]) + + +AC_CHECK_TYPES([in_addr_t, in_port_t], , , +[#include +#include ]) + +AC_CACHE_CHECK([for size_t], ac_cv_have_size_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ size_t foo; foo = 1235; ]])], + [ ac_cv_have_size_t="yes" ], [ ac_cv_have_size_t="no" + ]) +]) +if test "x$ac_cv_have_size_t" = "xyes" ; then + AC_DEFINE([HAVE_SIZE_T], [1], [define if you have size_t data type]) +fi + +AC_CACHE_CHECK([for ssize_t], ac_cv_have_ssize_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ ssize_t foo; foo = 1235; ]])], + [ ac_cv_have_ssize_t="yes" ], [ ac_cv_have_ssize_t="no" + ]) +]) +if test "x$ac_cv_have_ssize_t" = "xyes" ; then + AC_DEFINE([HAVE_SSIZE_T], [1], [define if you have ssize_t data type]) +fi + +AC_CACHE_CHECK([for clock_t], ac_cv_have_clock_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ clock_t foo; foo = 1235; ]])], + [ ac_cv_have_clock_t="yes" ], [ ac_cv_have_clock_t="no" + ]) +]) +if test "x$ac_cv_have_clock_t" = "xyes" ; then + AC_DEFINE([HAVE_CLOCK_T], [1], [define if you have clock_t data type]) +fi + +AC_CACHE_CHECK([for sa_family_t], ac_cv_have_sa_family_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ sa_family_t foo; foo = 1235; ]])], + [ ac_cv_have_sa_family_t="yes" ], + [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include + ]], [[ sa_family_t foo; foo = 1235; ]])], + [ ac_cv_have_sa_family_t="yes" ], + [ ac_cv_have_sa_family_t="no" ] + ) + ]) +]) +if test "x$ac_cv_have_sa_family_t" = "xyes" ; then + AC_DEFINE([HAVE_SA_FAMILY_T], [1], + [define if you have sa_family_t data type]) +fi + +AC_CACHE_CHECK([for pid_t], ac_cv_have_pid_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ pid_t foo; foo = 1235; ]])], + [ ac_cv_have_pid_t="yes" ], [ ac_cv_have_pid_t="no" + ]) +]) +if test "x$ac_cv_have_pid_t" = "xyes" ; then + AC_DEFINE([HAVE_PID_T], [1], [define if you have pid_t data type]) +fi + +AC_CACHE_CHECK([for mode_t], ac_cv_have_mode_t, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ mode_t foo; foo = 1235; ]])], + [ ac_cv_have_mode_t="yes" ], [ ac_cv_have_mode_t="no" + ]) +]) +if test "x$ac_cv_have_mode_t" = "xyes" ; then + AC_DEFINE([HAVE_MODE_T], [1], [define if you have mode_t data type]) +fi + + +AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ struct sockaddr_storage s; ]])], + [ ac_cv_have_struct_sockaddr_storage="yes" ], + [ ac_cv_have_struct_sockaddr_storage="no" + ]) +]) +if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then + AC_DEFINE([HAVE_STRUCT_SOCKADDR_STORAGE], [1], + [define if you have struct sockaddr_storage data type]) +fi + +AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ struct sockaddr_in6 s; s.sin6_family = 0; ]])], + [ ac_cv_have_struct_sockaddr_in6="yes" ], + [ ac_cv_have_struct_sockaddr_in6="no" + ]) +]) +if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then + AC_DEFINE([HAVE_STRUCT_SOCKADDR_IN6], [1], + [define if you have struct sockaddr_in6 data type]) +fi + +AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ struct in6_addr s; s.s6_addr[0] = 0; ]])], + [ ac_cv_have_struct_in6_addr="yes" ], + [ ac_cv_have_struct_in6_addr="no" + ]) +]) +if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then + AC_DEFINE([HAVE_STRUCT_IN6_ADDR], [1], + [define if you have struct in6_addr data type]) + +dnl Now check for sin6_scope_id + AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], , , + [ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + ]) +fi + +AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include + ]], [[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ]])], + [ ac_cv_have_struct_addrinfo="yes" ], + [ ac_cv_have_struct_addrinfo="no" + ]) +]) +if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then + AC_DEFINE([HAVE_STRUCT_ADDRINFO], [1], + [define if you have struct addrinfo data type]) +fi + +AC_CACHE_CHECK([for struct timeval], ac_cv_have_struct_timeval, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ struct timeval tv; tv.tv_sec = 1;]])], + [ ac_cv_have_struct_timeval="yes" ], + [ ac_cv_have_struct_timeval="no" + ]) +]) +if test "x$ac_cv_have_struct_timeval" = "xyes" ; then + AC_DEFINE([HAVE_STRUCT_TIMEVAL], [1], [define if you have struct timeval]) + have_struct_timeval=1 +fi + +AC_CACHE_CHECK([for struct timespec], ac_cv_have_struct_timespec, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #ifdef HAVE_SYS_TIME_H + # include + #endif + #ifdef HAVE_TIME_H + # include + #endif + ]], + [[ struct timespec ts; ts.tv_sec = 1;]])], + [ ac_cv_have_struct_timespec="yes" ], + [ ac_cv_have_struct_timespec="no" + ]) +]) +if test "x$ac_cv_have_struct_timespec" = "xyes" ; then + AC_DEFINE([HAVE_STRUCT_TIMESPEC], [1], [define if you have struct timespec]) + have_struct_timespec=1 +fi + +# We need int64_t or else certain parts of the compile will fail. +if test "x$ac_cv_have_int64_t" = "xno" && \ + test "x$ac_cv_sizeof_long_int" != "x8" && \ + test "x$ac_cv_sizeof_long_long_int" = "x0" ; then + echo "OpenSSH requires int64_t support. Contact your vendor or install" + echo "an alternative compiler (I.E., GCC) before continuing." + echo "" + exit 1; +else +dnl test snprintf (broken on SCO w/gcc) + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +#include +#ifdef HAVE_SNPRINTF +main() +{ + char buf[50]; + char expected_out[50]; + int mazsize = 50 ; +#if (SIZEOF_LONG_INT == 8) + long int num = 0x7fffffffffffffff; +#else + long long num = 0x7fffffffffffffffll; +#endif + strcpy(expected_out, "9223372036854775807"); + snprintf(buf, mazsize, "%lld", num); + if(strcmp(buf, expected_out) != 0) + exit(1); + exit(0); +} +#else +main() { exit(0); } +#endif + ]])], [ true ], [ AC_DEFINE([BROKEN_SNPRINTF]) ], + AC_MSG_WARN([cross compiling: Assuming working snprintf()]) + ) +fi + +dnl Checks for structure members +OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmp.h], [HAVE_HOST_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmpx.h], [HAVE_HOST_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([syslen], [utmpx.h], [HAVE_SYSLEN_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_pid], [utmp.h], [HAVE_PID_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmp.h], [HAVE_TYPE_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmpx.h], [HAVE_TYPE_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmp.h], [HAVE_TV_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmp.h], [HAVE_ID_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmpx.h], [HAVE_ID_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmp.h], [HAVE_ADDR_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmpx.h], [HAVE_ADDR_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmp.h], [HAVE_ADDR_V6_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmpx.h], [HAVE_ADDR_V6_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_exit], [utmp.h], [HAVE_EXIT_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmp.h], [HAVE_TIME_IN_UTMP]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmpx.h], [HAVE_TIME_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmpx.h], [HAVE_TV_IN_UTMPX]) +OSSH_CHECK_HEADER_FOR_FIELD([ut_ss], [utmpx.h], [HAVE_SS_IN_UTMPX]) + +AC_CHECK_MEMBERS([struct stat.st_blksize]) +AC_CHECK_MEMBERS([struct stat.st_mtim]) +AC_CHECK_MEMBERS([struct stat.st_mtime]) +AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, +struct passwd.pw_change, struct passwd.pw_expire], +[], [], [[ +#include +#include +]]) + +AC_CHECK_MEMBER([struct __res_state.retrans], [], [AC_DEFINE([__res_state], [state], + [Define if we don't have struct __res_state in resolv.h])], +[[ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#include +#include +#include +]]) + +AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], + ac_cv_have_ss_family_in_struct_ss, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ struct sockaddr_storage s; s.ss_family = 1; ]])], + [ ac_cv_have_ss_family_in_struct_ss="yes" ], + [ ac_cv_have_ss_family_in_struct_ss="no" ]) +]) +if test "x$ac_cv_have_ss_family_in_struct_ss" = "xyes" ; then + AC_DEFINE([HAVE_SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) +fi + +AC_CACHE_CHECK([for __ss_family field in struct sockaddr_storage], + ac_cv_have___ss_family_in_struct_ss, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ struct sockaddr_storage s; s.__ss_family = 1; ]])], + [ ac_cv_have___ss_family_in_struct_ss="yes" ], + [ ac_cv_have___ss_family_in_struct_ss="no" + ]) +]) +if test "x$ac_cv_have___ss_family_in_struct_ss" = "xyes" ; then + AC_DEFINE([HAVE___SS_FAMILY_IN_SS], [1], + [Fields in struct sockaddr_storage]) +fi + +dnl make sure we're using the real structure members and not defines +AC_CACHE_CHECK([for msg_accrights field in struct msghdr], + ac_cv_have_accrights_in_msghdr, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include + ]], [[ +#ifdef msg_accrights +#error "msg_accrights is a macro" +exit(1); +#endif +struct msghdr m; +m.msg_accrights = 0; +exit(0); + ]])], + [ ac_cv_have_accrights_in_msghdr="yes" ], + [ ac_cv_have_accrights_in_msghdr="no" ] + ) +]) +if test "x$ac_cv_have_accrights_in_msghdr" = "xyes" ; then + AC_DEFINE([HAVE_ACCRIGHTS_IN_MSGHDR], [1], + [Define if your system uses access rights style + file descriptor passing]) +fi + +AC_MSG_CHECKING([if struct statvfs.f_fsid is integral type]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + ]], [[ struct statvfs s; s.f_fsid = 0; ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + + AC_MSG_CHECKING([if fsid_t has member val]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ fsid_t t; t.val[0] = 0; ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([FSID_HAS_VAL], [1], [fsid_t has member val]) ], + [ AC_MSG_RESULT([no]) ]) + + AC_MSG_CHECKING([if f_fsid has member __val]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + ]], [[ fsid_t t; t.__val[0] = 0; ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([FSID_HAS___VAL], [1], [fsid_t has member __val]) ], + [ AC_MSG_RESULT([no]) ]) +]) + +AC_CACHE_CHECK([for msg_control field in struct msghdr], + ac_cv_have_control_in_msghdr, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include + ]], [[ +#ifdef msg_control +#error "msg_control is a macro" +exit(1); +#endif +struct msghdr m; +m.msg_control = 0; +exit(0); + ]])], + [ ac_cv_have_control_in_msghdr="yes" ], + [ ac_cv_have_control_in_msghdr="no" ] + ) +]) +if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then + AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1], + [Define if your system uses ancillary data style + file descriptor passing]) +fi + +AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ extern char *__progname; printf("%s", __progname); ]])], + [ ac_cv_libc_defines___progname="yes" ], + [ ac_cv_libc_defines___progname="no" + ]) +]) +if test "x$ac_cv_libc_defines___progname" = "xyes" ; then + AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname]) +fi + +AC_CACHE_CHECK([whether $CC implements __FUNCTION__], ac_cv_cc_implements___FUNCTION__, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ printf("%s", __FUNCTION__); ]])], + [ ac_cv_cc_implements___FUNCTION__="yes" ], + [ ac_cv_cc_implements___FUNCTION__="no" + ]) +]) +if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes" ; then + AC_DEFINE([HAVE___FUNCTION__], [1], + [Define if compiler implements __FUNCTION__]) +fi + +AC_CACHE_CHECK([whether $CC implements __func__], ac_cv_cc_implements___func__, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ printf("%s", __func__); ]])], + [ ac_cv_cc_implements___func__="yes" ], + [ ac_cv_cc_implements___func__="no" + ]) +]) +if test "x$ac_cv_cc_implements___func__" = "xyes" ; then + AC_DEFINE([HAVE___func__], [1], [Define if compiler implements __func__]) +fi + +AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +va_list x,y; + ]], [[ va_copy(x,y); ]])], + [ ac_cv_have_va_copy="yes" ], + [ ac_cv_have_va_copy="no" + ]) +]) +if test "x$ac_cv_have_va_copy" = "xyes" ; then + AC_DEFINE([HAVE_VA_COPY], [1], [Define if va_copy exists]) +fi + +AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +va_list x,y; + ]], [[ __va_copy(x,y); ]])], + [ ac_cv_have___va_copy="yes" ], [ ac_cv_have___va_copy="no" + ]) +]) +if test "x$ac_cv_have___va_copy" = "xyes" ; then + AC_DEFINE([HAVE___VA_COPY], [1], [Define if __va_copy exists]) +fi + +AC_CACHE_CHECK([whether getopt has optreset support], + ac_cv_have_getopt_optreset, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], + [[ extern int optreset; optreset = 0; ]])], + [ ac_cv_have_getopt_optreset="yes" ], + [ ac_cv_have_getopt_optreset="no" + ]) +]) +if test "x$ac_cv_have_getopt_optreset" = "xyes" ; then + AC_DEFINE([HAVE_GETOPT_OPTRESET], [1], + [Define if your getopt(3) defines and uses optreset]) +fi + +AC_CACHE_CHECK([if libc defines sys_errlist], ac_cv_libc_defines_sys_errlist, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], +[[ extern const char *const sys_errlist[]; printf("%s", sys_errlist[0]);]])], + [ ac_cv_libc_defines_sys_errlist="yes" ], + [ ac_cv_libc_defines_sys_errlist="no" + ]) +]) +if test "x$ac_cv_libc_defines_sys_errlist" = "xyes" ; then + AC_DEFINE([HAVE_SYS_ERRLIST], [1], + [Define if your system defines sys_errlist[]]) +fi + + +AC_CACHE_CHECK([if libc defines sys_nerr], ac_cv_libc_defines_sys_nerr, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], +[[ extern int sys_nerr; printf("%i", sys_nerr);]])], + [ ac_cv_libc_defines_sys_nerr="yes" ], + [ ac_cv_libc_defines_sys_nerr="no" + ]) +]) +if test "x$ac_cv_libc_defines_sys_nerr" = "xyes" ; then + AC_DEFINE([HAVE_SYS_NERR], [1], [Define if your system defines sys_nerr]) +fi + +# Check libraries needed by DNS fingerprint support +AC_SEARCH_LIBS([getrrsetbyname], [resolv], + [AC_DEFINE([HAVE_GETRRSETBYNAME], [1], + [Define if getrrsetbyname() exists])], + [ + # Needed by our getrrsetbyname() + AC_SEARCH_LIBS([res_query], [resolv]) + AC_SEARCH_LIBS([dn_expand], [resolv]) + AC_MSG_CHECKING([if res_query will link]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include +#include + ]], [[ + res_query (0, 0, 0, 0, 0); + ]])], + AC_MSG_RESULT([yes]), + [AC_MSG_RESULT([no]) + saved_LIBS="$LIBS" + LIBS="$LIBS -lresolv" + AC_MSG_CHECKING([for res_query in -lresolv]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include +#include + ]], [[ + res_query (0, 0, 0, 0, 0); + ]])], + [AC_MSG_RESULT([yes])], + [LIBS="$saved_LIBS" + AC_MSG_RESULT([no])]) + ]) + AC_CHECK_FUNCS([_getshort _getlong]) + AC_CHECK_DECLS([_getshort, _getlong], , , + [#include + #include ]) + AC_CHECK_MEMBER([HEADER.ad], + [AC_DEFINE([HAVE_HEADER_AD], [1], + [Define if HEADER.ad exists in arpa/nameser.h])], , + [#include ]) + ]) + +AC_MSG_CHECKING([if struct __res_state _res is an extern]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#include +#include +#include +extern struct __res_state _res; + ]], [[ +struct __res_state *volatile p = &_res; /* force resolution of _res */ +return 0; + ]],)], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE__RES_EXTERN], [1], + [Define if you have struct __res_state _res as an extern]) + ], + [ AC_MSG_RESULT([no]) ] +) + +# Check whether user wants SELinux support +SELINUX_MSG="no" +LIBSELINUX="" +AC_ARG_WITH([selinux], + [ --with-selinux Enable SELinux support], + [ if test "x$withval" != "xno" ; then + save_LIBS="$LIBS" + AC_DEFINE([WITH_SELINUX], [1], + [Define if you want SELinux support.]) + SELINUX_MSG="yes" + AC_CHECK_HEADER([selinux/selinux.h], , + AC_MSG_ERROR([SELinux support requires selinux.h header])) + AC_CHECK_LIB([selinux], [setexeccon], + [ LIBSELINUX="-lselinux" + LIBS="$LIBS -lselinux" + ], + AC_MSG_ERROR([SELinux support requires libselinux library])) + AC_CHECK_FUNCS([getseuserbyname get_default_context_with_level]) + LIBS="$save_LIBS $LIBSELINUX" + fi ] +) +AC_SUBST([SSHDLIBS]) + +# Check whether user wants Kerberos 5 support +KRB5_MSG="no" +AC_ARG_WITH([kerberos5], + [ --with-kerberos5=PATH Enable Kerberos 5 support], + [ if test "x$withval" != "xno" ; then + if test "x$withval" = "xyes" ; then + KRB5ROOT="/usr/local" + else + KRB5ROOT=${withval} + fi + + AC_DEFINE([KRB5], [1], [Define if you want Kerberos 5 support]) + KRB5_MSG="yes" + + AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) + use_pkgconfig_for_krb5= + if test "x$PKGCONFIG" != "xno"; then + AC_MSG_CHECKING([if $PKGCONFIG knows about kerberos5]) + if "$PKGCONFIG" krb5; then + AC_MSG_RESULT([yes]) + use_pkgconfig_for_krb5=yes + else + AC_MSG_RESULT([no]) + fi + fi + if test "x$use_pkgconfig_for_krb5" = "xyes"; then + K5CFLAGS=`$PKGCONFIG --cflags krb5` + K5LIBS=`$PKGCONFIG --libs krb5` + CPPFLAGS="$CPPFLAGS $K5CFLAGS" + + AC_MSG_CHECKING([for gssapi support]) + if "$PKGCONFIG" krb5-gssapi; then + AC_MSG_RESULT([yes]) + AC_DEFINE([GSSAPI], [1], + [Define this if you want GSSAPI + support in the version 2 protocol]) + GSSCFLAGS="`$PKGCONFIG --cflags krb5-gssapi`" + GSSLIBS="`$PKGCONFIG --libs krb5-gssapi`" + CPPFLAGS="$CPPFLAGS $GSSCFLAGS" + else + AC_MSG_RESULT([no]) + fi + AC_MSG_CHECKING([whether we are using Heimdal]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include + ]], [[ char *tmp = heimdal_version; ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([HEIMDAL], [1], + [Define this if you are using the Heimdal + version of Kerberos V5]) ], + [AC_MSG_RESULT([no]) + ]) + else + AC_PATH_TOOL([KRB5CONF], [krb5-config], + [$KRB5ROOT/bin/krb5-config], + [$KRB5ROOT/bin:$PATH]) + if test -x $KRB5CONF ; then + K5CFLAGS="`$KRB5CONF --cflags`" + K5LIBS="`$KRB5CONF --libs`" + CPPFLAGS="$CPPFLAGS $K5CFLAGS" + + AC_MSG_CHECKING([for gssapi support]) + if $KRB5CONF | grep gssapi >/dev/null ; then + AC_MSG_RESULT([yes]) + AC_DEFINE([GSSAPI], [1], + [Define this if you want GSSAPI + support in the version 2 protocol]) + GSSCFLAGS="`$KRB5CONF --cflags gssapi`" + GSSLIBS="`$KRB5CONF --libs gssapi`" + CPPFLAGS="$CPPFLAGS $GSSCFLAGS" + else + AC_MSG_RESULT([no]) + fi + AC_MSG_CHECKING([whether we are using Heimdal]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include + ]], [[ char *tmp = heimdal_version; ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([HEIMDAL], [1], + [Define this if you are using the Heimdal + version of Kerberos V5]) ], + [AC_MSG_RESULT([no]) + ]) + else + CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include" + LDFLAGS="$LDFLAGS -L${KRB5ROOT}/lib" + AC_MSG_CHECKING([whether we are using Heimdal]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include + ]], [[ char *tmp = heimdal_version; ]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([HEIMDAL]) + K5LIBS="-lkrb5" + K5LIBS="$K5LIBS -lcom_err -lasn1" + AC_CHECK_LIB([roken], [net_write], + [K5LIBS="$K5LIBS -lroken"]) + AC_CHECK_LIB([des], [des_cbc_encrypt], + [K5LIBS="$K5LIBS -ldes"]) + ], [ AC_MSG_RESULT([no]) + K5LIBS="-lkrb5 -lk5crypto -lcom_err" + ]) + AC_SEARCH_LIBS([dn_expand], [resolv]) + + AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context], + [ AC_DEFINE([GSSAPI]) + GSSLIBS="-lgssapi_krb5" ], + [ AC_CHECK_LIB([gssapi], [gss_init_sec_context], + [ AC_DEFINE([GSSAPI]) + GSSLIBS="-lgssapi" ], + [ AC_CHECK_LIB([gss], [gss_init_sec_context], + [ AC_DEFINE([GSSAPI]) + GSSLIBS="-lgss" ], + AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail])) + ]) + ]) + + AC_CHECK_HEADER([gssapi.h], , + [ unset ac_cv_header_gssapi_h + CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" + AC_CHECK_HEADERS([gssapi.h], , + AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail]) + ) + ] + ) + + oldCPP="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" + AC_CHECK_HEADER([gssapi_krb5.h], , + [ CPPFLAGS="$oldCPP" ]) + + fi + fi + if test -n "${rpath_opt}" ; then + LDFLAGS="$LDFLAGS ${rpath_opt}${KRB5ROOT}/lib" + fi + if test ! -z "$blibpath" ; then + blibpath="$blibpath:${KRB5ROOT}/lib" + fi + + AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h]) + AC_CHECK_HEADERS([gssapi_krb5.h gssapi/gssapi_krb5.h]) + AC_CHECK_HEADERS([gssapi_generic.h gssapi/gssapi_generic.h]) + + AC_SEARCH_LIBS([k_hasafs], [kafs], [AC_DEFINE([USE_AFS], [1], + [Define this if you want to use libkafs' AFS support])]) + + AC_CHECK_DECLS([GSS_C_NT_HOSTBASED_SERVICE], [], [], [[ +#ifdef HAVE_GSSAPI_H +# include +#elif defined(HAVE_GSSAPI_GSSAPI_H) +# include +#endif + +#ifdef HAVE_GSSAPI_GENERIC_H +# include +#elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H) +# include +#endif + ]]) + saved_LIBS="$LIBS" + LIBS="$LIBS $K5LIBS" + AC_CHECK_FUNCS([krb5_cc_new_unique krb5_get_error_message krb5_free_error_message]) + LIBS="$saved_LIBS" + + fi + ] +) +AC_SUBST([GSSLIBS]) +AC_SUBST([K5LIBS]) + +# Looking for programs, paths and files + +PRIVSEP_PATH=/var/empty +AC_ARG_WITH([privsep-path], + [ --with-privsep-path=xxx Path for privilege separation chroot (default=/var/empty)], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + PRIVSEP_PATH=$withval + fi + ] +) +AC_SUBST([PRIVSEP_PATH]) + +AC_ARG_WITH([xauth], + [ --with-xauth=PATH Specify path to xauth program ], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + xauth_path=$withval + fi + ], + [ + TestPath="$PATH" + TestPath="${TestPath}${PATH_SEPARATOR}/usr/X/bin" + TestPath="${TestPath}${PATH_SEPARATOR}/usr/bin/X11" + TestPath="${TestPath}${PATH_SEPARATOR}/usr/X11R6/bin" + TestPath="${TestPath}${PATH_SEPARATOR}/usr/openwin/bin" + AC_PATH_PROG([xauth_path], [xauth], , [$TestPath]) + if (test ! -z "$xauth_path" && test -x "/usr/openwin/bin/xauth") ; then + xauth_path="/usr/openwin/bin/xauth" + fi + ] +) + +STRIP_OPT=-s +AC_ARG_ENABLE([strip], + [ --disable-strip Disable calling strip(1) on install], + [ + if test "x$enableval" = "xno" ; then + STRIP_OPT= + fi + ] +) +AC_SUBST([STRIP_OPT]) + +if test -z "$xauth_path" ; then + XAUTH_PATH="undefined" + AC_SUBST([XAUTH_PATH]) +else + AC_DEFINE_UNQUOTED([XAUTH_PATH], ["$xauth_path"], + [Define if xauth is found in your path]) + XAUTH_PATH=$xauth_path + AC_SUBST([XAUTH_PATH]) +fi + +dnl # --with-maildir=/path/to/mail gets top priority. +dnl # if maildir is set in the platform case statement above we use that. +dnl # Otherwise we run a program to get the dir from system headers. +dnl # We first look for _PATH_MAILDIR then MAILDIR then _PATH_MAIL +dnl # If we find _PATH_MAILDIR we do nothing because that is what +dnl # session.c expects anyway. Otherwise we set to the value found +dnl # stripping any trailing slash. If for some strage reason our program +dnl # does not find what it needs, we default to /var/spool/mail. +# Check for mail directory +AC_ARG_WITH([maildir], + [ --with-maildir=/path/to/mail Specify your system mail directory], + [ + if test "X$withval" != X && test "x$withval" != xno && \ + test "x${withval}" != xyes; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"], + [Set this to your mail directory if you do not have _PATH_MAILDIR]) + fi + ],[ + if test "X$maildir" != "X"; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) + else + AC_MSG_CHECKING([Discovering system mail directory]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#ifdef HAVE_MAILLOCK_H +#include +#endif +#define DATA "conftest.maildir" + ]], [[ + FILE *fd; + int rc; + + fd = fopen(DATA,"w"); + if(fd == NULL) + exit(1); + +#if defined (_PATH_MAILDIR) + if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0) + exit(1); +#elif defined (MAILDIR) + if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0) + exit(1); +#elif defined (_PATH_MAIL) + if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0) + exit(1); +#else + exit (2); +#endif + + exit(0); + ]])], + [ + maildir_what=`awk -F: '{print $1}' conftest.maildir` + maildir=`awk -F: '{print $2}' conftest.maildir \ + | sed 's|/$||'` + AC_MSG_RESULT([Using: $maildir from $maildir_what]) + if test "x$maildir_what" != "x_PATH_MAILDIR"; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) + fi + ], + [ + if test "X$ac_status" = "X2";then +# our test program didn't find it. Default to /var/spool/mail + AC_MSG_RESULT([Using: default value of /var/spool/mail]) + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["/var/spool/mail"]) + else + AC_MSG_RESULT([*** not found ***]) + fi + ], + [ + AC_MSG_WARN([cross compiling: use --with-maildir=/path/to/mail]) + ] + ) + fi + ] +) # maildir + +if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then + AC_MSG_WARN([cross compiling: Disabling /dev/ptmx test (assuming yes)]) + disable_ptmx_check=yes + + AC_DEFINE_UNQUOTED([HAVE_DEV_PTMX], [1], + [Define if you have /dev/ptmx]) + have_dev_ptmx=1 +fi +if test -z "$no_dev_ptmx" ; then + if test "x$disable_ptmx_check" != "xyes" ; then + AC_CHECK_FILE(["/dev/ptmx"], + [ + AC_DEFINE_UNQUOTED([HAVE_DEV_PTMX], [1], + [Define if you have /dev/ptmx]) + have_dev_ptmx=1 + ] + ) + fi +fi + +if test ! -z "$cross_compiling" && test "x$cross_compiling" != "xyes"; then + AC_CHECK_FILE(["/dev/ptc"], + [ + AC_DEFINE_UNQUOTED([HAVE_DEV_PTS_AND_PTC], [1], + [Define if you have /dev/ptc]) + have_dev_ptc=1 + ] + ) +else + AC_MSG_WARN([cross compiling: Disabling /dev/ptc test]) +fi + +# Options from here on. Some of these are preset by platform above +AC_ARG_WITH([mantype], + [ --with-mantype=man|cat|doc Set man page type], + [ + case "$withval" in + man|cat|doc) + MANTYPE=$withval + ;; + *) + AC_MSG_ERROR([invalid man type: $withval]) + ;; + esac + ] +) +if test -z "$MANTYPE"; then + if ${MANDOC} ${srcdir}/ssh.1 >/dev/null 2>&1; then + MANTYPE=doc + elif ${NROFF} -mdoc ${srcdir}/ssh.1 >/dev/null 2>&1; then + MANTYPE=doc + elif ${NROFF} -man ${srcdir}/ssh.1 >/dev/null 2>&1; then + MANTYPE=man + else + MANTYPE=cat + fi +fi +AC_SUBST([MANTYPE]) +if test "$MANTYPE" = "doc"; then + mansubdir=man; +else + mansubdir=$MANTYPE; +fi +AC_SUBST([mansubdir]) + +# Whether to disable shadow password support +AC_ARG_WITH([shadow], + [ --without-shadow Disable shadow password support], + [ + if test "x$withval" = "xno" ; then + AC_DEFINE([DISABLE_SHADOW]) + disable_shadow=yes + fi + ] +) + +if test -z "$disable_shadow" ; then + AC_MSG_CHECKING([if the systems has expire shadow information]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +struct spwd sp; + ]], [[ sp.sp_expire = sp.sp_lstchg = sp.sp_inact = 0; ]])], + [ sp_expire_available=yes ], [ + ]) + + if test "x$sp_expire_available" = "xyes" ; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAS_SHADOW_EXPIRE], [1], + [Define if you want to use shadow password expire field]) + else + AC_MSG_RESULT([no]) + fi +fi + +# Use ip address instead of hostname in $DISPLAY +if test ! -z "$IPADDR_IN_DISPLAY" ; then + DISPLAY_HACK_MSG="yes" + AC_DEFINE([IPADDR_IN_DISPLAY], [1], + [Define if you need to use IP address + instead of hostname in $DISPLAY]) +else + DISPLAY_HACK_MSG="no" + AC_ARG_WITH([ipaddr-display], + [ --with-ipaddr-display Use ip address instead of hostname in $DISPLAY], + [ + if test "x$withval" != "xno" ; then + AC_DEFINE([IPADDR_IN_DISPLAY]) + DISPLAY_HACK_MSG="yes" + fi + ] + ) +fi + +# check for /etc/default/login and use it if present. +AC_ARG_ENABLE([etc-default-login], + [ --disable-etc-default-login Disable using PATH from /etc/default/login [no]], + [ if test "x$enableval" = "xno"; then + AC_MSG_NOTICE([/etc/default/login handling disabled]) + etc_default_login=no + else + etc_default_login=yes + fi ], + [ if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; + then + AC_MSG_WARN([cross compiling: not checking /etc/default/login]) + etc_default_login=no + else + etc_default_login=yes + fi ] +) + +if test "x$etc_default_login" != "xno"; then + AC_CHECK_FILE(["/etc/default/login"], + [ external_path_file=/etc/default/login ]) + if test "x$external_path_file" = "x/etc/default/login"; then + AC_DEFINE([HAVE_ETC_DEFAULT_LOGIN], [1], + [Define if your system has /etc/default/login]) + fi +fi + +dnl BSD systems use /etc/login.conf so --with-default-path= has no effect +if test $ac_cv_func_login_getcapbool = "yes" && \ + test $ac_cv_header_login_cap_h = "yes" ; then + external_path_file=/etc/login.conf +fi + +# Whether to mess with the default path +SERVER_PATH_MSG="(default)" +AC_ARG_WITH([default-path], + [ --with-default-path= Specify default $PATH environment for server], + [ + if test "x$external_path_file" = "x/etc/login.conf" ; then + AC_MSG_WARN([ +--with-default-path=PATH has no effect on this system. +Edit /etc/login.conf instead.]) + elif test "x$withval" != "xno" ; then + if test ! -z "$external_path_file" ; then + AC_MSG_WARN([ +--with-default-path=PATH will only be used if PATH is not defined in +$external_path_file .]) + fi + user_path="$withval" + SERVER_PATH_MSG="$withval" + fi + ], + [ if test "x$external_path_file" = "x/etc/login.conf" ; then + AC_MSG_WARN([Make sure the path to scp is in /etc/login.conf]) + else + if test ! -z "$external_path_file" ; then + AC_MSG_WARN([ +If PATH is defined in $external_path_file, ensure the path to scp is included, +otherwise scp will not work.]) + fi + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +/* find out what STDPATH is */ +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#ifndef _PATH_STDPATH +# ifdef _PATH_USERPATH /* Irix */ +# define _PATH_STDPATH _PATH_USERPATH +# else +# define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" +# endif +#endif +#include +#include +#include +#define DATA "conftest.stdpath" + ]], [[ + FILE *fd; + int rc; + + fd = fopen(DATA,"w"); + if(fd == NULL) + exit(1); + + if ((rc = fprintf(fd,"%s", _PATH_STDPATH)) < 0) + exit(1); + + exit(0); + ]])], + [ user_path=`cat conftest.stdpath` ], + [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ], + [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ] + ) +# make sure $bindir is in USER_PATH so scp will work + t_bindir="${bindir}" + while echo "${t_bindir}" | egrep '\$\{|NONE/' >/dev/null 2>&1; do + t_bindir=`eval echo ${t_bindir}` + case $t_bindir in + NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$prefix~"` ;; + esac + case $t_bindir in + NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$ac_default_prefix~"` ;; + esac + done + echo $user_path | grep ":$t_bindir" > /dev/null 2>&1 + if test $? -ne 0 ; then + echo $user_path | grep "^$t_bindir" > /dev/null 2>&1 + if test $? -ne 0 ; then + user_path=$user_path:$t_bindir + AC_MSG_RESULT([Adding $t_bindir to USER_PATH so scp will work]) + fi + fi + fi ] +) +if test "x$external_path_file" != "x/etc/login.conf" ; then + AC_DEFINE_UNQUOTED([USER_PATH], ["$user_path"], [Specify default $PATH]) + AC_SUBST([user_path]) +fi + +# Set superuser path separately to user path +AC_ARG_WITH([superuser-path], + [ --with-superuser-path= Specify different path for super-user], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + AC_DEFINE_UNQUOTED([SUPERUSER_PATH], ["$withval"], + [Define if you want a different $PATH + for the superuser]) + superuser_path=$withval + fi + ] +) + + +AC_MSG_CHECKING([if we need to convert IPv4 in IPv6-mapped addresses]) +IPV4_IN6_HACK_MSG="no" +AC_ARG_WITH(4in6, + [ --with-4in6 Check for and convert IPv4 in IPv6 mapped addresses], + [ + if test "x$withval" != "xno" ; then + AC_MSG_RESULT([yes]) + AC_DEFINE([IPV4_IN_IPV6], [1], + [Detect IPv4 in IPv6 mapped addresses + and treat as IPv4]) + IPV4_IN6_HACK_MSG="yes" + else + AC_MSG_RESULT([no]) + fi + ], [ + if test "x$inet6_default_4in6" = "xyes"; then + AC_MSG_RESULT([yes (default)]) + AC_DEFINE([IPV4_IN_IPV6]) + IPV4_IN6_HACK_MSG="yes" + else + AC_MSG_RESULT([no (default)]) + fi + ] +) + +# Whether to enable BSD auth support +BSD_AUTH_MSG=no +AC_ARG_WITH([bsd-auth], + [ --with-bsd-auth Enable BSD auth support], + [ + if test "x$withval" != "xno" ; then + AC_DEFINE([BSD_AUTH], [1], + [Define if you have BSD auth support]) + BSD_AUTH_MSG=yes + fi + ] +) + +# Where to place sshd.pid +piddir=/var/run +# make sure the directory exists +if test ! -d $piddir ; then + piddir=`eval echo ${sysconfdir}` + case $piddir in + NONE/*) piddir=`echo $piddir | sed "s~NONE~$ac_default_prefix~"` ;; + esac +fi + +AC_ARG_WITH([pid-dir], + [ --with-pid-dir=PATH Specify location of sshd.pid file], + [ + if test -n "$withval" && test "x$withval" != "xno" && \ + test "x${withval}" != "xyes"; then + piddir=$withval + if test ! -d $piddir ; then + AC_MSG_WARN([** no $piddir directory on this system **]) + fi + fi + ] +) + +AC_DEFINE_UNQUOTED([_PATH_SSH_PIDDIR], ["$piddir"], + [Specify location of ssh.pid]) +AC_SUBST([piddir]) + +dnl allow user to disable some login recording features +AC_ARG_ENABLE([lastlog], + [ --disable-lastlog disable use of lastlog even if detected [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_LASTLOG]) + fi + ] +) +AC_ARG_ENABLE([utmp], + [ --disable-utmp disable use of utmp even if detected [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_UTMP]) + fi + ] +) +AC_ARG_ENABLE([utmpx], + [ --disable-utmpx disable use of utmpx even if detected [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_UTMPX], [1], + [Define if you don't want to use utmpx]) + fi + ] +) +AC_ARG_ENABLE([wtmp], + [ --disable-wtmp disable use of wtmp even if detected [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_WTMP]) + fi + ] +) +AC_ARG_ENABLE([wtmpx], + [ --disable-wtmpx disable use of wtmpx even if detected [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_WTMPX], [1], + [Define if you don't want to use wtmpx]) + fi + ] +) +AC_ARG_ENABLE([libutil], + [ --disable-libutil disable use of libutil (login() etc.) [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_LOGIN]) + fi + ] +) +AC_ARG_ENABLE([pututline], + [ --disable-pututline disable use of pututline() etc. ([uw]tmp) [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_PUTUTLINE], [1], + [Define if you don't want to use pututline() + etc. to write [uw]tmp]) + fi + ] +) +AC_ARG_ENABLE([pututxline], + [ --disable-pututxline disable use of pututxline() etc. ([uw]tmpx) [no]], + [ + if test "x$enableval" = "xno" ; then + AC_DEFINE([DISABLE_PUTUTXLINE], [1], + [Define if you don't want to use pututxline() + etc. to write [uw]tmpx]) + fi + ] +) +AC_ARG_WITH([lastlog], + [ --with-lastlog=FILE|DIR specify lastlog location [common locations]], + [ + if test "x$withval" = "xno" ; then + AC_DEFINE([DISABLE_LASTLOG]) + elif test -n "$withval" && test "x${withval}" != "xyes"; then + conf_lastlog_location=$withval + fi + ] +) + +dnl lastlog, [uw]tmpx? detection +dnl NOTE: set the paths in the platform section to avoid the +dnl need for command-line parameters +dnl lastlog and [uw]tmp are subject to a file search if all else fails + +dnl lastlog detection +dnl NOTE: the code itself will detect if lastlog is a directory +AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#ifdef HAVE_LASTLOG_H +# include +#endif +#ifdef HAVE_PATHS_H +# include +#endif +#ifdef HAVE_LOGIN_H +# include +#endif + ]], [[ char *lastlog = LASTLOG_FILE; ]])], + [ AC_MSG_RESULT([yes]) ], + [ + AC_MSG_RESULT([no]) + AC_MSG_CHECKING([if your system defines _PATH_LASTLOG]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#ifdef HAVE_LASTLOG_H +# include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + ]], [[ char *lastlog = _PATH_LASTLOG; ]])], + [ AC_MSG_RESULT([yes]) ], + [ + AC_MSG_RESULT([no]) + system_lastlog_path=no + ]) +]) + +if test -z "$conf_lastlog_location"; then + if test x"$system_lastlog_path" = x"no" ; then + for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do + if (test -d "$f" || test -f "$f") ; then + conf_lastlog_location=$f + fi + done + if test -z "$conf_lastlog_location"; then + AC_MSG_WARN([** Cannot find lastlog **]) + dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx + fi + fi +fi + +if test -n "$conf_lastlog_location"; then + AC_DEFINE_UNQUOTED([CONF_LASTLOG_FILE], ["$conf_lastlog_location"], + [Define if you want to specify the path to your lastlog file]) +fi + +dnl utmp detection +AC_MSG_CHECKING([if your system defines UTMP_FILE]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif + ]], [[ char *utmp = UTMP_FILE; ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + system_utmp_path=no +]) +if test -z "$conf_utmp_location"; then + if test x"$system_utmp_path" = x"no" ; then + for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do + if test -f $f ; then + conf_utmp_location=$f + fi + done + if test -z "$conf_utmp_location"; then + AC_DEFINE([DISABLE_UTMP]) + fi + fi +fi +if test -n "$conf_utmp_location"; then + AC_DEFINE_UNQUOTED([CONF_UTMP_FILE], ["$conf_utmp_location"], + [Define if you want to specify the path to your utmp file]) +fi + +dnl wtmp detection +AC_MSG_CHECKING([if your system defines WTMP_FILE]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif + ]], [[ char *wtmp = WTMP_FILE; ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + system_wtmp_path=no +]) +if test -z "$conf_wtmp_location"; then + if test x"$system_wtmp_path" = x"no" ; then + for f in /usr/adm/wtmp /var/log/wtmp; do + if test -f $f ; then + conf_wtmp_location=$f + fi + done + if test -z "$conf_wtmp_location"; then + AC_DEFINE([DISABLE_WTMP]) + fi + fi +fi +if test -n "$conf_wtmp_location"; then + AC_DEFINE_UNQUOTED([CONF_WTMP_FILE], ["$conf_wtmp_location"], + [Define if you want to specify the path to your wtmp file]) +fi + +dnl wtmpx detection +AC_MSG_CHECKING([if your system defines WTMPX_FILE]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + ]], [[ char *wtmpx = WTMPX_FILE; ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + system_wtmpx_path=no +]) +if test -z "$conf_wtmpx_location"; then + if test x"$system_wtmpx_path" = x"no" ; then + AC_DEFINE([DISABLE_WTMPX]) + fi +else + AC_DEFINE_UNQUOTED([CONF_WTMPX_FILE], ["$conf_wtmpx_location"], + [Define if you want to specify the path to your wtmpx file]) +fi + + +if test ! -z "$blibpath" ; then + LDFLAGS="$LDFLAGS $blibflags$blibpath" + AC_MSG_WARN([Please check and edit blibpath in LDFLAGS in Makefile]) +fi + +AC_CHECK_MEMBER([struct lastlog.ll_line], [], [ + if test x$SKIP_DISABLE_LASTLOG_DEFINE != "xyes" ; then + AC_DEFINE([DISABLE_LASTLOG]) + fi + ], [ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UTMP_H +#include +#endif +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef HAVE_LASTLOG_H +#include +#endif + ]) + +AC_CHECK_MEMBER([struct utmp.ut_line], [], [ + AC_DEFINE([DISABLE_UTMP]) + AC_DEFINE([DISABLE_WTMP]) + ], [ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UTMP_H +#include +#endif +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef HAVE_LASTLOG_H +#include +#endif + ]) + +dnl Adding -Werror to CFLAGS early prevents configure tests from running. +dnl Add now. +CFLAGS="$CFLAGS $werror_flags" + +if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then + TEST_SSH_IPV6=no +else + TEST_SSH_IPV6=yes +fi +AC_CHECK_DECL([BROKEN_GETADDRINFO], [TEST_SSH_IPV6=no]) +AC_SUBST([TEST_SSH_IPV6], [$TEST_SSH_IPV6]) +AC_SUBST([TEST_SSH_UTF8], [$TEST_SSH_UTF8]) +AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS]) +AC_SUBST([UNSUPPORTED_ALGORITHMS], [$unsupported_algorithms]) +AC_SUBST([DEPEND], [$(cat $srcdir/.depend)]) + +CFLAGS="${CFLAGS} ${CFLAGS_AFTER}" +LDFLAGS="${LDFLAGS} ${LDFLAGS_AFTER}" + +# Make a copy of CFLAGS/LDFLAGS without PIE options. +LDFLAGS_NOPIE=`echo "$LDFLAGS" | sed 's/ -pie//'` +CFLAGS_NOPIE=`echo "$CFLAGS" | sed 's/ -fPIE//'` +AC_SUBST([LDFLAGS_NOPIE]) +AC_SUBST([CFLAGS_NOPIE]) + +AC_EXEEXT +AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \ + openbsd-compat/Makefile openbsd-compat/regress/Makefile \ + survey.sh]) +AC_OUTPUT + +# Print summary of options + +# Someone please show me a better way :) +A=`eval echo ${prefix}` ; A=`eval echo ${A}` +B=`eval echo ${bindir}` ; B=`eval echo ${B}` +C=`eval echo ${sbindir}` ; C=`eval echo ${C}` +D=`eval echo ${sysconfdir}` ; D=`eval echo ${D}` +E=`eval echo ${libexecdir}/ssh-askpass` ; E=`eval echo ${E}` +F=`eval echo ${mandir}/${mansubdir}X` ; F=`eval echo ${F}` +G=`eval echo ${piddir}` ; G=`eval echo ${G}` +H=`eval echo ${PRIVSEP_PATH}` ; H=`eval echo ${H}` +I=`eval echo ${user_path}` ; I=`eval echo ${I}` +J=`eval echo ${superuser_path}` ; J=`eval echo ${J}` + +echo "" +echo "OpenSSH has been configured with the following options:" +echo " User binaries: $B" +echo " System binaries: $C" +echo " Configuration files: $D" +echo " Askpass program: $E" +echo " Manual pages: $F" +echo " PID file: $G" +echo " Privilege separation chroot path: $H" +if test "x$external_path_file" = "x/etc/login.conf" ; then +echo " At runtime, sshd will use the path defined in $external_path_file" +echo " Make sure the path to scp is present, otherwise scp will not work" +else +echo " sshd default user PATH: $I" + if test ! -z "$external_path_file"; then +echo " (If PATH is set in $external_path_file it will be used instead. If" +echo " used, ensure the path to scp is present, otherwise scp will not work.)" + fi +fi +if test ! -z "$superuser_path" ; then +echo " sshd superuser user PATH: $J" +fi +echo " Manpage format: $MANTYPE" +echo " PAM support: $PAM_MSG" +echo " OSF SIA support: $SIA_MSG" +echo " KerberosV support: $KRB5_MSG" +echo " SELinux support: $SELINUX_MSG" +echo " libedit support: $LIBEDIT_MSG" +echo " libldns support: $LDNS_MSG" +echo " Solaris process contract support: $SPC_MSG" +echo " Solaris project support: $SP_MSG" +echo " Solaris privilege support: $SPP_MSG" +echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" +echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" +echo " BSD Auth support: $BSD_AUTH_MSG" +echo " Random number source: $RAND_MSG" +echo " Privsep sandbox style: $SANDBOX_STYLE" +echo " PKCS#11 support: $enable_pkcs11" +echo " U2F/FIDO support: $enable_sk" + +echo "" + +echo " Host: ${host}" +echo " Compiler: ${CC}" +echo " Compiler flags: ${CFLAGS}" +echo "Preprocessor flags: ${CPPFLAGS}" +echo " Linker flags: ${LDFLAGS}" +echo " Libraries: ${LIBS}" +if test ! -z "${SSHDLIBS}"; then +echo " +for sshd: ${SSHDLIBS}" +fi + +echo "" + +if test "x$MAKE_PACKAGE_SUPPORTED" = "xyes" ; then + echo "SVR4 style packages are supported with \"make package\"" + echo "" +fi + +if test "x$PAM_MSG" = "xyes" ; then + echo "PAM is enabled. You may need to install a PAM control file " + echo "for sshd, otherwise password authentication may fail. " + echo "Example PAM control files can be found in the contrib/ " + echo "subdirectory" + echo "" +fi + +if test ! -z "$NO_PEERCHECK" ; then + echo "WARNING: the operating system that you are using does not" + echo "appear to support getpeereid(), getpeerucred() or the" + echo "SO_PEERCRED getsockopt() option. These facilities are used to" + echo "enforce security checks to prevent unauthorised connections to" + echo "ssh-agent. Their absence increases the risk that a malicious" + echo "user can connect to your agent." + echo "" +fi + +if test "$AUDIT_MODULE" = "bsm" ; then + echo "WARNING: BSM audit support is currently considered EXPERIMENTAL." + echo "See the Solaris section in README.platform for details." +fi diff --git a/aosp/external/openssh/contrib/Makefile b/aosp/external/openssh/contrib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..45d878bdcf22135ddcf5778460740e75dc9b8b83 --- /dev/null +++ b/aosp/external/openssh/contrib/Makefile @@ -0,0 +1,22 @@ +PKG_CONFIG = pkg-config + +all: + @echo "Valid targets: gnome-ssh-askpass1 gnome-ssh-askpass2 gnome-ssk-askpass3" + +gnome-ssh-askpass1: gnome-ssh-askpass1.c + $(CC) $(CFLAGS) `gnome-config --cflags gnome gnomeui` \ + gnome-ssh-askpass1.c -o gnome-ssh-askpass1 \ + `gnome-config --libs gnome gnomeui` + +gnome-ssh-askpass2: gnome-ssh-askpass2.c + $(CC) $(CFLAGS) `$(PKG_CONFIG) --cflags gtk+-2.0` \ + gnome-ssh-askpass2.c -o gnome-ssh-askpass2 \ + `$(PKG_CONFIG) --libs gtk+-2.0 x11` + +gnome-ssh-askpass3: gnome-ssh-askpass3.c + $(CC) $(CFLAGS) `$(PKG_CONFIG) --cflags gtk+-3.0` \ + gnome-ssh-askpass3.c -o gnome-ssh-askpass3 \ + `$(PKG_CONFIG) --libs gtk+-3.0 x11` + +clean: + rm -f *.o gnome-ssh-askpass gnome-ssh-askpass[123] diff --git a/aosp/external/openssh/contrib/README b/aosp/external/openssh/contrib/README new file mode 100644 index 0000000000000000000000000000000000000000..60e19ba9faa834bf2ebdeafd04e01effb6ee0a24 --- /dev/null +++ b/aosp/external/openssh/contrib/README @@ -0,0 +1,70 @@ +Other patches and addons for OpenSSH. Please send submissions to +djm@mindrot.org + +Externally maintained +--------------------- + +SSH Proxy Command -- connect.c + +Shun-ichi GOTO has written a very useful ProxyCommand +which allows the use of outbound SSH from behind a SOCKS4, SOCKS5 or +https CONNECT style proxy server. His page for connect.c has extensive +documentation on its use as well as compiled versions for Win32. + +https://bitbucket.org/gotoh/connect/wiki/Home + + +X11 SSH Askpass: + +Jim Knoble has written an excellent X11 +passphrase requester. This is highly recommended: + +http://www.jmknoble.net/software/x11-ssh-askpass/ + + +In this directory +----------------- + +ssh-copy-id: + +Phil Hands' shell script to automate the process of adding +your public key to a remote machine's ~/.ssh/authorized_keys file. + +gnome-ssh-askpass[12]: + +A GNOME and Gtk2 passphrase requesters. Use "make gnome-ssh-askpass1" or +"make gnome-ssh-askpass2" to build. + +sshd.pam.generic: + +A generic PAM config file which may be useful on your system. YMMV + +sshd.pam.freebsd: + +A PAM config file which works with FreeBSD's PAM port. Contributed by +Dominik Brettnacher + +findssl.sh: + +Search for all instances of OpenSSL headers and libraries and print their +versions. This is intended to help diagnose OpenSSH's "OpenSSL headers do not +match your library" errors. + +aix: + Files to build an AIX native (installp or SMIT installable) package. + +caldera: + RPM spec file and scripts for building Caldera OpenLinuix packages + +cygwin: + Support files for Cygwin + +hpux: + Support files for HP-UX + +redhat: + RPM spec file and scripts for building Redhat packages + +suse: + RPM spec file and scripts for building SuSE packages + diff --git a/aosp/external/openssh/contrib/aix/README b/aosp/external/openssh/contrib/aix/README new file mode 100644 index 0000000000000000000000000000000000000000..1aa5919786cabcc79aec3df8b936b11fa1f94ec8 --- /dev/null +++ b/aosp/external/openssh/contrib/aix/README @@ -0,0 +1,49 @@ +Overview: + +This directory contains files to build an AIX native (installp or SMIT +installable) openssh package. + + +Directions: + +(optional) create config.local in your build dir +./configure [options] +contrib/aix/buildbff.sh + +The file config.local or the environment is read to set the following options +(default first): +PERMIT_ROOT_LOGIN=[no|yes] +X11_FORWARDING=[no|yes] +AIX_SRC=[no|yes] + +Acknowledgements: + +The contents of this directory are based on Ben Lindstrom's Solaris +buildpkg.sh. Ben also supplied inventory.sh. + +Jim Abbey's (GPL'ed) lppbuild-2.1 was used to learn how to build .bff's +and for comparison with the output from this script, however no code +from lppbuild is included and it is not required for operation. + +SRC support based on examples provided by Sandor Sklar and Maarten Kreuger. +PrivSep account handling fixes contributed by W. Earl Allen. + + +Other notes: + +The script treats all packages as USR packages (not ROOT+USR when +appropriate). It seems to work, though...... + +If there are any patches to this that have not yet been integrated they +may be found at http://www.dtucker.net/openssh/. + + +Disclaimer: + +It is hoped that it is useful but there is no warranty. If it breaks +you get to keep both pieces. + + + - Darren Tucker (dtucker at zip dot com dot au) + 2002/03/01 + diff --git a/aosp/external/openssh/contrib/aix/buildbff.sh b/aosp/external/openssh/contrib/aix/buildbff.sh new file mode 100644 index 0000000000000000000000000000000000000000..55113d9d38db379afba04ae795b7afc1edd874b7 --- /dev/null +++ b/aosp/external/openssh/contrib/aix/buildbff.sh @@ -0,0 +1,366 @@ +#!/bin/sh +# +# buildbff.sh: Create AIX SMIT-installable OpenSSH packages +# +# Author: Darren Tucker (dtucker at zip dot com dot au) +# This file is placed in the public domain and comes with absolutely +# no warranty. +# +# Based originally on Ben Lindstrom's buildpkg.sh for Solaris +# + +# +# Tunable configuration settings +# create a "config.local" in your build directory or set +# environment variables to override these. +# +[ -z "$PERMIT_ROOT_LOGIN" ] && PERMIT_ROOT_LOGIN=no +[ -z "$X11_FORWARDING" ] && X11_FORWARDING=no +[ -z "$AIX_SRC" ] && AIX_SRC=no + +umask 022 + +startdir=`pwd` + +perl -v >/dev/null || (echo perl required; exit 1) + +# Path to inventory.sh: same place as buildbff.sh +if echo $0 | egrep '^/' +then + inventory=`dirname $0`/inventory.sh # absolute path +else + inventory=`pwd`/`dirname $0`/inventory.sh # relative path +fi + +# +# We still support running from contrib/aix, but this is deprecated +# +if pwd | egrep 'contrib/aix$' +then + echo "Changing directory to `pwd`/../.." + echo "Please run buildbff.sh from your build directory in future." + cd ../.. + contribaix=1 +fi + +if [ ! -f Makefile ] +then + echo "Makefile not found (did you run configure?)" + exit 1 +fi + +# +# Directories used during build: +# current dir = $objdir directory you ran ./configure in. +# $objdir/$PKGDIR/ directory package files are constructed in +# $objdir/$PKGDIR/root/ package root ($FAKE_ROOT) +# +objdir=`pwd` +PKGNAME=openssh +PKGDIR=package + +# +# Collect local configuration settings to override defaults +# +if [ -s ./config.local ] +then + echo Reading local settings from config.local + . ./config.local +fi + +# +# Fill in some details from Makefile, like prefix and sysconfdir +# the eval also expands variables like sysconfdir=${prefix}/etc +# provided they are eval'ed in the correct order +# +for confvar in prefix exec_prefix bindir sbindir libexecdir datadir mandir mansubdir sysconfdir piddir srcdir +do + eval $confvar=`grep "^$confvar=" $objdir/Makefile | cut -d = -f 2` +done + +# +# Collect values of privsep user and privsep path +# currently only found in config.h +# +for confvar in SSH_PRIVSEP_USER PRIVSEP_PATH +do + eval $confvar=`awk '/#define[ \t]'$confvar'/{print $3}' $objdir/config.h` +done + +# Set privsep defaults if not defined +if [ -z "$SSH_PRIVSEP_USER" ] +then + SSH_PRIVSEP_USER=sshd +fi +if [ -z "$PRIVSEP_PATH" ] +then + PRIVSEP_PATH=/var/empty +fi + +# Clean package build directory +rm -rf $objdir/$PKGDIR +FAKE_ROOT=$objdir/$PKGDIR/root +mkdir -p $FAKE_ROOT + +# Start by faking root install +echo "Faking root install..." +cd $objdir +make install-nokeys DESTDIR=$FAKE_ROOT + +if [ $? -gt 0 ] +then + echo "Fake root install failed, stopping." + exit 1 +fi + +# +# Copy informational files to include in package +# +cp $srcdir/LICENCE $objdir/$PKGDIR/ +cp $srcdir/README* $objdir/$PKGDIR/ + +# +# Extract common info requires for the 'info' part of the package. +# AIX requires 4-part version numbers +# +VERSION=`./ssh -V 2>&1 | cut -f 1 -d , | cut -f 2 -d _` +MAJOR=`echo $VERSION | cut -f 1 -d p | cut -f 1 -d .` +MINOR=`echo $VERSION | cut -f 1 -d p | cut -f 2 -d .` +PATCH=`echo $VERSION | cut -f 1 -d p | cut -f 3 -d .` +PORTABLE=`echo $VERSION | awk 'BEGIN{FS="p"}{print $2}'` +[ "$PATCH" = "" ] && PATCH=0 +[ "$PORTABLE" = "" ] && PORTABLE=0 +BFFVERSION=`printf "%d.%d.%d.%d" $MAJOR $MINOR $PATCH $PORTABLE` + +echo "Building BFF for $PKGNAME $VERSION (package version $BFFVERSION)" + +# +# Set ssh and sshd parameters as per config.local +# +if [ "${PERMIT_ROOT_LOGIN}" = no ] +then + perl -p -i -e "s/#PermitRootLogin yes/PermitRootLogin no/" \ + $FAKE_ROOT/${sysconfdir}/sshd_config +fi +if [ "${X11_FORWARDING}" = yes ] +then + perl -p -i -e "s/#X11Forwarding no/X11Forwarding yes/" \ + $FAKE_ROOT/${sysconfdir}/sshd_config +fi + + +# Rename config files; postinstall script will copy them if necessary +for cfgfile in ssh_config sshd_config +do + mv $FAKE_ROOT/$sysconfdir/$cfgfile $FAKE_ROOT/$sysconfdir/$cfgfile.default +done + +# +# Generate lpp control files. +# working dir is $FAKE_ROOT but files are generated in dir above +# and moved into place just before creation of .bff +# +cd $FAKE_ROOT +echo Generating LPP control files +find . ! -name . -print >../openssh.al +$inventory >../openssh.inventory + +cat <../openssh.copyright +This software is distributed under a BSD-style license. +For the full text of the license, see /usr/lpp/openssh/LICENCE +EOD + +# +# openssh.size file allows filesystem expansion as required +# generate list of directories containing files +# then calculate disk usage for each directory and store in openssh.size +# +files=`find . -type f -print` +dirs=`for file in $files; do dirname $file; done | sort -u` +for dir in $dirs +do + du $dir +done > ../openssh.size + +# +# Create postinstall script +# +cat <>../openssh.post_i +#!/bin/sh + +echo Creating configs from defaults if necessary. +for cfgfile in ssh_config sshd_config +do + if [ ! -f $sysconfdir/\$cfgfile ] + then + echo "Creating \$cfgfile from default" + cp $sysconfdir/\$cfgfile.default $sysconfdir/\$cfgfile + else + echo "\$cfgfile already exists." + fi +done +echo + +# Create PrivilegeSeparation user and group if not present +echo Checking for PrivilegeSeparation user and group. +if cut -f1 -d: /etc/group | egrep '^'$SSH_PRIVSEP_USER'\$' >/dev/null +then + echo "PrivSep group $SSH_PRIVSEP_USER already exists." +else + echo "Creating PrivSep group $SSH_PRIVSEP_USER." + mkgroup -A $SSH_PRIVSEP_USER +fi + +# Create user if required +if lsuser "$SSH_PRIVSEP_USER" >/dev/null +then + echo "PrivSep user $SSH_PRIVSEP_USER already exists." +else + echo "Creating PrivSep user $SSH_PRIVSEP_USER." + mkuser gecos='SSHD PrivSep User' login=false rlogin=false account_locked=true pgrp=$SSH_PRIVSEP_USER $SSH_PRIVSEP_USER +fi + +if egrep '^[ \t]*UsePrivilegeSeparation[ \t]+no' $sysconfdir/sshd_config >/dev/null +then + echo UsePrivilegeSeparation not enabled, privsep directory not required. +else + # create chroot directory if required + if [ -d $PRIVSEP_PATH ] + then + echo "PrivSep chroot directory $PRIVSEP_PATH already exists." + else + echo "Creating PrivSep chroot directory $PRIVSEP_PATH." + mkdir $PRIVSEP_PATH + chown 0 $PRIVSEP_PATH + chgrp 0 $PRIVSEP_PATH + chmod 755 $PRIVSEP_PATH + fi +fi +echo + +# Generate keys unless they already exist +echo Creating host keys if required. +$bindir/ssh-keygen -A +echo + +# Set startup command depending on SRC support +if [ "$AIX_SRC" = "yes" ] +then + echo Creating SRC sshd subsystem. + rmssys -s sshd 2>&1 >/dev/null + mkssys -s sshd -p "$sbindir/sshd" -a '-D' -u 0 -S -n 15 -f 9 -R -G tcpip + startupcmd="start $sbindir/sshd \\\"\\\$src_running\\\"" + oldstartcmd="$sbindir/sshd" +else + startupcmd="$sbindir/sshd" + oldstartcmd="start $sbindir/sshd \\\"$src_running\\\"" +fi + +# If migrating to or from SRC, change previous startup command +# otherwise add to rc.tcpip +if egrep "^\$oldstartcmd" /etc/rc.tcpip >/dev/null +then + if sed "s|^\$oldstartcmd|\$startupcmd|g" /etc/rc.tcpip >/etc/rc.tcpip.new + then + chmod 0755 /etc/rc.tcpip.new + mv /etc/rc.tcpip /etc/rc.tcpip.old && \ + mv /etc/rc.tcpip.new /etc/rc.tcpip + else + echo "Updating /etc/rc.tcpip failed, please check." + fi +else + # Add to system startup if required + if grep "^\$startupcmd" /etc/rc.tcpip >/dev/null + then + echo "sshd found in rc.tcpip, not adding." + else + echo "Adding sshd to rc.tcpip" + echo >>/etc/rc.tcpip + echo "# Start sshd" >>/etc/rc.tcpip + echo "\$startupcmd" >>/etc/rc.tcpip + fi +fi +EOF + +# +# Create liblpp.a and move control files into it +# +echo Creating liblpp.a +( + cd .. + for i in openssh.al openssh.copyright openssh.inventory openssh.post_i openssh.size LICENCE README* + do + ar -r liblpp.a $i + rm $i + done +) + +# +# Create lpp_name +# +# This will end up looking something like: +# 4 R I OpenSSH { +# OpenSSH 3.0.2.1 1 N U en_US OpenSSH 3.0.2p1 Portable for AIX +# [ +# % +# /usr/local/bin 8073 +# /usr/local/etc 189 +# /usr/local/libexec 185 +# /usr/local/man/man1 145 +# /usr/local/man/man8 83 +# /usr/local/sbin 2105 +# /usr/local/share 3 +# % +# ] +# } + +echo Creating lpp_name +cat <../lpp_name +4 R I $PKGNAME { +$PKGNAME $BFFVERSION 1 N U en_US OpenSSH $VERSION Portable for AIX +[ +% +EOF + +for i in $bindir $sysconfdir $libexecdir $mandir/${mansubdir}1 $mandir/${mansubdir}8 $sbindir $datadir /usr/lpp/openssh +do + # get size in 512 byte blocks + if [ -d $FAKE_ROOT/$i ] + then + size=`du $FAKE_ROOT/$i | awk '{print $1}'` + echo "$i $size" >>../lpp_name + fi +done + +echo '%' >>../lpp_name +echo ']' >>../lpp_name +echo '}' >>../lpp_name + +# +# Move pieces into place +# +mkdir -p usr/lpp/openssh +mv ../liblpp.a usr/lpp/openssh +mv ../lpp_name . + +# +# Now invoke backup to create .bff file +# note: lpp_name needs to be the first file so we generate the +# file list on the fly and feed it to backup using -i +# +echo Creating $PKGNAME-$VERSION.bff with backup... +rm -f $PKGNAME-$VERSION.bff +( + echo "./lpp_name" + find . ! -name lpp_name -a ! -name . -print +) | backup -i -q -f ../$PKGNAME-$VERSION.bff $filelist + +# +# Move package into final location and clean up +# +mv ../$PKGNAME-$VERSION.bff $startdir +cd $startdir +rm -rf $objdir/$PKGDIR + +echo $0: done. + diff --git a/aosp/external/openssh/contrib/aix/inventory.sh b/aosp/external/openssh/contrib/aix/inventory.sh new file mode 100644 index 0000000000000000000000000000000000000000..7d76f49715c4230da5dddd6169610a6968df174c --- /dev/null +++ b/aosp/external/openssh/contrib/aix/inventory.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# +# inventory.sh +# +# Originally written by Ben Lindstrom, modified by Darren Tucker to use perl +# This file is placed into the public domain. +# +# This will produce an AIX package inventory file, which looks like: +# +# /usr/local/bin: +# class=apply,inventory,openssh +# owner=root +# group=system +# mode=755 +# type=DIRECTORY +# /usr/local/bin/slogin: +# class=apply,inventory,openssh +# owner=root +# group=system +# mode=777 +# type=SYMLINK +# target=ssh +# /usr/local/share/Ssh.bin: +# class=apply,inventory,openssh +# owner=root +# group=system +# mode=644 +# type=FILE +# size=VOLATILE +# checksum=VOLATILE + +find . ! -name . -print | perl -ne '{ + chomp; + if ( -l $_ ) { + ($dev,$ino,$mod,$nl,$uid,$gid,$rdev,$sz,$at,$mt,$ct,$bsz,$blk)=lstat; + } else { + ($dev,$ino,$mod,$nl,$uid,$gid,$rdev,$sz,$at,$mt,$ct,$bsz,$blk)=stat; + } + + # Start to display inventory information + $name = $_; + $name =~ s|^.||; # Strip leading dot from path + print "$name:\n"; + print "\tclass=apply,inventory,openssh\n"; + print "\towner=root\n"; + print "\tgroup=system\n"; + printf "\tmode=%lo\n", $mod & 07777; # Mask perm bits + + if ( -l $_ ) { + # Entry is SymLink + print "\ttype=SYMLINK\n"; + printf "\ttarget=%s\n", readlink($_); + } elsif ( -f $_ ) { + # Entry is File + print "\ttype=FILE\n"; + print "\tsize=$sz\n"; + print "\tchecksum=VOLATILE\n"; + } elsif ( -d $_ ) { + # Entry is Directory + print "\ttype=DIRECTORY\n"; + } +}' diff --git a/aosp/external/openssh/contrib/aix/pam.conf b/aosp/external/openssh/contrib/aix/pam.conf new file mode 100644 index 0000000000000000000000000000000000000000..f1528b005d4254140917fda17a2c545d1da49836 --- /dev/null +++ b/aosp/external/openssh/contrib/aix/pam.conf @@ -0,0 +1,20 @@ +# +# PAM configuration file /etc/pam.conf +# Example for OpenSSH on AIX 5.2 +# + +# Authentication Management +sshd auth required /usr/lib/security/pam_aix +OTHER auth required /usr/lib/security/pam_aix + +# Account Management +sshd account required /usr/lib/security/pam_aix +OTHER account required /usr/lib/security/pam_aix + +# Password Management +sshd password required /usr/lib/security/pam_aix +OTHER password required /usr/lib/security/pam_aix + +# Session Management +sshd session required /usr/lib/security/pam_aix +OTHER session required /usr/lib/security/pam_aix diff --git a/aosp/external/openssh/contrib/cygwin/Makefile b/aosp/external/openssh/contrib/cygwin/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4b78cd950576f711d5447985b572b6a2522dda22 --- /dev/null +++ b/aosp/external/openssh/contrib/cygwin/Makefile @@ -0,0 +1,78 @@ +srcdir=../.. +copyidsrcdir=.. +prefix=/usr +exec_prefix=$(prefix) +bindir=$(prefix)/bin +datadir=$(prefix)/share +mandir=$(datadir)/man +docdir=$(datadir)/doc +sshdocdir=$(docdir)/openssh +cygdocdir=$(docdir)/Cygwin +sysconfdir=/etc +defaultsdir=$(sysconfdir)/defaults/etc +inetdefdir=$(defaultsdir)/inetd.d +PRIVSEP_PATH=/var/empty +INSTALL=/usr/bin/install -c +MKDIR_P=$(srcdir)/mkinstalldirs + +DESTDIR= + +all: + @echo + @echo "Use \`make cygwin-postinstall DESTDIR=[package directory]'" + @echo "Be sure having DESTDIR set correctly!" + @echo + +move-config-files: $(DESTDIR)$(sysconfdir)/ssh_config $(DESTDIR)$(sysconfdir)/sshd_config + $(MKDIR_P) $(DESTDIR)$(defaultsdir) + mv $(DESTDIR)$(sysconfdir)/ssh_config $(DESTDIR)$(defaultsdir) + mv $(DESTDIR)$(sysconfdir)/sshd_config $(DESTDIR)$(defaultsdir) + +remove-empty-dir: + rm -rf $(DESTDIR)$(PRIVSEP_PATH) + +install-inetd-config: + $(MKDIR_P) $(DESTDIR)$(inetdefdir) + $(INSTALL) -m 644 sshd-inetd $(DESTDIR)$(inetdefdir)/sshd-inetd + +install-sshdoc: + $(MKDIR_P) $(DESTDIR)$(sshdocdir) + -$(INSTALL) -m 644 $(srcdir)/CREDITS $(DESTDIR)$(sshdocdir)/CREDITS + -$(INSTALL) -m 644 $(srcdir)/ChangeLog $(DESTDIR)$(sshdocdir)/ChangeLog + -$(INSTALL) -m 644 $(srcdir)/LICENCE $(DESTDIR)$(sshdocdir)/LICENCE + -$(INSTALL) -m 644 $(srcdir)/OVERVIEW $(DESTDIR)$(sshdocdir)/OVERVIEW + -$(INSTALL) -m 644 $(srcdir)/PROTOCOL $(DESTDIR)$(sshdocdir)/PROTOCOL + -$(INSTALL) -m 644 $(srcdir)/PROTOCOL.agent $(DESTDIR)$(sshdocdir)/PROTOCOL.agent + -$(INSTALL) -m 644 $(srcdir)/PROTOCOL.certkeys $(DESTDIR)$(sshdocdir)/PROTOCOL.certkeys + -$(INSTALL) -m 644 $(srcdir)/PROTOCOL.mux $(DESTDIR)$(sshdocdir)/PROTOCOL.mux + -$(INSTALL) -m 644 $(srcdir)/README $(DESTDIR)$(sshdocdir)/README + -$(INSTALL) -m 644 $(srcdir)/README.dns $(DESTDIR)$(sshdocdir)/README.dns + -$(INSTALL) -m 644 $(srcdir)/README.platform $(DESTDIR)$(sshdocdir)/README.platform + -$(INSTALL) -m 644 $(srcdir)/README.privsep $(DESTDIR)$(sshdocdir)/README.privsep + -$(INSTALL) -m 644 $(srcdir)/README.tun $(DESTDIR)$(sshdocdir)/README.tun + -$(INSTALL) -m 644 $(srcdir)/TODO $(DESTDIR)$(sshdocdir)/TODO + +install-cygwindoc: README + $(MKDIR_P) $(DESTDIR)$(cygdocdir) + $(INSTALL) -m 644 README $(DESTDIR)$(cygdocdir)/openssh.README + +install-doc: install-sshdoc install-cygwindoc + +install-scripts: ssh-host-config ssh-user-config + $(MKDIR_P) $(DESTDIR)$(bindir) + $(INSTALL) -m 755 ssh-host-config $(DESTDIR)$(bindir)/ssh-host-config + $(INSTALL) -m 755 ssh-user-config $(DESTDIR)$(bindir)/ssh-user-config + +install-copy-id: $(copyidsrcdir)/ssh-copy-id $(copyidsrcdir)/ssh-copy-id.1 + $(INSTALL) -m 755 $(copyidsrcdir)/ssh-copy-id $(DESTDIR)$(bindir)/ssh-copy-id + $(INSTALL) -m 644 $(copyidsrcdir)/ssh-copy-id.1 $(DESTDIR)$(mandir)/man1/ssh-copy-id.1 + +gzip-man-pages: + rm $(DESTDIR)$(mandir)/man1/slogin.1 + gzip $(DESTDIR)$(mandir)/man1/*.1 + gzip $(DESTDIR)$(mandir)/man5/*.5 + gzip $(DESTDIR)$(mandir)/man8/*.8 + cd $(DESTDIR)$(mandir)/man1 && ln -s ssh.1.gz slogin.1.gz + +cygwin-postinstall: move-config-files remove-empty-dir install-inetd-config install-doc install-scripts install-copy-id gzip-man-pages + @echo "Cygwin specific configuration finished." diff --git a/aosp/external/openssh/contrib/cygwin/README b/aosp/external/openssh/contrib/cygwin/README new file mode 100644 index 0000000000000000000000000000000000000000..3745051f598159aef54dda26915be2294c861b60 --- /dev/null +++ b/aosp/external/openssh/contrib/cygwin/README @@ -0,0 +1,91 @@ +This package describes important Cygwin specific stuff concerning OpenSSH. + +The binary package is usually built for recent Cygwin versions and might +not run on older versions. Please check http://cygwin.com/ for information +about current Cygwin releases. + +================== +Host configuration +================== + +If you are installing OpenSSH the first time, you can generate global config +files and server keys, as well as installing sshd as a service, by running + + /usr/bin/ssh-host-config + +Note that this binary archive doesn't contain default config files in /etc. +That files are only created if ssh-host-config is started. + +To support testing and unattended installation ssh-host-config got +some options: + +usage: ssh-host-config [OPTION]... +Options: + --debug -d Enable shell's debug output. + --yes -y Answer all questions with "yes" automatically. + --no -n Answer all questions with "no" automatically. + --cygwin -c Use "options" as value for CYGWIN environment var. + --name -N sshd windows service name. + --port -p sshd listens on port n. + --user -u privileged user for service, default 'cyg_server'. + --pwd -w Use "pwd" as password for privileged user. + --privileged On Windows XP, require privileged user + instead of LocalSystem for sshd service. + +Installing sshd as daemon via ssh-host-config is recommended. + +Alternatively you can start sshd via inetd, if you have the inetutils +package installed. Just run ssh-host-config, but answer "no" when asked +to install sshd as service. The ssh-host-config script also adds the +required lines to /etc/inetd.conf and /etc/services. + +================== +User configuration +================== + +Any user can simplify creating the own private and public keys by running + + /usr/bin/ssh-user-config + +To support testing and unattended installation ssh-user-config got +some options as well: + +usage: ssh-user-config [OPTION]... +Options: + --debug -d Enable shell's debug output. + --yes -y Answer all questions with "yes" automatically. + --no -n Answer all questions with "no" automatically. + --passphrase -p word Use "word" as passphrase automatically. + +Please note that OpenSSH does never use the value of $HOME to +search for the users configuration files! It always uses the +value of the pw_dir field in /etc/passwd as the home directory. +If no home directory is set in /etc/passwd, the root directory +is used instead! + +================ +Building OpenSSH +================ + +Building from source is easy. Just unpack the source archive, cd to that +directory, and call cygport: + + cygport openssh.cygport all + +You must have installed the following packages to be able to build OpenSSH +with the aforementioned cygport script: + + zlib + crypt + libssl-devel + libedit-devel + libkrb5-devel + +Please send requests, error reports etc. to cygwin@cygwin.com. + + +Have fun, + +Corinna Vinschen +Cygwin Developer +Red Hat Inc. diff --git a/aosp/external/openssh/contrib/cygwin/ssh-host-config b/aosp/external/openssh/contrib/cygwin/ssh-host-config new file mode 100644 index 0000000000000000000000000000000000000000..a8572e2ac879f3731999d3d8d3cb5e58883298aa --- /dev/null +++ b/aosp/external/openssh/contrib/cygwin/ssh-host-config @@ -0,0 +1,714 @@ +#!/bin/bash +# +# ssh-host-config, Copyright 2000-2014 Red Hat Inc. +# +# This file is part of the Cygwin port of OpenSSH. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +# THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# ====================================================================== +# Initialization +# ====================================================================== + +CSIH_SCRIPT=/usr/share/csih/cygwin-service-installation-helper.sh + +# List of apps used. This is checkad for existence in csih_sanity_check +# Don't use *any* transient commands before sourcing the csih helper script, +# otherwise the sanity checks are short-circuited. +declare -a csih_required_commands=( + /usr/bin/basename coreutils + /usr/bin/cat coreutils + /usr/bin/chmod coreutils + /usr/bin/dirname coreutils + /usr/bin/id coreutils + /usr/bin/mv coreutils + /usr/bin/rm coreutils + /usr/bin/cygpath cygwin + /usr/bin/mkpasswd cygwin + /usr/bin/mount cygwin + /usr/bin/ps cygwin + /usr/bin/umount cygwin + /usr/bin/cmp diffutils + /usr/bin/grep grep + /usr/bin/awk gawk + /usr/bin/ssh-keygen openssh + /usr/sbin/sshd openssh + /usr/bin/sed sed +) +csih_sanity_check_server=yes +source ${CSIH_SCRIPT} + +PROGNAME=$(/usr/bin/basename $0) +_tdir=$(/usr/bin/dirname $0) +PROGDIR=$(cd $_tdir && pwd) + +# Subdirectory where the new package is being installed +PREFIX=/usr + +# Directory where the config files are stored +SYSCONFDIR=/etc +LOCALSTATEDIR=/var + +sshd_config_configured=no +port_number=22 +service_name=cygsshd +strictmodes=yes +cygwin_value="" +user_account= +password_value= +opt_force=no + +# ====================================================================== +# Routine: update_services_file +# ====================================================================== +update_services_file() { + local _my_etcdir="/ssh-host-config.$$" + local _win_etcdir + local _services + local _spaces + local _serv_tmp + local _wservices + local ret=0 + + _win_etcdir="${SYSTEMROOT}\\system32\\drivers\\etc" + _services="${_my_etcdir}/services" + _spaces=" #" + _serv_tmp="${_my_etcdir}/srv.out.$$" + + /usr/bin/mount -o text,posix=0,noacl -f "${_win_etcdir}" "${_my_etcdir}" + + # Depends on the above mount + _wservices=`cygpath -w "${_services}"` + + # Add ssh 22/tcp and ssh 22/udp to services + if [ `/usr/bin/grep -q 'ssh[[:space:]][[:space:]]*22' "${_services}"; echo $?` -ne 0 ] + then + if /usr/bin/awk '{ if ( $2 ~ /^23\/tcp/ ) print "ssh 22/tcp'"${_spaces}"'SSH Remote Login Protocol\nssh 22/udp'"${_spaces}"'SSH Remote Login Protocol"; print $0; }' < "${_services}" > "${_serv_tmp}" + then + if /usr/bin/mv "${_serv_tmp}" "${_services}" + then + csih_inform "Added ssh to ${_wservices}" + else + csih_warning "Adding ssh to ${_wservices} failed!" + let ++ret + fi + /usr/bin/rm -f "${_serv_tmp}" + else + csih_warning "Adding ssh to ${_wservices} failed!" + let ++ret + fi + fi + /usr/bin/umount "${_my_etcdir}" + return $ret +} # --- End of update_services_file --- # + +# ====================================================================== +# Routine: sshd_strictmodes +# MODIFIES: strictmodes +# ====================================================================== +sshd_strictmodes() { + if [ "${sshd_config_configured}" != "yes" ] + then + echo + csih_inform "StrictModes is set to 'yes' by default." + csih_inform "This is the recommended setting, but it requires that the POSIX" + csih_inform "permissions of the user's home directory, the user's .ssh" + csih_inform "directory, and the user's ssh key files are tight so that" + csih_inform "only the user has write permissions." + csih_inform "On the other hand, StrictModes don't work well with default" + csih_inform "Windows permissions of a home directory mounted with the" + csih_inform "'noacl' option, and they don't work at all if the home" + csih_inform "directory is on a FAT or FAT32 partition." + if ! csih_request "Should StrictModes be used?" + then + strictmodes=no + fi + fi + return 0 +} + +# ====================================================================== +# Routine: sshd_privsep +# Try to create ssshd user account +# ====================================================================== +sshd_privsep() { + local ret=0 + + if [ "${sshd_config_configured}" != "yes" ] + then + if ! csih_create_unprivileged_user sshd + then + csih_error_recoverable "Could not create user 'sshd'!" + csih_error_recoverable "You will not be able to run an sshd service" + csih_error_recoverable "under a privileged account successfully." + csih_error_recoverable "Make sure to create a non-privileged user 'sshd'" + csih_error_recoverable "manually before trying to run the service!" + let ++ret + fi + fi + return $ret +} # --- End of sshd_privsep --- # + +# ====================================================================== +# Routine: sshd_config_tweak +# ====================================================================== +sshd_config_tweak() { + local ret=0 + + # Modify sshd_config + csih_inform "Updating ${SYSCONFDIR}/sshd_config file" + if [ "${port_number}" -ne 22 ] + then + /usr/bin/sed -i -e "s/^#\?[[:space:]]*Port[[:space:]].*/Port ${port_number}/" \ + ${SYSCONFDIR}/sshd_config + if [ $? -ne 0 ] + then + csih_warning "Setting listening port to ${port_number} failed!" + csih_warning "Check your ${SYSCONFDIR}/sshd_config file!" + let ++ret + fi + fi + if [ "${strictmodes}" = "no" ] + then + /usr/bin/sed -i -e "s/^#\?[[:space:]]*StrictModes[[:space:]].*/StrictModes no/" \ + ${SYSCONFDIR}/sshd_config + if [ $? -ne 0 ] + then + csih_warning "Setting StrictModes to 'no' failed!" + csih_warning "Check your ${SYSCONFDIR}/sshd_config file!" + let ++ret + fi + fi + return $ret +} # --- End of sshd_config_tweak --- # + +# ====================================================================== +# Routine: update_inetd_conf +# ====================================================================== +update_inetd_conf() { + local _inetcnf="${SYSCONFDIR}/inetd.conf" + local _inetcnf_tmp="${SYSCONFDIR}/inetd.conf.$$" + local _inetcnf_dir="${SYSCONFDIR}/inetd.d" + local _sshd_inetd_conf="${_inetcnf_dir}/sshd-inetd" + local _sshd_inetd_conf_tmp="${_inetcnf_dir}/sshd-inetd.$$" + local _with_comment=1 + local ret=0 + + if [ -d "${_inetcnf_dir}" ] + then + # we have inetutils-1.5 inetd.d support + if [ -f "${_inetcnf}" ] + then + /usr/bin/grep -q '^[[:space:]]*ssh' "${_inetcnf}" && _with_comment=0 + + # check for sshd OR ssh in top-level inetd.conf file, and remove + # will be replaced by a file in inetd.d/ + if [ $(/usr/bin/grep -q '^[# \t]*ssh' "${_inetcnf}"; echo $?) -eq 0 ] + then + /usr/bin/grep -v '^[# \t]*ssh' "${_inetcnf}" >> "${_inetcnf_tmp}" + if [ -f "${_inetcnf_tmp}" ] + then + if /usr/bin/mv "${_inetcnf_tmp}" "${_inetcnf}" + then + csih_inform "Removed ssh[d] from ${_inetcnf}" + else + csih_warning "Removing ssh[d] from ${_inetcnf} failed!" + let ++ret + fi + /usr/bin/rm -f "${_inetcnf_tmp}" + else + csih_warning "Removing ssh[d] from ${_inetcnf} failed!" + let ++ret + fi + fi + fi + + csih_install_config "${_sshd_inetd_conf}" "${SYSCONFDIR}/defaults" + if /usr/bin/cmp "${SYSCONFDIR}/defaults${_sshd_inetd_conf}" "${_sshd_inetd_conf}" >/dev/null 2>&1 + then + if [ "${_with_comment}" -eq 0 ] + then + /usr/bin/sed -e 's/@COMMENT@[[:space:]]*//' < "${_sshd_inetd_conf}" > "${_sshd_inetd_conf_tmp}" + else + /usr/bin/sed -e 's/@COMMENT@[[:space:]]*/# /' < "${_sshd_inetd_conf}" > "${_sshd_inetd_conf_tmp}" + fi + if /usr/bin/mv "${_sshd_inetd_conf_tmp}" "${_sshd_inetd_conf}" + then + csih_inform "Updated ${_sshd_inetd_conf}" + else + csih_warning "Updating ${_sshd_inetd_conf} failed!" + let ++ret + fi + fi + + elif [ -f "${_inetcnf}" ] + then + /usr/bin/grep -q '^[[:space:]]*sshd' "${_inetcnf}" && _with_comment=0 + + # check for sshd in top-level inetd.conf file, and remove + # will be replaced by a file in inetd.d/ + if [ `/usr/bin/grep -q '^#\?[[:space:]]*sshd' "${_inetcnf}"; echo $?` -eq 0 ] + then + /usr/bin/grep -v '^#\?[[:space:]]*sshd' "${_inetcnf}" >> "${_inetcnf_tmp}" + if [ -f "${_inetcnf_tmp}" ] + then + if /usr/bin/mv "${_inetcnf_tmp}" "${_inetcnf}" + then + csih_inform "Removed sshd from ${_inetcnf}" + else + csih_warning "Removing sshd from ${_inetcnf} failed!" + let ++ret + fi + /usr/bin/rm -f "${_inetcnf_tmp}" + else + csih_warning "Removing sshd from ${_inetcnf} failed!" + let ++ret + fi + fi + + # Add ssh line to inetd.conf + if [ `/usr/bin/grep -q '^[# \t]*ssh' "${_inetcnf}"; echo $?` -ne 0 ] + then + if [ "${_with_comment}" -eq 0 ] + then + echo 'ssh stream tcp nowait root /usr/sbin/sshd sshd -i' >> "${_inetcnf}" + else + echo '# ssh stream tcp nowait root /usr/sbin/sshd sshd -i' >> "${_inetcnf}" + fi + if [ $? -eq 0 ] + then + csih_inform "Added ssh to ${_inetcnf}" + else + csih_warning "Adding ssh to ${_inetcnf} failed!" + let ++ret + fi + fi + fi + return $ret +} # --- End of update_inetd_conf --- # + +# ====================================================================== +# Routine: check_service_files_ownership +# Checks that the files in /etc and /var belong to the right owner +# ====================================================================== +check_service_files_ownership() { + local run_service_as=$1 + local ret=0 + + if [ -z "${run_service_as}" ] + then + accnt_name=$(/usr/bin/cygrunsrv -VQ "${service_name}" | + /usr/bin/sed -ne 's/^Account *: *//gp') + if [ "${accnt_name}" = "LocalSystem" ] + then + # Convert "LocalSystem" to "SYSTEM" as is the correct account name + run_service_as="SYSTEM" + else + dom="${accnt_name%%\\*}" + accnt_name="${accnt_name#*\\}" + if [ "${dom}" = '.' ] + then + # Check local account + run_service_as=$(/usr/bin/mkpasswd -l -u "${accnt_name}" | + /usr/bin/awk -F: '{print $1;}') + else + # Check domain + run_service_as=$(/usr/bin/mkpasswd -d "${dom}" -u "${accnt_name}" | + /usr/bin/awk -F: '{print $1;}') + fi + fi + if [ -z "${run_service_as}" ] + then + csih_warning "Couldn't determine name of user running ${service_name} service from account database!" + csih_warning "As a result, this script cannot make sure that the files used" + csih_warning "by the ${service_name} service belong to the user running the service." + return 1 + fi + fi + for i in "${SYSCONFDIR}"/ssh_config "${SYSCONFDIR}"/sshd_config "${SYSCONFDIR}"/ssh_host_*key "${SYSCONFDIR}"/ssh_host_*key.pub + do + if [ -f "$i" ] + then + if ! chown "${run_service_as}".544 "$i" >/dev/null 2>&1 + then + csih_warning "Couldn't change owner of $i!" + let ++ret + fi + fi + done + if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/empty >/dev/null 2>&1 + then + csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/empty!" + let ++ret + fi + if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/log/lastlog >/dev/null 2>&1 + then + csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/log/lastlog!" + let ++ret + fi + if [ -f ${LOCALSTATEDIR}/log/sshd.log ] + then + if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/log/sshd.log >/dev/null 2>&1 + then + csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/log/sshd.log!" + let ++ret + fi + fi + if [ $ret -ne 0 ] + then + csih_warning "Couldn't change owner of important files to ${run_service_as}!" + csih_warning "This may cause the ${service_name} service to fail! Please make sure that" + csih_warning "you have sufficient permissions to change the ownership of files" + csih_warning "and try to run the ssh-host-config script again." + fi + return $ret +} # --- End of check_service_files_ownership --- # + +# ====================================================================== +# Routine: install_service +# Install sshd as a service +# ====================================================================== +install_service() { + local run_service_as + local password + local ret=0 + + echo + if /usr/bin/cygrunsrv -Q ${service_name} >/dev/null 2>&1 + then + csih_inform "Sshd service is already installed." + check_service_files_ownership "" || let ret+=$? + else + echo -e "${_csih_QUERY_STR} Do you want to install sshd as a service?" + if csih_request "(Say \"no\" if it is already installed as a service)" + then + csih_get_cygenv "${cygwin_value}" + + if ( [ "$csih_FORCE_PRIVILEGED_USER" != "yes" ] ) + then + # Enforce using privileged user on 64 bit Vista or W7 under WOW64 + is_wow64=$(/usr/bin/uname | /usr/bin/grep -q 'WOW' && echo 1 || echo 0) + + if ( csih_is_nt2003 && ! csih_is_windows8 && [ "${is_wow64}" = "1" ] ) + then + csih_inform "Running 32 bit Cygwin on 64 bit Windows Vista or Windows 7" + csih_inform "the SYSTEM account is not sufficient to setuid to a local" + csih_inform "user account. You need to have or to create a privileged" + csih_inform "account. This script will help you do so." + echo + csih_FORCE_PRIVILEGED_USER=yes + fi + fi + + if ( [ "$csih_FORCE_PRIVILEGED_USER" = "yes" ] ) + then + [ "${opt_force}" = "yes" ] && opt_f=-f + [ -n "${user_account}" ] && opt_u="-u ""${user_account}""" + csih_select_privileged_username ${opt_f} ${opt_u} sshd + + if ! csih_create_privileged_user "${password_value}" + then + csih_error_recoverable "There was a serious problem creating a privileged user." + csih_request "Do you want to proceed anyway?" || exit 1 + let ++ret + fi + # Never returns empty if NT or above + run_service_as=$(csih_service_should_run_as) + else + run_service_as="SYSTEM" + fi + + if [ "${run_service_as}" = "${csih_PRIVILEGED_USERNAME}" ] + then + password="${csih_PRIVILEGED_PASSWORD}" + if [ -z "${password}" ] + then + csih_get_value "Please enter the password for user '${run_service_as}':" "-s" + password="${csih_value}" + fi + fi + + # At this point, we either have $run_service_as = "system" and + # $password is empty, or $run_service_as is some privileged user and + # (hopefully) $password contains the correct password. So, from here + # out, we use '-z "${password}"' to discriminate the two cases. + + csih_check_user "${run_service_as}" + + if [ -n "${csih_cygenv}" ] + then + cygwin_env=( -e "CYGWIN=${csih_cygenv}" ) + fi + if [ -z "${password}" ] + then + if /usr/bin/cygrunsrv -I ${service_name} -d "CYGWIN ${service_name}" -p /usr/sbin/sshd \ + -a "-D" -y tcpip "${cygwin_env[@]}" + then + echo + csih_inform "The sshd service has been installed under the LocalSystem" + csih_inform "account (also known as SYSTEM). To start the service now, call" + csih_inform "\`net start ${service_name}' or \`cygrunsrv -S ${service_name}'. Otherwise, it" + csih_inform "will start automatically after the next reboot." + fi + else + if /usr/bin/cygrunsrv -I ${service_name} -d "CYGWIN ${service_name}" -p /usr/sbin/sshd \ + -a "-D" -y tcpip "${cygwin_env[@]}" \ + -u "${run_service_as}" -w "${password}" + then + /usr/bin/editrights -u "${run_service_as}" -a SeServiceLogonRight + echo + csih_inform "The sshd service has been installed under the '${run_service_as}'" + csih_inform "account. To start the service now, call \`net start ${service_name}' or" + csih_inform "\`cygrunsrv -S ${service_name}'. Otherwise, it will start automatically" + csih_inform "after the next reboot." + fi + fi + + if /usr/bin/cygrunsrv -Q ${service_name} >/dev/null 2>&1 + then + check_service_files_ownership "${run_service_as}" || let ret+=$? + else + csih_error_recoverable "Installing sshd as a service failed!" + let ++ret + fi + fi # user allowed us to install as service + fi # service not yet installed + return $ret +} # --- End of install_service --- # + +# ====================================================================== +# Main Entry Point +# ====================================================================== + +# Check how the script has been started. If +# (1) it has been started by giving the full path and +# that path is /etc/postinstall, OR +# (2) Otherwise, if the environment variable +# SSH_HOST_CONFIG_AUTO_ANSWER_NO is set +# then set auto_answer to "no". This allows automatic +# creation of the config files in /etc w/o overwriting +# them if they already exist. In both cases, color +# escape sequences are suppressed, so as to prevent +# cluttering setup's logfiles. +if [ "$PROGDIR" = "/etc/postinstall" ] +then + csih_auto_answer="no" + csih_disable_color + opt_force=yes +fi +if [ -n "${SSH_HOST_CONFIG_AUTO_ANSWER_NO}" ] +then + csih_auto_answer="no" + csih_disable_color + opt_force=yes +fi + +# ====================================================================== +# Parse options +# ====================================================================== +while : +do + case $# in + 0) + break + ;; + esac + + option=$1 + shift + + case "${option}" in + -d | --debug ) + set -x + csih_trace_on + ;; + + -y | --yes ) + csih_auto_answer=yes + opt_force=yes + ;; + + -n | --no ) + csih_auto_answer=no + opt_force=yes + ;; + + -c | --cygwin ) + cygwin_value="$1" + shift + ;; + + -N | --name ) + service_name=$1 + shift + ;; + + -p | --port ) + port_number=$1 + shift + ;; + + -u | --user ) + user_account="$1" + shift + ;; + + -w | --pwd ) + password_value="$1" + shift + ;; + + --privileged ) + csih_FORCE_PRIVILEGED_USER=yes + ;; + + *) + echo "usage: ${progname} [OPTION]..." + echo + echo "This script creates an OpenSSH host configuration." + echo + echo "Options:" + echo " --debug -d Enable shell's debug output." + echo " --yes -y Answer all questions with \"yes\" automatically." + echo " --no -n Answer all questions with \"no\" automatically." + echo " --cygwin -c Use \"options\" as value for CYGWIN environment var." + echo " --name -N sshd windows service name." + echo " --port -p sshd listens on port n." + echo " --user -u privileged user for service, default 'cyg_server'." + echo " --pwd -w Use \"pwd\" as password for privileged user." + echo " --privileged On Windows XP, require privileged user" + echo " instead of LocalSystem for sshd service." + echo + exit 1 + ;; + + esac +done + +# ====================================================================== +# Action! +# ====================================================================== + +# Check for running ssh/sshd processes first. Refuse to do anything while +# some ssh processes are still running +if /usr/bin/ps -ef | /usr/bin/grep -q '/sshd\?$' +then + echo + csih_error "There are still ssh processes running. Please shut them down first." +fi + +# Make sure the user is running in an administrative context +admin=$(/usr/bin/id -G | /usr/bin/grep -Eq '\<544\>' && echo yes || echo no) +if [ "${admin}" != "yes" ] +then + echo + csih_warning "Running this script typically requires administrator privileges!" + csih_warning "However, it seems your account does not have these privileges." + csih_warning "Here's the list of groups in your user token:" + echo + /usr/bin/id -Gnz | xargs -0n1 echo " " + echo + csih_warning "This usually means you're running this script from a non-admin" + csih_warning "desktop session, or in a non-elevated shell under UAC control." + echo + csih_warning "Make sure you have the appropriate privileges right now," + csih_warning "otherwise parts of this script will probably fail!" + echo + echo -e "${_csih_QUERY_STR} Are you sure you want to continue? (Say \"no\" if you're not sure" + if ! csih_request "you have the required privileges)" + then + echo + csih_inform "Ok. Exiting. Make sure to switch to an administrative account" + csih_inform "or to start this script from an elevated shell." + exit 1 + fi +fi + +echo + +warning_cnt=0 + +# Create /var/log/lastlog if not already exists +if [ -e ${LOCALSTATEDIR}/log/lastlog -a ! -f ${LOCALSTATEDIR}/log/lastlog ] +then + echo + csih_error_multi "${LOCALSTATEDIR}/log/lastlog exists, but is not a file." \ + "Cannot create ssh host configuration." +fi +if [ ! -e ${LOCALSTATEDIR}/log/lastlog ] +then + /usr/bin/cat /dev/null > ${LOCALSTATEDIR}/log/lastlog + if ! /usr/bin/chmod 644 ${LOCALSTATEDIR}/log/lastlog >/dev/null 2>&1 + then + csih_warning "Can't set permissions on ${LOCALSTATEDIR}/log/lastlog!" + let ++warning_cnt + fi +fi + +# Create /var/empty file used as chroot jail for privilege separation +csih_make_dir "${LOCALSTATEDIR}/empty" "Cannot create ${LOCALSTATEDIR}/empty directory." +if ! /usr/bin/chmod 755 "${LOCALSTATEDIR}/empty" >/dev/null 2>&1 +then + csih_warning "Can't set permissions on ${LOCALSTATEDIR}/empty!" + let ++warning_cnt +fi + +# generate missing host keys +csih_inform "Generating missing SSH host keys" +/usr/bin/ssh-keygen -A || let warning_cnt+=$? + +# handle ssh_config +csih_install_config "${SYSCONFDIR}/ssh_config" "${SYSCONFDIR}/defaults" || let ++warning_cnt +if /usr/bin/cmp "${SYSCONFDIR}/ssh_config" "${SYSCONFDIR}/defaults/${SYSCONFDIR}/ssh_config" >/dev/null 2>&1 +then + if [ "${port_number}" != "22" ] + then + csih_inform "Updating ${SYSCONFDIR}/ssh_config file with requested port" + echo "Host localhost" >> ${SYSCONFDIR}/ssh_config + echo " Port ${port_number}" >> ${SYSCONFDIR}/ssh_config + fi +fi + +# handle sshd_config +# make sure not to change the existing file +mod_before="" +if [ -e "${SYSCONFDIR}/sshd_config" ] +then + mod_before=$(stat "${SYSCONFDIR}/sshd_config" | grep '^Modify:') +fi +csih_install_config "${SYSCONFDIR}/sshd_config" "${SYSCONFDIR}/defaults" || let ++warning_cnt +mod_now=$(stat "${SYSCONFDIR}/sshd_config" | grep '^Modify:') +if ! /usr/bin/cmp "${SYSCONFDIR}/sshd_config" "${SYSCONFDIR}/defaults/${SYSCONFDIR}/sshd_config" >/dev/null 2>&1 +then + sshd_config_configured=yes +fi +if [ "${mod_before}" != "${mod_now}" ] +then + sshd_strictmodes || let warning_cnt+=$? + sshd_config_tweak || let warning_cnt+=$? +fi +#sshd_privsep || let warning_cnt+=$? +update_services_file || let warning_cnt+=$? +update_inetd_conf || let warning_cnt+=$? +install_service || let warning_cnt+=$? + +echo +if [ $warning_cnt -eq 0 ] +then + csih_inform "Host configuration finished. Have fun!" +else + csih_warning "Host configuration exited with ${warning_cnt} errors or warnings!" + csih_warning "Make sure that all problems reported are fixed," + csih_warning "then re-run ssh-host-config." +fi +exit $warning_cnt diff --git a/aosp/external/openssh/contrib/cygwin/ssh-user-config b/aosp/external/openssh/contrib/cygwin/ssh-user-config new file mode 100644 index 0000000000000000000000000000000000000000..6fa4bb3eac408bede5ac1ad18dcab1f2e44b52d7 --- /dev/null +++ b/aosp/external/openssh/contrib/cygwin/ssh-user-config @@ -0,0 +1,257 @@ +#!/bin/bash +# +# ssh-user-config, Copyright 2000-2014 Red Hat Inc. +# +# This file is part of the Cygwin port of OpenSSH. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +# THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# ====================================================================== +# Initialization +# ====================================================================== +PROGNAME=$(basename -- $0) +_tdir=$(dirname -- $0) +PROGDIR=$(cd $_tdir && pwd) + +CSIH_SCRIPT=/usr/share/csih/cygwin-service-installation-helper.sh + +# Subdirectory where the new package is being installed +PREFIX=/usr + +# Directory where the config files are stored +SYSCONFDIR=/etc + +source ${CSIH_SCRIPT} + +auto_passphrase="no" +passphrase="" +pwdhome= +with_passphrase= + +# ====================================================================== +# Routine: create_identity +# optionally create identity of type argument in ~/.ssh +# optionally add result to ~/.ssh/authorized_keys +# ====================================================================== +create_identity() { + local file="$1" + local type="$2" + local name="$3" + if [ ! -f "${pwdhome}/.ssh/${file}" ] + then + if csih_request "Shall I create a ${name} identity file for you?" + then + csih_inform "Generating ${pwdhome}/.ssh/${file}" + if [ "${with_passphrase}" = "yes" ] + then + ssh-keygen -t "${type}" -N "${passphrase}" -f "${pwdhome}/.ssh/${file}" > /dev/null + else + ssh-keygen -t "${type}" -f "${pwdhome}/.ssh/${file}" > /dev/null + fi + if csih_request "Do you want to use this identity to login to this machine?" + then + csih_inform "Adding to ${pwdhome}/.ssh/authorized_keys" + cat "${pwdhome}/.ssh/${file}.pub" >> "${pwdhome}/.ssh/authorized_keys" + fi + fi + fi +} # === End of create_ssh1_identity() === # +readonly -f create_identity + +# ====================================================================== +# Routine: check_user_homedir +# Perform various checks on the user's home directory +# SETS GLOBAL VARIABLE: +# pwdhome +# ====================================================================== +check_user_homedir() { + pwdhome=$(getent passwd $UID | awk -F: '{ print $6; }') + if [ "X${pwdhome}" = "X" ] + then + csih_error_multi \ + "There is no home directory set for you in the account database." \ + 'Setting $HOME is not sufficient!' + fi + + if [ ! -d "${pwdhome}" ] + then + csih_error_multi \ + "${pwdhome} is set in the account database as your home directory" \ + 'but it is not a valid directory. Cannot create user identity files.' + fi + + # If home is the root dir, set home to empty string to avoid error messages + # in subsequent parts of that script. + if [ "X${pwdhome}" = "X/" ] + then + # But first raise a warning! + csih_warning "Your home directory in the account database is set to root (/). This is not recommended!" + if csih_request "Would you like to proceed anyway?" + then + pwdhome='' + else + csih_warning "Exiting. Configuration is not complete" + exit 1 + fi + fi + + if [ -d "${pwdhome}" -a -n "`chmod -c g-w,o-w "${pwdhome}"`" ] + then + echo + csih_warning 'group and other have been revoked write permission to your home' + csih_warning "directory ${pwdhome}." + csih_warning 'This is required by OpenSSH to allow public key authentication using' + csih_warning 'the key files stored in your .ssh subdirectory.' + csih_warning 'Revert this change ONLY if you know what you are doing!' + echo + fi +} # === End of check_user_homedir() === # +readonly -f check_user_homedir + +# ====================================================================== +# Routine: check_user_dot_ssh_dir +# Perform various checks on the ~/.ssh directory +# PREREQUISITE: +# pwdhome -- check_user_homedir() +# ====================================================================== +check_user_dot_ssh_dir() { + if [ -e "${pwdhome}/.ssh" -a ! -d "${pwdhome}/.ssh" ] + then + csih_error "${pwdhome}/.ssh is existent but not a directory. Cannot create user identity files." + fi + + if [ ! -e "${pwdhome}/.ssh" ] + then + mkdir "${pwdhome}/.ssh" + if [ ! -e "${pwdhome}/.ssh" ] + then + csih_error "Creating users ${pwdhome}/.ssh directory failed" + fi + fi +} # === End of check_user_dot_ssh_dir() === # +readonly -f check_user_dot_ssh_dir + +# ====================================================================== +# Routine: fix_authorized_keys_perms +# Corrects the permissions of ~/.ssh/authorized_keys +# PREREQUISITE: +# pwdhome -- check_user_homedir() +# ====================================================================== +fix_authorized_keys_perms() { + if [ -e "${pwdhome}/.ssh/authorized_keys" ] + then + setfacl -b "${pwdhome}/.ssh/authorized_keys" 2>/dev/null || echo -n + if ! chmod u-x,g-wx,o-wx "${pwdhome}/.ssh/authorized_keys" + then + csih_warning "Setting correct permissions to ${pwdhome}/.ssh/authorized_keys" + csih_warning "failed. Please care for the correct permissions. The minimum requirement" + csih_warning "is, the owner needs read permissions." + echo + fi + fi +} # === End of fix_authorized_keys_perms() === # +readonly -f fix_authorized_keys_perms + + +# ====================================================================== +# Main Entry Point +# ====================================================================== + +# Check how the script has been started. If +# (1) it has been started by giving the full path and +# that path is /etc/postinstall, OR +# (2) Otherwise, if the environment variable +# SSH_USER_CONFIG_AUTO_ANSWER_NO is set +# then set auto_answer to "no". This allows automatic +# creation of the config files in /etc w/o overwriting +# them if they already exist. In both cases, color +# escape sequences are suppressed, so as to prevent +# cluttering setup's logfiles. +if [ "$PROGDIR" = "/etc/postinstall" ] +then + csih_auto_answer="no" + csih_disable_color +fi +if [ -n "${SSH_USER_CONFIG_AUTO_ANSWER_NO}" ] +then + csih_auto_answer="no" + csih_disable_color +fi + +# ====================================================================== +# Parse options +# ====================================================================== +while : +do + case $# in + 0) + break + ;; + esac + + option=$1 + shift + + case "$option" in + -d | --debug ) + set -x + csih_trace_on + ;; + + -y | --yes ) + csih_auto_answer=yes + ;; + + -n | --no ) + csih_auto_answer=no + ;; + + -p | --passphrase ) + with_passphrase="yes" + passphrase=$1 + shift + ;; + + *) + echo "usage: ${PROGNAME} [OPTION]..." + echo + echo "This script creates an OpenSSH user configuration." + echo + echo "Options:" + echo " --debug -d Enable shell's debug output." + echo " --yes -y Answer all questions with \"yes\" automatically." + echo " --no -n Answer all questions with \"no\" automatically." + echo " --passphrase -p word Use \"word\" as passphrase automatically." + echo + exit 1 + ;; + + esac +done + +# ====================================================================== +# Action! +# ====================================================================== + +check_user_homedir +check_user_dot_ssh_dir +create_identity id_rsa rsa "SSH2 RSA" +create_identity id_dsa dsa "SSH2 DSA" +create_identity id_ecdsa ecdsa "SSH2 ECDSA" +create_identity identity rsa1 "(deprecated) SSH1 RSA" +fix_authorized_keys_perms + +echo +csih_inform "Configuration finished. Have fun!" + + diff --git a/aosp/external/openssh/contrib/cygwin/sshd-inetd b/aosp/external/openssh/contrib/cygwin/sshd-inetd new file mode 100644 index 0000000000000000000000000000000000000000..aa6bf073f1b26b27e4af2045ec40f2f70c4c2a09 --- /dev/null +++ b/aosp/external/openssh/contrib/cygwin/sshd-inetd @@ -0,0 +1,4 @@ +# This file can be used to enable sshd as a slave of the inetd service +# To do so, the line below should be uncommented. +@COMMENT@ ssh stream tcp nowait root /usr/sbin/sshd sshd -i + diff --git a/aosp/external/openssh/contrib/findssl.sh b/aosp/external/openssh/contrib/findssl.sh new file mode 100644 index 0000000000000000000000000000000000000000..95a0d66dfe6357aa90e2bbc8fb0135e037c2220b --- /dev/null +++ b/aosp/external/openssh/contrib/findssl.sh @@ -0,0 +1,184 @@ +#!/bin/sh +# +# findssl.sh +# Search for all instances of OpenSSL headers and libraries +# and print their versions. +# Intended to help diagnose OpenSSH's "OpenSSL headers do not +# match your library" errors. +# +# Written by Darren Tucker (dtucker at zip dot com dot au) +# This file is placed in the public domain. +# +# Release history: +# 2002-07-27: Initial release. +# 2002-08-04: Added public domain notice. +# 2003-06-24: Incorporated readme, set library paths. First cvs version. +# 2004-12-13: Add traps to cleanup temp files, from Amarendra Godbole. +# +# "OpenSSL headers do not match your library" are usually caused by +# OpenSSH's configure picking up an older version of OpenSSL headers +# or libraries. You can use the following # procedure to help identify +# the cause. +# +# The output of configure will tell you the versions of the OpenSSL +# headers and libraries that were picked up, for example: +# +# checking OpenSSL header version... 90604f (OpenSSL 0.9.6d 9 May 2002) +# checking OpenSSL library version... 90602f (OpenSSL 0.9.6b [engine] 9 Jul 2001) +# checking whether OpenSSL's headers match the library... no +# configure: error: Your OpenSSL headers do not match your library +# +# Now run findssl.sh. This should identify the headers and libraries +# present and their versions. You should be able to identify the +# libraries and headers used and adjust your CFLAGS or remove incorrect +# versions. The output will show OpenSSL's internal version identifier +# and should look something like: + +# $ ./findssl.sh +# Searching for OpenSSL header files. +# 0x0090604fL /usr/include/openssl/opensslv.h +# 0x0090604fL /usr/local/ssl/include/openssl/opensslv.h +# +# Searching for OpenSSL shared library files. +# 0x0090602fL /lib/libcrypto.so.0.9.6b +# 0x0090602fL /lib/libcrypto.so.2 +# 0x0090581fL /usr/lib/libcrypto.so.0 +# 0x0090602fL /usr/lib/libcrypto.so +# 0x0090581fL /usr/lib/libcrypto.so.0.9.5a +# 0x0090600fL /usr/lib/libcrypto.so.0.9.6 +# 0x0090600fL /usr/lib/libcrypto.so.1 +# +# Searching for OpenSSL static library files. +# 0x0090602fL /usr/lib/libcrypto.a +# 0x0090604fL /usr/local/ssl/lib/libcrypto.a +# +# In this example, I gave configure no extra flags, so it's picking up +# the OpenSSL header from /usr/include/openssl (90604f) and the library +# from /usr/lib/ (90602f). + +# +# Adjust these to suit your compiler. +# You may also need to set the *LIB*PATH environment variables if +# DEFAULT_LIBPATH is not correct for your system. +# +CC=gcc +STATIC=-static + +# +# Cleanup on interrupt +# +trap 'rm -f conftest.c' INT HUP TERM + +# +# Set up conftest C source +# +rm -f findssl.log +cat >conftest.c < +int main(){printf("0x%08xL\n", SSLeay());} +EOD + +# +# Set default library paths if not already set +# +DEFAULT_LIBPATH=/usr/lib:/usr/local/lib +LIBPATH=${LIBPATH:=$DEFAULT_LIBPATH} +LD_LIBRARY_PATH=${LD_LIBRARY_PATH:=$DEFAULT_LIBPATH} +LIBRARY_PATH=${LIBRARY_PATH:=$DEFAULT_LIBPATH} +export LIBPATH LD_LIBRARY_PATH LIBRARY_PATH + +# not all platforms have a 'which' command +if which ls >/dev/null 2>/dev/null; then + : which is defined +else + which () { + saveIFS="$IFS" + IFS=: + for p in $PATH; do + if test -x "$p/$1" -a -f "$p/$1"; then + IFS="$saveIFS" + echo "$p/$1" + return 0 + fi + done + IFS="$saveIFS" + return 1 + } +fi + +# +# Search for OpenSSL headers and print versions +# +echo Searching for OpenSSL header files. +if [ -x "`which locate`" ] +then + headers=`locate opensslv.h` +else + headers=`find / -name opensslv.h -print 2>/dev/null` +fi + +for header in $headers +do + ver=`awk '/OPENSSL_VERSION_NUMBER/{printf \$3}' $header` + echo "$ver $header" +done +echo + +# +# Search for shared libraries. +# Relies on shared libraries looking like "libcrypto.s*" +# +echo Searching for OpenSSL shared library files. +if [ -x "`which locate`" ] +then + libraries=`locate libcrypto.s` +else + libraries=`find / -name 'libcrypto.s*' -print 2>/dev/null` +fi + +for lib in $libraries +do + (echo "Trying libcrypto $lib" >>findssl.log + dir=`dirname $lib` + LIBPATH="$dir:$LIBPATH" + LD_LIBRARY_PATH="$dir:$LIBPATH" + LIBRARY_PATH="$dir:$LIBPATH" + export LIBPATH LD_LIBRARY_PATH LIBRARY_PATH + ${CC} -o conftest conftest.c $lib 2>>findssl.log + if [ -x ./conftest ] + then + ver=`./conftest 2>/dev/null` + rm -f ./conftest + echo "$ver $lib" + fi) +done +echo + +# +# Search for static OpenSSL libraries and print versions +# +echo Searching for OpenSSL static library files. +if [ -x "`which locate`" ] +then + libraries=`locate libcrypto.a` +else + libraries=`find / -name libcrypto.a -print 2>/dev/null` +fi + +for lib in $libraries +do + libdir=`dirname $lib` + echo "Trying libcrypto $lib" >>findssl.log + ${CC} ${STATIC} -o conftest conftest.c -L${libdir} -lcrypto 2>>findssl.log + if [ -x ./conftest ] + then + ver=`./conftest 2>/dev/null` + rm -f ./conftest + echo "$ver $lib" + fi +done + +# +# Clean up +# +rm -f conftest.c diff --git a/aosp/external/openssh/contrib/gnome-ssh-askpass1.c b/aosp/external/openssh/contrib/gnome-ssh-askpass1.c new file mode 100644 index 0000000000000000000000000000000000000000..4c92c177b5d246149d9919bc09b421735faa3e18 --- /dev/null +++ b/aosp/external/openssh/contrib/gnome-ssh-askpass1.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000-2002 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is a simple GNOME SSH passphrase grabber. To use it, set the + * environment variable SSH_ASKPASS to point to the location of + * gnome-ssh-askpass before calling "ssh-add < /dev/null". + * + * There is only two run-time options: if you set the environment variable + * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab + * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the + * pointer will be grabbed too. These may have some benefit to security if + * you don't trust your X server. We grab the keyboard always. + */ + +/* + * Compile with: + * + * cc `gnome-config --cflags gnome gnomeui` \ + * gnome-ssh-askpass1.c -o gnome-ssh-askpass \ + * `gnome-config --libs gnome gnomeui` + * + */ + +#include +#include +#include +#include +#include +#include + +void +report_failed_grab (void) +{ + GtkWidget *err; + + err = gnome_message_box_new("Could not grab keyboard or mouse.\n" + "A malicious client may be eavesdropping on your session.", + GNOME_MESSAGE_BOX_ERROR, "EXIT", NULL); + gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); + gtk_object_set(GTK_OBJECT(err), "type", GTK_WINDOW_POPUP, NULL); + + gnome_dialog_run_and_close(GNOME_DIALOG(err)); +} + +int +passphrase_dialog(char *message) +{ + char *passphrase; + char **messages; + int result, i, grab_server, grab_pointer; + GtkWidget *dialog, *entry, *label; + + grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); + grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL); + + dialog = gnome_dialog_new("OpenSSH", GNOME_STOCK_BUTTON_OK, + GNOME_STOCK_BUTTON_CANCEL, NULL); + + messages = g_strsplit(message, "\\n", 0); + if (messages) + for(i = 0; messages[i]; i++) { + label = gtk_label_new(messages[i]); + gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), + label, FALSE, FALSE, 0); + } + + entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), entry, FALSE, + FALSE, 0); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_widget_grab_focus(entry); + + /* Center window and prepare for grab */ + gtk_object_set(GTK_OBJECT(dialog), "type", GTK_WINDOW_POPUP, NULL); + gnome_dialog_set_default(GNOME_DIALOG(dialog), 0); + gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, TRUE); + gnome_dialog_close_hides(GNOME_DIALOG(dialog), TRUE); + gtk_container_set_border_width(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox), + GNOME_PAD); + gtk_widget_show_all(dialog); + + /* Grab focus */ + if (grab_server) + XGrabServer(GDK_DISPLAY()); + if (grab_pointer && gdk_pointer_grab(dialog->window, TRUE, 0, + NULL, NULL, GDK_CURRENT_TIME)) + goto nograb; + if (gdk_keyboard_grab(dialog->window, FALSE, GDK_CURRENT_TIME)) + goto nograbkb; + + /* Make close dialog */ + gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry)); + + /* Run dialog */ + result = gnome_dialog_run(GNOME_DIALOG(dialog)); + + /* Ungrab */ + if (grab_server) + XUngrabServer(GDK_DISPLAY()); + if (grab_pointer) + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_flush(); + + /* Report passphrase if user selected OK */ + passphrase = gtk_entry_get_text(GTK_ENTRY(entry)); + if (result == 0) + puts(passphrase); + + /* Zero passphrase in memory */ + memset(passphrase, '\0', strlen(passphrase)); + gtk_entry_set_text(GTK_ENTRY(entry), passphrase); + + gnome_dialog_close(GNOME_DIALOG(dialog)); + return (result == 0 ? 0 : -1); + + /* + * At least one grab failed - ungrab what we got, and report the + * failure to the user. Note that XGrabServer() cannot fail. + */ + nograbkb: + gdk_pointer_ungrab(GDK_CURRENT_TIME); + nograb: + if (grab_server) + XUngrabServer(GDK_DISPLAY()); + gnome_dialog_close(GNOME_DIALOG(dialog)); + + report_failed_grab(); + return (-1); +} + +int +main(int argc, char **argv) +{ + char *message; + int result; + + gnome_init("GNOME ssh-askpass", "0.1", argc, argv); + + if (argc == 2) + message = argv[1]; + else + message = "Enter your OpenSSH passphrase:"; + + setvbuf(stdout, 0, _IONBF, 0); + result = passphrase_dialog(message); + + return (result); +} diff --git a/aosp/external/openssh/contrib/gnome-ssh-askpass2.c b/aosp/external/openssh/contrib/gnome-ssh-askpass2.c new file mode 100644 index 0000000000000000000000000000000000000000..a62f981529508e5b2626a252c5e72267f11decfc --- /dev/null +++ b/aosp/external/openssh/contrib/gnome-ssh-askpass2.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2000-2002 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* GTK2 support by Nalin Dahyabhai */ + +/* + * This is a simple GNOME SSH passphrase grabber. To use it, set the + * environment variable SSH_ASKPASS to point to the location of + * gnome-ssh-askpass before calling "ssh-add < /dev/null". + * + * There is only two run-time options: if you set the environment variable + * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab + * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the + * pointer will be grabbed too. These may have some benefit to security if + * you don't trust your X server. We grab the keyboard always. + */ + +#define GRAB_TRIES 16 +#define GRAB_WAIT 250 /* milliseconds */ + +#define PROMPT_ENTRY 0 +#define PROMPT_CONFIRM 1 +#define PROMPT_NONE 2 + +/* + * Compile with: + * + * cc -Wall `pkg-config --cflags gtk+-2.0` \ + * gnome-ssh-askpass2.c -o gnome-ssh-askpass \ + * `pkg-config --libs gtk+-2.0` + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void +report_failed_grab (GtkWidget *parent_window, const char *what) +{ + GtkWidget *err; + + err = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "Could not grab %s. A malicious client may be eavesdropping " + "on your session.", what); + gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); + + gtk_dialog_run(GTK_DIALOG(err)); + + gtk_widget_destroy(err); +} + +static void +ok_dialog(GtkWidget *entry, gpointer dialog) +{ + g_return_if_fail(GTK_IS_DIALOG(dialog)); + gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); +} + +static gboolean +check_none(GtkWidget *widget, GdkEventKey *event, gpointer dialog) +{ + switch (event->keyval) { + case GDK_KEY_Escape: + /* esc -> close dialog */ + gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); + return TRUE; + case GDK_KEY_Tab: + /* tab -> focus close button */ + gtk_widget_grab_focus(gtk_dialog_get_widget_for_response( + dialog, GTK_RESPONSE_CLOSE)); + return TRUE; + default: + /* eat all other key events */ + return TRUE; + } +} + +static int +parse_env_hex_color(const char *env, GdkColor *c) +{ + const char *s; + unsigned long ul; + char *ep; + size_t n; + + if ((s = getenv(env)) == NULL) + return 0; + + memset(c, 0, sizeof(*c)); + + /* Permit hex rgb or rrggbb optionally prefixed by '#' or '0x' */ + if (*s == '#') + s++; + else if (strncmp(s, "0x", 2) == 0) + s += 2; + n = strlen(s); + if (n != 3 && n != 6) + goto bad; + ul = strtoul(s, &ep, 16); + if (*ep != '\0' || ul > 0xffffff) { + bad: + fprintf(stderr, "Invalid $%s - invalid hex color code\n", env); + return 0; + } + /* Valid hex sequence; expand into a GdkColor */ + if (n == 3) { + /* 4-bit RGB */ + c->red = ((ul >> 8) & 0xf) << 12; + c->green = ((ul >> 4) & 0xf) << 12; + c->blue = (ul & 0xf) << 12; + } else { + /* 8-bit RGB */ + c->red = ((ul >> 16) & 0xff) << 8; + c->green = ((ul >> 8) & 0xff) << 8; + c->blue = (ul & 0xff) << 8; + } + return 1; +} + +static int +passphrase_dialog(char *message, int prompt_type) +{ + const char *failed; + char *passphrase, *local; + int result, grab_tries, grab_server, grab_pointer; + int buttons, default_response; + GtkWidget *parent_window, *dialog, *entry; + GdkGrabStatus status; + GdkColor fg, bg; + int fg_set = 0, bg_set = 0; + + grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); + grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL); + grab_tries = 0; + + fg_set = parse_env_hex_color("GNOME_SSH_ASKPASS_FG_COLOR", &fg); + bg_set = parse_env_hex_color("GNOME_SSH_ASKPASS_BG_COLOR", &bg); + + /* Create an invisible parent window so that GtkDialog doesn't + * complain. */ + parent_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + switch (prompt_type) { + case PROMPT_CONFIRM: + buttons = GTK_BUTTONS_YES_NO; + default_response = GTK_RESPONSE_YES; + break; + case PROMPT_NONE: + buttons = GTK_BUTTONS_CLOSE; + default_response = GTK_RESPONSE_CLOSE; + break; + default: + buttons = GTK_BUTTONS_OK_CANCEL; + default_response = GTK_RESPONSE_OK; + break; + } + + dialog = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, + GTK_MESSAGE_QUESTION, buttons, "%s", message); + + gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH"); + gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_response); + gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); + + if (fg_set) + gtk_widget_modify_fg(dialog, GTK_STATE_NORMAL, &fg); + if (bg_set) + gtk_widget_modify_bg(dialog, GTK_STATE_NORMAL, &bg); + + if (prompt_type == PROMPT_ENTRY || prompt_type == PROMPT_NONE) { + entry = gtk_entry_new(); + if (fg_set) + gtk_widget_modify_fg(entry, GTK_STATE_NORMAL, &fg); + if (bg_set) + gtk_widget_modify_bg(entry, GTK_STATE_NORMAL, &bg); + gtk_box_pack_start( + GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), + entry, FALSE, FALSE, 0); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_widget_grab_focus(entry); + if (prompt_type == PROMPT_ENTRY) { + gtk_widget_show(entry); + /* Make close dialog */ + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(ok_dialog), dialog); + } else { + /* + * Ensure the 'close' button is not focused by default + * but is still reachable via tab. This is a bit of a + * hack - it uses a hidden entry that responds to a + * couple of keypress events (escape and tab only). + */ + gtk_widget_realize(entry); + g_signal_connect(G_OBJECT(entry), "key_press_event", + G_CALLBACK(check_none), dialog); + } + } + + /* Grab focus */ + gtk_widget_show_now(dialog); + if (grab_pointer) { + for(;;) { + status = gdk_pointer_grab( + (gtk_widget_get_window(GTK_WIDGET(dialog))), TRUE, + 0, NULL, NULL, GDK_CURRENT_TIME); + if (status == GDK_GRAB_SUCCESS) + break; + usleep(GRAB_WAIT * 1000); + if (++grab_tries > GRAB_TRIES) { + failed = "mouse"; + goto nograb; + } + } + } + for(;;) { + status = gdk_keyboard_grab( + gtk_widget_get_window(GTK_WIDGET(dialog)), FALSE, + GDK_CURRENT_TIME); + if (status == GDK_GRAB_SUCCESS) + break; + usleep(GRAB_WAIT * 1000); + if (++grab_tries > GRAB_TRIES) { + failed = "keyboard"; + goto nograbkb; + } + } + if (grab_server) { + gdk_x11_grab_server(); + } + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + + /* Ungrab */ + if (grab_server) + XUngrabServer(gdk_x11_get_default_xdisplay()); + if (grab_pointer) + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_flush(); + + /* Report passphrase if user selected OK */ + if (prompt_type == PROMPT_ENTRY) { + passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + if (result == GTK_RESPONSE_OK) { + local = g_locale_from_utf8(passphrase, + strlen(passphrase), NULL, NULL, NULL); + if (local != NULL) { + puts(local); + memset(local, '\0', strlen(local)); + g_free(local); + } else { + puts(passphrase); + } + } + /* Zero passphrase in memory */ + memset(passphrase, '\b', strlen(passphrase)); + gtk_entry_set_text(GTK_ENTRY(entry), passphrase); + memset(passphrase, '\0', strlen(passphrase)); + g_free(passphrase); + } + + gtk_widget_destroy(dialog); + if (result != GTK_RESPONSE_OK && result != GTK_RESPONSE_YES) + return -1; + return 0; + + nograbkb: + /* + * At least one grab failed - ungrab what we got, and report + * the failure to the user. Note that XGrabServer() cannot + * fail. + */ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + nograb: + if (grab_server) + XUngrabServer(gdk_x11_get_default_xdisplay()); + gtk_widget_destroy(dialog); + + report_failed_grab(parent_window, failed); + + return (-1); +} + +int +main(int argc, char **argv) +{ + char *message, *prompt_mode; + int result, prompt_type = PROMPT_ENTRY; + + gtk_init(&argc, &argv); + + if (argc > 1) { + message = g_strjoinv(" ", argv + 1); + } else { + message = g_strdup("Enter your OpenSSH passphrase:"); + } + + if ((prompt_mode = getenv("SSH_ASKPASS_PROMPT")) != NULL) { + if (strcasecmp(prompt_mode, "confirm") == 0) + prompt_type = PROMPT_CONFIRM; + else if (strcasecmp(prompt_mode, "none") == 0) + prompt_type = PROMPT_NONE; + } + + setvbuf(stdout, 0, _IONBF, 0); + result = passphrase_dialog(message, prompt_type); + g_free(message); + + return (result); +} diff --git a/aosp/external/openssh/contrib/gnome-ssh-askpass3.c b/aosp/external/openssh/contrib/gnome-ssh-askpass3.c new file mode 100644 index 0000000000000000000000000000000000000000..e1a0533ef6240665afdfde16ecd3b0a832a81708 --- /dev/null +++ b/aosp/external/openssh/contrib/gnome-ssh-askpass3.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2000-2002 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* GTK2 support by Nalin Dahyabhai */ + +/* + * This is a simple GNOME SSH passphrase grabber. To use it, set the + * environment variable SSH_ASKPASS to point to the location of + * gnome-ssh-askpass before calling "ssh-add < /dev/null". + * + * There is only two run-time options: if you set the environment variable + * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab + * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the + * pointer will be grabbed too. These may have some benefit to security if + * you don't trust your X server. We grab the keyboard always. + */ + +#define GRAB_TRIES 16 +#define GRAB_WAIT 250 /* milliseconds */ + +#define PROMPT_ENTRY 0 +#define PROMPT_CONFIRM 1 +#define PROMPT_NONE 2 + +/* + * Compile with: + * + * cc -Wall `pkg-config --cflags gtk+-2.0` \ + * gnome-ssh-askpass2.c -o gnome-ssh-askpass \ + * `pkg-config --libs gtk+-2.0` + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void +ok_dialog(GtkWidget *entry, gpointer dialog) +{ + g_return_if_fail(GTK_IS_DIALOG(dialog)); + gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); +} + +static gboolean +check_none(GtkWidget *widget, GdkEventKey *event, gpointer dialog) +{ + switch (event->keyval) { + case GDK_KEY_Escape: + /* esc -> close dialog */ + gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); + return TRUE; + case GDK_KEY_Tab: + /* tab -> focus close button */ + gtk_widget_grab_focus(gtk_dialog_get_widget_for_response( + dialog, GTK_RESPONSE_CLOSE)); + return TRUE; + default: + /* eat all other key events */ + return TRUE; + } +} + +static int +parse_env_hex_color(const char *env, GdkColor *c) +{ + const char *s; + unsigned long ul; + char *ep; + size_t n; + + if ((s = getenv(env)) == NULL) + return 0; + + memset(c, 0, sizeof(*c)); + + /* Permit hex rgb or rrggbb optionally prefixed by '#' or '0x' */ + if (*s == '#') + s++; + else if (strncmp(s, "0x", 2) == 0) + s += 2; + n = strlen(s); + if (n != 3 && n != 6) + goto bad; + ul = strtoul(s, &ep, 16); + if (*ep != '\0' || ul > 0xffffff) { + bad: + fprintf(stderr, "Invalid $%s - invalid hex color code\n", env); + return 0; + } + /* Valid hex sequence; expand into a GdkColor */ + if (n == 3) { + /* 4-bit RGB */ + c->red = ((ul >> 8) & 0xf) << 12; + c->green = ((ul >> 4) & 0xf) << 12; + c->blue = (ul & 0xf) << 12; + } else { + /* 8-bit RGB */ + c->red = ((ul >> 16) & 0xff) << 8; + c->green = ((ul >> 8) & 0xff) << 8; + c->blue = (ul & 0xff) << 8; + } + return 1; +} + +static int +passphrase_dialog(char *message, int prompt_type) +{ + const char *failed; + char *passphrase, *local; + int result, grab_tries, grab_server, grab_pointer; + int buttons, default_response; + GtkWidget *parent_window, *dialog, *entry, *err; + GdkGrabStatus status; + GdkColor fg, bg; + GdkSeat *seat; + GdkDisplay *display; + GdkSeatCapabilities caps; + int fg_set = 0, bg_set = 0; + + grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); + grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL); + grab_tries = 0; + + fg_set = parse_env_hex_color("GNOME_SSH_ASKPASS_FG_COLOR", &fg); + bg_set = parse_env_hex_color("GNOME_SSH_ASKPASS_BG_COLOR", &bg); + + /* Create an invisible parent window so that GtkDialog doesn't + * complain. */ + parent_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + switch (prompt_type) { + case PROMPT_CONFIRM: + buttons = GTK_BUTTONS_YES_NO; + default_response = GTK_RESPONSE_YES; + break; + case PROMPT_NONE: + buttons = GTK_BUTTONS_CLOSE; + default_response = GTK_RESPONSE_CLOSE; + break; + default: + buttons = GTK_BUTTONS_OK_CANCEL; + default_response = GTK_RESPONSE_OK; + break; + } + + dialog = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, + GTK_MESSAGE_QUESTION, buttons, "%s", message); + + gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH"); + gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_response); + gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); + + if (fg_set) + gtk_widget_modify_fg(dialog, GTK_STATE_NORMAL, &fg); + if (bg_set) + gtk_widget_modify_bg(dialog, GTK_STATE_NORMAL, &bg); + + if (prompt_type == PROMPT_ENTRY || prompt_type == PROMPT_NONE) { + entry = gtk_entry_new(); + if (fg_set) + gtk_widget_modify_fg(entry, GTK_STATE_NORMAL, &fg); + if (bg_set) + gtk_widget_modify_bg(entry, GTK_STATE_NORMAL, &bg); + gtk_box_pack_start( + GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), + entry, FALSE, FALSE, 0); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_widget_grab_focus(entry); + if (prompt_type == PROMPT_ENTRY) { + gtk_widget_show(entry); + /* Make close dialog */ + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(ok_dialog), dialog); + } else { + /* + * Ensure the 'close' button is not focused by default + * but is still reachable via tab. This is a bit of a + * hack - it uses a hidden entry that responds to a + * couple of keypress events (escape and tab only). + */ + gtk_widget_realize(entry); + g_signal_connect(G_OBJECT(entry), "key_press_event", + G_CALLBACK(check_none), dialog); + } + } + /* Grab focus */ + gtk_widget_show_now(dialog); + display = gtk_widget_get_display(GTK_WIDGET(dialog)); + seat = gdk_display_get_default_seat(display); + caps = GDK_SEAT_CAPABILITY_KEYBOARD; + if (grab_pointer) + caps |= GDK_SEAT_CAPABILITY_ALL_POINTING; + if (grab_server) + caps = GDK_SEAT_CAPABILITY_ALL; + for (;;) { + status = gdk_seat_grab(seat, gtk_widget_get_window(dialog), + caps, TRUE, NULL, NULL, NULL, NULL); + if (status == GDK_GRAB_SUCCESS) + break; + usleep(GRAB_WAIT * 1000); + if (++grab_tries > GRAB_TRIES) + goto nograb; + } + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + + /* Ungrab */ + gdk_seat_ungrab(seat); + gdk_display_flush(display); + + /* Report passphrase if user selected OK */ + if (prompt_type == PROMPT_ENTRY) { + passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + if (result == GTK_RESPONSE_OK) { + local = g_locale_from_utf8(passphrase, + strlen(passphrase), NULL, NULL, NULL); + if (local != NULL) { + puts(local); + memset(local, '\0', strlen(local)); + g_free(local); + } else { + puts(passphrase); + } + } + /* Zero passphrase in memory */ + memset(passphrase, '\b', strlen(passphrase)); + gtk_entry_set_text(GTK_ENTRY(entry), passphrase); + memset(passphrase, '\0', strlen(passphrase)); + g_free(passphrase); + } + + gtk_widget_destroy(dialog); + if (result != GTK_RESPONSE_OK && result != GTK_RESPONSE_YES) + return -1; + return 0; + + nograb: + gtk_widget_destroy(dialog); + err = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "Could not grab input. A malicious client may be eavesdropping " + "on your session."); + gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); + gtk_dialog_run(GTK_DIALOG(err)); + gtk_widget_destroy(err); + return -1; +} + +int +main(int argc, char **argv) +{ + char *message, *prompt_mode; + int result, prompt_type = PROMPT_ENTRY; + + gtk_init(&argc, &argv); + + if (argc > 1) { + message = g_strjoinv(" ", argv + 1); + } else { + message = g_strdup("Enter your OpenSSH passphrase:"); + } + + if ((prompt_mode = getenv("SSH_ASKPASS_PROMPT")) != NULL) { + if (strcasecmp(prompt_mode, "confirm") == 0) + prompt_type = PROMPT_CONFIRM; + else if (strcasecmp(prompt_mode, "none") == 0) + prompt_type = PROMPT_NONE; + } + + setvbuf(stdout, 0, _IONBF, 0); + result = passphrase_dialog(message, prompt_type); + g_free(message); + + return (result); +} diff --git a/aosp/external/openssh/contrib/hpux/README b/aosp/external/openssh/contrib/hpux/README new file mode 100644 index 0000000000000000000000000000000000000000..f8bfa84e498668cca4a1c41658b3f41237bf0940 --- /dev/null +++ b/aosp/external/openssh/contrib/hpux/README @@ -0,0 +1,45 @@ +README for OpenSSH HP-UX contrib files +Kevin Steves + +sshd: configuration file for sshd.rc +sshd.rc: SSH startup script +egd: configuration file for egd.rc +egd.rc: EGD (entropy gathering daemon) startup script + +To install: + +sshd.rc: + +o Verify paths in sshd.rc match your local installation + (WHAT_PATH and WHAT_PID) +o Customize sshd if needed (SSHD_ARGS) +o Install: + + # cp sshd /etc/rc.config.d + # chmod 444 /etc/rc.config.d/sshd + # cp sshd.rc /sbin/init.d + # chmod 555 /sbin/init.d/sshd.rc + # ln -s /sbin/init.d/sshd.rc /sbin/rc1.d/K100sshd + # ln -s /sbin/init.d/sshd.rc /sbin/rc2.d/S900sshd + +egd.rc: + +o Verify egd.pl path in egd.rc matches your local installation + (WHAT_PATH) +o Customize egd if needed (EGD_ARGS and EGD_LOG) +o Add pseudo account: + + # groupadd egd + # useradd -g egd egd + # mkdir -p /etc/opt/egd + # chown egd:egd /etc/opt/egd + # chmod 711 /etc/opt/egd + +o Install: + + # cp egd /etc/rc.config.d + # chmod 444 /etc/rc.config.d/egd + # cp egd.rc /sbin/init.d + # chmod 555 /sbin/init.d/egd.rc + # ln -s /sbin/init.d/egd.rc /sbin/rc1.d/K600egd + # ln -s /sbin/init.d/egd.rc /sbin/rc2.d/S400egd diff --git a/aosp/external/openssh/contrib/hpux/egd b/aosp/external/openssh/contrib/hpux/egd new file mode 100644 index 0000000000000000000000000000000000000000..21af0bd13e7a0fbf12e5410ecec6d878d02b5d2a --- /dev/null +++ b/aosp/external/openssh/contrib/hpux/egd @@ -0,0 +1,15 @@ +# EGD_START: Set to 1 to start entropy gathering daemon +# EGD_ARGS: Command line arguments to pass to egd +# EGD_LOG: EGD stdout and stderr log file (default /etc/opt/egd/egd.log) +# +# To configure the egd environment: + +# groupadd egd +# useradd -g egd egd +# mkdir -p /etc/opt/egd +# chown egd:egd /etc/opt/egd +# chmod 711 /etc/opt/egd + +EGD_START=1 +EGD_ARGS='/etc/opt/egd/entropy' +EGD_LOG= diff --git a/aosp/external/openssh/contrib/hpux/egd.rc b/aosp/external/openssh/contrib/hpux/egd.rc new file mode 100644 index 0000000000000000000000000000000000000000..919dea7255cd77cb914cc7279d56d637086bad00 --- /dev/null +++ b/aosp/external/openssh/contrib/hpux/egd.rc @@ -0,0 +1,98 @@ +#!/sbin/sh + +# +# egd.rc: EGD start-up and shutdown script +# + +# Allowed exit values: +# 0 = success; causes "OK" to show up in checklist. +# 1 = failure; causes "FAIL" to show up in checklist. +# 2 = skip; causes "N/A" to show up in the checklist. +# Use this value if execution of this script is overridden +# by the use of a control variable, or if this script is not +# appropriate to execute for some other reason. +# 3 = reboot; causes the system to be rebooted after execution. + +# Input and output: +# stdin is redirected from /dev/null +# +# stdout and stderr are redirected to the /etc/rc.log file +# during checklist mode, or to the console in raw mode. + +umask 022 + +PATH=/usr/sbin:/usr/bin:/sbin +export PATH + +WHAT='EGD (entropy gathering daemon)' +WHAT_PATH=/opt/perl/bin/egd.pl +WHAT_CONFIG=/etc/rc.config.d/egd +WHAT_LOG=/etc/opt/egd/egd.log + +# NOTE: If your script executes in run state 0 or state 1, then /usr might +# not be available. Do not attempt to access commands or files in +# /usr unless your script executes in run state 2 or greater. Other +# file systems typically not mounted until run state 2 include /var +# and /opt. + +rval=0 + +# Check the exit value of a command run by this script. If non-zero, the +# exit code is echoed to the log file and the return value of this script +# is set to indicate failure. + +set_return() { + x=$? + if [ $x -ne 0 ]; then + echo "EXIT CODE: $x" + rval=1 # script FAILed + fi +} + +case $1 in +'start_msg') + echo "Starting $WHAT" + ;; + +'stop_msg') + echo "Stopping $WHAT" + ;; + +'start') + if [ -f $WHAT_CONFIG ] ; then + . $WHAT_CONFIG + else + echo "ERROR: $WHAT_CONFIG defaults file MISSING" + fi + + + if [ "$EGD_START" -eq 1 -a -x $WHAT_PATH ]; then + EGD_LOG=${EGD_LOG:-$WHAT_LOG} + su egd -c "nohup $WHAT_PATH $EGD_ARGS >$EGD_LOG 2>&1" && + echo $WHAT started + set_return + else + rval=2 + fi + ;; + +'stop') + pid=`ps -fuegd | awk '$1 == "egd" { print $2 }'` + if [ "X$pid" != "X" ]; then + if kill "$pid"; then + echo "$WHAT stopped" + else + rval=1 + echo "Unable to stop $WHAT" + fi + fi + set_return + ;; + +*) + echo "usage: $0 {start|stop|start_msg|stop_msg}" + rval=1 + ;; +esac + +exit $rval diff --git a/aosp/external/openssh/contrib/hpux/sshd b/aosp/external/openssh/contrib/hpux/sshd new file mode 100644 index 0000000000000000000000000000000000000000..8eb5e92a30bc91ed1262f90dc9abf2f719b0b63e --- /dev/null +++ b/aosp/external/openssh/contrib/hpux/sshd @@ -0,0 +1,5 @@ +# SSHD_START: Set to 1 to start SSH daemon +# SSHD_ARGS: Command line arguments to pass to sshd +# +SSHD_START=1 +SSHD_ARGS= diff --git a/aosp/external/openssh/contrib/hpux/sshd.rc b/aosp/external/openssh/contrib/hpux/sshd.rc new file mode 100644 index 0000000000000000000000000000000000000000..f9a10999b01c528cbc444c63f658f99961563256 --- /dev/null +++ b/aosp/external/openssh/contrib/hpux/sshd.rc @@ -0,0 +1,90 @@ +#!/sbin/sh + +# +# sshd.rc: SSH daemon start-up and shutdown script +# + +# Allowed exit values: +# 0 = success; causes "OK" to show up in checklist. +# 1 = failure; causes "FAIL" to show up in checklist. +# 2 = skip; causes "N/A" to show up in the checklist. +# Use this value if execution of this script is overridden +# by the use of a control variable, or if this script is not +# appropriate to execute for some other reason. +# 3 = reboot; causes the system to be rebooted after execution. + +# Input and output: +# stdin is redirected from /dev/null +# +# stdout and stderr are redirected to the /etc/rc.log file +# during checklist mode, or to the console in raw mode. + +PATH=/usr/sbin:/usr/bin:/sbin +export PATH + +WHAT='OpenSSH' +WHAT_PATH=/opt/openssh/sbin/sshd +WHAT_PID=/var/run/sshd.pid +WHAT_CONFIG=/etc/rc.config.d/sshd + +# NOTE: If your script executes in run state 0 or state 1, then /usr might +# not be available. Do not attempt to access commands or files in +# /usr unless your script executes in run state 2 or greater. Other +# file systems typically not mounted until run state 2 include /var +# and /opt. + +rval=0 + +# Check the exit value of a command run by this script. If non-zero, the +# exit code is echoed to the log file and the return value of this script +# is set to indicate failure. + +set_return() { + x=$? + if [ $x -ne 0 ]; then + echo "EXIT CODE: $x" + rval=1 # script FAILed + fi +} + +case $1 in +'start_msg') + echo "Starting $WHAT" + ;; + +'stop_msg') + echo "Stopping $WHAT" + ;; + +'start') + if [ -f $WHAT_CONFIG ] ; then + . $WHAT_CONFIG + else + echo "ERROR: $WHAT_CONFIG defaults file MISSING" + fi + + if [ "$SSHD_START" -eq 1 -a -x "$WHAT_PATH" ]; then + $WHAT_PATH $SSHD_ARGS && echo "$WHAT started" + set_return + else + rval=2 + fi + ;; + +'stop') + if kill `cat $WHAT_PID`; then + echo "$WHAT stopped" + else + rval=1 + echo "Unable to stop $WHAT" + fi + set_return + ;; + +*) + echo "usage: $0 {start|stop|start_msg|stop_msg}" + rval=1 + ;; +esac + +exit $rval diff --git a/aosp/external/openssh/contrib/redhat/gnome-ssh-askpass.csh b/aosp/external/openssh/contrib/redhat/gnome-ssh-askpass.csh new file mode 100644 index 0000000000000000000000000000000000000000..dd77712cdb3ace25992b38c1e65e24ea5108ad60 --- /dev/null +++ b/aosp/external/openssh/contrib/redhat/gnome-ssh-askpass.csh @@ -0,0 +1 @@ +setenv SSH_ASKPASS /usr/libexec/openssh/gnome-ssh-askpass diff --git a/aosp/external/openssh/contrib/redhat/gnome-ssh-askpass.sh b/aosp/external/openssh/contrib/redhat/gnome-ssh-askpass.sh new file mode 100644 index 0000000000000000000000000000000000000000..355189f45cbe75e4481dcbd6743cb6147fbd0693 --- /dev/null +++ b/aosp/external/openssh/contrib/redhat/gnome-ssh-askpass.sh @@ -0,0 +1,2 @@ +SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass +export SSH_ASKPASS diff --git a/aosp/external/openssh/contrib/redhat/openssh.spec b/aosp/external/openssh/contrib/redhat/openssh.spec new file mode 100644 index 0000000000000000000000000000000000000000..b8ba8bae046918dedb8849bdf76c377c1cb53062 --- /dev/null +++ b/aosp/external/openssh/contrib/redhat/openssh.spec @@ -0,0 +1,850 @@ +%global ver 9.0p1 +%global rel 1%{?dist} + +# OpenSSH privilege separation requires a user & group ID +%global sshd_uid 74 +%global sshd_gid 74 + +# Version of ssh-askpass +%global aversion 1.2.4.1 + +# Do we want to disable building of x11-askpass? (1=yes 0=no) +%global no_x11_askpass 0 + +# Do we want to disable building of gnome-askpass? (1=yes 0=no) +%global no_gnome_askpass 0 + +# Do we want to link against a static libcrypto? (1=yes 0=no) +%global static_libcrypto 0 + +# Do we want smartcard support (1=yes 0=no) +%global scard 0 + +# Use GTK2 instead of GNOME in gnome-ssh-askpass +%global gtk2 1 + +# Use build6x options for older RHEL builds +# RHEL 7 not yet supported +%if 0%{?rhel} > 6 +%global build6x 0 +%else +%global build6x 1 +%endif + +%if 0%{?fedora} >= 26 +%global compat_openssl 1 +%else +%global compat_openssl 0 +%endif + +# Do we want kerberos5 support (1=yes 0=no) +%global kerberos5 1 + +# Reserve options to override askpass settings with: +# rpm -ba|--rebuild --define 'skip_xxx 1' +%{?skip_x11_askpass:%global no_x11_askpass 1} +%{?skip_gnome_askpass:%global no_gnome_askpass 1} + +# Add option to build without GTK2 for older platforms with only GTK+. +# RedHat <= 7.2 and Red Hat Advanced Server 2.1 are examples. +# rpm -ba|--rebuild --define 'no_gtk2 1' +%{?no_gtk2:%global gtk2 0} + +# Is this a build for RHL 6.x or earlier? +%{?build_6x:%global build6x 1} + +# If this is RHL 6.x, the default configuration has sysconfdir in /usr/etc. +%if %{build6x} +%global _sysconfdir /etc +%endif + +# Options for static OpenSSL link: +# rpm -ba|--rebuild --define "static_openssl 1" +%{?static_openssl:%global static_libcrypto 1} + +# Options for Smartcard support: (needs libsectok and openssl-engine) +# rpm -ba|--rebuild --define "smartcard 1" +%{?smartcard:%global scard 1} + +# Is this a build for the rescue CD (without PAM)? (1=yes 0=no) +%global rescue 0 +%{?build_rescue:%global rescue 1} + +# Turn off some stuff for resuce builds +%if %{rescue} +%global kerberos5 0 +%endif + +Summary: The OpenSSH implementation of SSH protocol version 2. +Name: openssh +Version: %{ver} +%if %{rescue} +Release: %{rel}rescue +%else +Release: %{rel} +%endif +URL: https://www.openssh.com/portable.html +Source0: https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz +Source1: http://www.jmknoble.net/software/x11-ssh-askpass/x11-ssh-askpass-%{aversion}.tar.gz +License: BSD +Group: Applications/Internet +BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot +Obsoletes: ssh +%if %{build6x} +PreReq: initscripts >= 5.00 +%else +Requires: initscripts >= 5.20 +%endif +BuildRequires: perl +%if %{compat_openssl} +BuildRequires: compat-openssl10-devel +%else +BuildRequires: openssl-devel >= 1.0.1 +BuildRequires: openssl-devel < 1.1 +%endif +BuildRequires: /bin/login +%if ! %{build6x} +BuildRequires: glibc-devel, pam +%else +BuildRequires: /usr/include/security/pam_appl.h +%endif +%if ! %{no_x11_askpass} +BuildRequires: /usr/include/X11/Xlib.h +# Xt development tools +BuildRequires: libXt-devel +# Provides xmkmf +BuildRequires: imake +# Rely on relatively recent gtk +BuildRequires: gtk2-devel +%endif +%if ! %{no_gnome_askpass} +BuildRequires: pkgconfig +%endif +%if %{kerberos5} +BuildRequires: krb5-devel +BuildRequires: krb5-libs +%endif + +%package clients +Summary: OpenSSH clients. +Requires: openssh = %{version}-%{release} +Group: Applications/Internet +Obsoletes: ssh-clients + +%package server +Summary: The OpenSSH server daemon. +Group: System Environment/Daemons +Obsoletes: ssh-server +Requires: openssh = %{version}-%{release}, chkconfig >= 0.9 +%if ! %{build6x} +Requires: /etc/pam.d/system-auth +%endif + +%package askpass +Summary: A passphrase dialog for OpenSSH and X. +Group: Applications/Internet +Requires: openssh = %{version}-%{release} +Obsoletes: ssh-extras + +%package askpass-gnome +Summary: A passphrase dialog for OpenSSH, X, and GNOME. +Group: Applications/Internet +Requires: openssh = %{version}-%{release} +Obsoletes: ssh-extras + +%description +SSH (Secure SHell) is a program for logging into and executing +commands on a remote machine. SSH is intended to replace rlogin and +rsh, and to provide secure encrypted communications between two +untrusted hosts over an insecure network. X11 connections and +arbitrary TCP/IP ports can also be forwarded over the secure channel. + +OpenSSH is OpenBSD's version of the last free version of SSH, bringing +it up to date in terms of security and features, as well as removing +all patented algorithms to separate libraries. + +This package includes the core files necessary for both the OpenSSH +client and server. To make this package useful, you should also +install openssh-clients, openssh-server, or both. + +%description clients +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package includes +the clients necessary to make encrypted connections to SSH servers. +You'll also need to install the openssh package on OpenSSH clients. + +%description server +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package contains +the secure shell daemon (sshd). The sshd daemon allows SSH clients to +securely connect to your SSH server. You also need to have the openssh +package installed. + +%description askpass +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package contains +an X11 passphrase dialog for OpenSSH. + +%description askpass-gnome +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package contains +an X11 passphrase dialog for OpenSSH and the GNOME GUI desktop +environment. + +%prep + +%if ! %{no_x11_askpass} +%setup -q -a 1 +%else +%setup -q +%endif + +%build +%if %{rescue} +CFLAGS="$RPM_OPT_FLAGS -Os"; export CFLAGS +%endif + +%configure \ + --sysconfdir=%{_sysconfdir}/ssh \ + --libexecdir=%{_libexecdir}/openssh \ + --datadir=%{_datadir}/openssh \ + --with-default-path=/usr/local/bin:/bin:/usr/bin \ + --with-superuser-path=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin \ + --with-privsep-path=%{_var}/empty/sshd \ + --mandir=%{_mandir} \ + --with-mantype=man \ + --disable-strip \ +%if %{scard} + --with-smartcard \ +%endif +%if %{rescue} + --without-pam \ +%else + --with-pam \ +%endif +%if %{kerberos5} + --with-kerberos5=$K5DIR \ +%endif + + +%if %{static_libcrypto} +perl -pi -e "s|-lcrypto|%{_libdir}/libcrypto.a|g" Makefile +%endif + +make + +%if ! %{no_x11_askpass} +pushd x11-ssh-askpass-%{aversion} +%configure --libexecdir=%{_libexecdir}/openssh +xmkmf -a +make +popd +%endif + +# Define a variable to toggle gnome1/gtk2 building. This is necessary +# because RPM doesn't handle nested %if statements. +%if %{gtk2} + gtk2=yes +%else + gtk2=no +%endif + +%if ! %{no_gnome_askpass} +pushd contrib +if [ $gtk2 = yes ] ; then + make gnome-ssh-askpass2 + mv gnome-ssh-askpass2 gnome-ssh-askpass +else + make gnome-ssh-askpass1 + mv gnome-ssh-askpass1 gnome-ssh-askpass +fi +popd +%endif + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/ssh +mkdir -p -m755 $RPM_BUILD_ROOT%{_libexecdir}/openssh +mkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/sshd + +make install DESTDIR=$RPM_BUILD_ROOT + +install -d $RPM_BUILD_ROOT/etc/pam.d/ +install -d $RPM_BUILD_ROOT/etc/rc.d/init.d +install -d $RPM_BUILD_ROOT%{_libexecdir}/openssh +%if %{build6x} +install -m644 contrib/redhat/sshd.pam.old $RPM_BUILD_ROOT/etc/pam.d/sshd +%else +install -m644 contrib/redhat/sshd.pam $RPM_BUILD_ROOT/etc/pam.d/sshd +%endif +install -m755 contrib/redhat/sshd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/sshd + +%if ! %{no_x11_askpass} +install x11-ssh-askpass-%{aversion}/x11-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/x11-ssh-askpass +ln -s x11-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/ssh-askpass +%endif + +%if ! %{no_gnome_askpass} +install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass +%endif + +%if ! %{scard} + rm -f $RPM_BUILD_ROOT/usr/share/openssh/Ssh.bin +%endif + +%if ! %{no_gnome_askpass} +install -m 755 -d $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ +install -m 755 contrib/redhat/gnome-ssh-askpass.csh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ +install -m 755 contrib/redhat/gnome-ssh-askpass.sh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ +%endif + +perl -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_mandir}/man*/* + +%clean +rm -rf $RPM_BUILD_ROOT + +%triggerun server -- ssh-server +if [ "$1" != 0 -a -r /var/run/sshd.pid ] ; then + touch /var/run/sshd.restart +fi + +%triggerun server -- openssh-server < 2.5.0p1 +# Count the number of HostKey and HostDsaKey statements we have. +gawk 'BEGIN {IGNORECASE=1} + /^hostkey/ || /^hostdsakey/ {sawhostkey = sawhostkey + 1} + END {exit sawhostkey}' /etc/ssh/sshd_config +# And if we only found one, we know the client was relying on the old default +# behavior, which loaded the the SSH2 DSA host key when HostDsaKey wasn't +# specified. Now that HostKey is used for both SSH1 and SSH2 keys, specifying +# one nullifies the default, which would have loaded both. +if [ $? -eq 1 ] ; then + echo HostKey /etc/ssh/ssh_host_rsa_key >> /etc/ssh/sshd_config + echo HostKey /etc/ssh/ssh_host_dsa_key >> /etc/ssh/sshd_config +fi + +%triggerpostun server -- ssh-server +if [ "$1" != 0 ] ; then + /sbin/chkconfig --add sshd + if test -f /var/run/sshd.restart ; then + rm -f /var/run/sshd.restart + /sbin/service sshd start > /dev/null 2>&1 || : + fi +fi + +%pre server +%{_sbindir}/groupadd -r -g %{sshd_gid} sshd 2>/dev/null || : +%{_sbindir}/useradd -d /var/empty/sshd -s /bin/false -u %{sshd_uid} \ + -g sshd -M -r sshd 2>/dev/null || : + +%post server +/sbin/chkconfig --add sshd + +%postun server +/sbin/service sshd condrestart > /dev/null 2>&1 || : + +%preun server +if [ "$1" = 0 ] +then + /sbin/service sshd stop > /dev/null 2>&1 || : + /sbin/chkconfig --del sshd +fi + +%files +%defattr(-,root,root) +%doc CREDITS ChangeLog INSTALL LICENCE OVERVIEW README* PROTOCOL* TODO +%attr(0755,root,root) %{_bindir}/scp +%attr(0644,root,root) %{_mandir}/man1/scp.1* +%attr(0755,root,root) %dir %{_sysconfdir}/ssh +%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli +%if ! %{rescue} +%attr(0755,root,root) %{_bindir}/ssh-keygen +%attr(0644,root,root) %{_mandir}/man1/ssh-keygen.1* +%attr(0755,root,root) %dir %{_libexecdir}/openssh +%attr(4711,root,root) %{_libexecdir}/openssh/ssh-keysign +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-pkcs11-helper +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-sk-helper +%attr(0644,root,root) %{_mandir}/man8/ssh-keysign.8* +%attr(0644,root,root) %{_mandir}/man8/ssh-pkcs11-helper.8* +%attr(0644,root,root) %{_mandir}/man8/ssh-sk-helper.8* +%endif +%if %{scard} +%attr(0755,root,root) %dir %{_datadir}/openssh +%attr(0644,root,root) %{_datadir}/openssh/Ssh.bin +%endif + +%files clients +%defattr(-,root,root) +%attr(0755,root,root) %{_bindir}/ssh +%attr(0644,root,root) %{_mandir}/man1/ssh.1* +%attr(0644,root,root) %{_mandir}/man5/ssh_config.5* +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config +%if ! %{rescue} +%attr(2755,root,nobody) %{_bindir}/ssh-agent +%attr(0755,root,root) %{_bindir}/ssh-add +%attr(0755,root,root) %{_bindir}/ssh-keyscan +%attr(0755,root,root) %{_bindir}/sftp +%attr(0644,root,root) %{_mandir}/man1/ssh-agent.1* +%attr(0644,root,root) %{_mandir}/man1/ssh-add.1* +%attr(0644,root,root) %{_mandir}/man1/ssh-keyscan.1* +%attr(0644,root,root) %{_mandir}/man1/sftp.1* +%endif + +%if ! %{rescue} +%files server +%defattr(-,root,root) +%dir %attr(0111,root,root) %{_var}/empty/sshd +%attr(0755,root,root) %{_sbindir}/sshd +%attr(0755,root,root) %{_libexecdir}/openssh/sftp-server +%attr(0644,root,root) %{_mandir}/man8/sshd.8* +%attr(0644,root,root) %{_mandir}/man5/moduli.5* +%attr(0644,root,root) %{_mandir}/man5/sshd_config.5* +%attr(0644,root,root) %{_mandir}/man8/sftp-server.8* +%attr(0755,root,root) %dir %{_sysconfdir}/ssh +%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config +%attr(0600,root,root) %config(noreplace) /etc/pam.d/sshd +%attr(0755,root,root) %config /etc/rc.d/init.d/sshd +%endif + +%if ! %{no_x11_askpass} +%files askpass +%defattr(-,root,root) +%doc x11-ssh-askpass-%{aversion}/README +%doc x11-ssh-askpass-%{aversion}/ChangeLog +%doc x11-ssh-askpass-%{aversion}/SshAskpass*.ad +%{_libexecdir}/openssh/ssh-askpass +%attr(0755,root,root) %{_libexecdir}/openssh/x11-ssh-askpass +%endif + +%if ! %{no_gnome_askpass} +%files askpass-gnome +%defattr(-,root,root) +%attr(0755,root,root) %config %{_sysconfdir}/profile.d/gnome-ssh-askpass.* +%attr(0755,root,root) %{_libexecdir}/openssh/gnome-ssh-askpass +%endif + +%changelog +* Thu Oct 28 2021 Damien Miller +- Remove remaining traces of --with-md5-passwords + +* Mon Jul 20 2020 Damien Miller +- Add ssh-sk-helper and corresponding manual page. + +* Sat Feb 10 2018 Darren Tucker +- Update openssl-devel dependency to match current requirements. +- Handle Fedora >=6 openssl 1.0 compat libs. +- Remove SSH1 from description. +- Don't strip binaries at build time so that debuginfo package can be + created. + +* Sun Nov 16 2014 Nico Kadel-Garcia +- Add '--mandir' and '--with-mantype' for RHEL 5 compatibility +- Add 'dist' option to 'ver' so package names reflect OS at build time +- Always include x11-ssh-askpass tarball in SRPM +- Add openssh-x11-aspass BuildRequires for libXT-devel, imake, gtk2-devel +- Discard 'K5DIR' reporting, not usable inside 'mock' for RHEL 5 compatibility +- Discard obsolete '--with-rsh' configure option +- Update openssl-devel dependency to 0.9.8f, as found in autoconf + +* Wed Jul 14 2010 Tim Rice +- test for skip_x11_askpass (line 77) should have been for no_x11_askpass + +* Mon Jun 2 2003 Damien Miller +- Remove noip6 option. This may be controlled at run-time in client config + file using new AddressFamily directive + +* Mon May 12 2003 Damien Miller +- Don't install profile.d scripts when not building with GNOME/GTK askpass + (patch from bet@rahul.net) + +* Tue Oct 01 2002 Damien Miller +- Install ssh-agent setgid nobody to prevent ptrace() key theft attacks + +* Mon Sep 30 2002 Damien Miller +- Use contrib/ Makefile for building askpass programs + +* Fri Jun 21 2002 Damien Miller +- Merge in spec changes from seba@iq.pl (Sebastian Pachuta) +- Add new {ssh,sshd}_config.5 manpages +- Add new ssh-keysign program and remove setuid from ssh client + +* Fri May 10 2002 Damien Miller +- Merge in spec changes from RedHat, reorgansie a little +- Add Privsep user, group and directory + +* Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-2 +- bump and grind (through the build system) + +* Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-1 +- require sharutils for building (mindrot #137) +- require db1-devel only when building for 6.x (#55105), which probably won't + work anyway (3.1 requires OpenSSL 0.9.6 to build), but what the heck +- require pam-devel by file (not by package name) again +- add Markus's patch to compile with OpenSSL 0.9.5a (from + http://bugzilla.mindrot.org/show_bug.cgi?id=141) and apply it if we're + building for 6.x + +* Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-0 +- update to 3.1p1 + +* Tue Mar 5 2002 Nalin Dahyabhai SNAP-20020305 +- update to SNAP-20020305 +- drop debug patch, fixed upstream + +* Wed Feb 20 2002 Nalin Dahyabhai SNAP-20020220 +- update to SNAP-20020220 for testing purposes (you've been warned, if there's + anything to be warned about, gss patches won't apply, I don't mind) + +* Wed Feb 13 2002 Nalin Dahyabhai 3.0.2p1-3 +- add patches from Simon Wilkinson and Nicolas Williams for GSSAPI key + exchange, authentication, and named key support + +* Wed Jan 23 2002 Nalin Dahyabhai 3.0.2p1-2 +- remove dependency on db1-devel, which has just been swallowed up whole + by gnome-libs-devel + +* Sat Dec 29 2001 Nalin Dahyabhai +- adjust build dependencies so that build6x actually works right (fix + from Hugo van der Kooij) + +* Tue Dec 4 2001 Nalin Dahyabhai 3.0.2p1-1 +- update to 3.0.2p1 + +* Fri Nov 16 2001 Nalin Dahyabhai 3.0.1p1-1 +- update to 3.0.1p1 + +* Tue Nov 13 2001 Nalin Dahyabhai +- update to current CVS (not for use in distribution) + +* Thu Nov 8 2001 Nalin Dahyabhai 3.0p1-1 +- merge some of Damien Miller changes from the upstream + 3.0p1 spec file and init script + +* Wed Nov 7 2001 Nalin Dahyabhai +- update to 3.0p1 +- update to x11-ssh-askpass 1.2.4.1 +- change build dependency on a file from pam-devel to the pam-devel package +- replace primes with moduli + +* Thu Sep 27 2001 Nalin Dahyabhai 2.9p2-9 +- incorporate fix from Markus Friedl's advisory for IP-based authorization bugs + +* Thu Sep 13 2001 Bernhard Rosenkraenzer 2.9p2-8 +- Merge changes to rescue build from current sysadmin survival cd + +* Thu Sep 6 2001 Nalin Dahyabhai 2.9p2-7 +- fix scp's server's reporting of file sizes, and build with the proper + preprocessor define to get large-file capable open(), stat(), etc. + (sftp has been doing this correctly all along) (#51827) +- configure without --with-ipv4-default on RHL 7.x and newer (#45987,#52247) +- pull cvs patch to fix support for /etc/nologin for non-PAM logins (#47298) +- mark profile.d scriptlets as config files (#42337) +- refer to Jason Stone's mail for zsh workaround for exit-hanging quasi-bug +- change a couple of log() statements to debug() statements (#50751) +- pull cvs patch to add -t flag to sshd (#28611) +- clear fd_sets correctly (one bit per FD, not one byte per FD) (#43221) + +* Mon Aug 20 2001 Nalin Dahyabhai 2.9p2-6 +- add db1-devel as a BuildPrerequisite (noted by Hans Ecke) + +* Thu Aug 16 2001 Nalin Dahyabhai +- pull cvs patch to fix remote port forwarding with protocol 2 + +* Thu Aug 9 2001 Nalin Dahyabhai +- pull cvs patch to add session initialization to no-pty sessions +- pull cvs patch to not cut off challengeresponse auth needlessly +- refuse to do X11 forwarding if xauth isn't there, handy if you enable + it by default on a system that doesn't have X installed (#49263) + +* Wed Aug 8 2001 Nalin Dahyabhai +- don't apply patches to code we don't intend to build (spotted by Matt Galgoci) + +* Mon Aug 6 2001 Nalin Dahyabhai +- pass OPTIONS correctly to initlog (#50151) + +* Wed Jul 25 2001 Nalin Dahyabhai +- switch to x11-ssh-askpass 1.2.2 + +* Wed Jul 11 2001 Nalin Dahyabhai +- rebuild in new environment + +* Mon Jun 25 2001 Nalin Dahyabhai +- disable the gssapi patch + +* Mon Jun 18 2001 Nalin Dahyabhai +- update to 2.9p2 +- refresh to a new version of the gssapi patch + +* Thu Jun 7 2001 Nalin Dahyabhai +- change Copyright: BSD to License: BSD +- add Markus Friedl's unverified patch for the cookie file deletion problem + so that we can verify it +- drop patch to check if xauth is present (was folded into cookie patch) +- don't apply gssapi patches for the errata candidate +- clear supplemental groups list at startup + +* Fri May 25 2001 Nalin Dahyabhai +- fix an error parsing the new default sshd_config +- add a fix from Markus Friedl (via openssh-unix-dev) for ssh-keygen not + dealing with comments right + +* Thu May 24 2001 Nalin Dahyabhai +- add in Simon Wilkinson's GSSAPI patch to give it some testing in-house, + to be removed before the next beta cycle because it's a big departure + from the upstream version + +* Thu May 3 2001 Nalin Dahyabhai +- finish marking strings in the init script for translation +- modify init script to source /etc/sysconfig/sshd and pass $OPTIONS to sshd + at startup (change merged from openssh.com init script, originally by + Pekka Savola) +- refuse to do X11 forwarding if xauth isn't there, handy if you enable + it by default on a system that doesn't have X installed + +* Wed May 2 2001 Nalin Dahyabhai +- update to 2.9 +- drop various patches that came from or went upstream or to or from CVS + +* Wed Apr 18 2001 Nalin Dahyabhai +- only require initscripts 5.00 on 6.2 (reported by Peter Bieringer) + +* Sun Apr 8 2001 Preston Brown +- remove explicit openssl requirement, fixes builddistro issue +- make initscript stop() function wait until sshd really dead to avoid + races in condrestart + +* Mon Apr 2 2001 Nalin Dahyabhai +- mention that challengereponse supports PAM, so disabling password doesn't + limit users to pubkey and rsa auth (#34378) +- bypass the daemon() function in the init script and call initlog directly, + because daemon() won't start a daemon it detects is already running (like + open connections) +- require the version of openssl we had when we were built + +* Fri Mar 23 2001 Nalin Dahyabhai +- make do_pam_setcred() smart enough to know when to establish creds and + when to reinitialize them +- add in a couple of other fixes from Damien for inclusion in the errata + +* Thu Mar 22 2001 Nalin Dahyabhai +- update to 2.5.2p2 +- call setcred() again after initgroups, because the "creds" could actually + be group memberships + +* Tue Mar 20 2001 Nalin Dahyabhai +- update to 2.5.2p1 (includes endianness fixes in the rijndael implementation) +- don't enable challenge-response by default until we find a way to not + have too many userauth requests (we may make up to six pubkey and up to + three password attempts as it is) +- remove build dependency on rsh to match openssh.com's packages more closely + +* Sat Mar 3 2001 Nalin Dahyabhai +- remove dependency on openssl -- would need to be too precise + +* Fri Mar 2 2001 Nalin Dahyabhai +- rebuild in new environment + +* Mon Feb 26 2001 Nalin Dahyabhai +- Revert the patch to move pam_open_session. +- Init script and spec file changes from Pekka Savola. (#28750) +- Patch sftp to recognize '-o protocol' arguments. (#29540) + +* Thu Feb 22 2001 Nalin Dahyabhai +- Chuck the closing patch. +- Add a trigger to add host keys for protocol 2 to the config file, now that + configuration file syntax requires us to specify it with HostKey if we + specify any other HostKey values, which we do. + +* Tue Feb 20 2001 Nalin Dahyabhai +- Redo patch to move pam_open_session after the server setuid()s to the user. +- Rework the nopam patch to use be picked up by autoconf. + +* Mon Feb 19 2001 Nalin Dahyabhai +- Update for 2.5.1p1. +- Add init script mods from Pekka Savola. +- Tweak the init script to match the CVS contrib script more closely. +- Redo patch to ssh-add to try to adding both identity and id_dsa to also try + adding id_rsa. + +* Fri Feb 16 2001 Nalin Dahyabhai +- Update for 2.5.0p1. +- Use $RPM_OPT_FLAGS instead of -O when building gnome-ssh-askpass +- Resync with parts of Damien Miller's openssh.spec from CVS, including + update of x11 askpass to 1.2.0. +- Only require openssl (don't prereq) because we generate keys in the init + script now. + +* Tue Feb 13 2001 Nalin Dahyabhai +- Don't open a PAM session until we've forked and become the user (#25690). +- Apply Andrew Bartlett's patch for letting pam_authenticate() know which + host the user is attempting a login from. +- Resync with parts of Damien Miller's openssh.spec from CVS. +- Don't expose KbdInt responses in debug messages (from CVS). +- Detect and handle errors in rsa_{public,private}_decrypt (from CVS). + +* Wed Feb 7 2001 Trond Eivind Glomsrxd +- i18n-tweak to initscript. + +* Tue Jan 23 2001 Nalin Dahyabhai +- More gettextizing. +- Close all files after going into daemon mode (needs more testing). +- Extract patch from CVS to handle auth banners (in the client). +- Extract patch from CVS to handle compat weirdness. + +* Fri Jan 19 2001 Nalin Dahyabhai +- Finish with the gettextizing. + +* Thu Jan 18 2001 Nalin Dahyabhai +- Fix a bug in auth2-pam.c (#23877) +- Gettextize the init script. + +* Wed Dec 20 2000 Nalin Dahyabhai +- Incorporate a switch for using PAM configs for 6.x, just in case. + +* Tue Dec 5 2000 Nalin Dahyabhai +- Incorporate Bero's changes for a build specifically for rescue CDs. + +* Wed Nov 29 2000 Nalin Dahyabhai +- Don't treat pam_setcred() failure as fatal unless pam_authenticate() has + succeeded, to allow public-key authentication after a failure with "none" + authentication. (#21268) + +* Tue Nov 28 2000 Nalin Dahyabhai +- Update to x11-askpass 1.1.1. (#21301) +- Don't second-guess fixpaths, which causes paths to get fixed twice. (#21290) + +* Mon Nov 27 2000 Nalin Dahyabhai +- Merge multiple PAM text messages into subsequent prompts when possible when + doing keyboard-interactive authentication. + +* Sun Nov 26 2000 Nalin Dahyabhai +- Disable the built-in MD5 password support. We're using PAM. +- Take a crack at doing keyboard-interactive authentication with PAM, and + enable use of it in the default client configuration so that the client + will try it when the server disallows password authentication. +- Build with debugging flags. Build root policies strip all binaries anyway. + +* Tue Nov 21 2000 Nalin Dahyabhai +- Use DESTDIR instead of %%makeinstall. +- Remove /usr/X11R6/bin from the path-fixing patch. + +* Mon Nov 20 2000 Nalin Dahyabhai +- Add the primes file from the latest snapshot to the main package (#20884). +- Add the dev package to the prereq list (#19984). +- Remove the default path and mimic login's behavior in the server itself. + +* Fri Nov 17 2000 Nalin Dahyabhai +- Resync with conditional options in Damien Miller's .spec file for an errata. +- Change libexecdir from %%{_libexecdir}/ssh to %%{_libexecdir}/openssh. + +* Tue Nov 7 2000 Nalin Dahyabhai +- Update to OpenSSH 2.3.0p1. +- Update to x11-askpass 1.1.0. +- Enable keyboard-interactive authentication. + +* Mon Oct 30 2000 Nalin Dahyabhai +- Update to ssh-askpass-x11 1.0.3. +- Change authentication related messages to be private (#19966). + +* Tue Oct 10 2000 Nalin Dahyabhai +- Patch ssh-keygen to be able to list signatures for DSA public key files + it generates. + +* Thu Oct 5 2000 Nalin Dahyabhai +- Add BuildRequires on /usr/include/security/pam_appl.h to be sure we always + build PAM authentication in. +- Try setting SSH_ASKPASS if gnome-ssh-askpass is installed. +- Clean out no-longer-used patches. +- Patch ssh-add to try to add both identity and id_dsa, and to error only + when neither exists. + +* Mon Oct 2 2000 Nalin Dahyabhai +- Update x11-askpass to 1.0.2. (#17835) +- Add BuildRequiress for /bin/login and /usr/bin/rsh so that configure will + always find them in the right place. (#17909) +- Set the default path to be the same as the one supplied by /bin/login, but + add /usr/X11R6/bin. (#17909) +- Try to handle obsoletion of ssh-server more cleanly. Package names + are different, but init script name isn't. (#17865) + +* Wed Sep 6 2000 Nalin Dahyabhai +- Update to 2.2.0p1. (#17835) +- Tweak the init script to allow proper restarting. (#18023) + +* Wed Aug 23 2000 Nalin Dahyabhai +- Update to 20000823 snapshot. +- Change subpackage requirements from %%{version} to %%{version}-%%{release} +- Back out the pipe patch. + +* Mon Jul 17 2000 Nalin Dahyabhai +- Update to 2.1.1p4, which includes fixes for config file parsing problems. +- Move the init script back. +- Add Damien's quick fix for wackiness. + +* Wed Jul 12 2000 Nalin Dahyabhai +- Update to 2.1.1p3, which includes fixes for X11 forwarding and strtok(). + +* Thu Jul 6 2000 Nalin Dahyabhai +- Move condrestart to server postun. +- Move key generation to init script. +- Actually use the right patch for moving the key generation to the init script. +- Clean up the init script a bit. + +* Wed Jul 5 2000 Nalin Dahyabhai +- Fix X11 forwarding, from mail post by Chan Shih-Ping Richard. + +* Sun Jul 2 2000 Nalin Dahyabhai +- Update to 2.1.1p2. +- Use of strtok() considered harmful. + +* Sat Jul 1 2000 Nalin Dahyabhai +- Get the build root out of the man pages. + +* Thu Jun 29 2000 Nalin Dahyabhai +- Add and use condrestart support in the init script. +- Add newer initscripts as a prereq. + +* Tue Jun 27 2000 Nalin Dahyabhai +- Build in new environment (release 2) +- Move -clients subpackage to Applications/Internet group + +* Fri Jun 9 2000 Nalin Dahyabhai +- Update to 2.2.1p1 + +* Sat Jun 3 2000 Nalin Dahyabhai +- Patch to build with neither RSA nor RSAref. +- Miscellaneous FHS-compliance tweaks. +- Fix for possibly-compressed man pages. + +* Wed Mar 15 2000 Damien Miller +- Updated for new location +- Updated for new gnome-ssh-askpass build + +* Sun Dec 26 1999 Damien Miller +- Added Jim Knoble's askpass + +* Mon Nov 15 1999 Damien Miller +- Split subpackages further based on patch from jim knoble + +* Sat Nov 13 1999 Damien Miller +- Added 'Obsoletes' directives + +* Tue Nov 09 1999 Damien Miller +- Use make install +- Subpackages + +* Mon Nov 08 1999 Damien Miller +- Added links for slogin +- Fixed perms on manpages + +* Sat Oct 30 1999 Damien Miller +- Renamed init script + +* Fri Oct 29 1999 Damien Miller +- Back to old binary names + +* Thu Oct 28 1999 Damien Miller +- Use autoconf +- New binary names + +* Wed Oct 27 1999 Damien Miller +- Initial RPMification, based on Jan "Yenya" Kasprzak's spec. diff --git a/aosp/external/openssh/contrib/redhat/sshd.init b/aosp/external/openssh/contrib/redhat/sshd.init new file mode 100644 index 0000000000000000000000000000000000000000..8ee5fcd3bb4f3d248c6506c5a591fbf6534f0e9c --- /dev/null +++ b/aosp/external/openssh/contrib/redhat/sshd.init @@ -0,0 +1,105 @@ +#!/bin/bash +# +# Init file for OpenSSH server daemon +# +# chkconfig: 2345 55 25 +# description: OpenSSH server daemon +# +# processname: sshd +# config: /etc/ssh/ssh_host_key +# config: /etc/ssh/ssh_host_key.pub +# config: /etc/ssh/ssh_random_seed +# config: /etc/ssh/sshd_config +# pidfile: /var/run/sshd.pid + +# source function library +. /etc/rc.d/init.d/functions + +# pull in sysconfig settings +[ -f /etc/sysconfig/sshd ] && . /etc/sysconfig/sshd + +RETVAL=0 +prog="sshd" + +# Some functions to make the below more readable +SSHD=/usr/sbin/sshd +PID_FILE=/var/run/sshd.pid + +do_restart_sanity_check() +{ + $SSHD -t + RETVAL=$? + if [ $RETVAL -ne 0 ]; then + failure $"Configuration file or keys are invalid" + echo + fi +} + +start() +{ + # Create keys if necessary + /usr/bin/ssh-keygen -A + if [ -x /sbin/restorecon ]; then + /sbin/restorecon /etc/ssh/ssh_host_rsa_key.pub + /sbin/restorecon /etc/ssh/ssh_host_dsa_key.pub + /sbin/restorecon /etc/ssh/ssh_host_ecdsa_key.pub + fi + + echo -n $"Starting $prog:" + $SSHD $OPTIONS && success || failure + RETVAL=$? + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sshd + echo +} + +stop() +{ + echo -n $"Stopping $prog:" + killproc $SSHD -TERM + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sshd + echo +} + +reload() +{ + echo -n $"Reloading $prog:" + killproc $SSHD -HUP + RETVAL=$? + echo +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + reload) + reload + ;; + condrestart) + if [ -f /var/lock/subsys/sshd ] ; then + do_restart_sanity_check + if [ $RETVAL -eq 0 ] ; then + stop + # avoid race + sleep 3 + start + fi + fi + ;; + status) + status $SSHD + RETVAL=$? + ;; + *) + echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" + RETVAL=1 +esac +exit $RETVAL diff --git a/aosp/external/openssh/contrib/redhat/sshd.pam b/aosp/external/openssh/contrib/redhat/sshd.pam new file mode 100644 index 0000000000000000000000000000000000000000..ffa5adbe58e581d0918e12cdc6a0c936b41f0e89 --- /dev/null +++ b/aosp/external/openssh/contrib/redhat/sshd.pam @@ -0,0 +1,6 @@ +#%PAM-1.0 +auth required pam_stack.so service=system-auth +account required pam_nologin.so +account required pam_stack.so service=system-auth +password required pam_stack.so service=system-auth +session required pam_stack.so service=system-auth diff --git a/aosp/external/openssh/contrib/solaris/README b/aosp/external/openssh/contrib/solaris/README new file mode 100644 index 0000000000000000000000000000000000000000..cabecaa1a5c71f63fda476371e104d6eb0f2239a --- /dev/null +++ b/aosp/external/openssh/contrib/solaris/README @@ -0,0 +1,30 @@ +The following is a new package build script for Solaris. This is being +introduced into OpenSSH 3.0 and above in hopes of simplifying the build +process. As of 3.1p2 the script should work on all platforms that have +SVR4 style package tools. + +The build process is called a 'dummy install'.. Which means the software does +a "make install-nokeys DESTDIR=[fakeroot]". This way all manpages should +be handled correctly and key are deferred until the first time the sshd +is started. + +Directions: + +1. make -F Makefile.in distprep (Only if you are getting from the CVS tree) +2. ./configure --with-pam [..any other options you want..] +3. look at the top of buildpkg.sh for the configurable options and put + any changes you want in openssh-config.local. Additional customizations + can be done to the build process by creating one or more of the following + scripts that will be sourced by buildpkg.sh. + pkg_post_make_install_fixes.sh pkg-post-prototype-edit.sh + pkg-preinstall.local pkg-postinstall.local pkg-preremove.local + pkg-postremove.local pkg-request.local +4. Run "make package" + +If all goes well you should have a solaris package ready to be installed. + +If you have any problems with this script please post them to +openssh-unix-dev@mindrot.org and I will try to assist you as best as I can. + +- Ben Lindstrom + diff --git a/aosp/external/openssh/contrib/ssh-copy-id b/aosp/external/openssh/contrib/ssh-copy-id new file mode 100644 index 0000000000000000000000000000000000000000..cd122def30a1f5d41472224a47d67f6caf856c47 --- /dev/null +++ b/aosp/external/openssh/contrib/ssh-copy-id @@ -0,0 +1,377 @@ +#!/bin/sh + +# Copyright (c) 1999-2020 Philip Hands +# 2020 Matthias Blümel +# 2017 Sebastien Boyron +# 2013 Martin Kletzander +# 2010 Adeodato =?iso-8859-1?Q?Sim=F3?= +# 2010 Eric Moret +# 2009 Xr +# 2007 Justin Pryzby +# 2004 Reini Urban +# 2003 Colin Watson +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Shell script to install your public key(s) on a remote machine +# See the ssh-copy-id(1) man page for details + +# shellcheck shell=dash + +# check that we have something mildly sane as our shell, or try to find something better +if false ^ printf "%s: WARNING: ancient shell, hunting for a more modern one... " "$0" +then + SANE_SH=${SANE_SH:-/usr/bin/ksh} + if printf 'true ^ false\n' | "$SANE_SH" + then + printf "'%s' seems viable.\\n" "$SANE_SH" + exec "$SANE_SH" "$0" "$@" + else + cat <<-EOF + oh dear. + + If you have a more recent shell available, that supports \$(...) etc. + please try setting the environment variable SANE_SH to the path of that + shell, and then retry running this script. If that works, please report + a bug describing your setup, and the shell you used to make it work. + + EOF + printf '%s: ERROR: Less dimwitted shell required.\n' "$0" + exit 1 + fi +fi + +# shellcheck disable=SC2010 +DEFAULT_PUB_ID_FILE=$(ls -t "${HOME}"/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) +SSH="ssh -a -x" +umask 0177 + +usage () { + printf 'Usage: %s [-h|-?|-f|-n|-s] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [[-o ] ...] [user@]hostname\n' "$0" >&2 + printf '\t-f: force mode -- copy keys without trying to check if they are already installed\n' >&2 + printf '\t-n: dry run -- no keys are actually copied\n' >&2 + printf '\t-s: use sftp -- use sftp instead of executing remote-commands. Can be useful if the remote only allows sftp\n' >&2 + printf '\t-h|-?: print this help\n' >&2 + exit 1 +} + +# escape any single quotes in an argument +quote() { + printf '%s\n' "$1" | sed -e "s/'/'\\\\''/g" +} + +use_id_file() { + L_ID_FILE="$1" + + if [ -z "$L_ID_FILE" ] ; then + printf '%s: ERROR: no ID file found\n' "$0" + exit 1 + fi + + if expr "$L_ID_FILE" : '.*\.pub$' >/dev/null ; then + PUB_ID_FILE="$L_ID_FILE" + else + PUB_ID_FILE="$L_ID_FILE.pub" + fi + + [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) + + # check that the files are readable + for f in "$PUB_ID_FILE" ${PRIV_ID_FILE:+"$PRIV_ID_FILE"} ; do + ErrMSG=$( { : < "$f" ; } 2>&1 ) || { + L_PRIVMSG="" + [ "$f" = "$PRIV_ID_FILE" ] && L_PRIVMSG=" (to install the contents of '$PUB_ID_FILE' anyway, look at the -f option)" + printf "\\n%s: ERROR: failed to open ID file '%s': %s\\n" "$0" "$f" "$(printf '%s\n%s\n' "$ErrMSG" "$L_PRIVMSG" | sed -e 's/.*: *//')" + exit 1 + } + done + printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" "$PUB_ID_FILE" >&2 + GET_ID="cat \"$PUB_ID_FILE\"" +} + +if [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then + GET_ID="ssh-add -L" +fi + +while getopts "i:o:p:F:fnsh?" OPT +do + case "$OPT" in + i) + [ "${SEEN_OPT_I}" ] && { + printf '\n%s: ERROR: -i option must not be specified more than once\n\n' "$0" + usage + } + SEEN_OPT_I="yes" + use_id_file "${OPTARG:-$DEFAULT_PUB_ID_FILE}" + ;; + o|p|F) + SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }-$OPT '$(quote "${OPTARG}")'" + ;; + f) + FORCED=1 + ;; + n) + DRY_RUN=1 + ;; + s) + SFTP=sftp + ;; + h|\?) + usage + ;; + esac +done +#shift all args to keep only USER_HOST +shift $((OPTIND-1)) + +if [ $# = 0 ] ; then + usage +fi +if [ $# != 1 ] ; then + printf '%s: ERROR: Too many arguments. Expecting a target hostname, got: %s\n\n' "$0" "$SAVEARGS" >&2 + usage +fi + +# drop trailing colon +USER_HOST="$*" +# tack the hostname onto SSH_OPTS +SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }'$(quote "$USER_HOST")'" +# and populate "$@" for later use (only way to get proper quoting of options) +eval set -- "$SSH_OPTS" + +# shellcheck disable=SC2086 +if [ -z "$(eval $GET_ID)" ] && [ -r "${PUB_ID_FILE:=$DEFAULT_PUB_ID_FILE}" ] ; then + use_id_file "$PUB_ID_FILE" +fi + +# shellcheck disable=SC2086 +if [ -z "$(eval $GET_ID)" ] ; then + printf '%s: ERROR: No identities found\n' "$0" >&2 + exit 1 +fi + +# filter_ids() +# tries to log in using the keys piped to it, and filters out any that work +filter_ids() { + L_SUCCESS="$1" + L_TMP_ID_FILE="$SCRATCH_DIR"/popids_tmp_id + L_OUTPUT_FILE="$SCRATCH_DIR"/popids_output + + # repopulate "$@" inside this function + eval set -- "$SSH_OPTS" + + while read -r ID || [ "$ID" ] ; do + printf '%s\n' "$ID" > "$L_TMP_ID_FILE" + + # the next line assumes $PRIV_ID_FILE only set if using a single id file - this + # assumption will break if we implement the possibility of multiple -i options. + # The point being that if file based, ssh needs the private key, which it cannot + # find if only given the contents of the .pub file in an unrelated tmpfile + $SSH -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \ + -o ControlPath=none \ + -o LogLevel=INFO \ + -o PreferredAuthentications=publickey \ + -o IdentitiesOnly=yes "$@" exit >"$L_OUTPUT_FILE" 2>&1 /dev/null + # this error counts as a success if we're setting up an sftp connection + } + then + : > "$L_TMP_ID_FILE" + else + grep 'Permission denied' "$L_OUTPUT_FILE" >/dev/null || { + sed -e 's/^/ERROR: /' <"$L_OUTPUT_FILE" >"$L_TMP_ID_FILE" + cat >/dev/null #consume the other keys, causing loop to end + } + fi + + cat "$L_TMP_ID_FILE" + done +} + +# populate_new_ids() uses several global variables ($USER_HOST, $SSH_OPTS ...) +# and has the side effect of setting $NEW_IDS +populate_new_ids() { + if [ "$FORCED" ] ; then + # shellcheck disable=SC2086 + NEW_IDS=$(eval $GET_ID) + return + fi + + printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2 + # shellcheck disable=SC2086 + NEW_IDS=$(eval $GET_ID | filter_ids $1) + + if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then + printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2 + exit 1 + fi + if [ -z "$NEW_IDS" ] ; then + printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n' "$0" >&2 + printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' >&2 + exit 0 + fi + printf '%s: INFO: %d key(s) remain to be installed -- if you are prompted now it is to install the new keys\n' "$0" "$(printf '%s\n' "$NEW_IDS" | wc -l)" >&2 +} + +# installkey_sh [target_path] +# produce a one-liner to add the keys to remote authorized_keys file +# optionally takes an alternative path for authorized_keys +installkeys_sh() { + AUTH_KEY_FILE=${1:-.ssh/authorized_keys} + AUTH_KEY_DIR=$(dirname "${AUTH_KEY_FILE}") + + # In setting INSTALLKEYS_SH: + # the tr puts it all on one line (to placate tcsh) + # (hence the excessive use of semi-colons (;) ) + # then in the command: + # cd to be at $HOME, just in case; + # the -z `tail ...` checks for a trailing newline. The echo adds one if was missing + # the cat adds the keys we're getting via STDIN + # and if available restorecon is used to restore the SELinux context + INSTALLKEYS_SH=$(tr '\t\n' ' ' <<-EOF + cd; + umask 077; + mkdir -p "${AUTH_KEY_DIR}" && + { [ -z \`tail -1c ${AUTH_KEY_FILE} 2>/dev/null\` ] || + echo >> "${AUTH_KEY_FILE}" || exit 1; } && + cat >> "${AUTH_KEY_FILE}" || exit 1; + if type restorecon >/dev/null 2>&1; then + restorecon -F "${AUTH_KEY_DIR}" "${AUTH_KEY_FILE}"; + fi + EOF + ) + + # to defend against quirky remote shells: use 'exec sh -c' to get POSIX; + printf "exec sh -c '%s'" "${INSTALLKEYS_SH}" +} + +#shellcheck disable=SC2120 # the 'eval set' confuses this +installkeys_via_sftp() { + + # repopulate "$@" inside this function + eval set -- "$SSH_OPTS" + + L_KEYS=$SCRATCH_DIR/authorized_keys + L_SHARED_CON=$SCRATCH_DIR/master-conn + $SSH -f -N -M -S "$L_SHARED_CON" "$@" + L_CLEANUP="$SSH -S $L_SHARED_CON -O exit 'ignored' >/dev/null 2>&1 ; $SCRATCH_CLEANUP" + #shellcheck disable=SC2064 + trap "$L_CLEANUP" EXIT TERM INT QUIT + sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1 + -get .ssh/authorized_keys $L_KEYS + EOF + # add a newline or create file if it's missing, same like above + [ -z "$(tail -1c "$L_KEYS" 2>/dev/null)" ] || echo >> "$L_KEYS" + # append the keys being piped in here + cat >> "$L_KEYS" + sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1 + -mkdir .ssh + chmod 700 .ssh + put $L_KEYS .ssh/authorized_keys + chmod 600 .ssh/authorized_keys + EOF + #shellcheck disable=SC2064 + eval "$L_CLEANUP" && trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT +} + + +# create a scratch dir for any temporary files needed +if SCRATCH_DIR=$(mktemp -d ~/.ssh/ssh-copy-id.XXXXXXXXXX) && + [ "$SCRATCH_DIR" ] && [ -d "$SCRATCH_DIR" ] +then + chmod 0700 "$SCRATCH_DIR" + SCRATCH_CLEANUP="rm -rf \"$SCRATCH_DIR\"" + #shellcheck disable=SC2064 + trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT +else + printf '%s: ERROR: failed to create required temporary directory under ~/.ssh\n' "$0" >&2 + exit 1 +fi + +REMOTE_VERSION=$($SSH -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 | + sed -ne 's/.*remote software version //p') + +# shellcheck disable=SC2029 +case "$REMOTE_VERSION" in + NetScreen*) + populate_new_ids 1 + for KEY in $(printf "%s" "$NEW_IDS" | cut -d' ' -f2) ; do + KEY_NO=$((KEY_NO + 1)) + printf '%s\n' "$KEY" | grep ssh-dss >/dev/null || { + printf '%s: WARNING: Non-dsa key (#%d) skipped (NetScreen only supports DSA keys)\n' "$0" "$KEY_NO" >&2 + continue + } + [ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | $SSH -T "$@" >/dev/null 2>&1 + if [ $? = 255 ] ; then + printf '%s: ERROR: installation of key #%d failed (please report a bug describing what caused this, so that we can make this message useful)\n' "$0" "$KEY_NO" >&2 + else + ADDED=$((ADDED + 1)) + fi + done + if [ -z "$ADDED" ] ; then + exit 1 + fi + ;; + dropbear*) + populate_new_ids 0 + [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ + $SSH "$@" "$(installkeys_sh /etc/dropbear/authorized_keys)" \ + || exit 1 + ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) + ;; + *) + # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect + populate_new_ids 0 + if ! [ "$DRY_RUN" ] ; then + printf '%s\n' "$NEW_IDS" | \ + if [ "$SFTP" ] ; then + #shellcheck disable=SC2119 + installkeys_via_sftp + else + $SSH "$@" "$(installkeys_sh)" + fi || exit 1 + fi + ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) + ;; +esac + +if [ "$DRY_RUN" ] ; then + cat <<-EOF + =-=-=-=-=-=-=-= + Would have added the following key(s): + + $NEW_IDS + =-=-=-=-=-=-=-= + EOF +else + cat <<-EOF + + Number of key(s) added: $ADDED + + Now try logging into the machine, with: "${SFTP:-ssh} $SSH_OPTS" + and check to make sure that only the key(s) you wanted were added. + + EOF +fi + +# =-=-=-= diff --git a/aosp/external/openssh/contrib/ssh-copy-id.1 b/aosp/external/openssh/contrib/ssh-copy-id.1 new file mode 100644 index 0000000000000000000000000000000000000000..c141a296f77adbd64ec6f282840368cdbe69fcfb --- /dev/null +++ b/aosp/external/openssh/contrib/ssh-copy-id.1 @@ -0,0 +1,198 @@ +.ig \" -*- nroff -*- +Copyright (c) 1999-2020 hands.com Ltd. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.. +.Dd $Mdocdate: June 17 2010 $ +.Dt SSH-COPY-ID 1 +.Os +.Sh NAME +.Nm ssh-copy-id +.Nd use locally available keys to authorise logins on a remote machine +.Sh SYNOPSIS +.Nm +.Op Fl f +.Op Fl n +.Op Fl s +.Op Fl i Op Ar identity_file +.Op Fl p Ar port +.Op Fl o Ar ssh_option +.Op Ar user Ns @ Ns +.Ar hostname +.Nm +.Fl h | Fl ? +.br +.Sh DESCRIPTION +.Nm +is a script that uses +.Xr ssh 1 +to log into a remote machine (presumably using a login password, +so password authentication should be enabled, unless you've done some +clever use of multiple identities). It assembles a list of one or more +fingerprints (as described below) and tries to log in with each key, to +see if any of them are already installed (of course, if you are not using +.Xr ssh-agent 1 +this may result in you being repeatedly prompted for pass-phrases). +It then assembles a list of those that failed to log in, and using ssh, +enables logins with those keys on the remote server. By default it adds +the keys by appending them to the remote user's +.Pa ~/.ssh/authorized_keys +(creating the file, and directory, if necessary). It is also capable +of detecting if the remote system is a NetScreen, and using its +.Ql set ssh pka-dsa key ... +command instead. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl i Ar identity_file +Use only the key(s) contained in +.Ar identity_file +(rather than looking for identities via +.Xr ssh-add 1 +or in the +.Ic default_ID_file ) . +If the filename does not end in +.Pa .pub +this is added. If the filename is omitted, the +.Ic default_ID_file +is used. +.Pp +Note that this can be used to ensure that the keys copied have the +comment one prefers and/or extra options applied, by ensuring that the +key file has these set as preferred before the copy is attempted. +.It Fl f +Forced mode: doesn't check if the keys are present on the remote server. +This means that it does not need the private key. Of course, this can result +in more than one copy of the key being installed on the remote system. +.It Fl n +do a dry-run. Instead of installing keys on the remote system simply +prints the key(s) that would have been installed. +.It Fl s +SFTP mode: usually the public keys are installed by executing commands on the remote side. +With this option the user's +.Pa ~/.ssh/authorized_keys +file will be downloaded, modified locally and uploaded with sftp. +This option is useful if the server has restrictions on commands which can be used on the remote side. +.It Fl h , Fl ? +Print Usage summary +.It Fl p Ar port , Fl o Ar ssh_option +These two options are simply passed through untouched, along with their +argument, to allow one to set the port or other +.Xr ssh 1 +options, respectively. +.Pp +Rather than specifying these as command line options, it is often better to use (per-host) settings in +.Xr ssh 1 Ns 's +configuration file: +.Xr ssh_config 5 . +.El +.Pp +Default behaviour without +.Fl i , +is to check if +.Ql ssh-add -L +provides any output, and if so those keys are used. Note that this results in +the comment on the key being the filename that was given to +.Xr ssh-add 1 +when the key was loaded into your +.Xr ssh-agent 1 +rather than the comment contained in that file, which is a bit of a shame. +Otherwise, if +.Xr ssh-add 1 +provides no keys contents of the +.Ic default_ID_file +will be used. +.Pp +The +.Ic default_ID_file +is the most recent file that matches: +.Pa ~/.ssh/id*.pub , +(excluding those that match +.Pa ~/.ssh/*-cert.pub ) +so if you create a key that is not the one you want +.Nm +to use, just use +.Xr touch 1 +on your preferred key's +.Pa .pub +file to reinstate it as the most recent. +.Pp +.Sh EXAMPLES +If you have already installed keys from one system on a lot of remote +hosts, and you then create a new key, on a new client machine, say, +it can be difficult to keep track of which systems on which you've +installed the new key. One way of dealing with this is to load both +the new key and old key(s) into your +.Xr ssh-agent 1 . +Load the new key first, without the +.Fl c +option, then load one or more old keys into the agent, possibly by +ssh-ing to the client machine that has that old key, using the +.Fl A +option to allow agent forwarding: +.Pp +.D1 user@newclient$ ssh-add +.D1 user@newclient$ ssh -A old.client +.D1 user@oldl$ ssh-add -c +.D1 No ... prompt for pass-phrase ... +.D1 user@old$ logoff +.D1 user@newclient$ ssh someserver +.Pp +now, if the new key is installed on the server, you'll be allowed in +unprompted, whereas if you only have the old key(s) enabled, you'll be +asked for confirmation, which is your cue to log back out and run +.Pp +.D1 user@newclient$ ssh-copy-id -i someserver +.Pp +The reason you might want to specify the -i option in this case is to +ensure that the comment on the installed key is the one from the +.Pa .pub +file, rather than just the filename that was loaded into your agent. +It also ensures that only the id you intended is installed, rather than +all the keys that you have in your +.Xr ssh-agent 1 . +Of course, you can specify another id, or use the contents of the +.Xr ssh-agent 1 +as you prefer. +.Pp +Having mentioned +.Xr ssh-add 1 Ns 's +.Fl c +option, you might consider using this whenever using agent forwarding +to avoid your key being hijacked, but it is much better to instead use +.Xr ssh 1 Ns 's +.Ar ProxyCommand +and +.Fl W +option, +to bounce through remote servers while always doing direct end-to-end +authentication. This way the middle hop(s) don't get access to your +.Xr ssh-agent 1 . +A web search for +.Ql ssh proxycommand nc +should prove enlightening (N.B. the modern approach is to use the +.Fl W +option, rather than +.Xr nc 1 ) . +.Sh "SEE ALSO" +.Xr ssh 1 , +.Xr ssh-agent 1 , +.Xr sshd 8 diff --git a/aosp/external/openssh/contrib/sshd.pam.freebsd b/aosp/external/openssh/contrib/sshd.pam.freebsd new file mode 100644 index 0000000000000000000000000000000000000000..c0bc36410e4071e1f7e5d8dd6ed4356a164cc679 --- /dev/null +++ b/aosp/external/openssh/contrib/sshd.pam.freebsd @@ -0,0 +1,5 @@ +sshd auth required pam_unix.so try_first_pass +sshd account required pam_unix.so +sshd password required pam_permit.so +sshd session required pam_permit.so + diff --git a/aosp/external/openssh/contrib/sshd.pam.generic b/aosp/external/openssh/contrib/sshd.pam.generic new file mode 100644 index 0000000000000000000000000000000000000000..215f0fe300a47cfd982dcdee2110c7bff96bce93 --- /dev/null +++ b/aosp/external/openssh/contrib/sshd.pam.generic @@ -0,0 +1,8 @@ +#%PAM-1.0 +auth required /lib/security/pam_unix.so shadow nodelay +account required /lib/security/pam_nologin.so +account required /lib/security/pam_unix.so +password required /lib/security/pam_cracklib.so +password required /lib/security/pam_unix.so shadow nullok use_authtok +session required /lib/security/pam_unix.so +session required /lib/security/pam_limits.so diff --git a/aosp/external/openssh/contrib/suse/openssh.spec b/aosp/external/openssh/contrib/suse/openssh.spec new file mode 100644 index 0000000000000000000000000000000000000000..28b9086f4cab7920e19d2d9b022b4f05647315a9 --- /dev/null +++ b/aosp/external/openssh/contrib/suse/openssh.spec @@ -0,0 +1,245 @@ +# Default values for additional components +%define build_x11_askpass 1 + +# Define the UID/GID to use for privilege separation +%define sshd_gid 65 +%define sshd_uid 71 + +# The version of x11-ssh-askpass to use +%define xversion 1.2.4.1 + +# Allow the ability to override defaults with -D skip_xxx=1 +%{?skip_x11_askpass:%define build_x11_askpass 0} + +Summary: OpenSSH, a free Secure Shell (SSH) protocol implementation +Name: openssh +Version: 9.0p1 +URL: https://www.openssh.com/ +Release: 1 +Source0: openssh-%{version}.tar.gz +Source1: x11-ssh-askpass-%{xversion}.tar.gz +License: BSD +Group: Productivity/Networking/SSH +BuildRoot: %{_tmppath}/openssh-%{version}-buildroot +PreReq: openssl +Obsoletes: ssh +Provides: ssh +# +# (Build[ing] Prereq[uisites] only work for RPM 2.95 and newer.) +# building prerequisites -- stuff for +# OpenSSL (openssl-devel), +# and Gnome (glibdev, gtkdev, and gnlibsd) +# +BuildPrereq: openssl +BuildPrereq: zlib-devel +#BuildPrereq: glibdev +#BuildPrereq: gtkdev +#BuildPrereq: gnlibsd + +%package askpass +Summary: A passphrase dialog for OpenSSH and the X window System. +Group: Productivity/Networking/SSH +Requires: openssh = %{version} +Obsoletes: ssh-extras +Provides: openssh:${_libdir}/ssh/ssh-askpass + +%if %{build_x11_askpass} +BuildPrereq: XFree86-devel +%endif + +%description +Ssh (Secure Shell) is a program for logging into a remote machine and for +executing commands in a remote machine. It is intended to replace +rlogin and rsh, and provide secure encrypted communications between +two untrusted hosts over an insecure network. X11 connections and +arbitrary TCP/IP ports can also be forwarded over the secure channel. + +OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it +up to date in terms of security and features, as well as removing all +patented algorithms to separate libraries (OpenSSL). + +This package includes all files necessary for both the OpenSSH +client and server. + +%description askpass +Ssh (Secure Shell) is a program for logging into a remote machine and for +executing commands in a remote machine. It is intended to replace +rlogin and rsh, and provide secure encrypted communications between +two untrusted hosts over an insecure network. X11 connections and +arbitrary TCP/IP ports can also be forwarded over the secure channel. + +OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it +up to date in terms of security and features, as well as removing all +patented algorithms to separate libraries (OpenSSL). + +This package contains an X Window System passphrase dialog for OpenSSH. + +%changelog +* Mon Jul 20 2020 Damien Miller +- Add ssh-sk-helper and corresponding manual page. +* Wed Oct 26 2005 Iain Morgan +- Removed accidental inclusion of --without-zlib-version-check +* Tue Oct 25 2005 Iain Morgan +- Overhaul to deal with newer versions of SuSE and OpenSSH +* Mon Jun 12 2000 Damien Miller +- Glob manpages to catch compressed files +* Wed Mar 15 2000 Damien Miller +- Updated for new location +- Updated for new gnome-ssh-askpass build +* Sun Dec 26 1999 Chris Saia +- Made symlink to gnome-ssh-askpass called ssh-askpass +* Wed Nov 24 1999 Chris Saia +- Removed patches that included /etc/pam.d/sshd, /sbin/init.d/rc.sshd, and + /var/adm/fillup-templates/rc.config.sshd, since Damien merged these into + his released tarfile +- Changed permissions on ssh_config in the install procedure to 644 from 600 + even though it was correct in the %files section and thus right in the RPMs +- Postinstall script for the server now only prints "Generating SSH host + key..." if we need to actually do this, in order to eliminate a confusing + message if an SSH host key is already in place +- Marked all manual pages as %doc(umentation) +* Mon Nov 22 1999 Chris Saia +- Added flag to configure daemon with TCP Wrappers support +- Added building prerequisites (works in RPM 3.0 and newer) +* Thu Nov 18 1999 Chris Saia +- Made this package correct for SuSE. +- Changed instances of pam_pwdb.so to pam_unix.so, since it works more properly + with SuSE, and lib_pwdb.so isn't installed by default. +* Mon Nov 15 1999 Damien Miller +- Split subpackages further based on patch from jim knoble +* Sat Nov 13 1999 Damien Miller +- Added 'Obsoletes' directives +* Tue Nov 09 1999 Damien Miller +- Use make install +- Subpackages +* Mon Nov 08 1999 Damien Miller +- Added links for slogin +- Fixed perms on manpages +* Sat Oct 30 1999 Damien Miller +- Renamed init script +* Fri Oct 29 1999 Damien Miller +- Back to old binary names +* Thu Oct 28 1999 Damien Miller +- Use autoconf +- New binary names +* Wed Oct 27 1999 Damien Miller +- Initial RPMification, based on Jan "Yenya" Kasprzak's spec. + +%prep + +%if %{build_x11_askpass} +%setup -q -a 1 +%else +%setup -q +%endif + +%build +CFLAGS="$RPM_OPT_FLAGS" \ +%configure --prefix=/usr \ + --sysconfdir=%{_sysconfdir}/ssh \ + --mandir=%{_mandir} \ + --with-privsep-path=/var/lib/empty \ + --with-pam \ + --libexecdir=%{_libdir}/ssh +make + +%if %{build_x11_askpass} +cd x11-ssh-askpass-%{xversion} +%configure --mandir=/usr/X11R6/man \ + --libexecdir=%{_libdir}/ssh +xmkmf -a +make +cd .. +%endif + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT/ +install -d $RPM_BUILD_ROOT/etc/pam.d/ +install -d $RPM_BUILD_ROOT/etc/init.d/ +install -d $RPM_BUILD_ROOT/var/adm/fillup-templates +install -m644 contrib/sshd.pam.generic $RPM_BUILD_ROOT/etc/pam.d/sshd +install -m744 contrib/suse/rc.sshd $RPM_BUILD_ROOT/etc/init.d/sshd +install -m744 contrib/suse/sysconfig.ssh \ + $RPM_BUILD_ROOT/var/adm/fillup-templates + +%if %{build_x11_askpass} +cd x11-ssh-askpass-%{xversion} +make install install.man BINDIR=%{_libdir}/ssh DESTDIR=$RPM_BUILD_ROOT/ +rm -f $RPM_BUILD_ROOT/usr/share/Ssh.bin +%endif + +%clean +rm -rf $RPM_BUILD_ROOT + +%pre +/usr/sbin/groupadd -g %{sshd_gid} -o -r sshd 2> /dev/null || : +/usr/sbin/useradd -r -o -g sshd -u %{sshd_uid} -s /bin/false -c "SSH Privilege Separation User" -d /var/lib/sshd sshd 2> /dev/null || : + +%post +/usr/bin/ssh-keygen -A +%{fillup_and_insserv -n -y ssh sshd} +%run_permissions + +%verifyscript +%verify_permissions -e /etc/ssh/sshd_config -e /etc/ssh/ssh_config -e /usr/bin/ssh + +%preun +%stop_on_removal sshd + +%postun +%restart_on_update sshd +%{insserv_cleanup} + +%files +%defattr(-,root,root) +%doc ChangeLog OVERVIEW README* PROTOCOL* +%doc TODO CREDITS LICENCE +%attr(0755,root,root) %dir %{_sysconfdir}/ssh +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config +%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config +%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli +%attr(0644,root,root) %config(noreplace) /etc/pam.d/sshd +%attr(0755,root,root) %config /etc/init.d/sshd +%attr(0755,root,root) %{_bindir}/ssh-keygen +%attr(0755,root,root) %{_bindir}/scp +%attr(0755,root,root) %{_bindir}/ssh +%attr(0755,root,root) %{_bindir}/ssh-agent +%attr(0755,root,root) %{_bindir}/ssh-add +%attr(0755,root,root) %{_bindir}/ssh-keyscan +%attr(0755,root,root) %{_bindir}/sftp +%attr(0755,root,root) %{_sbindir}/sshd +%attr(0755,root,root) %dir %{_libdir}/ssh +%attr(0755,root,root) %{_libdir}/ssh/sftp-server +%attr(4711,root,root) %{_libdir}/ssh/ssh-keysign +%attr(0755,root,root) %{_libdir}/ssh/ssh-pkcs11-helper +%attr(0755,root,root) %{_libdir}/ssh/ssh-sk-helper +%attr(0644,root,root) %doc %{_mandir}/man1/scp.1* +%attr(0644,root,root) %doc %{_mandir}/man1/sftp.1* +%attr(0644,root,root) %doc %{_mandir}/man1/ssh.1* +%attr(0644,root,root) %doc %{_mandir}/man1/ssh-add.1* +%attr(0644,root,root) %doc %{_mandir}/man1/ssh-agent.1* +%attr(0644,root,root) %doc %{_mandir}/man1/ssh-keygen.1* +%attr(0644,root,root) %doc %{_mandir}/man1/ssh-keyscan.1* +%attr(0644,root,root) %doc %{_mandir}/man5/moduli.5* +%attr(0644,root,root) %doc %{_mandir}/man5/ssh_config.5* +%attr(0644,root,root) %doc %{_mandir}/man5/sshd_config.5* +%attr(0644,root,root) %doc %{_mandir}/man8/sftp-server.8* +%attr(0644,root,root) %doc %{_mandir}/man8/ssh-keysign.8* +%attr(0644,root,root) %doc %{_mandir}/man8/ssh-pkcs11-helper.8* +%attr(0644,root,root) %doc %{_mandir}/man8/ssh-sk-helper.8* +%attr(0644,root,root) %doc %{_mandir}/man8/sshd.8* +%attr(0644,root,root) /var/adm/fillup-templates/sysconfig.ssh + +%if %{build_x11_askpass} +%files askpass +%defattr(-,root,root) +%doc x11-ssh-askpass-%{xversion}/README +%doc x11-ssh-askpass-%{xversion}/ChangeLog +%doc x11-ssh-askpass-%{xversion}/SshAskpass*.ad +%attr(0755,root,root) %{_libdir}/ssh/ssh-askpass +%attr(0755,root,root) %{_libdir}/ssh/x11-ssh-askpass +%attr(0644,root,root) %doc /usr/X11R6/man/man1/ssh-askpass.1x* +%attr(0644,root,root) %doc /usr/X11R6/man/man1/x11-ssh-askpass.1x* +%attr(0644,root,root) %config /usr/X11R6/lib/X11/app-defaults/SshAskpass +%endif diff --git a/aosp/external/openssh/contrib/suse/rc.config.sshd b/aosp/external/openssh/contrib/suse/rc.config.sshd new file mode 100644 index 0000000000000000000000000000000000000000..baaa7a5a1f44a71b14528bc7dc34609ab26d665f --- /dev/null +++ b/aosp/external/openssh/contrib/suse/rc.config.sshd @@ -0,0 +1,5 @@ +# +# Start the Secure Shell (SSH) Daemon? +# +START_SSHD="yes" + diff --git a/aosp/external/openssh/contrib/suse/rc.sshd b/aosp/external/openssh/contrib/suse/rc.sshd new file mode 100644 index 0000000000000000000000000000000000000000..28f28e41d4e2624188453691ca1cbf84222f70fd --- /dev/null +++ b/aosp/external/openssh/contrib/suse/rc.sshd @@ -0,0 +1,121 @@ +#! /bin/sh +# Copyright (c) 1995-2000 SuSE GmbH Nuernberg, Germany. +# +# Author: Jiri Smid +# +# /etc/init.d/sshd +# +# and symbolic its link +# +# /usr/sbin/rcsshd +# +### BEGIN INIT INFO +# Provides: sshd +# Required-Start: $network $remote_fs +# Required-Stop: $network $remote_fs +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Description: Start the sshd daemon +### END INIT INFO + +SSHD_BIN=/usr/sbin/sshd +test -x $SSHD_BIN || exit 5 + +SSHD_SYSCONFIG=/etc/sysconfig/ssh +test -r $SSHD_SYSCONFIG || exit 6 +. $SSHD_SYSCONFIG + +SSHD_PIDFILE=/var/run/sshd.init.pid + +. /etc/rc.status + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v ditto but be verbose in local rc status +# rc_status -v -r ditto and clear the local rc status +# rc_failed set local and overall rc status to failed +# rc_reset clear local rc status (overall remains) +# rc_exit exit appropriate to overall rc status + +# First reset status of this service +rc_reset + +case "$1" in + start) + # Generate any missing host keys + ssh-keygen -A + echo -n "Starting SSH daemon" + ## Start daemon with startproc(8). If this fails + ## the echo return value is set appropriate. + + startproc -f -p $SSHD_PIDFILE $SSHD_BIN $SSHD_OPTS -o "PidFile=$SSHD_PIDFILE" + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down SSH daemon" + ## Stop daemon with killproc(8) and if this fails + ## set echo the echo return value. + + killproc -p $SSHD_PIDFILE -TERM $SSHD_BIN + + # Remember status and be verbose + rc_status -v + ;; + try-restart) + ## Stop the service and if this succeeds (i.e. the + ## service was running before), start it again. + $0 status >/dev/null && $0 restart + + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + force-reload|reload) + ## Signal the daemon to reload its config. Most daemons + ## do this on signal 1 (SIGHUP). + + echo -n "Reload service sshd" + + killproc -p $SSHD_PIDFILE -HUP $SSHD_BIN + + rc_status -v + + ;; + status) + echo -n "Checking for service sshd " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + # Status has a slightly different for the status command: + # 0 - service running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running + + checkproc -p $SSHD_PIDFILE $SSHD_BIN + + rc_status -v + ;; + probe) + ## Optional: Probe for the necessity of a reload, + ## give out the argument which is required for a reload. + + test /etc/ssh/sshd_config -nt $SSHD_PIDFILE && echo reload + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; +esac +rc_exit diff --git a/aosp/external/openssh/contrib/suse/sysconfig.ssh b/aosp/external/openssh/contrib/suse/sysconfig.ssh new file mode 100644 index 0000000000000000000000000000000000000000..c6a37e5cb753e58e8e3d4d4c433d0ba01a1569a1 --- /dev/null +++ b/aosp/external/openssh/contrib/suse/sysconfig.ssh @@ -0,0 +1,9 @@ +## Path: Network/Remote access/SSH +## Description: SSH server settings +## Type: string +## Default: "" +## ServiceRestart: sshd +# +# Options for sshd +# +SSHD_OPTS="" diff --git a/aosp/external/openssh/crypto_api.h b/aosp/external/openssh/crypto_api.h new file mode 100644 index 0000000000000000000000000000000000000000..5c3d97eaa401b7697e575de79e6761d6e19f7151 --- /dev/null +++ b/aosp/external/openssh/crypto_api.h @@ -0,0 +1,58 @@ +/* $OpenBSD: crypto_api.h,v 1.7 2021/01/08 02:33:13 dtucker Exp $ */ + +/* + * Assembled from generated headers and source files by Markus Friedl. + * Placed in the public domain. + */ + +#ifndef crypto_api_h +#define crypto_api_h + +#include "includes.h" + +#ifdef HAVE_STDINT_H +# include +#endif +#include + +typedef int8_t crypto_int8; +typedef uint8_t crypto_uint8; +typedef int16_t crypto_int16; +typedef uint16_t crypto_uint16; +typedef int32_t crypto_int32; +typedef uint32_t crypto_uint32; +typedef int64_t crypto_int64; +typedef uint64_t crypto_uint64; + +#define randombytes(buf, buf_len) arc4random_buf((buf), (buf_len)) +#define small_random32() arc4random() + +#define crypto_hash_sha512_BYTES 64U + +int crypto_hash_sha512(unsigned char *, const unsigned char *, + unsigned long long); + +int crypto_verify_32(const unsigned char *, const unsigned char *); + +#define crypto_sign_ed25519_SECRETKEYBYTES 64U +#define crypto_sign_ed25519_PUBLICKEYBYTES 32U +#define crypto_sign_ed25519_BYTES 64U + +int crypto_sign_ed25519(unsigned char *, unsigned long long *, + const unsigned char *, unsigned long long, const unsigned char *); +int crypto_sign_ed25519_open(unsigned char *, unsigned long long *, + const unsigned char *, unsigned long long, const unsigned char *); +int crypto_sign_ed25519_keypair(unsigned char *, unsigned char *); + +#define crypto_kem_sntrup761_PUBLICKEYBYTES 1158 +#define crypto_kem_sntrup761_SECRETKEYBYTES 1763 +#define crypto_kem_sntrup761_CIPHERTEXTBYTES 1039 +#define crypto_kem_sntrup761_BYTES 32 + +int crypto_kem_sntrup761_enc(unsigned char *cstr, unsigned char *k, + const unsigned char *pk); +int crypto_kem_sntrup761_dec(unsigned char *k, + const unsigned char *cstr, const unsigned char *sk); +int crypto_kem_sntrup761_keypair(unsigned char *pk, unsigned char *sk); + +#endif /* crypto_api_h */ diff --git a/aosp/external/openssh/defines.h b/aosp/external/openssh/defines.h new file mode 100644 index 0000000000000000000000000000000000000000..279e509aae22e4e2824a15c9be4dac7f89a5be5c --- /dev/null +++ b/aosp/external/openssh/defines.h @@ -0,0 +1,945 @@ +/* + * Copyright (c) 1999-2003 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEFINES_H +#define _DEFINES_H + +/* Constants */ + +#if defined(HAVE_DECL_SHUT_RD) && HAVE_DECL_SHUT_RD == 0 +enum +{ + SHUT_RD = 0, /* No more receptions. */ + SHUT_WR, /* No more transmissions. */ + SHUT_RDWR /* No more receptions or transmissions. */ +}; +# define SHUT_RD SHUT_RD +# define SHUT_WR SHUT_WR +# define SHUT_RDWR SHUT_RDWR +#endif + +/* + * Cygwin doesn't really have a notion of reserved ports. It is still + * is useful on the client side so for compatibility it defines as 1024 via + * netinet/in.h inside an enum. We * don't actually want that restriction + * so we want to set that to zero, but we can't do it direct in config.h + * because it'll cause a conflicting definition the first time we include + * netinet/in.h. + */ + +#ifdef HAVE_CYGWIN +#define IPPORT_RESERVED 0 +#endif + +/* + * Definitions for IP type of service (ip_tos) + */ +#include +#include +#ifndef IPTOS_LOWDELAY +# define IPTOS_LOWDELAY 0x10 +# define IPTOS_THROUGHPUT 0x08 +# define IPTOS_RELIABILITY 0x04 +# define IPTOS_LOWCOST 0x02 +# define IPTOS_MINCOST IPTOS_LOWCOST +#endif /* IPTOS_LOWDELAY */ + +/* + * Definitions for DiffServ Codepoints as per RFCs 2474, 3246, 4594 & 8622. + * These are the 6 most significant bits as they appear on the wire, so the + * two least significant bits must be zero. + */ +#ifndef IPTOS_DSCP_AF11 +# define IPTOS_DSCP_AF11 0x28 +# define IPTOS_DSCP_AF12 0x30 +# define IPTOS_DSCP_AF13 0x38 +# define IPTOS_DSCP_AF21 0x48 +# define IPTOS_DSCP_AF22 0x50 +# define IPTOS_DSCP_AF23 0x58 +# define IPTOS_DSCP_AF31 0x68 +# define IPTOS_DSCP_AF32 0x70 +# define IPTOS_DSCP_AF33 0x78 +# define IPTOS_DSCP_AF41 0x88 +# define IPTOS_DSCP_AF42 0x90 +# define IPTOS_DSCP_AF43 0x98 +# define IPTOS_DSCP_EF 0xb8 +#endif /* IPTOS_DSCP_AF11 */ +#ifndef IPTOS_DSCP_CS0 +# define IPTOS_DSCP_CS0 0x00 +# define IPTOS_DSCP_CS1 0x20 +# define IPTOS_DSCP_CS2 0x40 +# define IPTOS_DSCP_CS3 0x60 +# define IPTOS_DSCP_CS4 0x80 +# define IPTOS_DSCP_CS5 0xa0 +# define IPTOS_DSCP_CS6 0xc0 +# define IPTOS_DSCP_CS7 0xe0 +#endif /* IPTOS_DSCP_CS0 */ +#ifndef IPTOS_DSCP_EF +# define IPTOS_DSCP_EF 0xb8 +#endif /* IPTOS_DSCP_EF */ +#ifndef IPTOS_DSCP_LE +# define IPTOS_DSCP_LE 0x04 +#endif /* IPTOS_DSCP_LE */ +#ifndef IPTOS_PREC_CRITIC_ECP +# define IPTOS_PREC_CRITIC_ECP 0xa0 +#endif +#ifndef IPTOS_PREC_INTERNETCONTROL +# define IPTOS_PREC_INTERNETCONTROL 0xc0 +#endif +#ifndef IPTOS_PREC_NETCONTROL +# define IPTOS_PREC_NETCONTROL 0xe0 +#endif + +#ifndef PATH_MAX +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# endif +#endif + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# else /* PATH_MAX */ +# define MAXPATHLEN 64 +# endif /* PATH_MAX */ +#endif /* MAXPATHLEN */ + +#ifndef HOST_NAME_MAX +# include "netdb.h" /* for MAXHOSTNAMELEN */ +# if defined(_POSIX_HOST_NAME_MAX) +# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +# elif defined(MAXHOSTNAMELEN) +# define HOST_NAME_MAX MAXHOSTNAMELEN +# else +# define HOST_NAME_MAX 255 +# endif +#endif /* HOST_NAME_MAX */ + +#if defined(HAVE_DECL_MAXSYMLINKS) && HAVE_DECL_MAXSYMLINKS == 0 +# define MAXSYMLINKS 5 +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +#ifndef NGROUPS_MAX /* Disable groupaccess if NGROUP_MAX is not set */ +#ifdef NGROUPS +#define NGROUPS_MAX NGROUPS +#else +#define NGROUPS_MAX 0 +#endif +#endif + +#if defined(HAVE_DECL_O_NONBLOCK) && HAVE_DECL_O_NONBLOCK == 0 +# define O_NONBLOCK 00004 /* Non Blocking Open */ +#endif + +#ifndef S_IFSOCK +# define S_IFSOCK 0 +#endif /* S_IFSOCK */ + +#ifndef S_ISDIR +# define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR)) +#endif /* S_ISDIR */ + +#ifndef S_ISREG +# define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) +#endif /* S_ISREG */ + +#ifndef S_ISLNK +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif /* S_ISLNK */ + +#ifndef S_IXUSR +# define S_IXUSR 0000100 /* execute/search permission, */ +# define S_IXGRP 0000010 /* execute/search permission, */ +# define S_IXOTH 0000001 /* execute/search permission, */ +# define _S_IWUSR 0000200 /* write permission, */ +# define S_IWUSR _S_IWUSR /* write permission, owner */ +# define S_IWGRP 0000020 /* write permission, group */ +# define S_IWOTH 0000002 /* write permission, other */ +# define S_IRUSR 0000400 /* read permission, owner */ +# define S_IRGRP 0000040 /* read permission, group */ +# define S_IROTH 0000004 /* read permission, other */ +# define S_IRWXU 0000700 /* read, write, execute */ +# define S_IRWXG 0000070 /* read, write, execute */ +# define S_IRWXO 0000007 /* read, write, execute */ +#endif /* S_IXUSR */ + +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#ifndef MAP_FAILED +# define MAP_FAILED ((void *)-1) +#endif + +/* +SCO Open Server 3 has INADDR_LOOPBACK defined in rpc/rpc.h but +including rpc/rpc.h breaks Solaris 6 +*/ +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK ((u_long)0x7f000001) +#endif + +/* Types */ + +/* If sys/types.h does not supply intXX_t, supply them ourselves */ +/* (or die trying) */ + +#ifndef HAVE_U_INT +typedef unsigned int u_int; +#endif + +#ifndef HAVE_INTXX_T +typedef signed char int8_t; +# if (SIZEOF_SHORT_INT == 2) +typedef short int int16_t; +# else +# error "16 bit int type not found." +# endif +# if (SIZEOF_INT == 4) +typedef int int32_t; +# else +# error "32 bit int type not found." +# endif +#endif + +/* If sys/types.h does not supply u_intXX_t, supply them ourselves */ +#ifndef HAVE_U_INTXX_T +# ifdef HAVE_UINTXX_T +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; +# define HAVE_U_INTXX_T 1 +# else +typedef unsigned char u_int8_t; +# if (SIZEOF_SHORT_INT == 2) +typedef unsigned short int u_int16_t; +# else +# error "16 bit int type not found." +# endif +# if (SIZEOF_INT == 4) +typedef unsigned int u_int32_t; +# else +# error "32 bit int type not found." +# endif +# endif +#define __BIT_TYPES_DEFINED__ +#endif + +#if !defined(LLONG_MIN) && defined(LONG_LONG_MIN) +#define LLONG_MIN LONG_LONG_MIN +#endif +#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) +#define LLONG_MAX LONG_LONG_MAX +#endif + +#ifndef UINT32_MAX +# if defined(HAVE_DECL_UINT32_MAX) && (HAVE_DECL_UINT32_MAX == 0) +# if (SIZEOF_INT == 4) +# define UINT32_MAX UINT_MAX +# endif +# endif +#endif + +/* 64-bit types */ +#ifndef HAVE_INT64_T +# if (SIZEOF_LONG_INT == 8) +typedef long int int64_t; +# else +# if (SIZEOF_LONG_LONG_INT == 8) +typedef long long int int64_t; +# endif +# endif +#endif +#ifndef HAVE_U_INT64_T +# if (SIZEOF_LONG_INT == 8) +typedef unsigned long int u_int64_t; +# else +# if (SIZEOF_LONG_LONG_INT == 8) +typedef unsigned long long int u_int64_t; +# endif +# endif +#endif + +#ifndef HAVE_UINTXX_T +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; +typedef u_int64_t uint64_t; +#endif + +#ifndef HAVE_INTMAX_T +typedef long long intmax_t; +#endif + +#ifndef HAVE_UINTMAX_T +typedef unsigned long long uintmax_t; +#endif + +#if SIZEOF_TIME_T == SIZEOF_LONG_LONG_INT +# define SSH_TIME_T_MAX LLONG_MAX +#else +# define SSH_TIME_T_MAX INT_MAX +#endif + +#ifndef HAVE_U_CHAR +typedef unsigned char u_char; +# define HAVE_U_CHAR +#endif /* HAVE_U_CHAR */ + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((unsigned long long)-1) +#endif + +#ifndef SIZE_T_MAX +#define SIZE_T_MAX ULONG_MAX +#endif /* SIZE_T_MAX */ + +#ifndef HAVE_SIZE_T +typedef unsigned int size_t; +# define HAVE_SIZE_T +# define SIZE_T_MAX UINT_MAX +#endif /* HAVE_SIZE_T */ + +#ifndef SIZE_MAX +#define SIZE_MAX SIZE_T_MAX +#endif + +#ifndef INT32_MAX +# if (SIZEOF_INT == 4) +# define INT32_MAX INT_MAX +# elif (SIZEOF_LONG == 4) +# define INT32_MAX LONG_MAX +# else +# error "need INT32_MAX" +# endif +#endif + +#ifndef INT64_MAX +# if (SIZEOF_INT == 8) +# define INT64_MAX INT_MAX +# elif (SIZEOF_LONG == 8) +# define INT64_MAX LONG_MAX +# elif (SIZEOF_LONG_LONG_INT == 8) +# define INT64_MAX LLONG_MAX +# else +# error "need INT64_MAX" +# endif +#endif + +#ifndef HAVE_SSIZE_T +typedef int ssize_t; +#define SSIZE_MAX INT_MAX +# define HAVE_SSIZE_T +#endif /* HAVE_SSIZE_T */ + +#ifndef HAVE_CLOCK_T +typedef long clock_t; +# define HAVE_CLOCK_T +#endif /* HAVE_CLOCK_T */ + +#ifndef HAVE_SA_FAMILY_T +typedef int sa_family_t; +# define HAVE_SA_FAMILY_T +#endif /* HAVE_SA_FAMILY_T */ + +#ifndef HAVE_PID_T +typedef int pid_t; +# define HAVE_PID_T +#endif /* HAVE_PID_T */ + +#ifndef HAVE_SIG_ATOMIC_T +typedef int sig_atomic_t; +# define HAVE_SIG_ATOMIC_T +#endif /* HAVE_SIG_ATOMIC_T */ + +#ifndef HAVE_MODE_T +typedef int mode_t; +# define HAVE_MODE_T +#endif /* HAVE_MODE_T */ + +#if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS) +# define ss_family __ss_family +#endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */ + +#ifndef HAVE_SYS_UN_H +struct sockaddr_un { + short sun_family; /* AF_UNIX */ + char sun_path[108]; /* path name (gag) */ +}; +#endif /* HAVE_SYS_UN_H */ + +#ifndef HAVE_IN_ADDR_T +typedef u_int32_t in_addr_t; +#endif +#ifndef HAVE_IN_PORT_T +typedef u_int16_t in_port_t; +#endif + +#if defined(BROKEN_SYS_TERMIO_H) && !defined(_STRUCT_WINSIZE) +#define _STRUCT_WINSIZE +struct winsize { + unsigned short ws_row; /* rows, in characters */ + unsigned short ws_col; /* columns, in character */ + unsigned short ws_xpixel; /* horizontal size, pixels */ + unsigned short ws_ypixel; /* vertical size, pixels */ +}; +#endif + +/* bits needed for select that may not be in the system headers */ +#ifndef HAVE_FD_MASK + typedef unsigned long int fd_mask; +#endif + +#if defined(HAVE_DECL_NFDBITS) && HAVE_DECL_NFDBITS == 0 +# define NFDBITS (8 * sizeof(unsigned long)) +#endif + +#if defined(HAVE_DECL_HOWMANY) && HAVE_DECL_HOWMANY == 0 +# define howmany(x,y) (((x)+((y)-1))/(y)) +#endif + +/* Paths */ + +#ifndef _PATH_BSHELL +# define _PATH_BSHELL "/bin/sh" +#endif + +#ifdef USER_PATH +# ifdef _PATH_STDPATH +# undef _PATH_STDPATH +# endif +# define _PATH_STDPATH USER_PATH +#endif + +#ifndef _PATH_STDPATH +# define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" +#endif + +#ifndef SUPERUSER_PATH +# define SUPERUSER_PATH _PATH_STDPATH +#endif + +#ifndef _PATH_DEVNULL +# define _PATH_DEVNULL "/dev/null" +#endif + +/* user may have set a different path */ +#if defined(_PATH_MAILDIR) && defined(MAIL_DIRECTORY) +# undef _PATH_MAILDIR +#endif /* defined(_PATH_MAILDIR) && defined(MAIL_DIRECTORY) */ + +#ifdef MAIL_DIRECTORY +# define _PATH_MAILDIR MAIL_DIRECTORY +#endif + +#ifndef _PATH_NOLOGIN +# define _PATH_NOLOGIN "/etc/nologin" +#endif + +/* Define this to be the path of the xauth program. */ +#ifdef XAUTH_PATH +#define _PATH_XAUTH XAUTH_PATH +#endif /* XAUTH_PATH */ + +/* derived from XF4/xc/lib/dps/Xlibnet.h */ +#ifndef X_UNIX_PATH +# ifdef __hpux +# define X_UNIX_PATH "/var/spool/sockets/X11/%u" +# else +# define X_UNIX_PATH "/tmp/.X11-unix/X%u" +# endif +#endif /* X_UNIX_PATH */ +#define _PATH_UNIX_X X_UNIX_PATH + +#ifndef _PATH_TTY +# define _PATH_TTY "/dev/tty" +#endif + +/* Macros */ + +#if defined(HAVE_LOGIN_GETCAPBOOL) && defined(HAVE_LOGIN_CAP_H) +# define HAVE_LOGIN_CAP +#endif + +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +# define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef roundup +# define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +#ifndef timersub +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +#ifndef TIMEVAL_TO_TIMESPEC +#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} +#endif + +#ifndef TIMESPEC_TO_TIMEVAL +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ +} +#endif + +#ifndef timespeccmp +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#endif + +/* Operations on timespecs. */ +#ifndef timespecclear +#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0 +#endif +#ifndef timespeccmp +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#endif +#ifndef timespecadd +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#endif +#ifndef timespecsub +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif + +#ifndef __P +# define __P(x) x +#endif + +#if !defined(IN6_IS_ADDR_V4MAPPED) +# define IN6_IS_ADDR_V4MAPPED(a) \ + ((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \ + (((u_int32_t *) (a))[2] == htonl (0xffff))) +#endif /* !defined(IN6_IS_ADDR_V4MAPPED) */ + +#if !defined(__GNUC__) || (__GNUC__ < 2) +# define __attribute__(x) +#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ + +#if !defined(HAVE_ATTRIBUTE__SENTINEL__) && !defined(__sentinel__) +# define __sentinel__ +#endif + +#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) +# define __bounded__(x, y, z) +#endif + +#if !defined(HAVE_ATTRIBUTE__NONNULL__) && !defined(__nonnull__) +# define __nonnull__(x) +#endif + +#ifndef OSSH_ALIGNBYTES +#define OSSH_ALIGNBYTES (sizeof(int) - 1) +#endif +#ifndef __CMSG_ALIGN +#define __CMSG_ALIGN(p) (((u_int)(p) + OSSH_ALIGNBYTES) &~ OSSH_ALIGNBYTES) +#endif + +/* Length of the contents of a control message of length len */ +#ifndef CMSG_LEN +#define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) +#endif + +/* Length of the space taken up by a padded control message of length len */ +#ifndef CMSG_SPACE +#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) +#endif + +/* given pointer to struct cmsghdr, return pointer to data */ +#ifndef CMSG_DATA +#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + __CMSG_ALIGN(sizeof(struct cmsghdr))) +#endif /* CMSG_DATA */ + +/* + * RFC 2292 requires to check msg_controllen, in case that the kernel returns + * an empty list for some reasons. + */ +#ifndef CMSG_FIRSTHDR +#define CMSG_FIRSTHDR(mhdr) \ + ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ + (struct cmsghdr *)(mhdr)->msg_control : \ + (struct cmsghdr *)NULL) +#endif /* CMSG_FIRSTHDR */ + +#if defined(HAVE_DECL_OFFSETOF) && HAVE_DECL_OFFSETOF == 0 +# define offsetof(type, member) ((size_t) &((type *)0)->member) +#endif + +/* Set up BSD-style BYTE_ORDER definition if it isn't there already */ +/* XXX: doesn't try to cope with strange byte orders (PDP_ENDIAN) */ +#ifndef BYTE_ORDER +# ifndef LITTLE_ENDIAN +# define LITTLE_ENDIAN 1234 +# endif /* LITTLE_ENDIAN */ +# ifndef BIG_ENDIAN +# define BIG_ENDIAN 4321 +# endif /* BIG_ENDIAN */ +# ifdef WORDS_BIGENDIAN +# define BYTE_ORDER BIG_ENDIAN +# else /* WORDS_BIGENDIAN */ +# define BYTE_ORDER LITTLE_ENDIAN +# endif /* WORDS_BIGENDIAN */ +#endif /* BYTE_ORDER */ + +/* Function replacement / compatibility hacks */ + +#if !defined(HAVE_GETADDRINFO) && (defined(HAVE_OGETADDRINFO) || defined(HAVE_NGETADDRINFO)) +# define HAVE_GETADDRINFO +#endif + +#ifndef HAVE_GETOPT_OPTRESET +# undef getopt +# undef opterr +# undef optind +# undef optopt +# undef optreset +# undef optarg +# define getopt(ac, av, o) BSDgetopt(ac, av, o) +# define opterr BSDopterr +# define optind BSDoptind +# define optopt BSDoptopt +# define optreset BSDoptreset +# define optarg BSDoptarg +#endif + +#if defined(BROKEN_GETADDRINFO) && defined(HAVE_GETADDRINFO) +# undef HAVE_GETADDRINFO +#endif +#if defined(BROKEN_GETADDRINFO) && defined(HAVE_FREEADDRINFO) +# undef HAVE_FREEADDRINFO +#endif +#if defined(BROKEN_GETADDRINFO) && defined(HAVE_GAI_STRERROR) +# undef HAVE_GAI_STRERROR +#endif + +#if defined(HAVE_GETADDRINFO) +# if defined(HAVE_DECL_AI_NUMERICSERV) && HAVE_DECL_AI_NUMERICSERV == 0 +# define AI_NUMERICSERV 0 +# endif +#endif + +#if defined(BROKEN_UPDWTMPX) && defined(HAVE_UPDWTMPX) +# undef HAVE_UPDWTMPX +#endif + +#if defined(BROKEN_SHADOW_EXPIRE) && defined(HAS_SHADOW_EXPIRE) +# undef HAS_SHADOW_EXPIRE +#endif + +#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) && \ + defined(SYSLOG_R_SAFE_IN_SIGHAND) +# define DO_LOG_SAFE_IN_SIGHAND +#endif + +#if !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) +# define memmove(s1, s2, n) bcopy((s2), (s1), (n)) +#endif /* !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) */ + +#ifndef GETPGRP_VOID +# include +# define getpgrp() getpgrp(0) +#endif + +#ifdef USE_BSM_AUDIT +# define SSH_AUDIT_EVENTS +# define CUSTOM_SSH_AUDIT_EVENTS +#endif + +#ifdef USE_LINUX_AUDIT +# define SSH_AUDIT_EVENTS +# define CUSTOM_SSH_AUDIT_EVENTS +#endif + +#if !defined(HAVE___func__) && defined(HAVE___FUNCTION__) +# define __func__ __FUNCTION__ +#elif !defined(HAVE___func__) +# define __func__ "" +#endif + +#if defined(KRB5) && !defined(HEIMDAL) +# define krb5_get_err_text(context,code) error_message(code) +#endif + +/* Maximum number of file descriptors available */ +#ifdef HAVE_SYSCONF +# define SSH_SYSFDMAX sysconf(_SC_OPEN_MAX) +#else +# define SSH_SYSFDMAX 10000 +#endif + +#ifdef FSID_HAS_VAL +/* encode f_fsid into a 64 bit value */ +#define FSID_TO_ULONG(f) \ + ((((u_int64_t)(f).val[0] & 0xffffffffUL) << 32) | \ + ((f).val[1] & 0xffffffffUL)) +#elif defined(FSID_HAS___VAL) +#define FSID_TO_ULONG(f) \ + ((((u_int64_t)(f).__val[0] & 0xffffffffUL) << 32) | \ + ((f).__val[1] & 0xffffffffUL)) +#else +# define FSID_TO_ULONG(f) ((f)) +#endif + +#if defined(__Lynx__) + /* + * LynxOS defines these in param.h which we do not want to include since + * it will also pull in a bunch of kernel definitions. + */ +# define ALIGNBYTES (sizeof(int) - 1) +# define ALIGN(p) (((unsigned)p + ALIGNBYTES) & ~ALIGNBYTES) + /* Missing prototypes on LynxOS */ + int snprintf (char *, size_t, const char *, ...); + int mkstemp (char *); + char *crypt (const char *, const char *); + int seteuid (uid_t); + int setegid (gid_t); + char *mkdtemp (char *); + int rresvport_af (int *, sa_family_t); + int innetgr (const char *, const char *, const char *, const char *); +#endif + +/* + * Define this to use pipes instead of socketpairs for communicating with the + * client program. Socketpairs do not seem to work on all systems. + * + * configure.ac sets this for a few OS's which are known to have problems + * but you may need to set it yourself + */ +/* #define USE_PIPES 1 */ + +/** + ** login recorder definitions + **/ + +/* FIXME: put default paths back in */ +#ifndef UTMP_FILE +# ifdef _PATH_UTMP +# define UTMP_FILE _PATH_UTMP +# else +# ifdef CONF_UTMP_FILE +# define UTMP_FILE CONF_UTMP_FILE +# endif +# endif +#endif +#ifndef WTMP_FILE +# ifdef _PATH_WTMP +# define WTMP_FILE _PATH_WTMP +# else +# ifdef CONF_WTMP_FILE +# define WTMP_FILE CONF_WTMP_FILE +# endif +# endif +#endif +/* pick up the user's location for lastlog if given */ +#ifndef LASTLOG_FILE +# ifdef _PATH_LASTLOG +# define LASTLOG_FILE _PATH_LASTLOG +# else +# ifdef CONF_LASTLOG_FILE +# define LASTLOG_FILE CONF_LASTLOG_FILE +# endif +# endif +#endif + +#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) +# define USE_SHADOW +#endif + +/* The login() library function in libutil is first choice */ +#if defined(HAVE_LOGIN) && !defined(DISABLE_LOGIN) +# define USE_LOGIN + +#else +/* Simply select your favourite login types. */ +/* Can't do if-else because some systems use several... */ +# if !defined(DISABLE_UTMPX) +# define USE_UTMPX +# endif +# if defined(UTMP_FILE) && !defined(DISABLE_UTMP) +# define USE_UTMP +# endif +# if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) +# define USE_WTMPX +# endif +# if defined(WTMP_FILE) && !defined(DISABLE_WTMP) +# define USE_WTMP +# endif + +#endif + +#ifndef UT_LINESIZE +# define UT_LINESIZE 8 +#endif + +/* I hope that the presence of LASTLOG_FILE is enough to detect this */ +#if defined(LASTLOG_FILE) && !defined(DISABLE_LASTLOG) +# define USE_LASTLOG +#endif + +#ifdef HAVE_OSF_SIA +# ifdef USE_SHADOW +# undef USE_SHADOW +# endif +# define CUSTOM_SYS_AUTH_PASSWD 1 +#endif + +#if defined(HAVE_LIBIAF) && defined(HAVE_SET_ID) && !defined(HAVE_SECUREWARE) +# define CUSTOM_SYS_AUTH_PASSWD 1 +#endif +#if defined(HAVE_LIBIAF) && defined(HAVE_SET_ID) && !defined(BROKEN_LIBIAF) +# define USE_LIBIAF +#endif + +/* HP-UX 11.11 */ +#ifdef BTMP_FILE +# define _PATH_BTMP BTMP_FILE +#endif + +#if defined(USE_BTMP) && defined(_PATH_BTMP) +# define CUSTOM_FAILED_LOGIN +#endif + +/** end of login recorder definitions */ + +#ifdef BROKEN_GETGROUPS +# define getgroups(a,b) ((a)==0 && (b)==NULL ? NGROUPS_MAX : getgroups((a),(b))) +#endif + +#ifndef IOV_MAX +# if defined(_XOPEN_IOV_MAX) +# define IOV_MAX _XOPEN_IOV_MAX +# elif defined(DEF_IOV_MAX) +# define IOV_MAX DEF_IOV_MAX +# else +# define IOV_MAX 16 +# endif +#endif + +#ifndef EWOULDBLOCK +# define EWOULDBLOCK EAGAIN +#endif + +#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ +#define INET6_ADDRSTRLEN 46 +#endif + +#ifndef SSH_IOBUFSZ +# define SSH_IOBUFSZ 8192 +#endif + +/* + * We want functions in openbsd-compat, if enabled, to override system ones. + * We no-op out the weak symbol definition rather than remove it to reduce + * future sync problems. Some compilers (eg Unixware) do not allow an + * empty statement, so we use a bogus function declaration. + */ +#define DEF_WEAK(x) void __ssh_compat_weak_##x(void) + +/* + * Platforms that have arc4random_uniform() and not arc4random_stir() + * shouldn't need the latter. + */ +#if defined(HAVE_ARC4RANDOM) && defined(HAVE_ARC4RANDOM_UNIFORM) && \ + !defined(HAVE_ARC4RANDOM_STIR) +# define arc4random_stir() +#endif + +#ifndef HAVE_VA_COPY +# ifdef HAVE___VA_COPY +# define va_copy(dest, src) __va_copy(dest, src) +# else +# define va_copy(dest, src) (dest) = (src) +# endif +#endif + +#ifndef __predict_true +# if defined(__GNUC__) && \ + ((__GNUC__ > (2)) || (__GNUC__ == (2) && __GNUC_MINOR__ >= (96))) +# define __predict_true(exp) __builtin_expect(((exp) != 0), 1) +# define __predict_false(exp) __builtin_expect(((exp) != 0), 0) +# else +# define __predict_true(exp) ((exp) != 0) +# define __predict_false(exp) ((exp) != 0) +# endif /* gcc version */ +#endif /* __predict_true */ + +#if defined(HAVE_GLOB_H) && defined(GLOB_HAS_ALTDIRFUNC) && \ + defined(GLOB_HAS_GL_MATCHC) && defined(GLOB_HAS_GL_STATV) && \ + defined(HAVE_DECL_GLOB_NOMATCH) && HAVE_DECL_GLOB_NOMATCH != 0 && \ + !defined(BROKEN_GLOB) +# define USE_SYSTEM_GLOB +#endif + +/* + * sntrup761 uses variable length arrays and c99-style declarations after code, + * so only enable if the compiler supports them. + */ +#if defined(VARIABLE_LENGTH_ARRAYS) && defined(VARIABLE_DECLARATION_AFTER_CODE) +# define USE_SNTRUP761X25519 1 +#endif +#endif /* _DEFINES_H */ diff --git a/aosp/external/openssh/dh.c b/aosp/external/openssh/dh.c new file mode 100755 index 0000000000000000000000000000000000000000..07e7e2fd1eb1a2ba4fd2bffab93e1c5e446fbfc0 --- /dev/null +++ b/aosp/external/openssh/dh.c @@ -0,0 +1,505 @@ +/* $OpenBSD: dh.c,v 1.74 2021/04/03 06:18:40 djm Exp $ */ +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dh.h" +#include "pathnames.h" +#include "log.h" +#include "misc.h" +#include "ssherr.h" + +#include "openbsd-compat/openssl-compat.h" + +static const char *moduli_filename; + +void dh_set_moduli_file(const char *filename) +{ + moduli_filename = filename; +} + +static const char * get_moduli_filename(void) +{ + return moduli_filename ? moduli_filename : _PATH_DH_MODULI; +} + +static int +parse_prime(int linenum, char *line, struct dhgroup *dhg) +{ + char *cp, *arg; + char *strsize, *gen, *prime; + const char *errstr = NULL; + long long n; + + dhg->p = dhg->g = NULL; + cp = line; + if ((arg = strdelim(&cp)) == NULL) + return 0; + /* Ignore leading whitespace */ + if (*arg == '\0') + arg = strdelim(&cp); + if (!arg || !*arg || *arg == '#') + return 0; + + /* time */ + if (cp == NULL || *arg == '\0') + goto truncated; + arg = strsep(&cp, " "); /* type */ + if (cp == NULL || *arg == '\0') + goto truncated; + /* Ensure this is a safe prime */ + n = strtonum(arg, 0, 5, &errstr); + if (errstr != NULL || n != MODULI_TYPE_SAFE) { + error("moduli:%d: type is not %d", linenum, MODULI_TYPE_SAFE); + goto fail; + } + arg = strsep(&cp, " "); /* tests */ + if (cp == NULL || *arg == '\0') + goto truncated; + /* Ensure prime has been tested and is not composite */ + n = strtonum(arg, 0, 0x1f, &errstr); + if (errstr != NULL || + (n & MODULI_TESTS_COMPOSITE) || !(n & ~MODULI_TESTS_COMPOSITE)) { + error("moduli:%d: invalid moduli tests flag", linenum); + goto fail; + } + arg = strsep(&cp, " "); /* tries */ + if (cp == NULL || *arg == '\0') + goto truncated; + n = strtonum(arg, 0, 1<<30, &errstr); + if (errstr != NULL || n == 0) { + error("moduli:%d: invalid primality trial count", linenum); + goto fail; + } + strsize = strsep(&cp, " "); /* size */ + if (cp == NULL || *strsize == '\0' || + (dhg->size = (int)strtonum(strsize, 0, 64*1024, &errstr)) == 0 || + errstr) { + error("moduli:%d: invalid prime length", linenum); + goto fail; + } + /* The whole group is one bit larger */ + dhg->size++; + gen = strsep(&cp, " "); /* gen */ + if (cp == NULL || *gen == '\0') + goto truncated; + prime = strsep(&cp, " "); /* prime */ + if (cp != NULL || *prime == '\0') { + truncated: + error("moduli:%d: truncated", linenum); + goto fail; + } + + if ((dhg->g = BN_new()) == NULL || + (dhg->p = BN_new()) == NULL) { + error("parse_prime: BN_new failed"); + goto fail; + } + if (BN_hex2bn(&dhg->g, gen) == 0) { + error("moduli:%d: could not parse generator value", linenum); + goto fail; + } + if (BN_hex2bn(&dhg->p, prime) == 0) { + error("moduli:%d: could not parse prime value", linenum); + goto fail; + } + if (BN_num_bits(dhg->p) != dhg->size) { + error("moduli:%d: prime has wrong size: actual %d listed %d", + linenum, BN_num_bits(dhg->p), dhg->size - 1); + goto fail; + } + if (BN_cmp(dhg->g, BN_value_one()) <= 0) { + error("moduli:%d: generator is invalid", linenum); + goto fail; + } + return 1; + + fail: + BN_clear_free(dhg->g); + BN_clear_free(dhg->p); + dhg->g = dhg->p = NULL; + return 0; +} + +DH * +choose_dh(int min, int wantbits, int max) +{ + FILE *f; + char *line = NULL; + size_t linesize = 0; + int best, bestcount, which, linenum; + struct dhgroup dhg; + + if ((f = fopen(get_moduli_filename(), "r")) == NULL) { + logit("WARNING: could not open %s (%s), using fixed modulus", + get_moduli_filename(), strerror(errno)); + return (dh_new_group_fallback(max)); + } + + linenum = 0; + best = bestcount = 0; + while (getline(&line, &linesize, f) != -1) { + linenum++; + if (!parse_prime(linenum, line, &dhg)) + continue; + BN_clear_free(dhg.g); + BN_clear_free(dhg.p); + + if (dhg.size > max || dhg.size < min) + continue; + + if ((dhg.size > wantbits && dhg.size < best) || + (dhg.size > best && best < wantbits)) { + best = dhg.size; + bestcount = 0; + } + if (dhg.size == best) + bestcount++; + } + free(line); + line = NULL; + linesize = 0; + rewind(f); + + if (bestcount == 0) { + fclose(f); + logit("WARNING: no suitable primes in %s", + get_moduli_filename()); + return (dh_new_group_fallback(max)); + } + which = arc4random_uniform(bestcount); + + linenum = 0; + bestcount = 0; + while (getline(&line, &linesize, f) != -1) { + linenum++; + if (!parse_prime(linenum, line, &dhg)) + continue; + if ((dhg.size > max || dhg.size < min) || + dhg.size != best || + bestcount++ != which) { + BN_clear_free(dhg.g); + BN_clear_free(dhg.p); + continue; + } + break; + } + free(line); + line = NULL; + fclose(f); + if (bestcount != which + 1) { + logit("WARNING: selected prime disappeared in %s, giving up", + get_moduli_filename()); + return (dh_new_group_fallback(max)); + } + + return (dh_new_group(dhg.g, dhg.p)); +} + +/* diffie-hellman-groupN-sha1 */ + +int +dh_pub_is_valid(const DH *dh, const BIGNUM *dh_pub) +{ + int i; + int n = BN_num_bits(dh_pub); + int bits_set = 0; + BIGNUM *tmp; + const BIGNUM *dh_p; + + DH_get0_pqg(dh, &dh_p, NULL, NULL); + + if (BN_is_negative(dh_pub)) { + logit("invalid public DH value: negative"); + return 0; + } + if (BN_cmp(dh_pub, BN_value_one()) != 1) { /* pub_exp <= 1 */ + logit("invalid public DH value: <= 1"); + return 0; + } + + if ((tmp = BN_new()) == NULL) { + error_f("BN_new failed"); + return 0; + } + if (!BN_sub(tmp, dh_p, BN_value_one()) || + BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */ + BN_clear_free(tmp); + logit("invalid public DH value: >= p-1"); + return 0; + } + BN_clear_free(tmp); + + for (i = 0; i <= n; i++) + if (BN_is_bit_set(dh_pub, i)) + bits_set++; + debug2("bits set: %d/%d", bits_set, BN_num_bits(dh_p)); + + /* + * if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial + */ + if (bits_set < 4) { + logit("invalid public DH value (%d/%d)", + bits_set, BN_num_bits(dh_p)); + return 0; + } + return 1; +} + +int +dh_gen_key(DH *dh, int need) +{ + int pbits; + const BIGNUM *dh_p, *pub_key; + + DH_get0_pqg(dh, &dh_p, NULL, NULL); + + if (need < 0 || dh_p == NULL || + (pbits = BN_num_bits(dh_p)) <= 0 || + need > INT_MAX / 2 || 2 * need > pbits) + return SSH_ERR_INVALID_ARGUMENT; + if (need < 256) + need = 256; + /* + * Pollard Rho, Big step/Little Step attacks are O(sqrt(n)), + * so double requested need here. + */ + if (!DH_set_length1(dh, MINIMUM(need * 2, pbits - 1))) + return SSH_ERR_LIBCRYPTO_ERROR; + + if (DH_generate_key(dh) == 0) + return SSH_ERR_LIBCRYPTO_ERROR; + DH_get0_key(dh, &pub_key, NULL); + if (!dh_pub_is_valid(dh, pub_key)) + return SSH_ERR_INVALID_FORMAT; + return 0; +} + +DH * +dh_new_group_asc(const char *gen, const char *modulus) +{ + DH *dh; + BIGNUM *dh_p = NULL, *dh_g = NULL; + + if ((dh = DH_new()) == NULL) + return NULL; + if (BN_hex2bn(&dh_p, modulus) == 0 || + BN_hex2bn(&dh_g, gen) == 0) + goto fail; + if (!DH_set0_pqg(dh, dh_p, NULL, dh_g)) + goto fail; + return dh; + fail: + DH_free(dh); + BN_clear_free(dh_p); + BN_clear_free(dh_g); + return NULL; +} + +/* + * This just returns the group, we still need to generate the exchange + * value. + */ +DH * +dh_new_group(BIGNUM *gen, BIGNUM *modulus) +{ + DH *dh; + + if ((dh = DH_new()) == NULL) + return NULL; + if (!DH_set0_pqg(dh, modulus, NULL, gen)) { + DH_free(dh); + return NULL; + } + + return dh; +} + +/* rfc2409 "Second Oakley Group" (1024 bits) */ +DH * +dh_new_group1(void) +{ + static char *gen = "2", *group1 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" + "FFFFFFFF" "FFFFFFFF"; + + return (dh_new_group_asc(gen, group1)); +} + +/* rfc3526 group 14 "2048-bit MODP Group" */ +DH * +dh_new_group14(void) +{ + static char *gen = "2", *group14 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" + "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" + "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" + "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" + "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF"; + + return (dh_new_group_asc(gen, group14)); +} + +/* rfc3526 group 16 "4096-bit MODP Group" */ +DH * +dh_new_group16(void) +{ + static char *gen = "2", *group16 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" + "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" + "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" + "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" + "15728E5A" "8AAAC42D" "AD33170D" "04507A33" "A85521AB" "DF1CBA64" + "ECFB8504" "58DBEF0A" "8AEA7157" "5D060C7D" "B3970F85" "A6E1E4C7" + "ABF5AE8C" "DB0933D7" "1E8C94E0" "4A25619D" "CEE3D226" "1AD2EE6B" + "F12FFA06" "D98A0864" "D8760273" "3EC86A64" "521F2B18" "177B200C" + "BBE11757" "7A615D6C" "770988C0" "BAD946E2" "08E24FA0" "74E5AB31" + "43DB5BFC" "E0FD108E" "4B82D120" "A9210801" "1A723C12" "A787E6D7" + "88719A10" "BDBA5B26" "99C32718" "6AF4E23C" "1A946834" "B6150BDA" + "2583E9CA" "2AD44CE8" "DBBBC2DB" "04DE8EF9" "2E8EFC14" "1FBECAA6" + "287C5947" "4E6BC05D" "99B2964F" "A090C3A2" "233BA186" "515BE7ED" + "1F612970" "CEE2D7AF" "B81BDD76" "2170481C" "D0069127" "D5B05AA9" + "93B4EA98" "8D8FDDC1" "86FFB7DC" "90A6C08F" "4DF435C9" "34063199" + "FFFFFFFF" "FFFFFFFF"; + + return (dh_new_group_asc(gen, group16)); +} + +/* rfc3526 group 18 "8192-bit MODP Group" */ +DH * +dh_new_group18(void) +{ + static char *gen = "2", *group18 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" + "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" + "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" + "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" + "15728E5A" "8AAAC42D" "AD33170D" "04507A33" "A85521AB" "DF1CBA64" + "ECFB8504" "58DBEF0A" "8AEA7157" "5D060C7D" "B3970F85" "A6E1E4C7" + "ABF5AE8C" "DB0933D7" "1E8C94E0" "4A25619D" "CEE3D226" "1AD2EE6B" + "F12FFA06" "D98A0864" "D8760273" "3EC86A64" "521F2B18" "177B200C" + "BBE11757" "7A615D6C" "770988C0" "BAD946E2" "08E24FA0" "74E5AB31" + "43DB5BFC" "E0FD108E" "4B82D120" "A9210801" "1A723C12" "A787E6D7" + "88719A10" "BDBA5B26" "99C32718" "6AF4E23C" "1A946834" "B6150BDA" + "2583E9CA" "2AD44CE8" "DBBBC2DB" "04DE8EF9" "2E8EFC14" "1FBECAA6" + "287C5947" "4E6BC05D" "99B2964F" "A090C3A2" "233BA186" "515BE7ED" + "1F612970" "CEE2D7AF" "B81BDD76" "2170481C" "D0069127" "D5B05AA9" + "93B4EA98" "8D8FDDC1" "86FFB7DC" "90A6C08F" "4DF435C9" "34028492" + "36C3FAB4" "D27C7026" "C1D4DCB2" "602646DE" "C9751E76" "3DBA37BD" + "F8FF9406" "AD9E530E" "E5DB382F" "413001AE" "B06A53ED" "9027D831" + "179727B0" "865A8918" "DA3EDBEB" "CF9B14ED" "44CE6CBA" "CED4BB1B" + "DB7F1447" "E6CC254B" "33205151" "2BD7AF42" "6FB8F401" "378CD2BF" + "5983CA01" "C64B92EC" "F032EA15" "D1721D03" "F482D7CE" "6E74FEF6" + "D55E702F" "46980C82" "B5A84031" "900B1C9E" "59E7C97F" "BEC7E8F3" + "23A97A7E" "36CC88BE" "0F1D45B7" "FF585AC5" "4BD407B2" "2B4154AA" + "CC8F6D7E" "BF48E1D8" "14CC5ED2" "0F8037E0" "A79715EE" "F29BE328" + "06A1D58B" "B7C5DA76" "F550AA3D" "8A1FBFF0" "EB19CCB1" "A313D55C" + "DA56C9EC" "2EF29632" "387FE8D7" "6E3C0468" "043E8F66" "3F4860EE" + "12BF2D5B" "0B7474D6" "E694F91E" "6DBE1159" "74A3926F" "12FEE5E4" + "38777CB6" "A932DF8C" "D8BEC4D0" "73B931BA" "3BC832B6" "8D9DD300" + "741FA7BF" "8AFC47ED" "2576F693" "6BA42466" "3AAB639C" "5AE4F568" + "3423B474" "2BF1C978" "238F16CB" "E39D652D" "E3FDB8BE" "FC848AD9" + "22222E04" "A4037C07" "13EB57A8" "1A23F0C7" "3473FC64" "6CEA306B" + "4BCBC886" "2F8385DD" "FA9D4B7F" "A2C087E8" "79683303" "ED5BDD3A" + "062B3CF5" "B3A278A6" "6D2A13F8" "3F44F82D" "DF310EE0" "74AB6A36" + "4597E899" "A0255DC1" "64F31CC5" "0846851D" "F9AB4819" "5DED7EA1" + "B1D510BD" "7EE74D73" "FAF36BC3" "1ECFA268" "359046F4" "EB879F92" + "4009438B" "481C6CD7" "889A002E" "D5EE382B" "C9190DA6" "FC026E47" + "9558E447" "5677E9AA" "9E3050E2" "765694DF" "C81F56E8" "80B96E71" + "60C980DD" "98EDD3DF" "FFFFFFFF" "FFFFFFFF"; + + return (dh_new_group_asc(gen, group18)); +} + +/* Select fallback group used by DH-GEX if moduli file cannot be read. */ +DH * +dh_new_group_fallback(int max) +{ + debug3_f("requested max size %d", max); + if (max < 3072) { + debug3("using 2k bit group 14"); + return dh_new_group14(); + } else if (max < 6144) { + debug3("using 4k bit group 16"); + return dh_new_group16(); + } + debug3("using 8k bit group 18"); + return dh_new_group18(); +} + +/* + * Estimates the group order for a Diffie-Hellman group that has an + * attack complexity approximately the same as O(2**bits). + * Values from NIST Special Publication 800-57: Recommendation for Key + * Management Part 1 (rev 3) limited by the recommended maximum value + * from RFC4419 section 3. + */ +u_int +dh_estimate(int bits) +{ + if (bits <= 112) + return 2048; + if (bits <= 128) + return 3072; + if (bits <= 192) + return 7680; + return 8192; +} + +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/dh.h b/aosp/external/openssh/dh.h new file mode 100644 index 0000000000000000000000000000000000000000..c6326a39d53210dc064c4067901ccd48ef6db1e4 --- /dev/null +++ b/aosp/external/openssh/dh.h @@ -0,0 +1,84 @@ +/* $OpenBSD: dh.h,v 1.19 2021/03/12 04:08:19 dtucker Exp $ */ + +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef DH_H +#define DH_H + +#ifdef WITH_OPENSSL + +struct dhgroup { + int size; + BIGNUM *g; + BIGNUM *p; +}; + +DH *choose_dh(int, int, int); +DH *dh_new_group_asc(const char *, const char *); +DH *dh_new_group(BIGNUM *, BIGNUM *); +DH *dh_new_group1(void); +DH *dh_new_group14(void); +DH *dh_new_group16(void); +DH *dh_new_group18(void); +DH *dh_new_group_fallback(int); + +int dh_gen_key(DH *, int); +int dh_pub_is_valid(const DH *, const BIGNUM *); + +u_int dh_estimate(int); +void dh_set_moduli_file(const char *); + +/* + * Max value from RFC4419. + * Min value from RFC8270. + */ +#define DH_GRP_MIN 2048 +#define DH_GRP_MAX 8192 + +/* + * Values for "type" field of moduli(5) + * Specifies the internal structure of the prime modulus. + */ +#define MODULI_TYPE_UNKNOWN (0) +#define MODULI_TYPE_UNSTRUCTURED (1) +#define MODULI_TYPE_SAFE (2) +#define MODULI_TYPE_SCHNORR (3) +#define MODULI_TYPE_SOPHIE_GERMAIN (4) +#define MODULI_TYPE_STRONG (5) + +/* + * Values for "tests" field of moduli(5) + * Specifies the methods used in checking for primality. + * Usually, more than one test is used. + */ +#define MODULI_TESTS_UNTESTED (0x00) +#define MODULI_TESTS_COMPOSITE (0x01) +#define MODULI_TESTS_SIEVE (0x02) +#define MODULI_TESTS_MILLER_RABIN (0x04) +#define MODULI_TESTS_JACOBI (0x08) +#define MODULI_TESTS_ELLIPTIC (0x10) + +#endif /* WITH_OPENSSL */ + +#endif /* DH_H */ diff --git a/aosp/external/openssh/digest-libc.c b/aosp/external/openssh/digest-libc.c new file mode 100644 index 0000000000000000000000000000000000000000..6e77a4492dd40e378153b69a9d45abc1cd1f6a69 --- /dev/null +++ b/aosp/external/openssh/digest-libc.c @@ -0,0 +1,267 @@ +/* $OpenBSD: digest-libc.c,v 1.7 2020/02/26 13:40:09 jsg Exp $ */ +/* + * Copyright (c) 2013 Damien Miller + * Copyright (c) 2014 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifndef WITH_OPENSSL + +#include +#include +#include +#include + +#if 0 +#include +#include +#endif +#ifdef HAVE_SHA1_H +#include +#endif +#ifdef HAVE_SHA2_H +#include +#endif + +#if !defined(SHA256_BLOCK_LENGTH) && defined(SHA256_HMAC_BLOCK_SIZE) +#define SHA256_BLOCK_LENGTH SHA256_HMAC_BLOCK_SIZE +#endif +#if !defined(SHA384_BLOCK_LENGTH) && defined(SHA512_HMAC_BLOCK_SIZE) +#define SHA384_BLOCK_LENGTH SHA512_HMAC_BLOCK_SIZE +#endif +#if !defined(SHA512_BLOCK_LENGTH) && defined(SHA512_HMAC_BLOCK_SIZE) +#define SHA512_BLOCK_LENGTH SHA512_HMAC_BLOCK_SIZE +#endif + +#include "ssherr.h" +#include "sshbuf.h" +#include "digest.h" + +typedef void md_init_fn(void *mdctx); +typedef void md_update_fn(void *mdctx, const u_int8_t *m, size_t mlen); +typedef void md_final_fn(u_int8_t[], void *mdctx); + +struct ssh_digest_ctx { + int alg; + void *mdctx; +}; + +struct ssh_digest { + int id; + const char *name; + size_t block_len; + size_t digest_len; + size_t ctx_len; + md_init_fn *md_init; + md_update_fn *md_update; + md_final_fn *md_final; +}; + +/* NB. Indexed directly by algorithm number */ +const struct ssh_digest digests[SSH_DIGEST_MAX] = { + { + SSH_DIGEST_MD5, + "MD5", + MD5_BLOCK_LENGTH, + MD5_DIGEST_LENGTH, + sizeof(MD5_CTX), + (md_init_fn *) MD5Init, + (md_update_fn *) MD5Update, + (md_final_fn *) MD5Final + }, + { + SSH_DIGEST_SHA1, + "SHA1", + SHA1_BLOCK_LENGTH, + SHA1_DIGEST_LENGTH, + sizeof(SHA1_CTX), + (md_init_fn *) SHA1Init, + (md_update_fn *) SHA1Update, + (md_final_fn *) SHA1Final + }, + { + SSH_DIGEST_SHA256, + "SHA256", + SHA256_BLOCK_LENGTH, + SHA256_DIGEST_LENGTH, + sizeof(SHA2_CTX), + (md_init_fn *) SHA256Init, + (md_update_fn *) SHA256Update, + (md_final_fn *) SHA256Final + }, + { + SSH_DIGEST_SHA384, + "SHA384", + SHA384_BLOCK_LENGTH, + SHA384_DIGEST_LENGTH, + sizeof(SHA2_CTX), + (md_init_fn *) SHA384Init, + (md_update_fn *) SHA384Update, + (md_final_fn *) SHA384Final + }, + { + SSH_DIGEST_SHA512, + "SHA512", + SHA512_BLOCK_LENGTH, + SHA512_DIGEST_LENGTH, + sizeof(SHA2_CTX), + (md_init_fn *) SHA512Init, + (md_update_fn *) SHA512Update, + (md_final_fn *) SHA512Final + } +}; + +static const struct ssh_digest * +ssh_digest_by_alg(int alg) +{ + if (alg < 0 || alg >= SSH_DIGEST_MAX) + return NULL; + if (digests[alg].id != alg) /* sanity */ + return NULL; + return &(digests[alg]); +} + +int +ssh_digest_alg_by_name(const char *name) +{ + int alg; + + for (alg = 0; alg < SSH_DIGEST_MAX; alg++) { + if (strcasecmp(name, digests[alg].name) == 0) + return digests[alg].id; + } + return -1; +} + +const char * +ssh_digest_alg_name(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + + return digest == NULL ? NULL : digest->name; +} + +size_t +ssh_digest_bytes(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + + return digest == NULL ? 0 : digest->digest_len; +} + +size_t +ssh_digest_blocksize(struct ssh_digest_ctx *ctx) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); + + return digest == NULL ? 0 : digest->block_len; +} + +struct ssh_digest_ctx * +ssh_digest_start(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + struct ssh_digest_ctx *ret; + + if (digest == NULL || (ret = calloc(1, sizeof(*ret))) == NULL) + return NULL; + if ((ret->mdctx = calloc(1, digest->ctx_len)) == NULL) { + free(ret); + return NULL; + } + ret->alg = alg; + digest->md_init(ret->mdctx); + return ret; +} + +int +ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(from->alg); + + if (digest == NULL || from->alg != to->alg) + return SSH_ERR_INVALID_ARGUMENT; + memcpy(to->mdctx, from->mdctx, digest->ctx_len); + return 0; +} + +int +ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); + + if (digest == NULL) + return SSH_ERR_INVALID_ARGUMENT; + digest->md_update(ctx->mdctx, m, mlen); + return 0; +} + +int +ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b) +{ + return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b)); +} + +int +ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); + + if (digest == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen > UINT_MAX) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen < digest->digest_len) /* No truncation allowed */ + return SSH_ERR_INVALID_ARGUMENT; + digest->md_final(d, ctx->mdctx); + return 0; +} + +void +ssh_digest_free(struct ssh_digest_ctx *ctx) +{ + const struct ssh_digest *digest; + + if (ctx != NULL) { + digest = ssh_digest_by_alg(ctx->alg); + if (digest) { + explicit_bzero(ctx->mdctx, digest->ctx_len); + free(ctx->mdctx); + freezero(ctx, sizeof(*ctx)); + } + } +} + +int +ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) +{ + struct ssh_digest_ctx *ctx = ssh_digest_start(alg); + + if (ctx == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (ssh_digest_update(ctx, m, mlen) != 0 || + ssh_digest_final(ctx, d, dlen) != 0) + return SSH_ERR_INVALID_ARGUMENT; + ssh_digest_free(ctx); + return 0; +} + +int +ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) +{ + return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen); +} +#endif /* !WITH_OPENSSL */ diff --git a/aosp/external/openssh/digest-openssl.c b/aosp/external/openssh/digest-openssl.c new file mode 100644 index 0000000000000000000000000000000000000000..e073a807b148303348b949c5567cff23b32b3f48 --- /dev/null +++ b/aosp/external/openssh/digest-openssl.c @@ -0,0 +1,207 @@ +/* $OpenBSD: digest-openssl.c,v 1.9 2020/10/29 02:52:43 djm Exp $ */ +/* + * Copyright (c) 2013 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include +#include +#include +#include + +#include + +#include "openbsd-compat/openssl-compat.h" + +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" + +#ifndef HAVE_EVP_SHA256 +# define EVP_sha256 NULL +#endif +#ifndef HAVE_EVP_SHA384 +# define EVP_sha384 NULL +#endif +#ifndef HAVE_EVP_SHA512 +# define EVP_sha512 NULL +#endif + +struct ssh_digest_ctx { + int alg; + EVP_MD_CTX *mdctx; +}; + +struct ssh_digest { + int id; + const char *name; + size_t digest_len; + const EVP_MD *(*mdfunc)(void); +}; + +/* NB. Indexed directly by algorithm number */ +const struct ssh_digest digests[] = { + { SSH_DIGEST_MD5, "MD5", 16, EVP_md5 }, + { SSH_DIGEST_SHA1, "SHA1", 20, EVP_sha1 }, + { SSH_DIGEST_SHA256, "SHA256", 32, EVP_sha256 }, + { SSH_DIGEST_SHA384, "SHA384", 48, EVP_sha384 }, + { SSH_DIGEST_SHA512, "SHA512", 64, EVP_sha512 }, + { -1, NULL, 0, NULL }, +}; + +static const struct ssh_digest * +ssh_digest_by_alg(int alg) +{ + if (alg < 0 || alg >= SSH_DIGEST_MAX) + return NULL; + if (digests[alg].id != alg) /* sanity */ + return NULL; + if (digests[alg].mdfunc == NULL) + return NULL; + return &(digests[alg]); +} + +int +ssh_digest_alg_by_name(const char *name) +{ + int alg; + + for (alg = 0; digests[alg].id != -1; alg++) { + if (strcasecmp(name, digests[alg].name) == 0) + return digests[alg].id; + } + return -1; +} + +const char * +ssh_digest_alg_name(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + + return digest == NULL ? NULL : digest->name; +} + +size_t +ssh_digest_bytes(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + + return digest == NULL ? 0 : digest->digest_len; +} + +size_t +ssh_digest_blocksize(struct ssh_digest_ctx *ctx) +{ + return EVP_MD_CTX_block_size(ctx->mdctx); +} + +struct ssh_digest_ctx * +ssh_digest_start(int alg) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + struct ssh_digest_ctx *ret; + + if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL)) + return NULL; + ret->alg = alg; + if ((ret->mdctx = EVP_MD_CTX_new()) == NULL) { + free(ret); + return NULL; + } + if (EVP_DigestInit_ex(ret->mdctx, digest->mdfunc(), NULL) != 1) { + ssh_digest_free(ret); + return NULL; + } + return ret; +} + +int +ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) +{ + if (from->alg != to->alg) + return SSH_ERR_INVALID_ARGUMENT; + /* we have bcopy-style order while openssl has memcpy-style */ + if (!EVP_MD_CTX_copy_ex(to->mdctx, from->mdctx)) + return SSH_ERR_LIBCRYPTO_ERROR; + return 0; +} + +int +ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) +{ + if (EVP_DigestUpdate(ctx->mdctx, m, mlen) != 1) + return SSH_ERR_LIBCRYPTO_ERROR; + return 0; +} + +int +ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b) +{ + return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b)); +} + +int +ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); + u_int l = dlen; + + if (digest == NULL || dlen > UINT_MAX) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen < digest->digest_len) /* No truncation allowed */ + return SSH_ERR_INVALID_ARGUMENT; + if (EVP_DigestFinal_ex(ctx->mdctx, d, &l) != 1) + return SSH_ERR_LIBCRYPTO_ERROR; + if (l != digest->digest_len) /* sanity */ + return SSH_ERR_INTERNAL_ERROR; + return 0; +} + +void +ssh_digest_free(struct ssh_digest_ctx *ctx) +{ + if (ctx == NULL) + return; + EVP_MD_CTX_free(ctx->mdctx); + freezero(ctx, sizeof(*ctx)); +} + +int +ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) +{ + const struct ssh_digest *digest = ssh_digest_by_alg(alg); + u_int mdlen; + + if (digest == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen > UINT_MAX) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen < digest->digest_len) + return SSH_ERR_INVALID_ARGUMENT; + mdlen = dlen; + if (!EVP_Digest(m, mlen, d, &mdlen, digest->mdfunc(), NULL)) + return SSH_ERR_LIBCRYPTO_ERROR; + return 0; +} + +int +ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) +{ + return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen); +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/digest.h b/aosp/external/openssh/digest.h new file mode 100644 index 0000000000000000000000000000000000000000..274574d0e544df77df60e2b3fcf6dd825445f6e6 --- /dev/null +++ b/aosp/external/openssh/digest.h @@ -0,0 +1,70 @@ +/* $OpenBSD: digest.h,v 1.8 2017/05/08 22:57:38 djm Exp $ */ +/* + * Copyright (c) 2013 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DIGEST_H +#define _DIGEST_H + +/* Maximum digest output length */ +#define SSH_DIGEST_MAX_LENGTH 64 + +/* Digest algorithms */ +#define SSH_DIGEST_MD5 0 +#define SSH_DIGEST_SHA1 1 +#define SSH_DIGEST_SHA256 2 +#define SSH_DIGEST_SHA384 3 +#define SSH_DIGEST_SHA512 4 +#define SSH_DIGEST_MAX 5 + +struct sshbuf; +struct ssh_digest_ctx; + +/* Looks up a digest algorithm by name */ +int ssh_digest_alg_by_name(const char *name); + +/* Returns the algorithm name for a digest identifier */ +const char *ssh_digest_alg_name(int alg); + +/* Returns the algorithm's digest length in bytes or 0 for invalid algorithm */ +size_t ssh_digest_bytes(int alg); + +/* Returns the block size of the digest, e.g. for implementing HMAC */ +size_t ssh_digest_blocksize(struct ssh_digest_ctx *ctx); + +/* Copies internal state of digest of 'from' to 'to' */ +int ssh_digest_copy_state(struct ssh_digest_ctx *from, + struct ssh_digest_ctx *to); + +/* One-shot API */ +int ssh_digest_memory(int alg, const void *m, size_t mlen, + u_char *d, size_t dlen) + __attribute__((__bounded__(__buffer__, 2, 3))) + __attribute__((__bounded__(__buffer__, 4, 5))); +int ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) + __attribute__((__bounded__(__buffer__, 3, 4))); + +/* Update API */ +struct ssh_digest_ctx *ssh_digest_start(int alg); +int ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) + __attribute__((__bounded__(__buffer__, 2, 3))); +int ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, + const struct sshbuf *b); +int ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) + __attribute__((__bounded__(__buffer__, 2, 3))); +void ssh_digest_free(struct ssh_digest_ctx *ctx); + +#endif /* _DIGEST_H */ + diff --git a/aosp/external/openssh/dispatch.c b/aosp/external/openssh/dispatch.c new file mode 100644 index 0000000000000000000000000000000000000000..6e4c501e05733accc58f07c9c51b8e66b19d0ea3 --- /dev/null +++ b/aosp/external/openssh/dispatch.c @@ -0,0 +1,135 @@ +/* $OpenBSD: dispatch.c,v 1.32 2019/01/19 21:33:13 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include + +#include "ssh2.h" +#include "log.h" +#include "dispatch.h" +#include "packet.h" +#include "compat.h" +#include "ssherr.h" + +int +dispatch_protocol_error(int type, u_int32_t seq, struct ssh *ssh) +{ + int r; + + logit("dispatch_protocol_error: type %d seq %u", type, seq); + if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || + (r = sshpkt_put_u32(ssh, seq)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s", __func__); + return 0; +} + +int +dispatch_protocol_ignore(int type, u_int32_t seq, struct ssh *ssh) +{ + logit("dispatch_protocol_ignore: type %d seq %u", type, seq); + return 0; +} + +void +ssh_dispatch_init(struct ssh *ssh, dispatch_fn *dflt) +{ + u_int i; + for (i = 0; i < DISPATCH_MAX; i++) + ssh->dispatch[i] = dflt; +} + +void +ssh_dispatch_range(struct ssh *ssh, u_int from, u_int to, dispatch_fn *fn) +{ + u_int i; + + for (i = from; i <= to; i++) { + if (i >= DISPATCH_MAX) + break; + ssh->dispatch[i] = fn; + } +} + +void +ssh_dispatch_set(struct ssh *ssh, int type, dispatch_fn *fn) +{ + ssh->dispatch[type] = fn; +} + +int +ssh_dispatch_run(struct ssh *ssh, int mode, volatile sig_atomic_t *done) +{ + int r; + u_char type; + u_int32_t seqnr; + + for (;;) { + if (mode == DISPATCH_BLOCK) { + r = ssh_packet_read_seqnr(ssh, &type, &seqnr); + if (r != 0) + return r; + } else { + r = ssh_packet_read_poll_seqnr(ssh, &type, &seqnr); + if (r != 0) + return r; + if (type == SSH_MSG_NONE) + return 0; + } + if (type > 0 && type < DISPATCH_MAX && + ssh->dispatch[type] != NULL) { + if (ssh->dispatch_skip_packets) { + debug2("skipped packet (type %u)", type); + ssh->dispatch_skip_packets--; + continue; + } + r = (*ssh->dispatch[type])(type, seqnr, ssh); + if (r != 0) + return r; + } else { + r = sshpkt_disconnect(ssh, + "protocol error: rcvd type %d", type); + if (r != 0) + return r; + return SSH_ERR_DISCONNECTED; + } + if (done != NULL && *done) + return 0; + } +} + +void +ssh_dispatch_run_fatal(struct ssh *ssh, int mode, volatile sig_atomic_t *done) +{ + int r; + + if ((r = ssh_dispatch_run(ssh, mode, done)) != 0) + sshpkt_fatal(ssh, r, "%s", __func__); +} diff --git a/aosp/external/openssh/dispatch.h b/aosp/external/openssh/dispatch.h new file mode 100644 index 0000000000000000000000000000000000000000..a22d7749febbcb8322ec0d59206296618e0ba4be --- /dev/null +++ b/aosp/external/openssh/dispatch.h @@ -0,0 +1,49 @@ +/* $OpenBSD: dispatch.h,v 1.15 2019/01/19 21:45:31 djm Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DISPATCH_H +#define DISPATCH_H + +#define DISPATCH_MAX 255 + +enum { + DISPATCH_BLOCK, + DISPATCH_NONBLOCK +}; + +struct ssh; + +typedef int dispatch_fn(int, u_int32_t, struct ssh *); + +int dispatch_protocol_error(int, u_int32_t, struct ssh *); +int dispatch_protocol_ignore(int, u_int32_t, struct ssh *); +void ssh_dispatch_init(struct ssh *, dispatch_fn *); +void ssh_dispatch_set(struct ssh *, int, dispatch_fn *); +void ssh_dispatch_range(struct ssh *, u_int, u_int, dispatch_fn *); +int ssh_dispatch_run(struct ssh *, int, volatile sig_atomic_t *); +void ssh_dispatch_run_fatal(struct ssh *, int, volatile sig_atomic_t *); + +#endif diff --git a/aosp/external/openssh/dns.c b/aosp/external/openssh/dns.c new file mode 100644 index 0000000000000000000000000000000000000000..331089f575a4b419cb6fa93544bd71ff5d98ef26 --- /dev/null +++ b/aosp/external/openssh/dns.c @@ -0,0 +1,350 @@ +/* $OpenBSD: dns.c,v 1.42 2022/02/01 23:32:51 djm Exp $ */ + +/* + * Copyright (c) 2003 Wesley Griffin. All rights reserved. + * Copyright (c) 2003 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "sshkey.h" +#include "ssherr.h" +#include "dns.h" +#include "log.h" +#include "digest.h" + +static const char * const errset_text[] = { + "success", /* 0 ERRSET_SUCCESS */ + "out of memory", /* 1 ERRSET_NOMEMORY */ + "general failure", /* 2 ERRSET_FAIL */ + "invalid parameter", /* 3 ERRSET_INVAL */ + "name does not exist", /* 4 ERRSET_NONAME */ + "data does not exist", /* 5 ERRSET_NODATA */ +}; + +static const char * +dns_result_totext(unsigned int res) +{ + switch (res) { + case ERRSET_SUCCESS: + return errset_text[ERRSET_SUCCESS]; + case ERRSET_NOMEMORY: + return errset_text[ERRSET_NOMEMORY]; + case ERRSET_FAIL: + return errset_text[ERRSET_FAIL]; + case ERRSET_INVAL: + return errset_text[ERRSET_INVAL]; + case ERRSET_NONAME: + return errset_text[ERRSET_NONAME]; + case ERRSET_NODATA: + return errset_text[ERRSET_NODATA]; + default: + return "unknown error"; + } +} + +/* + * Read SSHFP parameters from key buffer. + * Caller must free digest which is allocated by sshkey_fingerprint_raw(). + */ +static int +dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, + u_char **digest, size_t *digest_len, struct sshkey *key) +{ + int r, success = 0; + int fp_alg = -1; + + switch (key->type) { + case KEY_RSA: + *algorithm = SSHFP_KEY_RSA; + break; + case KEY_DSA: + *algorithm = SSHFP_KEY_DSA; + break; + case KEY_ECDSA: + *algorithm = SSHFP_KEY_ECDSA; + break; + case KEY_ED25519: + *algorithm = SSHFP_KEY_ED25519; + break; + case KEY_XMSS: + *algorithm = SSHFP_KEY_XMSS; + break; + default: + *algorithm = SSHFP_KEY_RESERVED; /* 0 */ + } + + switch (*digest_type) { + case SSHFP_HASH_SHA1: + fp_alg = SSH_DIGEST_SHA1; + break; + case SSHFP_HASH_SHA256: + fp_alg = SSH_DIGEST_SHA256; + break; + default: + *digest_type = SSHFP_HASH_RESERVED; /* 0 */ + } + + if (*algorithm && *digest_type) { + if ((r = sshkey_fingerprint_raw(key, fp_alg, digest, + digest_len)) != 0) + fatal_fr(r, "sshkey_fingerprint_raw"); + success = 1; + } else { + *digest = NULL; + *digest_len = 0; + } + + return success; +} + +/* + * Read SSHFP parameters from rdata buffer. + */ +static int +dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, + u_char **digest, size_t *digest_len, u_char *rdata, int rdata_len) +{ + int success = 0; + + *algorithm = SSHFP_KEY_RESERVED; + *digest_type = SSHFP_HASH_RESERVED; + + if (rdata_len >= 2) { + *algorithm = rdata[0]; + *digest_type = rdata[1]; + *digest_len = rdata_len - 2; + + if (*digest_len > 0) { + *digest = xmalloc(*digest_len); + memcpy(*digest, rdata + 2, *digest_len); + } else { + *digest = (u_char *)xstrdup(""); + } + + success = 1; + } + + return success; +} + +/* + * Check if hostname is numerical. + * Returns -1 if hostname is numeric, 0 otherwise + */ +static int +is_numeric_hostname(const char *hostname) +{ + struct addrinfo hints, *ai; + + /* + * We shouldn't ever get a null host but if we do then log an error + * and return -1 which stops DNS key fingerprint processing. + */ + if (hostname == NULL) { + error("is_numeric_hostname called with NULL hostname"); + return -1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) { + freeaddrinfo(ai); + return -1; + } + + return 0; +} + +/* + * Verify the given hostname, address and host key using DNS. + * Returns 0 if lookup succeeds, -1 otherwise + */ +int +verify_host_key_dns(const char *hostname, struct sockaddr *address, + struct sshkey *hostkey, int *flags) +{ + u_int counter; + int result; + struct rrsetinfo *fingerprints = NULL; + + u_int8_t hostkey_algorithm; + u_char *hostkey_digest; + size_t hostkey_digest_len; + + u_int8_t dnskey_algorithm; + u_int8_t dnskey_digest_type; + u_char *dnskey_digest; + size_t dnskey_digest_len; + + *flags = 0; + + debug3("verify_host_key_dns"); + if (hostkey == NULL) + fatal("No key to look up!"); + + if (is_numeric_hostname(hostname)) { + debug("skipped DNS lookup for numerical hostname"); + return -1; + } + +#if !defined(ANDROID) + result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, + DNS_RDATATYPE_SSHFP, 0, &fingerprints); +#else + /* unsupported in Android. */ + result = -1; +#endif + + if (result) { + verbose("DNS lookup error: %s", dns_result_totext(result)); + return -1; + } + + if (fingerprints->rri_flags & RRSET_VALIDATED) { + *flags |= DNS_VERIFY_SECURE; + debug("found %d secure fingerprints in DNS", + fingerprints->rri_nrdatas); + } else { + debug("found %d insecure fingerprints in DNS", + fingerprints->rri_nrdatas); + } + + if (fingerprints->rri_nrdatas) + *flags |= DNS_VERIFY_FOUND; + + for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) { + /* + * Extract the key from the answer. Ignore any badly + * formatted fingerprints. + */ + if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, + &dnskey_digest, &dnskey_digest_len, + fingerprints->rri_rdatas[counter].rdi_data, + fingerprints->rri_rdatas[counter].rdi_length)) { + verbose("Error parsing fingerprint from DNS."); + continue; + } + debug3_f("checking SSHFP type %d fptype %d", dnskey_algorithm, + dnskey_digest_type); + + /* Calculate host key fingerprint. */ + if (!dns_read_key(&hostkey_algorithm, &dnskey_digest_type, + &hostkey_digest, &hostkey_digest_len, hostkey)) { + error("Error calculating key fingerprint."); +#if !defined(ANDROID) + freerrset(fingerprints); +#endif + return -1; + } + + /* Check if the current key is the same as the given key */ + if (hostkey_algorithm == dnskey_algorithm && + hostkey_digest_len == dnskey_digest_len) { + if (timingsafe_bcmp(hostkey_digest, dnskey_digest, + hostkey_digest_len) == 0) { + debug_f("matched SSHFP type %d fptype %d", + dnskey_algorithm, dnskey_digest_type); + *flags |= DNS_VERIFY_MATCH; + } else { + debug_f("failed SSHFP type %d fptype %d", + dnskey_algorithm, dnskey_digest_type); + *flags |= DNS_VERIFY_FAILED; + } + } + free(dnskey_digest); + free(hostkey_digest); /* from sshkey_fingerprint_raw() */ + } + +#if !defined(ANDROID) + freerrset(fingerprints); +#endif + + /* If any fingerprint failed to validate, return failure. */ + if (*flags & DNS_VERIFY_FAILED) + *flags &= ~DNS_VERIFY_MATCH; + + if (*flags & DNS_VERIFY_FOUND) + if (*flags & DNS_VERIFY_MATCH) + debug("matching host key fingerprint found in DNS"); + else + debug("mismatching host key fingerprint found in DNS"); + else + debug("no host key fingerprint found in DNS"); + + return 0; +} + +/* + * Export the fingerprint of a key as a DNS resource record + */ +int +export_dns_rr(const char *hostname, struct sshkey *key, FILE *f, int generic) +{ + u_int8_t rdata_pubkey_algorithm = 0; + u_int8_t rdata_digest_type = SSHFP_HASH_RESERVED; + u_int8_t dtype; + u_char *rdata_digest; + size_t i, rdata_digest_len; + int success = 0; + + for (dtype = SSHFP_HASH_SHA1; dtype < SSHFP_HASH_MAX; dtype++) { + rdata_digest_type = dtype; + if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, + &rdata_digest, &rdata_digest_len, key)) { + if (generic) { + fprintf(f, "%s IN TYPE%d \\# %zu %02x %02x ", + hostname, DNS_RDATATYPE_SSHFP, + 2 + rdata_digest_len, + rdata_pubkey_algorithm, rdata_digest_type); + } else { + fprintf(f, "%s IN SSHFP %d %d ", hostname, + rdata_pubkey_algorithm, rdata_digest_type); + } + for (i = 0; i < rdata_digest_len; i++) + fprintf(f, "%02x", rdata_digest[i]); + fprintf(f, "\n"); + free(rdata_digest); /* from sshkey_fingerprint_raw() */ + success = 1; + } + } + + /* No SSHFP record was generated at all */ + if (success == 0) { + error_f("unsupported algorithm and/or digest_type"); + } + + return success; +} diff --git a/aosp/external/openssh/dns.h b/aosp/external/openssh/dns.h new file mode 100644 index 0000000000000000000000000000000000000000..c9b61c4f28f89659a34ba1b6ee903e9e2d91fb7d --- /dev/null +++ b/aosp/external/openssh/dns.h @@ -0,0 +1,59 @@ +/* $OpenBSD: dns.h,v 1.19 2021/07/19 03:13:28 dtucker Exp $ */ + +/* + * Copyright (c) 2003 Wesley Griffin. All rights reserved. + * Copyright (c) 2003 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DNS_H +#define DNS_H + +enum sshfp_types { + SSHFP_KEY_RESERVED = 0, + SSHFP_KEY_RSA = 1, + SSHFP_KEY_DSA = 2, + SSHFP_KEY_ECDSA = 3, + SSHFP_KEY_ED25519 = 4, + SSHFP_KEY_XMSS = 5 +}; + +enum sshfp_hashes { + SSHFP_HASH_RESERVED = 0, + SSHFP_HASH_SHA1 = 1, + SSHFP_HASH_SHA256 = 2, + SSHFP_HASH_MAX = 3 +}; + +#define DNS_RDATACLASS_IN 1 +#define DNS_RDATATYPE_SSHFP 44 + +#define DNS_VERIFY_FOUND 0x00000001 +#define DNS_VERIFY_MATCH 0x00000002 +#define DNS_VERIFY_SECURE 0x00000004 +#define DNS_VERIFY_FAILED 0x00000008 + +int verify_host_key_dns(const char *, struct sockaddr *, + struct sshkey *, int *); +int export_dns_rr(const char *, struct sshkey *, FILE *, int); + +#endif /* DNS_H */ diff --git a/aosp/external/openssh/ed25519.c b/aosp/external/openssh/ed25519.c new file mode 100644 index 0000000000000000000000000000000000000000..767ec24d6df5c6c030a04cd2193d3070d7b067aa --- /dev/null +++ b/aosp/external/openssh/ed25519.c @@ -0,0 +1,144 @@ +/* $OpenBSD: ed25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ed25519.c + */ + +#include "includes.h" +#include "crypto_api.h" + +#include "ge25519.h" + +static void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen) +{ + unsigned long long i; + + for (i = 0;i < 32;++i) playground[i] = sm[i]; + for (i = 32;i < 64;++i) playground[i] = pk[i-32]; + for (i = 64;i < smlen;++i) playground[i] = sm[i]; + + crypto_hash_sha512(hram,playground,smlen); +} + + +int crypto_sign_ed25519_keypair( + unsigned char *pk, + unsigned char *sk + ) +{ + sc25519 scsk; + ge25519 gepk; + unsigned char extsk[64]; + int i; + + randombytes(sk, 32); + crypto_hash_sha512(extsk, sk, 32); + extsk[0] &= 248; + extsk[31] &= 127; + extsk[31] |= 64; + + sc25519_from32bytes(&scsk,extsk); + + ge25519_scalarmult_base(&gepk, &scsk); + ge25519_pack(pk, &gepk); + for(i=0;i<32;i++) + sk[32 + i] = pk[i]; + return 0; +} + +int crypto_sign_ed25519( + unsigned char *sm,unsigned long long *smlen, + const unsigned char *m,unsigned long long mlen, + const unsigned char *sk + ) +{ + sc25519 sck, scs, scsk; + ge25519 ger; + unsigned char r[32]; + unsigned char s[32]; + unsigned char extsk[64]; + unsigned long long i; + unsigned char hmg[crypto_hash_sha512_BYTES]; + unsigned char hram[crypto_hash_sha512_BYTES]; + + crypto_hash_sha512(extsk, sk, 32); + extsk[0] &= 248; + extsk[31] &= 127; + extsk[31] |= 64; + + *smlen = mlen+64; + for(i=0;i + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "openbsd-compat/openssl-compat.h" + +#include "ssh.h" +#include "misc.h" +#include "xmalloc.h" +#include "atomicio.h" +#include "pathnames.h" +#include "log.h" +#include "sshbuf.h" +#include "ssherr.h" + +/* + * Portable OpenSSH PRNG seeding: + * If OpenSSL has not "internally seeded" itself (e.g. pulled data from + * /dev/random), then collect RANDOM_SEED_SIZE bytes of randomness from + * PRNGd. + */ +#ifndef OPENSSL_PRNG_ONLY + +void +rexec_send_rng_seed(struct sshbuf *m) +{ + u_char buf[RANDOM_SEED_SIZE]; + size_t len = sizeof(buf); + int r; + + if (RAND_bytes(buf, sizeof(buf)) <= 0) { + error("Couldn't obtain random bytes (error %ld)", + ERR_get_error()); + len = 0; + } + if ((r = sshbuf_put_string(m, buf, len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + explicit_bzero(buf, sizeof(buf)); +} + +void +rexec_recv_rng_seed(struct sshbuf *m) +{ + const u_char *buf = NULL; + size_t len = 0; + int r; + + if ((r = sshbuf_get_string_direct(m, &buf, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + debug3("rexec_recv_rng_seed: seeding rng with %lu bytes", + (unsigned long)len); + RAND_add(buf, len, len); +} +#endif /* OPENSSL_PRNG_ONLY */ + +void +seed_rng(void) +{ + unsigned char buf[RANDOM_SEED_SIZE]; + + /* Initialise libcrypto */ + ssh_libcrypto_init(); + + if (!ssh_compatible_openssl(OPENSSL_VERSION_NUMBER, + OpenSSL_version_num())) + fatal("OpenSSL version mismatch. Built against %lx, you " + "have %lx", (u_long)OPENSSL_VERSION_NUMBER, + OpenSSL_version_num()); + +#ifndef OPENSSL_PRNG_ONLY + if (RAND_status() == 1) + debug3("RNG is ready, skipping seeding"); + else { + if (seed_from_prngd(buf, sizeof(buf)) == -1) + fatal("Could not obtain seed from PRNGd"); + RAND_add(buf, sizeof(buf), sizeof(buf)); + } +#endif /* OPENSSL_PRNG_ONLY */ + + if (RAND_status() != 1) + fatal("PRNG is not seeded"); + + /* Ensure arc4random() is primed */ + arc4random_buf(buf, sizeof(buf)); + explicit_bzero(buf, sizeof(buf)); +} + +#else /* WITH_OPENSSL */ + +#include +#include + +/* Actual initialisation is handled in arc4random() */ +void +seed_rng(void) +{ + unsigned char buf[RANDOM_SEED_SIZE]; + + /* Ensure arc4random() is primed */ + arc4random_buf(buf, sizeof(buf)); + explicit_bzero(buf, sizeof(buf)); +} + +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/entropy.h b/aosp/external/openssh/entropy.h new file mode 100644 index 0000000000000000000000000000000000000000..870164d30e90f18345c195cf4302749b135aa026 --- /dev/null +++ b/aosp/external/openssh/entropy.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1999-2000 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RANDOMS_H +#define _RANDOMS_H + +struct sshbuf; + +void seed_rng(void); +void rexec_send_rng_seed(struct sshbuf *); +void rexec_recv_rng_seed(struct sshbuf *); + +#endif /* _RANDOMS_H */ diff --git a/aosp/external/openssh/fatal.c b/aosp/external/openssh/fatal.c new file mode 100644 index 0000000000000000000000000000000000000000..16fbd320467a2ba5ff68a42e84b90ff31751ad45 --- /dev/null +++ b/aosp/external/openssh/fatal.c @@ -0,0 +1,46 @@ +/* $OpenBSD: fatal.c,v 1.11 2020/10/19 08:07:08 djm Exp $ */ +/* + * Copyright (c) 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include + +#include "log.h" + +/* Fatal messages. This function never returns. */ + +void +sshfatal(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sshlogv(file, func, line, showfunc, level, suffix, fmt, args); + va_end(args); + cleanup_exit(255); +} diff --git a/aosp/external/openssh/fe25519.c b/aosp/external/openssh/fe25519.c new file mode 100644 index 0000000000000000000000000000000000000000..e54fd154738b73605bd926c8f5b696cbf15689b2 --- /dev/null +++ b/aosp/external/openssh/fe25519.c @@ -0,0 +1,337 @@ +/* $OpenBSD: fe25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/fe25519.c + */ + +#include "includes.h" + +#define WINDOWSIZE 1 /* Should be 1,2, or 4 */ +#define WINDOWMASK ((1<>= 31; /* 1: yes; 0: no */ + return x; +} + +static crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ +{ + unsigned int x = a; + x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */ + x >>= 31; /* 0: yes; 1: no */ + x ^= 1; /* 1: yes; 0: no */ + return x; +} + +static crypto_uint32 times19(crypto_uint32 a) +{ + return (a << 4) + (a << 1) + a; +} + +static crypto_uint32 times38(crypto_uint32 a) +{ + return (a << 5) + (a << 2) + (a << 1); +} + +static void reduce_add_sub(fe25519 *r) +{ + crypto_uint32 t; + int i,rep; + + for(rep=0;rep<4;rep++) + { + t = r->v[31] >> 7; + r->v[31] &= 127; + t = times19(t); + r->v[0] += t; + for(i=0;i<31;i++) + { + t = r->v[i] >> 8; + r->v[i+1] += t; + r->v[i] &= 255; + } + } +} + +static void reduce_mul(fe25519 *r) +{ + crypto_uint32 t; + int i,rep; + + for(rep=0;rep<2;rep++) + { + t = r->v[31] >> 7; + r->v[31] &= 127; + t = times19(t); + r->v[0] += t; + for(i=0;i<31;i++) + { + t = r->v[i] >> 8; + r->v[i+1] += t; + r->v[i] &= 255; + } + } +} + +/* reduction modulo 2^255-19 */ +void fe25519_freeze(fe25519 *r) +{ + int i; + crypto_uint32 m = equal(r->v[31],127); + for(i=30;i>0;i--) + m &= equal(r->v[i],255); + m &= ge(r->v[0],237); + + m = -m; + + r->v[31] -= m&127; + for(i=30;i>0;i--) + r->v[i] -= m&255; + r->v[0] -= m&237; +} + +void fe25519_unpack(fe25519 *r, const unsigned char x[32]) +{ + int i; + for(i=0;i<32;i++) r->v[i] = x[i]; + r->v[31] &= 127; +} + +/* Assumes input x being reduced below 2^255 */ +void fe25519_pack(unsigned char r[32], const fe25519 *x) +{ + int i; + fe25519 y = *x; + fe25519_freeze(&y); + for(i=0;i<32;i++) + r[i] = y.v[i]; +} + +int fe25519_iszero(const fe25519 *x) +{ + int i; + int r; + fe25519 t = *x; + fe25519_freeze(&t); + r = equal(t.v[0],0); + for(i=1;i<32;i++) + r &= equal(t.v[i],0); + return r; +} + +int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y) +{ + int i; + fe25519 t1 = *x; + fe25519 t2 = *y; + fe25519_freeze(&t1); + fe25519_freeze(&t2); + for(i=0;i<32;i++) + if(t1.v[i] != t2.v[i]) return 0; + return 1; +} + +void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b) +{ + int i; + crypto_uint32 mask = b; + mask = -mask; + for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]); +} + +unsigned char fe25519_getparity(const fe25519 *x) +{ + fe25519 t = *x; + fe25519_freeze(&t); + return t.v[0] & 1; +} + +void fe25519_setone(fe25519 *r) +{ + int i; + r->v[0] = 1; + for(i=1;i<32;i++) r->v[i]=0; +} + +void fe25519_setzero(fe25519 *r) +{ + int i; + for(i=0;i<32;i++) r->v[i]=0; +} + +void fe25519_neg(fe25519 *r, const fe25519 *x) +{ + fe25519 t; + int i; + for(i=0;i<32;i++) t.v[i]=x->v[i]; + fe25519_setzero(r); + fe25519_sub(r, r, &t); +} + +void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y) +{ + int i; + for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i]; + reduce_add_sub(r); +} + +void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y) +{ + int i; + crypto_uint32 t[32]; + t[0] = x->v[0] + 0x1da; + t[31] = x->v[31] + 0xfe; + for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe; + for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i]; + reduce_add_sub(r); +} + +void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y) +{ + int i,j; + crypto_uint32 t[63]; + for(i=0;i<63;i++)t[i] = 0; + + for(i=0;i<32;i++) + for(j=0;j<32;j++) + t[i+j] += x->v[i] * y->v[j]; + + for(i=32;i<63;i++) + r->v[i-32] = t[i-32] + times38(t[i]); + r->v[31] = t[31]; /* result now in r[0]...r[31] */ + + reduce_mul(r); +} + +void fe25519_square(fe25519 *r, const fe25519 *x) +{ + fe25519_mul(r, x, x); +} + +void fe25519_invert(fe25519 *r, const fe25519 *x) +{ + fe25519 z2; + fe25519 z9; + fe25519 z11; + fe25519 z2_5_0; + fe25519 z2_10_0; + fe25519 z2_20_0; + fe25519 z2_50_0; + fe25519 z2_100_0; + fe25519 t0; + fe25519 t1; + int i; + + /* 2 */ fe25519_square(&z2,x); + /* 4 */ fe25519_square(&t1,&z2); + /* 8 */ fe25519_square(&t0,&t1); + /* 9 */ fe25519_mul(&z9,&t0,x); + /* 11 */ fe25519_mul(&z11,&z9,&z2); + /* 22 */ fe25519_square(&t0,&z11); + /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9); + + /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0); + /* 2^7 - 2^2 */ fe25519_square(&t1,&t0); + /* 2^8 - 2^3 */ fe25519_square(&t0,&t1); + /* 2^9 - 2^4 */ fe25519_square(&t1,&t0); + /* 2^10 - 2^5 */ fe25519_square(&t0,&t1); + /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0); + + /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0); + /* 2^12 - 2^2 */ fe25519_square(&t1,&t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); } + /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0); + + /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0); + /* 2^22 - 2^2 */ fe25519_square(&t1,&t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); } + /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0); + + /* 2^41 - 2^1 */ fe25519_square(&t1,&t0); + /* 2^42 - 2^2 */ fe25519_square(&t0,&t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); } + /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0); + + /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0); + /* 2^52 - 2^2 */ fe25519_square(&t1,&t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); } + /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0); + + /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0); + /* 2^102 - 2^2 */ fe25519_square(&t0,&t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); } + /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0); + + /* 2^201 - 2^1 */ fe25519_square(&t0,&t1); + /* 2^202 - 2^2 */ fe25519_square(&t1,&t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); } + /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0); + + /* 2^251 - 2^1 */ fe25519_square(&t1,&t0); + /* 2^252 - 2^2 */ fe25519_square(&t0,&t1); + /* 2^253 - 2^3 */ fe25519_square(&t1,&t0); + /* 2^254 - 2^4 */ fe25519_square(&t0,&t1); + /* 2^255 - 2^5 */ fe25519_square(&t1,&t0); + /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11); +} + +void fe25519_pow2523(fe25519 *r, const fe25519 *x) +{ + fe25519 z2; + fe25519 z9; + fe25519 z11; + fe25519 z2_5_0; + fe25519 z2_10_0; + fe25519 z2_20_0; + fe25519 z2_50_0; + fe25519 z2_100_0; + fe25519 t; + int i; + + /* 2 */ fe25519_square(&z2,x); + /* 4 */ fe25519_square(&t,&z2); + /* 8 */ fe25519_square(&t,&t); + /* 9 */ fe25519_mul(&z9,&t,x); + /* 11 */ fe25519_mul(&z11,&z9,&z2); + /* 22 */ fe25519_square(&t,&z11); + /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9); + + /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0); + /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); } + /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0); + + /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0); + /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); } + /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0); + + /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0); + /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); } + /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0); + + /* 2^41 - 2^1 */ fe25519_square(&t,&t); + /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); } + /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0); + + /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0); + /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); } + /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0); + + /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0); + /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); } + /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0); + + /* 2^201 - 2^1 */ fe25519_square(&t,&t); + /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); } + /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0); + + /* 2^251 - 2^1 */ fe25519_square(&t,&t); + /* 2^252 - 2^2 */ fe25519_square(&t,&t); + /* 2^252 - 3 */ fe25519_mul(r,&t,x); +} diff --git a/aosp/external/openssh/fe25519.h b/aosp/external/openssh/fe25519.h new file mode 100644 index 0000000000000000000000000000000000000000..41b3cbb49dc6f97ccf908fa65d5240ba87b965a9 --- /dev/null +++ b/aosp/external/openssh/fe25519.h @@ -0,0 +1,70 @@ +/* $OpenBSD: fe25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/fe25519.h + */ + +#ifndef FE25519_H +#define FE25519_H + +#include "crypto_api.h" + +#define fe25519 crypto_sign_ed25519_ref_fe25519 +#define fe25519_freeze crypto_sign_ed25519_ref_fe25519_freeze +#define fe25519_unpack crypto_sign_ed25519_ref_fe25519_unpack +#define fe25519_pack crypto_sign_ed25519_ref_fe25519_pack +#define fe25519_iszero crypto_sign_ed25519_ref_fe25519_iszero +#define fe25519_iseq_vartime crypto_sign_ed25519_ref_fe25519_iseq_vartime +#define fe25519_cmov crypto_sign_ed25519_ref_fe25519_cmov +#define fe25519_setone crypto_sign_ed25519_ref_fe25519_setone +#define fe25519_setzero crypto_sign_ed25519_ref_fe25519_setzero +#define fe25519_neg crypto_sign_ed25519_ref_fe25519_neg +#define fe25519_getparity crypto_sign_ed25519_ref_fe25519_getparity +#define fe25519_add crypto_sign_ed25519_ref_fe25519_add +#define fe25519_sub crypto_sign_ed25519_ref_fe25519_sub +#define fe25519_mul crypto_sign_ed25519_ref_fe25519_mul +#define fe25519_square crypto_sign_ed25519_ref_fe25519_square +#define fe25519_invert crypto_sign_ed25519_ref_fe25519_invert +#define fe25519_pow2523 crypto_sign_ed25519_ref_fe25519_pow2523 + +typedef struct +{ + crypto_uint32 v[32]; +} +fe25519; + +void fe25519_freeze(fe25519 *r); + +void fe25519_unpack(fe25519 *r, const unsigned char x[32]); + +void fe25519_pack(unsigned char r[32], const fe25519 *x); + +int fe25519_iszero(const fe25519 *x); + +int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y); + +void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b); + +void fe25519_setone(fe25519 *r); + +void fe25519_setzero(fe25519 *r); + +void fe25519_neg(fe25519 *r, const fe25519 *x); + +unsigned char fe25519_getparity(const fe25519 *x); + +void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y); + +void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y); + +void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y); + +void fe25519_square(fe25519 *r, const fe25519 *x); + +void fe25519_invert(fe25519 *r, const fe25519 *x); + +void fe25519_pow2523(fe25519 *r, const fe25519 *x); + +#endif diff --git a/aosp/external/openssh/fixalgorithms b/aosp/external/openssh/fixalgorithms new file mode 100644 index 0000000000000000000000000000000000000000..115dce81c71ec4ed4ed424f0ae7938164c7ef4db --- /dev/null +++ b/aosp/external/openssh/fixalgorithms @@ -0,0 +1,26 @@ +#!/bin/sh +# +# fixciphers - remove unsupported ciphers from man pages. +# Usage: fixpaths /path/to/sed cipher1 [cipher2] outfile +# +# Author: Darren Tucker (dtucker at zip com.au). Placed in the public domain. + +die() { + echo $* + exit -1 +} + +SED=$1 +shift + +for c in $*; do + subs="$subs -e /.Dq.$c.*$/d" + subs="$subs -e s/$c,//g" +done + +# now remove any entirely empty lines +subs="$subs -e /^$/d" + +${SED} $subs + +exit 0 diff --git a/aosp/external/openssh/fixpaths b/aosp/external/openssh/fixpaths new file mode 100644 index 0000000000000000000000000000000000000000..60a67990f173e053ed7bdd9da5a11c3d1955937e --- /dev/null +++ b/aosp/external/openssh/fixpaths @@ -0,0 +1,22 @@ +#!/bin/sh +# +# fixpaths - substitute makefile variables into text files +# Usage: fixpaths -Dsomething=somethingelse ... + +die() { + echo $* + exit -1 +} + +test -n "`echo $1|grep -- -D`" || \ + die $0: nothing to do - no substitutions listed! + +test -n "`echo $1|grep -- '-D[^=]\+=[^ ]\+'`" || \ + die $0: error in command line arguments. + +test -n "`echo $*|grep -- ' [^-]'`" || \ + die Usage: $0 '[-Dstring=replacement] [[infile] ...]' + +sed `echo $*|sed -e 's/-D\([^=]\+\)=\([^ ]*\)/-e s=\1=\2=g/g'` + +exit 0 diff --git a/aosp/external/openssh/ge25519.c b/aosp/external/openssh/ge25519.c new file mode 100644 index 0000000000000000000000000000000000000000..dfe3849b931b49df6e4cde6ad8877e5cc3ecc960 --- /dev/null +++ b/aosp/external/openssh/ge25519.c @@ -0,0 +1,321 @@ +/* $OpenBSD: ge25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519.c + */ + +#include "includes.h" + +#include "fe25519.h" +#include "sc25519.h" +#include "ge25519.h" + +/* + * Arithmetic on the twisted Edwards curve -x^2 + y^2 = 1 + dx^2y^2 + * with d = -(121665/121666) = 37095705934669439343138083508754565189542113879843219016388785533085940283555 + * Base point: (15112221349535400772501151409588531511454012693041857206046113283949847762202,46316835694926478169428394003475163141307993866256225615783033603165251855960); + */ + +/* d */ +static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00, + 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}}; +/* 2*d */ +static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00, + 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}}; +/* sqrt(-1) */ +static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F, + 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}}; + +#define ge25519_p3 ge25519 + +typedef struct +{ + fe25519 x; + fe25519 z; + fe25519 y; + fe25519 t; +} ge25519_p1p1; + +typedef struct +{ + fe25519 x; + fe25519 y; + fe25519 z; +} ge25519_p2; + +typedef struct +{ + fe25519 x; + fe25519 y; +} ge25519_aff; + + +/* Packed coordinates of the base point */ +const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69, + 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}}, + {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20, + 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}}; + +/* Multiples of the base point in affine representation */ +static const ge25519_aff ge25519_base_multiples_affine[425] = { +#include "ge25519_base.data" +}; + +static void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p) +{ + fe25519_mul(&r->x, &p->x, &p->t); + fe25519_mul(&r->y, &p->y, &p->z); + fe25519_mul(&r->z, &p->z, &p->t); +} + +static void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p) +{ + p1p1_to_p2((ge25519_p2 *)r, p); + fe25519_mul(&r->t, &p->x, &p->y); +} + +static void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q) +{ + fe25519 a,b,t1,t2,c,d,e,f,g,h,qt; + fe25519_mul(&qt, &q->x, &q->y); + fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */ + fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */ + fe25519_sub(&t1, &q->y, &q->x); + fe25519_add(&t2, &q->y, &q->x); + fe25519_mul(&a, &a, &t1); + fe25519_mul(&b, &b, &t2); + fe25519_sub(&e, &b, &a); /* E = B-A */ + fe25519_add(&h, &b, &a); /* H = B+A */ + fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */ + fe25519_mul(&c, &c, &ge25519_ec2d); + fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */ + fe25519_sub(&f, &d, &c); /* F = D-C */ + fe25519_add(&g, &d, &c); /* G = D+C */ + fe25519_mul(&r->x, &e, &f); + fe25519_mul(&r->y, &h, &g); + fe25519_mul(&r->z, &g, &f); + fe25519_mul(&r->t, &e, &h); +} + +static void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q) +{ + fe25519 a, b, c, d, t; + + fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */ + fe25519_sub(&t, &q->y, &q->x); + fe25519_mul(&a, &a, &t); + fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */ + fe25519_add(&t, &q->x, &q->y); + fe25519_mul(&b, &b, &t); + fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */ + fe25519_mul(&c, &c, &ge25519_ec2d); + fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */ + fe25519_add(&d, &d, &d); + fe25519_sub(&r->x, &b, &a); /* E = B-A */ + fe25519_sub(&r->t, &d, &c); /* F = D-C */ + fe25519_add(&r->z, &d, &c); /* G = D+C */ + fe25519_add(&r->y, &b, &a); /* H = B+A */ +} + +/* See http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd */ +static void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p) +{ + fe25519 a,b,c,d; + fe25519_square(&a, &p->x); + fe25519_square(&b, &p->y); + fe25519_square(&c, &p->z); + fe25519_add(&c, &c, &c); + fe25519_neg(&d, &a); + + fe25519_add(&r->x, &p->x, &p->y); + fe25519_square(&r->x, &r->x); + fe25519_sub(&r->x, &r->x, &a); + fe25519_sub(&r->x, &r->x, &b); + fe25519_add(&r->z, &d, &b); + fe25519_sub(&r->t, &r->z, &c); + fe25519_sub(&r->y, &d, &b); +} + +/* Constant-time version of: if(b) r = p */ +static void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b) +{ + fe25519_cmov(&r->x, &p->x, b); + fe25519_cmov(&r->y, &p->y, b); +} + +static unsigned char equal(signed char b,signed char c) +{ + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + crypto_uint32 y = x; /* 0: yes; 1..255: no */ + y -= 1; /* 4294967295: yes; 0..254: no */ + y >>= 31; /* 1: yes; 0: no */ + return y; +} + +static unsigned char negative(signed char b) +{ + unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return x; +} + +static void choose_t(ge25519_aff *t, unsigned long long pos, signed char b) +{ + /* constant time */ + fe25519 v; + *t = ge25519_base_multiples_affine[5*pos+0]; + cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1)); + cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2)); + cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3)); + cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4)); + fe25519_neg(&v, &t->x); + fe25519_cmov(&t->x, &v, negative(b)); +} + +static void setneutral(ge25519 *r) +{ + fe25519_setzero(&r->x); + fe25519_setone(&r->y); + fe25519_setone(&r->z); + fe25519_setzero(&r->t); +} + +/* ******************************************************************** + * EXPORTED FUNCTIONS + ******************************************************************** */ + +/* return 0 on success, -1 otherwise */ +int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32]) +{ + unsigned char par; + fe25519 t, chk, num, den, den2, den4, den6; + fe25519_setone(&r->z); + par = p[31] >> 7; + fe25519_unpack(&r->y, p); + fe25519_square(&num, &r->y); /* x = y^2 */ + fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */ + fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */ + fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */ + + /* Computation of sqrt(num/den) */ + /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */ + fe25519_square(&den2, &den); + fe25519_square(&den4, &den2); + fe25519_mul(&den6, &den4, &den2); + fe25519_mul(&t, &den6, &num); + fe25519_mul(&t, &t, &den); + + fe25519_pow2523(&t, &t); + /* 2. computation of r->x = t * num * den^3 */ + fe25519_mul(&t, &t, &num); + fe25519_mul(&t, &t, &den); + fe25519_mul(&t, &t, &den); + fe25519_mul(&r->x, &t, &den); + + /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */ + fe25519_square(&chk, &r->x); + fe25519_mul(&chk, &chk, &den); + if (!fe25519_iseq_vartime(&chk, &num)) + fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1); + + /* 4. Now we have one of the two square roots, except if input was not a square */ + fe25519_square(&chk, &r->x); + fe25519_mul(&chk, &chk, &den); + if (!fe25519_iseq_vartime(&chk, &num)) + return -1; + + /* 5. Choose the desired square root according to parity: */ + if(fe25519_getparity(&r->x) != (1-par)) + fe25519_neg(&r->x, &r->x); + + fe25519_mul(&r->t, &r->x, &r->y); + return 0; +} + +void ge25519_pack(unsigned char r[32], const ge25519_p3 *p) +{ + fe25519 tx, ty, zi; + fe25519_invert(&zi, &p->z); + fe25519_mul(&tx, &p->x, &zi); + fe25519_mul(&ty, &p->y, &zi); + fe25519_pack(r, &ty); + r[31] ^= fe25519_getparity(&tx) << 7; +} + +int ge25519_isneutral_vartime(const ge25519_p3 *p) +{ + int ret = 1; + if(!fe25519_iszero(&p->x)) ret = 0; + if(!fe25519_iseq_vartime(&p->y, &p->z)) ret = 0; + return ret; +} + +/* computes [s1]p1 + [s2]p2 */ +void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2) +{ + ge25519_p1p1 tp1p1; + ge25519_p3 pre[16]; + unsigned char b[127]; + int i; + + /* precomputation s2 s1 */ + setneutral(pre); /* 00 00 */ + pre[1] = *p1; /* 00 01 */ + dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */ + add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */ + pre[4] = *p2; /* 01 00 */ + add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */ + add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */ + add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */ + dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */ + add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */ + dbl_p1p1(&tp1p1,(ge25519_p2 *)&pre[5]); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */ + add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */ + add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */ + add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */ + add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */ + add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */ + + sc25519_2interleave2(b,s1,s2); + + /* scalar multiplication */ + *r = pre[b[126]]; + for(i=125;i>=0;i--) + { + dbl_p1p1(&tp1p1, (ge25519_p2 *)r); + p1p1_to_p2((ge25519_p2 *) r, &tp1p1); + dbl_p1p1(&tp1p1, (ge25519_p2 *)r); + if(b[i]!=0) + { + p1p1_to_p3(r, &tp1p1); + add_p1p1(&tp1p1, r, &pre[b[i]]); + } + if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1); + else p1p1_to_p3(r, &tp1p1); + } +} + +void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s) +{ + signed char b[85]; + int i; + ge25519_aff t; + sc25519_window3(b,s); + + choose_t((ge25519_aff *)r, 0, b[0]); + fe25519_setone(&r->z); + fe25519_mul(&r->t, &r->x, &r->y); + for(i=1;i<85;i++) + { + choose_t(&t, (unsigned long long) i, b[i]); + ge25519_mixadd2(r, &t); + } +} diff --git a/aosp/external/openssh/ge25519.h b/aosp/external/openssh/ge25519.h new file mode 100644 index 0000000000000000000000000000000000000000..a097637609317f2fc273ec4bf8f8cecce9fe0744 --- /dev/null +++ b/aosp/external/openssh/ge25519.h @@ -0,0 +1,43 @@ +/* $OpenBSD: ge25519.h,v 1.4 2015/02/16 18:26:26 miod Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519.h + */ + +#ifndef GE25519_H +#define GE25519_H + +#include "fe25519.h" +#include "sc25519.h" + +#define ge25519 crypto_sign_ed25519_ref_ge25519 +#define ge25519_base crypto_sign_ed25519_ref_ge25519_base +#define ge25519_unpackneg_vartime crypto_sign_ed25519_ref_unpackneg_vartime +#define ge25519_pack crypto_sign_ed25519_ref_pack +#define ge25519_isneutral_vartime crypto_sign_ed25519_ref_isneutral_vartime +#define ge25519_double_scalarmult_vartime crypto_sign_ed25519_ref_double_scalarmult_vartime +#define ge25519_scalarmult_base crypto_sign_ed25519_ref_scalarmult_base + +typedef struct +{ + fe25519 x; + fe25519 y; + fe25519 z; + fe25519 t; +} ge25519; + +extern const ge25519 ge25519_base; + +int ge25519_unpackneg_vartime(ge25519 *r, const unsigned char p[32]); + +void ge25519_pack(unsigned char r[32], const ge25519 *p); + +int ge25519_isneutral_vartime(const ge25519 *p); + +void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const sc25519 *s1, const ge25519 *p2, const sc25519 *s2); + +void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s); + +#endif diff --git a/aosp/external/openssh/ge25519_base.data b/aosp/external/openssh/ge25519_base.data new file mode 100644 index 0000000000000000000000000000000000000000..66fb1b61c67d2d30cec2781564903d90c9086a38 --- /dev/null +++ b/aosp/external/openssh/ge25519_base.data @@ -0,0 +1,858 @@ +/* $OpenBSD: ge25519_base.data,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519_base.data + */ + +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21}} , + {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}}, +{{{0x0e, 0xce, 0x43, 0x28, 0x4e, 0xa1, 0xc5, 0x83, 0x5f, 0xa4, 0xd7, 0x15, 0x45, 0x8e, 0x0d, 0x08, 0xac, 0xe7, 0x33, 0x18, 0x7d, 0x3b, 0x04, 0x3d, 0x6c, 0x04, 0x5a, 0x9f, 0x4c, 0x38, 0xab, 0x36}} , + {{0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0x0e, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39, 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd, 0x60, 0x22}}}, +{{{0x5c, 0xe2, 0xf8, 0xd3, 0x5f, 0x48, 0x62, 0xac, 0x86, 0x48, 0x62, 0x81, 0x19, 0x98, 0x43, 0x63, 0x3a, 0xc8, 0xda, 0x3e, 0x74, 0xae, 0xf4, 0x1f, 0x49, 0x8f, 0x92, 0x22, 0x4a, 0x9c, 0xae, 0x67}} , + {{0xd4, 0xb4, 0xf5, 0x78, 0x48, 0x68, 0xc3, 0x02, 0x04, 0x03, 0x24, 0x67, 0x17, 0xec, 0x16, 0x9f, 0xf7, 0x9e, 0x26, 0x60, 0x8e, 0xa1, 0x26, 0xa1, 0xab, 0x69, 0xee, 0x77, 0xd1, 0xb1, 0x67, 0x12}}}, +{{{0x70, 0xf8, 0xc9, 0xc4, 0x57, 0xa6, 0x3a, 0x49, 0x47, 0x15, 0xce, 0x93, 0xc1, 0x9e, 0x73, 0x1a, 0xf9, 0x20, 0x35, 0x7a, 0xb8, 0xd4, 0x25, 0x83, 0x46, 0xf1, 0xcf, 0x56, 0xdb, 0xa8, 0x3d, 0x20}} , + {{0x2f, 0x11, 0x32, 0xca, 0x61, 0xab, 0x38, 0xdf, 0xf0, 0x0f, 0x2f, 0xea, 0x32, 0x28, 0xf2, 0x4c, 0x6c, 0x71, 0xd5, 0x80, 0x85, 0xb8, 0x0e, 0x47, 0xe1, 0x95, 0x15, 0xcb, 0x27, 0xe8, 0xd0, 0x47}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xc8, 0x84, 0xa5, 0x08, 0xbc, 0xfd, 0x87, 0x3b, 0x99, 0x8b, 0x69, 0x80, 0x7b, 0xc6, 0x3a, 0xeb, 0x93, 0xcf, 0x4e, 0xf8, 0x5c, 0x2d, 0x86, 0x42, 0xb6, 0x71, 0xd7, 0x97, 0x5f, 0xe1, 0x42, 0x67}} , + {{0xb4, 0xb9, 0x37, 0xfc, 0xa9, 0x5b, 0x2f, 0x1e, 0x93, 0xe4, 0x1e, 0x62, 0xfc, 0x3c, 0x78, 0x81, 0x8f, 0xf3, 0x8a, 0x66, 0x09, 0x6f, 0xad, 0x6e, 0x79, 0x73, 0xe5, 0xc9, 0x00, 0x06, 0xd3, 0x21}}}, +{{{0xf8, 0xf9, 0x28, 0x6c, 0x6d, 0x59, 0xb2, 0x59, 0x74, 0x23, 0xbf, 0xe7, 0x33, 0x8d, 0x57, 0x09, 0x91, 0x9c, 0x24, 0x08, 0x15, 0x2b, 0xe2, 0xb8, 0xee, 0x3a, 0xe5, 0x27, 0x06, 0x86, 0xa4, 0x23}} , + {{0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a, 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96, 0x96, 0x70}}}, +{{{0x81, 0x6b, 0x88, 0xe8, 0x1e, 0xc7, 0x77, 0x96, 0x0e, 0xa1, 0xa9, 0x52, 0xe0, 0xd8, 0x0e, 0x61, 0x9e, 0x79, 0x2d, 0x95, 0x9c, 0x8d, 0x96, 0xe0, 0x06, 0x40, 0x5d, 0x87, 0x28, 0x5f, 0x98, 0x70}} , + {{0xf1, 0x79, 0x7b, 0xed, 0x4f, 0x44, 0xb2, 0xe7, 0x08, 0x0d, 0xc2, 0x08, 0x12, 0xd2, 0x9f, 0xdf, 0xcd, 0x93, 0x20, 0x8a, 0xcf, 0x33, 0xca, 0x6d, 0x89, 0xb9, 0x77, 0xc8, 0x93, 0x1b, 0x4e, 0x60}}}, +{{{0x26, 0x4f, 0x7e, 0x97, 0xf6, 0x40, 0xdd, 0x4f, 0xfc, 0x52, 0x78, 0xf9, 0x90, 0x31, 0x03, 0xe6, 0x7d, 0x56, 0x39, 0x0b, 0x1d, 0x56, 0x82, 0x85, 0xf9, 0x1a, 0x42, 0x17, 0x69, 0x6c, 0xcf, 0x39}} , + {{0x69, 0xd2, 0x06, 0x3a, 0x4f, 0x39, 0x2d, 0xf9, 0x38, 0x40, 0x8c, 0x4c, 0xe7, 0x05, 0x12, 0xb4, 0x78, 0x8b, 0xf8, 0xc0, 0xec, 0x93, 0xde, 0x7a, 0x6b, 0xce, 0x2c, 0xe1, 0x0e, 0xa9, 0x34, 0x44}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x0b, 0xa4, 0x3c, 0xb0, 0x0f, 0x7a, 0x51, 0xf1, 0x78, 0xd6, 0xd9, 0x6a, 0xfd, 0x46, 0xe8, 0xb8, 0xa8, 0x79, 0x1d, 0x87, 0xf9, 0x90, 0xf2, 0x9c, 0x13, 0x29, 0xf8, 0x0b, 0x20, 0x64, 0xfa, 0x05}} , + {{0x26, 0x09, 0xda, 0x17, 0xaf, 0x95, 0xd6, 0xfb, 0x6a, 0x19, 0x0d, 0x6e, 0x5e, 0x12, 0xf1, 0x99, 0x4c, 0xaa, 0xa8, 0x6f, 0x79, 0x86, 0xf4, 0x72, 0x28, 0x00, 0x26, 0xf9, 0xea, 0x9e, 0x19, 0x3d}}}, +{{{0x87, 0xdd, 0xcf, 0xf0, 0x5b, 0x49, 0xa2, 0x5d, 0x40, 0x7a, 0x23, 0x26, 0xa4, 0x7a, 0x83, 0x8a, 0xb7, 0x8b, 0xd2, 0x1a, 0xbf, 0xea, 0x02, 0x24, 0x08, 0x5f, 0x7b, 0xa9, 0xb1, 0xbe, 0x9d, 0x37}} , + {{0xfc, 0x86, 0x4b, 0x08, 0xee, 0xe7, 0xa0, 0xfd, 0x21, 0x45, 0x09, 0x34, 0xc1, 0x61, 0x32, 0x23, 0xfc, 0x9b, 0x55, 0x48, 0x53, 0x99, 0xf7, 0x63, 0xd0, 0x99, 0xce, 0x01, 0xe0, 0x9f, 0xeb, 0x28}}}, +{{{0x47, 0xfc, 0xab, 0x5a, 0x17, 0xf0, 0x85, 0x56, 0x3a, 0x30, 0x86, 0x20, 0x28, 0x4b, 0x8e, 0x44, 0x74, 0x3a, 0x6e, 0x02, 0xf1, 0x32, 0x8f, 0x9f, 0x3f, 0x08, 0x35, 0xe9, 0xca, 0x16, 0x5f, 0x6e}} , + {{0x1c, 0x59, 0x1c, 0x65, 0x5d, 0x34, 0xa4, 0x09, 0xcd, 0x13, 0x9c, 0x70, 0x7d, 0xb1, 0x2a, 0xc5, 0x88, 0xaf, 0x0b, 0x60, 0xc7, 0x9f, 0x34, 0x8d, 0xd6, 0xb7, 0x7f, 0xea, 0x78, 0x65, 0x8d, 0x77}}}, +{{{0x56, 0xa5, 0xc2, 0x0c, 0xdd, 0xbc, 0xb8, 0x20, 0x6d, 0x57, 0x61, 0xb5, 0xfb, 0x78, 0xb5, 0xd4, 0x49, 0x54, 0x90, 0x26, 0xc1, 0xcb, 0xe9, 0xe6, 0xbf, 0xec, 0x1d, 0x4e, 0xed, 0x07, 0x7e, 0x5e}} , + {{0xc7, 0xf6, 0x6c, 0x56, 0x31, 0x20, 0x14, 0x0e, 0xa8, 0xd9, 0x27, 0xc1, 0x9a, 0x3d, 0x1b, 0x7d, 0x0e, 0x26, 0xd3, 0x81, 0xaa, 0xeb, 0xf5, 0x6b, 0x79, 0x02, 0xf1, 0x51, 0x5c, 0x75, 0x55, 0x0f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x0a, 0x34, 0xcd, 0x82, 0x3c, 0x33, 0x09, 0x54, 0xd2, 0x61, 0x39, 0x30, 0x9b, 0xfd, 0xef, 0x21, 0x26, 0xd4, 0x70, 0xfa, 0xee, 0xf9, 0x31, 0x33, 0x73, 0x84, 0xd0, 0xb3, 0x81, 0xbf, 0xec, 0x2e}} , + {{0xe8, 0x93, 0x8b, 0x00, 0x64, 0xf7, 0x9c, 0xb8, 0x74, 0xe0, 0xe6, 0x49, 0x48, 0x4d, 0x4d, 0x48, 0xb6, 0x19, 0xa1, 0x40, 0xb7, 0xd9, 0x32, 0x41, 0x7c, 0x82, 0x37, 0xa1, 0x2d, 0xdc, 0xd2, 0x54}}}, +{{{0x68, 0x2b, 0x4a, 0x5b, 0xd5, 0xc7, 0x51, 0x91, 0x1d, 0xe1, 0x2a, 0x4b, 0xc4, 0x47, 0xf1, 0xbc, 0x7a, 0xb3, 0xcb, 0xc8, 0xb6, 0x7c, 0xac, 0x90, 0x05, 0xfd, 0xf3, 0xf9, 0x52, 0x3a, 0x11, 0x6b}} , + {{0x3d, 0xc1, 0x27, 0xf3, 0x59, 0x43, 0x95, 0x90, 0xc5, 0x96, 0x79, 0xf5, 0xf4, 0x95, 0x65, 0x29, 0x06, 0x9c, 0x51, 0x05, 0x18, 0xda, 0xb8, 0x2e, 0x79, 0x7e, 0x69, 0x59, 0x71, 0x01, 0xeb, 0x1a}}}, +{{{0x15, 0x06, 0x49, 0xb6, 0x8a, 0x3c, 0xea, 0x2f, 0x34, 0x20, 0x14, 0xc3, 0xaa, 0xd6, 0xaf, 0x2c, 0x3e, 0xbd, 0x65, 0x20, 0xe2, 0x4d, 0x4b, 0x3b, 0xeb, 0x9f, 0x4a, 0xc3, 0xad, 0xa4, 0x3b, 0x60}} , + {{0xbc, 0x58, 0xe6, 0xc0, 0x95, 0x2a, 0x2a, 0x81, 0x9a, 0x7a, 0xf3, 0xd2, 0x06, 0xbe, 0x48, 0xbc, 0x0c, 0xc5, 0x46, 0xe0, 0x6a, 0xd4, 0xac, 0x0f, 0xd9, 0xcc, 0x82, 0x34, 0x2c, 0xaf, 0xdb, 0x1f}}}, +{{{0xf7, 0x17, 0x13, 0xbd, 0xfb, 0xbc, 0xd2, 0xec, 0x45, 0xb3, 0x15, 0x31, 0xe9, 0xaf, 0x82, 0x84, 0x3d, 0x28, 0xc6, 0xfc, 0x11, 0xf5, 0x41, 0xb5, 0x8b, 0xd3, 0x12, 0x76, 0x52, 0xe7, 0x1a, 0x3c}} , + {{0x4e, 0x36, 0x11, 0x07, 0xa2, 0x15, 0x20, 0x51, 0xc4, 0x2a, 0xc3, 0x62, 0x8b, 0x5e, 0x7f, 0xa6, 0x0f, 0xf9, 0x45, 0x85, 0x6c, 0x11, 0x86, 0xb7, 0x7e, 0xe5, 0xd7, 0xf9, 0xc3, 0x91, 0x1c, 0x05}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xea, 0xd6, 0xde, 0x29, 0x3a, 0x00, 0xb9, 0x02, 0x59, 0xcb, 0x26, 0xc4, 0xba, 0x99, 0xb1, 0x97, 0x2f, 0x8e, 0x00, 0x92, 0x26, 0x4f, 0x52, 0xeb, 0x47, 0x1b, 0x89, 0x8b, 0x24, 0xc0, 0x13, 0x7d}} , + {{0xd5, 0x20, 0x5b, 0x80, 0xa6, 0x80, 0x20, 0x95, 0xc3, 0xe9, 0x9f, 0x8e, 0x87, 0x9e, 0x1e, 0x9e, 0x7a, 0xc7, 0xcc, 0x75, 0x6c, 0xa5, 0xf1, 0x91, 0x1a, 0xa8, 0x01, 0x2c, 0xab, 0x76, 0xa9, 0x59}}}, +{{{0xde, 0xc9, 0xb1, 0x31, 0x10, 0x16, 0xaa, 0x35, 0x14, 0x6a, 0xd4, 0xb5, 0x34, 0x82, 0x71, 0xd2, 0x4a, 0x5d, 0x9a, 0x1f, 0x53, 0x26, 0x3c, 0xe5, 0x8e, 0x8d, 0x33, 0x7f, 0xff, 0xa9, 0xd5, 0x17}} , + {{0x89, 0xaf, 0xf6, 0xa4, 0x64, 0xd5, 0x10, 0xe0, 0x1d, 0xad, 0xef, 0x44, 0xbd, 0xda, 0x83, 0xac, 0x7a, 0xa8, 0xf0, 0x1c, 0x07, 0xf9, 0xc3, 0x43, 0x6c, 0x3f, 0xb7, 0xd3, 0x87, 0x22, 0x02, 0x73}}}, +{{{0x64, 0x1d, 0x49, 0x13, 0x2f, 0x71, 0xec, 0x69, 0x87, 0xd0, 0x42, 0xee, 0x13, 0xec, 0xe3, 0xed, 0x56, 0x7b, 0xbf, 0xbd, 0x8c, 0x2f, 0x7d, 0x7b, 0x9d, 0x28, 0xec, 0x8e, 0x76, 0x2f, 0x6f, 0x08}} , + {{0x22, 0xf5, 0x5f, 0x4d, 0x15, 0xef, 0xfc, 0x4e, 0x57, 0x03, 0x36, 0x89, 0xf0, 0xeb, 0x5b, 0x91, 0xd6, 0xe2, 0xca, 0x01, 0xa5, 0xee, 0x52, 0xec, 0xa0, 0x3c, 0x8f, 0x33, 0x90, 0x5a, 0x94, 0x72}}}, +{{{0x8a, 0x4b, 0xe7, 0x38, 0xbc, 0xda, 0xc2, 0xb0, 0x85, 0xe1, 0x4a, 0xfe, 0x2d, 0x44, 0x84, 0xcb, 0x20, 0x6b, 0x2d, 0xbf, 0x11, 0x9c, 0xd7, 0xbe, 0xd3, 0x3e, 0x5f, 0xbf, 0x68, 0xbc, 0xa8, 0x07}} , + {{0x01, 0x89, 0x28, 0x22, 0x6a, 0x78, 0xaa, 0x29, 0x03, 0xc8, 0x74, 0x95, 0x03, 0x3e, 0xdc, 0xbd, 0x07, 0x13, 0xa8, 0xa2, 0x20, 0x2d, 0xb3, 0x18, 0x70, 0x42, 0xfd, 0x7a, 0xc4, 0xd7, 0x49, 0x72}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x02, 0xff, 0x32, 0x2b, 0x5c, 0x93, 0x54, 0x32, 0xe8, 0x57, 0x54, 0x1a, 0x8b, 0x33, 0x60, 0x65, 0xd3, 0x67, 0xa4, 0xc1, 0x26, 0xc4, 0xa4, 0x34, 0x1f, 0x9b, 0xa7, 0xa9, 0xf4, 0xd9, 0x4f, 0x5b}} , + {{0x46, 0x8d, 0xb0, 0x33, 0x54, 0x26, 0x5b, 0x68, 0xdf, 0xbb, 0xc5, 0xec, 0xc2, 0xf9, 0x3c, 0x5a, 0x37, 0xc1, 0x8e, 0x27, 0x47, 0xaa, 0x49, 0x5a, 0xf8, 0xfb, 0x68, 0x04, 0x23, 0xd1, 0xeb, 0x40}}}, +{{{0x65, 0xa5, 0x11, 0x84, 0x8a, 0x67, 0x9d, 0x9e, 0xd1, 0x44, 0x68, 0x7a, 0x34, 0xe1, 0x9f, 0xa3, 0x54, 0xcd, 0x07, 0xca, 0x79, 0x1f, 0x54, 0x2f, 0x13, 0x70, 0x4e, 0xee, 0xa2, 0xfa, 0xe7, 0x5d}} , + {{0x36, 0xec, 0x54, 0xf8, 0xce, 0xe4, 0x85, 0xdf, 0xf6, 0x6f, 0x1d, 0x90, 0x08, 0xbc, 0xe8, 0xc0, 0x92, 0x2d, 0x43, 0x6b, 0x92, 0xa9, 0x8e, 0xab, 0x0a, 0x2e, 0x1c, 0x1e, 0x64, 0x23, 0x9f, 0x2c}}}, +{{{0xa7, 0xd6, 0x2e, 0xd5, 0xcc, 0xd4, 0xcb, 0x5a, 0x3b, 0xa7, 0xf9, 0x46, 0x03, 0x1d, 0xad, 0x2b, 0x34, 0x31, 0x90, 0x00, 0x46, 0x08, 0x82, 0x14, 0xc4, 0xe0, 0x9c, 0xf0, 0xe3, 0x55, 0x43, 0x31}} , + {{0x60, 0xd6, 0xdd, 0x78, 0xe6, 0xd4, 0x22, 0x42, 0x1f, 0x00, 0xf9, 0xb1, 0x6a, 0x63, 0xe2, 0x92, 0x59, 0xd1, 0x1a, 0xb7, 0x00, 0x54, 0x29, 0xc9, 0xc1, 0xf6, 0x6f, 0x7a, 0xc5, 0x3c, 0x5f, 0x65}}}, +{{{0x27, 0x4f, 0xd0, 0x72, 0xb1, 0x11, 0x14, 0x27, 0x15, 0x94, 0x48, 0x81, 0x7e, 0x74, 0xd8, 0x32, 0xd5, 0xd1, 0x11, 0x28, 0x60, 0x63, 0x36, 0x32, 0x37, 0xb5, 0x13, 0x1c, 0xa0, 0x37, 0xe3, 0x74}} , + {{0xf1, 0x25, 0x4e, 0x11, 0x96, 0x67, 0xe6, 0x1c, 0xc2, 0xb2, 0x53, 0xe2, 0xda, 0x85, 0xee, 0xb2, 0x9f, 0x59, 0xf3, 0xba, 0xbd, 0xfa, 0xcf, 0x6e, 0xf9, 0xda, 0xa4, 0xb3, 0x02, 0x8f, 0x64, 0x08}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x34, 0x94, 0xf2, 0x64, 0x54, 0x47, 0x37, 0x07, 0x40, 0x8a, 0x20, 0xba, 0x4a, 0x55, 0xd7, 0x3f, 0x47, 0xba, 0x25, 0x23, 0x14, 0xb0, 0x2c, 0xe8, 0x55, 0xa8, 0xa6, 0xef, 0x51, 0xbd, 0x6f, 0x6a}} , + {{0x71, 0xd6, 0x16, 0x76, 0xb2, 0x06, 0xea, 0x79, 0xf5, 0xc4, 0xc3, 0x52, 0x7e, 0x61, 0xd1, 0xe1, 0xad, 0x70, 0x78, 0x1d, 0x16, 0x11, 0xf8, 0x7c, 0x2b, 0xfc, 0x55, 0x9f, 0x52, 0xf8, 0xf5, 0x16}}}, +{{{0x34, 0x96, 0x9a, 0xf6, 0xc5, 0xe0, 0x14, 0x03, 0x24, 0x0e, 0x4c, 0xad, 0x9e, 0x9a, 0x70, 0x23, 0x96, 0xb2, 0xf1, 0x2e, 0x9d, 0xc3, 0x32, 0x9b, 0x54, 0xa5, 0x73, 0xde, 0x88, 0xb1, 0x3e, 0x24}} , + {{0xf6, 0xe2, 0x4c, 0x1f, 0x5b, 0xb2, 0xaf, 0x82, 0xa5, 0xcf, 0x81, 0x10, 0x04, 0xef, 0xdb, 0xa2, 0xcc, 0x24, 0xb2, 0x7e, 0x0b, 0x7a, 0xeb, 0x01, 0xd8, 0x52, 0xf4, 0x51, 0x89, 0x29, 0x79, 0x37}}}, +{{{0x74, 0xde, 0x12, 0xf3, 0x68, 0xb7, 0x66, 0xc3, 0xee, 0x68, 0xdc, 0x81, 0xb5, 0x55, 0x99, 0xab, 0xd9, 0x28, 0x63, 0x6d, 0x8b, 0x40, 0x69, 0x75, 0x6c, 0xcd, 0x5c, 0x2a, 0x7e, 0x32, 0x7b, 0x29}} , + {{0x02, 0xcc, 0x22, 0x74, 0x4d, 0x19, 0x07, 0xc0, 0xda, 0xb5, 0x76, 0x51, 0x2a, 0xaa, 0xa6, 0x0a, 0x5f, 0x26, 0xd4, 0xbc, 0xaf, 0x48, 0x88, 0x7f, 0x02, 0xbc, 0xf2, 0xe1, 0xcf, 0xe9, 0xdd, 0x15}}}, +{{{0xed, 0xb5, 0x9a, 0x8c, 0x9a, 0xdd, 0x27, 0xf4, 0x7f, 0x47, 0xd9, 0x52, 0xa7, 0xcd, 0x65, 0xa5, 0x31, 0x22, 0xed, 0xa6, 0x63, 0x5b, 0x80, 0x4a, 0xad, 0x4d, 0xed, 0xbf, 0xee, 0x49, 0xb3, 0x06}} , + {{0xf8, 0x64, 0x8b, 0x60, 0x90, 0xe9, 0xde, 0x44, 0x77, 0xb9, 0x07, 0x36, 0x32, 0xc2, 0x50, 0xf5, 0x65, 0xdf, 0x48, 0x4c, 0x37, 0xaa, 0x68, 0xab, 0x9a, 0x1f, 0x3e, 0xff, 0x89, 0x92, 0xa0, 0x07}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x7d, 0x4f, 0x9c, 0x19, 0xc0, 0x4a, 0x31, 0xec, 0xf9, 0xaa, 0xeb, 0xb2, 0x16, 0x9c, 0xa3, 0x66, 0x5f, 0xd1, 0xd4, 0xed, 0xb8, 0x92, 0x1c, 0xab, 0xda, 0xea, 0xd9, 0x57, 0xdf, 0x4c, 0x2a, 0x48}} , + {{0x4b, 0xb0, 0x4e, 0x6e, 0x11, 0x3b, 0x51, 0xbd, 0x6a, 0xfd, 0xe4, 0x25, 0xa5, 0x5f, 0x11, 0x3f, 0x98, 0x92, 0x51, 0x14, 0xc6, 0x5f, 0x3c, 0x0b, 0xa8, 0xf7, 0xc2, 0x81, 0x43, 0xde, 0x91, 0x73}}}, +{{{0x3c, 0x8f, 0x9f, 0x33, 0x2a, 0x1f, 0x43, 0x33, 0x8f, 0x68, 0xff, 0x1f, 0x3d, 0x73, 0x6b, 0xbf, 0x68, 0xcc, 0x7d, 0x13, 0x6c, 0x24, 0x4b, 0xcc, 0x4d, 0x24, 0x0d, 0xfe, 0xde, 0x86, 0xad, 0x3b}} , + {{0x79, 0x51, 0x81, 0x01, 0xdc, 0x73, 0x53, 0xe0, 0x6e, 0x9b, 0xea, 0x68, 0x3f, 0x5c, 0x14, 0x84, 0x53, 0x8d, 0x4b, 0xc0, 0x9f, 0x9f, 0x89, 0x2b, 0x8c, 0xba, 0x86, 0xfa, 0xf2, 0xcd, 0xe3, 0x2d}}}, +{{{0x06, 0xf9, 0x29, 0x5a, 0xdb, 0x3d, 0x84, 0x52, 0xab, 0xcc, 0x6b, 0x60, 0x9d, 0xb7, 0x4a, 0x0e, 0x36, 0x63, 0x91, 0xad, 0xa0, 0x95, 0xb0, 0x97, 0x89, 0x4e, 0xcf, 0x7d, 0x3c, 0xe5, 0x7c, 0x28}} , + {{0x2e, 0x69, 0x98, 0xfd, 0xc6, 0xbd, 0xcc, 0xca, 0xdf, 0x9a, 0x44, 0x7e, 0x9d, 0xca, 0x89, 0x6d, 0xbf, 0x27, 0xc2, 0xf8, 0xcd, 0x46, 0x00, 0x2b, 0xb5, 0x58, 0x4e, 0xb7, 0x89, 0x09, 0xe9, 0x2d}}}, +{{{0x54, 0xbe, 0x75, 0xcb, 0x05, 0xb0, 0x54, 0xb7, 0xe7, 0x26, 0x86, 0x4a, 0xfc, 0x19, 0xcf, 0x27, 0x46, 0xd4, 0x22, 0x96, 0x5a, 0x11, 0xe8, 0xd5, 0x1b, 0xed, 0x71, 0xc5, 0x5d, 0xc8, 0xaf, 0x45}} , + {{0x40, 0x7b, 0x77, 0x57, 0x49, 0x9e, 0x80, 0x39, 0x23, 0xee, 0x81, 0x0b, 0x22, 0xcf, 0xdb, 0x7a, 0x2f, 0x14, 0xb8, 0x57, 0x8f, 0xa1, 0x39, 0x1e, 0x77, 0xfc, 0x0b, 0xa6, 0xbf, 0x8a, 0x0c, 0x6c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x77, 0x3a, 0xd4, 0xd8, 0x27, 0xcf, 0xe8, 0xa1, 0x72, 0x9d, 0xca, 0xdd, 0x0d, 0x96, 0xda, 0x79, 0xed, 0x56, 0x42, 0x15, 0x60, 0xc7, 0x1c, 0x6b, 0x26, 0x30, 0xf6, 0x6a, 0x95, 0x67, 0xf3, 0x0a}} , + {{0xc5, 0x08, 0xa4, 0x2b, 0x2f, 0xbd, 0x31, 0x81, 0x2a, 0xa6, 0xb6, 0xe4, 0x00, 0x91, 0xda, 0x3d, 0xb2, 0xb0, 0x96, 0xce, 0x8a, 0xd2, 0x8d, 0x70, 0xb3, 0xd3, 0x34, 0x01, 0x90, 0x8d, 0x10, 0x21}}}, +{{{0x33, 0x0d, 0xe7, 0xba, 0x4f, 0x07, 0xdf, 0x8d, 0xea, 0x7d, 0xa0, 0xc5, 0xd6, 0xb1, 0xb0, 0xe5, 0x57, 0x1b, 0x5b, 0xf5, 0x45, 0x13, 0x14, 0x64, 0x5a, 0xeb, 0x5c, 0xfc, 0x54, 0x01, 0x76, 0x2b}} , + {{0x02, 0x0c, 0xc2, 0xaf, 0x96, 0x36, 0xfe, 0x4a, 0xe2, 0x54, 0x20, 0x6a, 0xeb, 0xb2, 0x9f, 0x62, 0xd7, 0xce, 0xa2, 0x3f, 0x20, 0x11, 0x34, 0x37, 0xe0, 0x42, 0xed, 0x6f, 0xf9, 0x1a, 0xc8, 0x7d}}}, +{{{0xd8, 0xb9, 0x11, 0xe8, 0x36, 0x3f, 0x42, 0xc1, 0xca, 0xdc, 0xd3, 0xf1, 0xc8, 0x23, 0x3d, 0x4f, 0x51, 0x7b, 0x9d, 0x8d, 0xd8, 0xe4, 0xa0, 0xaa, 0xf3, 0x04, 0xd6, 0x11, 0x93, 0xc8, 0x35, 0x45}} , + {{0x61, 0x36, 0xd6, 0x08, 0x90, 0xbf, 0xa7, 0x7a, 0x97, 0x6c, 0x0f, 0x84, 0xd5, 0x33, 0x2d, 0x37, 0xc9, 0x6a, 0x80, 0x90, 0x3d, 0x0a, 0xa2, 0xaa, 0xe1, 0xb8, 0x84, 0xba, 0x61, 0x36, 0xdd, 0x69}}}, +{{{0x6b, 0xdb, 0x5b, 0x9c, 0xc6, 0x92, 0xbc, 0x23, 0xaf, 0xc5, 0xb8, 0x75, 0xf8, 0x42, 0xfa, 0xd6, 0xb6, 0x84, 0x94, 0x63, 0x98, 0x93, 0x48, 0x78, 0x38, 0xcd, 0xbb, 0x18, 0x34, 0xc3, 0xdb, 0x67}} , + {{0x96, 0xf3, 0x3a, 0x09, 0x56, 0xb0, 0x6f, 0x7c, 0x51, 0x1e, 0x1b, 0x39, 0x48, 0xea, 0xc9, 0x0c, 0x25, 0xa2, 0x7a, 0xca, 0xe7, 0x92, 0xfc, 0x59, 0x30, 0xa3, 0x89, 0x85, 0xdf, 0x6f, 0x43, 0x38}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x79, 0x84, 0x44, 0x19, 0xbd, 0xe9, 0x54, 0xc4, 0xc0, 0x6e, 0x2a, 0xa8, 0xa8, 0x9b, 0x43, 0xd5, 0x71, 0x22, 0x5f, 0xdc, 0x01, 0xfa, 0xdf, 0xb3, 0xb8, 0x47, 0x4b, 0x0a, 0xa5, 0x44, 0xea, 0x29}} , + {{0x05, 0x90, 0x50, 0xaf, 0x63, 0x5f, 0x9d, 0x9e, 0xe1, 0x9d, 0x38, 0x97, 0x1f, 0x6c, 0xac, 0x30, 0x46, 0xb2, 0x6a, 0x19, 0xd1, 0x4b, 0xdb, 0xbb, 0x8c, 0xda, 0x2e, 0xab, 0xc8, 0x5a, 0x77, 0x6c}}}, +{{{0x2b, 0xbe, 0xaf, 0xa1, 0x6d, 0x2f, 0x0b, 0xb1, 0x8f, 0xe3, 0xe0, 0x38, 0xcd, 0x0b, 0x41, 0x1b, 0x4a, 0x15, 0x07, 0xf3, 0x6f, 0xdc, 0xb8, 0xe9, 0xde, 0xb2, 0xa3, 0x40, 0x01, 0xa6, 0x45, 0x1e}} , + {{0x76, 0x0a, 0xda, 0x8d, 0x2c, 0x07, 0x3f, 0x89, 0x7d, 0x04, 0xad, 0x43, 0x50, 0x6e, 0xd2, 0x47, 0xcb, 0x8a, 0xe6, 0x85, 0x1a, 0x24, 0xf3, 0xd2, 0x60, 0xfd, 0xdf, 0x73, 0xa4, 0x0d, 0x73, 0x0e}}}, +{{{0xfd, 0x67, 0x6b, 0x71, 0x9b, 0x81, 0x53, 0x39, 0x39, 0xf4, 0xb8, 0xd5, 0xc3, 0x30, 0x9b, 0x3b, 0x7c, 0xa3, 0xf0, 0xd0, 0x84, 0x21, 0xd6, 0xbf, 0xb7, 0x4c, 0x87, 0x13, 0x45, 0x2d, 0xa7, 0x55}} , + {{0x5d, 0x04, 0xb3, 0x40, 0x28, 0x95, 0x2d, 0x30, 0x83, 0xec, 0x5e, 0xe4, 0xff, 0x75, 0xfe, 0x79, 0x26, 0x9d, 0x1d, 0x36, 0xcd, 0x0a, 0x15, 0xd2, 0x24, 0x14, 0x77, 0x71, 0xd7, 0x8a, 0x1b, 0x04}}}, +{{{0x5d, 0x93, 0xc9, 0xbe, 0xaa, 0x90, 0xcd, 0x9b, 0xfb, 0x73, 0x7e, 0xb0, 0x64, 0x98, 0x57, 0x44, 0x42, 0x41, 0xb1, 0xaf, 0xea, 0xc1, 0xc3, 0x22, 0xff, 0x60, 0x46, 0xcb, 0x61, 0x81, 0x70, 0x61}} , + {{0x0d, 0x82, 0xb9, 0xfe, 0x21, 0xcd, 0xc4, 0xf5, 0x98, 0x0c, 0x4e, 0x72, 0xee, 0x87, 0x49, 0xf8, 0xa1, 0x95, 0xdf, 0x8f, 0x2d, 0xbd, 0x21, 0x06, 0x7c, 0x15, 0xe8, 0x12, 0x6d, 0x93, 0xd6, 0x38}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x91, 0xf7, 0x51, 0xd9, 0xef, 0x7d, 0x42, 0x01, 0x13, 0xe9, 0xb8, 0x7f, 0xa6, 0x49, 0x17, 0x64, 0x21, 0x80, 0x83, 0x2c, 0x63, 0x4c, 0x60, 0x09, 0x59, 0x91, 0x92, 0x77, 0x39, 0x51, 0xf4, 0x48}} , + {{0x60, 0xd5, 0x22, 0x83, 0x08, 0x2f, 0xff, 0x99, 0x3e, 0x69, 0x6d, 0x88, 0xda, 0xe7, 0x5b, 0x52, 0x26, 0x31, 0x2a, 0xe5, 0x89, 0xde, 0x68, 0x90, 0xb6, 0x22, 0x5a, 0xbd, 0xd3, 0x85, 0x53, 0x31}}}, +{{{0xd8, 0xce, 0xdc, 0xf9, 0x3c, 0x4b, 0xa2, 0x1d, 0x2c, 0x2f, 0x36, 0xbe, 0x7a, 0xfc, 0xcd, 0xbc, 0xdc, 0xf9, 0x30, 0xbd, 0xff, 0x05, 0xc7, 0xe4, 0x8e, 0x17, 0x62, 0xf8, 0x4d, 0xa0, 0x56, 0x79}} , + {{0x82, 0xe7, 0xf6, 0xba, 0x53, 0x84, 0x0a, 0xa3, 0x34, 0xff, 0x3c, 0xa3, 0x6a, 0xa1, 0x37, 0xea, 0xdd, 0xb6, 0x95, 0xb3, 0x78, 0x19, 0x76, 0x1e, 0x55, 0x2f, 0x77, 0x2e, 0x7f, 0xc1, 0xea, 0x5e}}}, +{{{0x83, 0xe1, 0x6e, 0xa9, 0x07, 0x33, 0x3e, 0x83, 0xff, 0xcb, 0x1c, 0x9f, 0xb1, 0xa3, 0xb4, 0xc9, 0xe1, 0x07, 0x97, 0xff, 0xf8, 0x23, 0x8f, 0xce, 0x40, 0xfd, 0x2e, 0x5e, 0xdb, 0x16, 0x43, 0x2d}} , + {{0xba, 0x38, 0x02, 0xf7, 0x81, 0x43, 0x83, 0xa3, 0x20, 0x4f, 0x01, 0x3b, 0x8a, 0x04, 0x38, 0x31, 0xc6, 0x0f, 0xc8, 0xdf, 0xd7, 0xfa, 0x2f, 0x88, 0x3f, 0xfc, 0x0c, 0x76, 0xc4, 0xa6, 0x45, 0x72}}}, +{{{0xbb, 0x0c, 0xbc, 0x6a, 0xa4, 0x97, 0x17, 0x93, 0x2d, 0x6f, 0xde, 0x72, 0x10, 0x1c, 0x08, 0x2c, 0x0f, 0x80, 0x32, 0x68, 0x27, 0xd4, 0xab, 0xdd, 0xc5, 0x58, 0x61, 0x13, 0x6d, 0x11, 0x1e, 0x4d}} , + {{0x1a, 0xb9, 0xc9, 0x10, 0xfb, 0x1e, 0x4e, 0xf4, 0x84, 0x4b, 0x8a, 0x5e, 0x7b, 0x4b, 0xe8, 0x43, 0x8c, 0x8f, 0x00, 0xb5, 0x54, 0x13, 0xc5, 0x5c, 0xb6, 0x35, 0x4e, 0x9d, 0xe4, 0x5b, 0x41, 0x6d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x15, 0x7d, 0x12, 0x48, 0x82, 0x14, 0x42, 0xcd, 0x32, 0xd4, 0x4b, 0xc1, 0x72, 0x61, 0x2a, 0x8c, 0xec, 0xe2, 0xf8, 0x24, 0x45, 0x94, 0xe3, 0xbe, 0xdd, 0x67, 0xa8, 0x77, 0x5a, 0xae, 0x5b, 0x4b}} , + {{0xcb, 0x77, 0x9a, 0x20, 0xde, 0xb8, 0x23, 0xd9, 0xa0, 0x0f, 0x8c, 0x7b, 0xa5, 0xcb, 0xae, 0xb6, 0xec, 0x42, 0x67, 0x0e, 0x58, 0xa4, 0x75, 0x98, 0x21, 0x71, 0x84, 0xb3, 0xe0, 0x76, 0x94, 0x73}}}, +{{{0xdf, 0xfc, 0x69, 0x28, 0x23, 0x3f, 0x5b, 0xf8, 0x3b, 0x24, 0x37, 0xf3, 0x1d, 0xd5, 0x22, 0x6b, 0xd0, 0x98, 0xa8, 0x6c, 0xcf, 0xff, 0x06, 0xe1, 0x13, 0xdf, 0xb9, 0xc1, 0x0c, 0xa9, 0xbf, 0x33}} , + {{0xd9, 0x81, 0xda, 0xb2, 0x4f, 0x82, 0x9d, 0x43, 0x81, 0x09, 0xf1, 0xd2, 0x01, 0xef, 0xac, 0xf4, 0x2d, 0x7d, 0x01, 0x09, 0xf1, 0xff, 0xa5, 0x9f, 0xe5, 0xca, 0x27, 0x63, 0xdb, 0x20, 0xb1, 0x53}}}, +{{{0x67, 0x02, 0xe8, 0xad, 0xa9, 0x34, 0xd4, 0xf0, 0x15, 0x81, 0xaa, 0xc7, 0x4d, 0x87, 0x94, 0xea, 0x75, 0xe7, 0x4c, 0x94, 0x04, 0x0e, 0x69, 0x87, 0xe7, 0x51, 0x91, 0x10, 0x03, 0xc7, 0xbe, 0x56}} , + {{0x32, 0xfb, 0x86, 0xec, 0x33, 0x6b, 0x2e, 0x51, 0x2b, 0xc8, 0xfa, 0x6c, 0x70, 0x47, 0x7e, 0xce, 0x05, 0x0c, 0x71, 0xf3, 0xb4, 0x56, 0xa6, 0xdc, 0xcc, 0x78, 0x07, 0x75, 0xd0, 0xdd, 0xb2, 0x6a}}}, +{{{0xc6, 0xef, 0xb9, 0xc0, 0x2b, 0x22, 0x08, 0x1e, 0x71, 0x70, 0xb3, 0x35, 0x9c, 0x7a, 0x01, 0x92, 0x44, 0x9a, 0xf6, 0xb0, 0x58, 0x95, 0xc1, 0x9b, 0x02, 0xed, 0x2d, 0x7c, 0x34, 0x29, 0x49, 0x44}} , + {{0x45, 0x62, 0x1d, 0x2e, 0xff, 0x2a, 0x1c, 0x21, 0xa4, 0x25, 0x7b, 0x0d, 0x8c, 0x15, 0x39, 0xfc, 0x8f, 0x7c, 0xa5, 0x7d, 0x1e, 0x25, 0xa3, 0x45, 0xd6, 0xab, 0xbd, 0xcb, 0xc5, 0x5e, 0x78, 0x77}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd0, 0xd3, 0x42, 0xed, 0x1d, 0x00, 0x3c, 0x15, 0x2c, 0x9c, 0x77, 0x81, 0xd2, 0x73, 0xd1, 0x06, 0xd5, 0xc4, 0x7f, 0x94, 0xbb, 0x92, 0x2d, 0x2c, 0x4b, 0x45, 0x4b, 0xe9, 0x2a, 0x89, 0x6b, 0x2b}} , + {{0xd2, 0x0c, 0x88, 0xc5, 0x48, 0x4d, 0xea, 0x0d, 0x4a, 0xc9, 0x52, 0x6a, 0x61, 0x79, 0xe9, 0x76, 0xf3, 0x85, 0x52, 0x5c, 0x1b, 0x2c, 0xe1, 0xd6, 0xc4, 0x0f, 0x18, 0x0e, 0x4e, 0xf6, 0x1c, 0x7f}}}, +{{{0xb4, 0x04, 0x2e, 0x42, 0xcb, 0x1f, 0x2b, 0x11, 0x51, 0x7b, 0x08, 0xac, 0xaa, 0x3e, 0x9e, 0x52, 0x60, 0xb7, 0xc2, 0x61, 0x57, 0x8c, 0x84, 0xd5, 0x18, 0xa6, 0x19, 0xfc, 0xb7, 0x75, 0x91, 0x1b}} , + {{0xe8, 0x68, 0xca, 0x44, 0xc8, 0x38, 0x38, 0xcc, 0x53, 0x0a, 0x32, 0x35, 0xcc, 0x52, 0xcb, 0x0e, 0xf7, 0xc5, 0xe7, 0xec, 0x3d, 0x85, 0xcc, 0x58, 0xe2, 0x17, 0x47, 0xff, 0x9f, 0xa5, 0x30, 0x17}}}, +{{{0xe3, 0xae, 0xc8, 0xc1, 0x71, 0x75, 0x31, 0x00, 0x37, 0x41, 0x5c, 0x0e, 0x39, 0xda, 0x73, 0xa0, 0xc7, 0x97, 0x36, 0x6c, 0x5b, 0xf2, 0xee, 0x64, 0x0a, 0x3d, 0x89, 0x1e, 0x1d, 0x49, 0x8c, 0x37}} , + {{0x4c, 0xe6, 0xb0, 0xc1, 0xa5, 0x2a, 0x82, 0x09, 0x08, 0xad, 0x79, 0x9c, 0x56, 0xf6, 0xf9, 0xc1, 0xd7, 0x7c, 0x39, 0x7f, 0x93, 0xca, 0x11, 0x55, 0xbf, 0x07, 0x1b, 0x82, 0x29, 0x69, 0x95, 0x5c}}}, +{{{0x87, 0xee, 0xa6, 0x56, 0x9e, 0xc2, 0x9a, 0x56, 0x24, 0x42, 0x85, 0x4d, 0x98, 0x31, 0x1e, 0x60, 0x4d, 0x87, 0x85, 0x04, 0xae, 0x46, 0x12, 0xf9, 0x8e, 0x7f, 0xe4, 0x7f, 0xf6, 0x1c, 0x37, 0x01}} , + {{0x73, 0x4c, 0xb6, 0xc5, 0xc4, 0xe9, 0x6c, 0x85, 0x48, 0x4a, 0x5a, 0xac, 0xd9, 0x1f, 0x43, 0xf8, 0x62, 0x5b, 0xee, 0x98, 0x2a, 0x33, 0x8e, 0x79, 0xce, 0x61, 0x06, 0x35, 0xd8, 0xd7, 0xca, 0x71}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x72, 0xd3, 0xae, 0xa6, 0xca, 0x8f, 0xcd, 0xcc, 0x78, 0x8e, 0x19, 0x4d, 0xa7, 0xd2, 0x27, 0xe9, 0xa4, 0x3c, 0x16, 0x5b, 0x84, 0x80, 0xf9, 0xd0, 0xcc, 0x6a, 0x1e, 0xca, 0x1e, 0x67, 0xbd, 0x63}} , + {{0x7b, 0x6e, 0x2a, 0xd2, 0x87, 0x48, 0xff, 0xa1, 0xca, 0xe9, 0x15, 0x85, 0xdc, 0xdb, 0x2c, 0x39, 0x12, 0x91, 0xa9, 0x20, 0xaa, 0x4f, 0x29, 0xf4, 0x15, 0x7a, 0xd2, 0xf5, 0x32, 0xcc, 0x60, 0x04}}}, +{{{0xe5, 0x10, 0x47, 0x3b, 0xfa, 0x90, 0xfc, 0x30, 0xb5, 0xea, 0x6f, 0x56, 0x8f, 0xfb, 0x0e, 0xa7, 0x3b, 0xc8, 0xb2, 0xff, 0x02, 0x7a, 0x33, 0x94, 0x93, 0x2a, 0x03, 0xe0, 0x96, 0x3a, 0x6c, 0x0f}} , + {{0x5a, 0x63, 0x67, 0xe1, 0x9b, 0x47, 0x78, 0x9f, 0x38, 0x79, 0xac, 0x97, 0x66, 0x1d, 0x5e, 0x51, 0xee, 0x24, 0x42, 0xe8, 0x58, 0x4b, 0x8a, 0x03, 0x75, 0x86, 0x37, 0x86, 0xe2, 0x97, 0x4e, 0x3d}}}, +{{{0x3f, 0x75, 0x8e, 0xb4, 0xff, 0xd8, 0xdd, 0xd6, 0x37, 0x57, 0x9d, 0x6d, 0x3b, 0xbd, 0xd5, 0x60, 0x88, 0x65, 0x9a, 0xb9, 0x4a, 0x68, 0x84, 0xa2, 0x67, 0xdd, 0x17, 0x25, 0x97, 0x04, 0x8b, 0x5e}} , + {{0xbb, 0x40, 0x5e, 0xbc, 0x16, 0x92, 0x05, 0xc4, 0xc0, 0x4e, 0x72, 0x90, 0x0e, 0xab, 0xcf, 0x8a, 0xed, 0xef, 0xb9, 0x2d, 0x3b, 0xf8, 0x43, 0x5b, 0xba, 0x2d, 0xeb, 0x2f, 0x52, 0xd2, 0xd1, 0x5a}}}, +{{{0x40, 0xb4, 0xab, 0xe6, 0xad, 0x9f, 0x46, 0x69, 0x4a, 0xb3, 0x8e, 0xaa, 0xea, 0x9c, 0x8a, 0x20, 0x16, 0x5d, 0x8c, 0x13, 0xbd, 0xf6, 0x1d, 0xc5, 0x24, 0xbd, 0x90, 0x2a, 0x1c, 0xc7, 0x13, 0x3b}} , + {{0x54, 0xdc, 0x16, 0x0d, 0x18, 0xbe, 0x35, 0x64, 0x61, 0x52, 0x02, 0x80, 0xaf, 0x05, 0xf7, 0xa6, 0x42, 0xd3, 0x8f, 0x2e, 0x79, 0x26, 0xa8, 0xbb, 0xb2, 0x17, 0x48, 0xb2, 0x7a, 0x0a, 0x89, 0x14}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x20, 0xa8, 0x88, 0xe3, 0x91, 0xc0, 0x6e, 0xbb, 0x8a, 0x27, 0x82, 0x51, 0x83, 0xb2, 0x28, 0xa9, 0x83, 0xeb, 0xa6, 0xa9, 0x4d, 0x17, 0x59, 0x22, 0x54, 0x00, 0x50, 0x45, 0xcb, 0x48, 0x4b, 0x18}} , + {{0x33, 0x7c, 0xe7, 0x26, 0xba, 0x4d, 0x32, 0xfe, 0x53, 0xf4, 0xfa, 0x83, 0xe3, 0xa5, 0x79, 0x66, 0x73, 0xef, 0x80, 0x23, 0x68, 0xc2, 0x60, 0xdd, 0xa9, 0x33, 0xdc, 0x03, 0x7a, 0xe0, 0xe0, 0x3e}}}, +{{{0x34, 0x5c, 0x13, 0xfb, 0xc0, 0xe3, 0x78, 0x2b, 0x54, 0x58, 0x22, 0x9b, 0x76, 0x81, 0x7f, 0x93, 0x9c, 0x25, 0x3c, 0xd2, 0xe9, 0x96, 0x21, 0x26, 0x08, 0xf5, 0xed, 0x95, 0x11, 0xae, 0x04, 0x5a}} , + {{0xb9, 0xe8, 0xc5, 0x12, 0x97, 0x1f, 0x83, 0xfe, 0x3e, 0x94, 0x99, 0xd4, 0x2d, 0xf9, 0x52, 0x59, 0x5c, 0x82, 0xa6, 0xf0, 0x75, 0x7e, 0xe8, 0xec, 0xcc, 0xac, 0x18, 0x21, 0x09, 0x67, 0x66, 0x67}}}, +{{{0xb3, 0x40, 0x29, 0xd1, 0xcb, 0x1b, 0x08, 0x9e, 0x9c, 0xb7, 0x53, 0xb9, 0x3b, 0x71, 0x08, 0x95, 0x12, 0x1a, 0x58, 0xaf, 0x7e, 0x82, 0x52, 0x43, 0x4f, 0x11, 0x39, 0xf4, 0x93, 0x1a, 0x26, 0x05}} , + {{0x6e, 0x44, 0xa3, 0xf9, 0x64, 0xaf, 0xe7, 0x6d, 0x7d, 0xdf, 0x1e, 0xac, 0x04, 0xea, 0x3b, 0x5f, 0x9b, 0xe8, 0x24, 0x9d, 0x0e, 0xe5, 0x2e, 0x3e, 0xdf, 0xa9, 0xf7, 0xd4, 0x50, 0x71, 0xf0, 0x78}}}, +{{{0x3e, 0xa8, 0x38, 0xc2, 0x57, 0x56, 0x42, 0x9a, 0xb1, 0xe2, 0xf8, 0x45, 0xaa, 0x11, 0x48, 0x5f, 0x17, 0xc4, 0x54, 0x27, 0xdc, 0x5d, 0xaa, 0xdd, 0x41, 0xbc, 0xdf, 0x81, 0xb9, 0x53, 0xee, 0x52}} , + {{0xc3, 0xf1, 0xa7, 0x6d, 0xb3, 0x5f, 0x92, 0x6f, 0xcc, 0x91, 0xb8, 0x95, 0x05, 0xdf, 0x3c, 0x64, 0x57, 0x39, 0x61, 0x51, 0xad, 0x8c, 0x38, 0x7b, 0xc8, 0xde, 0x00, 0x34, 0xbe, 0xa1, 0xb0, 0x7e}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x25, 0x24, 0x1d, 0x8a, 0x67, 0x20, 0xee, 0x42, 0xeb, 0x38, 0xed, 0x0b, 0x8b, 0xcd, 0x46, 0x9d, 0x5e, 0x6b, 0x1e, 0x24, 0x9d, 0x12, 0x05, 0x1a, 0xcc, 0x05, 0x4e, 0x92, 0x38, 0xe1, 0x1f, 0x50}} , + {{0x4e, 0xee, 0x1c, 0x91, 0xe6, 0x11, 0xbd, 0x8e, 0x55, 0x1a, 0x18, 0x75, 0x66, 0xaf, 0x4d, 0x7b, 0x0f, 0xae, 0x6d, 0x85, 0xca, 0x82, 0x58, 0x21, 0x9c, 0x18, 0xe0, 0xed, 0xec, 0x22, 0x80, 0x2f}}}, +{{{0x68, 0x3b, 0x0a, 0x39, 0x1d, 0x6a, 0x15, 0x57, 0xfc, 0xf0, 0x63, 0x54, 0xdb, 0x39, 0xdb, 0xe8, 0x5c, 0x64, 0xff, 0xa0, 0x09, 0x4f, 0x3b, 0xb7, 0x32, 0x60, 0x99, 0x94, 0xfd, 0x94, 0x82, 0x2d}} , + {{0x24, 0xf6, 0x5a, 0x44, 0xf1, 0x55, 0x2c, 0xdb, 0xea, 0x7c, 0x84, 0x7c, 0x01, 0xac, 0xe3, 0xfd, 0xc9, 0x27, 0xc1, 0x5a, 0xb9, 0xde, 0x4f, 0x5a, 0x90, 0xdd, 0xc6, 0x67, 0xaa, 0x6f, 0x8a, 0x3a}}}, +{{{0x78, 0x52, 0x87, 0xc9, 0x97, 0x63, 0xb1, 0xdd, 0x54, 0x5f, 0xc1, 0xf8, 0xf1, 0x06, 0xa6, 0xa8, 0xa3, 0x88, 0x82, 0xd4, 0xcb, 0xa6, 0x19, 0xdd, 0xd1, 0x11, 0x87, 0x08, 0x17, 0x4c, 0x37, 0x2a}} , + {{0xa1, 0x0c, 0xf3, 0x08, 0x43, 0xd9, 0x24, 0x1e, 0x83, 0xa7, 0xdf, 0x91, 0xca, 0xbd, 0x69, 0x47, 0x8d, 0x1b, 0xe2, 0xb9, 0x4e, 0xb5, 0xe1, 0x76, 0xb3, 0x1c, 0x93, 0x03, 0xce, 0x5f, 0xb3, 0x5a}}}, +{{{0x1d, 0xda, 0xe4, 0x61, 0x03, 0x50, 0xa9, 0x8b, 0x68, 0x18, 0xef, 0xb2, 0x1c, 0x84, 0x3b, 0xa2, 0x44, 0x95, 0xa3, 0x04, 0x3b, 0xd6, 0x99, 0x00, 0xaf, 0x76, 0x42, 0x67, 0x02, 0x7d, 0x85, 0x56}} , + {{0xce, 0x72, 0x0e, 0x29, 0x84, 0xb2, 0x7d, 0xd2, 0x45, 0xbe, 0x57, 0x06, 0xed, 0x7f, 0xcf, 0xed, 0xcd, 0xef, 0x19, 0xd6, 0xbc, 0x15, 0x79, 0x64, 0xd2, 0x18, 0xe3, 0x20, 0x67, 0x3a, 0x54, 0x0b}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x52, 0xfd, 0x04, 0xc5, 0xfb, 0x99, 0xe7, 0xe8, 0xfb, 0x8c, 0xe1, 0x42, 0x03, 0xef, 0x9d, 0xd9, 0x9e, 0x4d, 0xf7, 0x80, 0xcf, 0x2e, 0xcc, 0x9b, 0x45, 0xc9, 0x7b, 0x7a, 0xbc, 0x37, 0xa8, 0x52}} , + {{0x96, 0x11, 0x41, 0x8a, 0x47, 0x91, 0xfe, 0xb6, 0xda, 0x7a, 0x54, 0x63, 0xd1, 0x14, 0x35, 0x05, 0x86, 0x8c, 0xa9, 0x36, 0x3f, 0xf2, 0x85, 0x54, 0x4e, 0x92, 0xd8, 0x85, 0x01, 0x46, 0xd6, 0x50}}}, +{{{0x53, 0xcd, 0xf3, 0x86, 0x40, 0xe6, 0x39, 0x42, 0x95, 0xd6, 0xcb, 0x45, 0x1a, 0x20, 0xc8, 0x45, 0x4b, 0x32, 0x69, 0x04, 0xb1, 0xaf, 0x20, 0x46, 0xc7, 0x6b, 0x23, 0x5b, 0x69, 0xee, 0x30, 0x3f}} , + {{0x70, 0x83, 0x47, 0xc0, 0xdb, 0x55, 0x08, 0xa8, 0x7b, 0x18, 0x6d, 0xf5, 0x04, 0x5a, 0x20, 0x0c, 0x4a, 0x8c, 0x60, 0xae, 0xae, 0x0f, 0x64, 0x55, 0x55, 0x2e, 0xd5, 0x1d, 0x53, 0x31, 0x42, 0x41}}}, +{{{0xca, 0xfc, 0x88, 0x6b, 0x96, 0x78, 0x0a, 0x8b, 0x83, 0xdc, 0xbc, 0xaf, 0x40, 0xb6, 0x8d, 0x7f, 0xef, 0xb4, 0xd1, 0x3f, 0xcc, 0xa2, 0x74, 0xc9, 0xc2, 0x92, 0x55, 0x00, 0xab, 0xdb, 0xbf, 0x4f}} , + {{0x93, 0x1c, 0x06, 0x2d, 0x66, 0x65, 0x02, 0xa4, 0x97, 0x18, 0xfd, 0x00, 0xe7, 0xab, 0x03, 0xec, 0xce, 0xc1, 0xbf, 0x37, 0xf8, 0x13, 0x53, 0xa5, 0xe5, 0x0c, 0x3a, 0xa8, 0x55, 0xb9, 0xff, 0x68}}}, +{{{0xe4, 0xe6, 0x6d, 0x30, 0x7d, 0x30, 0x35, 0xc2, 0x78, 0x87, 0xf9, 0xfc, 0x6b, 0x5a, 0xc3, 0xb7, 0x65, 0xd8, 0x2e, 0xc7, 0xa5, 0x0c, 0xc6, 0xdc, 0x12, 0xaa, 0xd6, 0x4f, 0xc5, 0x38, 0xbc, 0x0e}} , + {{0xe2, 0x3c, 0x76, 0x86, 0x38, 0xf2, 0x7b, 0x2c, 0x16, 0x78, 0x8d, 0xf5, 0xa4, 0x15, 0xda, 0xdb, 0x26, 0x85, 0xa0, 0x56, 0xdd, 0x1d, 0xe3, 0xb3, 0xfd, 0x40, 0xef, 0xf2, 0xd9, 0xa1, 0xb3, 0x04}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xdb, 0x49, 0x0e, 0xe6, 0x58, 0x10, 0x7a, 0x52, 0xda, 0xb5, 0x7d, 0x37, 0x6a, 0x3e, 0xa1, 0x78, 0xce, 0xc7, 0x1c, 0x24, 0x23, 0xdb, 0x7d, 0xfb, 0x8c, 0x8d, 0xdc, 0x30, 0x67, 0x69, 0x75, 0x3b}} , + {{0xa9, 0xea, 0x6d, 0x16, 0x16, 0x60, 0xf4, 0x60, 0x87, 0x19, 0x44, 0x8c, 0x4a, 0x8b, 0x3e, 0xfb, 0x16, 0x00, 0x00, 0x54, 0xa6, 0x9e, 0x9f, 0xef, 0xcf, 0xd9, 0xd2, 0x4c, 0x74, 0x31, 0xd0, 0x34}}}, +{{{0xa4, 0xeb, 0x04, 0xa4, 0x8c, 0x8f, 0x71, 0x27, 0x95, 0x85, 0x5d, 0x55, 0x4b, 0xb1, 0x26, 0x26, 0xc8, 0xae, 0x6a, 0x7d, 0xa2, 0x21, 0xca, 0xce, 0x38, 0xab, 0x0f, 0xd0, 0xd5, 0x2b, 0x6b, 0x00}} , + {{0xe5, 0x67, 0x0c, 0xf1, 0x3a, 0x9a, 0xea, 0x09, 0x39, 0xef, 0xd1, 0x30, 0xbc, 0x33, 0xba, 0xb1, 0x6a, 0xc5, 0x27, 0x08, 0x7f, 0x54, 0x80, 0x3d, 0xab, 0xf6, 0x15, 0x7a, 0xc2, 0x40, 0x73, 0x72}}}, +{{{0x84, 0x56, 0x82, 0xb6, 0x12, 0x70, 0x7f, 0xf7, 0xf0, 0xbd, 0x5b, 0xa9, 0xd5, 0xc5, 0x5f, 0x59, 0xbf, 0x7f, 0xb3, 0x55, 0x22, 0x02, 0xc9, 0x44, 0x55, 0x87, 0x8f, 0x96, 0x98, 0x64, 0x6d, 0x15}} , + {{0xb0, 0x8b, 0xaa, 0x1e, 0xec, 0xc7, 0xa5, 0x8f, 0x1f, 0x92, 0x04, 0xc6, 0x05, 0xf6, 0xdf, 0xa1, 0xcc, 0x1f, 0x81, 0xf5, 0x0e, 0x9c, 0x57, 0xdc, 0xe3, 0xbb, 0x06, 0x87, 0x1e, 0xfe, 0x23, 0x6c}}}, +{{{0xd8, 0x2b, 0x5b, 0x16, 0xea, 0x20, 0xf1, 0xd3, 0x68, 0x8f, 0xae, 0x5b, 0xd0, 0xa9, 0x1a, 0x19, 0xa8, 0x36, 0xfb, 0x2b, 0x57, 0x88, 0x7d, 0x90, 0xd5, 0xa6, 0xf3, 0xdc, 0x38, 0x89, 0x4e, 0x1f}} , + {{0xcc, 0x19, 0xda, 0x9b, 0x3b, 0x43, 0x48, 0x21, 0x2e, 0x23, 0x4d, 0x3d, 0xae, 0xf8, 0x8c, 0xfc, 0xdd, 0xa6, 0x74, 0x37, 0x65, 0xca, 0xee, 0x1a, 0x19, 0x8e, 0x9f, 0x64, 0x6f, 0x0c, 0x8b, 0x5a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x25, 0xb9, 0xc2, 0xf0, 0x72, 0xb8, 0x15, 0x16, 0xcc, 0x8d, 0x3c, 0x6f, 0x25, 0xed, 0xf4, 0x46, 0x2e, 0x0c, 0x60, 0x0f, 0xe2, 0x84, 0x34, 0x55, 0x89, 0x59, 0x34, 0x1b, 0xf5, 0x8d, 0xfe, 0x08}} , + {{0xf8, 0xab, 0x93, 0xbc, 0x44, 0xba, 0x1b, 0x75, 0x4b, 0x49, 0x6f, 0xd0, 0x54, 0x2e, 0x63, 0xba, 0xb5, 0xea, 0xed, 0x32, 0x14, 0xc9, 0x94, 0xd8, 0xc5, 0xce, 0xf4, 0x10, 0x68, 0xe0, 0x38, 0x27}}}, +{{{0x74, 0x1c, 0x14, 0x9b, 0xd4, 0x64, 0x61, 0x71, 0x5a, 0xb6, 0x21, 0x33, 0x4f, 0xf7, 0x8e, 0xba, 0xa5, 0x48, 0x9a, 0xc7, 0xfa, 0x9a, 0xf0, 0xb4, 0x62, 0xad, 0xf2, 0x5e, 0xcc, 0x03, 0x24, 0x1a}} , + {{0xf5, 0x76, 0xfd, 0xe4, 0xaf, 0xb9, 0x03, 0x59, 0xce, 0x63, 0xd2, 0x3b, 0x1f, 0xcd, 0x21, 0x0c, 0xad, 0x44, 0xa5, 0x97, 0xac, 0x80, 0x11, 0x02, 0x9b, 0x0c, 0xe5, 0x8b, 0xcd, 0xfb, 0x79, 0x77}}}, +{{{0x15, 0xbe, 0x9a, 0x0d, 0xba, 0x38, 0x72, 0x20, 0x8a, 0xf5, 0xbe, 0x59, 0x93, 0x79, 0xb7, 0xf6, 0x6a, 0x0c, 0x38, 0x27, 0x1a, 0x60, 0xf4, 0x86, 0x3b, 0xab, 0x5a, 0x00, 0xa0, 0xce, 0x21, 0x7d}} , + {{0x6c, 0xba, 0x14, 0xc5, 0xea, 0x12, 0x9e, 0x2e, 0x82, 0x63, 0xce, 0x9b, 0x4a, 0xe7, 0x1d, 0xec, 0xf1, 0x2e, 0x51, 0x1c, 0xf4, 0xd0, 0x69, 0x15, 0x42, 0x9d, 0xa3, 0x3f, 0x0e, 0xbf, 0xe9, 0x5c}}}, +{{{0xe4, 0x0d, 0xf4, 0xbd, 0xee, 0x31, 0x10, 0xed, 0xcb, 0x12, 0x86, 0xad, 0xd4, 0x2f, 0x90, 0x37, 0x32, 0xc3, 0x0b, 0x73, 0xec, 0x97, 0x85, 0xa4, 0x01, 0x1c, 0x76, 0x35, 0xfe, 0x75, 0xdd, 0x71}} , + {{0x11, 0xa4, 0x88, 0x9f, 0x3e, 0x53, 0x69, 0x3b, 0x1b, 0xe0, 0xf7, 0xba, 0x9b, 0xad, 0x4e, 0x81, 0x5f, 0xb5, 0x5c, 0xae, 0xbe, 0x67, 0x86, 0x37, 0x34, 0x8e, 0x07, 0x32, 0x45, 0x4a, 0x67, 0x39}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x90, 0x70, 0x58, 0x20, 0x03, 0x1e, 0x67, 0xb2, 0xc8, 0x9b, 0x58, 0xc5, 0xb1, 0xeb, 0x2d, 0x4a, 0xde, 0x82, 0x8c, 0xf2, 0xd2, 0x14, 0xb8, 0x70, 0x61, 0x4e, 0x73, 0xd6, 0x0b, 0x6b, 0x0d, 0x30}} , + {{0x81, 0xfc, 0x55, 0x5c, 0xbf, 0xa7, 0xc4, 0xbd, 0xe2, 0xf0, 0x4b, 0x8f, 0xe9, 0x7d, 0x99, 0xfa, 0xd3, 0xab, 0xbc, 0xc7, 0x83, 0x2b, 0x04, 0x7f, 0x0c, 0x19, 0x43, 0x03, 0x3d, 0x07, 0xca, 0x40}}}, +{{{0xf9, 0xc8, 0xbe, 0x8c, 0x16, 0x81, 0x39, 0x96, 0xf6, 0x17, 0x58, 0xc8, 0x30, 0x58, 0xfb, 0xc2, 0x03, 0x45, 0xd2, 0x52, 0x76, 0xe0, 0x6a, 0x26, 0x28, 0x5c, 0x88, 0x59, 0x6a, 0x5a, 0x54, 0x42}} , + {{0x07, 0xb5, 0x2e, 0x2c, 0x67, 0x15, 0x9b, 0xfb, 0x83, 0x69, 0x1e, 0x0f, 0xda, 0xd6, 0x29, 0xb1, 0x60, 0xe0, 0xb2, 0xba, 0x69, 0xa2, 0x9e, 0xbd, 0xbd, 0xe0, 0x1c, 0xbd, 0xcd, 0x06, 0x64, 0x70}}}, +{{{0x41, 0xfa, 0x8c, 0xe1, 0x89, 0x8f, 0x27, 0xc8, 0x25, 0x8f, 0x6f, 0x5f, 0x55, 0xf8, 0xde, 0x95, 0x6d, 0x2f, 0x75, 0x16, 0x2b, 0x4e, 0x44, 0xfd, 0x86, 0x6e, 0xe9, 0x70, 0x39, 0x76, 0x97, 0x7e}} , + {{0x17, 0x62, 0x6b, 0x14, 0xa1, 0x7c, 0xd0, 0x79, 0x6e, 0xd8, 0x8a, 0xa5, 0x6d, 0x8c, 0x93, 0xd2, 0x3f, 0xec, 0x44, 0x8d, 0x6e, 0x91, 0x01, 0x8c, 0x8f, 0xee, 0x01, 0x8f, 0xc0, 0xb4, 0x85, 0x0e}}}, +{{{0x02, 0x3a, 0x70, 0x41, 0xe4, 0x11, 0x57, 0x23, 0xac, 0xe6, 0xfc, 0x54, 0x7e, 0xcd, 0xd7, 0x22, 0xcb, 0x76, 0x9f, 0x20, 0xce, 0xa0, 0x73, 0x76, 0x51, 0x3b, 0xa4, 0xf8, 0xe3, 0x62, 0x12, 0x6c}} , + {{0x7f, 0x00, 0x9c, 0x26, 0x0d, 0x6f, 0x48, 0x7f, 0x3a, 0x01, 0xed, 0xc5, 0x96, 0xb0, 0x1f, 0x4f, 0xa8, 0x02, 0x62, 0x27, 0x8a, 0x50, 0x8d, 0x9a, 0x8b, 0x52, 0x0f, 0x1e, 0xcf, 0x41, 0x38, 0x19}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xf5, 0x6c, 0xd4, 0x2f, 0x0f, 0x69, 0x0f, 0x87, 0x3f, 0x61, 0x65, 0x1e, 0x35, 0x34, 0x85, 0xba, 0x02, 0x30, 0xac, 0x25, 0x3d, 0xe2, 0x62, 0xf1, 0xcc, 0xe9, 0x1b, 0xc2, 0xef, 0x6a, 0x42, 0x57}} , + {{0x34, 0x1f, 0x2e, 0xac, 0xd1, 0xc7, 0x04, 0x52, 0x32, 0x66, 0xb2, 0x33, 0x73, 0x21, 0x34, 0x54, 0xf7, 0x71, 0xed, 0x06, 0xb0, 0xff, 0xa6, 0x59, 0x6f, 0x8a, 0x4e, 0xfb, 0x02, 0xb0, 0x45, 0x6b}}}, +{{{0xf5, 0x48, 0x0b, 0x03, 0xc5, 0x22, 0x7d, 0x80, 0x08, 0x53, 0xfe, 0x32, 0xb1, 0xa1, 0x8a, 0x74, 0x6f, 0xbd, 0x3f, 0x85, 0xf4, 0xcf, 0xf5, 0x60, 0xaf, 0x41, 0x7e, 0x3e, 0x46, 0xa3, 0x5a, 0x20}} , + {{0xaa, 0x35, 0x87, 0x44, 0x63, 0x66, 0x97, 0xf8, 0x6e, 0x55, 0x0c, 0x04, 0x3e, 0x35, 0x50, 0xbf, 0x93, 0x69, 0xd2, 0x8b, 0x05, 0x55, 0x99, 0xbe, 0xe2, 0x53, 0x61, 0xec, 0xe8, 0x08, 0x0b, 0x32}}}, +{{{0xb3, 0x10, 0x45, 0x02, 0x69, 0x59, 0x2e, 0x97, 0xd9, 0x64, 0xf8, 0xdb, 0x25, 0x80, 0xdc, 0xc4, 0xd5, 0x62, 0x3c, 0xed, 0x65, 0x91, 0xad, 0xd1, 0x57, 0x81, 0x94, 0xaa, 0xa1, 0x29, 0xfc, 0x68}} , + {{0xdd, 0xb5, 0x7d, 0xab, 0x5a, 0x21, 0x41, 0x53, 0xbb, 0x17, 0x79, 0x0d, 0xd1, 0xa8, 0x0c, 0x0c, 0x20, 0x88, 0x09, 0xe9, 0x84, 0xe8, 0x25, 0x11, 0x67, 0x7a, 0x8b, 0x1a, 0xe4, 0x5d, 0xe1, 0x5d}}}, +{{{0x37, 0xea, 0xfe, 0x65, 0x3b, 0x25, 0xe8, 0xe1, 0xc2, 0xc5, 0x02, 0xa4, 0xbe, 0x98, 0x0a, 0x2b, 0x61, 0xc1, 0x9b, 0xe2, 0xd5, 0x92, 0xe6, 0x9e, 0x7d, 0x1f, 0xca, 0x43, 0x88, 0x8b, 0x2c, 0x59}} , + {{0xe0, 0xb5, 0x00, 0x1d, 0x2a, 0x6f, 0xaf, 0x79, 0x86, 0x2f, 0xa6, 0x5a, 0x93, 0xd1, 0xfe, 0xae, 0x3a, 0xee, 0xdb, 0x7c, 0x61, 0xbe, 0x7c, 0x01, 0xf9, 0xfe, 0x52, 0xdc, 0xd8, 0x52, 0xa3, 0x42}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x22, 0xaf, 0x13, 0x37, 0xbd, 0x37, 0x71, 0xac, 0x04, 0x46, 0x63, 0xac, 0xa4, 0x77, 0xed, 0x25, 0x38, 0xe0, 0x15, 0xa8, 0x64, 0x00, 0x0d, 0xce, 0x51, 0x01, 0xa9, 0xbc, 0x0f, 0x03, 0x1c, 0x04}} , + {{0x89, 0xf9, 0x80, 0x07, 0xcf, 0x3f, 0xb3, 0xe9, 0xe7, 0x45, 0x44, 0x3d, 0x2a, 0x7c, 0xe9, 0xe4, 0x16, 0x5c, 0x5e, 0x65, 0x1c, 0xc7, 0x7d, 0xc6, 0x7a, 0xfb, 0x43, 0xee, 0x25, 0x76, 0x46, 0x72}}}, +{{{0x02, 0xa2, 0xed, 0xf4, 0x8f, 0x6b, 0x0b, 0x3e, 0xeb, 0x35, 0x1a, 0xd5, 0x7e, 0xdb, 0x78, 0x00, 0x96, 0x8a, 0xa0, 0xb4, 0xcf, 0x60, 0x4b, 0xd4, 0xd5, 0xf9, 0x2d, 0xbf, 0x88, 0xbd, 0x22, 0x62}} , + {{0x13, 0x53, 0xe4, 0x82, 0x57, 0xfa, 0x1e, 0x8f, 0x06, 0x2b, 0x90, 0xba, 0x08, 0xb6, 0x10, 0x54, 0x4f, 0x7c, 0x1b, 0x26, 0xed, 0xda, 0x6b, 0xdd, 0x25, 0xd0, 0x4e, 0xea, 0x42, 0xbb, 0x25, 0x03}}}, +{{{0x51, 0x16, 0x50, 0x7c, 0xd5, 0x5d, 0xf6, 0x99, 0xe8, 0x77, 0x72, 0x4e, 0xfa, 0x62, 0xcb, 0x76, 0x75, 0x0c, 0xe2, 0x71, 0x98, 0x92, 0xd5, 0xfa, 0x45, 0xdf, 0x5c, 0x6f, 0x1e, 0x9e, 0x28, 0x69}} , + {{0x0d, 0xac, 0x66, 0x6d, 0xc3, 0x8b, 0xba, 0x16, 0xb5, 0xe2, 0xa0, 0x0d, 0x0c, 0xbd, 0xa4, 0x8e, 0x18, 0x6c, 0xf2, 0xdc, 0xf9, 0xdc, 0x4a, 0x86, 0x25, 0x95, 0x14, 0xcb, 0xd8, 0x1a, 0x04, 0x0f}}}, +{{{0x97, 0xa5, 0xdb, 0x8b, 0x2d, 0xaa, 0x42, 0x11, 0x09, 0xf2, 0x93, 0xbb, 0xd9, 0x06, 0x84, 0x4e, 0x11, 0xa8, 0xa0, 0x25, 0x2b, 0xa6, 0x5f, 0xae, 0xc4, 0xb4, 0x4c, 0xc8, 0xab, 0xc7, 0x3b, 0x02}} , + {{0xee, 0xc9, 0x29, 0x0f, 0xdf, 0x11, 0x85, 0xed, 0xce, 0x0d, 0x62, 0x2c, 0x8f, 0x4b, 0xf9, 0x04, 0xe9, 0x06, 0x72, 0x1d, 0x37, 0x20, 0x50, 0xc9, 0x14, 0xeb, 0xec, 0x39, 0xa7, 0x97, 0x2b, 0x4d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x69, 0xd1, 0x39, 0xbd, 0xfb, 0x33, 0xbe, 0xc4, 0xf0, 0x5c, 0xef, 0xf0, 0x56, 0x68, 0xfc, 0x97, 0x47, 0xc8, 0x72, 0xb6, 0x53, 0xa4, 0x0a, 0x98, 0xa5, 0xb4, 0x37, 0x71, 0xcf, 0x66, 0x50, 0x6d}} , + {{0x17, 0xa4, 0x19, 0x52, 0x11, 0x47, 0xb3, 0x5c, 0x5b, 0xa9, 0x2e, 0x22, 0xb4, 0x00, 0x52, 0xf9, 0x57, 0x18, 0xb8, 0xbe, 0x5a, 0xe3, 0xab, 0x83, 0xc8, 0x87, 0x0a, 0x2a, 0xd8, 0x8c, 0xbb, 0x54}}}, +{{{0xa9, 0x62, 0x93, 0x85, 0xbe, 0xe8, 0x73, 0x4a, 0x0e, 0xb0, 0xb5, 0x2d, 0x94, 0x50, 0xaa, 0xd3, 0xb2, 0xea, 0x9d, 0x62, 0x76, 0x3b, 0x07, 0x34, 0x4e, 0x2d, 0x70, 0xc8, 0x9a, 0x15, 0x66, 0x6b}} , + {{0xc5, 0x96, 0xca, 0xc8, 0x22, 0x1a, 0xee, 0x5f, 0xe7, 0x31, 0x60, 0x22, 0x83, 0x08, 0x63, 0xce, 0xb9, 0x32, 0x44, 0x58, 0x5d, 0x3a, 0x9b, 0xe4, 0x04, 0xd5, 0xef, 0x38, 0xef, 0x4b, 0xdd, 0x19}}}, +{{{0x4d, 0xc2, 0x17, 0x75, 0xa1, 0x68, 0xcd, 0xc3, 0xc6, 0x03, 0x44, 0xe3, 0x78, 0x09, 0x91, 0x47, 0x3f, 0x0f, 0xe4, 0x92, 0x58, 0xfa, 0x7d, 0x1f, 0x20, 0x94, 0x58, 0x5e, 0xbc, 0x19, 0x02, 0x6f}} , + {{0x20, 0xd6, 0xd8, 0x91, 0x54, 0xa7, 0xf3, 0x20, 0x4b, 0x34, 0x06, 0xfa, 0x30, 0xc8, 0x6f, 0x14, 0x10, 0x65, 0x74, 0x13, 0x4e, 0xf0, 0x69, 0x26, 0xce, 0xcf, 0x90, 0xf4, 0xd0, 0xc5, 0xc8, 0x64}}}, +{{{0x26, 0xa2, 0x50, 0x02, 0x24, 0x72, 0xf1, 0xf0, 0x4e, 0x2d, 0x93, 0xd5, 0x08, 0xe7, 0xae, 0x38, 0xf7, 0x18, 0xa5, 0x32, 0x34, 0xc2, 0xf0, 0xa6, 0xec, 0xb9, 0x61, 0x7b, 0x64, 0x99, 0xac, 0x71}} , + {{0x25, 0xcf, 0x74, 0x55, 0x1b, 0xaa, 0xa9, 0x38, 0x41, 0x40, 0xd5, 0x95, 0x95, 0xab, 0x1c, 0x5e, 0xbc, 0x41, 0x7e, 0x14, 0x30, 0xbe, 0x13, 0x89, 0xf4, 0xe5, 0xeb, 0x28, 0xc0, 0xc2, 0x96, 0x3a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2b, 0x77, 0x45, 0xec, 0x67, 0x76, 0x32, 0x4c, 0xb9, 0xdf, 0x25, 0x32, 0x6b, 0xcb, 0xe7, 0x14, 0x61, 0x43, 0xee, 0xba, 0x9b, 0x71, 0xef, 0xd2, 0x48, 0x65, 0xbb, 0x1b, 0x8a, 0x13, 0x1b, 0x22}} , + {{0x84, 0xad, 0x0c, 0x18, 0x38, 0x5a, 0xba, 0xd0, 0x98, 0x59, 0xbf, 0x37, 0xb0, 0x4f, 0x97, 0x60, 0x20, 0xb3, 0x9b, 0x97, 0xf6, 0x08, 0x6c, 0xa4, 0xff, 0xfb, 0xb7, 0xfa, 0x95, 0xb2, 0x51, 0x79}}}, +{{{0x28, 0x5c, 0x3f, 0xdb, 0x6b, 0x18, 0x3b, 0x5c, 0xd1, 0x04, 0x28, 0xde, 0x85, 0x52, 0x31, 0xb5, 0xbb, 0xf6, 0xa9, 0xed, 0xbe, 0x28, 0x4f, 0xb3, 0x7e, 0x05, 0x6a, 0xdb, 0x95, 0x0d, 0x1b, 0x1c}} , + {{0xd5, 0xc5, 0xc3, 0x9a, 0x0a, 0xd0, 0x31, 0x3e, 0x07, 0x36, 0x8e, 0xc0, 0x8a, 0x62, 0xb1, 0xca, 0xd6, 0x0e, 0x1e, 0x9d, 0xef, 0xab, 0x98, 0x4d, 0xbb, 0x6c, 0x05, 0xe0, 0xe4, 0x5d, 0xbd, 0x57}}}, +{{{0xcc, 0x21, 0x27, 0xce, 0xfd, 0xa9, 0x94, 0x8e, 0xe1, 0xab, 0x49, 0xe0, 0x46, 0x26, 0xa1, 0xa8, 0x8c, 0xa1, 0x99, 0x1d, 0xb4, 0x27, 0x6d, 0x2d, 0xc8, 0x39, 0x30, 0x5e, 0x37, 0x52, 0xc4, 0x6e}} , + {{0xa9, 0x85, 0xf4, 0xe7, 0xb0, 0x15, 0x33, 0x84, 0x1b, 0x14, 0x1a, 0x02, 0xd9, 0x3b, 0xad, 0x0f, 0x43, 0x6c, 0xea, 0x3e, 0x0f, 0x7e, 0xda, 0xdd, 0x6b, 0x4c, 0x7f, 0x6e, 0xd4, 0x6b, 0xbf, 0x0f}}}, +{{{0x47, 0x9f, 0x7c, 0x56, 0x7c, 0x43, 0x91, 0x1c, 0xbb, 0x4e, 0x72, 0x3e, 0x64, 0xab, 0xa0, 0xa0, 0xdf, 0xb4, 0xd8, 0x87, 0x3a, 0xbd, 0xa8, 0x48, 0xc9, 0xb8, 0xef, 0x2e, 0xad, 0x6f, 0x84, 0x4f}} , + {{0x2d, 0x2d, 0xf0, 0x1b, 0x7e, 0x2a, 0x6c, 0xf8, 0xa9, 0x6a, 0xe1, 0xf0, 0x99, 0xa1, 0x67, 0x9a, 0xd4, 0x13, 0xca, 0xca, 0xba, 0x27, 0x92, 0xaa, 0xa1, 0x5d, 0x50, 0xde, 0xcc, 0x40, 0x26, 0x0a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x9f, 0x3e, 0xf2, 0xb2, 0x90, 0xce, 0xdb, 0x64, 0x3e, 0x03, 0xdd, 0x37, 0x36, 0x54, 0x70, 0x76, 0x24, 0xb5, 0x69, 0x03, 0xfc, 0xa0, 0x2b, 0x74, 0xb2, 0x05, 0x0e, 0xcc, 0xd8, 0x1f, 0x6a, 0x1f}} , + {{0x19, 0x5e, 0x60, 0x69, 0x58, 0x86, 0xa0, 0x31, 0xbd, 0x32, 0xe9, 0x2c, 0x5c, 0xd2, 0x85, 0xba, 0x40, 0x64, 0xa8, 0x74, 0xf8, 0x0e, 0x1c, 0xb3, 0xa9, 0x69, 0xe8, 0x1e, 0x40, 0x64, 0x99, 0x77}}}, +{{{0x6c, 0x32, 0x4f, 0xfd, 0xbb, 0x5c, 0xbb, 0x8d, 0x64, 0x66, 0x4a, 0x71, 0x1f, 0x79, 0xa3, 0xad, 0x8d, 0xf9, 0xd4, 0xec, 0xcf, 0x67, 0x70, 0xfa, 0x05, 0x4a, 0x0f, 0x6e, 0xaf, 0x87, 0x0a, 0x6f}} , + {{0xc6, 0x36, 0x6e, 0x6c, 0x8c, 0x24, 0x09, 0x60, 0xbe, 0x26, 0xd2, 0x4c, 0x5e, 0x17, 0xca, 0x5f, 0x1d, 0xcc, 0x87, 0xe8, 0x42, 0x6a, 0xcb, 0xcb, 0x7d, 0x92, 0x05, 0x35, 0x81, 0x13, 0x60, 0x6b}}}, +{{{0xf4, 0x15, 0xcd, 0x0f, 0x0a, 0xaf, 0x4e, 0x6b, 0x51, 0xfd, 0x14, 0xc4, 0x2e, 0x13, 0x86, 0x74, 0x44, 0xcb, 0x66, 0x6b, 0xb6, 0x9d, 0x74, 0x56, 0x32, 0xac, 0x8d, 0x8e, 0x8c, 0x8c, 0x8c, 0x39}} , + {{0xca, 0x59, 0x74, 0x1a, 0x11, 0xef, 0x6d, 0xf7, 0x39, 0x5c, 0x3b, 0x1f, 0xfa, 0xe3, 0x40, 0x41, 0x23, 0x9e, 0xf6, 0xd1, 0x21, 0xa2, 0xbf, 0xad, 0x65, 0x42, 0x6b, 0x59, 0x8a, 0xe8, 0xc5, 0x7f}}}, +{{{0x64, 0x05, 0x7a, 0x84, 0x4a, 0x13, 0xc3, 0xf6, 0xb0, 0x6e, 0x9a, 0x6b, 0x53, 0x6b, 0x32, 0xda, 0xd9, 0x74, 0x75, 0xc4, 0xba, 0x64, 0x3d, 0x3b, 0x08, 0xdd, 0x10, 0x46, 0xef, 0xc7, 0x90, 0x1f}} , + {{0x7b, 0x2f, 0x3a, 0xce, 0xc8, 0xa1, 0x79, 0x3c, 0x30, 0x12, 0x44, 0x28, 0xf6, 0xbc, 0xff, 0xfd, 0xf4, 0xc0, 0x97, 0xb0, 0xcc, 0xc3, 0x13, 0x7a, 0xb9, 0x9a, 0x16, 0xe4, 0xcb, 0x4c, 0x34, 0x63}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x07, 0x4e, 0xd3, 0x2d, 0x09, 0x33, 0x0e, 0xd2, 0x0d, 0xbe, 0x3e, 0xe7, 0xe4, 0xaa, 0xb7, 0x00, 0x8b, 0xe8, 0xad, 0xaa, 0x7a, 0x8d, 0x34, 0x28, 0xa9, 0x81, 0x94, 0xc5, 0xe7, 0x42, 0xac, 0x47}} , + {{0x24, 0x89, 0x7a, 0x8f, 0xb5, 0x9b, 0xf0, 0xc2, 0x03, 0x64, 0xd0, 0x1e, 0xf5, 0xa4, 0xb2, 0xf3, 0x74, 0xe9, 0x1a, 0x16, 0xfd, 0xcb, 0x15, 0xea, 0xeb, 0x10, 0x6c, 0x35, 0xd1, 0xc1, 0xa6, 0x28}}}, +{{{0xcc, 0xd5, 0x39, 0xfc, 0xa5, 0xa4, 0xad, 0x32, 0x15, 0xce, 0x19, 0xe8, 0x34, 0x2b, 0x1c, 0x60, 0x91, 0xfc, 0x05, 0xa9, 0xb3, 0xdc, 0x80, 0x29, 0xc4, 0x20, 0x79, 0x06, 0x39, 0xc0, 0xe2, 0x22}} , + {{0xbb, 0xa8, 0xe1, 0x89, 0x70, 0x57, 0x18, 0x54, 0x3c, 0xf6, 0x0d, 0x82, 0x12, 0x05, 0x87, 0x96, 0x06, 0x39, 0xe3, 0xf8, 0xb3, 0x95, 0xe5, 0xd7, 0x26, 0xbf, 0x09, 0x5a, 0x94, 0xf9, 0x1c, 0x63}}}, +{{{0x2b, 0x8c, 0x2d, 0x9a, 0x8b, 0x84, 0xf2, 0x56, 0xfb, 0xad, 0x2e, 0x7f, 0xb7, 0xfc, 0x30, 0xe1, 0x35, 0x89, 0xba, 0x4d, 0xa8, 0x6d, 0xce, 0x8c, 0x8b, 0x30, 0xe0, 0xda, 0x29, 0x18, 0x11, 0x17}} , + {{0x19, 0xa6, 0x5a, 0x65, 0x93, 0xc3, 0xb5, 0x31, 0x22, 0x4f, 0xf3, 0xf6, 0x0f, 0xeb, 0x28, 0xc3, 0x7c, 0xeb, 0xce, 0x86, 0xec, 0x67, 0x76, 0x6e, 0x35, 0x45, 0x7b, 0xd8, 0x6b, 0x92, 0x01, 0x65}}}, +{{{0x3d, 0xd5, 0x9a, 0x64, 0x73, 0x36, 0xb1, 0xd6, 0x86, 0x98, 0x42, 0x3f, 0x8a, 0xf1, 0xc7, 0xf5, 0x42, 0xa8, 0x9c, 0x52, 0xa8, 0xdc, 0xf9, 0x24, 0x3f, 0x4a, 0xa1, 0xa4, 0x5b, 0xe8, 0x62, 0x1a}} , + {{0xc5, 0xbd, 0xc8, 0x14, 0xd5, 0x0d, 0xeb, 0xe1, 0xa5, 0xe6, 0x83, 0x11, 0x09, 0x00, 0x1d, 0x55, 0x83, 0x51, 0x7e, 0x75, 0x00, 0x81, 0xb9, 0xcb, 0xd8, 0xc5, 0xe5, 0xa1, 0xd9, 0x17, 0x6d, 0x1f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xea, 0xf9, 0xe4, 0xe9, 0xe1, 0x52, 0x3f, 0x51, 0x19, 0x0d, 0xdd, 0xd9, 0x9d, 0x93, 0x31, 0x87, 0x23, 0x09, 0xd5, 0x83, 0xeb, 0x92, 0x09, 0x76, 0x6e, 0xe3, 0xf8, 0xc0, 0xa2, 0x66, 0xb5, 0x36}} , + {{0x3a, 0xbb, 0x39, 0xed, 0x32, 0x02, 0xe7, 0x43, 0x7a, 0x38, 0x14, 0x84, 0xe3, 0x44, 0xd2, 0x5e, 0x94, 0xdd, 0x78, 0x89, 0x55, 0x4c, 0x73, 0x9e, 0xe1, 0xe4, 0x3e, 0x43, 0xd0, 0x4a, 0xde, 0x1b}}}, +{{{0xb2, 0xe7, 0x8f, 0xe3, 0xa3, 0xc5, 0xcb, 0x72, 0xee, 0x79, 0x41, 0xf8, 0xdf, 0xee, 0x65, 0xc5, 0x45, 0x77, 0x27, 0x3c, 0xbd, 0x58, 0xd3, 0x75, 0xe2, 0x04, 0x4b, 0xbb, 0x65, 0xf3, 0xc8, 0x0f}} , + {{0x24, 0x7b, 0x93, 0x34, 0xb5, 0xe2, 0x74, 0x48, 0xcd, 0xa0, 0x0b, 0x92, 0x97, 0x66, 0x39, 0xf4, 0xb0, 0xe2, 0x5d, 0x39, 0x6a, 0x5b, 0x45, 0x17, 0x78, 0x1e, 0xdb, 0x91, 0x81, 0x1c, 0xf9, 0x16}}}, +{{{0x16, 0xdf, 0xd1, 0x5a, 0xd5, 0xe9, 0x4e, 0x58, 0x95, 0x93, 0x5f, 0x51, 0x09, 0xc3, 0x2a, 0xc9, 0xd4, 0x55, 0x48, 0x79, 0xa4, 0xa3, 0xb2, 0xc3, 0x62, 0xaa, 0x8c, 0xe8, 0xad, 0x47, 0x39, 0x1b}} , + {{0x46, 0xda, 0x9e, 0x51, 0x3a, 0xe6, 0xd1, 0xa6, 0xbb, 0x4d, 0x7b, 0x08, 0xbe, 0x8c, 0xd5, 0xf3, 0x3f, 0xfd, 0xf7, 0x44, 0x80, 0x2d, 0x53, 0x4b, 0xd0, 0x87, 0x68, 0xc1, 0xb5, 0xd8, 0xf7, 0x07}}}, +{{{0xf4, 0x10, 0x46, 0xbe, 0xb7, 0xd2, 0xd1, 0xce, 0x5e, 0x76, 0xa2, 0xd7, 0x03, 0xdc, 0xe4, 0x81, 0x5a, 0xf6, 0x3c, 0xde, 0xae, 0x7a, 0x9d, 0x21, 0x34, 0xa5, 0xf6, 0xa9, 0x73, 0xe2, 0x8d, 0x60}} , + {{0xfa, 0x44, 0x71, 0xf6, 0x41, 0xd8, 0xc6, 0x58, 0x13, 0x37, 0xeb, 0x84, 0x0f, 0x96, 0xc7, 0xdc, 0xc8, 0xa9, 0x7a, 0x83, 0xb2, 0x2f, 0x31, 0xb1, 0x1a, 0xd8, 0x98, 0x3f, 0x11, 0xd0, 0x31, 0x3b}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x81, 0xd5, 0x34, 0x16, 0x01, 0xa3, 0x93, 0xea, 0x52, 0x94, 0xec, 0x93, 0xb7, 0x81, 0x11, 0x2d, 0x58, 0xf9, 0xb5, 0x0a, 0xaa, 0x4f, 0xf6, 0x2e, 0x3f, 0x36, 0xbf, 0x33, 0x5a, 0xe7, 0xd1, 0x08}} , + {{0x1a, 0xcf, 0x42, 0xae, 0xcc, 0xb5, 0x77, 0x39, 0xc4, 0x5b, 0x5b, 0xd0, 0x26, 0x59, 0x27, 0xd0, 0x55, 0x71, 0x12, 0x9d, 0x88, 0x3d, 0x9c, 0xea, 0x41, 0x6a, 0xf0, 0x50, 0x93, 0x93, 0xdd, 0x47}}}, +{{{0x6f, 0xc9, 0x51, 0x6d, 0x1c, 0xaa, 0xf5, 0xa5, 0x90, 0x3f, 0x14, 0xe2, 0x6e, 0x8e, 0x64, 0xfd, 0xac, 0xe0, 0x4e, 0x22, 0xe5, 0xc1, 0xbc, 0x29, 0x0a, 0x6a, 0x9e, 0xa1, 0x60, 0xcb, 0x2f, 0x0b}} , + {{0xdc, 0x39, 0x32, 0xf3, 0xa1, 0x44, 0xe9, 0xc5, 0xc3, 0x78, 0xfb, 0x95, 0x47, 0x34, 0x35, 0x34, 0xe8, 0x25, 0xde, 0x93, 0xc6, 0xb4, 0x76, 0x6d, 0x86, 0x13, 0xc6, 0xe9, 0x68, 0xb5, 0x01, 0x63}}}, +{{{0x1f, 0x9a, 0x52, 0x64, 0x97, 0xd9, 0x1c, 0x08, 0x51, 0x6f, 0x26, 0x9d, 0xaa, 0x93, 0x33, 0x43, 0xfa, 0x77, 0xe9, 0x62, 0x9b, 0x5d, 0x18, 0x75, 0xeb, 0x78, 0xf7, 0x87, 0x8f, 0x41, 0xb4, 0x4d}} , + {{0x13, 0xa8, 0x82, 0x3e, 0xe9, 0x13, 0xad, 0xeb, 0x01, 0xca, 0xcf, 0xda, 0xcd, 0xf7, 0x6c, 0xc7, 0x7a, 0xdc, 0x1e, 0x6e, 0xc8, 0x4e, 0x55, 0x62, 0x80, 0xea, 0x78, 0x0c, 0x86, 0xb9, 0x40, 0x51}}}, +{{{0x27, 0xae, 0xd3, 0x0d, 0x4c, 0x8f, 0x34, 0xea, 0x7d, 0x3c, 0xe5, 0x8a, 0xcf, 0x5b, 0x92, 0xd8, 0x30, 0x16, 0xb4, 0xa3, 0x75, 0xff, 0xeb, 0x27, 0xc8, 0x5c, 0x6c, 0xc2, 0xee, 0x6c, 0x21, 0x0b}} , + {{0xc3, 0xba, 0x12, 0x53, 0x2a, 0xaa, 0x77, 0xad, 0x19, 0x78, 0x55, 0x8a, 0x2e, 0x60, 0x87, 0xc2, 0x6e, 0x91, 0x38, 0x91, 0x3f, 0x7a, 0xc5, 0x24, 0x8f, 0x51, 0xc5, 0xde, 0xb0, 0x53, 0x30, 0x56}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x02, 0xfe, 0x54, 0x12, 0x18, 0xca, 0x7d, 0xa5, 0x68, 0x43, 0xa3, 0x6d, 0x14, 0x2a, 0x6a, 0xa5, 0x8e, 0x32, 0xe7, 0x63, 0x4f, 0xe3, 0xc6, 0x44, 0x3e, 0xab, 0x63, 0xca, 0x17, 0x86, 0x74, 0x3f}} , + {{0x1e, 0x64, 0xc1, 0x7d, 0x52, 0xdc, 0x13, 0x5a, 0xa1, 0x9c, 0x4e, 0xee, 0x99, 0x28, 0xbb, 0x4c, 0xee, 0xac, 0xa9, 0x1b, 0x89, 0xa2, 0x38, 0x39, 0x7b, 0xc4, 0x0f, 0x42, 0xe6, 0x89, 0xed, 0x0f}}}, +{{{0xf3, 0x3c, 0x8c, 0x80, 0x83, 0x10, 0x8a, 0x37, 0x50, 0x9c, 0xb4, 0xdf, 0x3f, 0x8c, 0xf7, 0x23, 0x07, 0xd6, 0xff, 0xa0, 0x82, 0x6c, 0x75, 0x3b, 0xe4, 0xb5, 0xbb, 0xe4, 0xe6, 0x50, 0xf0, 0x08}} , + {{0x62, 0xee, 0x75, 0x48, 0x92, 0x33, 0xf2, 0xf4, 0xad, 0x15, 0x7a, 0xa1, 0x01, 0x46, 0xa9, 0x32, 0x06, 0x88, 0xb6, 0x36, 0x47, 0x35, 0xb9, 0xb4, 0x42, 0x85, 0x76, 0xf0, 0x48, 0x00, 0x90, 0x38}}}, +{{{0x51, 0x15, 0x9d, 0xc3, 0x95, 0xd1, 0x39, 0xbb, 0x64, 0x9d, 0x15, 0x81, 0xc1, 0x68, 0xd0, 0xb6, 0xa4, 0x2c, 0x7d, 0x5e, 0x02, 0x39, 0x00, 0xe0, 0x3b, 0xa4, 0xcc, 0xca, 0x1d, 0x81, 0x24, 0x10}} , + {{0xe7, 0x29, 0xf9, 0x37, 0xd9, 0x46, 0x5a, 0xcd, 0x70, 0xfe, 0x4d, 0x5b, 0xbf, 0xa5, 0xcf, 0x91, 0xf4, 0xef, 0xee, 0x8a, 0x29, 0xd0, 0xe7, 0xc4, 0x25, 0x92, 0x8a, 0xff, 0x36, 0xfc, 0xe4, 0x49}}}, +{{{0xbd, 0x00, 0xb9, 0x04, 0x7d, 0x35, 0xfc, 0xeb, 0xd0, 0x0b, 0x05, 0x32, 0x52, 0x7a, 0x89, 0x24, 0x75, 0x50, 0xe1, 0x63, 0x02, 0x82, 0x8e, 0xe7, 0x85, 0x0c, 0xf2, 0x56, 0x44, 0x37, 0x83, 0x25}} , + {{0x8f, 0xa1, 0xce, 0xcb, 0x60, 0xda, 0x12, 0x02, 0x1e, 0x29, 0x39, 0x2a, 0x03, 0xb7, 0xeb, 0x77, 0x40, 0xea, 0xc9, 0x2b, 0x2c, 0xd5, 0x7d, 0x7e, 0x2c, 0xc7, 0x5a, 0xfd, 0xff, 0xc4, 0xd1, 0x62}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x1d, 0x88, 0x98, 0x5b, 0x4e, 0xfc, 0x41, 0x24, 0x05, 0xe6, 0x50, 0x2b, 0xae, 0x96, 0x51, 0xd9, 0x6b, 0x72, 0xb2, 0x33, 0x42, 0x98, 0x68, 0xbb, 0x10, 0x5a, 0x7a, 0x8c, 0x9d, 0x07, 0xb4, 0x05}} , + {{0x2f, 0x61, 0x9f, 0xd7, 0xa8, 0x3f, 0x83, 0x8c, 0x10, 0x69, 0x90, 0xe6, 0xcf, 0xd2, 0x63, 0xa3, 0xe4, 0x54, 0x7e, 0xe5, 0x69, 0x13, 0x1c, 0x90, 0x57, 0xaa, 0xe9, 0x53, 0x22, 0x43, 0x29, 0x23}}}, +{{{0xe5, 0x1c, 0xf8, 0x0a, 0xfd, 0x2d, 0x7e, 0xf5, 0xf5, 0x70, 0x7d, 0x41, 0x6b, 0x11, 0xfe, 0xbe, 0x99, 0xd1, 0x55, 0x29, 0x31, 0xbf, 0xc0, 0x97, 0x6c, 0xd5, 0x35, 0xcc, 0x5e, 0x8b, 0xd9, 0x69}} , + {{0x8e, 0x4e, 0x9f, 0x25, 0xf8, 0x81, 0x54, 0x2d, 0x0e, 0xd5, 0x54, 0x81, 0x9b, 0xa6, 0x92, 0xce, 0x4b, 0xe9, 0x8f, 0x24, 0x3b, 0xca, 0xe0, 0x44, 0xab, 0x36, 0xfe, 0xfb, 0x87, 0xd4, 0x26, 0x3e}}}, +{{{0x0f, 0x93, 0x9c, 0x11, 0xe7, 0xdb, 0xf1, 0xf0, 0x85, 0x43, 0x28, 0x15, 0x37, 0xdd, 0xde, 0x27, 0xdf, 0xad, 0x3e, 0x49, 0x4f, 0xe0, 0x5b, 0xf6, 0x80, 0x59, 0x15, 0x3c, 0x85, 0xb7, 0x3e, 0x12}} , + {{0xf5, 0xff, 0xcc, 0xf0, 0xb4, 0x12, 0x03, 0x5f, 0xc9, 0x84, 0xcb, 0x1d, 0x17, 0xe0, 0xbc, 0xcc, 0x03, 0x62, 0xa9, 0x8b, 0x94, 0xa6, 0xaa, 0x18, 0xcb, 0x27, 0x8d, 0x49, 0xa6, 0x17, 0x15, 0x07}}}, +{{{0xd9, 0xb6, 0xd4, 0x9d, 0xd4, 0x6a, 0xaf, 0x70, 0x07, 0x2c, 0x10, 0x9e, 0xbd, 0x11, 0xad, 0xe4, 0x26, 0x33, 0x70, 0x92, 0x78, 0x1c, 0x74, 0x9f, 0x75, 0x60, 0x56, 0xf4, 0x39, 0xa8, 0xa8, 0x62}} , + {{0x3b, 0xbf, 0x55, 0x35, 0x61, 0x8b, 0x44, 0x97, 0xe8, 0x3a, 0x55, 0xc1, 0xc8, 0x3b, 0xfd, 0x95, 0x29, 0x11, 0x60, 0x96, 0x1e, 0xcb, 0x11, 0x9d, 0xc2, 0x03, 0x8a, 0x1b, 0xc6, 0xd6, 0x45, 0x3d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x7e, 0x0e, 0x50, 0xb2, 0xcc, 0x0d, 0x6b, 0xa6, 0x71, 0x5b, 0x42, 0xed, 0xbd, 0xaf, 0xac, 0xf0, 0xfc, 0x12, 0xa2, 0x3f, 0x4e, 0xda, 0xe8, 0x11, 0xf3, 0x23, 0xe1, 0x04, 0x62, 0x03, 0x1c, 0x4e}} , + {{0xc8, 0xb1, 0x1b, 0x6f, 0x73, 0x61, 0x3d, 0x27, 0x0d, 0x7d, 0x7a, 0x25, 0x5f, 0x73, 0x0e, 0x2f, 0x93, 0xf6, 0x24, 0xd8, 0x4f, 0x90, 0xac, 0xa2, 0x62, 0x0a, 0xf0, 0x61, 0xd9, 0x08, 0x59, 0x6a}}}, +{{{0x6f, 0x2d, 0x55, 0xf8, 0x2f, 0x8e, 0xf0, 0x18, 0x3b, 0xea, 0xdd, 0x26, 0x72, 0xd1, 0xf5, 0xfe, 0xe5, 0xb8, 0xe6, 0xd3, 0x10, 0x48, 0x46, 0x49, 0x3a, 0x9f, 0x5e, 0x45, 0x6b, 0x90, 0xe8, 0x7f}} , + {{0xd3, 0x76, 0x69, 0x33, 0x7b, 0xb9, 0x40, 0x70, 0xee, 0xa6, 0x29, 0x6b, 0xdd, 0xd0, 0x5d, 0x8d, 0xc1, 0x3e, 0x4a, 0xea, 0x37, 0xb1, 0x03, 0x02, 0x03, 0x35, 0xf1, 0x28, 0x9d, 0xff, 0x00, 0x13}}}, +{{{0x7a, 0xdb, 0x12, 0xd2, 0x8a, 0x82, 0x03, 0x1b, 0x1e, 0xaf, 0xf9, 0x4b, 0x9c, 0xbe, 0xae, 0x7c, 0xe4, 0x94, 0x2a, 0x23, 0xb3, 0x62, 0x86, 0xe7, 0xfd, 0x23, 0xaa, 0x99, 0xbd, 0x2b, 0x11, 0x6c}} , + {{0x8d, 0xa6, 0xd5, 0xac, 0x9d, 0xcc, 0x68, 0x75, 0x7f, 0xc3, 0x4d, 0x4b, 0xdd, 0x6c, 0xbb, 0x11, 0x5a, 0x60, 0xe5, 0xbd, 0x7d, 0x27, 0x8b, 0xda, 0xb4, 0x95, 0xf6, 0x03, 0x27, 0xa4, 0x92, 0x3f}}}, +{{{0x22, 0xd6, 0xb5, 0x17, 0x84, 0xbf, 0x12, 0xcc, 0x23, 0x14, 0x4a, 0xdf, 0x14, 0x31, 0xbc, 0xa1, 0xac, 0x6e, 0xab, 0xfa, 0x57, 0x11, 0x53, 0xb3, 0x27, 0xe6, 0xf9, 0x47, 0x33, 0x44, 0x34, 0x1e}} , + {{0x79, 0xfc, 0xa6, 0xb4, 0x0b, 0x35, 0x20, 0xc9, 0x4d, 0x22, 0x84, 0xc4, 0xa9, 0x20, 0xec, 0x89, 0x94, 0xba, 0x66, 0x56, 0x48, 0xb9, 0x87, 0x7f, 0xca, 0x1e, 0x06, 0xed, 0xa5, 0x55, 0x59, 0x29}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x56, 0xe1, 0xf5, 0xf1, 0xd5, 0xab, 0xa8, 0x2b, 0xae, 0x89, 0xf3, 0xcf, 0x56, 0x9f, 0xf2, 0x4b, 0x31, 0xbc, 0x18, 0xa9, 0x06, 0x5b, 0xbe, 0xb4, 0x61, 0xf8, 0xb2, 0x06, 0x9c, 0x81, 0xab, 0x4c}} , + {{0x1f, 0x68, 0x76, 0x01, 0x16, 0x38, 0x2b, 0x0f, 0x77, 0x97, 0x92, 0x67, 0x4e, 0x86, 0x6a, 0x8b, 0xe5, 0xe8, 0x0c, 0xf7, 0x36, 0x39, 0xb5, 0x33, 0xe6, 0xcf, 0x5e, 0xbd, 0x18, 0xfb, 0x10, 0x1f}}}, +{{{0x83, 0xf0, 0x0d, 0x63, 0xef, 0x53, 0x6b, 0xb5, 0x6b, 0xf9, 0x83, 0xcf, 0xde, 0x04, 0x22, 0x9b, 0x2c, 0x0a, 0xe0, 0xa5, 0xd8, 0xc7, 0x9c, 0xa5, 0xa3, 0xf6, 0x6f, 0xcf, 0x90, 0x6b, 0x68, 0x7c}} , + {{0x33, 0x15, 0xd7, 0x7f, 0x1a, 0xd5, 0x21, 0x58, 0xc4, 0x18, 0xa5, 0xf0, 0xcc, 0x73, 0xa8, 0xfd, 0xfa, 0x18, 0xd1, 0x03, 0x91, 0x8d, 0x52, 0xd2, 0xa3, 0xa4, 0xd3, 0xb1, 0xea, 0x1d, 0x0f, 0x00}}}, +{{{0xcc, 0x48, 0x83, 0x90, 0xe5, 0xfd, 0x3f, 0x84, 0xaa, 0xf9, 0x8b, 0x82, 0x59, 0x24, 0x34, 0x68, 0x4f, 0x1c, 0x23, 0xd9, 0xcc, 0x71, 0xe1, 0x7f, 0x8c, 0xaf, 0xf1, 0xee, 0x00, 0xb6, 0xa0, 0x77}} , + {{0xf5, 0x1a, 0x61, 0xf7, 0x37, 0x9d, 0x00, 0xf4, 0xf2, 0x69, 0x6f, 0x4b, 0x01, 0x85, 0x19, 0x45, 0x4d, 0x7f, 0x02, 0x7c, 0x6a, 0x05, 0x47, 0x6c, 0x1f, 0x81, 0x20, 0xd4, 0xe8, 0x50, 0x27, 0x72}}}, +{{{0x2c, 0x3a, 0xe5, 0xad, 0xf4, 0xdd, 0x2d, 0xf7, 0x5c, 0x44, 0xb5, 0x5b, 0x21, 0xa3, 0x89, 0x5f, 0x96, 0x45, 0xca, 0x4d, 0xa4, 0x21, 0x99, 0x70, 0xda, 0xc4, 0xc4, 0xa0, 0xe5, 0xf4, 0xec, 0x0a}} , + {{0x07, 0x68, 0x21, 0x65, 0xe9, 0x08, 0xa0, 0x0b, 0x6a, 0x4a, 0xba, 0xb5, 0x80, 0xaf, 0xd0, 0x1b, 0xc5, 0xf5, 0x4b, 0x73, 0x50, 0x60, 0x2d, 0x71, 0x69, 0x61, 0x0e, 0xc0, 0x20, 0x40, 0x30, 0x19}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd0, 0x75, 0x57, 0x3b, 0xeb, 0x5c, 0x14, 0x56, 0x50, 0xc9, 0x4f, 0xb8, 0xb8, 0x1e, 0xa3, 0xf4, 0xab, 0xf5, 0xa9, 0x20, 0x15, 0x94, 0x82, 0xda, 0x96, 0x1c, 0x9b, 0x59, 0x8c, 0xff, 0xf4, 0x51}} , + {{0xc1, 0x3a, 0x86, 0xd7, 0xb0, 0x06, 0x84, 0x7f, 0x1b, 0xbd, 0xd4, 0x07, 0x78, 0x80, 0x2e, 0xb1, 0xb4, 0xee, 0x52, 0x38, 0xee, 0x9a, 0xf9, 0xf6, 0xf3, 0x41, 0x6e, 0xd4, 0x88, 0x95, 0xac, 0x35}}}, +{{{0x41, 0x97, 0xbf, 0x71, 0x6a, 0x9b, 0x72, 0xec, 0xf3, 0xf8, 0x6b, 0xe6, 0x0e, 0x6c, 0x69, 0xa5, 0x2f, 0x68, 0x52, 0xd8, 0x61, 0x81, 0xc0, 0x63, 0x3f, 0xa6, 0x3c, 0x13, 0x90, 0xe6, 0x8d, 0x56}} , + {{0xe8, 0x39, 0x30, 0x77, 0x23, 0xb1, 0xfd, 0x1b, 0x3d, 0x3e, 0x74, 0x4d, 0x7f, 0xae, 0x5b, 0x3a, 0xb4, 0x65, 0x0e, 0x3a, 0x43, 0xdc, 0xdc, 0x41, 0x47, 0xe6, 0xe8, 0x92, 0x09, 0x22, 0x48, 0x4c}}}, +{{{0x85, 0x57, 0x9f, 0xb5, 0xc8, 0x06, 0xb2, 0x9f, 0x47, 0x3f, 0xf0, 0xfa, 0xe6, 0xa9, 0xb1, 0x9b, 0x6f, 0x96, 0x7d, 0xf9, 0xa4, 0x65, 0x09, 0x75, 0x32, 0xa6, 0x6c, 0x7f, 0x47, 0x4b, 0x2f, 0x4f}} , + {{0x34, 0xe9, 0x59, 0x93, 0x9d, 0x26, 0x80, 0x54, 0xf2, 0xcc, 0x3c, 0xc2, 0x25, 0x85, 0xe3, 0x6a, 0xc1, 0x62, 0x04, 0xa7, 0x08, 0x32, 0x6d, 0xa1, 0x39, 0x84, 0x8a, 0x3b, 0x87, 0x5f, 0x11, 0x13}}}, +{{{0xda, 0x03, 0x34, 0x66, 0xc4, 0x0c, 0x73, 0x6e, 0xbc, 0x24, 0xb5, 0xf9, 0x70, 0x81, 0x52, 0xe9, 0xf4, 0x7c, 0x23, 0xdd, 0x9f, 0xb8, 0x46, 0xef, 0x1d, 0x22, 0x55, 0x7d, 0x71, 0xc4, 0x42, 0x33}} , + {{0xc5, 0x37, 0x69, 0x5b, 0xa8, 0xc6, 0x9d, 0xa4, 0xfc, 0x61, 0x6e, 0x68, 0x46, 0xea, 0xd7, 0x1c, 0x67, 0xd2, 0x7d, 0xfa, 0xf1, 0xcc, 0x54, 0x8d, 0x36, 0x35, 0xc9, 0x00, 0xdf, 0x6c, 0x67, 0x50}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x9a, 0x4d, 0x42, 0x29, 0x5d, 0xa4, 0x6b, 0x6f, 0xa8, 0x8a, 0x4d, 0x91, 0x7b, 0xd2, 0xdf, 0x36, 0xef, 0x01, 0x22, 0xc5, 0xcc, 0x8d, 0xeb, 0x58, 0x3d, 0xb3, 0x50, 0xfc, 0x8b, 0x97, 0x96, 0x33}} , + {{0x93, 0x33, 0x07, 0xc8, 0x4a, 0xca, 0xd0, 0xb1, 0xab, 0xbd, 0xdd, 0xa7, 0x7c, 0xac, 0x3e, 0x45, 0xcb, 0xcc, 0x07, 0x91, 0xbf, 0x35, 0x9d, 0xcb, 0x7d, 0x12, 0x3c, 0x11, 0x59, 0x13, 0xcf, 0x5c}}}, +{{{0x45, 0xb8, 0x41, 0xd7, 0xab, 0x07, 0x15, 0x00, 0x8e, 0xce, 0xdf, 0xb2, 0x43, 0x5c, 0x01, 0xdc, 0xf4, 0x01, 0x51, 0x95, 0x10, 0x5a, 0xf6, 0x24, 0x24, 0xa0, 0x19, 0x3a, 0x09, 0x2a, 0xaa, 0x3f}} , + {{0xdc, 0x8e, 0xeb, 0xc6, 0xbf, 0xdd, 0x11, 0x7b, 0xe7, 0x47, 0xe6, 0xce, 0xe7, 0xb6, 0xc5, 0xe8, 0x8a, 0xdc, 0x4b, 0x57, 0x15, 0x3b, 0x66, 0xca, 0x89, 0xa3, 0xfd, 0xac, 0x0d, 0xe1, 0x1d, 0x7a}}}, +{{{0x89, 0xef, 0xbf, 0x03, 0x75, 0xd0, 0x29, 0x50, 0xcb, 0x7d, 0xd6, 0xbe, 0xad, 0x5f, 0x7b, 0x00, 0x32, 0xaa, 0x98, 0xed, 0x3f, 0x8f, 0x92, 0xcb, 0x81, 0x56, 0x01, 0x63, 0x64, 0xa3, 0x38, 0x39}} , + {{0x8b, 0xa4, 0xd6, 0x50, 0xb4, 0xaa, 0x5d, 0x64, 0x64, 0x76, 0x2e, 0xa1, 0xa6, 0xb3, 0xb8, 0x7c, 0x7a, 0x56, 0xf5, 0x5c, 0x4e, 0x84, 0x5c, 0xfb, 0xdd, 0xca, 0x48, 0x8b, 0x48, 0xb9, 0xba, 0x34}}}, +{{{0xc5, 0xe3, 0xe8, 0xae, 0x17, 0x27, 0xe3, 0x64, 0x60, 0x71, 0x47, 0x29, 0x02, 0x0f, 0x92, 0x5d, 0x10, 0x93, 0xc8, 0x0e, 0xa1, 0xed, 0xba, 0xa9, 0x96, 0x1c, 0xc5, 0x76, 0x30, 0xcd, 0xf9, 0x30}} , + {{0x95, 0xb0, 0xbd, 0x8c, 0xbc, 0xa7, 0x4f, 0x7e, 0xfd, 0x4e, 0x3a, 0xbf, 0x5f, 0x04, 0x79, 0x80, 0x2b, 0x5a, 0x9f, 0x4f, 0x68, 0x21, 0x19, 0x71, 0xc6, 0x20, 0x01, 0x42, 0xaa, 0xdf, 0xae, 0x2c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x90, 0x6e, 0x7e, 0x4b, 0x71, 0x93, 0xc0, 0x72, 0xed, 0xeb, 0x71, 0x24, 0x97, 0x26, 0x9c, 0xfe, 0xcb, 0x3e, 0x59, 0x19, 0xa8, 0x0f, 0x75, 0x7d, 0xbe, 0x18, 0xe6, 0x96, 0x1e, 0x95, 0x70, 0x60}} , + {{0x89, 0x66, 0x3e, 0x1d, 0x4c, 0x5f, 0xfe, 0xc0, 0x04, 0x43, 0xd6, 0x44, 0x19, 0xb5, 0xad, 0xc7, 0x22, 0xdc, 0x71, 0x28, 0x64, 0xde, 0x41, 0x38, 0x27, 0x8f, 0x2c, 0x6b, 0x08, 0xb8, 0xb8, 0x7b}}}, +{{{0x3d, 0x70, 0x27, 0x9d, 0xd9, 0xaf, 0xb1, 0x27, 0xaf, 0xe3, 0x5d, 0x1e, 0x3a, 0x30, 0x54, 0x61, 0x60, 0xe8, 0xc3, 0x26, 0x3a, 0xbc, 0x7e, 0xf5, 0x81, 0xdd, 0x64, 0x01, 0x04, 0xeb, 0xc0, 0x1e}} , + {{0xda, 0x2c, 0xa4, 0xd1, 0xa1, 0xc3, 0x5c, 0x6e, 0x32, 0x07, 0x1f, 0xb8, 0x0e, 0x19, 0x9e, 0x99, 0x29, 0x33, 0x9a, 0xae, 0x7a, 0xed, 0x68, 0x42, 0x69, 0x7c, 0x07, 0xb3, 0x38, 0x2c, 0xf6, 0x3d}}}, +{{{0x64, 0xaa, 0xb5, 0x88, 0x79, 0x65, 0x38, 0x8c, 0x94, 0xd6, 0x62, 0x37, 0x7d, 0x64, 0xcd, 0x3a, 0xeb, 0xff, 0xe8, 0x81, 0x09, 0xc7, 0x6a, 0x50, 0x09, 0x0d, 0x28, 0x03, 0x0d, 0x9a, 0x93, 0x0a}} , + {{0x42, 0xa3, 0xf1, 0xc5, 0xb4, 0x0f, 0xd8, 0xc8, 0x8d, 0x15, 0x31, 0xbd, 0xf8, 0x07, 0x8b, 0xcd, 0x08, 0x8a, 0xfb, 0x18, 0x07, 0xfe, 0x8e, 0x52, 0x86, 0xef, 0xbe, 0xec, 0x49, 0x52, 0x99, 0x08}}}, +{{{0x0f, 0xa9, 0xd5, 0x01, 0xaa, 0x48, 0x4f, 0x28, 0x66, 0x32, 0x1a, 0xba, 0x7c, 0xea, 0x11, 0x80, 0x17, 0x18, 0x9b, 0x56, 0x88, 0x25, 0x06, 0x69, 0x12, 0x2c, 0xea, 0x56, 0x69, 0x41, 0x24, 0x19}} , + {{0xde, 0x21, 0xf0, 0xda, 0x8a, 0xfb, 0xb1, 0xb8, 0xcd, 0xc8, 0x6a, 0x82, 0x19, 0x73, 0xdb, 0xc7, 0xcf, 0x88, 0xeb, 0x96, 0xee, 0x6f, 0xfb, 0x06, 0xd2, 0xcd, 0x7d, 0x7b, 0x12, 0x28, 0x8e, 0x0c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x93, 0x44, 0x97, 0xce, 0x28, 0xff, 0x3a, 0x40, 0xc4, 0xf5, 0xf6, 0x9b, 0xf4, 0x6b, 0x07, 0x84, 0xfb, 0x98, 0xd8, 0xec, 0x8c, 0x03, 0x57, 0xec, 0x49, 0xed, 0x63, 0xb6, 0xaa, 0xff, 0x98, 0x28}} , + {{0x3d, 0x16, 0x35, 0xf3, 0x46, 0xbc, 0xb3, 0xf4, 0xc6, 0xb6, 0x4f, 0xfa, 0xf4, 0xa0, 0x13, 0xe6, 0x57, 0x45, 0x93, 0xb9, 0xbc, 0xd6, 0x59, 0xe7, 0x77, 0x94, 0x6c, 0xab, 0x96, 0x3b, 0x4f, 0x09}}}, +{{{0x5a, 0xf7, 0x6b, 0x01, 0x12, 0x4f, 0x51, 0xc1, 0x70, 0x84, 0x94, 0x47, 0xb2, 0x01, 0x6c, 0x71, 0xd7, 0xcc, 0x17, 0x66, 0x0f, 0x59, 0x5d, 0x5d, 0x10, 0x01, 0x57, 0x11, 0xf5, 0xdd, 0xe2, 0x34}} , + {{0x26, 0xd9, 0x1f, 0x5c, 0x58, 0xac, 0x8b, 0x03, 0xd2, 0xc3, 0x85, 0x0f, 0x3a, 0xc3, 0x7f, 0x6d, 0x8e, 0x86, 0xcd, 0x52, 0x74, 0x8f, 0x55, 0x77, 0x17, 0xb7, 0x8e, 0xb7, 0x88, 0xea, 0xda, 0x1b}}}, +{{{0xb6, 0xea, 0x0e, 0x40, 0x93, 0x20, 0x79, 0x35, 0x6a, 0x61, 0x84, 0x5a, 0x07, 0x6d, 0xf9, 0x77, 0x6f, 0xed, 0x69, 0x1c, 0x0d, 0x25, 0x76, 0xcc, 0xf0, 0xdb, 0xbb, 0xc5, 0xad, 0xe2, 0x26, 0x57}} , + {{0xcf, 0xe8, 0x0e, 0x6b, 0x96, 0x7d, 0xed, 0x27, 0xd1, 0x3c, 0xa9, 0xd9, 0x50, 0xa9, 0x98, 0x84, 0x5e, 0x86, 0xef, 0xd6, 0xf0, 0xf8, 0x0e, 0x89, 0x05, 0x2f, 0xd9, 0x5f, 0x15, 0x5f, 0x73, 0x79}}}, +{{{0xc8, 0x5c, 0x16, 0xfe, 0xed, 0x9f, 0x26, 0x56, 0xf6, 0x4b, 0x9f, 0xa7, 0x0a, 0x85, 0xfe, 0xa5, 0x8c, 0x87, 0xdd, 0x98, 0xce, 0x4e, 0xc3, 0x58, 0x55, 0xb2, 0x7b, 0x3d, 0xd8, 0x6b, 0xb5, 0x4c}} , + {{0x65, 0x38, 0xa0, 0x15, 0xfa, 0xa7, 0xb4, 0x8f, 0xeb, 0xc4, 0x86, 0x9b, 0x30, 0xa5, 0x5e, 0x4d, 0xea, 0x8a, 0x9a, 0x9f, 0x1a, 0xd8, 0x5b, 0x53, 0x14, 0x19, 0x25, 0x63, 0xb4, 0x6f, 0x1f, 0x5d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xac, 0x8f, 0xbc, 0x1e, 0x7d, 0x8b, 0x5a, 0x0b, 0x8d, 0xaf, 0x76, 0x2e, 0x71, 0xe3, 0x3b, 0x6f, 0x53, 0x2f, 0x3e, 0x90, 0x95, 0xd4, 0x35, 0x14, 0x4f, 0x8c, 0x3c, 0xce, 0x57, 0x1c, 0x76, 0x49}} , + {{0xa8, 0x50, 0xe1, 0x61, 0x6b, 0x57, 0x35, 0xeb, 0x44, 0x0b, 0x0c, 0x6e, 0xf9, 0x25, 0x80, 0x74, 0xf2, 0x8f, 0x6f, 0x7a, 0x3e, 0x7f, 0x2d, 0xf3, 0x4e, 0x09, 0x65, 0x10, 0x5e, 0x03, 0x25, 0x32}}}, +{{{0xa9, 0x60, 0xdc, 0x0f, 0x64, 0xe5, 0x1d, 0xe2, 0x8d, 0x4f, 0x79, 0x2f, 0x0e, 0x24, 0x02, 0x00, 0x05, 0x77, 0x43, 0x25, 0x3d, 0x6a, 0xc7, 0xb7, 0xbf, 0x04, 0x08, 0x65, 0xf4, 0x39, 0x4b, 0x65}} , + {{0x96, 0x19, 0x12, 0x6b, 0x6a, 0xb7, 0xe3, 0xdc, 0x45, 0x9b, 0xdb, 0xb4, 0xa8, 0xae, 0xdc, 0xa8, 0x14, 0x44, 0x65, 0x62, 0xce, 0x34, 0x9a, 0x84, 0x18, 0x12, 0x01, 0xf1, 0xe2, 0x7b, 0xce, 0x50}}}, +{{{0x41, 0x21, 0x30, 0x53, 0x1b, 0x47, 0x01, 0xb7, 0x18, 0xd8, 0x82, 0x57, 0xbd, 0xa3, 0x60, 0xf0, 0x32, 0xf6, 0x5b, 0xf0, 0x30, 0x88, 0x91, 0x59, 0xfd, 0x90, 0xa2, 0xb9, 0x55, 0x93, 0x21, 0x34}} , + {{0x97, 0x67, 0x9e, 0xeb, 0x6a, 0xf9, 0x6e, 0xd6, 0x73, 0xe8, 0x6b, 0x29, 0xec, 0x63, 0x82, 0x00, 0xa8, 0x99, 0x1c, 0x1d, 0x30, 0xc8, 0x90, 0x52, 0x90, 0xb6, 0x6a, 0x80, 0x4e, 0xff, 0x4b, 0x51}}}, +{{{0x0f, 0x7d, 0x63, 0x8c, 0x6e, 0x5c, 0xde, 0x30, 0xdf, 0x65, 0xfa, 0x2e, 0xb0, 0xa3, 0x25, 0x05, 0x54, 0xbd, 0x25, 0xba, 0x06, 0xae, 0xdf, 0x8b, 0xd9, 0x1b, 0xea, 0x38, 0xb3, 0x05, 0x16, 0x09}} , + {{0xc7, 0x8c, 0xbf, 0x64, 0x28, 0xad, 0xf8, 0xa5, 0x5a, 0x6f, 0xc9, 0xba, 0xd5, 0x7f, 0xd5, 0xd6, 0xbd, 0x66, 0x2f, 0x3d, 0xaa, 0x54, 0xf6, 0xba, 0x32, 0x22, 0x9a, 0x1e, 0x52, 0x05, 0xf4, 0x1d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xaa, 0x1f, 0xbb, 0xeb, 0xfe, 0xe4, 0x87, 0xfc, 0xb1, 0x2c, 0xb7, 0x88, 0xf4, 0xc6, 0xb9, 0xf5, 0x24, 0x46, 0xf2, 0xa5, 0x9f, 0x8f, 0x8a, 0x93, 0x70, 0x69, 0xd4, 0x56, 0xec, 0xfd, 0x06, 0x46}} , + {{0x4e, 0x66, 0xcf, 0x4e, 0x34, 0xce, 0x0c, 0xd9, 0xa6, 0x50, 0xd6, 0x5e, 0x95, 0xaf, 0xe9, 0x58, 0xfa, 0xee, 0x9b, 0xb8, 0xa5, 0x0f, 0x35, 0xe0, 0x43, 0x82, 0x6d, 0x65, 0xe6, 0xd9, 0x00, 0x0f}}}, +{{{0x7b, 0x75, 0x3a, 0xfc, 0x64, 0xd3, 0x29, 0x7e, 0xdd, 0x49, 0x9a, 0x59, 0x53, 0xbf, 0xb4, 0xa7, 0x52, 0xb3, 0x05, 0xab, 0xc3, 0xaf, 0x16, 0x1a, 0x85, 0x42, 0x32, 0xa2, 0x86, 0xfa, 0x39, 0x43}} , + {{0x0e, 0x4b, 0xa3, 0x63, 0x8a, 0xfe, 0xa5, 0x58, 0xf1, 0x13, 0xbd, 0x9d, 0xaa, 0x7f, 0x76, 0x40, 0x70, 0x81, 0x10, 0x75, 0x99, 0xbb, 0xbe, 0x0b, 0x16, 0xe9, 0xba, 0x62, 0x34, 0xcc, 0x07, 0x6d}}}, +{{{0xc3, 0xf1, 0xc6, 0x93, 0x65, 0xee, 0x0b, 0xbc, 0xea, 0x14, 0xf0, 0xc1, 0xf8, 0x84, 0x89, 0xc2, 0xc9, 0xd7, 0xea, 0x34, 0xca, 0xa7, 0xc4, 0x99, 0xd5, 0x50, 0x69, 0xcb, 0xd6, 0x21, 0x63, 0x7c}} , + {{0x99, 0xeb, 0x7c, 0x31, 0x73, 0x64, 0x67, 0x7f, 0x0c, 0x66, 0xaa, 0x8c, 0x69, 0x91, 0xe2, 0x26, 0xd3, 0x23, 0xe2, 0x76, 0x5d, 0x32, 0x52, 0xdf, 0x5d, 0xc5, 0x8f, 0xb7, 0x7c, 0x84, 0xb3, 0x70}}}, +{{{0xeb, 0x01, 0xc7, 0x36, 0x97, 0x4e, 0xb6, 0xab, 0x5f, 0x0d, 0x2c, 0xba, 0x67, 0x64, 0x55, 0xde, 0xbc, 0xff, 0xa6, 0xec, 0x04, 0xd3, 0x8d, 0x39, 0x56, 0x5e, 0xee, 0xf8, 0xe4, 0x2e, 0x33, 0x62}} , + {{0x65, 0xef, 0xb8, 0x9f, 0xc8, 0x4b, 0xa7, 0xfd, 0x21, 0x49, 0x9b, 0x92, 0x35, 0x82, 0xd6, 0x0a, 0x9b, 0xf2, 0x79, 0xf1, 0x47, 0x2f, 0x6a, 0x7e, 0x9f, 0xcf, 0x18, 0x02, 0x3c, 0xfb, 0x1b, 0x3e}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2f, 0x8b, 0xc8, 0x40, 0x51, 0xd1, 0xac, 0x1a, 0x0b, 0xe4, 0xa9, 0xa2, 0x42, 0x21, 0x19, 0x2f, 0x7b, 0x97, 0xbf, 0xf7, 0x57, 0x6d, 0x3f, 0x3d, 0x4f, 0x0f, 0xe2, 0xb2, 0x81, 0x00, 0x9e, 0x7b}} , + {{0x8c, 0x85, 0x2b, 0xc4, 0xfc, 0xf1, 0xab, 0xe8, 0x79, 0x22, 0xc4, 0x84, 0x17, 0x3a, 0xfa, 0x86, 0xa6, 0x7d, 0xf9, 0xf3, 0x6f, 0x03, 0x57, 0x20, 0x4d, 0x79, 0xf9, 0x6e, 0x71, 0x54, 0x38, 0x09}}}, +{{{0x40, 0x29, 0x74, 0xa8, 0x2f, 0x5e, 0xf9, 0x79, 0xa4, 0xf3, 0x3e, 0xb9, 0xfd, 0x33, 0x31, 0xac, 0x9a, 0x69, 0x88, 0x1e, 0x77, 0x21, 0x2d, 0xf3, 0x91, 0x52, 0x26, 0x15, 0xb2, 0xa6, 0xcf, 0x7e}} , + {{0xc6, 0x20, 0x47, 0x6c, 0xa4, 0x7d, 0xcb, 0x63, 0xea, 0x5b, 0x03, 0xdf, 0x3e, 0x88, 0x81, 0x6d, 0xce, 0x07, 0x42, 0x18, 0x60, 0x7e, 0x7b, 0x55, 0xfe, 0x6a, 0xf3, 0xda, 0x5c, 0x8b, 0x95, 0x10}}}, +{{{0x62, 0xe4, 0x0d, 0x03, 0xb4, 0xd7, 0xcd, 0xfa, 0xbd, 0x46, 0xdf, 0x93, 0x71, 0x10, 0x2c, 0xa8, 0x3b, 0xb6, 0x09, 0x05, 0x70, 0x84, 0x43, 0x29, 0xa8, 0x59, 0xf5, 0x8e, 0x10, 0xe4, 0xd7, 0x20}} , + {{0x57, 0x82, 0x1c, 0xab, 0xbf, 0x62, 0x70, 0xe8, 0xc4, 0xcf, 0xf0, 0x28, 0x6e, 0x16, 0x3c, 0x08, 0x78, 0x89, 0x85, 0x46, 0x0f, 0xf6, 0x7f, 0xcf, 0xcb, 0x7e, 0xb8, 0x25, 0xe9, 0x5a, 0xfa, 0x03}}}, +{{{0xfb, 0x95, 0x92, 0x63, 0x50, 0xfc, 0x62, 0xf0, 0xa4, 0x5e, 0x8c, 0x18, 0xc2, 0x17, 0x24, 0xb7, 0x78, 0xc2, 0xa9, 0xe7, 0x6a, 0x32, 0xd6, 0x29, 0x85, 0xaf, 0xcb, 0x8d, 0x91, 0x13, 0xda, 0x6b}} , + {{0x36, 0x0a, 0xc2, 0xb6, 0x4b, 0xa5, 0x5d, 0x07, 0x17, 0x41, 0x31, 0x5f, 0x62, 0x46, 0xf8, 0x92, 0xf9, 0x66, 0x48, 0x73, 0xa6, 0x97, 0x0d, 0x7d, 0x88, 0xee, 0x62, 0xb1, 0x03, 0xa8, 0x3f, 0x2c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x4a, 0xb1, 0x70, 0x8a, 0xa9, 0xe8, 0x63, 0x79, 0x00, 0xe2, 0x25, 0x16, 0xca, 0x4b, 0x0f, 0xa4, 0x66, 0xad, 0x19, 0x9f, 0x88, 0x67, 0x0c, 0x8b, 0xc2, 0x4a, 0x5b, 0x2b, 0x6d, 0x95, 0xaf, 0x19}} , + {{0x8b, 0x9d, 0xb6, 0xcc, 0x60, 0xb4, 0x72, 0x4f, 0x17, 0x69, 0x5a, 0x4a, 0x68, 0x34, 0xab, 0xa1, 0x45, 0x32, 0x3c, 0x83, 0x87, 0x72, 0x30, 0x54, 0x77, 0x68, 0xae, 0xfb, 0xb5, 0x8b, 0x22, 0x5e}}}, +{{{0xf1, 0xb9, 0x87, 0x35, 0xc5, 0xbb, 0xb9, 0xcf, 0xf5, 0xd6, 0xcd, 0xd5, 0x0c, 0x7c, 0x0e, 0xe6, 0x90, 0x34, 0xfb, 0x51, 0x42, 0x1e, 0x6d, 0xac, 0x9a, 0x46, 0xc4, 0x97, 0x29, 0x32, 0xbf, 0x45}} , + {{0x66, 0x9e, 0xc6, 0x24, 0xc0, 0xed, 0xa5, 0x5d, 0x88, 0xd4, 0xf0, 0x73, 0x97, 0x7b, 0xea, 0x7f, 0x42, 0xff, 0x21, 0xa0, 0x9b, 0x2f, 0x9a, 0xfd, 0x53, 0x57, 0x07, 0x84, 0x48, 0x88, 0x9d, 0x52}}}, +{{{0xc6, 0x96, 0x48, 0x34, 0x2a, 0x06, 0xaf, 0x94, 0x3d, 0xf4, 0x1a, 0xcf, 0xf2, 0xc0, 0x21, 0xc2, 0x42, 0x5e, 0xc8, 0x2f, 0x35, 0xa2, 0x3e, 0x29, 0xfa, 0x0c, 0x84, 0xe5, 0x89, 0x72, 0x7c, 0x06}} , + {{0x32, 0x65, 0x03, 0xe5, 0x89, 0xa6, 0x6e, 0xb3, 0x5b, 0x8e, 0xca, 0xeb, 0xfe, 0x22, 0x56, 0x8b, 0x5d, 0x14, 0x4b, 0x4d, 0xf9, 0xbe, 0xb5, 0xf5, 0xe6, 0x5c, 0x7b, 0x8b, 0xf4, 0x13, 0x11, 0x34}}}, +{{{0x07, 0xc6, 0x22, 0x15, 0xe2, 0x9c, 0x60, 0xa2, 0x19, 0xd9, 0x27, 0xae, 0x37, 0x4e, 0xa6, 0xc9, 0x80, 0xa6, 0x91, 0x8f, 0x12, 0x49, 0xe5, 0x00, 0x18, 0x47, 0xd1, 0xd7, 0x28, 0x22, 0x63, 0x39}} , + {{0xe8, 0xe2, 0x00, 0x7e, 0xf2, 0x9e, 0x1e, 0x99, 0x39, 0x95, 0x04, 0xbd, 0x1e, 0x67, 0x7b, 0xb2, 0x26, 0xac, 0xe6, 0xaa, 0xe2, 0x46, 0xd5, 0xe4, 0xe8, 0x86, 0xbd, 0xab, 0x7c, 0x55, 0x59, 0x6f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x24, 0x64, 0x6e, 0x9b, 0x35, 0x71, 0x78, 0xce, 0x33, 0x03, 0x21, 0x33, 0x36, 0xf1, 0x73, 0x9b, 0xb9, 0x15, 0x8b, 0x2c, 0x69, 0xcf, 0x4d, 0xed, 0x4f, 0x4d, 0x57, 0x14, 0x13, 0x82, 0xa4, 0x4d}} , + {{0x65, 0x6e, 0x0a, 0xa4, 0x59, 0x07, 0x17, 0xf2, 0x6b, 0x4a, 0x1f, 0x6e, 0xf6, 0xb5, 0xbc, 0x62, 0xe4, 0xb6, 0xda, 0xa2, 0x93, 0xbc, 0x29, 0x05, 0xd2, 0xd2, 0x73, 0x46, 0x03, 0x16, 0x40, 0x31}}}, +{{{0x4c, 0x73, 0x6d, 0x15, 0xbd, 0xa1, 0x4d, 0x5c, 0x13, 0x0b, 0x24, 0x06, 0x98, 0x78, 0x1c, 0x5b, 0xeb, 0x1f, 0x18, 0x54, 0x43, 0xd9, 0x55, 0x66, 0xda, 0x29, 0x21, 0xe8, 0xb8, 0x3c, 0x42, 0x22}} , + {{0xb4, 0xcd, 0x08, 0x6f, 0x15, 0x23, 0x1a, 0x0b, 0x22, 0xed, 0xd1, 0xf1, 0xa7, 0xc7, 0x73, 0x45, 0xf3, 0x9e, 0xce, 0x76, 0xb7, 0xf6, 0x39, 0xb6, 0x8e, 0x79, 0xbe, 0xe9, 0x9b, 0xcf, 0x7d, 0x62}}}, +{{{0x92, 0x5b, 0xfc, 0x72, 0xfd, 0xba, 0xf1, 0xfd, 0xa6, 0x7c, 0x95, 0xe3, 0x61, 0x3f, 0xe9, 0x03, 0xd4, 0x2b, 0xd4, 0x20, 0xd9, 0xdb, 0x4d, 0x32, 0x3e, 0xf5, 0x11, 0x64, 0xe3, 0xb4, 0xbe, 0x32}} , + {{0x86, 0x17, 0x90, 0xe7, 0xc9, 0x1f, 0x10, 0xa5, 0x6a, 0x2d, 0x39, 0xd0, 0x3b, 0xc4, 0xa6, 0xe9, 0x59, 0x13, 0xda, 0x1a, 0xe6, 0xa0, 0xb9, 0x3c, 0x50, 0xb8, 0x40, 0x7c, 0x15, 0x36, 0x5a, 0x42}}}, +{{{0xb4, 0x0b, 0x32, 0xab, 0xdc, 0x04, 0x51, 0x55, 0x21, 0x1e, 0x0b, 0x75, 0x99, 0x89, 0x73, 0x35, 0x3a, 0x91, 0x2b, 0xfe, 0xe7, 0x49, 0xea, 0x76, 0xc1, 0xf9, 0x46, 0xb9, 0x53, 0x02, 0x23, 0x04}} , + {{0xfc, 0x5a, 0x1e, 0x1d, 0x74, 0x58, 0x95, 0xa6, 0x8f, 0x7b, 0x97, 0x3e, 0x17, 0x3b, 0x79, 0x2d, 0xa6, 0x57, 0xef, 0x45, 0x02, 0x0b, 0x4d, 0x6e, 0x9e, 0x93, 0x8d, 0x2f, 0xd9, 0x9d, 0xdb, 0x04}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xc0, 0xd7, 0x56, 0x97, 0x58, 0x91, 0xde, 0x09, 0x4f, 0x9f, 0xbe, 0x63, 0xb0, 0x83, 0x86, 0x43, 0x5d, 0xbc, 0xe0, 0xf3, 0xc0, 0x75, 0xbf, 0x8b, 0x8e, 0xaa, 0xf7, 0x8b, 0x64, 0x6e, 0xb0, 0x63}} , + {{0x16, 0xae, 0x8b, 0xe0, 0x9b, 0x24, 0x68, 0x5c, 0x44, 0xc2, 0xd0, 0x08, 0xb7, 0x7b, 0x62, 0xfd, 0x7f, 0xd8, 0xd4, 0xb7, 0x50, 0xfd, 0x2c, 0x1b, 0xbf, 0x41, 0x95, 0xd9, 0x8e, 0xd8, 0x17, 0x1b}}}, +{{{0x86, 0x55, 0x37, 0x8e, 0xc3, 0x38, 0x48, 0x14, 0xb5, 0x97, 0xd2, 0xa7, 0x54, 0x45, 0xf1, 0x35, 0x44, 0x38, 0x9e, 0xf1, 0x1b, 0xb6, 0x34, 0x00, 0x3c, 0x96, 0xee, 0x29, 0x00, 0xea, 0x2c, 0x0b}} , + {{0xea, 0xda, 0x99, 0x9e, 0x19, 0x83, 0x66, 0x6d, 0xe9, 0x76, 0x87, 0x50, 0xd1, 0xfd, 0x3c, 0x60, 0x87, 0xc6, 0x41, 0xd9, 0x8e, 0xdb, 0x5e, 0xde, 0xaa, 0x9a, 0xd3, 0x28, 0xda, 0x95, 0xea, 0x47}}}, +{{{0xd0, 0x80, 0xba, 0x19, 0xae, 0x1d, 0xa9, 0x79, 0xf6, 0x3f, 0xac, 0x5d, 0x6f, 0x96, 0x1f, 0x2a, 0xce, 0x29, 0xb2, 0xff, 0x37, 0xf1, 0x94, 0x8f, 0x0c, 0xb5, 0x28, 0xba, 0x9a, 0x21, 0xf6, 0x66}} , + {{0x02, 0xfb, 0x54, 0xb8, 0x05, 0xf3, 0x81, 0x52, 0x69, 0x34, 0x46, 0x9d, 0x86, 0x76, 0x8f, 0xd7, 0xf8, 0x6a, 0x66, 0xff, 0xe6, 0xa7, 0x90, 0xf7, 0x5e, 0xcd, 0x6a, 0x9b, 0x55, 0xfc, 0x9d, 0x48}}}, +{{{0xbd, 0xaa, 0x13, 0xe6, 0xcd, 0x45, 0x4a, 0xa4, 0x59, 0x0a, 0x64, 0xb1, 0x98, 0xd6, 0x34, 0x13, 0x04, 0xe6, 0x97, 0x94, 0x06, 0xcb, 0xd4, 0x4e, 0xbb, 0x96, 0xcd, 0xd1, 0x57, 0xd1, 0xe3, 0x06}} , + {{0x7a, 0x6c, 0x45, 0x27, 0xc4, 0x93, 0x7f, 0x7d, 0x7c, 0x62, 0x50, 0x38, 0x3a, 0x6b, 0xb5, 0x88, 0xc6, 0xd9, 0xf1, 0x78, 0x19, 0xb9, 0x39, 0x93, 0x3d, 0xc9, 0xe0, 0x9c, 0x3c, 0xce, 0xf5, 0x72}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x24, 0xea, 0x23, 0x7d, 0x56, 0x2c, 0xe2, 0x59, 0x0e, 0x85, 0x60, 0x04, 0x88, 0x5a, 0x74, 0x1e, 0x4b, 0xef, 0x13, 0xda, 0x4c, 0xff, 0x83, 0x45, 0x85, 0x3f, 0x08, 0x95, 0x2c, 0x20, 0x13, 0x1f}} , + {{0x48, 0x5f, 0x27, 0x90, 0x5c, 0x02, 0x42, 0xad, 0x78, 0x47, 0x5c, 0xb5, 0x7e, 0x08, 0x85, 0x00, 0xfa, 0x7f, 0xfd, 0xfd, 0xe7, 0x09, 0x11, 0xf2, 0x7e, 0x1b, 0x38, 0x6c, 0x35, 0x6d, 0x33, 0x66}}}, +{{{0x93, 0x03, 0x36, 0x81, 0xac, 0xe4, 0x20, 0x09, 0x35, 0x4c, 0x45, 0xb2, 0x1e, 0x4c, 0x14, 0x21, 0xe6, 0xe9, 0x8a, 0x7b, 0x8d, 0xfe, 0x1e, 0xc6, 0x3e, 0xc1, 0x35, 0xfa, 0xe7, 0x70, 0x4e, 0x1d}} , + {{0x61, 0x2e, 0xc2, 0xdd, 0x95, 0x57, 0xd1, 0xab, 0x80, 0xe8, 0x63, 0x17, 0xb5, 0x48, 0xe4, 0x8a, 0x11, 0x9e, 0x72, 0xbe, 0x85, 0x8d, 0x51, 0x0a, 0xf2, 0x9f, 0xe0, 0x1c, 0xa9, 0x07, 0x28, 0x7b}}}, +{{{0xbb, 0x71, 0x14, 0x5e, 0x26, 0x8c, 0x3d, 0xc8, 0xe9, 0x7c, 0xd3, 0xd6, 0xd1, 0x2f, 0x07, 0x6d, 0xe6, 0xdf, 0xfb, 0x79, 0xd6, 0x99, 0x59, 0x96, 0x48, 0x40, 0x0f, 0x3a, 0x7b, 0xb2, 0xa0, 0x72}} , + {{0x4e, 0x3b, 0x69, 0xc8, 0x43, 0x75, 0x51, 0x6c, 0x79, 0x56, 0xe4, 0xcb, 0xf7, 0xa6, 0x51, 0xc2, 0x2c, 0x42, 0x0b, 0xd4, 0x82, 0x20, 0x1c, 0x01, 0x08, 0x66, 0xd7, 0xbf, 0x04, 0x56, 0xfc, 0x02}}}, +{{{0x24, 0xe8, 0xb7, 0x60, 0xae, 0x47, 0x80, 0xfc, 0xe5, 0x23, 0xe7, 0xc2, 0xc9, 0x85, 0xe6, 0x98, 0xa0, 0x29, 0x4e, 0xe1, 0x84, 0x39, 0x2d, 0x95, 0x2c, 0xf3, 0x45, 0x3c, 0xff, 0xaf, 0x27, 0x4c}} , + {{0x6b, 0xa6, 0xf5, 0x4b, 0x11, 0xbd, 0xba, 0x5b, 0x9e, 0xc4, 0xa4, 0x51, 0x1e, 0xbe, 0xd0, 0x90, 0x3a, 0x9c, 0xc2, 0x26, 0xb6, 0x1e, 0xf1, 0x95, 0x7d, 0xc8, 0x6d, 0x52, 0xe6, 0x99, 0x2c, 0x5f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x85, 0xe0, 0x24, 0x32, 0xb4, 0xd1, 0xef, 0xfc, 0x69, 0xa2, 0xbf, 0x8f, 0x72, 0x2c, 0x95, 0xf6, 0xe4, 0x6e, 0x7d, 0x90, 0xf7, 0x57, 0x81, 0xa0, 0xf7, 0xda, 0xef, 0x33, 0x07, 0xe3, 0x6b, 0x78}} , + {{0x36, 0x27, 0x3e, 0xc6, 0x12, 0x07, 0xab, 0x4e, 0xbe, 0x69, 0x9d, 0xb3, 0xbe, 0x08, 0x7c, 0x2a, 0x47, 0x08, 0xfd, 0xd4, 0xcd, 0x0e, 0x27, 0x34, 0x5b, 0x98, 0x34, 0x2f, 0x77, 0x5f, 0x3a, 0x65}}}, +{{{0x13, 0xaa, 0x2e, 0x4c, 0xf0, 0x22, 0xb8, 0x6c, 0xb3, 0x19, 0x4d, 0xeb, 0x6b, 0xd0, 0xa4, 0xc6, 0x9c, 0xdd, 0xc8, 0x5b, 0x81, 0x57, 0x89, 0xdf, 0x33, 0xa9, 0x68, 0x49, 0x80, 0xe4, 0xfe, 0x21}} , + {{0x00, 0x17, 0x90, 0x30, 0xe9, 0xd3, 0x60, 0x30, 0x31, 0xc2, 0x72, 0x89, 0x7a, 0x36, 0xa5, 0xbd, 0x39, 0x83, 0x85, 0x50, 0xa1, 0x5d, 0x6c, 0x41, 0x1d, 0xb5, 0x2c, 0x07, 0x40, 0x77, 0x0b, 0x50}}}, +{{{0x64, 0x34, 0xec, 0xc0, 0x9e, 0x44, 0x41, 0xaf, 0xa0, 0x36, 0x05, 0x6d, 0xea, 0x30, 0x25, 0x46, 0x35, 0x24, 0x9d, 0x86, 0xbd, 0x95, 0xf1, 0x6a, 0x46, 0xd7, 0x94, 0x54, 0xf9, 0x3b, 0xbd, 0x5d}} , + {{0x77, 0x5b, 0xe2, 0x37, 0xc7, 0xe1, 0x7c, 0x13, 0x8c, 0x9f, 0x7b, 0x7b, 0x2a, 0xce, 0x42, 0xa3, 0xb9, 0x2a, 0x99, 0xa8, 0xc0, 0xd8, 0x3c, 0x86, 0xb0, 0xfb, 0xe9, 0x76, 0x77, 0xf7, 0xf5, 0x56}}}, +{{{0xdf, 0xb3, 0x46, 0x11, 0x6e, 0x13, 0xb7, 0x28, 0x4e, 0x56, 0xdd, 0xf1, 0xac, 0xad, 0x58, 0xc3, 0xf8, 0x88, 0x94, 0x5e, 0x06, 0x98, 0xa1, 0xe4, 0x6a, 0xfb, 0x0a, 0x49, 0x5d, 0x8a, 0xfe, 0x77}} , + {{0x46, 0x02, 0xf5, 0xa5, 0xaf, 0xc5, 0x75, 0x6d, 0xba, 0x45, 0x35, 0x0a, 0xfe, 0xc9, 0xac, 0x22, 0x91, 0x8d, 0x21, 0x95, 0x33, 0x03, 0xc0, 0x8a, 0x16, 0xf3, 0x39, 0xe0, 0x01, 0x0f, 0x53, 0x3c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x34, 0x75, 0x37, 0x1f, 0x34, 0x4e, 0xa9, 0x1d, 0x68, 0x67, 0xf8, 0x49, 0x98, 0x96, 0xfc, 0x4c, 0x65, 0x97, 0xf7, 0x02, 0x4a, 0x52, 0x6c, 0x01, 0xbd, 0x48, 0xbb, 0x1b, 0xed, 0xa4, 0xe2, 0x53}} , + {{0x59, 0xd5, 0x9b, 0x5a, 0xa2, 0x90, 0xd3, 0xb8, 0x37, 0x4c, 0x55, 0x82, 0x28, 0x08, 0x0f, 0x7f, 0xaa, 0x81, 0x65, 0xe0, 0x0c, 0x52, 0xc9, 0xa3, 0x32, 0x27, 0x64, 0xda, 0xfd, 0x34, 0x23, 0x5a}}}, +{{{0xb5, 0xb0, 0x0c, 0x4d, 0xb3, 0x7b, 0x23, 0xc8, 0x1f, 0x8a, 0x39, 0x66, 0xe6, 0xba, 0x4c, 0x10, 0x37, 0xca, 0x9c, 0x7c, 0x05, 0x9e, 0xff, 0xc0, 0xf8, 0x8e, 0xb1, 0x8f, 0x6f, 0x67, 0x18, 0x26}} , + {{0x4b, 0x41, 0x13, 0x54, 0x23, 0x1a, 0xa4, 0x4e, 0xa9, 0x8b, 0x1e, 0x4b, 0xfc, 0x15, 0x24, 0xbb, 0x7e, 0xcb, 0xb6, 0x1e, 0x1b, 0xf5, 0xf2, 0xc8, 0x56, 0xec, 0x32, 0xa2, 0x60, 0x5b, 0xa0, 0x2a}}}, +{{{0xa4, 0x29, 0x47, 0x86, 0x2e, 0x92, 0x4f, 0x11, 0x4f, 0xf3, 0xb2, 0x5c, 0xd5, 0x3e, 0xa6, 0xb9, 0xc8, 0xe2, 0x33, 0x11, 0x1f, 0x01, 0x8f, 0xb0, 0x9b, 0xc7, 0xa5, 0xff, 0x83, 0x0f, 0x1e, 0x28}} , + {{0x1d, 0x29, 0x7a, 0xa1, 0xec, 0x8e, 0xb5, 0xad, 0xea, 0x02, 0x68, 0x60, 0x74, 0x29, 0x1c, 0xa5, 0xcf, 0xc8, 0x3b, 0x7d, 0x8b, 0x2b, 0x7c, 0xad, 0xa4, 0x40, 0x17, 0x51, 0x59, 0x7c, 0x2e, 0x5d}}}, +{{{0x0a, 0x6c, 0x4f, 0xbc, 0x3e, 0x32, 0xe7, 0x4a, 0x1a, 0x13, 0xc1, 0x49, 0x38, 0xbf, 0xf7, 0xc2, 0xd3, 0x8f, 0x6b, 0xad, 0x52, 0xf7, 0xcf, 0xbc, 0x27, 0xcb, 0x40, 0x67, 0x76, 0xcd, 0x6d, 0x56}} , + {{0xe5, 0xb0, 0x27, 0xad, 0xbe, 0x9b, 0xf2, 0xb5, 0x63, 0xde, 0x3a, 0x23, 0x95, 0xb7, 0x0a, 0x7e, 0xf3, 0x9e, 0x45, 0x6f, 0x19, 0x39, 0x75, 0x8f, 0x39, 0x3d, 0x0f, 0xc0, 0x9f, 0xf1, 0xe9, 0x51}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x88, 0xaa, 0x14, 0x24, 0x86, 0x94, 0x11, 0x12, 0x3e, 0x1a, 0xb5, 0xcc, 0xbb, 0xe0, 0x9c, 0xd5, 0x9c, 0x6d, 0xba, 0x58, 0x72, 0x8d, 0xfb, 0x22, 0x7b, 0x9f, 0x7c, 0x94, 0x30, 0xb3, 0x51, 0x21}} , + {{0xf6, 0x74, 0x3d, 0xf2, 0xaf, 0xd0, 0x1e, 0x03, 0x7c, 0x23, 0x6b, 0xc9, 0xfc, 0x25, 0x70, 0x90, 0xdc, 0x9a, 0xa4, 0xfb, 0x49, 0xfc, 0x3d, 0x0a, 0x35, 0x38, 0x6f, 0xe4, 0x7e, 0x50, 0x01, 0x2a}}}, +{{{0xd6, 0xe3, 0x96, 0x61, 0x3a, 0xfd, 0xef, 0x9b, 0x1f, 0x90, 0xa4, 0x24, 0x14, 0x5b, 0xc8, 0xde, 0x50, 0xb1, 0x1d, 0xaf, 0xe8, 0x55, 0x8a, 0x87, 0x0d, 0xfe, 0xaa, 0x3b, 0x82, 0x2c, 0x8d, 0x7b}} , + {{0x85, 0x0c, 0xaf, 0xf8, 0x83, 0x44, 0x49, 0xd9, 0x45, 0xcf, 0xf7, 0x48, 0xd9, 0x53, 0xb4, 0xf1, 0x65, 0xa0, 0xe1, 0xc3, 0xb3, 0x15, 0xed, 0x89, 0x9b, 0x4f, 0x62, 0xb3, 0x57, 0xa5, 0x45, 0x1c}}}, +{{{0x8f, 0x12, 0xea, 0xaf, 0xd1, 0x1f, 0x79, 0x10, 0x0b, 0xf6, 0xa3, 0x7b, 0xea, 0xac, 0x8b, 0x57, 0x32, 0x62, 0xe7, 0x06, 0x12, 0x51, 0xa0, 0x3b, 0x43, 0x5e, 0xa4, 0x20, 0x78, 0x31, 0xce, 0x0d}} , + {{0x84, 0x7c, 0xc2, 0xa6, 0x91, 0x23, 0xce, 0xbd, 0xdc, 0xf9, 0xce, 0xd5, 0x75, 0x30, 0x22, 0xe6, 0xf9, 0x43, 0x62, 0x0d, 0xf7, 0x75, 0x9d, 0x7f, 0x8c, 0xff, 0x7d, 0xe4, 0x72, 0xac, 0x9f, 0x1c}}}, +{{{0x88, 0xc1, 0x99, 0xd0, 0x3c, 0x1c, 0x5d, 0xb4, 0xef, 0x13, 0x0f, 0x90, 0xb9, 0x36, 0x2f, 0x95, 0x95, 0xc6, 0xdc, 0xde, 0x0a, 0x51, 0xe2, 0x8d, 0xf3, 0xbc, 0x51, 0xec, 0xdf, 0xb1, 0xa2, 0x5f}} , + {{0x2e, 0x68, 0xa1, 0x23, 0x7d, 0x9b, 0x40, 0x69, 0x85, 0x7b, 0x42, 0xbf, 0x90, 0x4b, 0xd6, 0x40, 0x2f, 0xd7, 0x52, 0x52, 0xb2, 0x21, 0xde, 0x64, 0xbd, 0x88, 0xc3, 0x6d, 0xa5, 0xfa, 0x81, 0x3f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xfb, 0xfd, 0x47, 0x7b, 0x8a, 0x66, 0x9e, 0x79, 0x2e, 0x64, 0x82, 0xef, 0xf7, 0x21, 0xec, 0xf6, 0xd8, 0x86, 0x09, 0x31, 0x7c, 0xdd, 0x03, 0x6a, 0x58, 0xa0, 0x77, 0xb7, 0x9b, 0x8c, 0x87, 0x1f}} , + {{0x55, 0x47, 0xe4, 0xa8, 0x3d, 0x55, 0x21, 0x34, 0xab, 0x1d, 0xae, 0xe0, 0xf4, 0xea, 0xdb, 0xc5, 0xb9, 0x58, 0xbf, 0xc4, 0x2a, 0x89, 0x31, 0x1a, 0xf4, 0x2d, 0xe1, 0xca, 0x37, 0x99, 0x47, 0x59}}}, +{{{0xc7, 0xca, 0x63, 0xc1, 0x49, 0xa9, 0x35, 0x45, 0x55, 0x7e, 0xda, 0x64, 0x32, 0x07, 0x50, 0xf7, 0x32, 0xac, 0xde, 0x75, 0x58, 0x9b, 0x11, 0xb2, 0x3a, 0x1f, 0xf5, 0xf7, 0x79, 0x04, 0xe6, 0x08}} , + {{0x46, 0xfa, 0x22, 0x4b, 0xfa, 0xe1, 0xfe, 0x96, 0xfc, 0x67, 0xba, 0x67, 0x97, 0xc4, 0xe7, 0x1b, 0x86, 0x90, 0x5f, 0xee, 0xf4, 0x5b, 0x11, 0xb2, 0xcd, 0xad, 0xee, 0xc2, 0x48, 0x6c, 0x2b, 0x1b}}}, +{{{0xe3, 0x39, 0x62, 0xb4, 0x4f, 0x31, 0x04, 0xc9, 0xda, 0xd5, 0x73, 0x51, 0x57, 0xc5, 0xb8, 0xf3, 0xa3, 0x43, 0x70, 0xe4, 0x61, 0x81, 0x84, 0xe2, 0xbb, 0xbf, 0x4f, 0x9e, 0xa4, 0x5e, 0x74, 0x06}} , + {{0x29, 0xac, 0xff, 0x27, 0xe0, 0x59, 0xbe, 0x39, 0x9c, 0x0d, 0x83, 0xd7, 0x10, 0x0b, 0x15, 0xb7, 0xe1, 0xc2, 0x2c, 0x30, 0x73, 0x80, 0x3a, 0x7d, 0x5d, 0xab, 0x58, 0x6b, 0xc1, 0xf0, 0xf4, 0x22}}}, +{{{0xfe, 0x7f, 0xfb, 0x35, 0x7d, 0xc6, 0x01, 0x23, 0x28, 0xc4, 0x02, 0xac, 0x1f, 0x42, 0xb4, 0x9d, 0xfc, 0x00, 0x94, 0xa5, 0xee, 0xca, 0xda, 0x97, 0x09, 0x41, 0x77, 0x87, 0x5d, 0x7b, 0x87, 0x78}} , + {{0xf5, 0xfb, 0x90, 0x2d, 0x81, 0x19, 0x9e, 0x2f, 0x6d, 0x85, 0x88, 0x8c, 0x40, 0x5c, 0x77, 0x41, 0x4d, 0x01, 0x19, 0x76, 0x60, 0xe8, 0x4c, 0x48, 0xe4, 0x33, 0x83, 0x32, 0x6c, 0xb4, 0x41, 0x03}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xff, 0x10, 0xc2, 0x09, 0x4f, 0x6e, 0xf4, 0xd2, 0xdf, 0x7e, 0xca, 0x7b, 0x1c, 0x1d, 0xba, 0xa3, 0xb6, 0xda, 0x67, 0x33, 0xd4, 0x87, 0x36, 0x4b, 0x11, 0x20, 0x05, 0xa6, 0x29, 0xc1, 0x87, 0x17}} , + {{0xf6, 0x96, 0xca, 0x2f, 0xda, 0x38, 0xa7, 0x1b, 0xfc, 0xca, 0x7d, 0xfe, 0x08, 0x89, 0xe2, 0x47, 0x2b, 0x6a, 0x5d, 0x4b, 0xfa, 0xa1, 0xb4, 0xde, 0xb6, 0xc2, 0x31, 0x51, 0xf5, 0xe0, 0xa4, 0x0b}}}, +{{{0x5c, 0xe5, 0xc6, 0x04, 0x8e, 0x2b, 0x57, 0xbe, 0x38, 0x85, 0x23, 0xcb, 0xb7, 0xbe, 0x4f, 0xa9, 0xd3, 0x6e, 0x12, 0xaa, 0xd5, 0xb2, 0x2e, 0x93, 0x29, 0x9a, 0x4a, 0x88, 0x18, 0x43, 0xf5, 0x01}} , + {{0x50, 0xfc, 0xdb, 0xa2, 0x59, 0x21, 0x8d, 0xbd, 0x7e, 0x33, 0xae, 0x2f, 0x87, 0x1a, 0xd0, 0x97, 0xc7, 0x0d, 0x4d, 0x63, 0x01, 0xef, 0x05, 0x84, 0xec, 0x40, 0xdd, 0xa8, 0x0a, 0x4f, 0x70, 0x0b}}}, +{{{0x41, 0x69, 0x01, 0x67, 0x5c, 0xd3, 0x8a, 0xc5, 0xcf, 0x3f, 0xd1, 0x57, 0xd1, 0x67, 0x3e, 0x01, 0x39, 0xb5, 0xcb, 0x81, 0x56, 0x96, 0x26, 0xb6, 0xc2, 0xe7, 0x5c, 0xfb, 0x63, 0x97, 0x58, 0x06}} , + {{0x0c, 0x0e, 0xf3, 0xba, 0xf0, 0xe5, 0xba, 0xb2, 0x57, 0x77, 0xc6, 0x20, 0x9b, 0x89, 0x24, 0xbe, 0xf2, 0x9c, 0x8a, 0xba, 0x69, 0xc1, 0xf1, 0xb0, 0x4f, 0x2a, 0x05, 0x9a, 0xee, 0x10, 0x7e, 0x36}}}, +{{{0x3f, 0x26, 0xe9, 0x40, 0xe9, 0x03, 0xad, 0x06, 0x69, 0x91, 0xe0, 0xd1, 0x89, 0x60, 0x84, 0x79, 0xde, 0x27, 0x6d, 0xe6, 0x76, 0xbd, 0xea, 0xe6, 0xae, 0x48, 0xc3, 0x67, 0xc0, 0x57, 0xcd, 0x2f}} , + {{0x7f, 0xc1, 0xdc, 0xb9, 0xc7, 0xbc, 0x86, 0x3d, 0x55, 0x4b, 0x28, 0x7a, 0xfb, 0x4d, 0xc7, 0xf8, 0xbc, 0x67, 0x2a, 0x60, 0x4d, 0x8f, 0x07, 0x0b, 0x1a, 0x17, 0xbf, 0xfa, 0xac, 0xa7, 0x3d, 0x1a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x91, 0x3f, 0xed, 0x5e, 0x18, 0x78, 0x3f, 0x23, 0x2c, 0x0d, 0x8c, 0x44, 0x00, 0xe8, 0xfb, 0xe9, 0x8e, 0xd6, 0xd1, 0x36, 0x58, 0x57, 0x9e, 0xae, 0x4b, 0x5c, 0x0b, 0x07, 0xbc, 0x6b, 0x55, 0x2b}} , + {{0x6f, 0x4d, 0x17, 0xd7, 0xe1, 0x84, 0xd9, 0x78, 0xb1, 0x90, 0xfd, 0x2e, 0xb3, 0xb5, 0x19, 0x3f, 0x1b, 0xfa, 0xc0, 0x68, 0xb3, 0xdd, 0x00, 0x2e, 0x89, 0xbd, 0x7e, 0x80, 0x32, 0x13, 0xa0, 0x7b}}}, +{{{0x1a, 0x6f, 0x40, 0xaf, 0x44, 0x44, 0xb0, 0x43, 0x8f, 0x0d, 0xd0, 0x1e, 0xc4, 0x0b, 0x19, 0x5d, 0x8e, 0xfe, 0xc1, 0xf3, 0xc5, 0x5c, 0x91, 0xf8, 0x04, 0x4e, 0xbe, 0x90, 0xb4, 0x47, 0x5c, 0x3f}} , + {{0xb0, 0x3b, 0x2c, 0xf3, 0xfe, 0x32, 0x71, 0x07, 0x3f, 0xaa, 0xba, 0x45, 0x60, 0xa8, 0x8d, 0xea, 0x54, 0xcb, 0x39, 0x10, 0xb4, 0xf2, 0x8b, 0xd2, 0x14, 0x82, 0x42, 0x07, 0x8e, 0xe9, 0x7c, 0x53}}}, +{{{0xb0, 0xae, 0xc1, 0x8d, 0xc9, 0x8f, 0xb9, 0x7a, 0x77, 0xef, 0xba, 0x79, 0xa0, 0x3c, 0xa8, 0xf5, 0x6a, 0xe2, 0x3f, 0x5d, 0x00, 0xe3, 0x4b, 0x45, 0x24, 0x7b, 0x43, 0x78, 0x55, 0x1d, 0x2b, 0x1e}} , + {{0x01, 0xb8, 0xd6, 0x16, 0x67, 0xa0, 0x15, 0xb9, 0xe1, 0x58, 0xa4, 0xa7, 0x31, 0x37, 0x77, 0x2f, 0x8b, 0x12, 0x9f, 0xf4, 0x3f, 0xc7, 0x36, 0x66, 0xd2, 0xa8, 0x56, 0xf7, 0x7f, 0x74, 0xc6, 0x41}}}, +{{{0x5d, 0xf8, 0xb4, 0xa8, 0x30, 0xdd, 0xcc, 0x38, 0xa5, 0xd3, 0xca, 0xd8, 0xd1, 0xf8, 0xb2, 0x31, 0x91, 0xd4, 0x72, 0x05, 0x57, 0x4a, 0x3b, 0x82, 0x4a, 0xc6, 0x68, 0x20, 0xe2, 0x18, 0x41, 0x61}} , + {{0x19, 0xd4, 0x8d, 0x47, 0x29, 0x12, 0x65, 0xb0, 0x11, 0x78, 0x47, 0xb5, 0xcb, 0xa3, 0xa5, 0xfa, 0x05, 0x85, 0x54, 0xa9, 0x33, 0x97, 0x8d, 0x2b, 0xc2, 0xfe, 0x99, 0x35, 0x28, 0xe5, 0xeb, 0x63}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xb1, 0x3f, 0x3f, 0xef, 0xd8, 0xf4, 0xfc, 0xb3, 0xa0, 0x60, 0x50, 0x06, 0x2b, 0x29, 0x52, 0x70, 0x15, 0x0b, 0x24, 0x24, 0xf8, 0x5f, 0x79, 0x18, 0xcc, 0xff, 0x89, 0x99, 0x84, 0xa1, 0xae, 0x13}} , + {{0x44, 0x1f, 0xb8, 0xc2, 0x01, 0xc1, 0x30, 0x19, 0x55, 0x05, 0x60, 0x10, 0xa4, 0x6c, 0x2d, 0x67, 0x70, 0xe5, 0x25, 0x1b, 0xf2, 0xbf, 0xdd, 0xfb, 0x70, 0x2b, 0xa1, 0x8c, 0x9c, 0x94, 0x84, 0x08}}}, +{{{0xe7, 0xc4, 0x43, 0x4d, 0xc9, 0x2b, 0x69, 0x5d, 0x1d, 0x3c, 0xaf, 0xbb, 0x43, 0x38, 0x4e, 0x98, 0x3d, 0xed, 0x0d, 0x21, 0x03, 0xfd, 0xf0, 0x99, 0x47, 0x04, 0xb0, 0x98, 0x69, 0x55, 0x72, 0x0f}} , + {{0x5e, 0xdf, 0x15, 0x53, 0x3b, 0x86, 0x80, 0xb0, 0xf1, 0x70, 0x68, 0x8f, 0x66, 0x7c, 0x0e, 0x49, 0x1a, 0xd8, 0x6b, 0xfe, 0x4e, 0xef, 0xca, 0x47, 0xd4, 0x03, 0xc1, 0x37, 0x50, 0x9c, 0xc1, 0x16}}}, +{{{0xcd, 0x24, 0xc6, 0x3e, 0x0c, 0x82, 0x9b, 0x91, 0x2b, 0x61, 0x4a, 0xb2, 0x0f, 0x88, 0x55, 0x5f, 0x5a, 0x57, 0xff, 0xe5, 0x74, 0x0b, 0x13, 0x43, 0x00, 0xd8, 0x6b, 0xcf, 0xd2, 0x15, 0x03, 0x2c}} , + {{0xdc, 0xff, 0x15, 0x61, 0x2f, 0x4a, 0x2f, 0x62, 0xf2, 0x04, 0x2f, 0xb5, 0x0c, 0xb7, 0x1e, 0x3f, 0x74, 0x1a, 0x0f, 0xd7, 0xea, 0xcd, 0xd9, 0x7d, 0xf6, 0x12, 0x0e, 0x2f, 0xdb, 0x5a, 0x3b, 0x16}}}, +{{{0x1b, 0x37, 0x47, 0xe3, 0xf5, 0x9e, 0xea, 0x2c, 0x2a, 0xe7, 0x82, 0x36, 0xf4, 0x1f, 0x81, 0x47, 0x92, 0x4b, 0x69, 0x0e, 0x11, 0x8c, 0x5d, 0x53, 0x5b, 0x81, 0x27, 0x08, 0xbc, 0xa0, 0xae, 0x25}} , + {{0x69, 0x32, 0xa1, 0x05, 0x11, 0x42, 0x00, 0xd2, 0x59, 0xac, 0x4d, 0x62, 0x8b, 0x13, 0xe2, 0x50, 0x5d, 0xa0, 0x9d, 0x9b, 0xfd, 0xbb, 0x12, 0x41, 0x75, 0x41, 0x9e, 0xcc, 0xdc, 0xc7, 0xdc, 0x5d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd9, 0xe3, 0x38, 0x06, 0x46, 0x70, 0x82, 0x5e, 0x28, 0x49, 0x79, 0xff, 0x25, 0xd2, 0x4e, 0x29, 0x8d, 0x06, 0xb0, 0x23, 0xae, 0x9b, 0x66, 0xe4, 0x7d, 0xc0, 0x70, 0x91, 0xa3, 0xfc, 0xec, 0x4e}} , + {{0x62, 0x12, 0x37, 0x6a, 0x30, 0xf6, 0x1e, 0xfb, 0x14, 0x5c, 0x0d, 0x0e, 0xb7, 0x81, 0x6a, 0xe7, 0x08, 0x05, 0xac, 0xaa, 0x38, 0x46, 0xe2, 0x73, 0xea, 0x4b, 0x07, 0x81, 0x43, 0x7c, 0x9e, 0x5e}}}, +{{{0xfc, 0xf9, 0x21, 0x4f, 0x2e, 0x76, 0x9b, 0x1f, 0x28, 0x60, 0x77, 0x43, 0x32, 0x9d, 0xbe, 0x17, 0x30, 0x2a, 0xc6, 0x18, 0x92, 0x66, 0x62, 0x30, 0x98, 0x40, 0x11, 0xa6, 0x7f, 0x18, 0x84, 0x28}} , + {{0x3f, 0xab, 0xd3, 0xf4, 0x8a, 0x76, 0xa1, 0x3c, 0xca, 0x2d, 0x49, 0xc3, 0xea, 0x08, 0x0b, 0x85, 0x17, 0x2a, 0xc3, 0x6c, 0x08, 0xfd, 0x57, 0x9f, 0x3d, 0x5f, 0xdf, 0x67, 0x68, 0x42, 0x00, 0x32}}}, +{{{0x51, 0x60, 0x1b, 0x06, 0x4f, 0x8a, 0x21, 0xba, 0x38, 0xa8, 0xba, 0xd6, 0x40, 0xf6, 0xe9, 0x9b, 0x76, 0x4d, 0x56, 0x21, 0x5b, 0x0a, 0x9b, 0x2e, 0x4f, 0x3d, 0x81, 0x32, 0x08, 0x9f, 0x97, 0x5b}} , + {{0xe5, 0x44, 0xec, 0x06, 0x9d, 0x90, 0x79, 0x9f, 0xd3, 0xe0, 0x79, 0xaf, 0x8f, 0x10, 0xfd, 0xdd, 0x04, 0xae, 0x27, 0x97, 0x46, 0x33, 0x79, 0xea, 0xb8, 0x4e, 0xca, 0x5a, 0x59, 0x57, 0xe1, 0x0e}}}, +{{{0x1a, 0xda, 0xf3, 0xa5, 0x41, 0x43, 0x28, 0xfc, 0x7e, 0xe7, 0x71, 0xea, 0xc6, 0x3b, 0x59, 0xcc, 0x2e, 0xd3, 0x40, 0xec, 0xb3, 0x13, 0x6f, 0x44, 0xcd, 0x13, 0xb2, 0x37, 0xf2, 0x6e, 0xd9, 0x1c}} , + {{0xe3, 0xdb, 0x60, 0xcd, 0x5c, 0x4a, 0x18, 0x0f, 0xef, 0x73, 0x36, 0x71, 0x8c, 0xf6, 0x11, 0xb4, 0xd8, 0xce, 0x17, 0x5e, 0x4f, 0x26, 0x77, 0x97, 0x5f, 0xcb, 0xef, 0x91, 0xeb, 0x6a, 0x62, 0x7a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x18, 0x4a, 0xa2, 0x97, 0x08, 0x81, 0x2d, 0x83, 0xc4, 0xcc, 0xf0, 0x83, 0x7e, 0xec, 0x0d, 0x95, 0x4c, 0x5b, 0xfb, 0xfa, 0x98, 0x80, 0x4a, 0x66, 0x56, 0x0c, 0x51, 0xb3, 0xf2, 0x04, 0x5d, 0x27}} , + {{0x3b, 0xb9, 0xb8, 0x06, 0x5a, 0x2e, 0xfe, 0xc3, 0x82, 0x37, 0x9c, 0xa3, 0x11, 0x1f, 0x9c, 0xa6, 0xda, 0x63, 0x48, 0x9b, 0xad, 0xde, 0x2d, 0xa6, 0xbc, 0x6e, 0x32, 0xda, 0x27, 0x65, 0xdd, 0x57}}}, +{{{0x84, 0x4f, 0x37, 0x31, 0x7d, 0x2e, 0xbc, 0xad, 0x87, 0x07, 0x2a, 0x6b, 0x37, 0xfc, 0x5f, 0xeb, 0x4e, 0x75, 0x35, 0xa6, 0xde, 0xab, 0x0a, 0x19, 0x3a, 0xb7, 0xb1, 0xef, 0x92, 0x6a, 0x3b, 0x3c}} , + {{0x3b, 0xb2, 0x94, 0x6d, 0x39, 0x60, 0xac, 0xee, 0xe7, 0x81, 0x1a, 0x3b, 0x76, 0x87, 0x5c, 0x05, 0x94, 0x2a, 0x45, 0xb9, 0x80, 0xe9, 0x22, 0xb1, 0x07, 0xcb, 0x40, 0x9e, 0x70, 0x49, 0x6d, 0x12}}}, +{{{0xfd, 0x18, 0x78, 0x84, 0xa8, 0x4c, 0x7d, 0x6e, 0x59, 0xa6, 0xe5, 0x74, 0xf1, 0x19, 0xa6, 0x84, 0x2e, 0x51, 0xc1, 0x29, 0x13, 0xf2, 0x14, 0x6b, 0x5d, 0x53, 0x51, 0xf7, 0xef, 0xbf, 0x01, 0x22}} , + {{0xa4, 0x4b, 0x62, 0x4c, 0xe6, 0xfd, 0x72, 0x07, 0xf2, 0x81, 0xfc, 0xf2, 0xbd, 0x12, 0x7c, 0x68, 0x76, 0x2a, 0xba, 0xf5, 0x65, 0xb1, 0x1f, 0x17, 0x0a, 0x38, 0xb0, 0xbf, 0xc0, 0xf8, 0xf4, 0x2a}}}, +{{{0x55, 0x60, 0x55, 0x5b, 0xe4, 0x1d, 0x71, 0x4c, 0x9d, 0x5b, 0x9f, 0x70, 0xa6, 0x85, 0x9a, 0x2c, 0xa0, 0xe2, 0x32, 0x48, 0xce, 0x9e, 0x2a, 0xa5, 0x07, 0x3b, 0xc7, 0x6c, 0x86, 0x77, 0xde, 0x3c}} , + {{0xf7, 0x18, 0x7a, 0x96, 0x7e, 0x43, 0x57, 0xa9, 0x55, 0xfc, 0x4e, 0xb6, 0x72, 0x00, 0xf2, 0xe4, 0xd7, 0x52, 0xd3, 0xd3, 0xb6, 0x85, 0xf6, 0x71, 0xc7, 0x44, 0x3f, 0x7f, 0xd7, 0xb3, 0xf2, 0x79}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x46, 0xca, 0xa7, 0x55, 0x7b, 0x79, 0xf3, 0xca, 0x5a, 0x65, 0xf6, 0xed, 0x50, 0x14, 0x7b, 0xe4, 0xc4, 0x2a, 0x65, 0x9e, 0xe2, 0xf9, 0xca, 0xa7, 0x22, 0x26, 0x53, 0xcb, 0x21, 0x5b, 0xa7, 0x31}} , + {{0x90, 0xd7, 0xc5, 0x26, 0x08, 0xbd, 0xb0, 0x53, 0x63, 0x58, 0xc3, 0x31, 0x5e, 0x75, 0x46, 0x15, 0x91, 0xa6, 0xf8, 0x2f, 0x1a, 0x08, 0x65, 0x88, 0x2f, 0x98, 0x04, 0xf1, 0x7c, 0x6e, 0x00, 0x77}}}, +{{{0x81, 0x21, 0x61, 0x09, 0xf6, 0x4e, 0xf1, 0x92, 0xee, 0x63, 0x61, 0x73, 0x87, 0xc7, 0x54, 0x0e, 0x42, 0x4b, 0xc9, 0x47, 0xd1, 0xb8, 0x7e, 0x91, 0x75, 0x37, 0x99, 0x28, 0xb8, 0xdd, 0x7f, 0x50}} , + {{0x89, 0x8f, 0xc0, 0xbe, 0x5d, 0xd6, 0x9f, 0xa0, 0xf0, 0x9d, 0x81, 0xce, 0x3a, 0x7b, 0x98, 0x58, 0xbb, 0xd7, 0x78, 0xc8, 0x3f, 0x13, 0xf1, 0x74, 0x19, 0xdf, 0xf8, 0x98, 0x89, 0x5d, 0xfa, 0x5f}}}, +{{{0x9e, 0x35, 0x85, 0x94, 0x47, 0x1f, 0x90, 0x15, 0x26, 0xd0, 0x84, 0xed, 0x8a, 0x80, 0xf7, 0x63, 0x42, 0x86, 0x27, 0xd7, 0xf4, 0x75, 0x58, 0xdc, 0x9c, 0xc0, 0x22, 0x7e, 0x20, 0x35, 0xfd, 0x1f}} , + {{0x68, 0x0e, 0x6f, 0x97, 0xba, 0x70, 0xbb, 0xa3, 0x0e, 0xe5, 0x0b, 0x12, 0xf4, 0xa2, 0xdc, 0x47, 0xf8, 0xe6, 0xd0, 0x23, 0x6c, 0x33, 0xa8, 0x99, 0x46, 0x6e, 0x0f, 0x44, 0xba, 0x76, 0x48, 0x0f}}}, +{{{0xa3, 0x2a, 0x61, 0x37, 0xe2, 0x59, 0x12, 0x0e, 0x27, 0xba, 0x64, 0x43, 0xae, 0xc0, 0x42, 0x69, 0x79, 0xa4, 0x1e, 0x29, 0x8b, 0x15, 0xeb, 0xf8, 0xaf, 0xd4, 0xa2, 0x68, 0x33, 0xb5, 0x7a, 0x24}} , + {{0x2c, 0x19, 0x33, 0xdd, 0x1b, 0xab, 0xec, 0x01, 0xb0, 0x23, 0xf8, 0x42, 0x2b, 0x06, 0x88, 0xea, 0x3d, 0x2d, 0x00, 0x2a, 0x78, 0x45, 0x4d, 0x38, 0xed, 0x2e, 0x2e, 0x44, 0x49, 0xed, 0xcb, 0x33}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xa0, 0x68, 0xe8, 0x41, 0x8f, 0x91, 0xf8, 0x11, 0x13, 0x90, 0x2e, 0xa7, 0xab, 0x30, 0xef, 0xad, 0xa0, 0x61, 0x00, 0x88, 0xef, 0xdb, 0xce, 0x5b, 0x5c, 0xbb, 0x62, 0xc8, 0x56, 0xf9, 0x00, 0x73}} , + {{0x3f, 0x60, 0xc1, 0x82, 0x2d, 0xa3, 0x28, 0x58, 0x24, 0x9e, 0x9f, 0xe3, 0x70, 0xcc, 0x09, 0x4e, 0x1a, 0x3f, 0x11, 0x11, 0x15, 0x07, 0x3c, 0xa4, 0x41, 0xe0, 0x65, 0xa3, 0x0a, 0x41, 0x6d, 0x11}}}, +{{{0x31, 0x40, 0x01, 0x52, 0x56, 0x94, 0x5b, 0x28, 0x8a, 0xaa, 0x52, 0xee, 0xd8, 0x0a, 0x05, 0x8d, 0xcd, 0xb5, 0xaa, 0x2e, 0x38, 0xaa, 0xb7, 0x87, 0xf7, 0x2b, 0xfb, 0x04, 0xcb, 0x84, 0x3d, 0x54}} , + {{0x20, 0xef, 0x59, 0xde, 0xa4, 0x2b, 0x93, 0x6e, 0x2e, 0xec, 0x42, 0x9a, 0xd4, 0x2d, 0xf4, 0x46, 0x58, 0x27, 0x2b, 0x18, 0x8f, 0x83, 0x3d, 0x69, 0x9e, 0xd4, 0x3e, 0xb6, 0xc5, 0xfd, 0x58, 0x03}}}, +{{{0x33, 0x89, 0xc9, 0x63, 0x62, 0x1c, 0x17, 0xb4, 0x60, 0xc4, 0x26, 0x68, 0x09, 0xc3, 0x2e, 0x37, 0x0f, 0x7b, 0xb4, 0x9c, 0xb6, 0xf9, 0xfb, 0xd4, 0x51, 0x78, 0xc8, 0x63, 0xea, 0x77, 0x47, 0x07}} , + {{0x32, 0xb4, 0x18, 0x47, 0x79, 0xcb, 0xd4, 0x5a, 0x07, 0x14, 0x0f, 0xa0, 0xd5, 0xac, 0xd0, 0x41, 0x40, 0xab, 0x61, 0x23, 0xe5, 0x2a, 0x2a, 0x6f, 0xf7, 0xa8, 0xd4, 0x76, 0xef, 0xe7, 0x45, 0x6c}}}, +{{{0xa1, 0x5e, 0x60, 0x4f, 0xfb, 0xe1, 0x70, 0x6a, 0x1f, 0x55, 0x4f, 0x09, 0xb4, 0x95, 0x33, 0x36, 0xc6, 0x81, 0x01, 0x18, 0x06, 0x25, 0x27, 0xa4, 0xb4, 0x24, 0xa4, 0x86, 0x03, 0x4c, 0xac, 0x02}} , + {{0x77, 0x38, 0xde, 0xd7, 0x60, 0x48, 0x07, 0xf0, 0x74, 0xa8, 0xff, 0x54, 0xe5, 0x30, 0x43, 0xff, 0x77, 0xfb, 0x21, 0x07, 0xff, 0xb2, 0x07, 0x6b, 0xe4, 0xe5, 0x30, 0xfc, 0x19, 0x6c, 0xa3, 0x01}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x13, 0xc5, 0x2c, 0xac, 0xd3, 0x83, 0x82, 0x7c, 0x29, 0xf7, 0x05, 0xa5, 0x00, 0xb6, 0x1f, 0x86, 0x55, 0xf4, 0xd6, 0x2f, 0x0c, 0x99, 0xd0, 0x65, 0x9b, 0x6b, 0x46, 0x0d, 0x43, 0xf8, 0x16, 0x28}} , + {{0x1e, 0x7f, 0xb4, 0x74, 0x7e, 0xb1, 0x89, 0x4f, 0x18, 0x5a, 0xab, 0x64, 0x06, 0xdf, 0x45, 0x87, 0xe0, 0x6a, 0xc6, 0xf0, 0x0e, 0xc9, 0x24, 0x35, 0x38, 0xea, 0x30, 0x54, 0xb4, 0xc4, 0x52, 0x54}}}, +{{{0xe9, 0x9f, 0xdc, 0x3f, 0xc1, 0x89, 0x44, 0x74, 0x27, 0xe4, 0xc1, 0x90, 0xff, 0x4a, 0xa7, 0x3c, 0xee, 0xcd, 0xf4, 0x1d, 0x25, 0x94, 0x7f, 0x63, 0x16, 0x48, 0xbc, 0x64, 0xfe, 0x95, 0xc4, 0x0c}} , + {{0x8b, 0x19, 0x75, 0x6e, 0x03, 0x06, 0x5e, 0x6a, 0x6f, 0x1a, 0x8c, 0xe3, 0xd3, 0x28, 0xf2, 0xe0, 0xb9, 0x7a, 0x43, 0x69, 0xe6, 0xd3, 0xc0, 0xfe, 0x7e, 0x97, 0xab, 0x6c, 0x7b, 0x8e, 0x13, 0x42}}}, +{{{0xd4, 0xca, 0x70, 0x3d, 0xab, 0xfb, 0x5f, 0x5e, 0x00, 0x0c, 0xcc, 0x77, 0x22, 0xf8, 0x78, 0x55, 0xae, 0x62, 0x35, 0xfb, 0x9a, 0xc6, 0x03, 0xe4, 0x0c, 0xee, 0xab, 0xc7, 0xc0, 0x89, 0x87, 0x54}} , + {{0x32, 0xad, 0xae, 0x85, 0x58, 0x43, 0xb8, 0xb1, 0xe6, 0x3e, 0x00, 0x9c, 0x78, 0x88, 0x56, 0xdb, 0x9c, 0xfc, 0x79, 0xf6, 0xf9, 0x41, 0x5f, 0xb7, 0xbc, 0x11, 0xf9, 0x20, 0x36, 0x1c, 0x53, 0x2b}}}, +{{{0x5a, 0x20, 0x5b, 0xa1, 0xa5, 0x44, 0x91, 0x24, 0x02, 0x63, 0x12, 0x64, 0xb8, 0x55, 0xf6, 0xde, 0x2c, 0xdb, 0x47, 0xb8, 0xc6, 0x0a, 0xc3, 0x00, 0x78, 0x93, 0xd8, 0xf5, 0xf5, 0x18, 0x28, 0x0a}} , + {{0xd6, 0x1b, 0x9a, 0x6c, 0xe5, 0x46, 0xea, 0x70, 0x96, 0x8d, 0x4e, 0x2a, 0x52, 0x21, 0x26, 0x4b, 0xb1, 0xbb, 0x0f, 0x7c, 0xa9, 0x9b, 0x04, 0xbb, 0x51, 0x08, 0xf1, 0x9a, 0xa4, 0x76, 0x7c, 0x18}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xfa, 0x94, 0xf7, 0x40, 0xd0, 0xd7, 0xeb, 0xa9, 0x82, 0x36, 0xd5, 0x15, 0xb9, 0x33, 0x7a, 0xbf, 0x8a, 0xf2, 0x63, 0xaa, 0x37, 0xf5, 0x59, 0xac, 0xbd, 0xbb, 0x32, 0x36, 0xbe, 0x73, 0x99, 0x38}} , + {{0x2c, 0xb3, 0xda, 0x7a, 0xd8, 0x3d, 0x99, 0xca, 0xd2, 0xf4, 0xda, 0x99, 0x8e, 0x4f, 0x98, 0xb7, 0xf4, 0xae, 0x3e, 0x9f, 0x8e, 0x35, 0x60, 0xa4, 0x33, 0x75, 0xa4, 0x04, 0x93, 0xb1, 0x6b, 0x4d}}}, +{{{0x97, 0x9d, 0xa8, 0xcd, 0x97, 0x7b, 0x9d, 0xb9, 0xe7, 0xa5, 0xef, 0xfd, 0xa8, 0x42, 0x6b, 0xc3, 0x62, 0x64, 0x7d, 0xa5, 0x1b, 0xc9, 0x9e, 0xd2, 0x45, 0xb9, 0xee, 0x03, 0xb0, 0xbf, 0xc0, 0x68}} , + {{0xed, 0xb7, 0x84, 0x2c, 0xf6, 0xd3, 0xa1, 0x6b, 0x24, 0x6d, 0x87, 0x56, 0x97, 0x59, 0x79, 0x62, 0x9f, 0xac, 0xed, 0xf3, 0xc9, 0x89, 0x21, 0x2e, 0x04, 0xb3, 0xcc, 0x2f, 0xbe, 0xd6, 0x0a, 0x4b}}}, +{{{0x39, 0x61, 0x05, 0xed, 0x25, 0x89, 0x8b, 0x5d, 0x1b, 0xcb, 0x0c, 0x55, 0xf4, 0x6a, 0x00, 0x8a, 0x46, 0xe8, 0x1e, 0xc6, 0x83, 0xc8, 0x5a, 0x76, 0xdb, 0xcc, 0x19, 0x7a, 0xcc, 0x67, 0x46, 0x0b}} , + {{0x53, 0xcf, 0xc2, 0xa1, 0xad, 0x6a, 0xf3, 0xcd, 0x8f, 0xc9, 0xde, 0x1c, 0xf8, 0x6c, 0x8f, 0xf8, 0x76, 0x42, 0xe7, 0xfe, 0xb2, 0x72, 0x21, 0x0a, 0x66, 0x74, 0x8f, 0xb7, 0xeb, 0xe4, 0x6f, 0x01}}}, +{{{0x22, 0x8c, 0x6b, 0xbe, 0xfc, 0x4d, 0x70, 0x62, 0x6e, 0x52, 0x77, 0x99, 0x88, 0x7e, 0x7b, 0x57, 0x7a, 0x0d, 0xfe, 0xdc, 0x72, 0x92, 0xf1, 0x68, 0x1d, 0x97, 0xd7, 0x7c, 0x8d, 0x53, 0x10, 0x37}} , + {{0x53, 0x88, 0x77, 0x02, 0xca, 0x27, 0xa8, 0xe5, 0x45, 0xe2, 0xa8, 0x48, 0x2a, 0xab, 0x18, 0xca, 0xea, 0x2d, 0x2a, 0x54, 0x17, 0x37, 0x32, 0x09, 0xdc, 0xe0, 0x4a, 0xb7, 0x7d, 0x82, 0x10, 0x7d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x8a, 0x64, 0x1e, 0x14, 0x0a, 0x57, 0xd4, 0xda, 0x5c, 0x96, 0x9b, 0x01, 0x4c, 0x67, 0xbf, 0x8b, 0x30, 0xfe, 0x08, 0xdb, 0x0d, 0xd5, 0xa8, 0xd7, 0x09, 0x11, 0x85, 0xa2, 0xd3, 0x45, 0xfb, 0x7e}} , + {{0xda, 0x8c, 0xc2, 0xd0, 0xac, 0x18, 0xe8, 0x52, 0x36, 0xd4, 0x21, 0xa3, 0xdd, 0x57, 0x22, 0x79, 0xb7, 0xf8, 0x71, 0x9d, 0xc6, 0x91, 0x70, 0x86, 0x56, 0xbf, 0xa1, 0x11, 0x8b, 0x19, 0xe1, 0x0f}}}, +{{{0x18, 0x32, 0x98, 0x2c, 0x8f, 0x91, 0xae, 0x12, 0xf0, 0x8c, 0xea, 0xf3, 0x3c, 0xb9, 0x5d, 0xe4, 0x69, 0xed, 0xb2, 0x47, 0x18, 0xbd, 0xce, 0x16, 0x52, 0x5c, 0x23, 0xe2, 0xa5, 0x25, 0x52, 0x5d}} , + {{0xb9, 0xb1, 0xe7, 0x5d, 0x4e, 0xbc, 0xee, 0xbb, 0x40, 0x81, 0x77, 0x82, 0x19, 0xab, 0xb5, 0xc6, 0xee, 0xab, 0x5b, 0x6b, 0x63, 0x92, 0x8a, 0x34, 0x8d, 0xcd, 0xee, 0x4f, 0x49, 0xe5, 0xc9, 0x7e}}}, +{{{0x21, 0xac, 0x8b, 0x22, 0xcd, 0xc3, 0x9a, 0xe9, 0x5e, 0x78, 0xbd, 0xde, 0xba, 0xad, 0xab, 0xbf, 0x75, 0x41, 0x09, 0xc5, 0x58, 0xa4, 0x7d, 0x92, 0xb0, 0x7f, 0xf2, 0xa1, 0xd1, 0xc0, 0xb3, 0x6d}} , + {{0x62, 0x4f, 0xd0, 0x75, 0x77, 0xba, 0x76, 0x77, 0xd7, 0xb8, 0xd8, 0x92, 0x6f, 0x98, 0x34, 0x3d, 0xd6, 0x4e, 0x1c, 0x0f, 0xf0, 0x8f, 0x2e, 0xf1, 0xb3, 0xbd, 0xb1, 0xb9, 0xec, 0x99, 0xb4, 0x07}}}, +{{{0x60, 0x57, 0x2e, 0x9a, 0x72, 0x1d, 0x6b, 0x6e, 0x58, 0x33, 0x24, 0x8c, 0x48, 0x39, 0x46, 0x8e, 0x89, 0x6a, 0x88, 0x51, 0x23, 0x62, 0xb5, 0x32, 0x09, 0x36, 0xe3, 0x57, 0xf5, 0x98, 0xde, 0x6f}} , + {{0x8b, 0x2c, 0x00, 0x48, 0x4a, 0xf9, 0x5b, 0x87, 0x69, 0x52, 0xe5, 0x5b, 0xd1, 0xb1, 0xe5, 0x25, 0x25, 0xe0, 0x9c, 0xc2, 0x13, 0x44, 0xe8, 0xb9, 0x0a, 0x70, 0xad, 0xbd, 0x0f, 0x51, 0x94, 0x69}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xa2, 0xdc, 0xab, 0xa9, 0x25, 0x2d, 0xac, 0x5f, 0x03, 0x33, 0x08, 0xe7, 0x7e, 0xfe, 0x95, 0x36, 0x3c, 0x5b, 0x3a, 0xd3, 0x05, 0x82, 0x1c, 0x95, 0x2d, 0xd8, 0x77, 0x7e, 0x02, 0xd9, 0x5b, 0x70}} , + {{0xc2, 0xfe, 0x1b, 0x0c, 0x67, 0xcd, 0xd6, 0xe0, 0x51, 0x8e, 0x2c, 0xe0, 0x79, 0x88, 0xf0, 0xcf, 0x41, 0x4a, 0xad, 0x23, 0xd4, 0x46, 0xca, 0x94, 0xa1, 0xc3, 0xeb, 0x28, 0x06, 0xfa, 0x17, 0x14}}}, +{{{0x7b, 0xaa, 0x70, 0x0a, 0x4b, 0xfb, 0xf5, 0xbf, 0x80, 0xc5, 0xcf, 0x08, 0x7a, 0xdd, 0xa1, 0xf4, 0x9d, 0x54, 0x50, 0x53, 0x23, 0x77, 0x23, 0xf5, 0x34, 0xa5, 0x22, 0xd1, 0x0d, 0x96, 0x2e, 0x47}} , + {{0xcc, 0xb7, 0x32, 0x89, 0x57, 0xd0, 0x98, 0x75, 0xe4, 0x37, 0x99, 0xa9, 0xe8, 0xba, 0xed, 0xba, 0xeb, 0xc7, 0x4f, 0x15, 0x76, 0x07, 0x0c, 0x4c, 0xef, 0x9f, 0x52, 0xfc, 0x04, 0x5d, 0x58, 0x10}}}, +{{{0xce, 0x82, 0xf0, 0x8f, 0x79, 0x02, 0xa8, 0xd1, 0xda, 0x14, 0x09, 0x48, 0xee, 0x8a, 0x40, 0x98, 0x76, 0x60, 0x54, 0x5a, 0xde, 0x03, 0x24, 0xf5, 0xe6, 0x2f, 0xe1, 0x03, 0xbf, 0x68, 0x82, 0x7f}} , + {{0x64, 0xe9, 0x28, 0xc7, 0xa4, 0xcf, 0x2a, 0xf9, 0x90, 0x64, 0x72, 0x2c, 0x8b, 0xeb, 0xec, 0xa0, 0xf2, 0x7d, 0x35, 0xb5, 0x90, 0x4d, 0x7f, 0x5b, 0x4a, 0x49, 0xe4, 0xb8, 0x3b, 0xc8, 0xa1, 0x2f}}}, +{{{0x8b, 0xc5, 0xcc, 0x3d, 0x69, 0xa6, 0xa1, 0x18, 0x44, 0xbc, 0x4d, 0x77, 0x37, 0xc7, 0x86, 0xec, 0x0c, 0xc9, 0xd6, 0x44, 0xa9, 0x23, 0x27, 0xb9, 0x03, 0x34, 0xa7, 0x0a, 0xd5, 0xc7, 0x34, 0x37}} , + {{0xf9, 0x7e, 0x3e, 0x66, 0xee, 0xf9, 0x99, 0x28, 0xff, 0xad, 0x11, 0xd8, 0xe2, 0x66, 0xc5, 0xcd, 0x0f, 0x0d, 0x0b, 0x6a, 0xfc, 0x7c, 0x24, 0xa8, 0x4f, 0xa8, 0x5e, 0x80, 0x45, 0x8b, 0x6c, 0x41}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xef, 0x1e, 0xec, 0xf7, 0x8d, 0x77, 0xf2, 0xea, 0xdb, 0x60, 0x03, 0x21, 0xc0, 0xff, 0x5e, 0x67, 0xc3, 0x71, 0x0b, 0x21, 0xb4, 0x41, 0xa0, 0x68, 0x38, 0xc6, 0x01, 0xa3, 0xd3, 0x51, 0x3c, 0x3c}} , + {{0x92, 0xf8, 0xd6, 0x4b, 0xef, 0x42, 0x13, 0xb2, 0x4a, 0xc4, 0x2e, 0x72, 0x3f, 0xc9, 0x11, 0xbd, 0x74, 0x02, 0x0e, 0xf5, 0x13, 0x9d, 0x83, 0x1a, 0x1b, 0xd5, 0x54, 0xde, 0xc4, 0x1e, 0x16, 0x6c}}}, +{{{0x27, 0x52, 0xe4, 0x63, 0xaa, 0x94, 0xe6, 0xc3, 0x28, 0x9c, 0xc6, 0x56, 0xac, 0xfa, 0xb6, 0xbd, 0xe2, 0xcc, 0x76, 0xc6, 0x27, 0x27, 0xa2, 0x8e, 0x78, 0x2b, 0x84, 0x72, 0x10, 0xbd, 0x4e, 0x2a}} , + {{0xea, 0xa7, 0x23, 0xef, 0x04, 0x61, 0x80, 0x50, 0xc9, 0x6e, 0xa5, 0x96, 0xd1, 0xd1, 0xc8, 0xc3, 0x18, 0xd7, 0x2d, 0xfd, 0x26, 0xbd, 0xcb, 0x7b, 0x92, 0x51, 0x0e, 0x4a, 0x65, 0x57, 0xb8, 0x49}}}, +{{{0xab, 0x55, 0x36, 0xc3, 0xec, 0x63, 0x55, 0x11, 0x55, 0xf6, 0xa5, 0xc7, 0x01, 0x5f, 0xfe, 0x79, 0xd8, 0x0a, 0xf7, 0x03, 0xd8, 0x98, 0x99, 0xf5, 0xd0, 0x00, 0x54, 0x6b, 0x66, 0x28, 0xf5, 0x25}} , + {{0x7a, 0x8d, 0xa1, 0x5d, 0x70, 0x5d, 0x51, 0x27, 0xee, 0x30, 0x65, 0x56, 0x95, 0x46, 0xde, 0xbd, 0x03, 0x75, 0xb4, 0x57, 0x59, 0x89, 0xeb, 0x02, 0x9e, 0xcc, 0x89, 0x19, 0xa7, 0xcb, 0x17, 0x67}}}, +{{{0x6a, 0xeb, 0xfc, 0x9a, 0x9a, 0x10, 0xce, 0xdb, 0x3a, 0x1c, 0x3c, 0x6a, 0x9d, 0xea, 0x46, 0xbc, 0x45, 0x49, 0xac, 0xe3, 0x41, 0x12, 0x7c, 0xf0, 0xf7, 0x4f, 0xf9, 0xf7, 0xff, 0x2c, 0x89, 0x04}} , + {{0x30, 0x31, 0x54, 0x1a, 0x46, 0xca, 0xe6, 0xc6, 0xcb, 0xe2, 0xc3, 0xc1, 0x8b, 0x75, 0x81, 0xbe, 0xee, 0xf8, 0xa3, 0x11, 0x1c, 0x25, 0xa3, 0xa7, 0x35, 0x51, 0x55, 0xe2, 0x25, 0xaa, 0xe2, 0x3a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xb4, 0x48, 0x10, 0x9f, 0x8a, 0x09, 0x76, 0xfa, 0xf0, 0x7a, 0xb0, 0x70, 0xf7, 0x83, 0x80, 0x52, 0x84, 0x2b, 0x26, 0xa2, 0xc4, 0x5d, 0x4f, 0xba, 0xb1, 0xc8, 0x40, 0x0d, 0x78, 0x97, 0xc4, 0x60}} , + {{0xd4, 0xb1, 0x6c, 0x08, 0xc7, 0x40, 0x38, 0x73, 0x5f, 0x0b, 0xf3, 0x76, 0x5d, 0xb2, 0xa5, 0x2f, 0x57, 0x57, 0x07, 0xed, 0x08, 0xa2, 0x6c, 0x4f, 0x08, 0x02, 0xb5, 0x0e, 0xee, 0x44, 0xfa, 0x22}}}, +{{{0x0f, 0x00, 0x3f, 0xa6, 0x04, 0x19, 0x56, 0x65, 0x31, 0x7f, 0x8b, 0xeb, 0x0d, 0xe1, 0x47, 0x89, 0x97, 0x16, 0x53, 0xfa, 0x81, 0xa7, 0xaa, 0xb2, 0xbf, 0x67, 0xeb, 0x72, 0x60, 0x81, 0x0d, 0x48}} , + {{0x7e, 0x13, 0x33, 0xcd, 0xa8, 0x84, 0x56, 0x1e, 0x67, 0xaf, 0x6b, 0x43, 0xac, 0x17, 0xaf, 0x16, 0xc0, 0x52, 0x99, 0x49, 0x5b, 0x87, 0x73, 0x7e, 0xb5, 0x43, 0xda, 0x6b, 0x1d, 0x0f, 0x2d, 0x55}}}, +{{{0xe9, 0x58, 0x1f, 0xff, 0x84, 0x3f, 0x93, 0x1c, 0xcb, 0xe1, 0x30, 0x69, 0xa5, 0x75, 0x19, 0x7e, 0x14, 0x5f, 0xf8, 0xfc, 0x09, 0xdd, 0xa8, 0x78, 0x9d, 0xca, 0x59, 0x8b, 0xd1, 0x30, 0x01, 0x13}} , + {{0xff, 0x76, 0x03, 0xc5, 0x4b, 0x89, 0x99, 0x70, 0x00, 0x59, 0x70, 0x9c, 0xd5, 0xd9, 0x11, 0x89, 0x5a, 0x46, 0xfe, 0xef, 0xdc, 0xd9, 0x55, 0x2b, 0x45, 0xa7, 0xb0, 0x2d, 0xfb, 0x24, 0xc2, 0x29}}}, +{{{0x38, 0x06, 0xf8, 0x0b, 0xac, 0x82, 0xc4, 0x97, 0x2b, 0x90, 0xe0, 0xf7, 0xa8, 0xab, 0x6c, 0x08, 0x80, 0x66, 0x90, 0x46, 0xf7, 0x26, 0x2d, 0xf8, 0xf1, 0xc4, 0x6b, 0x4a, 0x82, 0x98, 0x8e, 0x37}} , + {{0x8e, 0xb4, 0xee, 0xb8, 0xd4, 0x3f, 0xb2, 0x1b, 0xe0, 0x0a, 0x3d, 0x75, 0x34, 0x28, 0xa2, 0x8e, 0xc4, 0x92, 0x7b, 0xfe, 0x60, 0x6e, 0x6d, 0xb8, 0x31, 0x1d, 0x62, 0x0d, 0x78, 0x14, 0x42, 0x11}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x5e, 0xa8, 0xd8, 0x04, 0x9b, 0x73, 0xc9, 0xc9, 0xdc, 0x0d, 0x73, 0xbf, 0x0a, 0x0a, 0x73, 0xff, 0x18, 0x1f, 0x9c, 0x51, 0xaa, 0xc6, 0xf1, 0x83, 0x25, 0xfd, 0xab, 0xa3, 0x11, 0xd3, 0x01, 0x24}} , + {{0x4d, 0xe3, 0x7e, 0x38, 0x62, 0x5e, 0x64, 0xbb, 0x2b, 0x53, 0xb5, 0x03, 0x68, 0xc4, 0xf2, 0x2b, 0x5a, 0x03, 0x32, 0x99, 0x4a, 0x41, 0x9a, 0xe1, 0x1a, 0xae, 0x8c, 0x48, 0xf3, 0x24, 0x32, 0x65}}}, +{{{0xe8, 0xdd, 0xad, 0x3a, 0x8c, 0xea, 0xf4, 0xb3, 0xb2, 0xe5, 0x73, 0xf2, 0xed, 0x8b, 0xbf, 0xed, 0xb1, 0x0c, 0x0c, 0xfb, 0x2b, 0xf1, 0x01, 0x48, 0xe8, 0x26, 0x03, 0x8e, 0x27, 0x4d, 0x96, 0x72}} , + {{0xc8, 0x09, 0x3b, 0x60, 0xc9, 0x26, 0x4d, 0x7c, 0xf2, 0x9c, 0xd4, 0xa1, 0x3b, 0x26, 0xc2, 0x04, 0x33, 0x44, 0x76, 0x3c, 0x02, 0xbb, 0x11, 0x42, 0x0c, 0x22, 0xb7, 0xc6, 0xe1, 0xac, 0xb4, 0x0e}}}, +{{{0x6f, 0x85, 0xe7, 0xef, 0xde, 0x67, 0x30, 0xfc, 0xbf, 0x5a, 0xe0, 0x7b, 0x7a, 0x2a, 0x54, 0x6b, 0x5d, 0x62, 0x85, 0xa1, 0xf8, 0x16, 0x88, 0xec, 0x61, 0xb9, 0x96, 0xb5, 0xef, 0x2d, 0x43, 0x4d}} , + {{0x7c, 0x31, 0x33, 0xcc, 0xe4, 0xcf, 0x6c, 0xff, 0x80, 0x47, 0x77, 0xd1, 0xd8, 0xe9, 0x69, 0x97, 0x98, 0x7f, 0x20, 0x57, 0x1d, 0x1d, 0x4f, 0x08, 0x27, 0xc8, 0x35, 0x57, 0x40, 0xc6, 0x21, 0x0c}}}, +{{{0xd2, 0x8e, 0x9b, 0xfa, 0x42, 0x8e, 0xdf, 0x8f, 0xc7, 0x86, 0xf9, 0xa4, 0xca, 0x70, 0x00, 0x9d, 0x21, 0xbf, 0xec, 0x57, 0x62, 0x30, 0x58, 0x8c, 0x0d, 0x35, 0xdb, 0x5d, 0x8b, 0x6a, 0xa0, 0x5a}} , + {{0xc1, 0x58, 0x7c, 0x0d, 0x20, 0xdd, 0x11, 0x26, 0x5f, 0x89, 0x3b, 0x97, 0x58, 0xf8, 0x8b, 0xe3, 0xdf, 0x32, 0xe2, 0xfc, 0xd8, 0x67, 0xf2, 0xa5, 0x37, 0x1e, 0x6d, 0xec, 0x7c, 0x27, 0x20, 0x79}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd0, 0xe9, 0xc0, 0xfa, 0x95, 0x45, 0x23, 0x96, 0xf1, 0x2c, 0x79, 0x25, 0x14, 0xce, 0x40, 0x14, 0x44, 0x2c, 0x36, 0x50, 0xd9, 0x63, 0x56, 0xb7, 0x56, 0x3b, 0x9e, 0xa7, 0xef, 0x89, 0xbb, 0x0e}} , + {{0xce, 0x7f, 0xdc, 0x0a, 0xcc, 0x82, 0x1c, 0x0a, 0x78, 0x71, 0xe8, 0x74, 0x8d, 0x01, 0x30, 0x0f, 0xa7, 0x11, 0x4c, 0xdf, 0x38, 0xd7, 0xa7, 0x0d, 0xf8, 0x48, 0x52, 0x00, 0x80, 0x7b, 0x5f, 0x0e}}}, +{{{0x25, 0x83, 0xe6, 0x94, 0x7b, 0x81, 0xb2, 0x91, 0xae, 0x0e, 0x05, 0xc9, 0xa3, 0x68, 0x2d, 0xd9, 0x88, 0x25, 0x19, 0x2a, 0x61, 0x61, 0x21, 0x97, 0x15, 0xa1, 0x35, 0xa5, 0x46, 0xc8, 0xa2, 0x0e}} , + {{0x1b, 0x03, 0x0d, 0x8b, 0x5a, 0x1b, 0x97, 0x4b, 0xf2, 0x16, 0x31, 0x3d, 0x1f, 0x33, 0xa0, 0x50, 0x3a, 0x18, 0xbe, 0x13, 0xa1, 0x76, 0xc1, 0xba, 0x1b, 0xf1, 0x05, 0x7b, 0x33, 0xa8, 0x82, 0x3b}}}, +{{{0xba, 0x36, 0x7b, 0x6d, 0xa9, 0xea, 0x14, 0x12, 0xc5, 0xfa, 0x91, 0x00, 0xba, 0x9b, 0x99, 0xcc, 0x56, 0x02, 0xe9, 0xa0, 0x26, 0x40, 0x66, 0x8c, 0xc4, 0xf8, 0x85, 0x33, 0x68, 0xe7, 0x03, 0x20}} , + {{0x50, 0x5b, 0xff, 0xa9, 0xb2, 0xf1, 0xf1, 0x78, 0xcf, 0x14, 0xa4, 0xa9, 0xfc, 0x09, 0x46, 0x94, 0x54, 0x65, 0x0d, 0x9c, 0x5f, 0x72, 0x21, 0xe2, 0x97, 0xa5, 0x2d, 0x81, 0xce, 0x4a, 0x5f, 0x79}}}, +{{{0x3d, 0x5f, 0x5c, 0xd2, 0xbc, 0x7d, 0x77, 0x0e, 0x2a, 0x6d, 0x22, 0x45, 0x84, 0x06, 0xc4, 0xdd, 0xc6, 0xa6, 0xc6, 0xd7, 0x49, 0xad, 0x6d, 0x87, 0x91, 0x0e, 0x3a, 0x67, 0x1d, 0x2c, 0x1d, 0x56}} , + {{0xfe, 0x7a, 0x74, 0xcf, 0xd4, 0xd2, 0xe5, 0x19, 0xde, 0xd0, 0xdb, 0x70, 0x23, 0x69, 0xe6, 0x6d, 0xec, 0xec, 0xcc, 0x09, 0x33, 0x6a, 0x77, 0xdc, 0x6b, 0x22, 0x76, 0x5d, 0x92, 0x09, 0xac, 0x2d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x23, 0x15, 0x17, 0xeb, 0xd3, 0xdb, 0x12, 0x5e, 0x01, 0xf0, 0x91, 0xab, 0x2c, 0x41, 0xce, 0xac, 0xed, 0x1b, 0x4b, 0x2d, 0xbc, 0xdb, 0x17, 0x66, 0x89, 0x46, 0xad, 0x4b, 0x1e, 0x6f, 0x0b, 0x14}} , + {{0x11, 0xce, 0xbf, 0xb6, 0x77, 0x2d, 0x48, 0x22, 0x18, 0x4f, 0xa3, 0x5d, 0x4a, 0xb0, 0x70, 0x12, 0x3e, 0x54, 0xd7, 0xd8, 0x0e, 0x2b, 0x27, 0xdc, 0x53, 0xff, 0xca, 0x8c, 0x59, 0xb3, 0x4e, 0x44}}}, +{{{0x07, 0x76, 0x61, 0x0f, 0x66, 0xb2, 0x21, 0x39, 0x7e, 0xc0, 0xec, 0x45, 0x28, 0x82, 0xa1, 0x29, 0x32, 0x44, 0x35, 0x13, 0x5e, 0x61, 0x5e, 0x54, 0xcb, 0x7c, 0xef, 0xf6, 0x41, 0xcf, 0x9f, 0x0a}} , + {{0xdd, 0xf9, 0xda, 0x84, 0xc3, 0xe6, 0x8a, 0x9f, 0x24, 0xd2, 0x96, 0x5d, 0x39, 0x6f, 0x58, 0x8c, 0xc1, 0x56, 0x93, 0xab, 0xb5, 0x79, 0x3b, 0xd2, 0xa8, 0x73, 0x16, 0xed, 0xfa, 0xb4, 0x2f, 0x73}}}, +{{{0x8b, 0xb1, 0x95, 0xe5, 0x92, 0x50, 0x35, 0x11, 0x76, 0xac, 0xf4, 0x4d, 0x24, 0xc3, 0x32, 0xe6, 0xeb, 0xfe, 0x2c, 0x87, 0xc4, 0xf1, 0x56, 0xc4, 0x75, 0x24, 0x7a, 0x56, 0x85, 0x5a, 0x3a, 0x13}} , + {{0x0d, 0x16, 0xac, 0x3c, 0x4a, 0x58, 0x86, 0x3a, 0x46, 0x7f, 0x6c, 0xa3, 0x52, 0x6e, 0x37, 0xe4, 0x96, 0x9c, 0xe9, 0x5c, 0x66, 0x41, 0x67, 0xe4, 0xfb, 0x79, 0x0c, 0x05, 0xf6, 0x64, 0xd5, 0x7c}}}, +{{{0x28, 0xc1, 0xe1, 0x54, 0x73, 0xf2, 0xbf, 0x76, 0x74, 0x19, 0x19, 0x1b, 0xe4, 0xb9, 0xa8, 0x46, 0x65, 0x73, 0xf3, 0x77, 0x9b, 0x29, 0x74, 0x5b, 0xc6, 0x89, 0x6c, 0x2c, 0x7c, 0xf8, 0xb3, 0x0f}} , + {{0xf7, 0xd5, 0xe9, 0x74, 0x5d, 0xb8, 0x25, 0x16, 0xb5, 0x30, 0xbc, 0x84, 0xc5, 0xf0, 0xad, 0xca, 0x12, 0x28, 0xbc, 0x9d, 0xd4, 0xfa, 0x82, 0xe6, 0xe3, 0xbf, 0xa2, 0x15, 0x2c, 0xd4, 0x34, 0x10}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x61, 0xb1, 0x46, 0xba, 0x0e, 0x31, 0xa5, 0x67, 0x6c, 0x7f, 0xd6, 0xd9, 0x27, 0x85, 0x0f, 0x79, 0x14, 0xc8, 0x6c, 0x2f, 0x5f, 0x5b, 0x9c, 0x35, 0x3d, 0x38, 0x86, 0x77, 0x65, 0x55, 0x6a, 0x7b}} , + {{0xd3, 0xb0, 0x3a, 0x66, 0x60, 0x1b, 0x43, 0xf1, 0x26, 0x58, 0x99, 0x09, 0x8f, 0x2d, 0xa3, 0x14, 0x71, 0x85, 0xdb, 0xed, 0xf6, 0x26, 0xd5, 0x61, 0x9a, 0x73, 0xac, 0x0e, 0xea, 0xac, 0xb7, 0x0c}}}, +{{{0x5e, 0xf4, 0xe5, 0x17, 0x0e, 0x10, 0x9f, 0xe7, 0x43, 0x5f, 0x67, 0x5c, 0xac, 0x4b, 0xe5, 0x14, 0x41, 0xd2, 0xbf, 0x48, 0xf5, 0x14, 0xb0, 0x71, 0xc6, 0x61, 0xc1, 0xb2, 0x70, 0x58, 0xd2, 0x5a}} , + {{0x2d, 0xba, 0x16, 0x07, 0x92, 0x94, 0xdc, 0xbd, 0x50, 0x2b, 0xc9, 0x7f, 0x42, 0x00, 0xba, 0x61, 0xed, 0xf8, 0x43, 0xed, 0xf5, 0xf9, 0x40, 0x60, 0xb2, 0xb0, 0x82, 0xcb, 0xed, 0x75, 0xc7, 0x65}}}, +{{{0x80, 0xba, 0x0d, 0x09, 0x40, 0xa7, 0x39, 0xa6, 0x67, 0x34, 0x7e, 0x66, 0xbe, 0x56, 0xfb, 0x53, 0x78, 0xc4, 0x46, 0xe8, 0xed, 0x68, 0x6c, 0x7f, 0xce, 0xe8, 0x9f, 0xce, 0xa2, 0x64, 0x58, 0x53}} , + {{0xe8, 0xc1, 0xa9, 0xc2, 0x7b, 0x59, 0x21, 0x33, 0xe2, 0x43, 0x73, 0x2b, 0xac, 0x2d, 0xc1, 0x89, 0x3b, 0x15, 0xe2, 0xd5, 0xc0, 0x97, 0x8a, 0xfd, 0x6f, 0x36, 0x33, 0xb7, 0xb9, 0xc3, 0x88, 0x09}}}, +{{{0xd0, 0xb6, 0x56, 0x30, 0x5c, 0xae, 0xb3, 0x75, 0x44, 0xa4, 0x83, 0x51, 0x6e, 0x01, 0x65, 0xef, 0x45, 0x76, 0xe6, 0xf5, 0xa2, 0x0d, 0xd4, 0x16, 0x3b, 0x58, 0x2f, 0xf2, 0x2f, 0x36, 0x18, 0x3f}} , + {{0xfd, 0x2f, 0xe0, 0x9b, 0x1e, 0x8c, 0xc5, 0x18, 0xa9, 0xca, 0xd4, 0x2b, 0x35, 0xb6, 0x95, 0x0a, 0x9f, 0x7e, 0xfb, 0xc4, 0xef, 0x88, 0x7b, 0x23, 0x43, 0xec, 0x2f, 0x0d, 0x0f, 0x7a, 0xfc, 0x5c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x8d, 0xd2, 0xda, 0xc7, 0x44, 0xd6, 0x7a, 0xdb, 0x26, 0x7d, 0x1d, 0xb8, 0xe1, 0xde, 0x9d, 0x7a, 0x7d, 0x17, 0x7e, 0x1c, 0x37, 0x04, 0x8d, 0x2d, 0x7c, 0x5e, 0x18, 0x38, 0x1e, 0xaf, 0xc7, 0x1b}} , + {{0x33, 0x48, 0x31, 0x00, 0x59, 0xf6, 0xf2, 0xca, 0x0f, 0x27, 0x1b, 0x63, 0x12, 0x7e, 0x02, 0x1d, 0x49, 0xc0, 0x5d, 0x79, 0x87, 0xef, 0x5e, 0x7a, 0x2f, 0x1f, 0x66, 0x55, 0xd8, 0x09, 0xd9, 0x61}}}, +{{{0x54, 0x83, 0x02, 0x18, 0x82, 0x93, 0x99, 0x07, 0xd0, 0xa7, 0xda, 0xd8, 0x75, 0x89, 0xfa, 0xf2, 0xd9, 0xa3, 0xb8, 0x6b, 0x5a, 0x35, 0x28, 0xd2, 0x6b, 0x59, 0xc2, 0xf8, 0x45, 0xe2, 0xbc, 0x06}} , + {{0x65, 0xc0, 0xa3, 0x88, 0x51, 0x95, 0xfc, 0x96, 0x94, 0x78, 0xe8, 0x0d, 0x8b, 0x41, 0xc9, 0xc2, 0x58, 0x48, 0x75, 0x10, 0x2f, 0xcd, 0x2a, 0xc9, 0xa0, 0x6d, 0x0f, 0xdd, 0x9c, 0x98, 0x26, 0x3d}}}, +{{{0x2f, 0x66, 0x29, 0x1b, 0x04, 0x89, 0xbd, 0x7e, 0xee, 0x6e, 0xdd, 0xb7, 0x0e, 0xef, 0xb0, 0x0c, 0xb4, 0xfc, 0x7f, 0xc2, 0xc9, 0x3a, 0x3c, 0x64, 0xef, 0x45, 0x44, 0xaf, 0x8a, 0x90, 0x65, 0x76}} , + {{0xa1, 0x4c, 0x70, 0x4b, 0x0e, 0xa0, 0x83, 0x70, 0x13, 0xa4, 0xaf, 0xb8, 0x38, 0x19, 0x22, 0x65, 0x09, 0xb4, 0x02, 0x4f, 0x06, 0xf8, 0x17, 0xce, 0x46, 0x45, 0xda, 0x50, 0x7c, 0x8a, 0xd1, 0x4e}}}, +{{{0xf7, 0xd4, 0x16, 0x6c, 0x4e, 0x95, 0x9d, 0x5d, 0x0f, 0x91, 0x2b, 0x52, 0xfe, 0x5c, 0x34, 0xe5, 0x30, 0xe6, 0xa4, 0x3b, 0xf3, 0xf3, 0x34, 0x08, 0xa9, 0x4a, 0xa0, 0xb5, 0x6e, 0xb3, 0x09, 0x0a}} , + {{0x26, 0xd9, 0x5e, 0xa3, 0x0f, 0xeb, 0xa2, 0xf3, 0x20, 0x3b, 0x37, 0xd4, 0xe4, 0x9e, 0xce, 0x06, 0x3d, 0x53, 0xed, 0xae, 0x2b, 0xeb, 0xb6, 0x24, 0x0a, 0x11, 0xa3, 0x0f, 0xd6, 0x7f, 0xa4, 0x3a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xdb, 0x9f, 0x2c, 0xfc, 0xd6, 0xb2, 0x1e, 0x2e, 0x52, 0x7a, 0x06, 0x87, 0x2d, 0x86, 0x72, 0x2b, 0x6d, 0x90, 0x77, 0x46, 0x43, 0xb5, 0x7a, 0xf8, 0x60, 0x7d, 0x91, 0x60, 0x5b, 0x9d, 0x9e, 0x07}} , + {{0x97, 0x87, 0xc7, 0x04, 0x1c, 0x38, 0x01, 0x39, 0x58, 0xc7, 0x85, 0xa3, 0xfc, 0x64, 0x00, 0x64, 0x25, 0xa2, 0xbf, 0x50, 0x94, 0xca, 0x26, 0x31, 0x45, 0x0a, 0x24, 0xd2, 0x51, 0x29, 0x51, 0x16}}}, +{{{0x4d, 0x4a, 0xd7, 0x98, 0x71, 0x57, 0xac, 0x7d, 0x8b, 0x37, 0xbd, 0x63, 0xff, 0x87, 0xb1, 0x49, 0x95, 0x20, 0x7c, 0xcf, 0x7c, 0x59, 0xc4, 0x91, 0x9c, 0xef, 0xd0, 0xdb, 0x60, 0x09, 0x9d, 0x46}} , + {{0xcb, 0x78, 0x94, 0x90, 0xe4, 0x45, 0xb3, 0xf6, 0xd9, 0xf6, 0x57, 0x74, 0xd5, 0xf8, 0x83, 0x4f, 0x39, 0xc9, 0xbd, 0x88, 0xc2, 0x57, 0x21, 0x1f, 0x24, 0x32, 0x68, 0xf8, 0xc7, 0x21, 0x5f, 0x0b}}}, +{{{0x2a, 0x36, 0x68, 0xfc, 0x5f, 0xb6, 0x4f, 0xa5, 0xe3, 0x9d, 0x24, 0x2f, 0xc0, 0x93, 0x61, 0xcf, 0xf8, 0x0a, 0xed, 0xe1, 0xdb, 0x27, 0xec, 0x0e, 0x14, 0x32, 0x5f, 0x8e, 0xa1, 0x62, 0x41, 0x16}} , + {{0x95, 0x21, 0x01, 0xce, 0x95, 0x5b, 0x0e, 0x57, 0xc7, 0xb9, 0x62, 0xb5, 0x28, 0xca, 0x11, 0xec, 0xb4, 0x46, 0x06, 0x73, 0x26, 0xff, 0xfb, 0x66, 0x7d, 0xee, 0x5f, 0xb2, 0x56, 0xfd, 0x2a, 0x08}}}, +{{{0x92, 0x67, 0x77, 0x56, 0xa1, 0xff, 0xc4, 0xc5, 0x95, 0xf0, 0xe3, 0x3a, 0x0a, 0xca, 0x94, 0x4d, 0x9e, 0x7e, 0x3d, 0xb9, 0x6e, 0xb6, 0xb0, 0xce, 0xa4, 0x30, 0x89, 0x99, 0xe9, 0xad, 0x11, 0x59}} , + {{0xf6, 0x48, 0x95, 0xa1, 0x6f, 0x5f, 0xb7, 0xa5, 0xbb, 0x30, 0x00, 0x1c, 0xd2, 0x8a, 0xd6, 0x25, 0x26, 0x1b, 0xb2, 0x0d, 0x37, 0x6a, 0x05, 0xf4, 0x9d, 0x3e, 0x17, 0x2a, 0x43, 0xd2, 0x3a, 0x06}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x32, 0x99, 0x93, 0xd1, 0x9a, 0x72, 0xf3, 0xa9, 0x16, 0xbd, 0xb4, 0x4c, 0xdd, 0xf9, 0xd4, 0xb2, 0x64, 0x9a, 0xd3, 0x05, 0xe4, 0xa3, 0x73, 0x1c, 0xcb, 0x7e, 0x57, 0x67, 0xff, 0x04, 0xb3, 0x10}} , + {{0xb9, 0x4b, 0xa4, 0xad, 0xd0, 0x6d, 0x61, 0x23, 0xb4, 0xaf, 0x34, 0xa9, 0xaa, 0x65, 0xec, 0xd9, 0x69, 0xe3, 0x85, 0xcd, 0xcc, 0xe7, 0xb0, 0x9b, 0x41, 0xc1, 0x1c, 0xf9, 0xa0, 0xfa, 0xb7, 0x13}}}, +{{{0x04, 0xfd, 0x88, 0x3c, 0x0c, 0xd0, 0x09, 0x52, 0x51, 0x4f, 0x06, 0x19, 0xcc, 0xc3, 0xbb, 0xde, 0x80, 0xc5, 0x33, 0xbc, 0xf9, 0xf3, 0x17, 0x36, 0xdd, 0xc6, 0xde, 0xe8, 0x9b, 0x5d, 0x79, 0x1b}} , + {{0x65, 0x0a, 0xbe, 0x51, 0x57, 0xad, 0x50, 0x79, 0x08, 0x71, 0x9b, 0x07, 0x95, 0x8f, 0xfb, 0xae, 0x4b, 0x38, 0xba, 0xcf, 0x53, 0x2a, 0x86, 0x1e, 0xc0, 0x50, 0x5c, 0x67, 0x1b, 0xf6, 0x87, 0x6c}}}, +{{{0x4f, 0x00, 0xb2, 0x66, 0x55, 0xed, 0x4a, 0xed, 0x8d, 0xe1, 0x66, 0x18, 0xb2, 0x14, 0x74, 0x8d, 0xfd, 0x1a, 0x36, 0x0f, 0x26, 0x5c, 0x8b, 0x89, 0xf3, 0xab, 0xf2, 0xf3, 0x24, 0x67, 0xfd, 0x70}} , + {{0xfd, 0x4e, 0x2a, 0xc1, 0x3a, 0xca, 0x8f, 0x00, 0xd8, 0xec, 0x74, 0x67, 0xef, 0x61, 0xe0, 0x28, 0xd0, 0x96, 0xf4, 0x48, 0xde, 0x81, 0xe3, 0xef, 0xdc, 0xaa, 0x7d, 0xf3, 0xb6, 0x55, 0xa6, 0x65}}}, +{{{0xeb, 0xcb, 0xc5, 0x70, 0x91, 0x31, 0x10, 0x93, 0x0d, 0xc8, 0xd0, 0xef, 0x62, 0xe8, 0x6f, 0x82, 0xe3, 0x69, 0x3d, 0x91, 0x7f, 0x31, 0xe1, 0x26, 0x35, 0x3c, 0x4a, 0x2f, 0xab, 0xc4, 0x9a, 0x5e}} , + {{0xab, 0x1b, 0xb5, 0xe5, 0x2b, 0xc3, 0x0e, 0x29, 0xb0, 0xd0, 0x73, 0xe6, 0x4f, 0x64, 0xf2, 0xbc, 0xe4, 0xe4, 0xe1, 0x9a, 0x52, 0x33, 0x2f, 0xbd, 0xcc, 0x03, 0xee, 0x8a, 0xfa, 0x00, 0x5f, 0x50}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xf6, 0xdb, 0x0d, 0x22, 0x3d, 0xb5, 0x14, 0x75, 0x31, 0xf0, 0x81, 0xe2, 0xb9, 0x37, 0xa2, 0xa9, 0x84, 0x11, 0x9a, 0x07, 0xb5, 0x53, 0x89, 0x78, 0xa9, 0x30, 0x27, 0xa1, 0xf1, 0x4e, 0x5c, 0x2e}} , + {{0x8b, 0x00, 0x54, 0xfb, 0x4d, 0xdc, 0xcb, 0x17, 0x35, 0x40, 0xff, 0xb7, 0x8c, 0xfe, 0x4a, 0xe4, 0x4e, 0x99, 0x4e, 0xa8, 0x74, 0x54, 0x5d, 0x5c, 0x96, 0xa3, 0x12, 0x55, 0x36, 0x31, 0x17, 0x5c}}}, +{{{0xce, 0x24, 0xef, 0x7b, 0x86, 0xf2, 0x0f, 0x77, 0xe8, 0x5c, 0x7d, 0x87, 0x38, 0x2d, 0xef, 0xaf, 0xf2, 0x8c, 0x72, 0x2e, 0xeb, 0xb6, 0x55, 0x4b, 0x6e, 0xf1, 0x4e, 0x8a, 0x0e, 0x9a, 0x6c, 0x4c}} , + {{0x25, 0xea, 0x86, 0xc2, 0xd1, 0x4f, 0xb7, 0x3e, 0xa8, 0x5c, 0x8d, 0x66, 0x81, 0x25, 0xed, 0xc5, 0x4c, 0x05, 0xb9, 0xd8, 0xd6, 0x70, 0xbe, 0x73, 0x82, 0xe8, 0xa1, 0xe5, 0x1e, 0x71, 0xd5, 0x26}}}, +{{{0x4e, 0x6d, 0xc3, 0xa7, 0x4f, 0x22, 0x45, 0x26, 0xa2, 0x7e, 0x16, 0xf7, 0xf7, 0x63, 0xdc, 0x86, 0x01, 0x2a, 0x71, 0x38, 0x5c, 0x33, 0xc3, 0xce, 0x30, 0xff, 0xf9, 0x2c, 0x91, 0x71, 0x8a, 0x72}} , + {{0x8c, 0x44, 0x09, 0x28, 0xd5, 0x23, 0xc9, 0x8f, 0xf3, 0x84, 0x45, 0xc6, 0x9a, 0x5e, 0xff, 0xd2, 0xc7, 0x57, 0x93, 0xa3, 0xc1, 0x69, 0xdd, 0x62, 0x0f, 0xda, 0x5c, 0x30, 0x59, 0x5d, 0xe9, 0x4c}}}, +{{{0x92, 0x7e, 0x50, 0x27, 0x72, 0xd7, 0x0c, 0xd6, 0x69, 0x96, 0x81, 0x35, 0x84, 0x94, 0x35, 0x8b, 0x6c, 0xaa, 0x62, 0x86, 0x6e, 0x1c, 0x15, 0xf3, 0x6c, 0xb3, 0xff, 0x65, 0x1b, 0xa2, 0x9b, 0x59}} , + {{0xe2, 0xa9, 0x65, 0x88, 0xc4, 0x50, 0xfa, 0xbb, 0x3b, 0x6e, 0x5f, 0x44, 0x01, 0xca, 0x97, 0xd4, 0xdd, 0xf6, 0xcd, 0x3f, 0x3f, 0xe5, 0x97, 0x67, 0x2b, 0x8c, 0x66, 0x0f, 0x35, 0x9b, 0xf5, 0x07}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xf1, 0x59, 0x27, 0xd8, 0xdb, 0x5a, 0x11, 0x5e, 0x82, 0xf3, 0x38, 0xff, 0x1c, 0xed, 0xfe, 0x3f, 0x64, 0x54, 0x3f, 0x7f, 0xd1, 0x81, 0xed, 0xef, 0x65, 0xc5, 0xcb, 0xfd, 0xe1, 0x80, 0xcd, 0x11}} , + {{0xe0, 0xdb, 0x22, 0x28, 0xe6, 0xff, 0x61, 0x9d, 0x41, 0x14, 0x2d, 0x3b, 0x26, 0x22, 0xdf, 0xf1, 0x34, 0x81, 0xe9, 0x45, 0xee, 0x0f, 0x98, 0x8b, 0xa6, 0x3f, 0xef, 0xf7, 0x43, 0x19, 0xf1, 0x43}}}, +{{{0xee, 0xf3, 0x00, 0xa1, 0x50, 0xde, 0xc0, 0xb6, 0x01, 0xe3, 0x8c, 0x3c, 0x4d, 0x31, 0xd2, 0xb0, 0x58, 0xcd, 0xed, 0x10, 0x4a, 0x7a, 0xef, 0x80, 0xa9, 0x19, 0x32, 0xf3, 0xd8, 0x33, 0x8c, 0x06}} , + {{0xcb, 0x7d, 0x4f, 0xff, 0x30, 0xd8, 0x12, 0x3b, 0x39, 0x1c, 0x06, 0xf9, 0x4c, 0x34, 0x35, 0x71, 0xb5, 0x16, 0x94, 0x67, 0xdf, 0xee, 0x11, 0xde, 0xa4, 0x1d, 0x88, 0x93, 0x35, 0xa9, 0x32, 0x10}}}, +{{{0xe9, 0xc3, 0xbc, 0x7b, 0x5c, 0xfc, 0xb2, 0xf9, 0xc9, 0x2f, 0xe5, 0xba, 0x3a, 0x0b, 0xab, 0x64, 0x38, 0x6f, 0x5b, 0x4b, 0x93, 0xda, 0x64, 0xec, 0x4d, 0x3d, 0xa0, 0xf5, 0xbb, 0xba, 0x47, 0x48}} , + {{0x60, 0xbc, 0x45, 0x1f, 0x23, 0xa2, 0x3b, 0x70, 0x76, 0xe6, 0x97, 0x99, 0x4f, 0x77, 0x54, 0x67, 0x30, 0x9a, 0xe7, 0x66, 0xd6, 0xcd, 0x2e, 0x51, 0x24, 0x2c, 0x42, 0x4a, 0x11, 0xfe, 0x6f, 0x7e}}}, +{{{0x87, 0xc0, 0xb1, 0xf0, 0xa3, 0x6f, 0x0c, 0x93, 0xa9, 0x0a, 0x72, 0xef, 0x5c, 0xbe, 0x65, 0x35, 0xa7, 0x6a, 0x4e, 0x2c, 0xbf, 0x21, 0x23, 0xe8, 0x2f, 0x97, 0xc7, 0x3e, 0xc8, 0x17, 0xac, 0x1e}} , + {{0x7b, 0xef, 0x21, 0xe5, 0x40, 0xcc, 0x1e, 0xdc, 0xd6, 0xbd, 0x97, 0x7a, 0x7c, 0x75, 0x86, 0x7a, 0x25, 0x5a, 0x6e, 0x7c, 0xe5, 0x51, 0x3c, 0x1b, 0x5b, 0x82, 0x9a, 0x07, 0x60, 0xa1, 0x19, 0x04}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x96, 0x88, 0xa6, 0xab, 0x8f, 0xe3, 0x3a, 0x49, 0xf8, 0xfe, 0x34, 0xe7, 0x6a, 0xb2, 0xfe, 0x40, 0x26, 0x74, 0x57, 0x4c, 0xf6, 0xd4, 0x99, 0xce, 0x5d, 0x7b, 0x2f, 0x67, 0xd6, 0x5a, 0xe4, 0x4e}} , + {{0x5c, 0x82, 0xb3, 0xbd, 0x55, 0x25, 0xf6, 0x6a, 0x93, 0xa4, 0x02, 0xc6, 0x7d, 0x5c, 0xb1, 0x2b, 0x5b, 0xff, 0xfb, 0x56, 0xf8, 0x01, 0x41, 0x90, 0xc6, 0xb6, 0xac, 0x4f, 0xfe, 0xa7, 0x41, 0x70}}}, +{{{0xdb, 0xfa, 0x9b, 0x2c, 0xd4, 0x23, 0x67, 0x2c, 0x8a, 0x63, 0x6c, 0x07, 0x26, 0x48, 0x4f, 0xc2, 0x03, 0xd2, 0x53, 0x20, 0x28, 0xed, 0x65, 0x71, 0x47, 0xa9, 0x16, 0x16, 0x12, 0xbc, 0x28, 0x33}} , + {{0x39, 0xc0, 0xfa, 0xfa, 0xcd, 0x33, 0x43, 0xc7, 0x97, 0x76, 0x9b, 0x93, 0x91, 0x72, 0xeb, 0xc5, 0x18, 0x67, 0x4c, 0x11, 0xf0, 0xf4, 0xe5, 0x73, 0xb2, 0x5c, 0x1b, 0xc2, 0x26, 0x3f, 0xbf, 0x2b}}}, +{{{0x86, 0xe6, 0x8c, 0x1d, 0xdf, 0xca, 0xfc, 0xd5, 0xf8, 0x3a, 0xc3, 0x44, 0x72, 0xe6, 0x78, 0x9d, 0x2b, 0x97, 0xf8, 0x28, 0x45, 0xb4, 0x20, 0xc9, 0x2a, 0x8c, 0x67, 0xaa, 0x11, 0xc5, 0x5b, 0x2f}} , + {{0x17, 0x0f, 0x86, 0x52, 0xd7, 0x9d, 0xc3, 0x44, 0x51, 0x76, 0x32, 0x65, 0xb4, 0x37, 0x81, 0x99, 0x46, 0x37, 0x62, 0xed, 0xcf, 0x64, 0x9d, 0x72, 0x40, 0x7a, 0x4c, 0x0b, 0x76, 0x2a, 0xfb, 0x56}}}, +{{{0x33, 0xa7, 0x90, 0x7c, 0xc3, 0x6f, 0x17, 0xa5, 0xa0, 0x67, 0x72, 0x17, 0xea, 0x7e, 0x63, 0x14, 0x83, 0xde, 0xc1, 0x71, 0x2d, 0x41, 0x32, 0x7a, 0xf3, 0xd1, 0x2b, 0xd8, 0x2a, 0xa6, 0x46, 0x36}} , + {{0xac, 0xcc, 0x6b, 0x7c, 0xf9, 0xb8, 0x8b, 0x08, 0x5c, 0xd0, 0x7d, 0x8f, 0x73, 0xea, 0x20, 0xda, 0x86, 0xca, 0x00, 0xc7, 0xad, 0x73, 0x4d, 0xe9, 0xe8, 0xa9, 0xda, 0x1f, 0x03, 0x06, 0xdd, 0x24}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x9c, 0xb2, 0x61, 0x0a, 0x98, 0x2a, 0xa5, 0xd7, 0xee, 0xa9, 0xac, 0x65, 0xcb, 0x0a, 0x1e, 0xe2, 0xbe, 0xdc, 0x85, 0x59, 0x0f, 0x9c, 0xa6, 0x57, 0x34, 0xa5, 0x87, 0xeb, 0x7b, 0x1e, 0x0c, 0x3c}} , + {{0x2f, 0xbd, 0x84, 0x63, 0x0d, 0xb5, 0xa0, 0xf0, 0x4b, 0x9e, 0x93, 0xc6, 0x34, 0x9a, 0x34, 0xff, 0x73, 0x19, 0x2f, 0x6e, 0x54, 0x45, 0x2c, 0x92, 0x31, 0x76, 0x34, 0xf1, 0xb2, 0x26, 0xe8, 0x74}}}, +{{{0x0a, 0x67, 0x90, 0x6d, 0x0c, 0x4c, 0xcc, 0xc0, 0xe6, 0xbd, 0xa7, 0x5e, 0x55, 0x8c, 0xcd, 0x58, 0x9b, 0x11, 0xa2, 0xbb, 0x4b, 0xb1, 0x43, 0x04, 0x3c, 0x55, 0xed, 0x23, 0xfe, 0xcd, 0xb1, 0x53}} , + {{0x05, 0xfb, 0x75, 0xf5, 0x01, 0xaf, 0x38, 0x72, 0x58, 0xfc, 0x04, 0x29, 0x34, 0x7a, 0x67, 0xa2, 0x08, 0x50, 0x6e, 0xd0, 0x2b, 0x73, 0xd5, 0xb8, 0xe4, 0x30, 0x96, 0xad, 0x45, 0xdf, 0xa6, 0x5c}}}, +{{{0x0d, 0x88, 0x1a, 0x90, 0x7e, 0xdc, 0xd8, 0xfe, 0xc1, 0x2f, 0x5d, 0x67, 0xee, 0x67, 0x2f, 0xed, 0x6f, 0x55, 0x43, 0x5f, 0x87, 0x14, 0x35, 0x42, 0xd3, 0x75, 0xae, 0xd5, 0xd3, 0x85, 0x1a, 0x76}} , + {{0x87, 0xc8, 0xa0, 0x6e, 0xe1, 0xb0, 0xad, 0x6a, 0x4a, 0x34, 0x71, 0xed, 0x7c, 0xd6, 0x44, 0x03, 0x65, 0x4a, 0x5c, 0x5c, 0x04, 0xf5, 0x24, 0x3f, 0xb0, 0x16, 0x5e, 0x8c, 0xb2, 0xd2, 0xc5, 0x20}}}, +{{{0x98, 0x83, 0xc2, 0x37, 0xa0, 0x41, 0xa8, 0x48, 0x5c, 0x5f, 0xbf, 0xc8, 0xfa, 0x24, 0xe0, 0x59, 0x2c, 0xbd, 0xf6, 0x81, 0x7e, 0x88, 0xe6, 0xca, 0x04, 0xd8, 0x5d, 0x60, 0xbb, 0x74, 0xa7, 0x0b}} , + {{0x21, 0x13, 0x91, 0xbf, 0x77, 0x7a, 0x33, 0xbc, 0xe9, 0x07, 0x39, 0x0a, 0xdd, 0x7d, 0x06, 0x10, 0x9a, 0xee, 0x47, 0x73, 0x1b, 0x15, 0x5a, 0xfb, 0xcd, 0x4d, 0xd0, 0xd2, 0x3a, 0x01, 0xba, 0x54}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x48, 0xd5, 0x39, 0x4a, 0x0b, 0x20, 0x6a, 0x43, 0xa0, 0x07, 0x82, 0x5e, 0x49, 0x7c, 0xc9, 0x47, 0xf1, 0x7c, 0x37, 0xb9, 0x23, 0xef, 0x6b, 0x46, 0x45, 0x8c, 0x45, 0x76, 0xdf, 0x14, 0x6b, 0x6e}} , + {{0x42, 0xc9, 0xca, 0x29, 0x4c, 0x76, 0x37, 0xda, 0x8a, 0x2d, 0x7c, 0x3a, 0x58, 0xf2, 0x03, 0xb4, 0xb5, 0xb9, 0x1a, 0x13, 0x2d, 0xde, 0x5f, 0x6b, 0x9d, 0xba, 0x52, 0xc9, 0x5d, 0xb3, 0xf3, 0x30}}}, +{{{0x4c, 0x6f, 0xfe, 0x6b, 0x0c, 0x62, 0xd7, 0x48, 0x71, 0xef, 0xb1, 0x85, 0x79, 0xc0, 0xed, 0x24, 0xb1, 0x08, 0x93, 0x76, 0x8e, 0xf7, 0x38, 0x8e, 0xeb, 0xfe, 0x80, 0x40, 0xaf, 0x90, 0x64, 0x49}} , + {{0x4a, 0x88, 0xda, 0xc1, 0x98, 0x44, 0x3c, 0x53, 0x4e, 0xdb, 0x4b, 0xb9, 0x12, 0x5f, 0xcd, 0x08, 0x04, 0xef, 0x75, 0xe7, 0xb1, 0x3a, 0xe5, 0x07, 0xfa, 0xca, 0x65, 0x7b, 0x72, 0x10, 0x64, 0x7f}}}, +{{{0x3d, 0x81, 0xf0, 0xeb, 0x16, 0xfd, 0x58, 0x33, 0x8d, 0x7c, 0x1a, 0xfb, 0x20, 0x2c, 0x8a, 0xee, 0x90, 0xbb, 0x33, 0x6d, 0x45, 0xe9, 0x8e, 0x99, 0x85, 0xe1, 0x08, 0x1f, 0xc5, 0xf1, 0xb5, 0x46}} , + {{0xe4, 0xe7, 0x43, 0x4b, 0xa0, 0x3f, 0x2b, 0x06, 0xba, 0x17, 0xae, 0x3d, 0xe6, 0xce, 0xbd, 0xb8, 0xed, 0x74, 0x11, 0x35, 0xec, 0x96, 0xfe, 0x31, 0xe3, 0x0e, 0x7a, 0x4e, 0xc9, 0x1d, 0xcb, 0x20}}}, +{{{0xe0, 0x67, 0xe9, 0x7b, 0xdb, 0x96, 0x5c, 0xb0, 0x32, 0xd0, 0x59, 0x31, 0x90, 0xdc, 0x92, 0x97, 0xac, 0x09, 0x38, 0x31, 0x0f, 0x7e, 0xd6, 0x5d, 0xd0, 0x06, 0xb6, 0x1f, 0xea, 0xf0, 0x5b, 0x07}} , + {{0x81, 0x9f, 0xc7, 0xde, 0x6b, 0x41, 0x22, 0x35, 0x14, 0x67, 0x77, 0x3e, 0x90, 0x81, 0xb0, 0xd9, 0x85, 0x4c, 0xca, 0x9b, 0x3f, 0x04, 0x59, 0xd6, 0xaa, 0x17, 0xc3, 0x88, 0x34, 0x37, 0xba, 0x43}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x4c, 0xb6, 0x69, 0xc8, 0x81, 0x95, 0x94, 0x33, 0x92, 0x34, 0xe9, 0x3c, 0x84, 0x0d, 0x3d, 0x5a, 0x37, 0x9c, 0x22, 0xa0, 0xaa, 0x65, 0xce, 0xb4, 0xc2, 0x2d, 0x66, 0x67, 0x02, 0xff, 0x74, 0x10}} , + {{0x22, 0xb0, 0xd5, 0xe6, 0xc7, 0xef, 0xb1, 0xa7, 0x13, 0xda, 0x60, 0xb4, 0x80, 0xc1, 0x42, 0x7d, 0x10, 0x70, 0x97, 0x04, 0x4d, 0xda, 0x23, 0x89, 0xc2, 0x0e, 0x68, 0xcb, 0xde, 0xe0, 0x9b, 0x29}}}, +{{{0x33, 0xfe, 0x42, 0x2a, 0x36, 0x2b, 0x2e, 0x36, 0x64, 0x5c, 0x8b, 0xcc, 0x81, 0x6a, 0x15, 0x08, 0xa1, 0x27, 0xe8, 0x57, 0xe5, 0x78, 0x8e, 0xf2, 0x58, 0x19, 0x12, 0x42, 0xae, 0xc4, 0x63, 0x3e}} , + {{0x78, 0x96, 0x9c, 0xa7, 0xca, 0x80, 0xae, 0x02, 0x85, 0xb1, 0x7c, 0x04, 0x5c, 0xc1, 0x5b, 0x26, 0xc1, 0xba, 0xed, 0xa5, 0x59, 0x70, 0x85, 0x8c, 0x8c, 0xe8, 0x87, 0xac, 0x6a, 0x28, 0x99, 0x35}}}, +{{{0x9f, 0x04, 0x08, 0x28, 0xbe, 0x87, 0xda, 0x80, 0x28, 0x38, 0xde, 0x9f, 0xcd, 0xe4, 0xe3, 0x62, 0xfb, 0x2e, 0x46, 0x8d, 0x01, 0xb3, 0x06, 0x51, 0xd4, 0x19, 0x3b, 0x11, 0xfa, 0xe2, 0xad, 0x1e}} , + {{0xa0, 0x20, 0x99, 0x69, 0x0a, 0xae, 0xa3, 0x70, 0x4e, 0x64, 0x80, 0xb7, 0x85, 0x9c, 0x87, 0x54, 0x43, 0x43, 0x55, 0x80, 0x6d, 0x8d, 0x7c, 0xa9, 0x64, 0xca, 0x6c, 0x2e, 0x21, 0xd8, 0xc8, 0x6c}}}, +{{{0x91, 0x4a, 0x07, 0xad, 0x08, 0x75, 0xc1, 0x4f, 0xa4, 0xb2, 0xc3, 0x6f, 0x46, 0x3e, 0xb1, 0xce, 0x52, 0xab, 0x67, 0x09, 0x54, 0x48, 0x6b, 0x6c, 0xd7, 0x1d, 0x71, 0x76, 0xcb, 0xff, 0xdd, 0x31}} , + {{0x36, 0x88, 0xfa, 0xfd, 0xf0, 0x36, 0x6f, 0x07, 0x74, 0x88, 0x50, 0xd0, 0x95, 0x38, 0x4a, 0x48, 0x2e, 0x07, 0x64, 0x97, 0x11, 0x76, 0x01, 0x1a, 0x27, 0x4d, 0x8e, 0x25, 0x9a, 0x9b, 0x1c, 0x22}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xbe, 0x57, 0xbd, 0x0e, 0x0f, 0xac, 0x5e, 0x76, 0xa3, 0x71, 0xad, 0x2b, 0x10, 0x45, 0x02, 0xec, 0x59, 0xd5, 0x5d, 0xa9, 0x44, 0xcc, 0x25, 0x4c, 0xb3, 0x3c, 0x5b, 0x69, 0x07, 0x55, 0x26, 0x6b}} , + {{0x30, 0x6b, 0xd4, 0xa7, 0x51, 0x29, 0xe3, 0xf9, 0x7a, 0x75, 0x2a, 0x82, 0x2f, 0xd6, 0x1d, 0x99, 0x2b, 0x80, 0xd5, 0x67, 0x1e, 0x15, 0x9d, 0xca, 0xfd, 0xeb, 0xac, 0x97, 0x35, 0x09, 0x7f, 0x3f}}}, +{{{0x35, 0x0d, 0x34, 0x0a, 0xb8, 0x67, 0x56, 0x29, 0x20, 0xf3, 0x19, 0x5f, 0xe2, 0x83, 0x42, 0x73, 0x53, 0xa8, 0xc5, 0x02, 0x19, 0x33, 0xb4, 0x64, 0xbd, 0xc3, 0x87, 0x8c, 0xd7, 0x76, 0xed, 0x25}} , + {{0x47, 0x39, 0x37, 0x76, 0x0d, 0x1d, 0x0c, 0xf5, 0x5a, 0x6d, 0x43, 0x88, 0x99, 0x15, 0xb4, 0x52, 0x0f, 0x2a, 0xb3, 0xb0, 0x3f, 0xa6, 0xb3, 0x26, 0xb3, 0xc7, 0x45, 0xf5, 0x92, 0x5f, 0x9b, 0x17}}}, +{{{0x9d, 0x23, 0xbd, 0x15, 0xfe, 0x52, 0x52, 0x15, 0x26, 0x79, 0x86, 0xba, 0x06, 0x56, 0x66, 0xbb, 0x8c, 0x2e, 0x10, 0x11, 0xd5, 0x4a, 0x18, 0x52, 0xda, 0x84, 0x44, 0xf0, 0x3e, 0xe9, 0x8c, 0x35}} , + {{0xad, 0xa0, 0x41, 0xec, 0xc8, 0x4d, 0xb9, 0xd2, 0x6e, 0x96, 0x4e, 0x5b, 0xc5, 0xc2, 0xa0, 0x1b, 0xcf, 0x0c, 0xbf, 0x17, 0x66, 0x57, 0xc1, 0x17, 0x90, 0x45, 0x71, 0xc2, 0xe1, 0x24, 0xeb, 0x27}}}, +{{{0x2c, 0xb9, 0x42, 0xa4, 0xaf, 0x3b, 0x42, 0x0e, 0xc2, 0x0f, 0xf2, 0xea, 0x83, 0xaf, 0x9a, 0x13, 0x17, 0xb0, 0xbd, 0x89, 0x17, 0xe3, 0x72, 0xcb, 0x0e, 0x76, 0x7e, 0x41, 0x63, 0x04, 0x88, 0x71}} , + {{0x75, 0x78, 0x38, 0x86, 0x57, 0xdd, 0x9f, 0xee, 0x54, 0x70, 0x65, 0xbf, 0xf1, 0x2c, 0xe0, 0x39, 0x0d, 0xe3, 0x89, 0xfd, 0x8e, 0x93, 0x4f, 0x43, 0xdc, 0xd5, 0x5b, 0xde, 0xf9, 0x98, 0xe5, 0x7b}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xe7, 0x3b, 0x65, 0x11, 0xdf, 0xb2, 0xf2, 0x63, 0x94, 0x12, 0x6f, 0x5c, 0x9e, 0x77, 0xc1, 0xb6, 0xd8, 0xab, 0x58, 0x7a, 0x1d, 0x95, 0x73, 0xdd, 0xe7, 0xe3, 0x6f, 0xf2, 0x03, 0x1d, 0xdb, 0x76}} , + {{0xae, 0x06, 0x4e, 0x2c, 0x52, 0x1b, 0xbc, 0x5a, 0x5a, 0xa5, 0xbe, 0x27, 0xbd, 0xeb, 0xe1, 0x14, 0x17, 0x68, 0x26, 0x07, 0x03, 0xd1, 0x18, 0x0b, 0xdf, 0xf1, 0x06, 0x5c, 0xa6, 0x1b, 0xb9, 0x24}}}, +{{{0xc5, 0x66, 0x80, 0x13, 0x0e, 0x48, 0x8c, 0x87, 0x31, 0x84, 0xb4, 0x60, 0xed, 0xc5, 0xec, 0xb6, 0xc5, 0x05, 0x33, 0x5f, 0x2f, 0x7d, 0x40, 0xb6, 0x32, 0x1d, 0x38, 0x74, 0x1b, 0xf1, 0x09, 0x3d}} , + {{0xd4, 0x69, 0x82, 0xbc, 0x8d, 0xf8, 0x34, 0x36, 0x75, 0x55, 0x18, 0x55, 0x58, 0x3c, 0x79, 0xaf, 0x26, 0x80, 0xab, 0x9b, 0x95, 0x00, 0xf1, 0xcb, 0xda, 0xc1, 0x9f, 0xf6, 0x2f, 0xa2, 0xf4, 0x45}}}, +{{{0x17, 0xbe, 0xeb, 0x85, 0xed, 0x9e, 0xcd, 0x56, 0xf5, 0x17, 0x45, 0x42, 0xb4, 0x1f, 0x44, 0x4c, 0x05, 0x74, 0x15, 0x47, 0x00, 0xc6, 0x6a, 0x3d, 0x24, 0x09, 0x0d, 0x58, 0xb1, 0x42, 0xd7, 0x04}} , + {{0x8d, 0xbd, 0xa3, 0xc4, 0x06, 0x9b, 0x1f, 0x90, 0x58, 0x60, 0x74, 0xb2, 0x00, 0x3b, 0x3c, 0xd2, 0xda, 0x82, 0xbb, 0x10, 0x90, 0x69, 0x92, 0xa9, 0xb4, 0x30, 0x81, 0xe3, 0x7c, 0xa8, 0x89, 0x45}}}, +{{{0x3f, 0xdc, 0x05, 0xcb, 0x41, 0x3c, 0xc8, 0x23, 0x04, 0x2c, 0x38, 0x99, 0xe3, 0x68, 0x55, 0xf9, 0xd3, 0x32, 0xc7, 0xbf, 0xfa, 0xd4, 0x1b, 0x5d, 0xde, 0xdc, 0x10, 0x42, 0xc0, 0x42, 0xd9, 0x75}} , + {{0x2d, 0xab, 0x35, 0x4e, 0x87, 0xc4, 0x65, 0x97, 0x67, 0x24, 0xa4, 0x47, 0xad, 0x3f, 0x8e, 0xf3, 0xcb, 0x31, 0x17, 0x77, 0xc5, 0xe2, 0xd7, 0x8f, 0x3c, 0xc1, 0xcd, 0x56, 0x48, 0xc1, 0x6c, 0x69}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x14, 0xae, 0x5f, 0x88, 0x7b, 0xa5, 0x90, 0xdf, 0x10, 0xb2, 0x8b, 0x5e, 0x24, 0x17, 0xc3, 0xa3, 0xd4, 0x0f, 0x92, 0x61, 0x1a, 0x19, 0x5a, 0xad, 0x76, 0xbd, 0xd8, 0x1c, 0xdd, 0xe0, 0x12, 0x6d}} , + {{0x8e, 0xbd, 0x70, 0x8f, 0x02, 0xa3, 0x24, 0x4d, 0x5a, 0x67, 0xc4, 0xda, 0xf7, 0x20, 0x0f, 0x81, 0x5b, 0x7a, 0x05, 0x24, 0x67, 0x83, 0x0b, 0x2a, 0x80, 0xe7, 0xfd, 0x74, 0x4b, 0x9e, 0x5c, 0x0d}}}, +{{{0x94, 0xd5, 0x5f, 0x1f, 0xa2, 0xfb, 0xeb, 0xe1, 0x07, 0x34, 0xf8, 0x20, 0xad, 0x81, 0x30, 0x06, 0x2d, 0xa1, 0x81, 0x95, 0x36, 0xcf, 0x11, 0x0b, 0xaf, 0xc1, 0x2b, 0x9a, 0x6c, 0x55, 0xc1, 0x16}} , + {{0x36, 0x4f, 0xf1, 0x5e, 0x74, 0x35, 0x13, 0x28, 0xd7, 0x11, 0xcf, 0xb8, 0xde, 0x93, 0xb3, 0x05, 0xb8, 0xb5, 0x73, 0xe9, 0xeb, 0xad, 0x19, 0x1e, 0x89, 0x0f, 0x8b, 0x15, 0xd5, 0x8c, 0xe3, 0x23}}}, +{{{0x33, 0x79, 0xe7, 0x18, 0xe6, 0x0f, 0x57, 0x93, 0x15, 0xa0, 0xa7, 0xaa, 0xc4, 0xbf, 0x4f, 0x30, 0x74, 0x95, 0x5e, 0x69, 0x4a, 0x5b, 0x45, 0xe4, 0x00, 0xeb, 0x23, 0x74, 0x4c, 0xdf, 0x6b, 0x45}} , + {{0x97, 0x29, 0x6c, 0xc4, 0x42, 0x0b, 0xdd, 0xc0, 0x29, 0x5c, 0x9b, 0x34, 0x97, 0xd0, 0xc7, 0x79, 0x80, 0x63, 0x74, 0xe4, 0x8e, 0x37, 0xb0, 0x2b, 0x7c, 0xe8, 0x68, 0x6c, 0xc3, 0x82, 0x97, 0x57}}}, +{{{0x22, 0xbe, 0x83, 0xb6, 0x4b, 0x80, 0x6b, 0x43, 0x24, 0x5e, 0xef, 0x99, 0x9b, 0xa8, 0xfc, 0x25, 0x8d, 0x3b, 0x03, 0x94, 0x2b, 0x3e, 0xe7, 0x95, 0x76, 0x9b, 0xcc, 0x15, 0xdb, 0x32, 0xe6, 0x66}} , + {{0x84, 0xf0, 0x4a, 0x13, 0xa6, 0xd6, 0xfa, 0x93, 0x46, 0x07, 0xf6, 0x7e, 0x5c, 0x6d, 0x5e, 0xf6, 0xa6, 0xe7, 0x48, 0xf0, 0x06, 0xea, 0xff, 0x90, 0xc1, 0xcc, 0x4c, 0x19, 0x9c, 0x3c, 0x4e, 0x53}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2a, 0x50, 0xe3, 0x07, 0x15, 0x59, 0xf2, 0x8b, 0x81, 0xf2, 0xf3, 0xd3, 0x6c, 0x99, 0x8c, 0x70, 0x67, 0xec, 0xcc, 0xee, 0x9e, 0x59, 0x45, 0x59, 0x7d, 0x47, 0x75, 0x69, 0xf5, 0x24, 0x93, 0x5d}} , + {{0x6a, 0x4f, 0x1b, 0xbe, 0x6b, 0x30, 0xcf, 0x75, 0x46, 0xe3, 0x7b, 0x9d, 0xfc, 0xcd, 0xd8, 0x5c, 0x1f, 0xb4, 0xc8, 0xe2, 0x24, 0xec, 0x1a, 0x28, 0x05, 0x32, 0x57, 0xfd, 0x3c, 0x5a, 0x98, 0x10}}}, +{{{0xa3, 0xdb, 0xf7, 0x30, 0xd8, 0xc2, 0x9a, 0xe1, 0xd3, 0xce, 0x22, 0xe5, 0x80, 0x1e, 0xd9, 0xe4, 0x1f, 0xab, 0xc0, 0x71, 0x1a, 0x86, 0x0e, 0x27, 0x99, 0x5b, 0xfa, 0x76, 0x99, 0xb0, 0x08, 0x3c}} , + {{0x2a, 0x93, 0xd2, 0x85, 0x1b, 0x6a, 0x5d, 0xa6, 0xee, 0xd1, 0xd1, 0x33, 0xbd, 0x6a, 0x36, 0x73, 0x37, 0x3a, 0x44, 0xb4, 0xec, 0xa9, 0x7a, 0xde, 0x83, 0x40, 0xd7, 0xdf, 0x28, 0xba, 0xa2, 0x30}}}, +{{{0xd3, 0xb5, 0x6d, 0x05, 0x3f, 0x9f, 0xf3, 0x15, 0x8d, 0x7c, 0xca, 0xc9, 0xfc, 0x8a, 0x7c, 0x94, 0xb0, 0x63, 0x36, 0x9b, 0x78, 0xd1, 0x91, 0x1f, 0x93, 0xd8, 0x57, 0x43, 0xde, 0x76, 0xa3, 0x43}} , + {{0x9b, 0x35, 0xe2, 0xa9, 0x3d, 0x32, 0x1e, 0xbb, 0x16, 0x28, 0x70, 0xe9, 0x45, 0x2f, 0x8f, 0x70, 0x7f, 0x08, 0x7e, 0x53, 0xc4, 0x7a, 0xbf, 0xf7, 0xe1, 0xa4, 0x6a, 0xd8, 0xac, 0x64, 0x1b, 0x11}}}, +{{{0xb2, 0xeb, 0x47, 0x46, 0x18, 0x3e, 0x1f, 0x99, 0x0c, 0xcc, 0xf1, 0x2c, 0xe0, 0xe7, 0x8f, 0xe0, 0x01, 0x7e, 0x65, 0xb8, 0x0c, 0xd0, 0xfb, 0xc8, 0xb9, 0x90, 0x98, 0x33, 0x61, 0x3b, 0xd8, 0x27}} , + {{0xa0, 0xbe, 0x72, 0x3a, 0x50, 0x4b, 0x74, 0xab, 0x01, 0xc8, 0x93, 0xc5, 0xe4, 0xc7, 0x08, 0x6c, 0xb4, 0xca, 0xee, 0xeb, 0x8e, 0xd7, 0x4e, 0x26, 0xc6, 0x1d, 0xe2, 0x71, 0xaf, 0x89, 0xa0, 0x2a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x98, 0x0b, 0xe4, 0xde, 0xdb, 0xa8, 0xfa, 0x82, 0x74, 0x06, 0x52, 0x6d, 0x08, 0x52, 0x8a, 0xff, 0x62, 0xc5, 0x6a, 0x44, 0x0f, 0x51, 0x8c, 0x1f, 0x6e, 0xb6, 0xc6, 0x2c, 0x81, 0xd3, 0x76, 0x46}} , + {{0xf4, 0x29, 0x74, 0x2e, 0x80, 0xa7, 0x1a, 0x8f, 0xf6, 0xbd, 0xd6, 0x8e, 0xbf, 0xc1, 0x95, 0x2a, 0xeb, 0xa0, 0x7f, 0x45, 0xa0, 0x50, 0x14, 0x05, 0xb1, 0x57, 0x4c, 0x74, 0xb7, 0xe2, 0x89, 0x7d}}}, +{{{0x07, 0xee, 0xa7, 0xad, 0xb7, 0x09, 0x0b, 0x49, 0x4e, 0xbf, 0xca, 0xe5, 0x21, 0xe6, 0xe6, 0xaf, 0xd5, 0x67, 0xf3, 0xce, 0x7e, 0x7c, 0x93, 0x7b, 0x5a, 0x10, 0x12, 0x0e, 0x6c, 0x06, 0x11, 0x75}} , + {{0xd5, 0xfc, 0x86, 0xa3, 0x3b, 0xa3, 0x3e, 0x0a, 0xfb, 0x0b, 0xf7, 0x36, 0xb1, 0x5b, 0xda, 0x70, 0xb7, 0x00, 0xa7, 0xda, 0x88, 0x8f, 0x84, 0xa8, 0xbc, 0x1c, 0x39, 0xb8, 0x65, 0xf3, 0x4d, 0x60}}}, +{{{0x96, 0x9d, 0x31, 0xf4, 0xa2, 0xbe, 0x81, 0xb9, 0xa5, 0x59, 0x9e, 0xba, 0x07, 0xbe, 0x74, 0x58, 0xd8, 0xeb, 0xc5, 0x9f, 0x3d, 0xd1, 0xf4, 0xae, 0xce, 0x53, 0xdf, 0x4f, 0xc7, 0x2a, 0x89, 0x4d}} , + {{0x29, 0xd8, 0xf2, 0xaa, 0xe9, 0x0e, 0xf7, 0x2e, 0x5f, 0x9d, 0x8a, 0x5b, 0x09, 0xed, 0xc9, 0x24, 0x22, 0xf4, 0x0f, 0x25, 0x8f, 0x1c, 0x84, 0x6e, 0x34, 0x14, 0x6c, 0xea, 0xb3, 0x86, 0x5d, 0x04}}}, +{{{0x07, 0x98, 0x61, 0xe8, 0x6a, 0xd2, 0x81, 0x49, 0x25, 0xd5, 0x5b, 0x18, 0xc7, 0x35, 0x52, 0x51, 0xa4, 0x46, 0xad, 0x18, 0x0d, 0xc9, 0x5f, 0x18, 0x91, 0x3b, 0xb4, 0xc0, 0x60, 0x59, 0x8d, 0x66}} , + {{0x03, 0x1b, 0x79, 0x53, 0x6e, 0x24, 0xae, 0x57, 0xd9, 0x58, 0x09, 0x85, 0x48, 0xa2, 0xd3, 0xb5, 0xe2, 0x4d, 0x11, 0x82, 0xe6, 0x86, 0x3c, 0xe9, 0xb1, 0x00, 0x19, 0xc2, 0x57, 0xf7, 0x66, 0x7a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x0f, 0xe3, 0x89, 0x03, 0xd7, 0x22, 0x95, 0x9f, 0xca, 0xb4, 0x8d, 0x9e, 0x6d, 0x97, 0xff, 0x8d, 0x21, 0x59, 0x07, 0xef, 0x03, 0x2d, 0x5e, 0xf8, 0x44, 0x46, 0xe7, 0x85, 0x80, 0xc5, 0x89, 0x50}} , + {{0x8b, 0xd8, 0x53, 0x86, 0x24, 0x86, 0x29, 0x52, 0x01, 0xfa, 0x20, 0xc3, 0x4e, 0x95, 0xcb, 0xad, 0x7b, 0x34, 0x94, 0x30, 0xb7, 0x7a, 0xfa, 0x96, 0x41, 0x60, 0x2b, 0xcb, 0x59, 0xb9, 0xca, 0x50}}}, +{{{0xc2, 0x5b, 0x9b, 0x78, 0x23, 0x1b, 0x3a, 0x88, 0x94, 0x5f, 0x0a, 0x9b, 0x98, 0x2b, 0x6e, 0x53, 0x11, 0xf6, 0xff, 0xc6, 0x7d, 0x42, 0xcc, 0x02, 0x80, 0x40, 0x0d, 0x1e, 0xfb, 0xaf, 0x61, 0x07}} , + {{0xb0, 0xe6, 0x2f, 0x81, 0x70, 0xa1, 0x2e, 0x39, 0x04, 0x7c, 0xc4, 0x2c, 0x87, 0x45, 0x4a, 0x5b, 0x69, 0x97, 0xac, 0x6d, 0x2c, 0x10, 0x42, 0x7c, 0x3b, 0x15, 0x70, 0x60, 0x0e, 0x11, 0x6d, 0x3a}}}, +{{{0x9b, 0x18, 0x80, 0x5e, 0xdb, 0x05, 0xbd, 0xc6, 0xb7, 0x3c, 0xc2, 0x40, 0x4d, 0x5d, 0xce, 0x97, 0x8a, 0x34, 0x15, 0xab, 0x28, 0x5d, 0x10, 0xf0, 0x37, 0x0c, 0xcc, 0x16, 0xfa, 0x1f, 0x33, 0x0d}} , + {{0x19, 0xf9, 0x35, 0xaa, 0x59, 0x1a, 0x0c, 0x5c, 0x06, 0xfc, 0x6a, 0x0b, 0x97, 0x53, 0x36, 0xfc, 0x2a, 0xa5, 0x5a, 0x9b, 0x30, 0xef, 0x23, 0xaf, 0x39, 0x5d, 0x9a, 0x6b, 0x75, 0x57, 0x48, 0x0b}}}, +{{{0x26, 0xdc, 0x76, 0x3b, 0xfc, 0xf9, 0x9c, 0x3f, 0x89, 0x0b, 0x62, 0x53, 0xaf, 0x83, 0x01, 0x2e, 0xbc, 0x6a, 0xc6, 0x03, 0x0d, 0x75, 0x2a, 0x0d, 0xe6, 0x94, 0x54, 0xcf, 0xb3, 0xe5, 0x96, 0x25}} , + {{0xfe, 0x82, 0xb1, 0x74, 0x31, 0x8a, 0xa7, 0x6f, 0x56, 0xbd, 0x8d, 0xf4, 0xe0, 0x94, 0x51, 0x59, 0xde, 0x2c, 0x5a, 0xf4, 0x84, 0x6b, 0x4a, 0x88, 0x93, 0xc0, 0x0c, 0x9a, 0xac, 0xa7, 0xa0, 0x68}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x25, 0x0d, 0xd6, 0xc7, 0x23, 0x47, 0x10, 0xad, 0xc7, 0x08, 0x5c, 0x87, 0x87, 0x93, 0x98, 0x18, 0xb8, 0xd3, 0x9c, 0xac, 0x5a, 0x3d, 0xc5, 0x75, 0xf8, 0x49, 0x32, 0x14, 0xcc, 0x51, 0x96, 0x24}} , + {{0x65, 0x9c, 0x5d, 0xf0, 0x37, 0x04, 0xf0, 0x34, 0x69, 0x2a, 0xf0, 0xa5, 0x64, 0xca, 0xde, 0x2b, 0x5b, 0x15, 0x10, 0xd2, 0xab, 0x06, 0xdd, 0xc4, 0xb0, 0xb6, 0x5b, 0xc1, 0x17, 0xdf, 0x8f, 0x02}}}, +{{{0xbd, 0x59, 0x3d, 0xbf, 0x5c, 0x31, 0x44, 0x2c, 0x32, 0x94, 0x04, 0x60, 0x84, 0x0f, 0xad, 0x00, 0xb6, 0x8f, 0xc9, 0x1d, 0xcc, 0x5c, 0xa2, 0x49, 0x0e, 0x50, 0x91, 0x08, 0x9a, 0x43, 0x55, 0x05}} , + {{0x5d, 0x93, 0x55, 0xdf, 0x9b, 0x12, 0x19, 0xec, 0x93, 0x85, 0x42, 0x9e, 0x66, 0x0f, 0x9d, 0xaf, 0x99, 0xaf, 0x26, 0x89, 0xbc, 0x61, 0xfd, 0xff, 0xce, 0x4b, 0xf4, 0x33, 0x95, 0xc9, 0x35, 0x58}}}, +{{{0x12, 0x55, 0xf9, 0xda, 0xcb, 0x44, 0xa7, 0xdc, 0x57, 0xe2, 0xf9, 0x9a, 0xe6, 0x07, 0x23, 0x60, 0x54, 0xa7, 0x39, 0xa5, 0x9b, 0x84, 0x56, 0x6e, 0xaa, 0x8b, 0x8f, 0xb0, 0x2c, 0x87, 0xaf, 0x67}} , + {{0x00, 0xa9, 0x4c, 0xb2, 0x12, 0xf8, 0x32, 0xa8, 0x7a, 0x00, 0x4b, 0x49, 0x32, 0xba, 0x1f, 0x5d, 0x44, 0x8e, 0x44, 0x7a, 0xdc, 0x11, 0xfb, 0x39, 0x08, 0x57, 0x87, 0xa5, 0x12, 0x42, 0x93, 0x0e}}}, +{{{0x17, 0xb4, 0xae, 0x72, 0x59, 0xd0, 0xaa, 0xa8, 0x16, 0x8b, 0x63, 0x11, 0xb3, 0x43, 0x04, 0xda, 0x0c, 0xa8, 0xb7, 0x68, 0xdd, 0x4e, 0x54, 0xe7, 0xaf, 0x5d, 0x5d, 0x05, 0x76, 0x36, 0xec, 0x0d}} , + {{0x6d, 0x7c, 0x82, 0x32, 0x38, 0x55, 0x57, 0x74, 0x5b, 0x7d, 0xc3, 0xc4, 0xfb, 0x06, 0x29, 0xf0, 0x13, 0x55, 0x54, 0xc6, 0xa7, 0xdc, 0x4c, 0x9f, 0x98, 0x49, 0x20, 0xa8, 0xc3, 0x8d, 0xfa, 0x48}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x87, 0x47, 0x9d, 0xe9, 0x25, 0xd5, 0xe3, 0x47, 0x78, 0xdf, 0x85, 0xa7, 0x85, 0x5e, 0x7a, 0x4c, 0x5f, 0x79, 0x1a, 0xf3, 0xa2, 0xb2, 0x28, 0xa0, 0x9c, 0xdd, 0x30, 0x40, 0xd4, 0x38, 0xbd, 0x28}} , + {{0xfc, 0xbb, 0xd5, 0x78, 0x6d, 0x1d, 0xd4, 0x99, 0xb4, 0xaa, 0x44, 0x44, 0x7a, 0x1b, 0xd8, 0xfe, 0xb4, 0x99, 0xb9, 0xcc, 0xe7, 0xc4, 0xd3, 0x3a, 0x73, 0x83, 0x41, 0x5c, 0x40, 0xd7, 0x2d, 0x55}}}, +{{{0x26, 0xe1, 0x7b, 0x5f, 0xe5, 0xdc, 0x3f, 0x7d, 0xa1, 0xa7, 0x26, 0x44, 0x22, 0x23, 0xc0, 0x8f, 0x7d, 0xf1, 0xb5, 0x11, 0x47, 0x7b, 0x19, 0xd4, 0x75, 0x6f, 0x1e, 0xa5, 0x27, 0xfe, 0xc8, 0x0e}} , + {{0xd3, 0x11, 0x3d, 0xab, 0xef, 0x2c, 0xed, 0xb1, 0x3d, 0x7c, 0x32, 0x81, 0x6b, 0xfe, 0xf8, 0x1c, 0x3c, 0x7b, 0xc0, 0x61, 0xdf, 0xb8, 0x75, 0x76, 0x7f, 0xaa, 0xd8, 0x93, 0xaf, 0x3d, 0xe8, 0x3d}}}, +{{{0xfd, 0x5b, 0x4e, 0x8d, 0xb6, 0x7e, 0x82, 0x9b, 0xef, 0xce, 0x04, 0x69, 0x51, 0x52, 0xff, 0xef, 0xa0, 0x52, 0xb5, 0x79, 0x17, 0x5e, 0x2f, 0xde, 0xd6, 0x3c, 0x2d, 0xa0, 0x43, 0xb4, 0x0b, 0x19}} , + {{0xc0, 0x61, 0x48, 0x48, 0x17, 0xf4, 0x9e, 0x18, 0x51, 0x2d, 0xea, 0x2f, 0xf2, 0xf2, 0xe0, 0xa3, 0x14, 0xb7, 0x8b, 0x3a, 0x30, 0xf5, 0x81, 0xc1, 0x5d, 0x71, 0x39, 0x62, 0x55, 0x1f, 0x60, 0x5a}}}, +{{{0xe5, 0x89, 0x8a, 0x76, 0x6c, 0xdb, 0x4d, 0x0a, 0x5b, 0x72, 0x9d, 0x59, 0x6e, 0x63, 0x63, 0x18, 0x7c, 0xe3, 0xfa, 0xe2, 0xdb, 0xa1, 0x8d, 0xf4, 0xa5, 0xd7, 0x16, 0xb2, 0xd0, 0xb3, 0x3f, 0x39}} , + {{0xce, 0x60, 0x09, 0x6c, 0xf5, 0x76, 0x17, 0x24, 0x80, 0x3a, 0x96, 0xc7, 0x94, 0x2e, 0xf7, 0x6b, 0xef, 0xb5, 0x05, 0x96, 0xef, 0xd3, 0x7b, 0x51, 0xda, 0x05, 0x44, 0x67, 0xbc, 0x07, 0x21, 0x4e}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xe9, 0x73, 0x6f, 0x21, 0xb9, 0xde, 0x22, 0x7d, 0xeb, 0x97, 0x31, 0x10, 0xa3, 0xea, 0xe1, 0xc6, 0x37, 0xeb, 0x8f, 0x43, 0x58, 0xde, 0x41, 0x64, 0x0e, 0x3e, 0x07, 0x99, 0x3d, 0xf1, 0xdf, 0x1e}} , + {{0xf8, 0xad, 0x43, 0xc2, 0x17, 0x06, 0xe2, 0xe4, 0xa9, 0x86, 0xcd, 0x18, 0xd7, 0x78, 0xc8, 0x74, 0x66, 0xd2, 0x09, 0x18, 0xa5, 0xf1, 0xca, 0xa6, 0x62, 0x92, 0xc1, 0xcb, 0x00, 0xeb, 0x42, 0x2e}}}, +{{{0x7b, 0x34, 0x24, 0x4c, 0xcf, 0x38, 0xe5, 0x6c, 0x0a, 0x01, 0x2c, 0x22, 0x0b, 0x24, 0x38, 0xad, 0x24, 0x7e, 0x19, 0xf0, 0x6c, 0xf9, 0x31, 0xf4, 0x35, 0x11, 0xf6, 0x46, 0x33, 0x3a, 0x23, 0x59}} , + {{0x20, 0x0b, 0xa1, 0x08, 0x19, 0xad, 0x39, 0x54, 0xea, 0x3e, 0x23, 0x09, 0xb6, 0xe2, 0xd2, 0xbc, 0x4d, 0xfc, 0x9c, 0xf0, 0x13, 0x16, 0x22, 0x3f, 0xb9, 0xd2, 0x11, 0x86, 0x90, 0x55, 0xce, 0x3c}}}, +{{{0xc4, 0x0b, 0x4b, 0x62, 0x99, 0x37, 0x84, 0x3f, 0x74, 0xa2, 0xf9, 0xce, 0xe2, 0x0b, 0x0f, 0x2a, 0x3d, 0xa3, 0xe3, 0xdb, 0x5a, 0x9d, 0x93, 0xcc, 0xa5, 0xef, 0x82, 0x91, 0x1d, 0xe6, 0x6c, 0x68}} , + {{0xa3, 0x64, 0x17, 0x9b, 0x8b, 0xc8, 0x3a, 0x61, 0xe6, 0x9d, 0xc6, 0xed, 0x7b, 0x03, 0x52, 0x26, 0x9d, 0x3a, 0xb3, 0x13, 0xcc, 0x8a, 0xfd, 0x2c, 0x1a, 0x1d, 0xed, 0x13, 0xd0, 0x55, 0x57, 0x0e}}}, +{{{0x1a, 0xea, 0xbf, 0xfd, 0x4a, 0x3c, 0x8e, 0xec, 0x29, 0x7e, 0x77, 0x77, 0x12, 0x99, 0xd7, 0x84, 0xf9, 0x55, 0x7f, 0xf1, 0x8b, 0xb4, 0xd2, 0x95, 0xa3, 0x8d, 0xf0, 0x8a, 0xa7, 0xeb, 0x82, 0x4b}} , + {{0x2c, 0x28, 0xf4, 0x3a, 0xf6, 0xde, 0x0a, 0xe0, 0x41, 0x44, 0x23, 0xf8, 0x3f, 0x03, 0x64, 0x9f, 0xc3, 0x55, 0x4c, 0xc6, 0xc1, 0x94, 0x1c, 0x24, 0x5d, 0x5f, 0x92, 0x45, 0x96, 0x57, 0x37, 0x14}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xc1, 0xcd, 0x90, 0x66, 0xb9, 0x76, 0xa0, 0x5b, 0xa5, 0x85, 0x75, 0x23, 0xf9, 0x89, 0xa5, 0x82, 0xb2, 0x6f, 0xb1, 0xeb, 0xc4, 0x69, 0x6f, 0x18, 0x5a, 0xed, 0x94, 0x3d, 0x9d, 0xd9, 0x2c, 0x1a}} , + {{0x35, 0xb0, 0xe6, 0x73, 0x06, 0xb7, 0x37, 0xe0, 0xf8, 0xb0, 0x22, 0xe8, 0xd2, 0xed, 0x0b, 0xef, 0xe6, 0xc6, 0x5a, 0x99, 0x9e, 0x1a, 0x9f, 0x04, 0x97, 0xe4, 0x4d, 0x0b, 0xbe, 0xba, 0x44, 0x40}}}, +{{{0xc1, 0x56, 0x96, 0x91, 0x5f, 0x1f, 0xbb, 0x54, 0x6f, 0x88, 0x89, 0x0a, 0xb2, 0xd6, 0x41, 0x42, 0x6a, 0x82, 0xee, 0x14, 0xaa, 0x76, 0x30, 0x65, 0x0f, 0x67, 0x39, 0xa6, 0x51, 0x7c, 0x49, 0x24}} , + {{0x35, 0xa3, 0x78, 0xd1, 0x11, 0x0f, 0x75, 0xd3, 0x70, 0x46, 0xdb, 0x20, 0x51, 0xcb, 0x92, 0x80, 0x54, 0x10, 0x74, 0x36, 0x86, 0xa9, 0xd7, 0xa3, 0x08, 0x78, 0xf1, 0x01, 0x29, 0xf8, 0x80, 0x3b}}}, +{{{0xdb, 0xa7, 0x9d, 0x9d, 0xbf, 0xa0, 0xcc, 0xed, 0x53, 0xa2, 0xa2, 0x19, 0x39, 0x48, 0x83, 0x19, 0x37, 0x58, 0xd1, 0x04, 0x28, 0x40, 0xf7, 0x8a, 0xc2, 0x08, 0xb7, 0xa5, 0x42, 0xcf, 0x53, 0x4c}} , + {{0xa7, 0xbb, 0xf6, 0x8e, 0xad, 0xdd, 0xf7, 0x90, 0xdd, 0x5f, 0x93, 0x89, 0xae, 0x04, 0x37, 0xe6, 0x9a, 0xb7, 0xe8, 0xc0, 0xdf, 0x16, 0x2a, 0xbf, 0xc4, 0x3a, 0x3c, 0x41, 0xd5, 0x89, 0x72, 0x5a}}}, +{{{0x1f, 0x96, 0xff, 0x34, 0x2c, 0x13, 0x21, 0xcb, 0x0a, 0x89, 0x85, 0xbe, 0xb3, 0x70, 0x9e, 0x1e, 0xde, 0x97, 0xaf, 0x96, 0x30, 0xf7, 0x48, 0x89, 0x40, 0x8d, 0x07, 0xf1, 0x25, 0xf0, 0x30, 0x58}} , + {{0x1e, 0xd4, 0x93, 0x57, 0xe2, 0x17, 0xe7, 0x9d, 0xab, 0x3c, 0x55, 0x03, 0x82, 0x2f, 0x2b, 0xdb, 0x56, 0x1e, 0x30, 0x2e, 0x24, 0x47, 0x6e, 0xe6, 0xff, 0x33, 0x24, 0x2c, 0x75, 0x51, 0xd4, 0x67}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2b, 0x06, 0xd9, 0xa1, 0x5d, 0xe1, 0xf4, 0xd1, 0x1e, 0x3c, 0x9a, 0xc6, 0x29, 0x2b, 0x13, 0x13, 0x78, 0xc0, 0xd8, 0x16, 0x17, 0x2d, 0x9e, 0xa9, 0xc9, 0x79, 0x57, 0xab, 0x24, 0x91, 0x92, 0x19}} , + {{0x69, 0xfb, 0xa1, 0x9c, 0xa6, 0x75, 0x49, 0x7d, 0x60, 0x73, 0x40, 0x42, 0xc4, 0x13, 0x0a, 0x95, 0x79, 0x1e, 0x04, 0x83, 0x94, 0x99, 0x9b, 0x1e, 0x0c, 0xe8, 0x1f, 0x54, 0xef, 0xcb, 0xc0, 0x52}}}, +{{{0x14, 0x89, 0x73, 0xa1, 0x37, 0x87, 0x6a, 0x7a, 0xcf, 0x1d, 0xd9, 0x2e, 0x1a, 0x67, 0xed, 0x74, 0xc0, 0xf0, 0x9c, 0x33, 0xdd, 0xdf, 0x08, 0xbf, 0x7b, 0xd1, 0x66, 0xda, 0xe6, 0xc9, 0x49, 0x08}} , + {{0xe9, 0xdd, 0x5e, 0x55, 0xb0, 0x0a, 0xde, 0x21, 0x4c, 0x5a, 0x2e, 0xd4, 0x80, 0x3a, 0x57, 0x92, 0x7a, 0xf1, 0xc4, 0x2c, 0x40, 0xaf, 0x2f, 0xc9, 0x92, 0x03, 0xe5, 0x5a, 0xbc, 0xdc, 0xf4, 0x09}}}, +{{{0xf3, 0xe1, 0x2b, 0x7c, 0x05, 0x86, 0x80, 0x93, 0x4a, 0xad, 0xb4, 0x8f, 0x7e, 0x99, 0x0c, 0xfd, 0xcd, 0xef, 0xd1, 0xff, 0x2c, 0x69, 0x34, 0x13, 0x41, 0x64, 0xcf, 0x3b, 0xd0, 0x90, 0x09, 0x1e}} , + {{0x9d, 0x45, 0xd6, 0x80, 0xe6, 0x45, 0xaa, 0xf4, 0x15, 0xaa, 0x5c, 0x34, 0x87, 0x99, 0xa2, 0x8c, 0x26, 0x84, 0x62, 0x7d, 0xb6, 0x29, 0xc0, 0x52, 0xea, 0xf5, 0x81, 0x18, 0x0f, 0x35, 0xa9, 0x0e}}}, +{{{0xe7, 0x20, 0x72, 0x7c, 0x6d, 0x94, 0x5f, 0x52, 0x44, 0x54, 0xe3, 0xf1, 0xb2, 0xb0, 0x36, 0x46, 0x0f, 0xae, 0x92, 0xe8, 0x70, 0x9d, 0x6e, 0x79, 0xb1, 0xad, 0x37, 0xa9, 0x5f, 0xc0, 0xde, 0x03}} , + {{0x15, 0x55, 0x37, 0xc6, 0x1c, 0x27, 0x1c, 0x6d, 0x14, 0x4f, 0xca, 0xa4, 0xc4, 0x88, 0x25, 0x46, 0x39, 0xfc, 0x5a, 0xe5, 0xfe, 0x29, 0x11, 0x69, 0xf5, 0x72, 0x84, 0x4d, 0x78, 0x9f, 0x94, 0x15}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xec, 0xd3, 0xff, 0x57, 0x0b, 0xb0, 0xb2, 0xdc, 0xf8, 0x4f, 0xe2, 0x12, 0xd5, 0x36, 0xbe, 0x6b, 0x09, 0x43, 0x6d, 0xa3, 0x4d, 0x90, 0x2d, 0xb8, 0x74, 0xe8, 0x71, 0x45, 0x19, 0x8b, 0x0c, 0x6a}} , + {{0xb8, 0x42, 0x1c, 0x03, 0xad, 0x2c, 0x03, 0x8e, 0xac, 0xd7, 0x98, 0x29, 0x13, 0xc6, 0x02, 0x29, 0xb5, 0xd4, 0xe7, 0xcf, 0xcc, 0x8b, 0x83, 0xec, 0x35, 0xc7, 0x9c, 0x74, 0xb7, 0xad, 0x85, 0x5f}}}, +{{{0x78, 0x84, 0xe1, 0x56, 0x45, 0x69, 0x68, 0x5a, 0x4f, 0xb8, 0xb1, 0x29, 0xff, 0x33, 0x03, 0x31, 0xb7, 0xcb, 0x96, 0x25, 0xe6, 0xe6, 0x41, 0x98, 0x1a, 0xbb, 0x03, 0x56, 0xf2, 0xb2, 0x91, 0x34}} , + {{0x2c, 0x6c, 0xf7, 0x66, 0xa4, 0x62, 0x6b, 0x39, 0xb3, 0xba, 0x65, 0xd3, 0x1c, 0xf8, 0x11, 0xaa, 0xbe, 0xdc, 0x80, 0x59, 0x87, 0xf5, 0x7b, 0xe5, 0xe3, 0xb3, 0x3e, 0x39, 0xda, 0xbe, 0x88, 0x09}}}, +{{{0x8b, 0xf1, 0xa0, 0xf5, 0xdc, 0x29, 0xb4, 0xe2, 0x07, 0xc6, 0x7a, 0x00, 0xd0, 0x89, 0x17, 0x51, 0xd4, 0xbb, 0xd4, 0x22, 0xea, 0x7e, 0x7d, 0x7c, 0x24, 0xea, 0xf2, 0xe8, 0x22, 0x12, 0x95, 0x06}} , + {{0xda, 0x7c, 0xa4, 0x0c, 0xf4, 0xba, 0x6e, 0xe1, 0x89, 0xb5, 0x59, 0xca, 0xf1, 0xc0, 0x29, 0x36, 0x09, 0x44, 0xe2, 0x7f, 0xd1, 0x63, 0x15, 0x99, 0xea, 0x25, 0xcf, 0x0c, 0x9d, 0xc0, 0x44, 0x6f}}}, +{{{0x1d, 0x86, 0x4e, 0xcf, 0xf7, 0x37, 0x10, 0x25, 0x8f, 0x12, 0xfb, 0x19, 0xfb, 0xe0, 0xed, 0x10, 0xc8, 0xe2, 0xf5, 0x75, 0xb1, 0x33, 0xc0, 0x96, 0x0d, 0xfb, 0x15, 0x6c, 0x0d, 0x07, 0x5f, 0x05}} , + {{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}} diff --git a/aosp/external/openssh/groupaccess.c b/aosp/external/openssh/groupaccess.c new file mode 100644 index 0000000000000000000000000000000000000000..80d3019152c283b8d8315dfebab852ec4df27119 --- /dev/null +++ b/aosp/external/openssh/groupaccess.c @@ -0,0 +1,134 @@ +/* $OpenBSD: groupaccess.c,v 1.17 2019/03/06 22:14:23 dtucker Exp $ */ +/* + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "groupaccess.h" +#include "match.h" +#include "log.h" + +static int ngroups; +static char **groups_byname; + +/* + * Initialize group access list for user with primary (base) and + * supplementary groups. Return the number of groups in the list. + */ +int +ga_init(const char *user, gid_t base) +{ + gid_t *groups_bygid; + int i, j, retry = 0; + struct group *gr; + + if (ngroups > 0) + ga_free(); + + ngroups = NGROUPS_MAX; +#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) + ngroups = MAX(NGROUPS_MAX, sysconf(_SC_NGROUPS_MAX)); +#endif + + groups_bygid = xcalloc(ngroups, sizeof(*groups_bygid)); + while (getgrouplist(user, base, groups_bygid, &ngroups) == -1) { + if (retry++ > 0) + fatal("getgrouplist: groups list too small"); + groups_bygid = xreallocarray(groups_bygid, ngroups, + sizeof(*groups_bygid)); + } + groups_byname = xcalloc(ngroups, sizeof(*groups_byname)); + + for (i = 0, j = 0; i < ngroups; i++) + if ((gr = getgrgid(groups_bygid[i])) != NULL) + groups_byname[j++] = xstrdup(gr->gr_name); + free(groups_bygid); + return (ngroups = j); +} + +/* + * Return 1 if one of user's groups is contained in groups. + * Return 0 otherwise. Use match_pattern() for string comparison. + */ +int +ga_match(char * const *groups, int n) +{ + int i, j; + + for (i = 0; i < ngroups; i++) + for (j = 0; j < n; j++) + if (match_pattern(groups_byname[i], groups[j])) + return 1; + return 0; +} + +/* + * Return 1 if one of user's groups matches group_pattern list. + * Return 0 on negated or no match. + */ +int +ga_match_pattern_list(const char *group_pattern) +{ + int i, found = 0; + + for (i = 0; i < ngroups; i++) { + switch (match_usergroup_pattern_list(groups_byname[i], + group_pattern)) { + case -1: + return 0; /* Negated match wins */ + case 0: + continue; + case 1: + found = 1; + } + } + return found; +} + +/* + * Free memory allocated for group access list. + */ +void +ga_free(void) +{ + int i; + + if (ngroups > 0) { + for (i = 0; i < ngroups; i++) + free(groups_byname[i]); + ngroups = 0; + free(groups_byname); + groups_byname = NULL; + } +} diff --git a/aosp/external/openssh/groupaccess.h b/aosp/external/openssh/groupaccess.h new file mode 100644 index 0000000000000000000000000000000000000000..000578e76461df6ccbef1dcad35f3399f40ac68d --- /dev/null +++ b/aosp/external/openssh/groupaccess.h @@ -0,0 +1,35 @@ +/* $OpenBSD: groupaccess.h,v 1.8 2008/07/04 03:44:59 djm Exp $ */ + +/* + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GROUPACCESS_H +#define GROUPACCESS_H + +int ga_init(const char *, gid_t); +int ga_match(char * const *, int); +int ga_match_pattern_list(const char *); +void ga_free(void); + +#endif diff --git a/aosp/external/openssh/gss-genr.c b/aosp/external/openssh/gss-genr.c new file mode 100644 index 0000000000000000000000000000000000000000..2cd695e543c433cc02496f7f84360b36d7eac97a --- /dev/null +++ b/aosp/external/openssh/gss-genr.c @@ -0,0 +1,303 @@ +/* $OpenBSD: gss-genr.c,v 1.28 2021/01/27 10:05:28 djm Exp $ */ + +/* + * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef GSSAPI + +#include + +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "log.h" +#include "ssh2.h" + +#include "ssh-gss.h" + +/* sshbuf_get for gss_buffer_desc */ +int +ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) +{ + int r; + u_char *p; + size_t len; + + if ((r = sshbuf_get_string(b, &p, &len)) != 0) + return r; + g->value = p; + g->length = len; + return 0; +} + +/* Check that the OID in a data stream matches that in the context */ +int +ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +{ + return (ctx != NULL && ctx->oid != GSS_C_NO_OID && + ctx->oid->length == len && + memcmp(ctx->oid->elements, data, len) == 0); +} + +/* Set the contexts OID from a data stream */ +void +ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) +{ + if (ctx->oid != GSS_C_NO_OID) { + free(ctx->oid->elements); + free(ctx->oid); + } + ctx->oid = xcalloc(1, sizeof(gss_OID_desc)); + ctx->oid->length = len; + ctx->oid->elements = xmalloc(len); + memcpy(ctx->oid->elements, data, len); +} + +/* Set the contexts OID */ +void +ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) +{ + ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); +} + +/* All this effort to report an error ... */ +void +ssh_gssapi_error(Gssctxt *ctxt) +{ + char *s; + + s = ssh_gssapi_last_error(ctxt, NULL, NULL); + debug("%s", s); + free(s); +} + +char * +ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, + OM_uint32 *minor_status) +{ + OM_uint32 lmin; + gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; + OM_uint32 ctx; + struct sshbuf *b; + char *ret; + int r; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + if (major_status != NULL) + *major_status = ctxt->major; + if (minor_status != NULL) + *minor_status = ctxt->minor; + + ctx = 0; + /* The GSSAPI error */ + do { + gss_display_status(&lmin, ctxt->major, + GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); + + if ((r = sshbuf_put(b, msg.value, msg.length)) != 0 || + (r = sshbuf_put_u8(b, '\n')) != 0) + fatal_fr(r, "assemble GSS_CODE"); + + gss_release_buffer(&lmin, &msg); + } while (ctx != 0); + + /* The mechanism specific error */ + do { + gss_display_status(&lmin, ctxt->minor, + GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); + + if ((r = sshbuf_put(b, msg.value, msg.length)) != 0 || + (r = sshbuf_put_u8(b, '\n')) != 0) + fatal_fr(r, "assemble MECH_CODE"); + + gss_release_buffer(&lmin, &msg); + } while (ctx != 0); + + if ((r = sshbuf_put_u8(b, '\n')) != 0) + fatal_fr(r, "assemble newline"); + ret = xstrdup((const char *)sshbuf_ptr(b)); + sshbuf_free(b); + return (ret); +} + +/* + * Initialise our GSSAPI context. We use this opaque structure to contain all + * of the data which both the client and server need to persist across + * {accept,init}_sec_context calls, so that when we do it from the userauth + * stuff life is a little easier + */ +void +ssh_gssapi_build_ctx(Gssctxt **ctx) +{ + *ctx = xcalloc(1, sizeof (Gssctxt)); + (*ctx)->context = GSS_C_NO_CONTEXT; + (*ctx)->name = GSS_C_NO_NAME; + (*ctx)->oid = GSS_C_NO_OID; + (*ctx)->creds = GSS_C_NO_CREDENTIAL; + (*ctx)->client = GSS_C_NO_NAME; + (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; +} + +/* Delete our context, providing it has been built correctly */ +void +ssh_gssapi_delete_ctx(Gssctxt **ctx) +{ + OM_uint32 ms; + + if ((*ctx) == NULL) + return; + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); + if ((*ctx)->name != GSS_C_NO_NAME) + gss_release_name(&ms, &(*ctx)->name); + if ((*ctx)->oid != GSS_C_NO_OID) { + free((*ctx)->oid->elements); + free((*ctx)->oid); + (*ctx)->oid = GSS_C_NO_OID; + } + if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms, &(*ctx)->creds); + if ((*ctx)->client != GSS_C_NO_NAME) + gss_release_name(&ms, &(*ctx)->client); + if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms, &(*ctx)->client_creds); + + free(*ctx); + *ctx = NULL; +} + +/* + * Wrapper to init_sec_context + * Requires that the context contains: + * oid + * server name (from ssh_gssapi_import_name) + */ +OM_uint32 +ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, + gss_buffer_desc* send_tok, OM_uint32 *flags) +{ + int deleg_flag = 0; + + if (deleg_creds) { + deleg_flag = GSS_C_DELEG_FLAG; + debug("Delegating credentials"); + } + + ctx->major = gss_init_sec_context(&ctx->minor, + GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx); + + return (ctx->major); +} + +/* Create a service name for the given host */ +OM_uint32 +ssh_gssapi_import_name(Gssctxt *ctx, const char *host) +{ + gss_buffer_desc gssbuf; + char *val; + + xasprintf(&val, "host@%s", host); + gssbuf.value = val; + gssbuf.length = strlen(gssbuf.value); + + if ((ctx->major = gss_import_name(&ctx->minor, + &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) + ssh_gssapi_error(ctx); + + free(gssbuf.value); + return (ctx->major); +} + +OM_uint32 +ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) +{ + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); + + return (ctx->major); +} + +void +ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, + const char *context, const struct sshbuf *session_id) +{ + int r; + + sshbuf_reset(b); + if ((r = sshbuf_put_stringb(b, session_id)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, user)) != 0 || + (r = sshbuf_put_cstring(b, service)) != 0 || + (r = sshbuf_put_cstring(b, context)) != 0) + fatal_fr(r, "assemble buildmic"); +} + +int +ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) +{ + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && + (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) + return 0; /* false */ + + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); + gss_release_buffer(&minor, &token); + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor, &(*ctx)->context, + GSS_C_NO_BUFFER); + } + + if (GSS_ERROR(major)) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); +} + +#endif /* GSSAPI */ diff --git a/aosp/external/openssh/gss-serv-krb5.c b/aosp/external/openssh/gss-serv-krb5.c new file mode 100644 index 0000000000000000000000000000000000000000..a151bc1e4ad2f2db5d7c22125029c41b531612a5 --- /dev/null +++ b/aosp/external/openssh/gss-serv-krb5.c @@ -0,0 +1,211 @@ +/* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */ + +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef GSSAPI +#ifdef KRB5 + +#include + +#include +#include + +#include "xmalloc.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" + +#include "ssh-gss.h" + +extern ServerOptions options; + +#ifdef HEIMDAL +# include +#endif +#ifdef HAVE_GSSAPI_KRB5_H +# include +#elif HAVE_GSSAPI_GSSAPI_KRB5_H +# include +#endif + +static krb5_context krb_context = NULL; + +/* Initialise the krb5 library, for the stuff that GSSAPI won't do */ + +static int +ssh_gssapi_krb5_init(void) +{ + krb5_error_code problem; + + if (krb_context != NULL) + return 1; + + problem = krb5_init_context(&krb_context); + if (problem) { + logit("Cannot initialize krb5 context"); + return 0; + } + + return 1; +} + +/* Check if this user is OK to login. This only works with krb5 - other + * GSSAPI mechanisms will need their own. + * Returns true if the user is OK to log in, otherwise returns 0 + */ + +static int +ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) +{ + krb5_principal princ; + int retval; + const char *errmsg; + + if (ssh_gssapi_krb5_init() == 0) + return 0; + + if ((retval = krb5_parse_name(krb_context, client->exportedname.value, + &princ))) { + errmsg = krb5_get_error_message(krb_context, retval); + logit("krb5_parse_name(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); + return 0; + } + if (krb5_kuserok(krb_context, princ, name)) { + retval = 1; + logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", + name, (char *)client->displayname.value); + } else + retval = 0; + + krb5_free_principal(krb_context, princ); + return retval; +} + + +/* This writes out any forwarded credentials from the structure populated + * during userauth. Called after we have setuid to the user */ + +static void +ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) +{ + krb5_ccache ccache; + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; + int len; + const char *errmsg; + + if (client->creds == NULL) { + debug("No credentials stored"); + return; + } + + if (ssh_gssapi_krb5_init() == 0) + return; + +#ifdef HEIMDAL +# ifdef HAVE_KRB5_CC_NEW_UNIQUE + if ((problem = krb5_cc_new_unique(krb_context, krb5_fcc_ops.prefix, + NULL, &ccache)) != 0) { + errmsg = krb5_get_error_message(krb_context, problem); + logit("krb5_cc_new_unique(): %.100s", errmsg); +# else + if ((problem = krb5_cc_gen_new(krb_context, &krb5_fcc_ops, &ccache))) { + logit("krb5_cc_gen_new(): %.100s", + krb5_get_err_text(krb_context, problem)); +# endif + krb5_free_error_message(krb_context, errmsg); + return; + } +#else + if ((problem = ssh_krb5_cc_gen(krb_context, &ccache))) { + errmsg = krb5_get_error_message(krb_context, problem); + logit("ssh_krb5_cc_gen(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); + return; + } +#endif /* #ifdef HEIMDAL */ + + if ((problem = krb5_parse_name(krb_context, + client->exportedname.value, &princ))) { + errmsg = krb5_get_error_message(krb_context, problem); + logit("krb5_parse_name(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); + return; + } + + if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { + errmsg = krb5_get_error_message(krb_context, problem); + logit("krb5_cc_initialize(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); + krb5_free_principal(krb_context, princ); + krb5_cc_destroy(krb_context, ccache); + return; + } + + krb5_free_principal(krb_context, princ); + + if ((maj_status = gss_krb5_copy_ccache(&min_status, + client->creds, ccache))) { + logit("gss_krb5_copy_ccache() failed"); + krb5_cc_destroy(krb_context, ccache); + return; + } + + client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); + client->store.envvar = "KRB5CCNAME"; + len = strlen(client->store.filename) + 6; + client->store.envval = xmalloc(len); + snprintf(client->store.envval, len, "FILE:%s", client->store.filename); + +#ifdef USE_PAM + if (options.use_pam) + do_pam_putenv(client->store.envvar, client->store.envval); +#endif + + krb5_cc_close(krb_context, ccache); + + return; +} + +ssh_gssapi_mech gssapi_kerberos_mech = { + "toWM5Slw5Ew8Mqkay+al2g==", + "Kerberos", + {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}, + NULL, + &ssh_gssapi_krb5_userok, + NULL, + &ssh_gssapi_krb5_storecreds +}; + +#endif /* KRB5 */ + +#endif /* GSSAPI */ diff --git a/aosp/external/openssh/gss-serv.c b/aosp/external/openssh/gss-serv.c new file mode 100644 index 0000000000000000000000000000000000000000..b5d4bb2d18b22803e2d222eb6dd61a73eee92472 --- /dev/null +++ b/aosp/external/openssh/gss-serv.c @@ -0,0 +1,404 @@ +/* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */ + +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef GSSAPI + +#include + +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "log.h" +#include "channels.h" +#include "session.h" +#include "misc.h" +#include "servconf.h" + +#include "ssh-gss.h" + +extern ServerOptions options; + +static ssh_gssapi_client gssapi_client = + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, + GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; + +ssh_gssapi_mech gssapi_null_mech = + { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; + +#ifdef KRB5 +extern ssh_gssapi_mech gssapi_kerberos_mech; +#endif + +ssh_gssapi_mech* supported_mechs[]= { +#ifdef KRB5 + &gssapi_kerberos_mech, +#endif + &gssapi_null_mech, +}; + +/* + * ssh_gssapi_supported_oids() can cause sandbox violations, so prepare the + * list of supported mechanisms before privsep is set up. + */ +static gss_OID_set supported_oids; + +void +ssh_gssapi_prepare_supported_oids(void) +{ + ssh_gssapi_supported_oids(&supported_oids); +} + +OM_uint32 +ssh_gssapi_test_oid_supported(OM_uint32 *ms, gss_OID member, int *present) +{ + if (supported_oids == NULL) + ssh_gssapi_prepare_supported_oids(); + return gss_test_oid_set_member(ms, member, supported_oids, present); +} + +/* + * Acquire credentials for a server running on the current host. + * Requires that the context structure contains a valid OID + */ + +/* Returns a GSSAPI error code */ +/* Privileged (called from ssh_gssapi_server_ctx) */ +static OM_uint32 +ssh_gssapi_acquire_cred(Gssctxt *ctx) +{ + OM_uint32 status; + char lname[NI_MAXHOST]; + gss_OID_set oidset; + + if (options.gss_strict_acceptor) { + gss_create_empty_oid_set(&status, &oidset); + gss_add_oid_set_member(&status, ctx->oid, &oidset); + + if (gethostname(lname, MAXHOSTNAMELEN)) { + gss_release_oid_set(&status, &oidset); + return (-1); + } + + if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) { + gss_release_oid_set(&status, &oidset); + return (ctx->major); + } + + if ((ctx->major = gss_acquire_cred(&ctx->minor, + ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds, + NULL, NULL))) + ssh_gssapi_error(ctx); + + gss_release_oid_set(&status, &oidset); + return (ctx->major); + } else { + ctx->name = GSS_C_NO_NAME; + ctx->creds = GSS_C_NO_CREDENTIAL; + } + return GSS_S_COMPLETE; +} + +/* Privileged */ +OM_uint32 +ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) +{ + if (*ctx) + ssh_gssapi_delete_ctx(ctx); + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + return (ssh_gssapi_acquire_cred(*ctx)); +} + +/* Unprivileged */ +void +ssh_gssapi_supported_oids(gss_OID_set *oidset) +{ + int i = 0; + OM_uint32 min_status; + int present; + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status, oidset); + gss_indicate_mechs(&min_status, &supported); + + while (supported_mechs[i]->name != NULL) { + if (GSS_ERROR(gss_test_oid_set_member(&min_status, + &supported_mechs[i]->oid, supported, &present))) + present = 0; + if (present) + gss_add_oid_set_member(&min_status, + &supported_mechs[i]->oid, oidset); + i++; + } + + gss_release_oid_set(&min_status, &supported); +} + + +/* Wrapper around accept_sec_context + * Requires that the context contains: + * oid + * credentials (from ssh_gssapi_acquire_cred) + */ +/* Privileged */ +OM_uint32 +ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok, + gss_buffer_desc *send_tok, OM_uint32 *flags) +{ + OM_uint32 status; + gss_OID mech; + + ctx->major = gss_accept_sec_context(&ctx->minor, + &ctx->context, ctx->creds, recv_tok, + GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech, + send_tok, flags, NULL, &ctx->client_creds); + + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx); + + if (ctx->client_creds) + debug("Received some client credentials"); + else + debug("Got no client credentials"); + + status = ctx->major; + + /* Now, if we're complete and we have the right flags, then + * we flag the user as also having been authenticated + */ + + if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && + (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) { + if (ssh_gssapi_getclient(ctx, &gssapi_client)) + fatal("Couldn't convert client name"); + } + + return (status); +} + +/* + * This parses an exported name, extracting the mechanism specific portion + * to use for ACL checking. It verifies that the name belongs the mechanism + * originally selected. + */ +static OM_uint32 +ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name) +{ + u_char *tok; + OM_uint32 offset; + OM_uint32 oidl; + + tok = ename->value; + + /* + * Check that ename is long enough for all of the fixed length + * header, and that the initial ID bytes are correct + */ + + if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0) + return GSS_S_FAILURE; + + /* + * Extract the OID, and check it. Here GSSAPI breaks with tradition + * and does use the OID type and length bytes. To confuse things + * there are two lengths - the first including these, and the + * second without. + */ + + oidl = get_u16(tok+2); /* length including next two bytes */ + oidl = oidl-2; /* turn it into the _real_ length of the variable OID */ + + /* + * Check the BER encoding for correct type and length, that the + * string is long enough and that the OID matches that in our context + */ + if (tok[4] != 0x06 || tok[5] != oidl || + ename->length < oidl+6 || + !ssh_gssapi_check_oid(ctx, tok+6, oidl)) + return GSS_S_FAILURE; + + offset = oidl+6; + + if (ename->length < offset+4) + return GSS_S_FAILURE; + + name->length = get_u32(tok+offset); + offset += 4; + + if (UINT_MAX - offset < name->length) + return GSS_S_FAILURE; + if (ename->length < offset+name->length) + return GSS_S_FAILURE; + + name->value = xmalloc(name->length+1); + memcpy(name->value, tok+offset, name->length); + ((char *)name->value)[name->length] = 0; + + return GSS_S_COMPLETE; +} + +/* Extract the client details from a given context. This can only reliably + * be called once for a context */ + +/* Privileged (called from accept_secure_ctx) */ +OM_uint32 +ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) +{ + int i = 0; + + gss_buffer_desc ename; + + client->mech = NULL; + + while (supported_mechs[i]->name != NULL) { + if (supported_mechs[i]->oid.length == ctx->oid->length && + (memcmp(supported_mechs[i]->oid.elements, + ctx->oid->elements, ctx->oid->length) == 0)) + client->mech = supported_mechs[i]; + i++; + } + + if (client->mech == NULL) + return GSS_S_FAILURE; + + if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, + &client->displayname, NULL))) { + ssh_gssapi_error(ctx); + return (ctx->major); + } + + if ((ctx->major = gss_export_name(&ctx->minor, ctx->client, + &ename))) { + ssh_gssapi_error(ctx); + return (ctx->major); + } + + if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename, + &client->exportedname))) { + return (ctx->major); + } + + /* We can't copy this structure, so we just move the pointer to it */ + client->creds = ctx->client_creds; + ctx->client_creds = GSS_C_NO_CREDENTIAL; + return (ctx->major); +} + +/* As user - called on fatal/exit */ +void +ssh_gssapi_cleanup_creds(void) +{ + if (gssapi_client.store.filename != NULL) { + /* Unlink probably isn't sufficient */ + debug("removing gssapi cred file\"%s\"", + gssapi_client.store.filename); + unlink(gssapi_client.store.filename); + } +} + +/* As user */ +void +ssh_gssapi_storecreds(void) +{ + if (gssapi_client.mech && gssapi_client.mech->storecreds) { + (*gssapi_client.mech->storecreds)(&gssapi_client); + } else + debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); +} + +/* This allows GSSAPI methods to do things to the child's environment based + * on the passed authentication process and credentials. + */ +/* As user */ +void +ssh_gssapi_do_child(char ***envp, u_int *envsizep) +{ + + if (gssapi_client.store.envvar != NULL && + gssapi_client.store.envval != NULL) { + debug("Setting %s to %s", gssapi_client.store.envvar, + gssapi_client.store.envval); + child_set_env(envp, envsizep, gssapi_client.store.envvar, + gssapi_client.store.envval); + } +} + +/* Privileged */ +int +ssh_gssapi_userok(char *user) +{ + OM_uint32 lmin; + + if (gssapi_client.exportedname.length == 0 || + gssapi_client.exportedname.value == NULL) { + debug("No suitable client data"); + return 0; + } + if (gssapi_client.mech && gssapi_client.mech->userok) + if ((*gssapi_client.mech->userok)(&gssapi_client, user)) + return 1; + else { + /* Destroy delegated credentials if userok fails */ + gss_release_buffer(&lmin, &gssapi_client.displayname); + gss_release_buffer(&lmin, &gssapi_client.exportedname); + gss_release_cred(&lmin, &gssapi_client.creds); + explicit_bzero(&gssapi_client, + sizeof(ssh_gssapi_client)); + return 0; + } + else + debug("ssh_gssapi_userok: Unknown GSSAPI mechanism"); + return (0); +} + +/* Privileged */ +OM_uint32 +ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +{ + ctx->major = gss_verify_mic(&ctx->minor, ctx->context, + gssbuf, gssmic, NULL); + + return (ctx->major); +} + +/* Privileged */ +const char *ssh_gssapi_displayname(void) +{ + if (gssapi_client.displayname.length == 0 || + gssapi_client.displayname.value == NULL) + return NULL; + return (char *)gssapi_client.displayname.value; +} + +#endif diff --git a/aosp/external/openssh/hash.c b/aosp/external/openssh/hash.c new file mode 100644 index 0000000000000000000000000000000000000000..b4f8f6c50d5e22006abe66c0ffd247a69d219fa6 --- /dev/null +++ b/aosp/external/openssh/hash.c @@ -0,0 +1,43 @@ +/* $OpenBSD: hash.c,v 1.6 2019/11/29 00:11:21 djm Exp $ */ +/* + * Public domain. Author: Christian Weisgerber + * API compatible reimplementation of function from nacl + */ + +#include "includes.h" + +#include "crypto_api.h" + +#include + +#ifdef WITH_OPENSSL +#include + +int +crypto_hash_sha512(unsigned char *out, const unsigned char *in, + unsigned long long inlen) +{ + + if (!EVP_Digest(in, inlen, out, NULL, EVP_sha512(), NULL)) + return -1; + return 0; +} + +#else +# ifdef HAVE_SHA2_H +# include +# endif + +int +crypto_hash_sha512(unsigned char *out, const unsigned char *in, + unsigned long long inlen) +{ + + SHA2_CTX ctx; + + SHA512Init(&ctx); + SHA512Update(&ctx, in, inlen); + SHA512Final(out, &ctx); + return 0; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/hmac.c b/aosp/external/openssh/hmac.c new file mode 100644 index 0000000000000000000000000000000000000000..7b588019e74b07b594fa7da4b5d9683c4e78122c --- /dev/null +++ b/aosp/external/openssh/hmac.c @@ -0,0 +1,198 @@ +/* $OpenBSD: hmac.c,v 1.14 2020/02/26 13:40:09 jsg Exp $ */ +/* + * Copyright (c) 2014 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include +#include + +#include "sshbuf.h" +#include "digest.h" +#include "hmac.h" + +struct ssh_hmac_ctx { + int alg; + struct ssh_digest_ctx *ictx; + struct ssh_digest_ctx *octx; + struct ssh_digest_ctx *digest; + u_char *buf; + size_t buf_len; +}; + +size_t +ssh_hmac_bytes(int alg) +{ + return ssh_digest_bytes(alg); +} + +struct ssh_hmac_ctx * +ssh_hmac_start(int alg) +{ + struct ssh_hmac_ctx *ret; + + if ((ret = calloc(1, sizeof(*ret))) == NULL) + return NULL; + ret->alg = alg; + if ((ret->ictx = ssh_digest_start(alg)) == NULL || + (ret->octx = ssh_digest_start(alg)) == NULL || + (ret->digest = ssh_digest_start(alg)) == NULL) + goto fail; + ret->buf_len = ssh_digest_blocksize(ret->ictx); + if ((ret->buf = calloc(1, ret->buf_len)) == NULL) + goto fail; + return ret; +fail: + ssh_hmac_free(ret); + return NULL; +} + +int +ssh_hmac_init(struct ssh_hmac_ctx *ctx, const void *key, size_t klen) +{ + size_t i; + + /* reset ictx and octx if no is key given */ + if (key != NULL) { + /* truncate long keys */ + if (klen <= ctx->buf_len) + memcpy(ctx->buf, key, klen); + else if (ssh_digest_memory(ctx->alg, key, klen, ctx->buf, + ctx->buf_len) < 0) + return -1; + for (i = 0; i < ctx->buf_len; i++) + ctx->buf[i] ^= 0x36; + if (ssh_digest_update(ctx->ictx, ctx->buf, ctx->buf_len) < 0) + return -1; + for (i = 0; i < ctx->buf_len; i++) + ctx->buf[i] ^= 0x36 ^ 0x5c; + if (ssh_digest_update(ctx->octx, ctx->buf, ctx->buf_len) < 0) + return -1; + explicit_bzero(ctx->buf, ctx->buf_len); + } + /* start with ictx */ + if (ssh_digest_copy_state(ctx->ictx, ctx->digest) < 0) + return -1; + return 0; +} + +int +ssh_hmac_update(struct ssh_hmac_ctx *ctx, const void *m, size_t mlen) +{ + return ssh_digest_update(ctx->digest, m, mlen); +} + +int +ssh_hmac_update_buffer(struct ssh_hmac_ctx *ctx, const struct sshbuf *b) +{ + return ssh_digest_update_buffer(ctx->digest, b); +} + +int +ssh_hmac_final(struct ssh_hmac_ctx *ctx, u_char *d, size_t dlen) +{ + size_t len; + + len = ssh_digest_bytes(ctx->alg); + if (dlen < len || + ssh_digest_final(ctx->digest, ctx->buf, len)) + return -1; + /* switch to octx */ + if (ssh_digest_copy_state(ctx->octx, ctx->digest) < 0 || + ssh_digest_update(ctx->digest, ctx->buf, len) < 0 || + ssh_digest_final(ctx->digest, d, dlen) < 0) + return -1; + return 0; +} + +void +ssh_hmac_free(struct ssh_hmac_ctx *ctx) +{ + if (ctx != NULL) { + ssh_digest_free(ctx->ictx); + ssh_digest_free(ctx->octx); + ssh_digest_free(ctx->digest); + if (ctx->buf) { + explicit_bzero(ctx->buf, ctx->buf_len); + free(ctx->buf); + } + freezero(ctx, sizeof(*ctx)); + } +} + +#ifdef TEST + +/* cc -DTEST hmac.c digest.c buffer.c cleanup.c fatal.c log.c xmalloc.c -lcrypto */ +static void +hmac_test(void *key, size_t klen, void *m, size_t mlen, u_char *e, size_t elen) +{ + struct ssh_hmac_ctx *ctx; + size_t i; + u_char digest[16]; + + if ((ctx = ssh_hmac_start(SSH_DIGEST_MD5)) == NULL) + printf("ssh_hmac_start failed"); + if (ssh_hmac_init(ctx, key, klen) < 0 || + ssh_hmac_update(ctx, m, mlen) < 0 || + ssh_hmac_final(ctx, digest, sizeof(digest)) < 0) + printf("ssh_hmac_xxx failed"); + ssh_hmac_free(ctx); + + if (memcmp(e, digest, elen)) { + for (i = 0; i < elen; i++) + printf("[%zu] %2.2x %2.2x\n", i, e[i], digest[i]); + printf("mismatch\n"); + } else + printf("ok\n"); +} + +int +main(int argc, char **argv) +{ + /* try test vectors from RFC 2104 */ + + u_char key1[16] = { + 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, + 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb }; + u_char *data1 = "Hi There"; + u_char dig1[16] = { + 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c, + 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d }; + + u_char *key2 = "Jefe"; + u_char *data2 = "what do ya want for nothing?"; + u_char dig2[16] = { + 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03, + 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 }; + + u_char key3[16]; + u_char data3[50]; + u_char dig3[16] = { + 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88, + 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 }; + memset(key3, 0xaa, sizeof(key3)); + memset(data3, 0xdd, sizeof(data3)); + + hmac_test(key1, sizeof(key1), data1, strlen(data1), dig1, sizeof(dig1)); + hmac_test(key2, strlen(key2), data2, strlen(data2), dig2, sizeof(dig2)); + hmac_test(key3, sizeof(key3), data3, sizeof(data3), dig3, sizeof(dig3)); + + return 0; +} + +#endif diff --git a/aosp/external/openssh/hmac.h b/aosp/external/openssh/hmac.h new file mode 100644 index 0000000000000000000000000000000000000000..42b33d00288984f7037d70a4326c63767ab62151 --- /dev/null +++ b/aosp/external/openssh/hmac.h @@ -0,0 +1,38 @@ +/* $OpenBSD: hmac.h,v 1.9 2014/06/24 01:13:21 djm Exp $ */ +/* + * Copyright (c) 2014 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HMAC_H +#define _HMAC_H + +/* Returns the algorithm's digest length in bytes or 0 for invalid algorithm */ +size_t ssh_hmac_bytes(int alg); + +struct sshbuf; +struct ssh_hmac_ctx; +struct ssh_hmac_ctx *ssh_hmac_start(int alg); + +/* Sets the state of the HMAC or resets the state if key == NULL */ +int ssh_hmac_init(struct ssh_hmac_ctx *ctx, const void *key, size_t klen) + __attribute__((__bounded__(__buffer__, 2, 3))); +int ssh_hmac_update(struct ssh_hmac_ctx *ctx, const void *m, size_t mlen) + __attribute__((__bounded__(__buffer__, 2, 3))); +int ssh_hmac_update_buffer(struct ssh_hmac_ctx *ctx, const struct sshbuf *b); +int ssh_hmac_final(struct ssh_hmac_ctx *ctx, u_char *d, size_t dlen) + __attribute__((__bounded__(__buffer__, 2, 3))); +void ssh_hmac_free(struct ssh_hmac_ctx *ctx); + +#endif /* _HMAC_H */ diff --git a/aosp/external/openssh/hostfile.c b/aosp/external/openssh/hostfile.c new file mode 100644 index 0000000000000000000000000000000000000000..bd49e3ac7c48b12f09316a028e471019b7ea3b4c --- /dev/null +++ b/aosp/external/openssh/hostfile.c @@ -0,0 +1,937 @@ +/* $OpenBSD: hostfile.c,v 1.93 2022/01/06 22:02:52 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for manipulating the known hosts files. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. + * Copyright (c) 1999 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "match.h" +#include "sshkey.h" +#include "hostfile.h" +#include "log.h" +#include "misc.h" +#include "pathnames.h" +#include "ssherr.h" +#include "digest.h" +#include "hmac.h" +#include "sshbuf.h" + +/* XXX hmac is too easy to dictionary attack; use bcrypt? */ + +static int +extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len) +{ + char *p, *b64salt; + u_int b64len; + int ret; + + if (l < sizeof(HASH_MAGIC) - 1) { + debug2("extract_salt: string too short"); + return (-1); + } + if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { + debug2("extract_salt: invalid magic identifier"); + return (-1); + } + s += sizeof(HASH_MAGIC) - 1; + l -= sizeof(HASH_MAGIC) - 1; + if ((p = memchr(s, HASH_DELIM, l)) == NULL) { + debug2("extract_salt: missing salt termination character"); + return (-1); + } + + b64len = p - s; + /* Sanity check */ + if (b64len == 0 || b64len > 1024) { + debug2("extract_salt: bad encoded salt length %u", b64len); + return (-1); + } + b64salt = xmalloc(1 + b64len); + memcpy(b64salt, s, b64len); + b64salt[b64len] = '\0'; + + ret = __b64_pton(b64salt, salt, salt_len); + free(b64salt); + if (ret == -1) { + debug2("extract_salt: salt decode error"); + return (-1); + } + if (ret != (int)ssh_hmac_bytes(SSH_DIGEST_SHA1)) { + debug2("extract_salt: expected salt len %zd, got %d", + ssh_hmac_bytes(SSH_DIGEST_SHA1), ret); + return (-1); + } + + return (0); +} + +char * +host_hash(const char *host, const char *name_from_hostfile, u_int src_len) +{ + struct ssh_hmac_ctx *ctx; + u_char salt[256], result[256]; + char uu_salt[512], uu_result[512]; + char *encoded = NULL; + u_int len; + + len = ssh_digest_bytes(SSH_DIGEST_SHA1); + + if (name_from_hostfile == NULL) { + /* Create new salt */ + arc4random_buf(salt, len); + } else { + /* Extract salt from known host entry */ + if (extract_salt(name_from_hostfile, src_len, salt, + sizeof(salt)) == -1) + return (NULL); + } + + if ((ctx = ssh_hmac_start(SSH_DIGEST_SHA1)) == NULL || + ssh_hmac_init(ctx, salt, len) < 0 || + ssh_hmac_update(ctx, host, strlen(host)) < 0 || + ssh_hmac_final(ctx, result, sizeof(result))) + fatal_f("ssh_hmac failed"); + ssh_hmac_free(ctx); + + if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 || + __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1) + fatal_f("__b64_ntop failed"); + xasprintf(&encoded, "%s%s%c%s", HASH_MAGIC, uu_salt, HASH_DELIM, + uu_result); + + return (encoded); +} + +/* + * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the + * pointer over the key. Skips any whitespace at the beginning and at end. + */ + +int +hostfile_read_key(char **cpp, u_int *bitsp, struct sshkey *ret) +{ + char *cp; + + /* Skip leading whitespace. */ + for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) + ; + + if (sshkey_read(ret, &cp) != 0) + return 0; + + /* Skip trailing whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + + /* Return results. */ + *cpp = cp; + if (bitsp != NULL) + *bitsp = sshkey_size(ret); + return 1; +} + +static HostkeyMarker +check_markers(char **cpp) +{ + char marker[32], *sp, *cp = *cpp; + int ret = MRK_NONE; + + while (*cp == '@') { + /* Only one marker is allowed */ + if (ret != MRK_NONE) + return MRK_ERROR; + /* Markers are terminated by whitespace */ + if ((sp = strchr(cp, ' ')) == NULL && + (sp = strchr(cp, '\t')) == NULL) + return MRK_ERROR; + /* Extract marker for comparison */ + if (sp <= cp + 1 || sp >= cp + sizeof(marker)) + return MRK_ERROR; + memcpy(marker, cp, sp - cp); + marker[sp - cp] = '\0'; + if (strcmp(marker, CA_MARKER) == 0) + ret = MRK_CA; + else if (strcmp(marker, REVOKE_MARKER) == 0) + ret = MRK_REVOKE; + else + return MRK_ERROR; + + /* Skip past marker and any whitespace that follows it */ + cp = sp; + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + } + *cpp = cp; + return ret; +} + +struct hostkeys * +init_hostkeys(void) +{ + struct hostkeys *ret = xcalloc(1, sizeof(*ret)); + + ret->entries = NULL; + return ret; +} + +struct load_callback_ctx { + const char *host; + u_long num_loaded; + struct hostkeys *hostkeys; +}; + +static int +record_hostkey(struct hostkey_foreach_line *l, void *_ctx) +{ + struct load_callback_ctx *ctx = (struct load_callback_ctx *)_ctx; + struct hostkeys *hostkeys = ctx->hostkeys; + struct hostkey_entry *tmp; + + if (l->status == HKF_STATUS_INVALID) { + /* XXX make this verbose() in the future */ + debug("%s:%ld: parse error in hostkeys file", + l->path, l->linenum); + return 0; + } + + debug3_f("found %skey type %s in file %s:%lu", + l->marker == MRK_NONE ? "" : + (l->marker == MRK_CA ? "ca " : "revoked "), + sshkey_type(l->key), l->path, l->linenum); + if ((tmp = recallocarray(hostkeys->entries, hostkeys->num_entries, + hostkeys->num_entries + 1, sizeof(*hostkeys->entries))) == NULL) + return SSH_ERR_ALLOC_FAIL; + hostkeys->entries = tmp; + hostkeys->entries[hostkeys->num_entries].host = xstrdup(ctx->host); + hostkeys->entries[hostkeys->num_entries].file = xstrdup(l->path); + hostkeys->entries[hostkeys->num_entries].line = l->linenum; + hostkeys->entries[hostkeys->num_entries].key = l->key; + l->key = NULL; /* steal it */ + hostkeys->entries[hostkeys->num_entries].marker = l->marker; + hostkeys->entries[hostkeys->num_entries].note = l->note; + hostkeys->num_entries++; + ctx->num_loaded++; + + return 0; +} + +void +load_hostkeys_file(struct hostkeys *hostkeys, const char *host, + const char *path, FILE *f, u_int note) +{ + int r; + struct load_callback_ctx ctx; + + ctx.host = host; + ctx.num_loaded = 0; + ctx.hostkeys = hostkeys; + + if ((r = hostkeys_foreach_file(path, f, record_hostkey, &ctx, host, + NULL, HKF_WANT_MATCH|HKF_WANT_PARSE_KEY, note)) != 0) { + if (r != SSH_ERR_SYSTEM_ERROR && errno != ENOENT) + debug_fr(r, "hostkeys_foreach failed for %s", path); + } + if (ctx.num_loaded != 0) + debug3_f("loaded %lu keys from %s", ctx.num_loaded, host); +} + +void +load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path, + u_int note) +{ + FILE *f; + + if ((f = fopen(path, "r")) == NULL) { + debug_f("fopen %s: %s", path, strerror(errno)); + return; + } + + load_hostkeys_file(hostkeys, host, path, f, note); + fclose(f); +} + +void +free_hostkeys(struct hostkeys *hostkeys) +{ + u_int i; + + for (i = 0; i < hostkeys->num_entries; i++) { + free(hostkeys->entries[i].host); + free(hostkeys->entries[i].file); + sshkey_free(hostkeys->entries[i].key); + explicit_bzero(hostkeys->entries + i, sizeof(*hostkeys->entries)); + } + free(hostkeys->entries); + freezero(hostkeys, sizeof(*hostkeys)); +} + +static int +check_key_not_revoked(struct hostkeys *hostkeys, struct sshkey *k) +{ + int is_cert = sshkey_is_cert(k); + u_int i; + + for (i = 0; i < hostkeys->num_entries; i++) { + if (hostkeys->entries[i].marker != MRK_REVOKE) + continue; + if (sshkey_equal_public(k, hostkeys->entries[i].key)) + return -1; + if (is_cert && k != NULL && + sshkey_equal_public(k->cert->signature_key, + hostkeys->entries[i].key)) + return -1; + } + return 0; +} + +/* + * Match keys against a specified key, or look one up by key type. + * + * If looking for a keytype (key == NULL) and one is found then return + * HOST_FOUND, otherwise HOST_NEW. + * + * If looking for a key (key != NULL): + * 1. If the key is a cert and a matching CA is found, return HOST_OK + * 2. If the key is not a cert and a matching key is found, return HOST_OK + * 3. If no key matches but a key with a different type is found, then + * return HOST_CHANGED + * 4. If no matching keys are found, then return HOST_NEW. + * + * Finally, check any found key is not revoked. + */ +static HostStatus +check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, + struct sshkey *k, int keytype, int nid, const struct hostkey_entry **found) +{ + u_int i; + HostStatus end_return = HOST_NEW; + int want_cert = sshkey_is_cert(k); + HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE; + + if (found != NULL) + *found = NULL; + + for (i = 0; i < hostkeys->num_entries; i++) { + if (hostkeys->entries[i].marker != want_marker) + continue; + if (k == NULL) { + if (hostkeys->entries[i].key->type != keytype) + continue; + if (nid != -1 && + sshkey_type_plain(keytype) == KEY_ECDSA && + hostkeys->entries[i].key->ecdsa_nid != nid) + continue; + end_return = HOST_FOUND; + if (found != NULL) + *found = hostkeys->entries + i; + k = hostkeys->entries[i].key; + break; + } + if (want_cert) { + if (sshkey_equal_public(k->cert->signature_key, + hostkeys->entries[i].key)) { + /* A matching CA exists */ + end_return = HOST_OK; + if (found != NULL) + *found = hostkeys->entries + i; + break; + } + } else { + if (sshkey_equal(k, hostkeys->entries[i].key)) { + end_return = HOST_OK; + if (found != NULL) + *found = hostkeys->entries + i; + break; + } + /* A non-matching key exists */ + end_return = HOST_CHANGED; + if (found != NULL) + *found = hostkeys->entries + i; + } + } + if (check_key_not_revoked(hostkeys, k) != 0) { + end_return = HOST_REVOKED; + if (found != NULL) + *found = NULL; + } + return end_return; +} + +HostStatus +check_key_in_hostkeys(struct hostkeys *hostkeys, struct sshkey *key, + const struct hostkey_entry **found) +{ + if (key == NULL) + fatal("no key to look up"); + return check_hostkeys_by_key_or_type(hostkeys, key, 0, -1, found); +} + +int +lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, int nid, + const struct hostkey_entry **found) +{ + return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype, nid, + found) == HOST_FOUND); +} + +int +lookup_marker_in_hostkeys(struct hostkeys *hostkeys, int want_marker) +{ + u_int i; + + for (i = 0; i < hostkeys->num_entries; i++) { + if (hostkeys->entries[i].marker == (HostkeyMarker)want_marker) + return 1; + } + return 0; +} + +static int +write_host_entry(FILE *f, const char *host, const char *ip, + const struct sshkey *key, int store_hash) +{ + int r, success = 0; + char *hashed_host = NULL, *lhost; + + lhost = xstrdup(host); + lowercase(lhost); + + if (store_hash) { + if ((hashed_host = host_hash(lhost, NULL, 0)) == NULL) { + error_f("host_hash failed"); + free(lhost); + return 0; + } + fprintf(f, "%s ", hashed_host); + } else if (ip != NULL) + fprintf(f, "%s,%s ", lhost, ip); + else { + fprintf(f, "%s ", lhost); + } + free(hashed_host); + free(lhost); + if ((r = sshkey_write(key, f)) == 0) + success = 1; + else + error_fr(r, "sshkey_write"); + fputc('\n', f); + /* If hashing is enabled, the IP address needs to go on its own line */ + if (success && store_hash && ip != NULL) + success = write_host_entry(f, ip, NULL, key, 1); + return success; +} + +/* + * Create user ~/.ssh directory if it doesn't exist and we want to write to it. + * If notify is set, a message will be emitted if the directory is created. + */ +void +hostfile_create_user_ssh_dir(const char *filename, int notify) +{ + char *dotsshdir = NULL, *p; + size_t len; + struct stat st; + + if ((p = strrchr(filename, '/')) == NULL) + return; + len = p - filename; + dotsshdir = tilde_expand_filename("~/" _PATH_SSH_USER_DIR, getuid()); + if (strlen(dotsshdir) > len || strncmp(filename, dotsshdir, len) != 0) + goto out; /* not ~/.ssh prefixed */ + if (stat(dotsshdir, &st) == 0) + goto out; /* dir already exists */ + else if (errno != ENOENT) + error("Could not stat %s: %s", dotsshdir, strerror(errno)); + else { +#ifdef WITH_SELINUX + ssh_selinux_setfscreatecon(dotsshdir); +#endif + if (mkdir(dotsshdir, 0700) == -1) + error("Could not create directory '%.200s' (%s).", + dotsshdir, strerror(errno)); + else if (notify) + logit("Created directory '%s'.", dotsshdir); +#ifdef WITH_SELINUX + ssh_selinux_setfscreatecon(NULL); +#endif + } + out: + free(dotsshdir); +} + +/* + * Appends an entry to the host file. Returns false if the entry could not + * be appended. + */ +int +add_host_to_hostfile(const char *filename, const char *host, + const struct sshkey *key, int store_hash) +{ + FILE *f; + int success; + + if (key == NULL) + return 1; /* XXX ? */ + hostfile_create_user_ssh_dir(filename, 0); + f = fopen(filename, "a"); + if (!f) + return 0; + success = write_host_entry(f, host, NULL, key, store_hash); + fclose(f); + return success; +} + +struct host_delete_ctx { + FILE *out; + int quiet; + const char *host, *ip; + u_int *match_keys; /* mask of HKF_MATCH_* for this key */ + struct sshkey * const *keys; + size_t nkeys; + int modified; +}; + +static int +host_delete(struct hostkey_foreach_line *l, void *_ctx) +{ + struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx; + int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; + size_t i; + + /* Don't remove CA and revocation lines */ + if (l->status == HKF_STATUS_MATCHED && l->marker == MRK_NONE) { + /* + * If this line contains one of the keys that we will be + * adding later, then don't change it and mark the key for + * skipping. + */ + for (i = 0; i < ctx->nkeys; i++) { + if (!sshkey_equal(ctx->keys[i], l->key)) + continue; + ctx->match_keys[i] |= l->match; + fprintf(ctx->out, "%s\n", l->line); + debug3_f("%s key already at %s:%ld", + sshkey_type(l->key), l->path, l->linenum); + return 0; + } + + /* + * Hostname matches and has no CA/revoke marker, delete it + * by *not* writing the line to ctx->out. + */ + do_log2(loglevel, "%s%s%s:%ld: Removed %s key for host %s", + ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", + l->path, l->linenum, sshkey_type(l->key), ctx->host); + ctx->modified = 1; + return 0; + } + /* Retain non-matching hosts and invalid lines when deleting */ + if (l->status == HKF_STATUS_INVALID) { + do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry", + ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", + l->path, l->linenum); + } + fprintf(ctx->out, "%s\n", l->line); + return 0; +} + +int +hostfile_replace_entries(const char *filename, const char *host, const char *ip, + struct sshkey **keys, size_t nkeys, int store_hash, int quiet, int hash_alg) +{ + int r, fd, oerrno = 0; + int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; + struct host_delete_ctx ctx; + char *fp, *temp = NULL, *back = NULL; + const char *what; + mode_t omask; + size_t i; + u_int want; + + omask = umask(077); + + memset(&ctx, 0, sizeof(ctx)); + ctx.host = host; + ctx.ip = ip; + ctx.quiet = quiet; + + if ((ctx.match_keys = calloc(nkeys, sizeof(*ctx.match_keys))) == NULL) + return SSH_ERR_ALLOC_FAIL; + ctx.keys = keys; + ctx.nkeys = nkeys; + ctx.modified = 0; + + /* + * Prepare temporary file for in-place deletion. + */ + if ((r = asprintf(&temp, "%s.XXXXXXXXXXX", filename)) == -1 || + (r = asprintf(&back, "%s.old", filename)) == -1) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + + if ((fd = mkstemp(temp)) == -1) { + oerrno = errno; + error_f("mkstemp: %s", strerror(oerrno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if ((ctx.out = fdopen(fd, "w")) == NULL) { + oerrno = errno; + close(fd); + error_f("fdopen: %s", strerror(oerrno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + + /* Remove stale/mismatching entries for the specified host */ + if ((r = hostkeys_foreach(filename, host_delete, &ctx, host, ip, + HKF_WANT_PARSE_KEY, 0)) != 0) { + oerrno = errno; + error_fr(r, "hostkeys_foreach"); + goto fail; + } + + /* Re-add the requested keys */ + want = HKF_MATCH_HOST | (ip == NULL ? 0 : HKF_MATCH_IP); + for (i = 0; i < nkeys; i++) { + if (keys[i] == NULL || (want & ctx.match_keys[i]) == want) + continue; + if ((fp = sshkey_fingerprint(keys[i], hash_alg, + SSH_FP_DEFAULT)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + /* write host/ip */ + what = ""; + if (ctx.match_keys[i] == 0) { + what = "Adding new key"; + if (!write_host_entry(ctx.out, host, ip, + keys[i], store_hash)) { + r = SSH_ERR_INTERNAL_ERROR; + goto fail; + } + } else if ((want & ~ctx.match_keys[i]) == HKF_MATCH_HOST) { + what = "Fixing match (hostname)"; + if (!write_host_entry(ctx.out, host, NULL, + keys[i], store_hash)) { + r = SSH_ERR_INTERNAL_ERROR; + goto fail; + } + } else if ((want & ~ctx.match_keys[i]) == HKF_MATCH_IP) { + what = "Fixing match (address)"; + if (!write_host_entry(ctx.out, ip, NULL, + keys[i], store_hash)) { + r = SSH_ERR_INTERNAL_ERROR; + goto fail; + } + } + do_log2(loglevel, "%s%s%s for %s%s%s to %s: %s %s", + quiet ? __func__ : "", quiet ? ": " : "", what, + host, ip == NULL ? "" : ",", ip == NULL ? "" : ip, filename, + sshkey_ssh_name(keys[i]), fp); + free(fp); + ctx.modified = 1; + } + fclose(ctx.out); + ctx.out = NULL; + + if (ctx.modified) { + /* Backup the original file and replace it with the temporary */ + if (unlink(back) == -1 && errno != ENOENT) { + oerrno = errno; + error_f("unlink %.100s: %s", back, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if (link(filename, back) == -1) { + oerrno = errno; + error_f("link %.100s to %.100s: %s", filename, + back, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + if (rename(temp, filename) == -1) { + oerrno = errno; + error_f("rename \"%s\" to \"%s\": %s", temp, + filename, strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto fail; + } + } else { + /* No changes made; just delete the temporary file */ + if (unlink(temp) != 0) + error_f("unlink \"%s\": %s", temp, strerror(errno)); + } + + /* success */ + r = 0; + fail: + if (temp != NULL && r != 0) + unlink(temp); + free(temp); + free(back); + if (ctx.out != NULL) + fclose(ctx.out); + free(ctx.match_keys); + umask(omask); + if (r == SSH_ERR_SYSTEM_ERROR) + errno = oerrno; + return r; +} + +static int +match_maybe_hashed(const char *host, const char *names, int *was_hashed) +{ + int hashed = *names == HASH_DELIM, ret; + char *hashed_host = NULL; + size_t nlen = strlen(names); + + if (was_hashed != NULL) + *was_hashed = hashed; + if (hashed) { + if ((hashed_host = host_hash(host, names, nlen)) == NULL) + return -1; + ret = (nlen == strlen(hashed_host) && + strncmp(hashed_host, names, nlen) == 0); + free(hashed_host); + return ret; + } + return match_hostname(host, names) == 1; +} + +int +hostkeys_foreach_file(const char *path, FILE *f, hostkeys_foreach_fn *callback, + void *ctx, const char *host, const char *ip, u_int options, u_int note) +{ + char *line = NULL, ktype[128]; + u_long linenum = 0; + char *cp, *cp2; + u_int kbits; + int hashed; + int s, r = 0; + struct hostkey_foreach_line lineinfo; + size_t linesize = 0, l; + + memset(&lineinfo, 0, sizeof(lineinfo)); + if (host == NULL && (options & HKF_WANT_MATCH) != 0) + return SSH_ERR_INVALID_ARGUMENT; + + while (getline(&line, &linesize, f) != -1) { + linenum++; + line[strcspn(line, "\n")] = '\0'; + + free(lineinfo.line); + sshkey_free(lineinfo.key); + memset(&lineinfo, 0, sizeof(lineinfo)); + lineinfo.path = path; + lineinfo.linenum = linenum; + lineinfo.line = xstrdup(line); + lineinfo.marker = MRK_NONE; + lineinfo.status = HKF_STATUS_OK; + lineinfo.keytype = KEY_UNSPEC; + lineinfo.note = note; + + /* Skip any leading whitespace, comments and empty lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') { + if ((options & HKF_WANT_MATCH) == 0) { + lineinfo.status = HKF_STATUS_COMMENT; + if ((r = callback(&lineinfo, ctx)) != 0) + break; + } + continue; + } + + if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) { + verbose_f("invalid marker at %s:%lu", path, linenum); + if ((options & HKF_WANT_MATCH) == 0) + goto bad; + continue; + } + + /* Find the end of the host name portion. */ + for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) + ; + lineinfo.hosts = cp; + *cp2++ = '\0'; + + /* Check if the host name matches. */ + if (host != NULL) { + if ((s = match_maybe_hashed(host, lineinfo.hosts, + &hashed)) == -1) { + debug2_f("%s:%ld: bad host hash \"%.32s\"", + path, linenum, lineinfo.hosts); + goto bad; + } + if (s == 1) { + lineinfo.status = HKF_STATUS_MATCHED; + lineinfo.match |= HKF_MATCH_HOST | + (hashed ? HKF_MATCH_HOST_HASHED : 0); + } + /* Try matching IP address if supplied */ + if (ip != NULL) { + if ((s = match_maybe_hashed(ip, lineinfo.hosts, + &hashed)) == -1) { + debug2_f("%s:%ld: bad ip hash " + "\"%.32s\"", path, linenum, + lineinfo.hosts); + goto bad; + } + if (s == 1) { + lineinfo.status = HKF_STATUS_MATCHED; + lineinfo.match |= HKF_MATCH_IP | + (hashed ? HKF_MATCH_IP_HASHED : 0); + } + } + /* + * Skip this line if host matching requested and + * neither host nor address matched. + */ + if ((options & HKF_WANT_MATCH) != 0 && + lineinfo.status != HKF_STATUS_MATCHED) + continue; + } + + /* Got a match. Skip host name and any following whitespace */ + for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) + ; + if (*cp2 == '\0' || *cp2 == '#') { + debug2("%s:%ld: truncated before key type", + path, linenum); + goto bad; + } + lineinfo.rawkey = cp = cp2; + + if ((options & HKF_WANT_PARSE_KEY) != 0) { + /* + * Extract the key from the line. This will skip + * any leading whitespace. Ignore badly formatted + * lines. + */ + if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) { + error_f("sshkey_new failed"); + r = SSH_ERR_ALLOC_FAIL; + break; + } + if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) { + goto bad; + } + lineinfo.keytype = lineinfo.key->type; + lineinfo.comment = cp; + } else { + /* Extract and parse key type */ + l = strcspn(lineinfo.rawkey, " \t"); + if (l <= 1 || l >= sizeof(ktype) || + lineinfo.rawkey[l] == '\0') + goto bad; + memcpy(ktype, lineinfo.rawkey, l); + ktype[l] = '\0'; + lineinfo.keytype = sshkey_type_from_name(ktype); + + /* + * Assume legacy RSA1 if the first component is a short + * decimal number. + */ + if (lineinfo.keytype == KEY_UNSPEC && l < 8 && + strspn(ktype, "0123456789") == l) + goto bad; + + /* + * Check that something other than whitespace follows + * the key type. This won't catch all corruption, but + * it does catch trivial truncation. + */ + cp2 += l; /* Skip past key type */ + for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) + ; + if (*cp2 == '\0' || *cp2 == '#') { + debug2("%s:%ld: truncated after key type", + path, linenum); + lineinfo.keytype = KEY_UNSPEC; + } + if (lineinfo.keytype == KEY_UNSPEC) { + bad: + sshkey_free(lineinfo.key); + lineinfo.key = NULL; + lineinfo.status = HKF_STATUS_INVALID; + if ((r = callback(&lineinfo, ctx)) != 0) + break; + continue; + } + } + if ((r = callback(&lineinfo, ctx)) != 0) + break; + } + sshkey_free(lineinfo.key); + free(lineinfo.line); + free(line); + return r; +} + +int +hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, + const char *host, const char *ip, u_int options, u_int note) +{ + FILE *f; + int r, oerrno; + + if ((f = fopen(path, "r")) == NULL) + return SSH_ERR_SYSTEM_ERROR; + + debug3_f("reading file \"%s\"", path); + r = hostkeys_foreach_file(path, f, callback, ctx, host, ip, + options, note); + oerrno = errno; + fclose(f); + errno = oerrno; + return r; +} diff --git a/aosp/external/openssh/hostfile.h b/aosp/external/openssh/hostfile.h new file mode 100644 index 0000000000000000000000000000000000000000..a24a4e3290597ec91270209f7adb5d5c04c6f821 --- /dev/null +++ b/aosp/external/openssh/hostfile.h @@ -0,0 +1,123 @@ +/* $OpenBSD: hostfile.h,v 1.29 2021/01/26 00:51:30 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +#ifndef HOSTFILE_H +#define HOSTFILE_H + +typedef enum { + HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND +} HostStatus; + +typedef enum { + MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA +} HostkeyMarker; + +struct hostkey_entry { + char *host; + char *file; + u_long line; + struct sshkey *key; + HostkeyMarker marker; + u_int note; /* caller-specific note/flag */ +}; +struct hostkeys { + struct hostkey_entry *entries; + u_int num_entries; +}; + +struct hostkeys *init_hostkeys(void); +void load_hostkeys(struct hostkeys *, const char *, + const char *, u_int); +void load_hostkeys_file(struct hostkeys *, const char *, + const char *, FILE *, u_int note); +void free_hostkeys(struct hostkeys *); + +HostStatus check_key_in_hostkeys(struct hostkeys *, struct sshkey *, + const struct hostkey_entry **); +int lookup_key_in_hostkeys_by_type(struct hostkeys *, int, int, + const struct hostkey_entry **); +int lookup_marker_in_hostkeys(struct hostkeys *, int); + +int hostfile_read_key(char **, u_int *, struct sshkey *); +int add_host_to_hostfile(const char *, const char *, + const struct sshkey *, int); + +int hostfile_replace_entries(const char *filename, + const char *host, const char *ip, struct sshkey **keys, size_t nkeys, + int store_hash, int quiet, int hash_alg); + +#define HASH_MAGIC "|1|" +#define HASH_DELIM '|' + +#define CA_MARKER "@cert-authority" +#define REVOKE_MARKER "@revoked" + +char *host_hash(const char *, const char *, u_int); + +/* + * Iterate through a hostkeys file, optionally parsing keys and matching + * hostnames. Allows access to the raw keyfile lines to allow + * streaming edits to the file to take place. + */ +#define HKF_WANT_MATCH (1) /* return only matching hosts/addrs */ +#define HKF_WANT_PARSE_KEY (1<<1) /* need key parsed */ + +#define HKF_STATUS_OK 0 /* Line parsed, didn't match host */ +#define HKF_STATUS_INVALID 1 /* line had parse error */ +#define HKF_STATUS_COMMENT 2 /* valid line contained no key */ +#define HKF_STATUS_MATCHED 3 /* hostname or IP matched */ + +#define HKF_MATCH_HOST (1) /* hostname matched */ +#define HKF_MATCH_IP (1<<1) /* address matched */ +#define HKF_MATCH_HOST_HASHED (1<<2) /* hostname was hashed */ +#define HKF_MATCH_IP_HASHED (1<<3) /* address was hashed */ +/* XXX HKF_MATCH_KEY_TYPE? */ + +/* + * The callback function receives this as an argument for each matching + * hostkey line. The callback may "steal" the 'key' field by setting it to NULL. + * If a parse error occurred, then "hosts" and subsequent options may be NULL. + */ +struct hostkey_foreach_line { + const char *path; /* Path of file */ + u_long linenum; /* Line number */ + u_int status; /* One of HKF_STATUS_* */ + u_int match; /* Zero or more of HKF_MATCH_* OR'd together */ + char *line; /* Entire key line; mutable by callback */ + int marker; /* CA/revocation markers; indicated by MRK_* value */ + const char *hosts; /* Raw hosts text, may be hashed or list multiple */ + const char *rawkey; /* Text of key and any comment following it */ + int keytype; /* Type of key; KEY_UNSPEC for invalid/comment lines */ + struct sshkey *key; /* Key, if parsed ok and HKF_WANT_MATCH_HOST set */ + const char *comment; /* Any comment following the key */ + u_int note; /* caller-specified note copied from arguments */ +}; + +/* + * Callback fires for each line (or matching line if a HKF_WANT_* option + * is set). The foreach loop will terminate if the callback returns a non- + * zero exit status. + */ +typedef int hostkeys_foreach_fn(struct hostkey_foreach_line *l, void *ctx); + +/* Iterate over a hostkeys file */ +int hostkeys_foreach(const char *path, + hostkeys_foreach_fn *callback, void *ctx, + const char *host, const char *ip, u_int options, u_int note); +int hostkeys_foreach_file(const char *path, FILE *f, + hostkeys_foreach_fn *callback, void *ctx, + const char *host, const char *ip, u_int options, u_int note); + +void hostfile_create_user_ssh_dir(const char *, int); + +#endif diff --git a/aosp/external/openssh/includes.h b/aosp/external/openssh/includes.h new file mode 100644 index 0000000000000000000000000000000000000000..6d17ef6da9338cfd0de8c044f2a1483991c166e4 --- /dev/null +++ b/aosp/external/openssh/includes.h @@ -0,0 +1,178 @@ +/* $OpenBSD: includes.h,v 1.54 2006/07/22 20:48:23 stevesk Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file includes most of the needed system headers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +#include "config.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* activate extra prototypes for glibc */ +#endif + +#include +#include /* For CMSG_* */ + +#ifdef HAVE_LIMITS_H +# include /* For PATH_MAX, _POSIX_HOST_NAME_MAX */ +#endif +#ifdef HAVE_BSTRING_H +# include +#endif +#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_TTYENT_H +# include +#endif +#ifdef HAVE_UTIME_H +# include +#endif +#ifdef HAVE_MAILLOCK_H +# include /* For _PATH_MAILDIR */ +#endif +#ifdef HAVE_NEXT +# include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + +/* + *-*-nto-qnx needs these headers for strcasecmp and LASTLOG_FILE respectively + */ +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_LOGIN_H +# include +#endif + +#ifdef HAVE_UTMP_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif +#ifdef HAVE_LASTLOG_H +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_BSDTTY_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#include +#ifdef HAVE_SYS_BITYPES_H +# include /* For u_intXX_t */ +#endif +#ifdef HAVE_SYS_CDEFS_H +# include /* For __P() */ +#endif +#ifdef HAVE_SYS_STAT_H +# include /* For S_* constants and macros */ +#endif +#ifdef HAVE_SYS_SYSMACROS_H +# include /* For MIN, MAX, etc */ +#endif +#ifdef HAVE_SYS_TIME_H +# include /* for timespeccmp if present */ +#endif +#ifdef HAVE_SYS_MMAN_H +#include /* for MAP_ANONYMOUS */ +#endif +#ifdef HAVE_SYS_STRTIO_H +#include /* for TIOCCBRK on HP-UX */ +#endif +#if defined(HAVE_SYS_PTMS_H) && defined(HAVE_DEV_PTMX) +# if defined(HAVE_SYS_STREAM_H) +# include /* reqd for queue_t on Solaris 2.5.1 */ +# endif +#include /* for grantpt() and friends */ +#endif + +#include +#include /* For typedefs */ +#ifdef HAVE_RPC_TYPES_H +# include /* For INADDR_LOOPBACK */ +#endif +#ifdef USE_PAM +#if defined(HAVE_SECURITY_PAM_APPL_H) +# include +#elif defined (HAVE_PAM_PAM_APPL_H) +# include +#endif +#endif +#ifdef HAVE_READPASSPHRASE_H +# include +#endif + +#ifdef HAVE_IA_H +# include +#endif + +#ifdef HAVE_IAF_H +# include +#endif + +#ifdef HAVE_TMPDIR_H +# include +#endif + +#if defined(HAVE_BSD_LIBUTIL_H) +# include +#elif defined(HAVE_LIBUTIL_H) +# include +#endif + +#if defined(KRB5) && defined(USE_AFS) +# include +# include +#endif + +#if defined(HAVE_SYS_SYSLOG_H) +# include +#endif + +#include + +/* + * On HP-UX 11.11, shadow.h and prot.h provide conflicting declarations + * of getspnam when _INCLUDE__STDC__ is defined, so we unset it here. + */ +#ifdef GETSPNAM_CONFLICTING_DEFS +# ifdef _INCLUDE__STDC__ +# undef _INCLUDE__STDC__ +# endif +#endif + +#ifdef WITH_OPENSSL +#include /* For OPENSSL_VERSION_NUMBER */ +#endif + +#include "defines.h" + +#include "platform.h" +#include "openbsd-compat/openbsd-compat.h" +#include "openbsd-compat/bsd-nextstep.h" + +#include "entropy.h" + +#endif /* INCLUDES_H */ diff --git a/aosp/external/openssh/install-sh b/aosp/external/openssh/install-sh new file mode 100644 index 0000000000000000000000000000000000000000..377bb8687ffe16bfc79ea25c8667cabf72aaf2c2 --- /dev/null +++ b/aosp/external/openssh/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/aosp/external/openssh/int32_minmax.inc b/aosp/external/openssh/int32_minmax.inc new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/aosp/external/openssh/kex.c b/aosp/external/openssh/kex.c new file mode 100644 index 0000000000000000000000000000000000000000..0bcd27dc5825c41a0ad4a1139d519bb2e7b9b1e3 --- /dev/null +++ b/aosp/external/openssh/kex.c @@ -0,0 +1,1421 @@ +/* $OpenBSD: kex.c,v 1.172 2022/02/01 23:32:51 djm Exp $ */ +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif + +#ifdef WITH_OPENSSL +#include +#include +#endif + +#include "ssh.h" +#include "ssh2.h" +#include "atomicio.h" +#include "version.h" +#include "packet.h" +#include "compat.h" +#include "cipher.h" +#include "sshkey.h" +#include "kex.h" +#include "log.h" +#include "mac.h" +#include "match.h" +#include "misc.h" +#include "dispatch.h" +#include "monitor.h" + +#include "ssherr.h" +#include "sshbuf.h" +#include "digest.h" + +/* prototype */ +static int kex_choose_conf(struct ssh *); +static int kex_input_newkeys(int, u_int32_t, struct ssh *); + +static const char * const proposal_names[PROPOSAL_MAX] = { + "KEX algorithms", + "host key algorithms", + "ciphers ctos", + "ciphers stoc", + "MACs ctos", + "MACs stoc", + "compression ctos", + "compression stoc", + "languages ctos", + "languages stoc", +}; + +struct kexalg { + char *name; + u_int type; + int ec_nid; + int hash_alg; +}; +static const struct kexalg kexalgs[] = { +#ifdef WITH_OPENSSL + { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, + { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, + { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 }, + { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, +#ifdef HAVE_EVP_SHA256 + { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, +#endif /* HAVE_EVP_SHA256 */ +#ifdef OPENSSL_HAS_ECC + { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, + NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, + { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, + SSH_DIGEST_SHA384 }, +# ifdef OPENSSL_HAS_NISTP521 + { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, + SSH_DIGEST_SHA512 }, +# endif /* OPENSSL_HAS_NISTP521 */ +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ +#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) + { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, + { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, +#ifdef USE_SNTRUP761X25519 + { KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0, + SSH_DIGEST_SHA512 }, +#endif +#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ + { NULL, 0, -1, -1}, +}; + +char * +kex_alg_list(char sep) +{ + char *ret = NULL, *tmp; + size_t nlen, rlen = 0; + const struct kexalg *k; + + for (k = kexalgs; k->name != NULL; k++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(k->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; + memcpy(ret + rlen, k->name, nlen + 1); + rlen += nlen; + } + return ret; +} + +static const struct kexalg * +kex_alg_by_name(const char *name) +{ + const struct kexalg *k; + + for (k = kexalgs; k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; + } + return NULL; +} + +/* Validate KEX method name list */ +int +kex_names_valid(const char *names) +{ + char *s, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + if ((s = cp = strdup(names)) == NULL) + return 0; + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + if (kex_alg_by_name(p) == NULL) { + error("Unsupported KEX algorithm \"%.100s\"", p); + free(s); + return 0; + } + } + debug3("kex names ok: [%s]", names); + free(s); + return 1; +} + +/* + * Concatenate algorithm names, avoiding duplicates in the process. + * Caller must free returned string. + */ +char * +kex_names_cat(const char *a, const char *b) +{ + char *ret = NULL, *tmp = NULL, *cp, *p, *m; + size_t len; + + if (a == NULL || *a == '\0') + return strdup(b); + if (b == NULL || *b == '\0') + return strdup(a); + if (strlen(b) > 1024*1024) + return NULL; + len = strlen(a) + strlen(b) + 2; + if ((tmp = cp = strdup(b)) == NULL || + (ret = calloc(1, len)) == NULL) { + free(tmp); + return NULL; + } + strlcpy(ret, a, len); + for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { + if ((m = match_list(ret, p, NULL)) != NULL) { + free(m); + continue; /* Algorithm already present */ + } + if (strlcat(ret, ",", len) >= len || + strlcat(ret, p, len) >= len) { + free(tmp); + free(ret); + return NULL; /* Shouldn't happen */ + } + } + free(tmp); + return ret; +} + +/* + * Assemble a list of algorithms from a default list and a string from a + * configuration file. The user-provided string may begin with '+' to + * indicate that it should be appended to the default, '-' that the + * specified names should be removed, or '^' that they should be placed + * at the head. + */ +int +kex_assemble_names(char **listp, const char *def, const char *all) +{ + char *cp, *tmp, *patterns; + char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if (listp == NULL || def == NULL || all == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + if (*listp == NULL || **listp == '\0') { + if ((*listp = strdup(def)) == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; + } + + list = *listp; + *listp = NULL; + if (*list == '+') { + /* Append names to default list */ + if ((tmp = kex_names_cat(def, list + 1)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + free(list); + list = tmp; + } else if (*list == '-') { + /* Remove names from default list */ + if ((*listp = match_filter_denylist(def, list + 1)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + free(list); + /* filtering has already been done */ + return 0; + } else if (*list == '^') { + /* Place names at head of default list */ + if ((tmp = kex_names_cat(list + 1, def)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + free(list); + list = tmp; + } else { + /* Explicit list, overrides default - just use "list" as is */ + } + + /* + * The supplied names may be a pattern-list. For the -list case, + * the patterns are applied above. For the +list and explicit list + * cases we need to do it now. + */ + ret = NULL; + if ((patterns = opatterns = strdup(list)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + /* Apply positive (i.e. non-negated) patterns from the list */ + while ((cp = strsep(&patterns, ",")) != NULL) { + if (*cp == '!') { + /* negated matches are not supported here */ + r = SSH_ERR_INVALID_ARGUMENT; + goto fail; + } + free(matching); + if ((matching = match_filter_allowlist(all, cp)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + if ((tmp = kex_names_cat(ret, matching)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto fail; + } + free(ret); + ret = tmp; + } + if (ret == NULL || *ret == '\0') { + /* An empty name-list is an error */ + /* XXX better error code? */ + r = SSH_ERR_INVALID_ARGUMENT; + goto fail; + } + + /* success */ + *listp = ret; + ret = NULL; + r = 0; + + fail: + free(matching); + free(opatterns); + free(list); + free(ret); + return r; +} + +/* put algorithm proposal into buffer */ +int +kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) +{ + u_int i; + int r; + + sshbuf_reset(b); + + /* + * add a dummy cookie, the cookie will be overwritten by + * kex_send_kexinit(), each time a kexinit is set + */ + for (i = 0; i < KEX_COOKIE_LEN; i++) { + if ((r = sshbuf_put_u8(b, 0)) != 0) + return r; + } + for (i = 0; i < PROPOSAL_MAX; i++) { + if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) + return r; + } + if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ + (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ + return r; + return 0; +} + +/* parse buffer and return algorithm proposal */ +int +kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) +{ + struct sshbuf *b = NULL; + u_char v; + u_int i; + char **proposal = NULL; + int r; + + *propp = NULL; + if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((b = sshbuf_fromb(raw)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */ + error_fr(r, "consume cookie"); + goto out; + } + /* extract kex init proposal strings */ + for (i = 0; i < PROPOSAL_MAX; i++) { + if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) { + error_fr(r, "parse proposal %u", i); + goto out; + } + debug2("%s: %s", proposal_names[i], proposal[i]); + } + /* first kex follows / reserved */ + if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ + (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */ + error_fr(r, "parse"); + goto out; + } + if (first_kex_follows != NULL) + *first_kex_follows = v; + debug2("first_kex_follows %d ", v); + debug2("reserved %u ", i); + r = 0; + *propp = proposal; + out: + if (r != 0 && proposal != NULL) + kex_prop_free(proposal); + sshbuf_free(b); + return r; +} + +void +kex_prop_free(char **proposal) +{ + u_int i; + + if (proposal == NULL) + return; + for (i = 0; i < PROPOSAL_MAX; i++) + free(proposal[i]); + free(proposal); +} + +/* ARGSUSED */ +int +kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh) +{ + int r; + + error("kex protocol error: type %d seq %u", type, seq); + if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || + (r = sshpkt_put_u32(ssh, seq)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + return 0; +} + +static void +kex_reset_dispatch(struct ssh *ssh) +{ + ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, + SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); +} + +static int +kex_send_ext_info(struct ssh *ssh) +{ + int r; + char *algs; + + debug("Sending SSH2_MSG_EXT_INFO"); + if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) + return SSH_ERR_ALLOC_FAIL; + /* XXX filter algs list by allowed pubkey/hostbased types */ + if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || + (r = sshpkt_put_u32(ssh, 2)) != 0 || + (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || + (r = sshpkt_put_cstring(ssh, algs)) != 0 || + (r = sshpkt_put_cstring(ssh, + "publickey-hostbound@openssh.com")) != 0 || + (r = sshpkt_put_cstring(ssh, "0")) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_fr(r, "compose"); + goto out; + } + /* success */ + r = 0; + out: + free(algs); + return r; +} + +int +kex_send_newkeys(struct ssh *ssh) +{ + int r; + + kex_reset_dispatch(ssh); + if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + debug("SSH2_MSG_NEWKEYS sent"); + ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); + if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0) + if ((r = kex_send_ext_info(ssh)) != 0) + return r; + debug("expecting SSH2_MSG_NEWKEYS"); + return 0; +} + +int +kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + u_int32_t i, ninfo; + char *name; + u_char *val; + size_t vlen; + int r; + + debug("SSH2_MSG_EXT_INFO received"); + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); + if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) + return r; + for (i = 0; i < ninfo; i++) { + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) + return r; + if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) { + free(name); + return r; + } + if (strcmp(name, "server-sig-algs") == 0) { + /* Ensure no \0 lurking in value */ + if (memchr(val, '\0', vlen) != NULL) { + error_f("nul byte in %s", name); + return SSH_ERR_INVALID_FORMAT; + } + debug_f("%s=<%s>", name, val); + kex->server_sig_algs = val; + val = NULL; + } else if (strcmp(name, + "publickey-hostbound@openssh.com") == 0) { + /* XXX refactor */ + /* Ensure no \0 lurking in value */ + if (memchr(val, '\0', vlen) != NULL) { + error_f("nul byte in %s", name); + return SSH_ERR_INVALID_FORMAT; + } + debug_f("%s=<%s>", name, val); + if (strcmp(val, "0") == 0) + kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND; + else { + debug_f("unsupported version of %s extension", + name); + } + } else + debug_f("%s (unrecognised)", name); + free(name); + free(val); + } + return sshpkt_get_end(ssh); +} + +static int +kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + int r; + + debug("SSH2_MSG_NEWKEYS received"); + ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); + if ((r = sshpkt_get_end(ssh)) != 0) + return r; + if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) + return r; + kex->done = 1; + kex->flags &= ~KEX_INITIAL; + sshbuf_reset(kex->peer); + /* sshbuf_reset(kex->my); */ + kex->flags &= ~KEX_INIT_SENT; + free(kex->name); + kex->name = NULL; + return 0; +} + +int +kex_send_kexinit(struct ssh *ssh) +{ + u_char *cookie; + struct kex *kex = ssh->kex; + int r; + + if (kex == NULL) { + error_f("no kex"); + return SSH_ERR_INTERNAL_ERROR; + } + if (kex->flags & KEX_INIT_SENT) + return 0; + kex->done = 0; + + /* generate a random cookie */ + if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) { + error_f("bad kex length: %zu < %d", + sshbuf_len(kex->my), KEX_COOKIE_LEN); + return SSH_ERR_INVALID_FORMAT; + } + if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) { + error_f("buffer error"); + return SSH_ERR_INTERNAL_ERROR; + } + arc4random_buf(cookie, KEX_COOKIE_LEN); + + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || + (r = sshpkt_putb(ssh, kex->my)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_fr(r, "compose reply"); + return r; + } + debug("SSH2_MSG_KEXINIT sent"); + kex->flags |= KEX_INIT_SENT; + return 0; +} + +/* ARGSUSED */ +int +kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + const u_char *ptr; + u_int i; + size_t dlen; + int r; + + debug("SSH2_MSG_KEXINIT received"); + if (kex == NULL) { + error_f("no kex"); + return SSH_ERR_INTERNAL_ERROR; + } + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); + ptr = sshpkt_ptr(ssh, &dlen); + if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) + return r; + + /* discard packet */ + for (i = 0; i < KEX_COOKIE_LEN; i++) { + if ((r = sshpkt_get_u8(ssh, NULL)) != 0) { + error_fr(r, "discard cookie"); + return r; + } + } + for (i = 0; i < PROPOSAL_MAX; i++) { + if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { + error_fr(r, "discard proposal"); + return r; + } + } + /* + * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported + * KEX method has the server move first, but a server might be using + * a custom method or one that we otherwise don't support. We should + * be prepared to remember first_kex_follows here so we can eat a + * packet later. + * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means + * for cases where the server *doesn't* go first. I guess we should + * ignore it when it is set for these cases, which is what we do now. + */ + if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ + (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ + (r = sshpkt_get_end(ssh)) != 0) + return r; + + if (!(kex->flags & KEX_INIT_SENT)) + if ((r = kex_send_kexinit(ssh)) != 0) + return r; + if ((r = kex_choose_conf(ssh)) != 0) + return r; + + if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) + return (kex->kex[kex->kex_type])(ssh); + + error_f("unknown kex type %u", kex->kex_type); + return SSH_ERR_INTERNAL_ERROR; +} + +struct kex * +kex_new(void) +{ + struct kex *kex; + + if ((kex = calloc(1, sizeof(*kex))) == NULL || + (kex->peer = sshbuf_new()) == NULL || + (kex->my = sshbuf_new()) == NULL || + (kex->client_version = sshbuf_new()) == NULL || + (kex->server_version = sshbuf_new()) == NULL || + (kex->session_id = sshbuf_new()) == NULL) { + kex_free(kex); + return NULL; + } + return kex; +} + +void +kex_free_newkeys(struct newkeys *newkeys) +{ + if (newkeys == NULL) + return; + if (newkeys->enc.key) { + explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); + free(newkeys->enc.key); + newkeys->enc.key = NULL; + } + if (newkeys->enc.iv) { + explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); + free(newkeys->enc.iv); + newkeys->enc.iv = NULL; + } + free(newkeys->enc.name); + explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); + free(newkeys->comp.name); + explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); + mac_clear(&newkeys->mac); + if (newkeys->mac.key) { + explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); + free(newkeys->mac.key); + newkeys->mac.key = NULL; + } + free(newkeys->mac.name); + explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); + freezero(newkeys, sizeof(*newkeys)); +} + +void +kex_free(struct kex *kex) +{ + u_int mode; + + if (kex == NULL) + return; + +#ifdef WITH_OPENSSL + DH_free(kex->dh); +#ifdef OPENSSL_HAS_ECC + EC_KEY_free(kex->ec_client_key); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + for (mode = 0; mode < MODE_MAX; mode++) { + kex_free_newkeys(kex->newkeys[mode]); + kex->newkeys[mode] = NULL; + } + sshbuf_free(kex->peer); + sshbuf_free(kex->my); + sshbuf_free(kex->client_version); + sshbuf_free(kex->server_version); + sshbuf_free(kex->client_pub); + sshbuf_free(kex->session_id); + sshbuf_free(kex->initial_sig); + sshkey_free(kex->initial_hostkey); + free(kex->failed_choice); + free(kex->hostkey_alg); + free(kex->name); + free(kex); +} + +int +kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) +{ + int r; + + if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0) + return r; + ssh->kex->flags = KEX_INITIAL; + kex_reset_dispatch(ssh); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); + return 0; +} + +int +kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) +{ + int r; + + if ((r = kex_ready(ssh, proposal)) != 0) + return r; + if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ + kex_free(ssh->kex); + ssh->kex = NULL; + return r; + } + return 0; +} + +/* + * Request key re-exchange, returns 0 on success or a ssherr.h error + * code otherwise. Must not be called if KEX is incomplete or in-progress. + */ +int +kex_start_rekex(struct ssh *ssh) +{ + if (ssh->kex == NULL) { + error_f("no kex"); + return SSH_ERR_INTERNAL_ERROR; + } + if (ssh->kex->done == 0) { + error_f("requested twice"); + return SSH_ERR_INTERNAL_ERROR; + } + ssh->kex->done = 0; + return kex_send_kexinit(ssh); +} + +static int +choose_enc(struct sshenc *enc, char *client, char *server) +{ + char *name = match_list(client, server, NULL); + + if (name == NULL) + return SSH_ERR_NO_CIPHER_ALG_MATCH; + if ((enc->cipher = cipher_by_name(name)) == NULL) { + error_f("unsupported cipher %s", name); + free(name); + return SSH_ERR_INTERNAL_ERROR; + } + enc->name = name; + enc->enabled = 0; + enc->iv = NULL; + enc->iv_len = cipher_ivlen(enc->cipher); + enc->key = NULL; + enc->key_len = cipher_keylen(enc->cipher); + enc->block_size = cipher_blocksize(enc->cipher); + return 0; +} + +static int +choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) +{ + char *name = match_list(client, server, NULL); + + if (name == NULL) + return SSH_ERR_NO_MAC_ALG_MATCH; + if (mac_setup(mac, name) < 0) { + error_f("unsupported MAC %s", name); + free(name); + return SSH_ERR_INTERNAL_ERROR; + } + mac->name = name; + mac->key = NULL; + mac->enabled = 0; + return 0; +} + +static int +choose_comp(struct sshcomp *comp, char *client, char *server) +{ + char *name = match_list(client, server, NULL); + + if (name == NULL) + return SSH_ERR_NO_COMPRESS_ALG_MATCH; +#ifdef WITH_ZLIB + if (strcmp(name, "zlib@openssh.com") == 0) { + comp->type = COMP_DELAYED; + } else if (strcmp(name, "zlib") == 0) { + comp->type = COMP_ZLIB; + } else +#endif /* WITH_ZLIB */ + if (strcmp(name, "none") == 0) { + comp->type = COMP_NONE; + } else { + error_f("unsupported compression scheme %s", name); + free(name); + return SSH_ERR_INTERNAL_ERROR; + } + comp->name = name; + return 0; +} + +static int +choose_kex(struct kex *k, char *client, char *server) +{ + const struct kexalg *kexalg; + + k->name = match_list(client, server, NULL); + + debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); + if (k->name == NULL) + return SSH_ERR_NO_KEX_ALG_MATCH; + if ((kexalg = kex_alg_by_name(k->name)) == NULL) { + error_f("unsupported KEX method %s", k->name); + return SSH_ERR_INTERNAL_ERROR; + } + k->kex_type = kexalg->type; + k->hash_alg = kexalg->hash_alg; + k->ec_nid = kexalg->ec_nid; + return 0; +} + +static int +choose_hostkeyalg(struct kex *k, char *client, char *server) +{ + free(k->hostkey_alg); + k->hostkey_alg = match_list(client, server, NULL); + + debug("kex: host key algorithm: %s", + k->hostkey_alg ? k->hostkey_alg : "(no match)"); + if (k->hostkey_alg == NULL) + return SSH_ERR_NO_HOSTKEY_ALG_MATCH; + k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); + if (k->hostkey_type == KEY_UNSPEC) { + error_f("unsupported hostkey algorithm %s", k->hostkey_alg); + return SSH_ERR_INTERNAL_ERROR; + } + k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); + return 0; +} + +static int +proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) +{ + static int check[] = { + PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 + }; + int *idx; + char *p; + + for (idx = &check[0]; *idx != -1; idx++) { + if ((p = strchr(my[*idx], ',')) != NULL) + *p = '\0'; + if ((p = strchr(peer[*idx], ',')) != NULL) + *p = '\0'; + if (strcmp(my[*idx], peer[*idx]) != 0) { + debug2("proposal mismatch: my %s peer %s", + my[*idx], peer[*idx]); + return (0); + } + } + debug2("proposals match"); + return (1); +} + +/* returns non-zero if proposal contains any algorithm from algs */ +static int +has_any_alg(const char *proposal, const char *algs) +{ + char *cp; + + if ((cp = match_list(proposal, algs, NULL)) == NULL) + return 0; + free(cp); + return 1; +} + +static int +kex_choose_conf(struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + struct newkeys *newkeys; + char **my = NULL, **peer = NULL; + char **cprop, **sprop; + int nenc, nmac, ncomp; + u_int mode, ctos, need, dh_need, authlen; + int r, first_kex_follows; + + debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); + if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) + goto out; + debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); + if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) + goto out; + + if (kex->server) { + cprop=peer; + sprop=my; + } else { + cprop=my; + sprop=peer; + } + + /* Check whether client supports ext_info_c */ + if (kex->server && (kex->flags & KEX_INITIAL)) { + char *ext; + + ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); + kex->ext_info_c = (ext != NULL); + free(ext); + } + + /* Check whether client supports rsa-sha2 algorithms */ + if (kex->server && (kex->flags & KEX_INITIAL)) { + if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], + "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com")) + kex->flags |= KEX_RSA_SHA2_256_SUPPORTED; + if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], + "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com")) + kex->flags |= KEX_RSA_SHA2_512_SUPPORTED; + } + + /* Algorithm Negotiation */ + if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], + sprop[PROPOSAL_KEX_ALGS])) != 0) { + kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; + peer[PROPOSAL_KEX_ALGS] = NULL; + goto out; + } + if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { + kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; + peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; + goto out; + } + for (mode = 0; mode < MODE_MAX; mode++) { + if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + kex->newkeys[mode] = newkeys; + ctos = (!kex->server && mode == MODE_OUT) || + (kex->server && mode == MODE_IN); + nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; + nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; + ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; + if ((r = choose_enc(&newkeys->enc, cprop[nenc], + sprop[nenc])) != 0) { + kex->failed_choice = peer[nenc]; + peer[nenc] = NULL; + goto out; + } + authlen = cipher_authlen(newkeys->enc.cipher); + /* ignore mac for authenticated encryption */ + if (authlen == 0 && + (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], + sprop[nmac])) != 0) { + kex->failed_choice = peer[nmac]; + peer[nmac] = NULL; + goto out; + } + if ((r = choose_comp(&newkeys->comp, cprop[ncomp], + sprop[ncomp])) != 0) { + kex->failed_choice = peer[ncomp]; + peer[ncomp] = NULL; + goto out; + } + debug("kex: %s cipher: %s MAC: %s compression: %s", + ctos ? "client->server" : "server->client", + newkeys->enc.name, + authlen == 0 ? newkeys->mac.name : "", + newkeys->comp.name); + } + need = dh_need = 0; + for (mode = 0; mode < MODE_MAX; mode++) { + newkeys = kex->newkeys[mode]; + need = MAXIMUM(need, newkeys->enc.key_len); + need = MAXIMUM(need, newkeys->enc.block_size); + need = MAXIMUM(need, newkeys->enc.iv_len); + need = MAXIMUM(need, newkeys->mac.key_len); + dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher)); + dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); + dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); + dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); + } + /* XXX need runden? */ + kex->we_need = need; + kex->dh_need = dh_need; + + /* ignore the next message if the proposals do not match */ + if (first_kex_follows && !proposals_match(my, peer)) + ssh->dispatch_skip_packets = 1; + r = 0; + out: + kex_prop_free(my); + kex_prop_free(peer); + return r; +} + +static int +derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, + const struct sshbuf *shared_secret, u_char **keyp) +{ + struct kex *kex = ssh->kex; + struct ssh_digest_ctx *hashctx = NULL; + char c = id; + u_int have; + size_t mdsz; + u_char *digest; + int r; + + if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* K1 = HASH(K || H || "A" || session_id) */ + if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || + ssh_digest_update_buffer(hashctx, shared_secret) != 0 || + ssh_digest_update(hashctx, hash, hashlen) != 0 || + ssh_digest_update(hashctx, &c, 1) != 0 || + ssh_digest_update_buffer(hashctx, kex->session_id) != 0 || + ssh_digest_final(hashctx, digest, mdsz) != 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + error_f("KEX hash failed"); + goto out; + } + ssh_digest_free(hashctx); + hashctx = NULL; + + /* + * expand key: + * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) + * Key = K1 || K2 || ... || Kn + */ + for (have = mdsz; need > have; have += mdsz) { + if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || + ssh_digest_update_buffer(hashctx, shared_secret) != 0 || + ssh_digest_update(hashctx, hash, hashlen) != 0 || + ssh_digest_update(hashctx, digest, have) != 0 || + ssh_digest_final(hashctx, digest + have, mdsz) != 0) { + error_f("KDF failed"); + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + ssh_digest_free(hashctx); + hashctx = NULL; + } +#ifdef DEBUG_KEX + fprintf(stderr, "key '%c'== ", c); + dump_digest("key", digest, need); +#endif + *keyp = digest; + digest = NULL; + r = 0; + out: + free(digest); + ssh_digest_free(hashctx); + return r; +} + +#define NKEYS 6 +int +kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, + const struct sshbuf *shared_secret) +{ + struct kex *kex = ssh->kex; + u_char *keys[NKEYS]; + u_int i, j, mode, ctos; + int r; + + /* save initial hash as session id */ + if ((kex->flags & KEX_INITIAL) != 0) { + if (sshbuf_len(kex->session_id) != 0) { + error_f("already have session ID at kex"); + return SSH_ERR_INTERNAL_ERROR; + } + if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0) + return r; + } else if (sshbuf_len(kex->session_id) == 0) { + error_f("no session ID in rekex"); + return SSH_ERR_INTERNAL_ERROR; + } + for (i = 0; i < NKEYS; i++) { + if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, + shared_secret, &keys[i])) != 0) { + for (j = 0; j < i; j++) + free(keys[j]); + return r; + } + } + for (mode = 0; mode < MODE_MAX; mode++) { + ctos = (!kex->server && mode == MODE_OUT) || + (kex->server && mode == MODE_IN); + kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; + kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; + kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; + } + return 0; +} + +int +kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) +{ + struct kex *kex = ssh->kex; + + *pubp = NULL; + *prvp = NULL; + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) { + error_f("missing hostkey loader"); + return SSH_ERR_INVALID_ARGUMENT; + } + *pubp = kex->load_host_public_key(kex->hostkey_type, + kex->hostkey_nid, ssh); + *prvp = kex->load_host_private_key(kex->hostkey_type, + kex->hostkey_nid, ssh); + if (*pubp == NULL) + return SSH_ERR_NO_HOSTKEY_LOADED; + return 0; +} + +int +kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) +{ + struct kex *kex = ssh->kex; + + if (kex->verify_host_key == NULL) { + error_f("missing hostkey verifier"); + return SSH_ERR_INVALID_ARGUMENT; + } + if (server_host_key->type != kex->hostkey_type || + (kex->hostkey_type == KEY_ECDSA && + server_host_key->ecdsa_nid != kex->hostkey_nid)) + return SSH_ERR_KEY_TYPE_MISMATCH; + if (kex->verify_host_key(server_host_key, ssh) == -1) + return SSH_ERR_SIGNATURE_INVALID; + return 0; +} + +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) +void +dump_digest(const char *msg, const u_char *digest, int len) +{ + fprintf(stderr, "%s\n", msg); + sshbuf_dump_data(digest, len, stderr); +} +#endif + +/* + * Send a plaintext error message to the peer, suffixed by \r\n. + * Only used during banner exchange, and there only for the server. + */ +static void +send_error(struct ssh *ssh, char *msg) +{ + char *crnl = "\r\n"; + + if (!ssh->kex->server) + return; + + if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), + msg, strlen(msg)) != strlen(msg) || + atomicio(vwrite, ssh_packet_get_connection_out(ssh), + crnl, strlen(crnl)) != strlen(crnl)) + error_f("write: %.100s", strerror(errno)); +} + +/* + * Sends our identification string and waits for the peer's. Will block for + * up to timeout_ms (or indefinitely if timeout_ms <= 0). + * Returns on 0 success or a ssherr.h code on failure. + */ +int +kex_exchange_identification(struct ssh *ssh, int timeout_ms, + const char *version_addendum) +{ + int remote_major, remote_minor, mismatch, oerrno = 0; + size_t len, i, n; + int r, expect_nl; + u_char c; + struct sshbuf *our_version = ssh->kex->server ? + ssh->kex->server_version : ssh->kex->client_version; + struct sshbuf *peer_version = ssh->kex->server ? + ssh->kex->client_version : ssh->kex->server_version; + char *our_version_string = NULL, *peer_version_string = NULL; + char *cp, *remote_version = NULL; + + /* Prepare and send our banner */ + sshbuf_reset(our_version); + if (version_addendum != NULL && *version_addendum == '\0') + version_addendum = NULL; + if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n", + PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, + version_addendum == NULL ? "" : " ", + version_addendum == NULL ? "" : version_addendum)) != 0) { + oerrno = errno; + error_fr(r, "sshbuf_putf"); + goto out; + } + + if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), + sshbuf_mutable_ptr(our_version), + sshbuf_len(our_version)) != sshbuf_len(our_version)) { + oerrno = errno; + debug_f("write: %.100s", strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */ + oerrno = errno; + error_fr(r, "sshbuf_consume_end"); + goto out; + } + our_version_string = sshbuf_dup_string(our_version); + if (our_version_string == NULL) { + error_f("sshbuf_dup_string failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + debug("Local version string %.100s", our_version_string); + + /* Read other side's version identification. */ + for (n = 0; ; n++) { + if (n >= SSH_MAX_PRE_BANNER_LINES) { + send_error(ssh, "No SSH identification string " + "received."); + error_f("No SSH version received in first %u lines " + "from server", SSH_MAX_PRE_BANNER_LINES); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + sshbuf_reset(peer_version); + expect_nl = 0; + for (i = 0; ; i++) { + if (timeout_ms > 0) { + r = waitrfd(ssh_packet_get_connection_in(ssh), + &timeout_ms); + if (r == -1 && errno == ETIMEDOUT) { + send_error(ssh, "Timed out waiting " + "for SSH identification string."); + error("Connection timed out during " + "banner exchange"); + r = SSH_ERR_CONN_TIMEOUT; + goto out; + } else if (r == -1) { + oerrno = errno; + error_f("%s", strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + } + + len = atomicio(read, ssh_packet_get_connection_in(ssh), + &c, 1); + if (len != 1 && errno == EPIPE) { + error_f("Connection closed by remote host"); + r = SSH_ERR_CONN_CLOSED; + goto out; + } else if (len != 1) { + oerrno = errno; + error_f("read: %.100s", strerror(errno)); + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + if (c == '\r') { + expect_nl = 1; + continue; + } + if (c == '\n') + break; + if (c == '\0' || expect_nl) { + error_f("banner line contains invalid " + "characters"); + goto invalid; + } + if ((r = sshbuf_put_u8(peer_version, c)) != 0) { + oerrno = errno; + error_fr(r, "sshbuf_put"); + goto out; + } + if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { + error_f("banner line too long"); + goto invalid; + } + } + /* Is this an actual protocol banner? */ + if (sshbuf_len(peer_version) > 4 && + memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0) + break; + /* If not, then just log the line and continue */ + if ((cp = sshbuf_dup_string(peer_version)) == NULL) { + error_f("sshbuf_dup_string failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* Do not accept lines before the SSH ident from a client */ + if (ssh->kex->server) { + error_f("client sent invalid protocol identifier " + "\"%.256s\"", cp); + free(cp); + goto invalid; + } + debug_f("banner line %zu: %s", n, cp); + free(cp); + } + peer_version_string = sshbuf_dup_string(peer_version); + if (peer_version_string == NULL) + error_f("sshbuf_dup_string failed"); + /* XXX must be same size for sscanf */ + if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) { + error_f("calloc failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* + * Check that the versions match. In future this might accept + * several versions and set appropriate flags to handle them. + */ + if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n", + &remote_major, &remote_minor, remote_version) != 3) { + error("Bad remote protocol version identification: '%.100s'", + peer_version_string); + invalid: + send_error(ssh, "Invalid SSH identification string."); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + debug("Remote protocol version %d.%d, remote software version %.100s", + remote_major, remote_minor, remote_version); + compat_banner(ssh, remote_version); + + mismatch = 0; + switch (remote_major) { + case 2: + break; + case 1: + if (remote_minor != 99) + mismatch = 1; + break; + default: + mismatch = 1; + break; + } + if (mismatch) { + error("Protocol major versions differ: %d vs. %d", + PROTOCOL_MAJOR_2, remote_major); + send_error(ssh, "Protocol major versions differ."); + r = SSH_ERR_NO_PROTOCOL_VERSION; + goto out; + } + + if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) { + logit("probed from %s port %d with %s. Don't panic.", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + peer_version_string); + r = SSH_ERR_CONN_CLOSED; /* XXX */ + goto out; + } + if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) { + logit("scanned from %s port %d with %s. Don't panic.", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + peer_version_string); + r = SSH_ERR_CONN_CLOSED; /* XXX */ + goto out; + } + if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) { + logit("Remote version \"%.100s\" uses unsafe RSA signature " + "scheme; disabling use of RSA keys", remote_version); + } + /* success */ + r = 0; + out: + free(our_version_string); + free(peer_version_string); + free(remote_version); + if (r == SSH_ERR_SYSTEM_ERROR) + errno = oerrno; + return r; +} + diff --git a/aosp/external/openssh/kex.h b/aosp/external/openssh/kex.h new file mode 100644 index 0000000000000000000000000000000000000000..c35329501871a3a446c202691aed72aef76910b6 --- /dev/null +++ b/aosp/external/openssh/kex.h @@ -0,0 +1,266 @@ +/* $OpenBSD: kex.h,v 1.117 2022/01/06 21:55:23 djm Exp $ */ + +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KEX_H +#define KEX_H + +#include "mac.h" +#include "crypto_api.h" + +#ifdef WITH_OPENSSL +# include +# include +# include +# ifdef OPENSSL_HAS_ECC +# include +# else /* OPENSSL_HAS_ECC */ +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +# endif /* OPENSSL_HAS_ECC */ +#else /* WITH_OPENSSL */ +# define DH void +# define BIGNUM void +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +#endif /* WITH_OPENSSL */ + +#define KEX_COOKIE_LEN 16 + +#define KEX_DH1 "diffie-hellman-group1-sha1" +#define KEX_DH14_SHA1 "diffie-hellman-group14-sha1" +#define KEX_DH14_SHA256 "diffie-hellman-group14-sha256" +#define KEX_DH16_SHA512 "diffie-hellman-group16-sha512" +#define KEX_DH18_SHA512 "diffie-hellman-group18-sha512" +#define KEX_DHGEX_SHA1 "diffie-hellman-group-exchange-sha1" +#define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" +#define KEX_ECDH_SHA2_NISTP256 "ecdh-sha2-nistp256" +#define KEX_ECDH_SHA2_NISTP384 "ecdh-sha2-nistp384" +#define KEX_ECDH_SHA2_NISTP521 "ecdh-sha2-nistp521" +#define KEX_CURVE25519_SHA256 "curve25519-sha256" +#define KEX_CURVE25519_SHA256_OLD "curve25519-sha256@libssh.org" +#define KEX_SNTRUP761X25519_SHA512 "sntrup761x25519-sha512@openssh.com" + +#define COMP_NONE 0 +/* pre-auth compression (COMP_ZLIB) is only supported in the client */ +#define COMP_ZLIB 1 +#define COMP_DELAYED 2 + +#define CURVE25519_SIZE 32 + +enum kex_init_proposals { + PROPOSAL_KEX_ALGS, + PROPOSAL_SERVER_HOST_KEY_ALGS, + PROPOSAL_ENC_ALGS_CTOS, + PROPOSAL_ENC_ALGS_STOC, + PROPOSAL_MAC_ALGS_CTOS, + PROPOSAL_MAC_ALGS_STOC, + PROPOSAL_COMP_ALGS_CTOS, + PROPOSAL_COMP_ALGS_STOC, + PROPOSAL_LANG_CTOS, + PROPOSAL_LANG_STOC, + PROPOSAL_MAX +}; + +enum kex_modes { + MODE_IN, + MODE_OUT, + MODE_MAX +}; + +enum kex_exchange { + KEX_DH_GRP1_SHA1, + KEX_DH_GRP14_SHA1, + KEX_DH_GRP14_SHA256, + KEX_DH_GRP16_SHA512, + KEX_DH_GRP18_SHA512, + KEX_DH_GEX_SHA1, + KEX_DH_GEX_SHA256, + KEX_ECDH_SHA2, + KEX_C25519_SHA256, + KEX_KEM_SNTRUP761X25519_SHA512, + KEX_MAX +}; + +/* kex->flags */ +#define KEX_INIT_SENT 0x0001 +#define KEX_INITIAL 0x0002 +#define KEX_HAS_PUBKEY_HOSTBOUND 0x0004 +#define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */ +#define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */ + +struct sshenc { + char *name; + const struct sshcipher *cipher; + int enabled; + u_int key_len; + u_int iv_len; + u_int block_size; + u_char *key; + u_char *iv; +}; +struct sshcomp { + u_int type; + int enabled; + char *name; +}; +struct newkeys { + struct sshenc enc; + struct sshmac mac; + struct sshcomp comp; +}; + +struct ssh; +struct sshbuf; + +struct kex { + struct newkeys *newkeys[MODE_MAX]; + u_int we_need; + u_int dh_need; + int server; + char *name; + char *hostkey_alg; + int hostkey_type; + int hostkey_nid; + u_int kex_type; + char *server_sig_algs; + int ext_info_c; + struct sshbuf *my; + struct sshbuf *peer; + struct sshbuf *client_version; + struct sshbuf *server_version; + struct sshbuf *session_id; + struct sshbuf *initial_sig; + struct sshkey *initial_hostkey; + sig_atomic_t done; + u_int flags; + int hash_alg; + int ec_nid; + char *failed_choice; + int (*verify_host_key)(struct sshkey *, struct ssh *); + struct sshkey *(*load_host_public_key)(int, int, struct ssh *); + struct sshkey *(*load_host_private_key)(int, int, struct ssh *); + int (*host_key_index)(struct sshkey *, int, struct ssh *); + int (*sign)(struct ssh *, struct sshkey *, struct sshkey *, + u_char **, size_t *, const u_char *, size_t, const char *); + int (*kex[KEX_MAX])(struct ssh *); + /* kex specific state */ + DH *dh; /* DH */ + u_int min, max, nbits; /* GEX */ + EC_KEY *ec_client_key; /* ECDH */ + const EC_GROUP *ec_group; /* ECDH */ + u_char c25519_client_key[CURVE25519_SIZE]; /* 25519 + KEM */ + u_char c25519_client_pubkey[CURVE25519_SIZE]; /* 25519 */ + u_char sntrup761_client_key[crypto_kem_sntrup761_SECRETKEYBYTES]; /* KEM */ + struct sshbuf *client_pub; +}; + +int kex_names_valid(const char *); +char *kex_alg_list(char); +char *kex_names_cat(const char *, const char *); +int kex_assemble_names(char **, const char *, const char *); + +int kex_exchange_identification(struct ssh *, int, const char *); + +struct kex *kex_new(void); +int kex_ready(struct ssh *, char *[PROPOSAL_MAX]); +int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); +void kex_free_newkeys(struct newkeys *); +void kex_free(struct kex *); + +int kex_buf2prop(struct sshbuf *, int *, char ***); +int kex_prop2buf(struct sshbuf *, char *proposal[PROPOSAL_MAX]); +void kex_prop_free(char **); +int kex_load_hostkey(struct ssh *, struct sshkey **, struct sshkey **); +int kex_verify_host_key(struct ssh *, struct sshkey *); + +int kex_send_kexinit(struct ssh *); +int kex_input_kexinit(int, u_int32_t, struct ssh *); +int kex_input_ext_info(int, u_int32_t, struct ssh *); +int kex_protocol_error(int, u_int32_t, struct ssh *); +int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *); +int kex_send_newkeys(struct ssh *); +int kex_start_rekex(struct ssh *); + +int kexgex_client(struct ssh *); +int kexgex_server(struct ssh *); +int kex_gen_client(struct ssh *); +int kex_gen_server(struct ssh *); + +int kex_dh_keypair(struct kex *); +int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, + struct sshbuf **); +int kex_dh_dec(struct kex *, const struct sshbuf *, struct sshbuf **); + +int kex_ecdh_keypair(struct kex *); +int kex_ecdh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, + struct sshbuf **); +int kex_ecdh_dec(struct kex *, const struct sshbuf *, struct sshbuf **); + +int kex_c25519_keypair(struct kex *); +int kex_c25519_enc(struct kex *, const struct sshbuf *, struct sshbuf **, + struct sshbuf **); +int kex_c25519_dec(struct kex *, const struct sshbuf *, struct sshbuf **); + +int kex_kem_sntrup761x25519_keypair(struct kex *); +int kex_kem_sntrup761x25519_enc(struct kex *, const struct sshbuf *, + struct sshbuf **, struct sshbuf **); +int kex_kem_sntrup761x25519_dec(struct kex *, const struct sshbuf *, + struct sshbuf **); + +int kex_dh_keygen(struct kex *); +int kex_dh_compute_key(struct kex *, BIGNUM *, struct sshbuf *); + +int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *, + const struct sshbuf *, const struct sshbuf *, const struct sshbuf *, + int, int, int, + const BIGNUM *, const BIGNUM *, const BIGNUM *, + const BIGNUM *, const u_char *, size_t, + u_char *, size_t *); + +void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) + __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); +int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], + const u_char pub[CURVE25519_SIZE], struct sshbuf *out) + __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); +int kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE], + const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int) + __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); + +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) +void dump_digest(const char *, const u_char *, int); +#endif + +#if !defined(WITH_OPENSSL) || !defined(OPENSSL_HAS_ECC) +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#endif + +#endif diff --git a/aosp/external/openssh/kexc25519.c b/aosp/external/openssh/kexc25519.c new file mode 100644 index 0000000000000000000000000000000000000000..f13d766d7247b28422ec378b314e386a4be4251e --- /dev/null +++ b/aosp/external/openssh/kexc25519.c @@ -0,0 +1,199 @@ +/* $OpenBSD: kexc25519.c,v 1.17 2019/01/21 10:40:11 djm Exp $ */ +/* + * Copyright (c) 2019 Markus Friedl. All rights reserved. + * Copyright (c) 2010 Damien Miller. All rights reserved. + * Copyright (c) 2013 Aris Adamantiadis. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include + +#include "sshkey.h" +#include "kex.h" +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" +#include "ssh2.h" + +extern int crypto_scalarmult_curve25519(u_char a[CURVE25519_SIZE], + const u_char b[CURVE25519_SIZE], const u_char c[CURVE25519_SIZE]) + __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))) + __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE))); + +void +kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) +{ + static const u_char basepoint[CURVE25519_SIZE] = {9}; + + arc4random_buf(key, CURVE25519_SIZE); + crypto_scalarmult_curve25519(pub, key, basepoint); +} + +int +kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE], + const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int raw) +{ + u_char shared_key[CURVE25519_SIZE]; + u_char zero[CURVE25519_SIZE]; + int r; + + crypto_scalarmult_curve25519(shared_key, key, pub); + + /* Check for all-zero shared secret */ + explicit_bzero(zero, CURVE25519_SIZE); + if (timingsafe_bcmp(zero, shared_key, CURVE25519_SIZE) == 0) + return SSH_ERR_KEY_INVALID_EC_VALUE; + +#ifdef DEBUG_KEXECDH + dump_digest("shared secret", shared_key, CURVE25519_SIZE); +#endif + if (raw) + r = sshbuf_put(out, shared_key, CURVE25519_SIZE); + else + r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); + explicit_bzero(shared_key, CURVE25519_SIZE); + return r; +} + +int +kexc25519_shared_key(const u_char key[CURVE25519_SIZE], + const u_char pub[CURVE25519_SIZE], struct sshbuf *out) +{ + return kexc25519_shared_key_ext(key, pub, out, 0); +} + +int +kex_c25519_keypair(struct kex *kex) +{ + struct sshbuf *buf = NULL; + u_char *cp = NULL; + int r; + + if ((buf = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_reserve(buf, CURVE25519_SIZE, &cp)) != 0) + goto out; + kexc25519_keygen(kex->c25519_client_key, cp); +#ifdef DEBUG_KEXECDH + dump_digest("client public key c25519:", cp, CURVE25519_SIZE); +#endif + kex->client_pub = buf; + buf = NULL; + out: + sshbuf_free(buf); + return r; +} + +int +kex_c25519_enc(struct kex *kex, const struct sshbuf *client_blob, + struct sshbuf **server_blobp, struct sshbuf **shared_secretp) +{ + struct sshbuf *server_blob = NULL; + struct sshbuf *buf = NULL; + const u_char *client_pub; + u_char *server_pub; + u_char server_key[CURVE25519_SIZE]; + int r; + + *server_blobp = NULL; + *shared_secretp = NULL; + + if (sshbuf_len(client_blob) != CURVE25519_SIZE) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + client_pub = sshbuf_ptr(client_blob); +#ifdef DEBUG_KEXECDH + dump_digest("client public key 25519:", client_pub, CURVE25519_SIZE); +#endif + /* allocate space for encrypted KEM key and ECDH pub key */ + if ((server_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_reserve(server_blob, CURVE25519_SIZE, &server_pub)) != 0) + goto out; + kexc25519_keygen(server_key, server_pub); + /* allocate shared secret */ + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 0)) < 0) + goto out; +#ifdef DEBUG_KEXECDH + dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE); + dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); +#endif + *server_blobp = server_blob; + *shared_secretp = buf; + server_blob = NULL; + buf = NULL; + out: + explicit_bzero(server_key, sizeof(server_key)); + sshbuf_free(server_blob); + sshbuf_free(buf); + return r; +} + +int +kex_c25519_dec(struct kex *kex, const struct sshbuf *server_blob, + struct sshbuf **shared_secretp) +{ + struct sshbuf *buf = NULL; + const u_char *server_pub; + int r; + + *shared_secretp = NULL; + + if (sshbuf_len(server_blob) != CURVE25519_SIZE) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + server_pub = sshbuf_ptr(server_blob); +#ifdef DEBUG_KEXECDH + dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE); +#endif + /* shared secret */ + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub, + buf, 0)) < 0) + goto out; +#ifdef DEBUG_KEXECDH + dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); +#endif + *shared_secretp = buf; + buf = NULL; + out: + sshbuf_free(buf); + return r; +} diff --git a/aosp/external/openssh/kexdh.c b/aosp/external/openssh/kexdh.c new file mode 100644 index 0000000000000000000000000000000000000000..393ff48911bc6cd6fc2c2a531f550d52e6c0ebf9 --- /dev/null +++ b/aosp/external/openssh/kexdh.c @@ -0,0 +1,202 @@ +/* $OpenBSD: kexdh.c,v 1.34 2020/12/04 02:29:25 djm Exp $ */ +/* + * Copyright (c) 2019 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include +#include + +#include "openbsd-compat/openssl-compat.h" +#include + +#include "sshkey.h" +#include "kex.h" +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" +#include "dh.h" + +int +kex_dh_keygen(struct kex *kex) +{ + switch (kex->kex_type) { + case KEX_DH_GRP1_SHA1: + kex->dh = dh_new_group1(); + break; + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: + kex->dh = dh_new_group14(); + break; + case KEX_DH_GRP16_SHA512: + kex->dh = dh_new_group16(); + break; + case KEX_DH_GRP18_SHA512: + kex->dh = dh_new_group18(); + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + if (kex->dh == NULL) + return SSH_ERR_ALLOC_FAIL; + return (dh_gen_key(kex->dh, kex->we_need * 8)); +} + +int +kex_dh_compute_key(struct kex *kex, BIGNUM *dh_pub, struct sshbuf *out) +{ + BIGNUM *shared_secret = NULL; + u_char *kbuf = NULL; + size_t klen = 0; + int kout, r; + +#ifdef DEBUG_KEXDH + fprintf(stderr, "dh_pub= "); + BN_print_fp(stderr, dh_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_pub)); + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "\n"); +#endif + + if (!dh_pub_is_valid(kex->dh, dh_pub)) { + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } + klen = DH_size(kex->dh); + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((kout = DH_compute_key(kbuf, dh_pub, kex->dh)) < 0 || + BN_bin2bn(kbuf, kout, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, kout); +#endif + r = sshbuf_put_bignum2(out, shared_secret); + out: + freezero(kbuf, klen); + BN_clear_free(shared_secret); + return r; +} + +int +kex_dh_keypair(struct kex *kex) +{ + const BIGNUM *pub_key; + struct sshbuf *buf = NULL; + int r; + + if ((r = kex_dh_keygen(kex)) != 0) + return r; + DH_get0_key(kex->dh, &pub_key, NULL); + if ((buf = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_bignum2(buf, pub_key)) != 0 || + (r = sshbuf_get_u32(buf, NULL)) != 0) + goto out; +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, pub_key); + fprintf(stderr, "\n"); +#endif + kex->client_pub = buf; + buf = NULL; + out: + sshbuf_free(buf); + return r; +} + +int +kex_dh_enc(struct kex *kex, const struct sshbuf *client_blob, + struct sshbuf **server_blobp, struct sshbuf **shared_secretp) +{ + const BIGNUM *pub_key; + struct sshbuf *server_blob = NULL; + int r; + + *server_blobp = NULL; + *shared_secretp = NULL; + + if ((r = kex_dh_keygen(kex)) != 0) + goto out; + DH_get0_key(kex->dh, &pub_key, NULL); + if ((server_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_bignum2(server_blob, pub_key)) != 0 || + (r = sshbuf_get_u32(server_blob, NULL)) != 0) + goto out; + if ((r = kex_dh_dec(kex, client_blob, shared_secretp)) != 0) + goto out; + *server_blobp = server_blob; + server_blob = NULL; + out: + DH_free(kex->dh); + kex->dh = NULL; + sshbuf_free(server_blob); + return r; +} + +int +kex_dh_dec(struct kex *kex, const struct sshbuf *dh_blob, + struct sshbuf **shared_secretp) +{ + struct sshbuf *buf = NULL; + BIGNUM *dh_pub = NULL; + int r; + + *shared_secretp = NULL; + + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_stringb(buf, dh_blob)) != 0 || + (r = sshbuf_get_bignum2(buf, &dh_pub)) != 0) + goto out; + sshbuf_reset(buf); + if ((r = kex_dh_compute_key(kex, dh_pub, buf)) != 0) + goto out; + *shared_secretp = buf; + buf = NULL; + out: + BN_free(dh_pub); + DH_free(kex->dh); + kex->dh = NULL; + sshbuf_free(buf); + return r; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/kexecdh.c b/aosp/external/openssh/kexecdh.c new file mode 100644 index 0000000000000000000000000000000000000000..efb2e55a6d42481d4058b0fe3055d36199a12531 --- /dev/null +++ b/aosp/external/openssh/kexecdh.c @@ -0,0 +1,239 @@ +/* $OpenBSD: kexecdh.c,v 1.10 2019/01/21 10:40:11 djm Exp $ */ +/* + * Copyright (c) 2010 Damien Miller. All rights reserved. + * Copyright (c) 2019 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + +#include + +#include +#include +#include + +#include + +#include "sshkey.h" +#include "kex.h" +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" + +static int +kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key, + const EC_GROUP *, struct sshbuf **); + +int +kex_ecdh_keypair(struct kex *kex) +{ + EC_KEY *client_key = NULL; + const EC_GROUP *group; + const EC_POINT *public_key; + struct sshbuf *buf = NULL; + int r; + + if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(client_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + group = EC_KEY_get0_group(client_key); + public_key = EC_KEY_get0_public_key(client_key); + + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_ec(buf, public_key, group)) != 0 || + (r = sshbuf_get_u32(buf, NULL)) != 0) + goto out; +#ifdef DEBUG_KEXECDH + fputs("client private key:\n", stderr); + sshkey_dump_ec_key(client_key); +#endif + kex->ec_client_key = client_key; + kex->ec_group = group; + client_key = NULL; /* owned by the kex */ + kex->client_pub = buf; + buf = NULL; + out: + EC_KEY_free(client_key); + sshbuf_free(buf); + return r; +} + +int +kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob, + struct sshbuf **server_blobp, struct sshbuf **shared_secretp) +{ + const EC_GROUP *group; + const EC_POINT *pub_key; + EC_KEY *server_key = NULL; + struct sshbuf *server_blob = NULL; + int r; + + *server_blobp = NULL; + *shared_secretp = NULL; + + if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(server_key) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + group = EC_KEY_get0_group(server_key); + +#ifdef DEBUG_KEXECDH + fputs("server private key:\n", stderr); + sshkey_dump_ec_key(server_key); +#endif + pub_key = EC_KEY_get0_public_key(server_key); + if ((server_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_ec(server_blob, pub_key, group)) != 0 || + (r = sshbuf_get_u32(server_blob, NULL)) != 0) + goto out; + if ((r = kex_ecdh_dec_key_group(kex, client_blob, server_key, group, + shared_secretp)) != 0) + goto out; + *server_blobp = server_blob; + server_blob = NULL; + out: + EC_KEY_free(server_key); + sshbuf_free(server_blob); + return r; +} + +static int +kex_ecdh_dec_key_group(struct kex *kex, const struct sshbuf *ec_blob, + EC_KEY *key, const EC_GROUP *group, struct sshbuf **shared_secretp) +{ + struct sshbuf *buf = NULL; + BIGNUM *shared_secret = NULL; + EC_POINT *dh_pub = NULL; + u_char *kbuf = NULL; + size_t klen = 0; + int r; + + *shared_secretp = NULL; + + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_stringb(buf, ec_blob)) != 0) + goto out; + if ((dh_pub = EC_POINT_new(group)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_ec(buf, dh_pub, group)) != 0) { + goto out; + } + sshbuf_reset(buf); + +#ifdef DEBUG_KEXECDH + fputs("public key:\n", stderr); + sshkey_dump_ec_point(group, dh_pub); +#endif + if (sshkey_ec_validate_public(group, dh_pub) != 0) { + r = SSH_ERR_MESSAGE_INCOMPLETE; + goto out; + } + klen = (EC_GROUP_get_degree(group) + 7) / 8; + if ((kbuf = malloc(klen)) == NULL || + (shared_secret = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (ECDH_compute_key(kbuf, klen, dh_pub, key, NULL) != (int)klen || + BN_bin2bn(kbuf, klen, shared_secret) == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +#ifdef DEBUG_KEXECDH + dump_digest("shared secret", kbuf, klen); +#endif + if ((r = sshbuf_put_bignum2(buf, shared_secret)) != 0) + goto out; + *shared_secretp = buf; + buf = NULL; + out: + EC_POINT_clear_free(dh_pub); + BN_clear_free(shared_secret); + freezero(kbuf, klen); + sshbuf_free(buf); + return r; +} + +int +kex_ecdh_dec(struct kex *kex, const struct sshbuf *server_blob, + struct sshbuf **shared_secretp) +{ + int r; + + r = kex_ecdh_dec_key_group(kex, server_blob, kex->ec_client_key, + kex->ec_group, shared_secretp); + EC_KEY_free(kex->ec_client_key); + kex->ec_client_key = NULL; + return r; +} + +#else + +#include "ssherr.h" + +struct kex; +struct sshbuf; +struct sshkey; + +int +kex_ecdh_keypair(struct kex *kex) +{ + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} + +int +kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob, + struct sshbuf **server_blobp, struct sshbuf **shared_secretp) +{ + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} + +int +kex_ecdh_dec(struct kex *kex, const struct sshbuf *server_blob, + struct sshbuf **shared_secretp) +{ + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} +#endif /* defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) */ diff --git a/aosp/external/openssh/kexgen.c b/aosp/external/openssh/kexgen.c new file mode 100644 index 0000000000000000000000000000000000000000..20f3c57110a8491e20a8bd6206bf2087da1490ca --- /dev/null +++ b/aosp/external/openssh/kexgen.c @@ -0,0 +1,371 @@ +/* $OpenBSD: kexgen.c,v 1.8 2021/12/19 22:08:06 djm Exp $ */ +/* + * Copyright (c) 2019 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#include "sshkey.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "ssh2.h" +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" + +static int input_kex_gen_init(int, u_int32_t, struct ssh *); +static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); + +static int +kex_gen_hash( + int hash_alg, + const struct sshbuf *client_version, + const struct sshbuf *server_version, + const struct sshbuf *client_kexinit, + const struct sshbuf *server_kexinit, + const struct sshbuf *server_host_key_blob, + const struct sshbuf *client_pub, + const struct sshbuf *server_pub, + const struct sshbuf *shared_secret, + u_char *hash, size_t *hashlen) +{ + struct sshbuf *b; + int r; + + if (*hashlen < ssh_digest_bytes(hash_alg)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_stringb(b, client_version)) != 0 || + (r = sshbuf_put_stringb(b, server_version)) != 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, sshbuf_len(client_kexinit) + 1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_putb(b, client_kexinit)) != 0 || + (r = sshbuf_put_u32(b, sshbuf_len(server_kexinit) + 1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_putb(b, server_kexinit)) != 0 || + (r = sshbuf_put_stringb(b, server_host_key_blob)) != 0 || + (r = sshbuf_put_stringb(b, client_pub)) != 0 || + (r = sshbuf_put_stringb(b, server_pub)) != 0 || + (r = sshbuf_putb(b, shared_secret)) != 0) { + sshbuf_free(b); + return r; + } +#ifdef DEBUG_KEX + sshbuf_dump(b, stderr); +#endif + if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); + *hashlen = ssh_digest_bytes(hash_alg); +#ifdef DEBUG_KEX + dump_digest("hash", hash, *hashlen); +#endif + return 0; +} + +int +kex_gen_client(struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + int r; + + switch (kex->kex_type) { +#ifdef WITH_OPENSSL + case KEX_DH_GRP1_SHA1: + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: + case KEX_DH_GRP16_SHA512: + case KEX_DH_GRP18_SHA512: + r = kex_dh_keypair(kex); + break; + case KEX_ECDH_SHA2: + r = kex_ecdh_keypair(kex); + break; +#endif + case KEX_C25519_SHA256: + r = kex_c25519_keypair(kex); + break; + case KEX_KEM_SNTRUP761X25519_SHA512: + r = kex_kem_sntrup761x25519_keypair(kex); + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + if (r != 0) + return r; + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_INIT)) != 0 || + (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &input_kex_gen_reply); + return 0; +} + +static int +input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + struct sshkey *server_host_key = NULL; + struct sshbuf *shared_secret = NULL; + struct sshbuf *server_blob = NULL; + struct sshbuf *tmp = NULL, *server_host_key_blob = NULL; + u_char *signature = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, hashlen; + int r; + + debug("SSH2_MSG_KEX_ECDH_REPLY received"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_REPLY, &kex_protocol_error); + + /* hostkey */ + if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) + goto out; + /* sshkey_fromb() consumes its buffer, so make a copy */ + if ((tmp = sshbuf_fromb(server_host_key_blob)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshkey_fromb(tmp, &server_host_key)) != 0) + goto out; + if ((r = kex_verify_host_key(ssh, server_host_key)) != 0) + goto out; + + /* Q_S, server public key */ + /* signed H */ + if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + + /* compute shared secret */ + switch (kex->kex_type) { +#ifdef WITH_OPENSSL + case KEX_DH_GRP1_SHA1: + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: + case KEX_DH_GRP16_SHA512: + case KEX_DH_GRP18_SHA512: + r = kex_dh_dec(kex, server_blob, &shared_secret); + break; + case KEX_ECDH_SHA2: + r = kex_ecdh_dec(kex, server_blob, &shared_secret); + break; +#endif + case KEX_C25519_SHA256: + r = kex_c25519_dec(kex, server_blob, &shared_secret); + break; + case KEX_KEM_SNTRUP761X25519_SHA512: + r = kex_kem_sntrup761x25519_dec(kex, server_blob, + &shared_secret); + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + if (r !=0 ) + goto out; + + /* calc and verify H */ + hashlen = sizeof(hash); + if ((r = kex_gen_hash( + kex->hash_alg, + kex->client_version, + kex->server_version, + kex->my, + kex->peer, + server_host_key_blob, + kex->client_pub, + server_blob, + shared_secret, + hash, &hashlen)) != 0) + goto out; + + if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen, + kex->hostkey_alg, ssh->compat, NULL)) != 0) + goto out; + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 || + (r = kex_send_newkeys(ssh)) != 0) + goto out; + + /* save initial signature and hostkey */ + if ((kex->flags & KEX_INITIAL) != 0) { + if (kex->initial_hostkey != NULL || kex->initial_sig != NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if ((kex->initial_sig = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put(kex->initial_sig, signature, slen)) != 0) + goto out; + kex->initial_hostkey = server_host_key; + server_host_key = NULL; + } + /* success */ +out: + explicit_bzero(hash, sizeof(hash)); + explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); + explicit_bzero(kex->sntrup761_client_key, + sizeof(kex->sntrup761_client_key)); + sshbuf_free(server_host_key_blob); + free(signature); + sshbuf_free(tmp); + sshkey_free(server_host_key); + sshbuf_free(server_blob); + sshbuf_free(shared_secret); + sshbuf_free(kex->client_pub); + kex->client_pub = NULL; + return r; +} + +int +kex_gen_server(struct ssh *ssh) +{ + debug("expecting SSH2_MSG_KEX_ECDH_INIT"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &input_kex_gen_init); + return 0; +} + +static int +input_kex_gen_init(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + struct sshkey *server_host_private, *server_host_public; + struct sshbuf *shared_secret = NULL; + struct sshbuf *server_pubkey = NULL; + struct sshbuf *client_pubkey = NULL; + struct sshbuf *server_host_key_blob = NULL; + u_char *signature = NULL, hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, hashlen; + int r; + + debug("SSH2_MSG_KEX_ECDH_INIT received"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_ECDH_INIT, &kex_protocol_error); + + if ((r = kex_load_hostkey(ssh, &server_host_private, + &server_host_public)) != 0) + goto out; + + if ((r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + + /* compute shared secret */ + switch (kex->kex_type) { +#ifdef WITH_OPENSSL + case KEX_DH_GRP1_SHA1: + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: + case KEX_DH_GRP16_SHA512: + case KEX_DH_GRP18_SHA512: + r = kex_dh_enc(kex, client_pubkey, &server_pubkey, + &shared_secret); + break; + case KEX_ECDH_SHA2: + r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey, + &shared_secret); + break; +#endif + case KEX_C25519_SHA256: + r = kex_c25519_enc(kex, client_pubkey, &server_pubkey, + &shared_secret); + break; + case KEX_KEM_SNTRUP761X25519_SHA512: + r = kex_kem_sntrup761x25519_enc(kex, client_pubkey, + &server_pubkey, &shared_secret); + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + if (r !=0 ) + goto out; + + /* calc H */ + if ((server_host_key_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshkey_putb(server_host_public, server_host_key_blob)) != 0) + goto out; + hashlen = sizeof(hash); + if ((r = kex_gen_hash( + kex->hash_alg, + kex->client_version, + kex->server_version, + kex->peer, + kex->my, + server_host_key_blob, + client_pubkey, + server_pubkey, + shared_secret, + hash, &hashlen)) != 0) + goto out; + + /* sign H */ + if ((r = kex->sign(ssh, server_host_private, server_host_public, + &signature, &slen, hash, hashlen, kex->hostkey_alg)) != 0) + goto out; + + /* send server hostkey, ECDH pubkey 'Q_S' and signed H */ + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 || + (r = sshpkt_put_stringb(ssh, server_host_key_blob)) != 0 || + (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 || + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 || + (r = kex_send_newkeys(ssh)) != 0) + goto out; + /* retain copy of hostkey used at initial KEX */ + if (kex->initial_hostkey == NULL && + (r = sshkey_from_private(server_host_public, + &kex->initial_hostkey)) != 0) + goto out; + /* success */ +out: + explicit_bzero(hash, sizeof(hash)); + sshbuf_free(server_host_key_blob); + free(signature); + sshbuf_free(shared_secret); + sshbuf_free(client_pubkey); + sshbuf_free(server_pubkey); + return r; +} diff --git a/aosp/external/openssh/kexgex.c b/aosp/external/openssh/kexgex.c new file mode 100644 index 0000000000000000000000000000000000000000..8040a13202fc03bbfe65590d9082f58b3359e82a --- /dev/null +++ b/aosp/external/openssh/kexgex.c @@ -0,0 +1,104 @@ +/* $OpenBSD: kexgex.c,v 1.32 2019/01/23 00:30:41 djm Exp $ */ +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include + +#include "openbsd-compat/openssl-compat.h" + +#include "sshkey.h" +#include "cipher.h" +#include "kex.h" +#include "ssh2.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "digest.h" + +int +kexgex_hash( + int hash_alg, + const struct sshbuf *client_version, + const struct sshbuf *server_version, + const struct sshbuf *client_kexinit, + const struct sshbuf *server_kexinit, + const struct sshbuf *server_host_key_blob, + int min, int wantbits, int max, + const BIGNUM *prime, + const BIGNUM *gen, + const BIGNUM *client_dh_pub, + const BIGNUM *server_dh_pub, + const u_char *shared_secret, size_t secretlen, + u_char *hash, size_t *hashlen) +{ + struct sshbuf *b; + int r; + + if (*hashlen < ssh_digest_bytes(SSH_DIGEST_SHA1)) + return SSH_ERR_INVALID_ARGUMENT; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_stringb(b, client_version)) < 0 || + (r = sshbuf_put_stringb(b, server_version)) < 0 || + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + (r = sshbuf_put_u32(b, sshbuf_len(client_kexinit) + 1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_putb(b, client_kexinit)) != 0 || + (r = sshbuf_put_u32(b, sshbuf_len(server_kexinit) + 1)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) != 0 || + (r = sshbuf_putb(b, server_kexinit)) != 0 || + (r = sshbuf_put_stringb(b, server_host_key_blob)) != 0 || + (min != -1 && (r = sshbuf_put_u32(b, min)) != 0) || + (r = sshbuf_put_u32(b, wantbits)) != 0 || + (max != -1 && (r = sshbuf_put_u32(b, max)) != 0) || + (r = sshbuf_put_bignum2(b, prime)) != 0 || + (r = sshbuf_put_bignum2(b, gen)) != 0 || + (r = sshbuf_put_bignum2(b, client_dh_pub)) != 0 || + (r = sshbuf_put_bignum2(b, server_dh_pub)) != 0 || + (r = sshbuf_put(b, shared_secret, secretlen)) != 0) { + sshbuf_free(b); + return r; + } +#ifdef DEBUG_KEXDH + sshbuf_dump(b, stderr); +#endif + if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { + sshbuf_free(b); + return SSH_ERR_LIBCRYPTO_ERROR; + } + sshbuf_free(b); + *hashlen = ssh_digest_bytes(hash_alg); +#ifdef DEBUG_KEXDH + dump_digest("hash", hash, *hashlen); +#endif + return 0; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/kexgexc.c b/aosp/external/openssh/kexgexc.c new file mode 100644 index 0000000000000000000000000000000000000000..e99e0cf216e79fa2778e419390f3868385faca45 --- /dev/null +++ b/aosp/external/openssh/kexgexc.c @@ -0,0 +1,241 @@ +/* $OpenBSD: kexgexc.c,v 1.38 2021/12/19 22:08:06 djm Exp $ */ +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include + +#include +#include +#include +#include + +#include "openbsd-compat/openssl-compat.h" + +#include "sshkey.h" +#include "cipher.h" +#include "digest.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#include "compat.h" +#include "dispatch.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "misc.h" + +static int input_kex_dh_gex_group(int, u_int32_t, struct ssh *); +static int input_kex_dh_gex_reply(int, u_int32_t, struct ssh *); + +int +kexgex_client(struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + int r; + u_int nbits; + + nbits = dh_estimate(kex->dh_need * 8); + + kex->min = DH_GRP_MIN; + kex->max = DH_GRP_MAX; + kex->nbits = nbits; + if (ssh->compat & SSH_BUG_DHGEX_LARGE) + kex->nbits = MINIMUM(kex->nbits, 4096); + /* New GEX request */ + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, kex->min)) != 0 || + (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || + (r = sshpkt_put_u32(ssh, kex->max)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent", + kex->min, kex->nbits, kex->max); +#ifdef DEBUG_KEXDH + fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", + kex->min, kex->nbits, kex->max); +#endif + debug("expecting SSH2_MSG_KEX_DH_GEX_GROUP"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, + &input_kex_dh_gex_group); + r = 0; + out: + return r; +} + +static int +input_kex_dh_gex_group(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + BIGNUM *p = NULL, *g = NULL; + const BIGNUM *pub_key; + int r, bits; + + debug("SSH2_MSG_KEX_DH_GEX_GROUP received"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, &kex_protocol_error); + + if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 || + (r = sshpkt_get_bignum2(ssh, &g)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + if ((bits = BN_num_bits(p)) < 0 || + (u_int)bits < kex->min || (u_int)bits > kex->max) { + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } + if ((kex->dh = dh_new_group(g, p)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + p = g = NULL; /* belong to kex->dh now */ + + /* generate and send 'e', client DH public key */ + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) + goto out; + DH_get0_key(kex->dh, &pub_key, NULL); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || + (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, pub_key); + fprintf(stderr, "\n"); +#endif + debug("expecting SSH2_MSG_KEX_DH_GEX_REPLY"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); + r = 0; +out: + BN_clear_free(p); + BN_clear_free(g); + return r; +} + +static int +input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + BIGNUM *dh_server_pub = NULL; + const BIGNUM *pub_key, *dh_p, *dh_g; + struct sshbuf *shared_secret = NULL; + struct sshbuf *tmp = NULL, *server_host_key_blob = NULL; + struct sshkey *server_host_key = NULL; + u_char *signature = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, hashlen; + int r; + + debug("SSH2_MSG_KEX_DH_GEX_REPLY received"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &kex_protocol_error); + + /* key, cert */ + if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0) + goto out; + /* sshkey_fromb() consumes its buffer, so make a copy */ + if ((tmp = sshbuf_fromb(server_host_key_blob)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshkey_fromb(tmp, &server_host_key)) != 0 || + (r = kex_verify_host_key(ssh, server_host_key)) != 0) + goto out; + /* DH parameter f, server public DH key, signed H */ + if ((r = sshpkt_get_bignum2(ssh, &dh_server_pub)) != 0 || + (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + if ((shared_secret = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) + goto out; + if (ssh->compat & SSH_OLD_DHGEX) + kex->min = kex->max = -1; + + /* calc and verify H */ + DH_get0_key(kex->dh, &pub_key, NULL); + DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); + hashlen = sizeof(hash); + if ((r = kexgex_hash( + kex->hash_alg, + kex->client_version, + kex->server_version, + kex->my, + kex->peer, + server_host_key_blob, + kex->min, kex->nbits, kex->max, + dh_p, dh_g, + pub_key, + dh_server_pub, + sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), + hash, &hashlen)) != 0) + goto out; + + if ((r = sshkey_verify(server_host_key, signature, slen, hash, + hashlen, kex->hostkey_alg, ssh->compat, NULL)) != 0) + goto out; + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 || + (r = kex_send_newkeys(ssh)) != 0) + goto out; + + /* save initial signature and hostkey */ + if ((kex->flags & KEX_INITIAL) != 0) { + if (kex->initial_hostkey != NULL || kex->initial_sig != NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if ((kex->initial_sig = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put(kex->initial_sig, signature, slen)) != 0) + goto out; + kex->initial_hostkey = server_host_key; + server_host_key = NULL; + } + /* success */ + out: + explicit_bzero(hash, sizeof(hash)); + DH_free(kex->dh); + kex->dh = NULL; + BN_clear_free(dh_server_pub); + sshbuf_free(shared_secret); + sshkey_free(server_host_key); + sshbuf_free(tmp); + sshbuf_free(server_host_key_blob); + free(signature); + return r; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/kexgexs.c b/aosp/external/openssh/kexgexs.c new file mode 100644 index 0000000000000000000000000000000000000000..72b444f6906b262f426515481d5add0dec8da12c --- /dev/null +++ b/aosp/external/openssh/kexgexs.c @@ -0,0 +1,217 @@ +/* $OpenBSD: kexgexs.c,v 1.44 2021/12/19 22:08:06 djm Exp $ */ +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + + +#include +#include +#include +#include + +#include + +#include "openbsd-compat/openssl-compat.h" + +#include "sshkey.h" +#include "cipher.h" +#include "digest.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#include "compat.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "dispatch.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "misc.h" + +static int input_kex_dh_gex_request(int, u_int32_t, struct ssh *); +static int input_kex_dh_gex_init(int, u_int32_t, struct ssh *); + +int +kexgex_server(struct ssh *ssh) +{ + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, + &input_kex_dh_gex_request); + debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST"); + return 0; +} + +static int +input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + int r; + u_int min = 0, max = 0, nbits = 0; + const BIGNUM *dh_p, *dh_g; + + debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, &kex_protocol_error); + + if ((r = sshpkt_get_u32(ssh, &min)) != 0 || + (r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_u32(ssh, &max)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; + kex->min = min; + kex->max = max; + min = MAXIMUM(DH_GRP_MIN, min); + max = MINIMUM(DH_GRP_MAX, max); + nbits = MAXIMUM(DH_GRP_MIN, nbits); + nbits = MINIMUM(DH_GRP_MAX, nbits); + + if (kex->max < kex->min || kex->nbits < kex->min || + kex->max < kex->nbits || kex->max < DH_GRP_MIN) { + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } + + /* Contact privileged parent */ + kex->dh = PRIVSEP(choose_dh(min, nbits, max)); + if (kex->dh == NULL) { + sshpkt_disconnect(ssh, "no matching DH grp found"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); + DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || + (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 || + (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + /* Compute our exchange value in parallel with the client */ + if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) + goto out; + + debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); + r = 0; + out: + return r; +} + +static int +input_kex_dh_gex_init(int type, u_int32_t seq, struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + BIGNUM *dh_client_pub = NULL; + const BIGNUM *pub_key, *dh_p, *dh_g; + struct sshbuf *shared_secret = NULL; + struct sshbuf *server_host_key_blob = NULL; + struct sshkey *server_host_public, *server_host_private; + u_char *signature = NULL; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t slen, hashlen; + int r; + + debug("SSH2_MSG_KEX_DH_GEX_INIT received"); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &kex_protocol_error); + + if ((r = kex_load_hostkey(ssh, &server_host_private, + &server_host_public)) != 0) + goto out; + + /* key, cert */ + if ((r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + if ((shared_secret = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0) + goto out; + if ((server_host_key_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshkey_putb(server_host_public, server_host_key_blob)) != 0) + goto out; + + /* calc H */ + DH_get0_key(kex->dh, &pub_key, NULL); + DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); + hashlen = sizeof(hash); + if ((r = kexgex_hash( + kex->hash_alg, + kex->client_version, + kex->server_version, + kex->peer, + kex->my, + server_host_key_blob, + kex->min, kex->nbits, kex->max, + dh_p, dh_g, + dh_client_pub, + pub_key, + sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), + hash, &hashlen)) != 0) + goto out; + + /* sign H */ + if ((r = kex->sign(ssh, server_host_private, server_host_public, + &signature, &slen, hash, hashlen, kex->hostkey_alg)) < 0) + goto out; + + /* send server hostkey, DH pubkey 'f' and signed H */ + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || + (r = sshpkt_put_stringb(ssh, server_host_key_blob)) != 0 || + (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 || + (r = kex_send_newkeys(ssh)) != 0) + goto out; + + /* retain copy of hostkey used at initial KEX */ + if (kex->initial_hostkey == NULL && + (r = sshkey_from_private(server_host_public, + &kex->initial_hostkey)) != 0) + goto out; + /* success */ + out: + explicit_bzero(hash, sizeof(hash)); + DH_free(kex->dh); + kex->dh = NULL; + BN_clear_free(dh_client_pub); + sshbuf_free(shared_secret); + sshbuf_free(server_host_key_blob); + free(signature); + return r; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/kexsntrup761x25519.c b/aosp/external/openssh/kexsntrup761x25519.c new file mode 100644 index 0000000000000000000000000000000000000000..6afb1bad560dd044b37910f911b15ddd11eedb91 --- /dev/null +++ b/aosp/external/openssh/kexsntrup761x25519.c @@ -0,0 +1,251 @@ +/* $OpenBSD: kexsntrup761x25519.c,v 1.2 2021/12/05 12:28:27 jsg Exp $ */ +/* + * Copyright (c) 2019 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef USE_SNTRUP761X25519 + +#include + +#include +#include +#include + +#include "sshkey.h" +#include "kex.h" +#include "sshbuf.h" +#include "digest.h" +#include "ssherr.h" + +int +kex_kem_sntrup761x25519_keypair(struct kex *kex) +{ + struct sshbuf *buf = NULL; + u_char *cp = NULL; + size_t need; + int r; + + if ((buf = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; + if ((r = sshbuf_reserve(buf, need, &cp)) != 0) + goto out; + crypto_kem_sntrup761_keypair(cp, kex->sntrup761_client_key); +#ifdef DEBUG_KEXECDH + dump_digest("client public key sntrup761:", cp, + crypto_kem_sntrup761_PUBLICKEYBYTES); +#endif + cp += crypto_kem_sntrup761_PUBLICKEYBYTES; + kexc25519_keygen(kex->c25519_client_key, cp); +#ifdef DEBUG_KEXECDH + dump_digest("client public key c25519:", cp, CURVE25519_SIZE); +#endif + kex->client_pub = buf; + buf = NULL; + out: + sshbuf_free(buf); + return r; +} + +int +kex_kem_sntrup761x25519_enc(struct kex *kex, + const struct sshbuf *client_blob, struct sshbuf **server_blobp, + struct sshbuf **shared_secretp) +{ + struct sshbuf *server_blob = NULL; + struct sshbuf *buf = NULL; + const u_char *client_pub; + u_char *kem_key, *ciphertext, *server_pub; + u_char server_key[CURVE25519_SIZE]; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t need; + int r; + + *server_blobp = NULL; + *shared_secretp = NULL; + + /* client_blob contains both KEM and ECDH client pubkeys */ + need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; + if (sshbuf_len(client_blob) != need) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + client_pub = sshbuf_ptr(client_blob); +#ifdef DEBUG_KEXECDH + dump_digest("client public key sntrup761:", client_pub, + crypto_kem_sntrup761_PUBLICKEYBYTES); + dump_digest("client public key 25519:", + client_pub + crypto_kem_sntrup761_PUBLICKEYBYTES, + CURVE25519_SIZE); +#endif + /* allocate buffer for concatenation of KEM key and ECDH shared key */ + /* the buffer will be hashed and the result is the shared secret */ + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_reserve(buf, crypto_kem_sntrup761_BYTES, + &kem_key)) != 0) + goto out; + /* allocate space for encrypted KEM key and ECDH pub key */ + if ((server_blob = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE; + if ((r = sshbuf_reserve(server_blob, need, &ciphertext)) != 0) + goto out; + /* generate and encrypt KEM key with client key */ + crypto_kem_sntrup761_enc(ciphertext, kem_key, client_pub); + /* generate ECDH key pair, store server pubkey after ciphertext */ + server_pub = ciphertext + crypto_kem_sntrup761_CIPHERTEXTBYTES; + kexc25519_keygen(server_key, server_pub); + /* append ECDH shared key */ + client_pub += crypto_kem_sntrup761_PUBLICKEYBYTES; + if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0) + goto out; + if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0) + goto out; +#ifdef DEBUG_KEXECDH + dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE); + dump_digest("server cipher text:", ciphertext, + crypto_kem_sntrup761_CIPHERTEXTBYTES); + dump_digest("server kem key:", kem_key, crypto_kem_sntrup761_BYTES); + dump_digest("concatenation of KEM key and ECDH shared key:", + sshbuf_ptr(buf), sshbuf_len(buf)); +#endif + /* string-encoded hash is resulting shared secret */ + sshbuf_reset(buf); + if ((r = sshbuf_put_string(buf, hash, + ssh_digest_bytes(kex->hash_alg))) != 0) + goto out; +#ifdef DEBUG_KEXECDH + dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); +#endif + *server_blobp = server_blob; + *shared_secretp = buf; + server_blob = NULL; + buf = NULL; + out: + explicit_bzero(hash, sizeof(hash)); + explicit_bzero(server_key, sizeof(server_key)); + sshbuf_free(server_blob); + sshbuf_free(buf); + return r; +} + +int +kex_kem_sntrup761x25519_dec(struct kex *kex, + const struct sshbuf *server_blob, struct sshbuf **shared_secretp) +{ + struct sshbuf *buf = NULL; + u_char *kem_key = NULL; + const u_char *ciphertext, *server_pub; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t need; + int r, decoded; + + *shared_secretp = NULL; + + need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE; + if (sshbuf_len(server_blob) != need) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + ciphertext = sshbuf_ptr(server_blob); + server_pub = ciphertext + crypto_kem_sntrup761_CIPHERTEXTBYTES; +#ifdef DEBUG_KEXECDH + dump_digest("server cipher text:", ciphertext, + crypto_kem_sntrup761_CIPHERTEXTBYTES); + dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE); +#endif + /* hash concatenation of KEM key and ECDH shared key */ + if ((buf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_reserve(buf, crypto_kem_sntrup761_BYTES, + &kem_key)) != 0) + goto out; + decoded = crypto_kem_sntrup761_dec(kem_key, ciphertext, + kex->sntrup761_client_key); + if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub, + buf, 1)) < 0) + goto out; + if ((r = ssh_digest_buffer(kex->hash_alg, buf, hash, sizeof(hash))) != 0) + goto out; +#ifdef DEBUG_KEXECDH + dump_digest("client kem key:", kem_key, crypto_kem_sntrup761_BYTES); + dump_digest("concatenation of KEM key and ECDH shared key:", + sshbuf_ptr(buf), sshbuf_len(buf)); +#endif + sshbuf_reset(buf); + if ((r = sshbuf_put_string(buf, hash, + ssh_digest_bytes(kex->hash_alg))) != 0) + goto out; +#ifdef DEBUG_KEXECDH + dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); +#endif + if (decoded != 0) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + *shared_secretp = buf; + buf = NULL; + out: + explicit_bzero(hash, sizeof(hash)); + sshbuf_free(buf); + return r; +} + +#else + +#include "ssherr.h" + +struct kex; +struct sshbuf; +struct sshkey; + +int +kex_kem_sntrup761x25519_keypair(struct kex *kex) +{ + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} + +int +kex_kem_sntrup761x25519_enc(struct kex *kex, + const struct sshbuf *client_blob, struct sshbuf **server_blobp, + struct sshbuf **shared_secretp) +{ + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} + +int +kex_kem_sntrup761x25519_dec(struct kex *kex, + const struct sshbuf *server_blob, struct sshbuf **shared_secretp) +{ + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} +#endif /* USE_SNTRUP761X25519 */ diff --git a/aosp/external/openssh/krl.c b/aosp/external/openssh/krl.c new file mode 100644 index 0000000000000000000000000000000000000000..17b88edde77774441f6945e7102bb737738237c3 --- /dev/null +++ b/aosp/external/openssh/krl.c @@ -0,0 +1,1447 @@ +/* + * Copyright (c) 2012 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: krl.c,v 1.53 2021/06/04 06:19:07 djm Exp $ */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sshbuf.h" +#include "ssherr.h" +#include "sshkey.h" +#include "authfile.h" +#include "misc.h" +#include "log.h" +#include "digest.h" +#include "bitmap.h" +#include "utf8.h" + +#include "krl.h" + +/* #define DEBUG_KRL */ +#ifdef DEBUG_KRL +# define KRL_DBG(x) debug3_f x +#else +# define KRL_DBG(x) +#endif + +/* + * Trees of revoked serial numbers, key IDs and keys. This allows + * quick searching, querying and producing lists in canonical order. + */ + +/* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */ +struct revoked_serial { + u_int64_t lo, hi; + RB_ENTRY(revoked_serial) tree_entry; +}; +static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b); +RB_HEAD(revoked_serial_tree, revoked_serial); +RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp) + +/* Tree of key IDs */ +struct revoked_key_id { + char *key_id; + RB_ENTRY(revoked_key_id) tree_entry; +}; +static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b); +RB_HEAD(revoked_key_id_tree, revoked_key_id); +RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp) + +/* Tree of blobs (used for keys and fingerprints) */ +struct revoked_blob { + u_char *blob; + size_t len; + RB_ENTRY(revoked_blob) tree_entry; +}; +static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b); +RB_HEAD(revoked_blob_tree, revoked_blob); +RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp) + +/* Tracks revoked certs for a single CA */ +struct revoked_certs { + struct sshkey *ca_key; + struct revoked_serial_tree revoked_serials; + struct revoked_key_id_tree revoked_key_ids; + TAILQ_ENTRY(revoked_certs) entry; +}; +TAILQ_HEAD(revoked_certs_list, revoked_certs); + +struct ssh_krl { + u_int64_t krl_version; + u_int64_t generated_date; + u_int64_t flags; + char *comment; + struct revoked_blob_tree revoked_keys; + struct revoked_blob_tree revoked_sha1s; + struct revoked_blob_tree revoked_sha256s; + struct revoked_certs_list revoked_certs; +}; + +/* Return equal if a and b overlap */ +static int +serial_cmp(struct revoked_serial *a, struct revoked_serial *b) +{ + if (a->hi >= b->lo && a->lo <= b->hi) + return 0; + return a->lo < b->lo ? -1 : 1; +} + +static int +key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b) +{ + return strcmp(a->key_id, b->key_id); +} + +static int +blob_cmp(struct revoked_blob *a, struct revoked_blob *b) +{ + int r; + + if (a->len != b->len) { + if ((r = memcmp(a->blob, b->blob, MINIMUM(a->len, b->len))) != 0) + return r; + return a->len > b->len ? 1 : -1; + } else + return memcmp(a->blob, b->blob, a->len); +} + +struct ssh_krl * +ssh_krl_init(void) +{ + struct ssh_krl *krl; + + if ((krl = calloc(1, sizeof(*krl))) == NULL) + return NULL; + RB_INIT(&krl->revoked_keys); + RB_INIT(&krl->revoked_sha1s); + RB_INIT(&krl->revoked_sha256s); + TAILQ_INIT(&krl->revoked_certs); + return krl; +} + +static void +revoked_certs_free(struct revoked_certs *rc) +{ + struct revoked_serial *rs, *trs; + struct revoked_key_id *rki, *trki; + + RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) { + RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs); + free(rs); + } + RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) { + RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki); + free(rki->key_id); + free(rki); + } + sshkey_free(rc->ca_key); +} + +void +ssh_krl_free(struct ssh_krl *krl) +{ + struct revoked_blob *rb, *trb; + struct revoked_certs *rc, *trc; + + if (krl == NULL) + return; + + free(krl->comment); + RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) { + RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb); + free(rb->blob); + free(rb); + } + RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) { + RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb); + free(rb->blob); + free(rb); + } + RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha256s, trb) { + RB_REMOVE(revoked_blob_tree, &krl->revoked_sha256s, rb); + free(rb->blob); + free(rb); + } + TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) { + TAILQ_REMOVE(&krl->revoked_certs, rc, entry); + revoked_certs_free(rc); + } +} + +void +ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version) +{ + krl->krl_version = version; +} + +int +ssh_krl_set_comment(struct ssh_krl *krl, const char *comment) +{ + free(krl->comment); + if ((krl->comment = strdup(comment)) == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; +} + +/* + * Find the revoked_certs struct for a CA key. If allow_create is set then + * create a new one in the tree if one did not exist already. + */ +static int +revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key, + struct revoked_certs **rcp, int allow_create) +{ + struct revoked_certs *rc; + int r; + + *rcp = NULL; + TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { + if ((ca_key == NULL && rc->ca_key == NULL) || + sshkey_equal(rc->ca_key, ca_key)) { + *rcp = rc; + return 0; + } + } + if (!allow_create) + return 0; + /* If this CA doesn't exist in the list then add it now */ + if ((rc = calloc(1, sizeof(*rc))) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (ca_key == NULL) + rc->ca_key = NULL; + else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) { + free(rc); + return r; + } + RB_INIT(&rc->revoked_serials); + RB_INIT(&rc->revoked_key_ids); + TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry); + KRL_DBG(("new CA %s", ca_key == NULL ? "*" : sshkey_type(ca_key))); + *rcp = rc; + return 0; +} + +static int +insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi) +{ + struct revoked_serial rs, *ers, *crs, *irs; + + KRL_DBG(("insert %llu:%llu", lo, hi)); + memset(&rs, 0, sizeof(rs)); + rs.lo = lo; + rs.hi = hi; + ers = RB_NFIND(revoked_serial_tree, rt, &rs); + if (ers == NULL || serial_cmp(ers, &rs) != 0) { + /* No entry matches. Just insert */ + if ((irs = malloc(sizeof(rs))) == NULL) + return SSH_ERR_ALLOC_FAIL; + memcpy(irs, &rs, sizeof(*irs)); + ers = RB_INSERT(revoked_serial_tree, rt, irs); + if (ers != NULL) { + KRL_DBG(("bad: ers != NULL")); + /* Shouldn't happen */ + free(irs); + return SSH_ERR_INTERNAL_ERROR; + } + ers = irs; + } else { + KRL_DBG(("overlap found %llu:%llu", ers->lo, ers->hi)); + /* + * The inserted entry overlaps an existing one. Grow the + * existing entry. + */ + if (ers->lo > lo) + ers->lo = lo; + if (ers->hi < hi) + ers->hi = hi; + } + + /* + * The inserted or revised range might overlap or abut adjacent ones; + * coalesce as necessary. + */ + + /* Check predecessors */ + while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) { + KRL_DBG(("pred %llu:%llu", crs->lo, crs->hi)); + if (ers->lo != 0 && crs->hi < ers->lo - 1) + break; + /* This entry overlaps. */ + if (crs->lo < ers->lo) { + ers->lo = crs->lo; + KRL_DBG(("pred extend %llu:%llu", ers->lo, ers->hi)); + } + RB_REMOVE(revoked_serial_tree, rt, crs); + free(crs); + } + /* Check successors */ + while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) { + KRL_DBG(("succ %llu:%llu", crs->lo, crs->hi)); + if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1) + break; + /* This entry overlaps. */ + if (crs->hi > ers->hi) { + ers->hi = crs->hi; + KRL_DBG(("succ extend %llu:%llu", ers->lo, ers->hi)); + } + RB_REMOVE(revoked_serial_tree, rt, crs); + free(crs); + } + KRL_DBG(("done, final %llu:%llu", ers->lo, ers->hi)); + return 0; +} + +int +ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key, + u_int64_t serial) +{ + return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial); +} + +int +ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, + const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi) +{ + struct revoked_certs *rc; + int r; + + if (lo > hi || lo == 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) + return r; + return insert_serial_range(&rc->revoked_serials, lo, hi); +} + +int +ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key, + const char *key_id) +{ + struct revoked_key_id *rki, *erki; + struct revoked_certs *rc; + int r; + + if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) + return r; + + KRL_DBG(("revoke %s", key_id)); + if ((rki = calloc(1, sizeof(*rki))) == NULL || + (rki->key_id = strdup(key_id)) == NULL) { + free(rki); + return SSH_ERR_ALLOC_FAIL; + } + erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki); + if (erki != NULL) { + free(rki->key_id); + free(rki); + } + return 0; +} + +/* Convert "key" to a public key blob without any certificate information */ +static int +plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen) +{ + struct sshkey *kcopy; + int r; + + if ((r = sshkey_from_private(key, &kcopy)) != 0) + return r; + if (sshkey_is_cert(kcopy)) { + if ((r = sshkey_drop_cert(kcopy)) != 0) { + sshkey_free(kcopy); + return r; + } + } + r = sshkey_to_blob(kcopy, blob, blen); + sshkey_free(kcopy); + return r; +} + +/* Revoke a key blob. Ownership of blob is transferred to the tree */ +static int +revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, size_t len) +{ + struct revoked_blob *rb, *erb; + + if ((rb = calloc(1, sizeof(*rb))) == NULL) + return SSH_ERR_ALLOC_FAIL; + rb->blob = blob; + rb->len = len; + erb = RB_INSERT(revoked_blob_tree, rbt, rb); + if (erb != NULL) { + free(rb->blob); + free(rb); + } + return 0; +} + +int +ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key) +{ + u_char *blob; + size_t len; + int r; + + debug3_f("revoke type %s", sshkey_type(key)); + if ((r = plain_key_blob(key, &blob, &len)) != 0) + return r; + return revoke_blob(&krl->revoked_keys, blob, len); +} + +static int +revoke_by_hash(struct revoked_blob_tree *target, const u_char *p, size_t len) +{ + u_char *blob; + int r; + + /* need to copy hash, as revoke_blob steals ownership */ + if ((blob = malloc(len)) == NULL) + return SSH_ERR_SYSTEM_ERROR; + memcpy(blob, p, len); + if ((r = revoke_blob(target, blob, len)) != 0) { + free(blob); + return r; + } + return 0; +} + +int +ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len) +{ + debug3_f("revoke by sha1"); + if (len != 20) + return SSH_ERR_INVALID_FORMAT; + return revoke_by_hash(&krl->revoked_sha1s, p, len); +} + +int +ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len) +{ + debug3_f("revoke by sha256"); + if (len != 32) + return SSH_ERR_INVALID_FORMAT; + return revoke_by_hash(&krl->revoked_sha256s, p, len); +} + +int +ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key) +{ + /* XXX replace with SHA256? */ + if (!sshkey_is_cert(key)) + return ssh_krl_revoke_key_explicit(krl, key); + + if (key->cert->serial == 0) { + return ssh_krl_revoke_cert_by_key_id(krl, + key->cert->signature_key, + key->cert->key_id); + } else { + return ssh_krl_revoke_cert_by_serial(krl, + key->cert->signature_key, + key->cert->serial); + } +} + +/* + * Select the most compact section type to emit next in a KRL based on + * the current section type, the run length of contiguous revoked serial + * numbers and the gaps from the last and to the next revoked serial. + * Applies a mostly-accurate bit cost model to select the section type + * that will minimise the size of the resultant KRL. + */ +static int +choose_next_state(int current_state, u_int64_t contig, int final, + u_int64_t last_gap, u_int64_t next_gap, int *force_new_section) +{ + int new_state; + u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart; + + /* + * Avoid unsigned overflows. + * The limits are high enough to avoid confusing the calculations. + */ + contig = MINIMUM(contig, 1ULL<<31); + last_gap = MINIMUM(last_gap, 1ULL<<31); + next_gap = MINIMUM(next_gap, 1ULL<<31); + + /* + * Calculate the cost to switch from the current state to candidates. + * NB. range sections only ever contain a single range, so their + * switching cost is independent of the current_state. + */ + cost_list = cost_bitmap = cost_bitmap_restart = 0; + cost_range = 8; + switch (current_state) { + case KRL_SECTION_CERT_SERIAL_LIST: + cost_bitmap_restart = cost_bitmap = 8 + 64; + break; + case KRL_SECTION_CERT_SERIAL_BITMAP: + cost_list = 8; + cost_bitmap_restart = 8 + 64; + break; + case KRL_SECTION_CERT_SERIAL_RANGE: + case 0: + cost_bitmap_restart = cost_bitmap = 8 + 64; + cost_list = 8; + } + + /* Estimate base cost in bits of each section type */ + cost_list += 64 * contig + (final ? 0 : 8+64); + cost_range += (2 * 64) + (final ? 0 : 8+64); + cost_bitmap += last_gap + contig + (final ? 0 : MINIMUM(next_gap, 8+64)); + cost_bitmap_restart += contig + (final ? 0 : MINIMUM(next_gap, 8+64)); + + /* Convert to byte costs for actual comparison */ + cost_list = (cost_list + 7) / 8; + cost_bitmap = (cost_bitmap + 7) / 8; + cost_bitmap_restart = (cost_bitmap_restart + 7) / 8; + cost_range = (cost_range + 7) / 8; + + /* Now pick the best choice */ + *force_new_section = 0; + new_state = KRL_SECTION_CERT_SERIAL_BITMAP; + cost = cost_bitmap; + if (cost_range < cost) { + new_state = KRL_SECTION_CERT_SERIAL_RANGE; + cost = cost_range; + } + if (cost_list < cost) { + new_state = KRL_SECTION_CERT_SERIAL_LIST; + cost = cost_list; + } + if (cost_bitmap_restart < cost) { + new_state = KRL_SECTION_CERT_SERIAL_BITMAP; + *force_new_section = 1; + cost = cost_bitmap_restart; + } + KRL_DBG(("contig %llu last_gap %llu next_gap %llu final %d, costs:" + "list %llu range %llu bitmap %llu new bitmap %llu, " + "selected 0x%02x%s", (long long unsigned)contig, + (long long unsigned)last_gap, (long long unsigned)next_gap, final, + (long long unsigned)cost_list, (long long unsigned)cost_range, + (long long unsigned)cost_bitmap, + (long long unsigned)cost_bitmap_restart, new_state, + *force_new_section ? " restart" : "")); + return new_state; +} + +static int +put_bitmap(struct sshbuf *buf, struct bitmap *bitmap) +{ + size_t len; + u_char *blob; + int r; + + len = bitmap_nbytes(bitmap); + if ((blob = malloc(len)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (bitmap_to_string(bitmap, blob, len) != 0) { + free(blob); + return SSH_ERR_INTERNAL_ERROR; + } + r = sshbuf_put_bignum2_bytes(buf, blob, len); + free(blob); + return r; +} + +/* Generate a KRL_SECTION_CERTIFICATES KRL section */ +static int +revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf) +{ + int final, force_new_sect, r = SSH_ERR_INTERNAL_ERROR; + u_int64_t i, contig, gap, last = 0, bitmap_start = 0; + struct revoked_serial *rs, *nrs; + struct revoked_key_id *rki; + int next_state, state = 0; + struct sshbuf *sect; + struct bitmap *bitmap = NULL; + + if ((sect = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* Store the header: optional CA scope key, reserved */ + if (rc->ca_key == NULL) { + if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) + goto out; + } else { + if ((r = sshkey_puts(rc->ca_key, buf)) != 0) + goto out; + } + if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) + goto out; + + /* Store the revoked serials. */ + for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials); + rs != NULL; + rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) { + KRL_DBG(("serial %llu:%llu state 0x%02x", + (long long unsigned)rs->lo, (long long unsigned)rs->hi, + state)); + + /* Check contiguous length and gap to next section (if any) */ + nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs); + final = nrs == NULL; + gap = nrs == NULL ? 0 : nrs->lo - rs->hi; + contig = 1 + (rs->hi - rs->lo); + + /* Choose next state based on these */ + next_state = choose_next_state(state, contig, final, + state == 0 ? 0 : rs->lo - last, gap, &force_new_sect); + + /* + * If the current section is a range section or has a different + * type to the next section, then finish it off now. + */ + if (state != 0 && (force_new_sect || next_state != state || + state == KRL_SECTION_CERT_SERIAL_RANGE)) { + KRL_DBG(("finish state 0x%02x", state)); + switch (state) { + case KRL_SECTION_CERT_SERIAL_LIST: + case KRL_SECTION_CERT_SERIAL_RANGE: + break; + case KRL_SECTION_CERT_SERIAL_BITMAP: + if ((r = put_bitmap(sect, bitmap)) != 0) + goto out; + bitmap_free(bitmap); + bitmap = NULL; + break; + } + if ((r = sshbuf_put_u8(buf, state)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + sshbuf_reset(sect); + } + + /* If we are starting a new section then prepare it now */ + if (next_state != state || force_new_sect) { + KRL_DBG(("start state 0x%02x", + next_state)); + state = next_state; + sshbuf_reset(sect); + switch (state) { + case KRL_SECTION_CERT_SERIAL_LIST: + case KRL_SECTION_CERT_SERIAL_RANGE: + break; + case KRL_SECTION_CERT_SERIAL_BITMAP: + if ((bitmap = bitmap_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + bitmap_start = rs->lo; + if ((r = sshbuf_put_u64(sect, + bitmap_start)) != 0) + goto out; + break; + } + } + + /* Perform section-specific processing */ + switch (state) { + case KRL_SECTION_CERT_SERIAL_LIST: + for (i = 0; i < contig; i++) { + if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0) + goto out; + } + break; + case KRL_SECTION_CERT_SERIAL_RANGE: + if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 || + (r = sshbuf_put_u64(sect, rs->hi)) != 0) + goto out; + break; + case KRL_SECTION_CERT_SERIAL_BITMAP: + if (rs->lo - bitmap_start > INT_MAX) { + error_f("insane bitmap gap"); + goto out; + } + for (i = 0; i < contig; i++) { + if (bitmap_set_bit(bitmap, + rs->lo + i - bitmap_start) != 0) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + } + break; + } + last = rs->hi; + } + /* Flush the remaining section, if any */ + if (state != 0) { + KRL_DBG(("serial final flush for state 0x%02x", state)); + switch (state) { + case KRL_SECTION_CERT_SERIAL_LIST: + case KRL_SECTION_CERT_SERIAL_RANGE: + break; + case KRL_SECTION_CERT_SERIAL_BITMAP: + if ((r = put_bitmap(sect, bitmap)) != 0) + goto out; + bitmap_free(bitmap); + bitmap = NULL; + break; + } + if ((r = sshbuf_put_u8(buf, state)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + } + KRL_DBG(("serial done ")); + + /* Now output a section for any revocations by key ID */ + sshbuf_reset(sect); + RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { + KRL_DBG(("key ID %s", rki->key_id)); + if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0) + goto out; + } + if (sshbuf_len(sect) != 0) { + if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + } + r = 0; + out: + bitmap_free(bitmap); + sshbuf_free(sect); + return r; +} + +int +ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, + struct sshkey **sign_keys, u_int nsign_keys) +{ + int r = SSH_ERR_INTERNAL_ERROR; + struct revoked_certs *rc; + struct revoked_blob *rb; + struct sshbuf *sect; + u_char *sblob = NULL; + size_t slen, i; + + if (krl->generated_date == 0) + krl->generated_date = time(NULL); + + if ((sect = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* Store the header */ + if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 || + (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 || + (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 || + (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 || + (r = sshbuf_put_u64(buf, krl->flags)) != 0 || + (r = sshbuf_put_string(buf, NULL, 0)) != 0 || + (r = sshbuf_put_cstring(buf, krl->comment)) != 0) + goto out; + + /* Store sections for revoked certificates */ + TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { + sshbuf_reset(sect); + if ((r = revoked_certs_generate(rc, sect)) != 0) + goto out; + if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + } + + /* Finally, output sections for revocations by public key/hash */ + sshbuf_reset(sect); + RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { + KRL_DBG(("key len %zu ", rb->len)); + if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) + goto out; + } + if (sshbuf_len(sect) != 0) { + if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + } + sshbuf_reset(sect); + RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { + KRL_DBG(("hash len %zu ", rb->len)); + if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) + goto out; + } + if (sshbuf_len(sect) != 0) { + if ((r = sshbuf_put_u8(buf, + KRL_SECTION_FINGERPRINT_SHA1)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + } + sshbuf_reset(sect); + RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { + KRL_DBG(("hash len %zu ", rb->len)); + if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) + goto out; + } + if (sshbuf_len(sect) != 0) { + if ((r = sshbuf_put_u8(buf, + KRL_SECTION_FINGERPRINT_SHA256)) != 0 || + (r = sshbuf_put_stringb(buf, sect)) != 0) + goto out; + } + + for (i = 0; i < nsign_keys; i++) { + KRL_DBG(("sig key %s", sshkey_ssh_name(sign_keys[i]))); + if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || + (r = sshkey_puts(sign_keys[i], buf)) != 0) + goto out; + /* XXX support sk-* keys */ + if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, + sshbuf_ptr(buf), sshbuf_len(buf), NULL, NULL, + NULL, 0)) != 0) + goto out; + KRL_DBG(("signature sig len %zu", slen)); + if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) + goto out; + } + + r = 0; + out: + free(sblob); + sshbuf_free(sect); + return r; +} + +static void +format_timestamp(u_int64_t timestamp, char *ts, size_t nts) +{ + time_t t; + struct tm *tm; + + t = timestamp; + tm = localtime(&t); + if (tm == NULL) + strlcpy(ts, "", nts); + else { + *ts = '\0'; + strftime(ts, nts, "%Y%m%dT%H%M%S", tm); + } +} + +static int +parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl) +{ + int r = SSH_ERR_INTERNAL_ERROR; + u_char type; + const u_char *blob; + size_t blen, nbits; + struct sshbuf *subsect = NULL; + u_int64_t serial, serial_lo, serial_hi; + struct bitmap *bitmap = NULL; + char *key_id = NULL; + struct sshkey *ca_key = NULL; + + if ((subsect = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* Header: key, reserved */ + if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 || + (r = sshbuf_skip_string(buf)) != 0) + goto out; + if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0) + goto out; + + while (sshbuf_len(buf) > 0) { + sshbuf_free(subsect); + subsect = NULL; + if ((r = sshbuf_get_u8(buf, &type)) != 0 || + (r = sshbuf_froms(buf, &subsect)) != 0) + goto out; + KRL_DBG(("subsection type 0x%02x", type)); + /* sshbuf_dump(subsect, stderr); */ + + switch (type) { + case KRL_SECTION_CERT_SERIAL_LIST: + while (sshbuf_len(subsect) > 0) { + if ((r = sshbuf_get_u64(subsect, &serial)) != 0) + goto out; + if ((r = ssh_krl_revoke_cert_by_serial(krl, + ca_key, serial)) != 0) + goto out; + } + break; + case KRL_SECTION_CERT_SERIAL_RANGE: + if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || + (r = sshbuf_get_u64(subsect, &serial_hi)) != 0) + goto out; + if ((r = ssh_krl_revoke_cert_by_serial_range(krl, + ca_key, serial_lo, serial_hi)) != 0) + goto out; + break; + case KRL_SECTION_CERT_SERIAL_BITMAP: + if ((bitmap = bitmap_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || + (r = sshbuf_get_bignum2_bytes_direct(subsect, + &blob, &blen)) != 0) + goto out; + if (bitmap_from_string(bitmap, blob, blen) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + nbits = bitmap_nbits(bitmap); + for (serial = 0; serial < (u_int64_t)nbits; serial++) { + if (serial > 0 && serial_lo + serial == 0) { + error_f("bitmap wraps u64"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (!bitmap_test_bit(bitmap, serial)) + continue; + if ((r = ssh_krl_revoke_cert_by_serial(krl, + ca_key, serial_lo + serial)) != 0) + goto out; + } + bitmap_free(bitmap); + bitmap = NULL; + break; + case KRL_SECTION_CERT_KEY_ID: + while (sshbuf_len(subsect) > 0) { + if ((r = sshbuf_get_cstring(subsect, + &key_id, NULL)) != 0) + goto out; + if ((r = ssh_krl_revoke_cert_by_key_id(krl, + ca_key, key_id)) != 0) + goto out; + free(key_id); + key_id = NULL; + } + break; + default: + error("Unsupported KRL certificate section %u", type); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(subsect) > 0) { + error("KRL certificate section contains unparsed data"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + r = 0; + out: + if (bitmap != NULL) + bitmap_free(bitmap); + free(key_id); + sshkey_free(ca_key); + sshbuf_free(subsect); + return r; +} + +static int +blob_section(struct sshbuf *sect, struct revoked_blob_tree *target_tree, + size_t expected_len) +{ + u_char *rdata = NULL; + size_t rlen = 0; + int r; + + while (sshbuf_len(sect) > 0) { + if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0) + return r; + if (expected_len != 0 && rlen != expected_len) { + error_f("bad length"); + free(rdata); + return SSH_ERR_INVALID_FORMAT; + } + if ((r = revoke_blob(target_tree, rdata, rlen)) != 0) { + free(rdata); + return r; + } + } + return 0; +} + +/* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */ +int +ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, + const struct sshkey **sign_ca_keys, size_t nsign_ca_keys) +{ + struct sshbuf *copy = NULL, *sect = NULL; + struct ssh_krl *krl = NULL; + char timestamp[64]; + int r = SSH_ERR_INTERNAL_ERROR, sig_seen; + struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used; + u_char type; + const u_char *blob; + size_t i, j, sig_off, sects_off, blen, nca_used; + u_int format_version; + + nca_used = 0; + *krlp = NULL; + if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 || + memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) { + debug3_f("not a KRL"); + return SSH_ERR_KRL_BAD_MAGIC; + } + + /* Take a copy of the KRL buffer so we can verify its signature later */ + if ((copy = sshbuf_fromb(buf)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0) + goto out; + + if ((krl = ssh_krl_init()) == NULL) { + error_f("alloc failed"); + goto out; + } + + if ((r = sshbuf_get_u32(copy, &format_version)) != 0) + goto out; + if (format_version != KRL_FORMAT_VERSION) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 || + (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || + (r = sshbuf_get_u64(copy, &krl->flags)) != 0 || + (r = sshbuf_skip_string(copy)) != 0 || + (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) + goto out; + + format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); + debug("KRL version %llu generated at %s%s%s", + (long long unsigned)krl->krl_version, timestamp, + *krl->comment ? ": " : "", krl->comment); + + /* + * 1st pass: verify signatures, if any. This is done to avoid + * detailed parsing of data whose provenance is unverified. + */ + sig_seen = 0; + if (sshbuf_len(buf) < sshbuf_len(copy)) { + /* Shouldn't happen */ + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + sects_off = sshbuf_len(buf) - sshbuf_len(copy); + while (sshbuf_len(copy) > 0) { + if ((r = sshbuf_get_u8(copy, &type)) != 0 || + (r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) + goto out; + KRL_DBG(("first pass, section 0x%02x", type)); + if (type != KRL_SECTION_SIGNATURE) { + if (sig_seen) { + error("KRL contains non-signature section " + "after signature"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* Not interested for now. */ + continue; + } + sig_seen = 1; + /* First string component is the signing key */ + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(buf) < sshbuf_len(copy)) { + /* Shouldn't happen */ + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + sig_off = sshbuf_len(buf) - sshbuf_len(copy); + /* Second string component is the signature itself */ + if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* Check signature over entire KRL up to this point */ + if ((r = sshkey_verify(key, blob, blen, + sshbuf_ptr(buf), sig_off, NULL, 0, NULL)) != 0) + goto out; + /* Check if this key has already signed this KRL */ + for (i = 0; i < nca_used; i++) { + if (sshkey_equal(ca_used[i], key)) { + error("KRL signed more than once with " + "the same key"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + /* Record keys used to sign the KRL */ + tmp_ca_used = recallocarray(ca_used, nca_used, nca_used + 1, + sizeof(*ca_used)); + if (tmp_ca_used == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + ca_used = tmp_ca_used; + ca_used[nca_used++] = key; + key = NULL; + } + + if (sshbuf_len(copy) != 0) { + /* Shouldn't happen */ + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + + /* + * 2nd pass: parse and load the KRL, skipping the header to the point + * where the section start. + */ + sshbuf_free(copy); + if ((copy = sshbuf_fromb(buf)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_consume(copy, sects_off)) != 0) + goto out; + while (sshbuf_len(copy) > 0) { + sshbuf_free(sect); + sect = NULL; + if ((r = sshbuf_get_u8(copy, &type)) != 0 || + (r = sshbuf_froms(copy, §)) != 0) + goto out; + KRL_DBG(("second pass, section 0x%02x", type)); + + switch (type) { + case KRL_SECTION_CERTIFICATES: + if ((r = parse_revoked_certs(sect, krl)) != 0) + goto out; + break; + case KRL_SECTION_EXPLICIT_KEY: + if ((r = blob_section(sect, + &krl->revoked_keys, 0)) != 0) + goto out; + break; + case KRL_SECTION_FINGERPRINT_SHA1: + if ((r = blob_section(sect, + &krl->revoked_sha1s, 20)) != 0) + goto out; + break; + case KRL_SECTION_FINGERPRINT_SHA256: + if ((r = blob_section(sect, + &krl->revoked_sha256s, 32)) != 0) + goto out; + break; + case KRL_SECTION_SIGNATURE: + /* Handled above, but still need to stay in synch */ + sshbuf_free(sect); + sect = NULL; + if ((r = sshbuf_skip_string(copy)) != 0) + goto out; + break; + default: + error("Unsupported KRL section %u", type); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sect != NULL && sshbuf_len(sect) > 0) { + error("KRL section contains unparsed data"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* Check that the key(s) used to sign the KRL weren't revoked */ + sig_seen = 0; + for (i = 0; i < nca_used; i++) { + if (ssh_krl_check_key(krl, ca_used[i]) == 0) + sig_seen = 1; + else { + sshkey_free(ca_used[i]); + ca_used[i] = NULL; + } + } + if (nca_used && !sig_seen) { + error("All keys used to sign KRL were revoked"); + r = SSH_ERR_KEY_REVOKED; + goto out; + } + + /* If we have CA keys, then verify that one was used to sign the KRL */ + if (sig_seen && nsign_ca_keys != 0) { + sig_seen = 0; + for (i = 0; !sig_seen && i < nsign_ca_keys; i++) { + for (j = 0; j < nca_used; j++) { + if (ca_used[j] == NULL) + continue; + if (sshkey_equal(ca_used[j], sign_ca_keys[i])) { + sig_seen = 1; + break; + } + } + } + if (!sig_seen) { + r = SSH_ERR_SIGNATURE_INVALID; + error("KRL not signed with any trusted key"); + goto out; + } + } + + *krlp = krl; + r = 0; + out: + if (r != 0) + ssh_krl_free(krl); + for (i = 0; i < nca_used; i++) + sshkey_free(ca_used[i]); + free(ca_used); + sshkey_free(key); + sshbuf_free(copy); + sshbuf_free(sect); + return r; +} + +/* Checks certificate serial number and key ID revocation */ +static int +is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc) +{ + struct revoked_serial rs, *ers; + struct revoked_key_id rki, *erki; + + /* Check revocation by cert key ID */ + memset(&rki, 0, sizeof(rki)); + rki.key_id = key->cert->key_id; + erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); + if (erki != NULL) { + KRL_DBG(("revoked by key ID")); + return SSH_ERR_KEY_REVOKED; + } + + /* + * Zero serials numbers are ignored (it's the default when the + * CA doesn't specify one). + */ + if (key->cert->serial == 0) + return 0; + + memset(&rs, 0, sizeof(rs)); + rs.lo = rs.hi = key->cert->serial; + ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); + if (ers != NULL) { + KRL_DBG(("revoked serial %llu matched %llu:%llu", + key->cert->serial, ers->lo, ers->hi)); + return SSH_ERR_KEY_REVOKED; + } + return 0; +} + +/* Checks whether a given key/cert is revoked. Does not check its CA */ +static int +is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) +{ + struct revoked_blob rb, *erb; + struct revoked_certs *rc; + int r; + + /* Check explicitly revoked hashes first */ + memset(&rb, 0, sizeof(rb)); + if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, + &rb.blob, &rb.len)) != 0) + return r; + erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); + free(rb.blob); + if (erb != NULL) { + KRL_DBG(("revoked by key SHA1")); + return SSH_ERR_KEY_REVOKED; + } + memset(&rb, 0, sizeof(rb)); + if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256, + &rb.blob, &rb.len)) != 0) + return r; + erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb); + free(rb.blob); + if (erb != NULL) { + KRL_DBG(("revoked by key SHA256")); + return SSH_ERR_KEY_REVOKED; + } + + /* Next, explicit keys */ + memset(&rb, 0, sizeof(rb)); + if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0) + return r; + erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); + free(rb.blob); + if (erb != NULL) { + KRL_DBG(("revoked by explicit key")); + return SSH_ERR_KEY_REVOKED; + } + + if (!sshkey_is_cert(key)) + return 0; + + /* Check cert revocation for the specified CA */ + if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key, + &rc, 0)) != 0) + return r; + if (rc != NULL) { + if ((r = is_cert_revoked(key, rc)) != 0) + return r; + } + /* Check cert revocation for the wildcard CA */ + if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0) + return r; + if (rc != NULL) { + if ((r = is_cert_revoked(key, rc)) != 0) + return r; + } + + KRL_DBG(("%llu no match", key->cert->serial)); + return 0; +} + +int +ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key) +{ + int r; + + KRL_DBG(("checking key")); + if ((r = is_key_revoked(krl, key)) != 0) + return r; + if (sshkey_is_cert(key)) { + debug2_f("checking CA key"); + if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0) + return r; + } + KRL_DBG(("key okay")); + return 0; +} + +int +ssh_krl_file_contains_key(const char *path, const struct sshkey *key) +{ + struct sshbuf *krlbuf = NULL; + struct ssh_krl *krl = NULL; + int oerrno = 0, r; + + if (path == NULL) + return 0; + if ((r = sshbuf_load_file(path, &krlbuf)) != 0) { + oerrno = errno; + goto out; + } + if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0) + goto out; + debug2_f("checking KRL %s", path); + r = ssh_krl_check_key(krl, key); + out: + sshbuf_free(krlbuf); + ssh_krl_free(krl); + if (r != 0) + errno = oerrno; + return r; +} + +int +krl_dump(struct ssh_krl *krl, FILE *f) +{ + struct sshkey *key = NULL; + struct revoked_blob *rb; + struct revoked_certs *rc; + struct revoked_serial *rs; + struct revoked_key_id *rki; + int r, ret = 0; + char *fp, timestamp[64]; + + /* Try to print in a KRL spec-compatible format */ + format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); + fprintf(f, "# KRL version %llu\n", + (unsigned long long)krl->krl_version); + fprintf(f, "# Generated at %s\n", timestamp); + if (krl->comment != NULL && *krl->comment != '\0') { + r = INT_MAX; + asmprintf(&fp, INT_MAX, &r, "%s", krl->comment); + fprintf(f, "# Comment: %s\n", fp); + free(fp); + } + fputc('\n', f); + + RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { + if ((r = sshkey_from_blob(rb->blob, rb->len, &key)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + error_r(r, "parse KRL key"); + continue; + } + if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) { + ret = SSH_ERR_INVALID_FORMAT; + error("sshkey_fingerprint failed"); + continue; + } + fprintf(f, "hash: SHA256:%s # %s\n", fp, sshkey_ssh_name(key)); + free(fp); + free(key); + } + RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) { + fp = tohex(rb->blob, rb->len); + fprintf(f, "hash: SHA256:%s\n", fp); + free(fp); + } + RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { + /* + * There is not KRL spec keyword for raw SHA1 hashes, so + * print them as comments. + */ + fp = tohex(rb->blob, rb->len); + fprintf(f, "# hash SHA1:%s\n", fp); + free(fp); + } + + TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { + fputc('\n', f); + if (rc->ca_key == NULL) + fprintf(f, "# Wildcard CA\n"); + else { + if ((fp = sshkey_fingerprint(rc->ca_key, + SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) { + ret = SSH_ERR_INVALID_FORMAT; + error("sshkey_fingerprint failed"); + continue; + } + fprintf(f, "# CA key %s %s\n", + sshkey_ssh_name(rc->ca_key), fp); + free(fp); + } + RB_FOREACH(rs, revoked_serial_tree, &rc->revoked_serials) { + if (rs->lo == rs->hi) { + fprintf(f, "serial: %llu\n", + (unsigned long long)rs->lo); + } else { + fprintf(f, "serial: %llu-%llu\n", + (unsigned long long)rs->lo, + (unsigned long long)rs->hi); + } + } + RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { + /* + * We don't want key IDs with embedded newlines to + * mess up the display. + */ + r = INT_MAX; + asmprintf(&fp, INT_MAX, &r, "%s", rki->key_id); + fprintf(f, "id: %s\n", fp); + free(fp); + } + } + return ret; +} diff --git a/aosp/external/openssh/krl.h b/aosp/external/openssh/krl.h new file mode 100644 index 0000000000000000000000000000000000000000..ca6d3f2843fd16d81363899306e23802e218d955 --- /dev/null +++ b/aosp/external/openssh/krl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2012 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: krl.h,v 1.8 2020/04/03 02:26:56 djm Exp $ */ + +#ifndef _KRL_H +#define _KRL_H + +/* Functions to manage key revocation lists */ + +#define KRL_MAGIC "SSHKRL\n\0" +#define KRL_FORMAT_VERSION 1 + +/* KRL section types */ +#define KRL_SECTION_CERTIFICATES 1 +#define KRL_SECTION_EXPLICIT_KEY 2 +#define KRL_SECTION_FINGERPRINT_SHA1 3 +#define KRL_SECTION_SIGNATURE 4 +#define KRL_SECTION_FINGERPRINT_SHA256 5 + +/* KRL_SECTION_CERTIFICATES subsection types */ +#define KRL_SECTION_CERT_SERIAL_LIST 0x20 +#define KRL_SECTION_CERT_SERIAL_RANGE 0x21 +#define KRL_SECTION_CERT_SERIAL_BITMAP 0x22 +#define KRL_SECTION_CERT_KEY_ID 0x23 + +struct sshkey; +struct sshbuf; +struct ssh_krl; + +struct ssh_krl *ssh_krl_init(void); +void ssh_krl_free(struct ssh_krl *krl); +void ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version); +int ssh_krl_set_comment(struct ssh_krl *krl, const char *comment); +int ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, + const struct sshkey *ca_key, u_int64_t serial); +int ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, + const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi); +int ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, + const struct sshkey *ca_key, const char *key_id); +int ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key); +int ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len); +int ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len); +int ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key); +int ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, + struct sshkey **sign_keys, u_int nsign_keys); +int ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, + const struct sshkey **sign_ca_keys, size_t nsign_ca_keys); +int ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key); +int ssh_krl_file_contains_key(const char *path, const struct sshkey *key); +int krl_dump(struct ssh_krl *krl, FILE *f); + +#endif /* _KRL_H */ + diff --git a/aosp/external/openssh/log.c b/aosp/external/openssh/log.c new file mode 100644 index 0000000000000000000000000000000000000000..99bf046a792aaaedd8fed19902e5f6e91b58c384 --- /dev/null +++ b/aosp/external/openssh/log.c @@ -0,0 +1,500 @@ +/* $OpenBSD: log.c,v 1.60 2021/09/16 15:11:19 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +# include +#endif + +#include "log.h" +#include "match.h" + +static LogLevel log_level = SYSLOG_LEVEL_INFO; +static int log_on_stderr = 1; +static int log_stderr_fd = STDERR_FILENO; +static int log_facility = LOG_AUTH; +static const char *argv0; +static log_handler_fn *log_handler; +static void *log_handler_ctx; +static char **log_verbose; +static size_t nlog_verbose; + +extern char *__progname; + +#define LOG_SYSLOG_VIS (VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL) +#define LOG_STDERR_VIS (VIS_SAFE|VIS_OCTAL) + +/* textual representation of log-facilities/levels */ + +static struct { + const char *name; + SyslogFacility val; +} log_facilities[] = { + { "DAEMON", SYSLOG_FACILITY_DAEMON }, + { "USER", SYSLOG_FACILITY_USER }, + { "AUTH", SYSLOG_FACILITY_AUTH }, +#ifdef LOG_AUTHPRIV + { "AUTHPRIV", SYSLOG_FACILITY_AUTHPRIV }, +#endif + { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, + { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, + { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, + { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, + { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, + { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, + { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, + { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, + { NULL, SYSLOG_FACILITY_NOT_SET } +}; + +static struct { + const char *name; + LogLevel val; +} log_levels[] = +{ + { "QUIET", SYSLOG_LEVEL_QUIET }, + { "FATAL", SYSLOG_LEVEL_FATAL }, + { "ERROR", SYSLOG_LEVEL_ERROR }, + { "INFO", SYSLOG_LEVEL_INFO }, + { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, + { "DEBUG", SYSLOG_LEVEL_DEBUG1 }, + { "DEBUG1", SYSLOG_LEVEL_DEBUG1 }, + { "DEBUG2", SYSLOG_LEVEL_DEBUG2 }, + { "DEBUG3", SYSLOG_LEVEL_DEBUG3 }, + { NULL, SYSLOG_LEVEL_NOT_SET } +}; + +LogLevel +log_level_get(void) +{ + return log_level; +} + +SyslogFacility +log_facility_number(char *name) +{ + int i; + + if (name != NULL) + for (i = 0; log_facilities[i].name; i++) + if (strcasecmp(log_facilities[i].name, name) == 0) + return log_facilities[i].val; + return SYSLOG_FACILITY_NOT_SET; +} + +const char * +log_facility_name(SyslogFacility facility) +{ + u_int i; + + for (i = 0; log_facilities[i].name; i++) + if (log_facilities[i].val == facility) + return log_facilities[i].name; + return NULL; +} + +LogLevel +log_level_number(char *name) +{ + int i; + + if (name != NULL) + for (i = 0; log_levels[i].name; i++) + if (strcasecmp(log_levels[i].name, name) == 0) + return log_levels[i].val; + return SYSLOG_LEVEL_NOT_SET; +} + +const char * +log_level_name(LogLevel level) +{ + u_int i; + + for (i = 0; log_levels[i].name != NULL; i++) + if (log_levels[i].val == level) + return log_levels[i].name; + return NULL; +} + +void +log_verbose_add(const char *s) +{ + char **tmp; + + /* Ignore failures here */ + if ((tmp = recallocarray(log_verbose, nlog_verbose, nlog_verbose + 1, + sizeof(*log_verbose))) != NULL) { + log_verbose = tmp; + if ((log_verbose[nlog_verbose] = strdup(s)) != NULL) + nlog_verbose++; + } +} + +void +log_verbose_reset(void) +{ + size_t i; + + for (i = 0; i < nlog_verbose; i++) + free(log_verbose[i]); + free(log_verbose); + log_verbose = NULL; + nlog_verbose = 0; +} + +/* + * Initialize the log. + */ + +void +log_init(const char *av0, LogLevel level, SyslogFacility facility, + int on_stderr) +{ +#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + struct syslog_data sdata = SYSLOG_DATA_INIT; +#endif + + argv0 = av0; + + if (log_change_level(level) != 0) { + fprintf(stderr, "Unrecognized internal syslog level code %d\n", + (int) level); + exit(1); + } + + log_handler = NULL; + log_handler_ctx = NULL; + + log_on_stderr = on_stderr; + if (on_stderr) + return; + + switch (facility) { + case SYSLOG_FACILITY_DAEMON: + log_facility = LOG_DAEMON; + break; + case SYSLOG_FACILITY_USER: + log_facility = LOG_USER; + break; + case SYSLOG_FACILITY_AUTH: + log_facility = LOG_AUTH; + break; +#ifdef LOG_AUTHPRIV + case SYSLOG_FACILITY_AUTHPRIV: + log_facility = LOG_AUTHPRIV; + break; +#endif + case SYSLOG_FACILITY_LOCAL0: + log_facility = LOG_LOCAL0; + break; + case SYSLOG_FACILITY_LOCAL1: + log_facility = LOG_LOCAL1; + break; + case SYSLOG_FACILITY_LOCAL2: + log_facility = LOG_LOCAL2; + break; + case SYSLOG_FACILITY_LOCAL3: + log_facility = LOG_LOCAL3; + break; + case SYSLOG_FACILITY_LOCAL4: + log_facility = LOG_LOCAL4; + break; + case SYSLOG_FACILITY_LOCAL5: + log_facility = LOG_LOCAL5; + break; + case SYSLOG_FACILITY_LOCAL6: + log_facility = LOG_LOCAL6; + break; + case SYSLOG_FACILITY_LOCAL7: + log_facility = LOG_LOCAL7; + break; + default: + fprintf(stderr, + "Unrecognized internal syslog facility code %d\n", + (int) facility); + exit(1); + } + + /* + * If an external library (eg libwrap) attempts to use syslog + * immediately after reexec, syslog may be pointing to the wrong + * facility, so we force an open/close of syslog here. + */ +#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); + closelog_r(&sdata); +#else + openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); + closelog(); +#endif +} + +int +log_change_level(LogLevel new_log_level) +{ + /* no-op if log_init has not been called */ + if (argv0 == NULL) + return 0; + + switch (new_log_level) { + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_ERROR: + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + case SYSLOG_LEVEL_DEBUG1: + case SYSLOG_LEVEL_DEBUG2: + case SYSLOG_LEVEL_DEBUG3: + log_level = new_log_level; + return 0; + default: + return -1; + } +} + +int +log_is_on_stderr(void) +{ + return log_on_stderr && log_stderr_fd == STDERR_FILENO; +} + +/* redirect what would usually get written to stderr to specified file */ +void +log_redirect_stderr_to(const char *logfile) +{ + int fd; + + if (logfile == NULL) { + if (log_stderr_fd != STDERR_FILENO) { + close(log_stderr_fd); + log_stderr_fd = STDERR_FILENO; + } + return; + } + + if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) { + fprintf(stderr, "Couldn't open logfile %s: %s\n", logfile, + strerror(errno)); + exit(1); + } + log_stderr_fd = fd; +} + +#define MSGBUFSIZ 1024 + +void +set_log_handler(log_handler_fn *handler, void *ctx) +{ + log_handler = handler; + log_handler_ctx = ctx; +} + +static void +do_log(LogLevel level, int force, const char *suffix, const char *fmt, + va_list args) +{ +#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + struct syslog_data sdata = SYSLOG_DATA_INIT; +#endif + char msgbuf[MSGBUFSIZ]; + char fmtbuf[MSGBUFSIZ]; + char *txt = NULL; + int pri = LOG_INFO; + int saved_errno = errno; + log_handler_fn *tmp_handler; + const char *progname = argv0 != NULL ? argv0 : __progname; + + if (!force && level > log_level) + return; + + switch (level) { + case SYSLOG_LEVEL_FATAL: + if (!log_on_stderr) + txt = "fatal"; + pri = LOG_CRIT; + break; + case SYSLOG_LEVEL_ERROR: + if (!log_on_stderr) + txt = "error"; + pri = LOG_ERR; + break; + case SYSLOG_LEVEL_INFO: + pri = LOG_INFO; + break; + case SYSLOG_LEVEL_VERBOSE: + pri = LOG_INFO; + break; + case SYSLOG_LEVEL_DEBUG1: + txt = "debug1"; + pri = LOG_DEBUG; + break; + case SYSLOG_LEVEL_DEBUG2: + txt = "debug2"; + pri = LOG_DEBUG; + break; + case SYSLOG_LEVEL_DEBUG3: + txt = "debug3"; + pri = LOG_DEBUG; + break; + default: + txt = "internal error"; + pri = LOG_ERR; + break; + } + if (txt != NULL && log_handler == NULL) { + snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); + } else { + vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + } + if (suffix != NULL) { + snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", msgbuf, suffix); + strlcpy(msgbuf, fmtbuf, sizeof(msgbuf)); + } + strnvis(fmtbuf, msgbuf, sizeof(fmtbuf), + log_on_stderr ? LOG_STDERR_VIS : LOG_SYSLOG_VIS); + if (log_handler != NULL) { + /* Avoid recursion */ + tmp_handler = log_handler; + log_handler = NULL; + tmp_handler(level, force, fmtbuf, log_handler_ctx); + log_handler = tmp_handler; + } else if (log_on_stderr) { + snprintf(msgbuf, sizeof msgbuf, "%s%s%.*s\r\n", + (log_on_stderr > 1) ? progname : "", + (log_on_stderr > 1) ? ": " : "", + (int)sizeof msgbuf - 3, fmtbuf); + (void)write(log_stderr_fd, msgbuf, strlen(msgbuf)); + } else { +#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + openlog_r(progname, LOG_PID, log_facility, &sdata); + syslog_r(pri, &sdata, "%.500s", fmtbuf); + closelog_r(&sdata); +#else + openlog(progname, LOG_PID, log_facility); + syslog(pri, "%.500s", fmtbuf); + closelog(); +#endif + } + errno = saved_errno; +} + +void +sshlog(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sshlogv(file, func, line, showfunc, level, suffix, fmt, args); + va_end(args); +} + +void +sshlogdie(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sshlogv(file, func, line, showfunc, SYSLOG_LEVEL_INFO, + suffix, fmt, args); + va_end(args); + cleanup_exit(255); +} + +void +sshsigdie(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sshlogv(file, func, line, showfunc, SYSLOG_LEVEL_FATAL, + suffix, fmt, args); + va_end(args); + _exit(1); +} + +void +sshlogv(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, va_list args) +{ + char tag[128], fmt2[MSGBUFSIZ + 128]; + int forced = 0; + const char *cp; + size_t i; + + snprintf(tag, sizeof(tag), "%.48s:%.48s():%d (pid=%ld)", + (cp = strrchr(file, '/')) == NULL ? file : cp + 1, func, line, + (long)getpid()); + for (i = 0; i < nlog_verbose; i++) { + if (match_pattern_list(tag, log_verbose[i], 0) == 1) { + forced = 1; + break; + } + } + + if (forced) + snprintf(fmt2, sizeof(fmt2), "%s: %s", tag, fmt); + else if (showfunc) + snprintf(fmt2, sizeof(fmt2), "%s: %s", func, fmt); + else + strlcpy(fmt2, fmt, sizeof(fmt2)); + + do_log(level, forced, suffix, fmt2, args); +} + +void +sshlogdirect(LogLevel level, int forced, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + do_log(level, forced, NULL, fmt, args); + va_end(args); +} diff --git a/aosp/external/openssh/log.h b/aosp/external/openssh/log.h new file mode 100644 index 0000000000000000000000000000000000000000..6218b4177a6f2efc02d1a4dd11904d9bc8d698cd --- /dev/null +++ b/aosp/external/openssh/log.h @@ -0,0 +1,132 @@ +/* $OpenBSD: log.h,v 1.33 2021/04/15 16:24:31 markus Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef SSH_LOG_H +#define SSH_LOG_H + +#include /* va_list */ +#include "ssherr.h" /* ssh_err() */ + +/* Supported syslog facilities and levels. */ +typedef enum { + SYSLOG_FACILITY_DAEMON, + SYSLOG_FACILITY_USER, + SYSLOG_FACILITY_AUTH, +#ifdef LOG_AUTHPRIV + SYSLOG_FACILITY_AUTHPRIV, +#endif + SYSLOG_FACILITY_LOCAL0, + SYSLOG_FACILITY_LOCAL1, + SYSLOG_FACILITY_LOCAL2, + SYSLOG_FACILITY_LOCAL3, + SYSLOG_FACILITY_LOCAL4, + SYSLOG_FACILITY_LOCAL5, + SYSLOG_FACILITY_LOCAL6, + SYSLOG_FACILITY_LOCAL7, + SYSLOG_FACILITY_NOT_SET = -1 +} SyslogFacility; + +typedef enum { + SYSLOG_LEVEL_QUIET, + SYSLOG_LEVEL_FATAL, + SYSLOG_LEVEL_ERROR, + SYSLOG_LEVEL_INFO, + SYSLOG_LEVEL_VERBOSE, + SYSLOG_LEVEL_DEBUG1, + SYSLOG_LEVEL_DEBUG2, + SYSLOG_LEVEL_DEBUG3, + SYSLOG_LEVEL_NOT_SET = -1 +} LogLevel; + +typedef void (log_handler_fn)(LogLevel, int, const char *, void *); + +void log_init(const char *, LogLevel, SyslogFacility, int); +LogLevel log_level_get(void); +int log_change_level(LogLevel); +int log_is_on_stderr(void); +void log_redirect_stderr_to(const char *); +void log_verbose_add(const char *); +void log_verbose_reset(void); + +SyslogFacility log_facility_number(char *); +const char * log_facility_name(SyslogFacility); +LogLevel log_level_number(char *); +const char * log_level_name(LogLevel); + +void set_log_handler(log_handler_fn *, void *); +void cleanup_exit(int) __attribute__((noreturn)); + +void sshlog(const char *, const char *, int, int, + LogLevel, const char *, const char *, ...) + __attribute__((format(printf, 7, 8))); +void sshlogv(const char *, const char *, int, int, + LogLevel, const char *, const char *, va_list); +void sshsigdie(const char *, const char *, int, int, + LogLevel, const char *, const char *, ...) __attribute__((noreturn)) + __attribute__((format(printf, 7, 8))); +void sshlogdie(const char *, const char *, int, int, + LogLevel, const char *, const char *, ...) __attribute__((noreturn)) + __attribute__((format(printf, 7, 8))); +void sshfatal(const char *, const char *, int, int, + LogLevel, const char *, const char *, ...) __attribute__((noreturn)) + __attribute__((format(printf, 7, 8))); +void sshlogdirect(LogLevel, int, const char *, ...) + __attribute__((format(printf, 3, 4))); + +#define do_log2(level, ...) sshlog(__FILE__, __func__, __LINE__, 0, level, NULL, __VA_ARGS__) +#define debug3(...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_DEBUG3, NULL, __VA_ARGS__) +#define debug2(...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_DEBUG2, NULL, __VA_ARGS__) +#define debug(...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_DEBUG1, NULL, __VA_ARGS__) +#define verbose(...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_VERBOSE, NULL, __VA_ARGS__) +#define logit(...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_INFO, NULL, __VA_ARGS__) +#define error(...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__) +#define fatal(...) sshfatal(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_FATAL, NULL, __VA_ARGS__) +#define logdie(...) sshlogdie(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__) +#define sigdie(...) sshsigdie(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__) + +/* Variants that prepend the caller's function */ +#define do_log2_f(level, ...) sshlog(__FILE__, __func__, __LINE__, 1, level, NULL, __VA_ARGS__) +#define debug3_f(...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_DEBUG3, NULL, __VA_ARGS__) +#define debug2_f(...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_DEBUG2, NULL, __VA_ARGS__) +#define debug_f(...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_DEBUG1, NULL, __VA_ARGS__) +#define verbose_f(...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_VERBOSE, NULL, __VA_ARGS__) +#define logit_f(...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_INFO, NULL, __VA_ARGS__) +#define error_f(...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__) +#define fatal_f(...) sshfatal(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_FATAL, NULL, __VA_ARGS__) +#define logdie_f(...) sshlogdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__) +#define sigdie_f(...) sshsigdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__) + +/* Variants that appends a ssh_err message */ +#define do_log2_r(r, level, ...) sshlog(__FILE__, __func__, __LINE__, 0, level, ssh_err(r), __VA_ARGS__) +#define debug3_r(r, ...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_DEBUG3, ssh_err(r), __VA_ARGS__) +#define debug2_r(r, ...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_DEBUG2, ssh_err(r), __VA_ARGS__) +#define debug_r(r, ...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_DEBUG1, ssh_err(r), __VA_ARGS__) +#define verbose_r(r, ...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_VERBOSE, ssh_err(r), __VA_ARGS__) +#define logit_r(r, ...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_INFO, ssh_err(r), __VA_ARGS__) +#define error_r(r, ...) sshlog(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__) +#define fatal_r(r, ...) sshfatal(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_FATAL, ssh_err(r), __VA_ARGS__) +#define logdie_r(r, ...) sshlogdie(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__) +#define sigdie_r(r, ...) sshsigdie(__FILE__, __func__, __LINE__, 0, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__) +#define do_log2_fr(r, level, ...) sshlog(__FILE__, __func__, __LINE__, 1, level, ssh_err(r), __VA_ARGS__) +#define debug3_fr(r, ...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_DEBUG3, ssh_err(r), __VA_ARGS__) +#define debug2_fr(r, ...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_DEBUG2, ssh_err(r), __VA_ARGS__) +#define debug_fr(r, ...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_DEBUG1, ssh_err(r), __VA_ARGS__) +#define verbose_fr(r, ...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_VERBOSE, ssh_err(r), __VA_ARGS__) +#define logit_fr(r, ...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_INFO, ssh_err(r), __VA_ARGS__) +#define error_fr(r, ...) sshlog(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__) +#define fatal_fr(r, ...) sshfatal(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_FATAL, ssh_err(r), __VA_ARGS__) +#define logdie_fr(r, ...) sshlogdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__) +#define sigdie_fr(r, ...) sshsigdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__) + +#endif diff --git a/aosp/external/openssh/loginrec.c b/aosp/external/openssh/loginrec.c new file mode 100644 index 0000000000000000000000000000000000000000..4f21499586afc5d87f2d28f639cb725e366cd25e --- /dev/null +++ b/aosp/external/openssh/loginrec.c @@ -0,0 +1,1730 @@ +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * Portions copyright (c) 1998 Todd C. Miller + * Portions copyright (c) 1996 Jason Downs + * Portions copyright (c) 1996 Theo de Raadt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The btmp logging code is derived from login.c from util-linux and is under + * the the following license: + * + * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +/** + ** loginrec.c: platform-independent login recording and lastlog retrieval + **/ + +/* + * The new login code explained + * ============================ + * + * This code attempts to provide a common interface to login recording + * (utmp and friends) and last login time retrieval. + * + * Its primary means of achieving this is to use 'struct logininfo', a + * union of all the useful fields in the various different types of + * system login record structures one finds on UNIX variants. + * + * We depend on autoconf to define which recording methods are to be + * used, and which fields are contained in the relevant data structures + * on the local system. Many C preprocessor symbols affect which code + * gets compiled here. + * + * The code is designed to make it easy to modify a particular + * recording method, without affecting other methods nor requiring so + * many nested conditional compilation blocks as were commonplace in + * the old code. + * + * For login recording, we try to use the local system's libraries as + * these are clearly most likely to work correctly. For utmp systems + * this usually means login() and logout() or setutent() etc., probably + * in libutil, along with logwtmp() etc. On these systems, we fall back + * to writing the files directly if we have to, though this method + * requires very thorough testing so we do not corrupt local auditing + * information. These files and their access methods are very system + * specific indeed. + * + * For utmpx systems, the corresponding library functions are + * setutxent() etc. To the author's knowledge, all utmpx systems have + * these library functions and so no direct write is attempted. If such + * a system exists and needs support, direct analogues of the [uw]tmp + * code should suffice. + * + * Retrieving the time of last login ('lastlog') is in some ways even + * more problemmatic than login recording. Some systems provide a + * simple table of all users which we seek based on uid and retrieve a + * relatively standard structure. Others record the same information in + * a directory with a separate file, and others don't record the + * information separately at all. For systems in the latter category, + * we look backwards in the wtmp or wtmpx file for the last login entry + * for our user. Naturally this is slower and on busy systems could + * incur a significant performance penalty. + * + * Calling the new code + * -------------------- + * + * In OpenSSH all login recording and retrieval is performed in + * login.c. Here you'll find working examples. Also, in the logintest.c + * program there are more examples. + * + * Internal handler calling method + * ------------------------------- + * + * When a call is made to login_login() or login_logout(), both + * routines set a struct logininfo flag defining which action (log in, + * or log out) is to be taken. They both then call login_write(), which + * calls whichever of the many structure-specific handlers autoconf + * selects for the local system. + * + * The handlers themselves handle system data structure specifics. Both + * struct utmp and struct utmpx have utility functions (see + * construct_utmp*()) to try to make it simpler to add extra systems + * that introduce new features to either structure. + * + * While it may seem terribly wasteful to replicate so much similar + * code for each method, experience has shown that maintaining code to + * write both struct utmp and utmpx in one function, whilst maintaining + * support for all systems whether they have library support or not, is + * a difficult and time-consuming task. + * + * Lastlog support proceeds similarly. Functions login_get_lastlog() + * (and its OpenSSH-tuned friend login_get_lastlog_time()) call + * getlast_entry(), which tries one of three methods to find the last + * login time. It uses local system lastlog support if it can, + * otherwise it tries wtmp or wtmpx before giving up and returning 0, + * meaning "tilt". + * + * Maintenance + * ----------- + * + * In many cases it's possible to tweak autoconf to select the correct + * methods for a particular platform, either by improving the detection + * code (best), or by presetting DISABLE_ or CONF__FILE + * symbols for the platform. + * + * Use logintest to check which symbols are defined before modifying + * configure.ac and loginrec.c. (You have to build logintest yourself + * with 'make logintest' as it's not built by default.) + * + * Otherwise, patches to the specific method(s) are very helpful! + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "sshkey.h" +#include "hostfile.h" +#include "ssh.h" +#include "loginrec.h" +#include "log.h" +#include "atomicio.h" +#include "packet.h" +#include "canohost.h" +#include "auth.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "misc.h" + +#ifdef HAVE_UTIL_H +# include +#endif + +/** + ** prototypes for helper functions in this file + **/ + +#if HAVE_UTMP_H +void set_utmp_time(struct logininfo *li, struct utmp *ut); +void construct_utmp(struct logininfo *li, struct utmp *ut); +#endif + +#ifdef HAVE_UTMPX_H +void set_utmpx_time(struct logininfo *li, struct utmpx *ut); +void construct_utmpx(struct logininfo *li, struct utmpx *ut); +#endif + +int utmp_write_entry(struct logininfo *li); +int utmpx_write_entry(struct logininfo *li); +int wtmp_write_entry(struct logininfo *li); +int wtmpx_write_entry(struct logininfo *li); +int lastlog_write_entry(struct logininfo *li); +int syslogin_write_entry(struct logininfo *li); + +int getlast_entry(struct logininfo *li); +int lastlog_get_entry(struct logininfo *li); +int utmpx_get_entry(struct logininfo *li); +int wtmp_get_entry(struct logininfo *li); +int wtmpx_get_entry(struct logininfo *li); + +extern struct sshbuf *loginmsg; + +/* pick the shortest string */ +#define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2)) + +/** + ** platform-independent login functions + **/ + +/* + * login_login(struct logininfo *) - Record a login + * + * Call with a pointer to a struct logininfo initialised with + * login_init_entry() or login_alloc_entry() + * + * Returns: + * >0 if successful + * 0 on failure (will use OpenSSH's logging facilities for diagnostics) + */ +int +login_login(struct logininfo *li) +{ + li->type = LTYPE_LOGIN; + return (login_write(li)); +} + + +/* + * login_logout(struct logininfo *) - Record a logout + * + * Call as with login_login() + * + * Returns: + * >0 if successful + * 0 on failure (will use OpenSSH's logging facilities for diagnostics) + */ +int +login_logout(struct logininfo *li) +{ + li->type = LTYPE_LOGOUT; + return (login_write(li)); +} + +/* + * login_get_lastlog_time(int) - Retrieve the last login time + * + * Retrieve the last login time for the given uid. Will try to use the + * system lastlog facilities if they are available, but will fall back + * to looking in wtmp/wtmpx if necessary + * + * Returns: + * 0 on failure, or if user has never logged in + * Time in seconds from the epoch if successful + * + * Useful preprocessor symbols: + * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog + * info + * USE_LASTLOG: If set, indicates the presence of system lastlog + * facilities. If this and DISABLE_LASTLOG are not set, + * try to retrieve lastlog information from wtmp/wtmpx. + */ +unsigned int +login_get_lastlog_time(const uid_t uid) +{ + struct logininfo li; + + if (login_get_lastlog(&li, uid)) + return (li.tv_sec); + else + return (0); +} + +/* + * login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry + * + * Retrieve a logininfo structure populated (only partially) with + * information from the system lastlog data, or from wtmp/wtmpx if no + * system lastlog information exists. + * + * Note this routine must be given a pre-allocated logininfo. + * + * Returns: + * >0: A pointer to your struct logininfo if successful + * 0 on failure (will use OpenSSH's logging facilities for diagnostics) + */ +struct logininfo * +login_get_lastlog(struct logininfo *li, const uid_t uid) +{ + struct passwd *pw; + + memset(li, '\0', sizeof(*li)); + li->uid = uid; + + /* + * If we don't have a 'real' lastlog, we need the username to + * reliably search wtmp(x) for the last login (see + * wtmp_get_entry().) + */ + pw = getpwuid(uid); + if (pw == NULL) + fatal("%s: Cannot find account for uid %ld", __func__, + (long)uid); + + if (strlcpy(li->username, pw->pw_name, sizeof(li->username)) >= + sizeof(li->username)) { + error("%s: username too long (%lu > max %lu)", __func__, + (unsigned long)strlen(pw->pw_name), + (unsigned long)sizeof(li->username) - 1); + return NULL; + } + + if (getlast_entry(li)) + return (li); + else + return (NULL); +} + +/* + * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise + * a logininfo structure + * + * This function creates a new struct logininfo, a data structure + * meant to carry the information required to portably record login info. + * + * Returns a pointer to a newly created struct logininfo. If memory + * allocation fails, the program halts. + */ +struct +logininfo *login_alloc_entry(pid_t pid, const char *username, + const char *hostname, const char *line) +{ + struct logininfo *newli; + + newli = xmalloc(sizeof(*newli)); + login_init_entry(newli, pid, username, hostname, line); + return (newli); +} + + +/* login_free_entry(struct logininfo *) - free struct memory */ +void +login_free_entry(struct logininfo *li) +{ + free(li); +} + + +/* login_init_entry(struct logininfo *, int, char*, char*, char*) + * - initialise a struct logininfo + * + * Populates a new struct logininfo, a data structure meant to carry + * the information required to portably record login info. + * + * Returns: 1 + */ +int +login_init_entry(struct logininfo *li, pid_t pid, const char *username, + const char *hostname, const char *line) +{ + struct passwd *pw; + + memset(li, 0, sizeof(*li)); + + li->pid = pid; + + /* set the line information */ + if (line) + line_fullname(li->line, line, sizeof(li->line)); + + if (username) { + strlcpy(li->username, username, sizeof(li->username)); + pw = getpwnam(li->username); + if (pw == NULL) { + fatal("%s: Cannot find user \"%s\"", __func__, + li->username); + } + li->uid = pw->pw_uid; + } + + if (hostname) + strlcpy(li->hostname, hostname, sizeof(li->hostname)); + + return (1); +} + +/* + * login_set_current_time(struct logininfo *) - set the current time + * + * Set the current time in a logininfo structure. This function is + * meant to eliminate the need to deal with system dependencies for + * time handling. + */ +void +login_set_current_time(struct logininfo *li) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + li->tv_sec = tv.tv_sec; + li->tv_usec = tv.tv_usec; +} + +/* copy a sockaddr_* into our logininfo */ +void +login_set_addr(struct logininfo *li, const struct sockaddr *sa, + const unsigned int sa_size) +{ + unsigned int bufsize = sa_size; + + /* make sure we don't overrun our union */ + if (sizeof(li->hostaddr) < sa_size) + bufsize = sizeof(li->hostaddr); + + memcpy(&li->hostaddr.sa, sa, bufsize); +} + + +/** + ** login_write: Call low-level recording functions based on autoconf + ** results + **/ +int +login_write(struct logininfo *li) +{ +#ifndef HAVE_CYGWIN + if (geteuid() != 0) { + logit("Attempt to write login records by non-root user (aborting)"); + return (1); + } +#endif + + /* set the timestamp */ + login_set_current_time(li); +#ifdef USE_LOGIN + syslogin_write_entry(li); +#endif +#ifdef USE_LASTLOG + if (li->type == LTYPE_LOGIN) + lastlog_write_entry(li); +#endif +#ifdef USE_UTMP + utmp_write_entry(li); +#endif +#ifdef USE_WTMP + wtmp_write_entry(li); +#endif +#ifdef USE_UTMPX + utmpx_write_entry(li); +#endif +#ifdef USE_WTMPX + wtmpx_write_entry(li); +#endif +#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN + if (li->type == LTYPE_LOGIN && + !sys_auth_record_login(li->username,li->hostname,li->line, + loginmsg)) + logit("Writing login record failed for %s", li->username); +#endif +#ifdef SSH_AUDIT_EVENTS + if (li->type == LTYPE_LOGIN) + audit_session_open(li); + else if (li->type == LTYPE_LOGOUT) + audit_session_close(li); +#endif + return (0); +} + +#ifdef LOGIN_NEEDS_UTMPX +int +login_utmp_only(struct logininfo *li) +{ + li->type = LTYPE_LOGIN; + login_set_current_time(li); +# ifdef USE_UTMP + utmp_write_entry(li); +# endif +# ifdef USE_WTMP + wtmp_write_entry(li); +# endif +# ifdef USE_UTMPX + utmpx_write_entry(li); +# endif +# ifdef USE_WTMPX + wtmpx_write_entry(li); +# endif + return (0); +} +#endif + +/** + ** getlast_entry: Call low-level functions to retrieve the last login + ** time. + **/ + +/* take the uid in li and return the last login time */ +int +getlast_entry(struct logininfo *li) +{ +#ifdef USE_LASTLOG + return(lastlog_get_entry(li)); +#else /* !USE_LASTLOG */ +#if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ + defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) + return (utmpx_get_entry(li)); +#endif + +#if defined(DISABLE_LASTLOG) + /* On some systems we shouldn't even try to obtain last login + * time, e.g. AIX */ + return (0); +# elif defined(USE_WTMP) && \ + (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) + /* retrieve last login time from utmp */ + return (wtmp_get_entry(li)); +# elif defined(USE_WTMPX) && \ + (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) + /* If wtmp isn't available, try wtmpx */ + return (wtmpx_get_entry(li)); +# else + /* Give up: No means of retrieving last login time */ + return (0); +# endif /* DISABLE_LASTLOG */ +#endif /* USE_LASTLOG */ +} + + + +/* + * 'line' string utility functions + * + * These functions process the 'line' string into one of three forms: + * + * 1. The full filename (including '/dev') + * 2. The stripped name (excluding '/dev') + * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 + * /dev/pts/1 -> ts/1 ) + * + * Form 3 is used on some systems to identify a .tmp.? entry when + * attempting to remove it. Typically both addition and removal is + * performed by one application - say, sshd - so as long as the choice + * uniquely identifies a terminal it's ok. + */ + + +/* + * line_fullname(): add the leading '/dev/' if it doesn't exist make + * sure dst has enough space, if not just copy src (ugh) + */ +char * +line_fullname(char *dst, const char *src, u_int dstsize) +{ + memset(dst, '\0', dstsize); + if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) + strlcpy(dst, src, dstsize); + else { + strlcpy(dst, "/dev/", dstsize); + strlcat(dst, src, dstsize); + } + return (dst); +} + +/* line_stripname(): strip the leading '/dev' if it exists, return dst */ +char * +line_stripname(char *dst, const char *src, int dstsize) +{ + memset(dst, '\0', dstsize); + if (strncmp(src, "/dev/", 5) == 0) + strlcpy(dst, src + 5, dstsize); + else + strlcpy(dst, src, dstsize); + return (dst); +} + +/* + * line_abbrevname(): Return the abbreviated (usually four-character) + * form of the line (Just use the last characters of the + * full name.) + * + * NOTE: use strncpy because we do NOT necessarily want zero + * termination + */ +char * +line_abbrevname(char *dst, const char *src, int dstsize) +{ + size_t len; + + memset(dst, '\0', dstsize); + + /* Always skip prefix if present */ + if (strncmp(src, "/dev/", 5) == 0) + src += 5; + +#ifdef WITH_ABBREV_NO_TTY + if (strncmp(src, "tty", 3) == 0) + src += 3; +#endif + + len = strlen(src); + + if (len > 0) { + if (((int)len - dstsize) > 0) + src += ((int)len - dstsize); + + /* note: _don't_ change this to strlcpy */ + strncpy(dst, src, (size_t)dstsize); + } + + return (dst); +} + +/** + ** utmp utility functions + ** + ** These functions manipulate struct utmp, taking system differences + ** into account. + **/ + +#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) + +/* build the utmp structure */ +void +set_utmp_time(struct logininfo *li, struct utmp *ut) +{ +# if defined(HAVE_TV_IN_UTMP) + ut->ut_tv.tv_sec = li->tv_sec; + ut->ut_tv.tv_usec = li->tv_usec; +# elif defined(HAVE_TIME_IN_UTMP) + ut->ut_time = li->tv_sec; +# endif +} + +void +construct_utmp(struct logininfo *li, + struct utmp *ut) +{ +# ifdef HAVE_ADDR_V6_IN_UTMP + struct sockaddr_in6 *sa6; +# endif + + memset(ut, '\0', sizeof(*ut)); + + /* First fill out fields used for both logins and logouts */ + +# ifdef HAVE_ID_IN_UTMP + line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); +# endif + +# ifdef HAVE_TYPE_IN_UTMP + /* This is done here to keep utmp constants out of struct logininfo */ + switch (li->type) { + case LTYPE_LOGIN: + ut->ut_type = USER_PROCESS; + break; + case LTYPE_LOGOUT: + ut->ut_type = DEAD_PROCESS; + break; + } +# endif + set_utmp_time(li, ut); + + line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); + +# ifdef HAVE_PID_IN_UTMP + ut->ut_pid = li->pid; +# endif + + /* If we're logging out, leave all other fields blank */ + if (li->type == LTYPE_LOGOUT) + return; + + /* + * These fields are only used when logging in, and are blank + * for logouts. + */ + + /* Use strncpy because we don't necessarily want null termination */ + strncpy(ut->ut_name, li->username, + MIN_SIZEOF(ut->ut_name, li->username)); +# ifdef HAVE_HOST_IN_UTMP + strncpy(ut->ut_host, li->hostname, + MIN_SIZEOF(ut->ut_host, li->hostname)); +# endif +# ifdef HAVE_ADDR_IN_UTMP + /* this is just a 32-bit IP address */ + if (li->hostaddr.sa.sa_family == AF_INET) + ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; +# endif +# ifdef HAVE_ADDR_V6_IN_UTMP + /* this is just a 128-bit IPv6 address */ + if (li->hostaddr.sa.sa_family == AF_INET6) { + sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); + memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); + if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { + ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; + ut->ut_addr_v6[1] = 0; + ut->ut_addr_v6[2] = 0; + ut->ut_addr_v6[3] = 0; + } + } +# endif +} +#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ + +/** + ** utmpx utility functions + ** + ** These functions manipulate struct utmpx, accounting for system + ** variations. + **/ + +#if defined(USE_UTMPX) || defined (USE_WTMPX) +/* build the utmpx structure */ +void +set_utmpx_time(struct logininfo *li, struct utmpx *utx) +{ +# if defined(HAVE_TV_IN_UTMPX) + utx->ut_tv.tv_sec = li->tv_sec; + utx->ut_tv.tv_usec = li->tv_usec; +# elif defined(HAVE_TIME_IN_UTMPX) + utx->ut_time = li->tv_sec; +# endif +} + +void +construct_utmpx(struct logininfo *li, struct utmpx *utx) +{ +# ifdef HAVE_ADDR_V6_IN_UTMP + struct sockaddr_in6 *sa6; +# endif + memset(utx, '\0', sizeof(*utx)); + +# ifdef HAVE_ID_IN_UTMPX + line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); +# endif + + /* this is done here to keep utmp constants out of loginrec.h */ + switch (li->type) { + case LTYPE_LOGIN: + utx->ut_type = USER_PROCESS; + break; + case LTYPE_LOGOUT: + utx->ut_type = DEAD_PROCESS; + break; + } + line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); + set_utmpx_time(li, utx); + utx->ut_pid = li->pid; + + /* strncpy(): Don't necessarily want null termination */ + strncpy(utx->ut_user, li->username, + MIN_SIZEOF(utx->ut_user, li->username)); + + if (li->type == LTYPE_LOGOUT) + return; + + /* + * These fields are only used when logging in, and are blank + * for logouts. + */ + +# ifdef HAVE_HOST_IN_UTMPX + strncpy(utx->ut_host, li->hostname, + MIN_SIZEOF(utx->ut_host, li->hostname)); +# endif +# ifdef HAVE_SS_IN_UTMPX + utx->ut_ss = li->hostaddr.sa_storage; +# endif +# ifdef HAVE_ADDR_IN_UTMPX + /* this is just a 32-bit IP address */ + if (li->hostaddr.sa.sa_family == AF_INET) + utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; +# endif +# ifdef HAVE_ADDR_V6_IN_UTMP + /* this is just a 128-bit IPv6 address */ + if (li->hostaddr.sa.sa_family == AF_INET6) { + sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); + memcpy(utx->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); + if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { + utx->ut_addr_v6[0] = utx->ut_addr_v6[3]; + utx->ut_addr_v6[1] = 0; + utx->ut_addr_v6[2] = 0; + utx->ut_addr_v6[3] = 0; + } + } +# endif +# ifdef HAVE_SYSLEN_IN_UTMPX + /* ut_syslen is the length of the utx_host string */ + utx->ut_syslen = MINIMUM(strlen(li->hostname), sizeof(utx->ut_host)); +# endif +} +#endif /* USE_UTMPX || USE_WTMPX */ + +/** + ** Low-level utmp functions + **/ + +/* FIXME: (ATL) utmp_write_direct needs testing */ +#ifdef USE_UTMP + +/* if we can, use pututline() etc. */ +# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ + defined(HAVE_PUTUTLINE) +# define UTMP_USE_LIBRARY +# endif + + +/* write a utmp entry with the system's help (pututline() and pals) */ +# ifdef UTMP_USE_LIBRARY +static int +utmp_write_library(struct logininfo *li, struct utmp *ut) +{ + setutent(); + pututline(ut); +# ifdef HAVE_ENDUTENT + endutent(); +# endif + return (1); +} +# else /* UTMP_USE_LIBRARY */ + +/* + * Write a utmp entry direct to the file + * This is a slightly modification of code in OpenBSD's login.c + */ +static int +utmp_write_direct(struct logininfo *li, struct utmp *ut) +{ + struct utmp old_ut; + register int fd; + int tty; + + /* FIXME: (ATL) ttyslot() needs local implementation */ + +#if defined(HAVE_GETTTYENT) + struct ttyent *ty; + + tty=0; + setttyent(); + while (NULL != (ty = getttyent())) { + tty++; + if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) + break; + } + endttyent(); + + if (NULL == ty) { + logit("%s: tty not found", __func__); + return (0); + } +#else /* FIXME */ + + tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ + +#endif /* HAVE_GETTTYENT */ + + if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { + off_t pos, ret; + + pos = (off_t)tty * sizeof(struct utmp); + if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { + logit("%s: lseek: %s", __func__, strerror(errno)); + close(fd); + return (0); + } + if (ret != pos) { + logit("%s: Couldn't seek to tty %d slot in %s", + __func__, tty, UTMP_FILE); + close(fd); + return (0); + } + /* + * Prevent luser from zero'ing out ut_host. + * If the new ut_line is empty but the old one is not + * and ut_line and ut_name match, preserve the old ut_line. + */ + if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && + (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && + (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && + (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) + memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); + + if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { + logit("%s: lseek: %s", __func__, strerror(errno)); + close(fd); + return (0); + } + if (ret != pos) { + logit("%s: Couldn't seek to tty %d slot in %s", + __func__, tty, UTMP_FILE); + close(fd); + return (0); + } + if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { + logit("%s: error writing %s: %s", __func__, + UTMP_FILE, strerror(errno)); + close(fd); + return (0); + } + + close(fd); + return (1); + } else { + return (0); + } +} +# endif /* UTMP_USE_LIBRARY */ + +static int +utmp_perform_login(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); +# ifdef UTMP_USE_LIBRARY + if (!utmp_write_library(li, &ut)) { + logit("%s: utmp_write_library() failed", __func__); + return (0); + } +# else + if (!utmp_write_direct(li, &ut)) { + logit("%s: utmp_write_direct() failed", __func__); + return (0); + } +# endif + return (1); +} + + +static int +utmp_perform_logout(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); +# ifdef UTMP_USE_LIBRARY + if (!utmp_write_library(li, &ut)) { + logit("%s: utmp_write_library() failed", __func__); + return (0); + } +# else + if (!utmp_write_direct(li, &ut)) { + logit("%s: utmp_write_direct() failed", __func__); + return (0); + } +# endif + return (1); +} + + +int +utmp_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return (utmp_perform_login(li)); + + case LTYPE_LOGOUT: + return (utmp_perform_logout(li)); + + default: + logit("%s: invalid type field", __func__); + return (0); + } +} +#endif /* USE_UTMP */ + + +/** + ** Low-level utmpx functions + **/ + +/* not much point if we don't want utmpx entries */ +#ifdef USE_UTMPX + +/* if we have the wherewithall, use pututxline etc. */ +# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ + defined(HAVE_PUTUTXLINE) +# define UTMPX_USE_LIBRARY +# endif + + +/* write a utmpx entry with the system's help (pututxline() and pals) */ +# ifdef UTMPX_USE_LIBRARY +static int +utmpx_write_library(struct logininfo *li, struct utmpx *utx) +{ + setutxent(); + pututxline(utx); + +# ifdef HAVE_ENDUTXENT + endutxent(); +# endif + return (1); +} + +# else /* UTMPX_USE_LIBRARY */ + +/* write a utmp entry direct to the file */ +static int +utmpx_write_direct(struct logininfo *li, struct utmpx *utx) +{ + logit("%s: not implemented!", __func__); + return (0); +} +# endif /* UTMPX_USE_LIBRARY */ + +static int +utmpx_perform_login(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); +# ifdef UTMPX_USE_LIBRARY + if (!utmpx_write_library(li, &utx)) { + logit("%s: utmp_write_library() failed", __func__); + return (0); + } +# else + if (!utmpx_write_direct(li, &ut)) { + logit("%s: utmp_write_direct() failed", __func__); + return (0); + } +# endif + return (1); +} + + +static int +utmpx_perform_logout(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); +# ifdef HAVE_ID_IN_UTMPX + line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); +# endif +# ifdef HAVE_TYPE_IN_UTMPX + utx.ut_type = DEAD_PROCESS; +# endif + +# ifdef UTMPX_USE_LIBRARY + utmpx_write_library(li, &utx); +# else + utmpx_write_direct(li, &utx); +# endif + return (1); +} + +int +utmpx_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return (utmpx_perform_login(li)); + case LTYPE_LOGOUT: + return (utmpx_perform_logout(li)); + default: + logit("%s: invalid type field", __func__); + return (0); + } +} +#endif /* USE_UTMPX */ + + +/** + ** Low-level wtmp functions + **/ + +#ifdef USE_WTMP + +/* + * Write a wtmp entry direct to the end of the file + * This is a slight modification of code in OpenBSD's logwtmp.c + */ +static int +wtmp_write(struct logininfo *li, struct utmp *ut) +{ + struct stat buf; + int fd, ret = 1; + + if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { + logit("%s: problem writing %s: %s", __func__, + WTMP_FILE, strerror(errno)); + return (0); + } + if (fstat(fd, &buf) == 0) + if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { + ftruncate(fd, buf.st_size); + logit("%s: problem writing %s: %s", __func__, + WTMP_FILE, strerror(errno)); + ret = 0; + } + close(fd); + return (ret); +} + +static int +wtmp_perform_login(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); + return (wtmp_write(li, &ut)); +} + + +static int +wtmp_perform_logout(struct logininfo *li) +{ + struct utmp ut; + + construct_utmp(li, &ut); + return (wtmp_write(li, &ut)); +} + + +int +wtmp_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return (wtmp_perform_login(li)); + case LTYPE_LOGOUT: + return (wtmp_perform_logout(li)); + default: + logit("%s: invalid type field", __func__); + return (0); + } +} + + +/* + * Notes on fetching login data from wtmp/wtmpx + * + * Logouts are usually recorded with (amongst other things) a blank + * username on a given tty line. However, some systems (HP-UX is one) + * leave all fields set, but change the ut_type field to DEAD_PROCESS. + * + * Since we're only looking for logins here, we know that the username + * must be set correctly. On systems that leave it in, we check for + * ut_type==USER_PROCESS (indicating a login.) + * + * Portability: Some systems may set something other than USER_PROCESS + * to indicate a login process. I don't know of any as I write. Also, + * it's possible that some systems may both leave the username in + * place and not have ut_type. + */ + +/* return true if this wtmp entry indicates a login */ +static int +wtmp_islogin(struct logininfo *li, struct utmp *ut) +{ + if (strncmp(li->username, ut->ut_name, + MIN_SIZEOF(li->username, ut->ut_name)) == 0) { +# ifdef HAVE_TYPE_IN_UTMP + if (ut->ut_type & USER_PROCESS) + return (1); +# else + return (1); +# endif + } + return (0); +} + +int +wtmp_get_entry(struct logininfo *li) +{ + struct stat st; + struct utmp ut; + int fd, found = 0; + + /* Clear the time entries in our logininfo */ + li->tv_sec = li->tv_usec = 0; + + if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { + logit("%s: problem opening %s: %s", __func__, + WTMP_FILE, strerror(errno)); + return (0); + } + if (fstat(fd, &st) != 0) { + logit("%s: couldn't stat %s: %s", __func__, + WTMP_FILE, strerror(errno)); + close(fd); + return (0); + } + + /* Seek to the start of the last struct utmp */ + if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { + /* Looks like we've got a fresh wtmp file */ + close(fd); + return (0); + } + + while (!found) { + if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { + logit("%s: read of %s failed: %s", __func__, + WTMP_FILE, strerror(errno)); + close (fd); + return (0); + } + if (wtmp_islogin(li, &ut) ) { + found = 1; + /* + * We've already checked for a time in struct + * utmp, in login_getlast() + */ +# ifdef HAVE_TIME_IN_UTMP + li->tv_sec = ut.ut_time; +# else +# if HAVE_TV_IN_UTMP + li->tv_sec = ut.ut_tv.tv_sec; +# endif +# endif + line_fullname(li->line, ut.ut_line, + MIN_SIZEOF(li->line, ut.ut_line)); +# ifdef HAVE_HOST_IN_UTMP + strlcpy(li->hostname, ut.ut_host, + MIN_SIZEOF(li->hostname, ut.ut_host)); +# endif + continue; + } + /* Seek back 2 x struct utmp */ + if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { + /* We've found the start of the file, so quit */ + close(fd); + return (0); + } + } + + /* We found an entry. Tidy up and return */ + close(fd); + return (1); +} +# endif /* USE_WTMP */ + + +/** + ** Low-level wtmpx functions + **/ + +#ifdef USE_WTMPX +/* + * Write a wtmpx entry direct to the end of the file + * This is a slight modification of code in OpenBSD's logwtmp.c + */ +static int +wtmpx_write(struct logininfo *li, struct utmpx *utx) +{ +#ifndef HAVE_UPDWTMPX + struct stat buf; + int fd, ret = 1; + + if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { + logit("%s: problem opening %s: %s", __func__, + WTMPX_FILE, strerror(errno)); + return (0); + } + + if (fstat(fd, &buf) == 0) + if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { + ftruncate(fd, buf.st_size); + logit("%s: problem writing %s: %s", __func__, + WTMPX_FILE, strerror(errno)); + ret = 0; + } + close(fd); + + return (ret); +#else + updwtmpx(WTMPX_FILE, utx); + return (1); +#endif +} + + +static int +wtmpx_perform_login(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); + return (wtmpx_write(li, &utx)); +} + + +static int +wtmpx_perform_logout(struct logininfo *li) +{ + struct utmpx utx; + + construct_utmpx(li, &utx); + return (wtmpx_write(li, &utx)); +} + + +int +wtmpx_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return (wtmpx_perform_login(li)); + case LTYPE_LOGOUT: + return (wtmpx_perform_logout(li)); + default: + logit("%s: invalid type field", __func__); + return (0); + } +} + +/* Please see the notes above wtmp_islogin() for information about the + next two functions */ + +/* Return true if this wtmpx entry indicates a login */ +static int +wtmpx_islogin(struct logininfo *li, struct utmpx *utx) +{ + if (strncmp(li->username, utx->ut_user, + MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { +# ifdef HAVE_TYPE_IN_UTMPX + if (utx->ut_type == USER_PROCESS) + return (1); +# else + return (1); +# endif + } + return (0); +} + + +int +wtmpx_get_entry(struct logininfo *li) +{ + struct stat st; + struct utmpx utx; + int fd, found=0; + + /* Clear the time entries */ + li->tv_sec = li->tv_usec = 0; + + if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { + logit("%s: problem opening %s: %s", __func__, + WTMPX_FILE, strerror(errno)); + return (0); + } + if (fstat(fd, &st) != 0) { + logit("%s: couldn't stat %s: %s", __func__, + WTMPX_FILE, strerror(errno)); + close(fd); + return (0); + } + + /* Seek to the start of the last struct utmpx */ + if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { + /* probably a newly rotated wtmpx file */ + close(fd); + return (0); + } + + while (!found) { + if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { + logit("%s: read of %s failed: %s", __func__, + WTMPX_FILE, strerror(errno)); + close (fd); + return (0); + } + /* + * Logouts are recorded as a blank username on a particular + * line. So, we just need to find the username in struct utmpx + */ + if (wtmpx_islogin(li, &utx)) { + found = 1; +# if defined(HAVE_TV_IN_UTMPX) + li->tv_sec = utx.ut_tv.tv_sec; +# elif defined(HAVE_TIME_IN_UTMPX) + li->tv_sec = utx.ut_time; +# endif + line_fullname(li->line, utx.ut_line, sizeof(li->line)); +# if defined(HAVE_HOST_IN_UTMPX) + strlcpy(li->hostname, utx.ut_host, + MIN_SIZEOF(li->hostname, utx.ut_host)); +# endif + continue; + } + if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { + close(fd); + return (0); + } + } + + close(fd); + return (1); +} +#endif /* USE_WTMPX */ + +/** + ** Low-level libutil login() functions + **/ + +#ifdef USE_LOGIN +static int +syslogin_perform_login(struct logininfo *li) +{ + struct utmp *ut; + + ut = xmalloc(sizeof(*ut)); + construct_utmp(li, ut); + login(ut); + free(ut); + + return (1); +} + +static int +syslogin_perform_logout(struct logininfo *li) +{ +# ifdef HAVE_LOGOUT + char line[UT_LINESIZE]; + + (void)line_stripname(line, li->line, sizeof(line)); + + if (!logout(line)) + logit("%s: logout() returned an error", __func__); +# ifdef HAVE_LOGWTMP + else + logwtmp(line, "", ""); +# endif + /* FIXME: (ATL - if the need arises) What to do if we have + * login, but no logout? what if logout but no logwtmp? All + * routines are in libutil so they should all be there, + * but... */ +# endif + return (1); +} + +int +syslogin_write_entry(struct logininfo *li) +{ + switch (li->type) { + case LTYPE_LOGIN: + return (syslogin_perform_login(li)); + case LTYPE_LOGOUT: + return (syslogin_perform_logout(li)); + default: + logit("%s: Invalid type field", __func__); + return (0); + } +} +#endif /* USE_LOGIN */ + +/* end of file log-syslogin.c */ + +/** + ** Low-level lastlog functions + **/ + +#ifdef USE_LASTLOG + +#if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME) +/* open the file (using filemode) and seek to the login entry */ +static int +lastlog_openseek(struct logininfo *li, int *fd, int filemode) +{ + off_t offset; + char lastlog_file[1024]; + struct stat st; + + if (stat(LASTLOG_FILE, &st) != 0) { + logit("%s: Couldn't stat %s: %s", __func__, + LASTLOG_FILE, strerror(errno)); + return (0); + } + if (S_ISDIR(st.st_mode)) { + snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", + LASTLOG_FILE, li->username); + } else if (S_ISREG(st.st_mode)) { + strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); + } else { + logit("%s: %.100s is not a file or directory!", __func__, + LASTLOG_FILE); + return (0); + } + + *fd = open(lastlog_file, filemode, 0600); + if (*fd < 0) { + debug("%s: Couldn't open %s: %s", __func__, + lastlog_file, strerror(errno)); + return (0); + } + + if (S_ISREG(st.st_mode)) { + /* find this uid's offset in the lastlog file */ + offset = (off_t) ((u_long)li->uid * sizeof(struct lastlog)); + + if (lseek(*fd, offset, SEEK_SET) != offset) { + logit("%s: %s->lseek(): %s", __func__, + lastlog_file, strerror(errno)); + close(*fd); + return (0); + } + } + + return (1); +} +#endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */ + +#ifdef LASTLOG_WRITE_PUTUTXLINE +int +lastlog_write_entry(struct logininfo *li) +{ + switch(li->type) { + case LTYPE_LOGIN: + return 1; /* lastlog written by pututxline */ + default: + logit("lastlog_write_entry: Invalid type field"); + return 0; + } +} +#else /* LASTLOG_WRITE_PUTUTXLINE */ +int +lastlog_write_entry(struct logininfo *li) +{ + struct lastlog last; + int fd; + + switch(li->type) { + case LTYPE_LOGIN: + /* create our struct lastlog */ + memset(&last, '\0', sizeof(last)); + line_stripname(last.ll_line, li->line, sizeof(last.ll_line)); + strlcpy(last.ll_host, li->hostname, + MIN_SIZEOF(last.ll_host, li->hostname)); + last.ll_time = li->tv_sec; + + if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) + return (0); + + /* write the entry */ + if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { + close(fd); + logit("%s: Error writing to %s: %s", __func__, + LASTLOG_FILE, strerror(errno)); + return (0); + } + + close(fd); + return (1); + default: + logit("%s: Invalid type field", __func__); + return (0); + } +} +#endif /* LASTLOG_WRITE_PUTUTXLINE */ + +#ifdef HAVE_GETLASTLOGXBYNAME +int +lastlog_get_entry(struct logininfo *li) +{ + struct lastlogx l, *ll; + + if ((ll = getlastlogxbyname(li->username, &l)) == NULL) { + memset(&l, '\0', sizeof(l)); + ll = &l; + } + line_fullname(li->line, ll->ll_line, sizeof(li->line)); + strlcpy(li->hostname, ll->ll_host, + MIN_SIZEOF(li->hostname, ll->ll_host)); + li->tv_sec = ll->ll_tv.tv_sec; + li->tv_usec = ll->ll_tv.tv_usec; + return (1); +} +#else /* HAVE_GETLASTLOGXBYNAME */ +int +lastlog_get_entry(struct logininfo *li) +{ + struct lastlog last; + int fd, ret; + + if (!lastlog_openseek(li, &fd, O_RDONLY)) + return (0); + + ret = atomicio(read, fd, &last, sizeof(last)); + close(fd); + + switch (ret) { + case 0: + memset(&last, '\0', sizeof(last)); + /* FALLTHRU */ + case sizeof(last): + line_fullname(li->line, last.ll_line, sizeof(li->line)); + strlcpy(li->hostname, last.ll_host, + MIN_SIZEOF(li->hostname, last.ll_host)); + li->tv_sec = last.ll_time; + return (1); + case -1: + error("%s: Error reading from %s: %s", __func__, + LASTLOG_FILE, strerror(errno)); + return (0); + default: + error("%s: Error reading from %s: Expecting %d, got %d", + __func__, LASTLOG_FILE, (int)sizeof(last), ret); + return (0); + } + + /* NOTREACHED */ + return (0); +} +#endif /* HAVE_GETLASTLOGXBYNAME */ +#endif /* USE_LASTLOG */ + +#if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ + defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) +int +utmpx_get_entry(struct logininfo *li) +{ + struct utmpx *utx; + + if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0) + return (0); + utx = getutxuser(li->username); + if (utx == NULL) { + endutxent(); + return (0); + } + + line_fullname(li->line, utx->ut_line, + MIN_SIZEOF(li->line, utx->ut_line)); + strlcpy(li->hostname, utx->ut_host, + MIN_SIZEOF(li->hostname, utx->ut_host)); + li->tv_sec = utx->ut_tv.tv_sec; + li->tv_usec = utx->ut_tv.tv_usec; + endutxent(); + return (1); +} +#endif /* USE_UTMPX && HAVE_SETUTXDB && UTXDB_LASTLOGIN && HAVE_GETUTXUSER */ + +#ifdef USE_BTMP + /* + * Logs failed login attempts in _PATH_BTMP if that exists. + * The most common login failure is to give password instead of username. + * So the _PATH_BTMP file checked for the correct permission, so that + * only root can read it. + */ + +void +record_failed_login(struct ssh *ssh, const char *username, const char *hostname, + const char *ttyn) +{ + int fd; + struct utmp ut; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + struct sockaddr_in *a4; + struct sockaddr_in6 *a6; + time_t t; + struct stat fst; + + if (geteuid() != 0) + return; + if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { + debug("Unable to open the btmp file %s: %s", _PATH_BTMP, + strerror(errno)); + return; + } + if (fstat(fd, &fst) < 0) { + logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, + strerror(errno)); + goto out; + } + if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){ + logit("Excess permission or bad ownership on file %s", + _PATH_BTMP); + goto out; + } + + memset(&ut, 0, sizeof(ut)); + /* strncpy because we don't necessarily want nul termination */ + strncpy(ut.ut_user, username, sizeof(ut.ut_user)); + strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); + + time(&t); + ut.ut_time = t; /* ut_time is not always a time_t */ + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = getpid(); + + /* strncpy because we don't necessarily want nul termination */ + strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); + + if (ssh_packet_connection_is_on_socket(ssh) && + getpeername(ssh_packet_get_connection_in(ssh), + (struct sockaddr *)&from, &fromlen) == 0) { + ipv64_normalise_mapped(&from, &fromlen); + if (from.ss_family == AF_INET) { + a4 = (struct sockaddr_in *)&from; + memcpy(&ut.ut_addr, &(a4->sin_addr), + MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); + } +#ifdef HAVE_ADDR_V6_IN_UTMP + if (from.ss_family == AF_INET6) { + a6 = (struct sockaddr_in6 *)&from; + memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), + MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); + } +#endif + } + + if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) + error("Failed to write to %s: %s", _PATH_BTMP, + strerror(errno)); + +out: + close(fd); +} +#endif /* USE_BTMP */ diff --git a/aosp/external/openssh/loginrec.h b/aosp/external/openssh/loginrec.h new file mode 100644 index 0000000000000000000000000000000000000000..02bceb604c7faa3af940b029ae2444e9239889b8 --- /dev/null +++ b/aosp/external/openssh/loginrec.h @@ -0,0 +1,134 @@ +#ifndef _HAVE_LOGINREC_H_ +#define _HAVE_LOGINREC_H_ + +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + ** loginrec.h: platform-independent login recording and lastlog retrieval + **/ + +#include "includes.h" + +struct ssh; + +/** + ** you should use the login_* calls to work around platform dependencies + **/ + +/* + * login_netinfo structure + */ + +union login_netinfo { + struct sockaddr sa; + struct sockaddr_in sa_in; + struct sockaddr_storage sa_storage; +}; + +/* + * * logininfo structure * + */ +/* types - different to utmp.h 'type' macros */ +/* (though set to the same value as linux, openbsd and others...) */ +#define LTYPE_LOGIN 7 +#define LTYPE_LOGOUT 8 + +/* string lengths - set very long */ +#define LINFO_PROGSIZE 64 +#define LINFO_LINESIZE 64 +#define LINFO_NAMESIZE 512 +#define LINFO_HOSTSIZE 256 + +struct logininfo { + char progname[LINFO_PROGSIZE]; /* name of program (for PAM) */ + int progname_null; + short int type; /* type of login (LTYPE_*) */ + pid_t pid; /* PID of login process */ + uid_t uid; /* UID of this user */ + char line[LINFO_LINESIZE]; /* tty/pty name */ + char username[LINFO_NAMESIZE]; /* login username */ + char hostname[LINFO_HOSTSIZE]; /* remote hostname */ + /* 'exit_status' structure components */ + int exit; /* process exit status */ + int termination; /* process termination status */ + /* struct timeval (sys/time.h) isn't always available, if it isn't we'll + * use time_t's value as tv_sec and set tv_usec to 0 + */ + unsigned int tv_sec; + unsigned int tv_usec; + union login_netinfo hostaddr; /* caller's host address(es) */ +}; /* struct logininfo */ + +/* + * login recording functions + */ + +/** 'public' functions */ + +/* construct a new login entry */ +struct logininfo *login_alloc_entry(pid_t pid, const char *username, + const char *hostname, const char *line); +/* free a structure */ +void login_free_entry(struct logininfo *li); +/* fill out a pre-allocated structure with useful information */ +int login_init_entry(struct logininfo *li, pid_t pid, const char *username, + const char *hostname, const char *line); +/* place the current time in a logininfo struct */ +void login_set_current_time(struct logininfo *li); + +/* record the entry */ +int login_login (struct logininfo *li); +int login_logout(struct logininfo *li); +#ifdef LOGIN_NEEDS_UTMPX +int login_utmp_only(struct logininfo *li); +#endif + +/** End of public functions */ + +/* record the entry */ +int login_write (struct logininfo *li); +int login_log_entry(struct logininfo *li); + +/* set the network address based on network address type */ +void login_set_addr(struct logininfo *li, const struct sockaddr *sa, + const unsigned int sa_size); + +/* + * lastlog retrieval functions + */ +/* lastlog *entry* functions fill out a logininfo */ +struct logininfo *login_get_lastlog(struct logininfo *li, const uid_t uid); +/* lastlog *time* functions return time_t equivalent (uint) */ +unsigned int login_get_lastlog_time(const uid_t uid); + +/* produce various forms of the line filename */ +char *line_fullname(char *dst, const char *src, u_int dstsize); +char *line_stripname(char *dst, const char *src, int dstsize); +char *line_abbrevname(char *dst, const char *src, int dstsize); + +void record_failed_login(struct ssh *, const char *, const char *, + const char *); + +#endif /* _HAVE_LOGINREC_H_ */ diff --git a/aosp/external/openssh/logintest.c b/aosp/external/openssh/logintest.c new file mode 100644 index 0000000000000000000000000000000000000000..6ee1cdc23645d1e9ce725a65dc325278607caea5 --- /dev/null +++ b/aosp/external/openssh/logintest.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + ** logintest.c: simple test driver for platform-independent login recording + ** and lastlog retrieval + **/ + +#include "includes.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TIME_H +#include +#endif + +#include "loginrec.h" + +extern char *__progname; + +#define PAUSE_BEFORE_LOGOUT 3 + +int nologtest = 0; +int compile_opts_only = 0; +int be_verbose = 0; + + +/* Dump a logininfo to stdout. Assumes a tab size of 8 chars. */ +void +dump_logininfo(struct logininfo *li, char *descname) +{ + /* yes I know how nasty this is */ + printf("struct logininfo %s = {\n\t" + "progname\t'%s'\n\ttype\t\t%d\n\t" + "pid\t\t%d\n\tuid\t\t%d\n\t" + "line\t\t'%s'\n\tusername\t'%s'\n\t" + "hostname\t'%s'\n\texit\t\t%d\n\ttermination\t%d\n\t" + "tv_sec\t%d\n\ttv_usec\t%d\n\t" + "struct login_netinfo hostaddr {\n\t\t" + "struct sockaddr sa {\n" + "\t\t\tfamily\t%d\n\t\t}\n" + "\t}\n" + "}\n", + descname, li->progname, li->type, + li->pid, li->uid, li->line, + li->username, li->hostname, li->exit, + li->termination, li->tv_sec, li->tv_usec, + li->hostaddr.sa.sa_family); +} + + +int +testAPI() +{ + struct logininfo *li1; + struct passwd *pw; + struct hostent *he; + struct sockaddr_in sa_in4; + char cmdstring[256], stripline[8]; + char username[32]; +#ifdef HAVE_TIME_H + time_t t0, t1, t2, logintime, logouttime; + char s_t0[64],s_t1[64],s_t2[64]; + char s_logintime[64], s_logouttime[64]; /* ctime() strings */ +#endif + + printf("**\n** Testing the API...\n**\n"); + + pw = getpwuid(getuid()); + strlcpy(username, pw->pw_name, sizeof(username)); + + /* gethostname(hostname, sizeof(hostname)); */ + + printf("login_alloc_entry test (no host info):\n"); + + /* FIXME fake tty more effectively - this could upset some platforms */ + li1 = login_alloc_entry((int)getpid(), username, NULL, ttyname(0)); + strlcpy(li1->progname, "OpenSSH-logintest", sizeof(li1->progname)); + + if (be_verbose) + dump_logininfo(li1, "li1"); + + printf("Setting host address info for 'localhost' (may call out):\n"); + if (! (he = gethostbyname("localhost"))) { + printf("Couldn't set hostname(lookup failed)\n"); + } else { + /* NOTE: this is messy, but typically a program wouldn't have to set + * any of this, a sockaddr_in* would be already prepared */ + memcpy((void *)&(sa_in4.sin_addr), (void *)&(he->h_addr_list[0][0]), + sizeof(struct in_addr)); + login_set_addr(li1, (struct sockaddr *) &sa_in4, sizeof(sa_in4)); + strlcpy(li1->hostname, "localhost", sizeof(li1->hostname)); + } + if (be_verbose) + dump_logininfo(li1, "li1"); + + if ((int)geteuid() != 0) { + printf("NOT RUNNING LOGIN TESTS - you are not root!\n"); + return 1; + } + + if (nologtest) + return 1; + + line_stripname(stripline, li1->line, sizeof(stripline)); + + printf("Performing an invalid login attempt (no type field)\n--\n"); + login_write(li1); + printf("--\n(Should have written errors to stderr)\n"); + +#ifdef HAVE_TIME_H + (void)time(&t0); + strlcpy(s_t0, ctime(&t0), sizeof(s_t0)); + t1 = login_get_lastlog_time(getuid()); + strlcpy(s_t1, ctime(&t1), sizeof(s_t1)); + printf("Before logging in:\n\tcurrent time is %d - %s\t" + "lastlog time is %d - %s\n", + (int)t0, s_t0, (int)t1, s_t1); +#endif + + printf("Performing a login on line %s ", stripline); +#ifdef HAVE_TIME_H + (void)time(&logintime); + strlcpy(s_logintime, ctime(&logintime), sizeof(s_logintime)); + printf("at %d - %s", (int)logintime, s_logintime); +#endif + printf("--\n"); + login_login(li1); + + snprintf(cmdstring, sizeof(cmdstring), "who | grep '%s '", + stripline); + system(cmdstring); + + printf("--\nPausing for %d second(s)...\n", PAUSE_BEFORE_LOGOUT); + sleep(PAUSE_BEFORE_LOGOUT); + + printf("Performing a logout "); +#ifdef HAVE_TIME_H + (void)time(&logouttime); + strlcpy(s_logouttime, ctime(&logouttime), sizeof(s_logouttime)); + printf("at %d - %s", (int)logouttime, s_logouttime); +#endif + printf("\nThe root login shown above should be gone.\n" + "If the root login hasn't gone, but another user on the same\n" + "pty has, this is OK - we're hacking it here, and there\n" + "shouldn't be two users on one pty in reality...\n" + "-- ('who' output follows)\n"); + login_logout(li1); + + system(cmdstring); + printf("-- ('who' output ends)\n"); + +#ifdef HAVE_TIME_H + t2 = login_get_lastlog_time(getuid()); + strlcpy(s_t2, ctime(&t2), sizeof(s_t2)); + printf("After logging in, lastlog time is %d - %s\n", (int)t2, s_t2); + if (t1 == t2) + printf("The lastlog times before and after logging in are the " + "same.\nThis indicates that lastlog is ** NOT WORKING " + "CORRECTLY **\n"); + else if (t0 != t2) + /* We can be off by a second or so, even when recording works fine. + * I'm not 100% sure why, but it's true. */ + printf("** The login time and the lastlog time differ.\n" + "** This indicates that lastlog is either recording the " + "wrong time,\n** or retrieving the wrong entry.\n" + "If it's off by less than %d second(s) " + "run the test again.\n", PAUSE_BEFORE_LOGOUT); + else + printf("lastlog agrees with the login time. This is a good thing.\n"); + +#endif + + printf("--\nThe output of 'last' shown next should have " + "an entry for root \n on %s for the time shown above:\n--\n", + stripline); + snprintf(cmdstring, sizeof(cmdstring), "last | grep '%s ' | head -3", + stripline); + system(cmdstring); + + printf("--\nEnd of login test.\n"); + + login_free_entry(li1); + + return 1; +} /* testAPI() */ + + +void +testLineName(char *line) +{ + /* have to null-terminate - these functions are designed for + * structures with fixed-length char arrays, and don't null-term.*/ + char full[17], strip[9], abbrev[5]; + + memset(full, '\0', sizeof(full)); + memset(strip, '\0', sizeof(strip)); + memset(abbrev, '\0', sizeof(abbrev)); + + line_fullname(full, line, sizeof(full)-1); + line_stripname(strip, full, sizeof(strip)-1); + line_abbrevname(abbrev, full, sizeof(abbrev)-1); + printf("%s: %s, %s, %s\n", line, full, strip, abbrev); + +} /* testLineName() */ + + +int +testOutput() +{ + printf("**\n** Testing linename functions\n**\n"); + testLineName("/dev/pts/1"); + testLineName("pts/1"); + testLineName("pts/999"); + testLineName("/dev/ttyp00"); + testLineName("ttyp00"); + + return 1; +} /* testOutput() */ + + +/* show which options got compiled in */ +void +showOptions(void) +{ + printf("**\n** Compile-time options\n**\n"); + + printf("login recording methods selected:\n"); +#ifdef USE_LOGIN + printf("\tUSE_LOGIN\n"); +#endif +#ifdef USE_UTMP + printf("\tUSE_UTMP (UTMP_FILE=%s)\n", UTMP_FILE); +#endif +#ifdef USE_UTMPX + printf("\tUSE_UTMPX\n"); +#endif +#ifdef USE_WTMP + printf("\tUSE_WTMP (WTMP_FILE=%s)\n", WTMP_FILE); +#endif +#ifdef USE_WTMPX + printf("\tUSE_WTMPX (WTMPX_FILE=%s)\n", WTMPX_FILE); +#endif +#ifdef USE_LASTLOG + printf("\tUSE_LASTLOG (LASTLOG_FILE=%s)\n", LASTLOG_FILE); +#endif + printf("\n"); + +} /* showOptions() */ + + +int +main(int argc, char *argv[]) +{ + printf("Platform-independent login recording test driver\n"); + + __progname = ssh_get_progname(argv[0]); + if (argc == 2) { + if (strncmp(argv[1], "-i", 3) == 0) + compile_opts_only = 1; + else if (strncmp(argv[1], "-v", 3) == 0) + be_verbose=1; + } + + if (!compile_opts_only) { + if (be_verbose && !testOutput()) + return 1; + + if (!testAPI()) + return 1; + } + + showOptions(); + + return 0; +} /* main() */ + diff --git a/aosp/external/openssh/m4/openssh.m4 b/aosp/external/openssh/m4/openssh.m4 new file mode 100644 index 0000000000000000000000000000000000000000..8c33c701b8b42ee7eef99cdcbaf0c695591fe0b8 --- /dev/null +++ b/aosp/external/openssh/m4/openssh.m4 @@ -0,0 +1,203 @@ +dnl OpenSSH-specific autoconf macros +dnl + +dnl OSSH_CHECK_CFLAG_COMPILE(check_flag[, define_flag]) +dnl Check that $CC accepts a flag 'check_flag'. If it is supported append +dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append +dnl 'check_flag'. +AC_DEFUN([OSSH_CHECK_CFLAG_COMPILE], [{ + AC_MSG_CHECKING([if $CC supports compile flag $1]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $WERROR $1" + _define_flag="$2" + test "x$_define_flag" = "x" && _define_flag="$1" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#include +#include +/* Trivial function to help test for -fzero-call-used-regs */ +void f(int n) {} +int main(int argc, char **argv) { + (void)argv; + /* Some math to catch -ftrapv problems in the toolchain */ + int i = 123 * argc, j = 456 + argc, k = 789 - argc; + float l = i * 2.1; + double m = l / 0.5; + long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; + f(0); + printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); + /* + * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does + * not understand comments and we don't use the "fallthrough" attribute + * that it's looking for. + */ + switch(i){ + case 0: j += i; + /* FALLTHROUGH */ + default: j += k; + } + exit(0); +} + ]])], + [ +if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null +then + AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" +else + AC_MSG_RESULT([yes]) + CFLAGS="$saved_CFLAGS $_define_flag" +fi], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" ] + ) +}]) + +dnl OSSH_CHECK_CFLAG_LINK(check_flag[, define_flag]) +dnl Check that $CC accepts a flag 'check_flag'. If it is supported append +dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append +dnl 'check_flag'. +AC_DEFUN([OSSH_CHECK_CFLAG_LINK], [{ + AC_MSG_CHECKING([if $CC supports compile flag $1 and linking succeeds]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $WERROR $1" + _define_flag="$2" + test "x$_define_flag" = "x" && _define_flag="$1" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +#include +#include +int main(int argc, char **argv) { + (void)argv; + /* Some math to catch -ftrapv problems in the toolchain */ + int i = 123 * argc, j = 456 + argc, k = 789 - argc; + float l = i * 2.1; + double m = l / 0.5; + long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; + long long int p = n * o; + printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); + exit(0); +} + ]])], + [ +if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null +then + AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" +else + AC_MSG_RESULT([yes]) + CFLAGS="$saved_CFLAGS $_define_flag" +fi], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" ] + ) +}]) + +dnl OSSH_CHECK_LDFLAG_LINK(check_flag[, define_flag]) +dnl Check that $LD accepts a flag 'check_flag'. If it is supported append +dnl 'define_flag' to $LDFLAGS. If 'define_flag' is not specified, then append +dnl 'check_flag'. +AC_DEFUN([OSSH_CHECK_LDFLAG_LINK], [{ + AC_MSG_CHECKING([if $LD supports link flag $1]) + saved_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $WERROR $1" + _define_flag="$2" + test "x$_define_flag" = "x" && _define_flag="$1" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +#include +#include +int main(int argc, char **argv) { + (void)argv; + /* Some math to catch -ftrapv problems in the toolchain */ + int i = 123 * argc, j = 456 + argc, k = 789 - argc; + float l = i * 2.1; + double m = l / 0.5; + long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; + long long p = n * o; + printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); + exit(0); +} + ]])], + [ +if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null +then + AC_MSG_RESULT([no]) + LDFLAGS="$saved_LDFLAGS" +else + AC_MSG_RESULT([yes]) + LDFLAGS="$saved_LDFLAGS $_define_flag" +fi ], + [ AC_MSG_RESULT([no]) + LDFLAGS="$saved_LDFLAGS" ] + ) +}]) + +dnl OSSH_CHECK_HEADER_FOR_FIELD(field, header, symbol) +dnl Does AC_EGREP_HEADER on 'header' for the string 'field' +dnl If found, set 'symbol' to be defined. Cache the result. +dnl TODO: This is not foolproof, better to compile and read from there +AC_DEFUN([OSSH_CHECK_HEADER_FOR_FIELD], [ +# look for field '$1' in header '$2' + dnl This strips characters illegal to m4 from the header filename + ossh_safe=`echo "$2" | sed 'y%./+-%__p_%'` + dnl + ossh_varname="ossh_cv_$ossh_safe""_has_"$1 + AC_MSG_CHECKING(for $1 field in $2) + AC_CACHE_VAL($ossh_varname, [ + AC_EGREP_HEADER($1, $2, [ dnl + eval "$ossh_varname=yes" dnl + ], [ dnl + eval "$ossh_varname=no" dnl + ]) dnl + ]) + ossh_result=`eval 'echo $'"$ossh_varname"` + if test -n "`echo $ossh_varname`"; then + AC_MSG_RESULT($ossh_result) + if test "x$ossh_result" = "xyes"; then + AC_DEFINE($3, 1, [Define if you have $1 in $2]) + fi + else + AC_MSG_RESULT(no) + fi +]) + +dnl Check for socklen_t: historically on BSD it is an int, and in +dnl POSIX 1g it is a type of its own, but some platforms use different +dnl types for the argument to getsockopt, getpeername, etc. So we +dnl have to test to find something that will work. +AC_DEFUN([TYPE_SOCKLEN_T], +[ + AC_CHECK_TYPE([socklen_t], ,[ + AC_MSG_CHECKING([for socklen_t equivalent]) + AC_CACHE_VAL([curl_cv_socklen_t_equiv], + [ + # Systems have either "struct sockaddr *" or + # "void *" as the second argument to getpeername + curl_cv_socklen_t_equiv= + for arg2 in "struct sockaddr" void; do + for t in int size_t unsigned long "unsigned long"; do + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + #include + #include + int getpeername (int, $arg2 *, $t *); + ]], [[ + $t len; + getpeername(0,0,&len); + ]]) + ],[ + curl_cv_socklen_t_equiv="$t" + break + ]) + done + done + + if test "x$curl_cv_socklen_t_equiv" = x; then + AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) + fi + ]) + AC_MSG_RESULT($curl_cv_socklen_t_equiv) + AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv, + [type to use in place of socklen_t if not defined])], + [#include +#include ]) +]) + diff --git a/aosp/external/openssh/mac.c b/aosp/external/openssh/mac.c new file mode 100644 index 0000000000000000000000000000000000000000..f3dda66928668cc2df3544cd1b9f8d5915cc88b9 --- /dev/null +++ b/aosp/external/openssh/mac.c @@ -0,0 +1,262 @@ +/* $OpenBSD: mac.c,v 1.35 2019/09/06 04:53:27 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include + +#include "digest.h" +#include "hmac.h" +#include "umac.h" +#include "mac.h" +#include "misc.h" +#include "ssherr.h" +#include "sshbuf.h" + +#include "openbsd-compat/openssl-compat.h" + +#define SSH_DIGEST 1 /* SSH_DIGEST_XXX */ +#define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */ +#define SSH_UMAC128 3 + +struct macalg { + char *name; + int type; + int alg; + int truncatebits; /* truncate digest if != 0 */ + int key_len; /* just for UMAC */ + int len; /* just for UMAC */ + int etm; /* Encrypt-then-MAC */ +}; + +static const struct macalg macs[] = { + /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ + { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, + { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 }, + { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, + { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, + { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 }, + { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 }, + { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 }, + { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 }, + + /* Encrypt-then-MAC variants */ + { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, + { "hmac-sha1-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 1 }, + { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, + { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, + { "hmac-md5-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 1 }, + { "hmac-md5-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 }, + { "umac-64-etm@openssh.com", SSH_UMAC, 0, 0, 128, 64, 1 }, + { "umac-128-etm@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 1 }, + + { NULL, 0, 0, 0, 0, 0, 0 } +}; + +/* Returns a list of supported MACs separated by the specified char. */ +char * +mac_alg_list(char sep) +{ + char *ret = NULL, *tmp; + size_t nlen, rlen = 0; + const struct macalg *m; + + for (m = macs; m->name != NULL; m++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(m->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; + memcpy(ret + rlen, m->name, nlen + 1); + rlen += nlen; + } + return ret; +} + +static int +mac_setup_by_alg(struct sshmac *mac, const struct macalg *macalg) +{ + mac->type = macalg->type; + if (mac->type == SSH_DIGEST) { + if ((mac->hmac_ctx = ssh_hmac_start(macalg->alg)) == NULL) + return SSH_ERR_ALLOC_FAIL; + mac->key_len = mac->mac_len = ssh_hmac_bytes(macalg->alg); + } else { + mac->mac_len = macalg->len / 8; + mac->key_len = macalg->key_len / 8; + mac->umac_ctx = NULL; + } + if (macalg->truncatebits != 0) + mac->mac_len = macalg->truncatebits / 8; + mac->etm = macalg->etm; + return 0; +} + +int +mac_setup(struct sshmac *mac, char *name) +{ + const struct macalg *m; + + for (m = macs; m->name != NULL; m++) { + if (strcmp(name, m->name) != 0) + continue; + if (mac != NULL) + return mac_setup_by_alg(mac, m); + return 0; + } + return SSH_ERR_INVALID_ARGUMENT; +} + +int +mac_init(struct sshmac *mac) +{ + if (mac->key == NULL) + return SSH_ERR_INVALID_ARGUMENT; + switch (mac->type) { + case SSH_DIGEST: + if (mac->hmac_ctx == NULL || + ssh_hmac_init(mac->hmac_ctx, mac->key, mac->key_len) < 0) + return SSH_ERR_INVALID_ARGUMENT; + return 0; + case SSH_UMAC: + if ((mac->umac_ctx = umac_new(mac->key)) == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; + case SSH_UMAC128: + if ((mac->umac_ctx = umac128_new(mac->key)) == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; + default: + return SSH_ERR_INVALID_ARGUMENT; + } +} + +int +mac_compute(struct sshmac *mac, u_int32_t seqno, + const u_char *data, int datalen, + u_char *digest, size_t dlen) +{ + static union { + u_char m[SSH_DIGEST_MAX_LENGTH]; + u_int64_t for_align; + } u; + u_char b[4]; + u_char nonce[8]; + + if (mac->mac_len > sizeof(u)) + return SSH_ERR_INTERNAL_ERROR; + + switch (mac->type) { + case SSH_DIGEST: + put_u32(b, seqno); + /* reset HMAC context */ + if (ssh_hmac_init(mac->hmac_ctx, NULL, 0) < 0 || + ssh_hmac_update(mac->hmac_ctx, b, sizeof(b)) < 0 || + ssh_hmac_update(mac->hmac_ctx, data, datalen) < 0 || + ssh_hmac_final(mac->hmac_ctx, u.m, sizeof(u.m)) < 0) + return SSH_ERR_LIBCRYPTO_ERROR; + break; + case SSH_UMAC: + POKE_U64(nonce, seqno); + umac_update(mac->umac_ctx, data, datalen); + umac_final(mac->umac_ctx, u.m, nonce); + break; + case SSH_UMAC128: + put_u64(nonce, seqno); + umac128_update(mac->umac_ctx, data, datalen); + umac128_final(mac->umac_ctx, u.m, nonce); + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + if (digest != NULL) { + if (dlen > mac->mac_len) + dlen = mac->mac_len; + memcpy(digest, u.m, dlen); + } + return 0; +} + +int +mac_check(struct sshmac *mac, u_int32_t seqno, + const u_char *data, size_t dlen, + const u_char *theirmac, size_t mlen) +{ + u_char ourmac[SSH_DIGEST_MAX_LENGTH]; + int r; + + if (mac->mac_len > mlen) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = mac_compute(mac, seqno, data, dlen, + ourmac, sizeof(ourmac))) != 0) + return r; + if (timingsafe_bcmp(ourmac, theirmac, mac->mac_len) != 0) + return SSH_ERR_MAC_INVALID; + return 0; +} + +void +mac_clear(struct sshmac *mac) +{ + if (mac->type == SSH_UMAC) { + if (mac->umac_ctx != NULL) + umac_delete(mac->umac_ctx); + } else if (mac->type == SSH_UMAC128) { + if (mac->umac_ctx != NULL) + umac128_delete(mac->umac_ctx); + } else if (mac->hmac_ctx != NULL) + ssh_hmac_free(mac->hmac_ctx); + mac->hmac_ctx = NULL; + mac->umac_ctx = NULL; +} + +/* XXX copied from ciphers_valid */ +#define MAC_SEP "," +int +mac_valid(const char *names) +{ + char *maclist, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + if ((maclist = cp = strdup(names)) == NULL) + return 0; + for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0'; + (p = strsep(&cp, MAC_SEP))) { + if (mac_setup(NULL, p) < 0) { + free(maclist); + return 0; + } + } + free(maclist); + return 1; +} diff --git a/aosp/external/openssh/mac.h b/aosp/external/openssh/mac.h new file mode 100644 index 0000000000000000000000000000000000000000..0b119d7a1c3b0e524b356a9014b7f02a9a0422ea --- /dev/null +++ b/aosp/external/openssh/mac.h @@ -0,0 +1,53 @@ +/* $OpenBSD: mac.h,v 1.10 2016/07/08 03:44:42 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SSHMAC_H +#define SSHMAC_H + +#include + +struct sshmac { + char *name; + int enabled; + u_int mac_len; + u_char *key; + u_int key_len; + int type; + int etm; /* Encrypt-then-MAC */ + struct ssh_hmac_ctx *hmac_ctx; + struct umac_ctx *umac_ctx; +}; + +int mac_valid(const char *); +char *mac_alg_list(char); +int mac_setup(struct sshmac *, char *); +int mac_init(struct sshmac *); +int mac_compute(struct sshmac *, u_int32_t, const u_char *, int, + u_char *, size_t); +int mac_check(struct sshmac *, u_int32_t, const u_char *, size_t, + const u_char *, size_t); +void mac_clear(struct sshmac *); + +#endif /* SSHMAC_H */ diff --git a/aosp/external/openssh/match.c b/aosp/external/openssh/match.c new file mode 100644 index 0000000000000000000000000000000000000000..3ac854d38f0042867be47868469edb795e7ff836 --- /dev/null +++ b/aosp/external/openssh/match.c @@ -0,0 +1,364 @@ +/* $OpenBSD: match.c,v 1.43 2020/11/03 22:53:12 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Simple pattern matching, with '*' and '?' as wildcards. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "match.h" +#include "misc.h" + +/* + * Returns true if the given string matches the pattern (which may contain ? + * and * as wildcards), and zero if it does not match. + */ +int +match_pattern(const char *s, const char *pattern) +{ + for (;;) { + /* If at end of pattern, accept if also at end of string. */ + if (!*pattern) + return !*s; + + if (*pattern == '*') { + /* Skip this and any consecutive asterisks. */ + while (*pattern == '*') + pattern++; + + /* If at end of pattern, accept immediately. */ + if (!*pattern) + return 1; + + /* If next character in pattern is known, optimize. */ + if (*pattern != '?' && *pattern != '*') { + /* + * Look instances of the next character in + * pattern, and try to match starting from + * those. + */ + for (; *s; s++) + if (*s == *pattern && + match_pattern(s + 1, pattern + 1)) + return 1; + /* Failed. */ + return 0; + } + /* + * Move ahead one character at a time and try to + * match at each position. + */ + for (; *s; s++) + if (match_pattern(s, pattern)) + return 1; + /* Failed. */ + return 0; + } + /* + * There must be at least one more character in the string. + * If we are at the end, fail. + */ + if (!*s) + return 0; + + /* Check if the next character of the string is acceptable. */ + if (*pattern != '?' && *pattern != *s) + return 0; + + /* Move to the next character, both in string and in pattern. */ + s++; + pattern++; + } + /* NOTREACHED */ +} + +/* + * Tries to match the string against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns -1 if negation matches, 1 if there is + * a positive match, 0 if there is no match at all. + */ +int +match_pattern_list(const char *string, const char *pattern, int dolower) +{ + char sub[1024]; + int negated; + int got_positive; + u_int i, subi, len = strlen(pattern); + + got_positive = 0; + for (i = 0; i < len;) { + /* Check if the subpattern is negated. */ + if (pattern[i] == '!') { + negated = 1; + i++; + } else + negated = 0; + + /* + * Extract the subpattern up to a comma or end. Convert the + * subpattern to lowercase. + */ + for (subi = 0; + i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; + subi++, i++) + sub[subi] = dolower && isupper((u_char)pattern[i]) ? + tolower((u_char)pattern[i]) : pattern[i]; + /* If subpattern too long, return failure (no match). */ + if (subi >= sizeof(sub) - 1) + return 0; + + /* If the subpattern was terminated by a comma, then skip it. */ + if (i < len && pattern[i] == ',') + i++; + + /* Null-terminate the subpattern. */ + sub[subi] = '\0'; + + /* Try to match the subpattern against the string. */ + if (match_pattern(string, sub)) { + if (negated) + return -1; /* Negative */ + else + got_positive = 1; /* Positive */ + } + } + + /* + * Return success if got a positive match. If there was a negative + * match, we have already returned -1 and never get here. + */ + return got_positive; +} + +/* Match a list representing users or groups. */ +int +match_usergroup_pattern_list(const char *string, const char *pattern) +{ +#ifdef HAVE_CYGWIN + /* Windows usernames may be Unicode and are not case sensitive */ + return cygwin_ug_match_pattern_list(string, pattern); +#else + /* Case sensitive match */ + return match_pattern_list(string, pattern, 0); +#endif +} + +/* + * Tries to match the host name (which must be in all lowercase) against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns -1 if negation matches, 1 if there is + * a positive match, 0 if there is no match at all. + */ +int +match_hostname(const char *host, const char *pattern) +{ + char *hostcopy = xstrdup(host); + int r; + + lowercase(hostcopy); + r = match_pattern_list(hostcopy, pattern, 1); + free(hostcopy); + return r; +} + +/* + * returns 0 if we get a negative match for the hostname or the ip + * or if we get no match at all. returns -1 on error, or 1 on + * successful match. + */ +int +match_host_and_ip(const char *host, const char *ipaddr, + const char *patterns) +{ + int mhost, mip; + + if ((mip = addr_match_list(ipaddr, patterns)) == -2) + return -1; /* error in ipaddr match */ + else if (host == NULL || ipaddr == NULL || mip == -1) + return 0; /* negative ip address match, or testing pattern */ + + /* negative hostname match */ + if ((mhost = match_hostname(host, patterns)) == -1) + return 0; + /* no match at all */ + if (mhost == 0 && mip == 0) + return 0; + return 1; +} + +/* + * Match user, user@host_or_ip, user@host_or_ip_list against pattern. + * If user, host and ipaddr are all NULL then validate pattern/ + * Returns -1 on invalid pattern, 0 on no match, 1 on match. + */ +int +match_user(const char *user, const char *host, const char *ipaddr, + const char *pattern) +{ + char *p, *pat; + int ret; + + /* test mode */ + if (user == NULL && host == NULL && ipaddr == NULL) { + if ((p = strchr(pattern, '@')) != NULL && + match_host_and_ip(NULL, NULL, p + 1) < 0) + return -1; + return 0; + } + + if ((p = strchr(pattern, '@')) == NULL) + return match_pattern(user, pattern); + + pat = xstrdup(pattern); + p = strchr(pat, '@'); + *p++ = '\0'; + + if ((ret = match_pattern(user, pat)) == 1) + ret = match_host_and_ip(host, ipaddr, p); + free(pat); + + return ret; +} + +/* + * Returns first item from client-list that is also supported by server-list, + * caller must free the returned string. + */ +#define MAX_PROP 40 +#define SEP "," +char * +match_list(const char *client, const char *server, u_int *next) +{ + char *sproposals[MAX_PROP]; + char *c, *s, *p, *ret, *cp, *sp; + int i, j, nproposals; + + c = cp = xstrdup(client); + s = sp = xstrdup(server); + + for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; + (p = strsep(&sp, SEP)), i++) { + if (i < MAX_PROP) + sproposals[i] = p; + else + break; + } + nproposals = i; + + for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; + (p = strsep(&cp, SEP)), i++) { + for (j = 0; j < nproposals; j++) { + if (strcmp(p, sproposals[j]) == 0) { + ret = xstrdup(p); + if (next != NULL) + *next = (cp == NULL) ? + strlen(c) : (u_int)(cp - c); + free(c); + free(s); + return ret; + } + } + } + if (next != NULL) + *next = strlen(c); + free(c); + free(s); + return NULL; +} + +/* + * Filter proposal using pattern-list filter. + * "denylist" determines sense of filter: + * non-zero indicates that items matching filter should be excluded. + * zero indicates that only items matching filter should be included. + * returns NULL on allocation error, otherwise caller must free result. + */ +static char * +filter_list(const char *proposal, const char *filter, int denylist) +{ + size_t len = strlen(proposal) + 1; + char *fix_prop = malloc(len); + char *orig_prop = strdup(proposal); + char *cp, *tmp; + int r; + + if (fix_prop == NULL || orig_prop == NULL) { + free(orig_prop); + free(fix_prop); + return NULL; + } + + tmp = orig_prop; + *fix_prop = '\0'; + while ((cp = strsep(&tmp, ",")) != NULL) { + r = match_pattern_list(cp, filter, 0); + if ((denylist && r != 1) || (!denylist && r == 1)) { + if (*fix_prop != '\0') + strlcat(fix_prop, ",", len); + strlcat(fix_prop, cp, len); + } + } + free(orig_prop); + return fix_prop; +} + +/* + * Filters a comma-separated list of strings, excluding any entry matching + * the 'filter' pattern list. Caller must free returned string. + */ +char * +match_filter_denylist(const char *proposal, const char *filter) +{ + return filter_list(proposal, filter, 1); +} + +/* + * Filters a comma-separated list of strings, including only entries matching + * the 'filter' pattern list. Caller must free returned string. + */ +char * +match_filter_allowlist(const char *proposal, const char *filter) +{ + return filter_list(proposal, filter, 0); +} diff --git a/aosp/external/openssh/match.h b/aosp/external/openssh/match.h new file mode 100644 index 0000000000000000000000000000000000000000..312ca6e1679c27a06a8e15a8361e964436f02a05 --- /dev/null +++ b/aosp/external/openssh/match.h @@ -0,0 +1,30 @@ +/* $OpenBSD: match.h,v 1.20 2020/07/05 23:59:45 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +#ifndef MATCH_H +#define MATCH_H + +int match_pattern(const char *, const char *); +int match_pattern_list(const char *, const char *, int); +int match_usergroup_pattern_list(const char *, const char *); +int match_hostname(const char *, const char *); +int match_host_and_ip(const char *, const char *, const char *); +int match_user(const char *, const char *, const char *, const char *); +char *match_list(const char *, const char *, u_int *); +char *match_filter_denylist(const char *, const char *); +char *match_filter_allowlist(const char *, const char *); + +/* addrmatch.c */ +int addr_match_list(const char *, const char *); +int addr_match_cidr_list(const char *, const char *); +#endif diff --git a/aosp/external/openssh/mdoc2man.awk b/aosp/external/openssh/mdoc2man.awk new file mode 100644 index 0000000000000000000000000000000000000000..d393ae6f1476c7b743edc0d9efe94590da613d32 --- /dev/null +++ b/aosp/external/openssh/mdoc2man.awk @@ -0,0 +1,370 @@ +#!/usr/bin/awk +# +# Version history: +# v4+ Adapted for OpenSSH Portable (see cvs Id and history) +# v3, I put the program under a proper license +# Dan Nelson added .An, .Aq and fixed a typo +# v2, fixed to work on GNU awk --posix and MacOS X +# v1, first attempt, didn't work on MacOS X +# +# Copyright (c) 2003 Peter Stuge +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +BEGIN { + optlist=0 + oldoptlist=0 + nospace=0 + synopsis=0 + reference=0 + block=0 + ext=0 + extopt=0 + literal=0 + prenl=0 + breakw=0 + line="" +} + +function wtail() { + retval="" + while(w0;i--) { + add(refauthors[i]) + if(i>1) + add(", ") + } + if(nrefauthors>1) + add(" and ") + if(nrefauthors>0) + add(refauthors[0] ", ") + add("\\fI" reftitle "\\fP") + if(length(refissue)) + add(", " refissue) + if(length(refreport)) { + add(", " refreport) + } + if(length(refdate)) + add(", " refdate) + if(length(refopt)) + add(", " refopt) + add(".") + reference=0 + } else if(reference) { + if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } + if(match(words[w],"^%T$")) { + reftitle=wtail() + sub("^\"","",reftitle) + sub("\"$","",reftitle) + } + if(match(words[w],"^%N$")) { refissue=wtail() } + if(match(words[w],"^%D$")) { refdate=wtail() } + if(match(words[w],"^%O$")) { refopt=wtail() } + if(match(words[w],"^%R$")) { refreport=wtail() } + } else if(match(words[w],"^Nm$")) { + if(synopsis) { + add(".br") + prenl++ + } + n=words[++w] + if(!length(name)) + name=n + if(!length(n)) + n=name + add("\\fB" n "\\fP") + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Nd$")) { + add("\\- " wtail()) + } else if(match(words[w],"^Fl$")) { + add("\\fB\\-" words[++w] "\\fP") + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Ar$")) { + add("\\fI") + if(w==nwords) + add("file ...\\fP") + else { + add(words[++w] "\\fP") + while(match(words[w+1],"^\\|$")) + add(OFS words[++w] " \\fI" words[++w] "\\fP") + } + if(!nospace&&match(words[w+1],"^[\\.,]")) + nospace=1 + } else if(match(words[w],"^Cm$")) { + add("\\fB" words[++w] "\\fP") + while(w") + if(option) + add("]") + if(ext&&!extopt&&!match(line," $")) + add(OFS) + if(!ext&&!extopt&&length(line)) { + print line + prenl=0 + line="" + } +} diff --git a/aosp/external/openssh/misc.c b/aosp/external/openssh/misc.c new file mode 100644 index 0000000000000000000000000000000000000000..bdb36ff1a512fb578dcd246d9c0459baa4a7d2e0 --- /dev/null +++ b/aosp/external/openssh/misc.c @@ -0,0 +1,2800 @@ +/* $OpenBSD: misc.c,v 1.175 2022/03/20 08:51:21 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2005-2020 Damien Miller. All rights reserved. + * Copyright (c) 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_LIBGEN_H +# include +#endif +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#include +#include +#endif +#ifdef SSH_TUN_OPENBSD +#include +#endif + +#include "xmalloc.h" +#include "misc.h" +#include "log.h" +#include "ssh.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "platform.h" + +/* remove newline at end of string */ +char * +chop(char *s) +{ + char *t = s; + while (*t) { + if (*t == '\n' || *t == '\r') { + *t = '\0'; + return s; + } + t++; + } + return s; + +} + +/* remove whitespace from end of string */ +void +rtrim(char *s) +{ + size_t i; + + if ((i = strlen(s)) == 0) + return; + for (i--; i > 0; i--) { + if (isspace((int)s[i])) + s[i] = '\0'; + } +} + +/* set/unset filedescriptor to non-blocking */ +int +set_nonblock(int fd) +{ + int val; + + val = fcntl(fd, F_GETFL); + if (val == -1) { + error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); + return (-1); + } + if (val & O_NONBLOCK) { + debug3("fd %d is O_NONBLOCK", fd); + return (0); + } + debug2("fd %d setting O_NONBLOCK", fd); + val |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, val) == -1) { + debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, + strerror(errno)); + return (-1); + } + return (0); +} + +int +unset_nonblock(int fd) +{ + int val; + + val = fcntl(fd, F_GETFL); + if (val == -1) { + error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); + return (-1); + } + if (!(val & O_NONBLOCK)) { + debug3("fd %d is not O_NONBLOCK", fd); + return (0); + } + debug("fd %d clearing O_NONBLOCK", fd); + val &= ~O_NONBLOCK; + if (fcntl(fd, F_SETFL, val) == -1) { + debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", + fd, strerror(errno)); + return (-1); + } + return (0); +} + +const char * +ssh_gai_strerror(int gaierr) +{ + if (gaierr == EAI_SYSTEM && errno != 0) + return strerror(errno); + return gai_strerror(gaierr); +} + +/* disable nagle on socket */ +void +set_nodelay(int fd) +{ + int opt; + socklen_t optlen; + + optlen = sizeof opt; + if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { + debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); + return; + } + if (opt == 1) { + debug2("fd %d is TCP_NODELAY", fd); + return; + } + opt = 1; + debug2("fd %d setting TCP_NODELAY", fd); + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) + error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); +} + +/* Allow local port reuse in TIME_WAIT */ +int +set_reuseaddr(int fd) +{ + int on = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); + return -1; + } + return 0; +} + +/* Get/set routing domain */ +char * +get_rdomain(int fd) +{ +#if defined(HAVE_SYS_GET_RDOMAIN) + return sys_get_rdomain(fd); +#elif defined(__OpenBSD__) + int rtable; + char *ret; + socklen_t len = sizeof(rtable); + + if (getsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, &len) == -1) { + error("Failed to get routing domain for fd %d: %s", + fd, strerror(errno)); + return NULL; + } + xasprintf(&ret, "%d", rtable); + return ret; +#else /* defined(__OpenBSD__) */ + return NULL; +#endif +} + +int +set_rdomain(int fd, const char *name) +{ +#if defined(HAVE_SYS_SET_RDOMAIN) + return sys_set_rdomain(fd, name); +#elif defined(__OpenBSD__) + int rtable; + const char *errstr; + + if (name == NULL) + return 0; /* default table */ + + rtable = (int)strtonum(name, 0, 255, &errstr); + if (errstr != NULL) { + /* Shouldn't happen */ + error("Invalid routing domain \"%s\": %s", name, errstr); + return -1; + } + if (setsockopt(fd, SOL_SOCKET, SO_RTABLE, + &rtable, sizeof(rtable)) == -1) { + error("Failed to set routing domain %d on fd %d: %s", + rtable, fd, strerror(errno)); + return -1; + } + return 0; +#else /* defined(__OpenBSD__) */ + error("Setting routing domain is not supported on this platform"); + return -1; +#endif +} + +int +get_sock_af(int fd) +{ + struct sockaddr_storage to; + socklen_t tolen = sizeof(to); + + memset(&to, 0, sizeof(to)); + if (getsockname(fd, (struct sockaddr *)&to, &tolen) == -1) + return -1; +#ifdef IPV4_IN_IPV6 + if (to.ss_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&to)->sin6_addr)) + return AF_INET; +#endif + return to.ss_family; +} + +void +set_sock_tos(int fd, int tos) +{ +#ifndef IP_TOS_IS_BROKEN + int af; + + switch ((af = get_sock_af(fd))) { + case -1: + /* assume not a socket */ + break; + case AF_INET: +# ifdef IP_TOS + debug3_f("set socket %d IP_TOS 0x%02x", fd, tos); + if (setsockopt(fd, IPPROTO_IP, IP_TOS, + &tos, sizeof(tos)) == -1) { + error("setsockopt socket %d IP_TOS %d: %s:", + fd, tos, strerror(errno)); + } +# endif /* IP_TOS */ + break; + case AF_INET6: +# ifdef IPV6_TCLASS + debug3_f("set socket %d IPV6_TCLASS 0x%02x", fd, tos); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, + &tos, sizeof(tos)) == -1) { + error("setsockopt socket %d IPV6_TCLASS %d: %.100s:", + fd, tos, strerror(errno)); + } +# endif /* IPV6_TCLASS */ + break; + default: + debug2_f("unsupported socket family %d", af); + break; + } +#endif /* IP_TOS_IS_BROKEN */ +} + +/* + * Wait up to *timeoutp milliseconds for events on fd. Updates + * *timeoutp with time remaining. + * Returns 0 if fd ready or -1 on timeout or error (see errno). + */ +static int +waitfd(int fd, int *timeoutp, short events) +{ + struct pollfd pfd; + struct timeval t_start; + int oerrno, r; + + pfd.fd = fd; + pfd.events = events; + for (; *timeoutp >= 0;) { + monotime_tv(&t_start); + r = poll(&pfd, 1, *timeoutp); + oerrno = errno; + ms_subtract_diff(&t_start, timeoutp); + errno = oerrno; + if (r > 0) + return 0; + else if (r == -1 && errno != EAGAIN && errno != EINTR) + return -1; + else if (r == 0) + break; + } + /* timeout */ + errno = ETIMEDOUT; + return -1; +} + +/* + * Wait up to *timeoutp milliseconds for fd to be readable. Updates + * *timeoutp with time remaining. + * Returns 0 if fd ready or -1 on timeout or error (see errno). + */ +int +waitrfd(int fd, int *timeoutp) { + return waitfd(fd, timeoutp, POLLIN); +} + +/* + * Attempt a non-blocking connect(2) to the specified address, waiting up to + * *timeoutp milliseconds for the connection to complete. If the timeout is + * <=0, then wait indefinitely. + * + * Returns 0 on success or -1 on failure. + */ +int +timeout_connect(int sockfd, const struct sockaddr *serv_addr, + socklen_t addrlen, int *timeoutp) +{ + int optval = 0; + socklen_t optlen = sizeof(optval); + + /* No timeout: just do a blocking connect() */ + if (timeoutp == NULL || *timeoutp <= 0) + return connect(sockfd, serv_addr, addrlen); + + set_nonblock(sockfd); + for (;;) { + if (connect(sockfd, serv_addr, addrlen) == 0) { + /* Succeeded already? */ + unset_nonblock(sockfd); + return 0; + } else if (errno == EINTR) + continue; + else if (errno != EINPROGRESS) + return -1; + break; + } + + if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT) == -1) + return -1; + + /* Completed or failed */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { + debug("getsockopt: %s", strerror(errno)); + return -1; + } + if (optval != 0) { + errno = optval; + return -1; + } + unset_nonblock(sockfd); + return 0; +} + +/* Characters considered whitespace in strsep calls. */ +#define WHITESPACE " \t\r\n" +#define QUOTE "\"" + +/* return next token in configuration line */ +static char * +strdelim_internal(char **s, int split_equals) +{ + char *old; + int wspace = 0; + + if (*s == NULL) + return NULL; + + old = *s; + + *s = strpbrk(*s, + split_equals ? WHITESPACE QUOTE "=" : WHITESPACE QUOTE); + if (*s == NULL) + return (old); + + if (*s[0] == '\"') { + memmove(*s, *s + 1, strlen(*s)); /* move nul too */ + /* Find matching quote */ + if ((*s = strpbrk(*s, QUOTE)) == NULL) { + return (NULL); /* no matching quote */ + } else { + *s[0] = '\0'; + *s += strspn(*s + 1, WHITESPACE) + 1; + return (old); + } + } + + /* Allow only one '=' to be skipped */ + if (split_equals && *s[0] == '=') + wspace = 1; + *s[0] = '\0'; + + /* Skip any extra whitespace after first token */ + *s += strspn(*s + 1, WHITESPACE) + 1; + if (split_equals && *s[0] == '=' && !wspace) + *s += strspn(*s + 1, WHITESPACE) + 1; + + return (old); +} + +/* + * Return next token in configuration line; splts on whitespace or a + * single '=' character. + */ +char * +strdelim(char **s) +{ + return strdelim_internal(s, 1); +} + +/* + * Return next token in configuration line; splts on whitespace only. + */ +char * +strdelimw(char **s) +{ + return strdelim_internal(s, 0); +} + +struct passwd * +pwcopy(struct passwd *pw) +{ + struct passwd *copy = xcalloc(1, sizeof(*copy)); + + copy->pw_name = xstrdup(pw->pw_name); + copy->pw_passwd = xstrdup(pw->pw_passwd == NULL ? "*" : pw->pw_passwd); +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + if (pw->pw_gecos) copy->pw_gecos = xstrdup(pw->pw_gecos); +#endif + copy->pw_uid = pw->pw_uid; + copy->pw_gid = pw->pw_gid; +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + copy->pw_expire = pw->pw_expire; +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + copy->pw_change = pw->pw_change; +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + if (pw->pw_class) copy->pw_class = xstrdup(pw->pw_class); +#endif + if (pw->pw_dir) copy->pw_dir = xstrdup(pw->pw_dir); + if (pw->pw_shell) copy->pw_shell = xstrdup(pw->pw_shell); + return copy; +} + +/* + * Convert ASCII string to TCP/IP port number. + * Port must be >=0 and <=65535. + * Return -1 if invalid. + */ +int +a2port(const char *s) +{ + struct servent *se; + long long port; + const char *errstr; + + port = strtonum(s, 0, 65535, &errstr); + if (errstr == NULL) + return (int)port; + if ((se = getservbyname(s, "tcp")) != NULL) + return ntohs(se->s_port); + return -1; +} + +int +a2tun(const char *s, int *remote) +{ + const char *errstr = NULL; + char *sp, *ep; + int tun; + + if (remote != NULL) { + *remote = SSH_TUNID_ANY; + sp = xstrdup(s); + if ((ep = strchr(sp, ':')) == NULL) { + free(sp); + return (a2tun(s, NULL)); + } + ep[0] = '\0'; ep++; + *remote = a2tun(ep, NULL); + tun = a2tun(sp, NULL); + free(sp); + return (*remote == SSH_TUNID_ERR ? *remote : tun); + } + + if (strcasecmp(s, "any") == 0) + return (SSH_TUNID_ANY); + + tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); + if (errstr != NULL) + return (SSH_TUNID_ERR); + + return (tun); +} + +#define SECONDS 1 +#define MINUTES (SECONDS * 60) +#define HOURS (MINUTES * 60) +#define DAYS (HOURS * 24) +#define WEEKS (DAYS * 7) + +/* + * Convert a time string into seconds; format is + * a sequence of: + * time[qualifier] + * + * Valid time qualifiers are: + * seconds + * s|S seconds + * m|M minutes + * h|H hours + * d|D days + * w|W weeks + * + * Examples: + * 90m 90 minutes + * 1h30m 90 minutes + * 2d 2 days + * 1w 1 week + * + * Return -1 if time string is invalid. + */ +int +convtime(const char *s) +{ + long total, secs, multiplier; + const char *p; + char *endp; + + errno = 0; + total = 0; + p = s; + + if (p == NULL || *p == '\0') + return -1; + + while (*p) { + secs = strtol(p, &endp, 10); + if (p == endp || + (errno == ERANGE && (secs == INT_MIN || secs == INT_MAX)) || + secs < 0) + return -1; + + multiplier = 1; + switch (*endp++) { + case '\0': + endp--; + break; + case 's': + case 'S': + break; + case 'm': + case 'M': + multiplier = MINUTES; + break; + case 'h': + case 'H': + multiplier = HOURS; + break; + case 'd': + case 'D': + multiplier = DAYS; + break; + case 'w': + case 'W': + multiplier = WEEKS; + break; + default: + return -1; + } + if (secs > INT_MAX / multiplier) + return -1; + secs *= multiplier; + if (total > INT_MAX - secs) + return -1; + total += secs; + if (total < 0) + return -1; + p = endp; + } + + return total; +} + +#define TF_BUFS 8 +#define TF_LEN 9 + +const char * +fmt_timeframe(time_t t) +{ + char *buf; + static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ + static int idx = 0; + unsigned int sec, min, hrs, day; + unsigned long long week; + + buf = tfbuf[idx++]; + if (idx == TF_BUFS) + idx = 0; + + week = t; + + sec = week % 60; + week /= 60; + min = week % 60; + week /= 60; + hrs = week % 24; + week /= 24; + day = week % 7; + week /= 7; + + if (week > 0) + snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); + else if (day > 0) + snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); + else + snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); + + return (buf); +} + +/* + * Returns a standardized host+port identifier string. + * Caller must free returned string. + */ +char * +put_host_port(const char *host, u_short port) +{ + char *hoststr; + + if (port == 0 || port == SSH_DEFAULT_PORT) + return(xstrdup(host)); + if (asprintf(&hoststr, "[%s]:%d", host, (int)port) == -1) + fatal("put_host_port: asprintf: %s", strerror(errno)); + debug3("put_host_port: %s", hoststr); + return hoststr; +} + +/* + * Search for next delimiter between hostnames/addresses and ports. + * Argument may be modified (for termination). + * Returns *cp if parsing succeeds. + * *cp is set to the start of the next field, if one was found. + * The delimiter char, if present, is stored in delim. + * If this is the last field, *cp is set to NULL. + */ +char * +hpdelim2(char **cp, char *delim) +{ + char *s, *old; + + if (cp == NULL || *cp == NULL) + return NULL; + + old = s = *cp; + if (*s == '[') { + if ((s = strchr(s, ']')) == NULL) + return NULL; + else + s++; + } else if ((s = strpbrk(s, ":/")) == NULL) + s = *cp + strlen(*cp); /* skip to end (see first case below) */ + + switch (*s) { + case '\0': + *cp = NULL; /* no more fields*/ + break; + + case ':': + case '/': + if (delim != NULL) + *delim = *s; + *s = '\0'; /* terminate */ + *cp = s + 1; + break; + + default: + return NULL; + } + + return old; +} + +/* The common case: only accept colon as delimiter. */ +char * +hpdelim(char **cp) +{ + char *r, delim = '\0'; + + r = hpdelim2(cp, &delim); + if (delim == '/') + return NULL; + return r; +} + +char * +cleanhostname(char *host) +{ + if (*host == '[' && host[strlen(host) - 1] == ']') { + host[strlen(host) - 1] = '\0'; + return (host + 1); + } else + return host; +} + +char * +colon(char *cp) +{ + int flag = 0; + + if (*cp == ':') /* Leading colon is part of file name. */ + return NULL; + if (*cp == '[') + flag = 1; + + for (; *cp; ++cp) { + if (*cp == '@' && *(cp+1) == '[') + flag = 1; + if (*cp == ']' && *(cp+1) == ':' && flag) + return (cp+1); + if (*cp == ':' && !flag) + return (cp); + if (*cp == '/') + return NULL; + } + return NULL; +} + +/* + * Parse a [user@]host:[path] string. + * Caller must free returned user, host and path. + * Any of the pointer return arguments may be NULL (useful for syntax checking). + * If user was not specified then *userp will be set to NULL. + * If host was not specified then *hostp will be set to NULL. + * If path was not specified then *pathp will be set to ".". + * Returns 0 on success, -1 on failure. + */ +int +parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp) +{ + char *user = NULL, *host = NULL, *path = NULL; + char *sdup, *tmp; + int ret = -1; + + if (userp != NULL) + *userp = NULL; + if (hostp != NULL) + *hostp = NULL; + if (pathp != NULL) + *pathp = NULL; + + sdup = xstrdup(s); + + /* Check for remote syntax: [user@]host:[path] */ + if ((tmp = colon(sdup)) == NULL) + goto out; + + /* Extract optional path */ + *tmp++ = '\0'; + if (*tmp == '\0') + tmp = "."; + path = xstrdup(tmp); + + /* Extract optional user and mandatory host */ + tmp = strrchr(sdup, '@'); + if (tmp != NULL) { + *tmp++ = '\0'; + host = xstrdup(cleanhostname(tmp)); + if (*sdup != '\0') + user = xstrdup(sdup); + } else { + host = xstrdup(cleanhostname(sdup)); + user = NULL; + } + + /* Success */ + if (userp != NULL) { + *userp = user; + user = NULL; + } + if (hostp != NULL) { + *hostp = host; + host = NULL; + } + if (pathp != NULL) { + *pathp = path; + path = NULL; + } + ret = 0; +out: + free(sdup); + free(user); + free(host); + free(path); + return ret; +} + +/* + * Parse a [user@]host[:port] string. + * Caller must free returned user and host. + * Any of the pointer return arguments may be NULL (useful for syntax checking). + * If user was not specified then *userp will be set to NULL. + * If port was not specified then *portp will be -1. + * Returns 0 on success, -1 on failure. + */ +int +parse_user_host_port(const char *s, char **userp, char **hostp, int *portp) +{ + char *sdup, *cp, *tmp; + char *user = NULL, *host = NULL; + int port = -1, ret = -1; + + if (userp != NULL) + *userp = NULL; + if (hostp != NULL) + *hostp = NULL; + if (portp != NULL) + *portp = -1; + + if ((sdup = tmp = strdup(s)) == NULL) + return -1; + /* Extract optional username */ + if ((cp = strrchr(tmp, '@')) != NULL) { + *cp = '\0'; + if (*tmp == '\0') + goto out; + if ((user = strdup(tmp)) == NULL) + goto out; + tmp = cp + 1; + } + /* Extract mandatory hostname */ + if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0') + goto out; + host = xstrdup(cleanhostname(cp)); + /* Convert and verify optional port */ + if (tmp != NULL && *tmp != '\0') { + if ((port = a2port(tmp)) <= 0) + goto out; + } + /* Success */ + if (userp != NULL) { + *userp = user; + user = NULL; + } + if (hostp != NULL) { + *hostp = host; + host = NULL; + } + if (portp != NULL) + *portp = port; + ret = 0; + out: + free(sdup); + free(user); + free(host); + return ret; +} + +/* + * Converts a two-byte hex string to decimal. + * Returns the decimal value or -1 for invalid input. + */ +static int +hexchar(const char *s) +{ + unsigned char result[2]; + int i; + + for (i = 0; i < 2; i++) { + if (s[i] >= '0' && s[i] <= '9') + result[i] = (unsigned char)(s[i] - '0'); + else if (s[i] >= 'a' && s[i] <= 'f') + result[i] = (unsigned char)(s[i] - 'a') + 10; + else if (s[i] >= 'A' && s[i] <= 'F') + result[i] = (unsigned char)(s[i] - 'A') + 10; + else + return -1; + } + return (result[0] << 4) | result[1]; +} + +/* + * Decode an url-encoded string. + * Returns a newly allocated string on success or NULL on failure. + */ +static char * +urldecode(const char *src) +{ + char *ret, *dst; + int ch; + + ret = xmalloc(strlen(src) + 1); + for (dst = ret; *src != '\0'; src++) { + switch (*src) { + case '+': + *dst++ = ' '; + break; + case '%': + if (!isxdigit((unsigned char)src[1]) || + !isxdigit((unsigned char)src[2]) || + (ch = hexchar(src + 1)) == -1) { + free(ret); + return NULL; + } + *dst++ = ch; + src += 2; + break; + default: + *dst++ = *src; + break; + } + } + *dst = '\0'; + + return ret; +} + +/* + * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI. + * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04 + * Either user or path may be url-encoded (but not host or port). + * Caller must free returned user, host and path. + * Any of the pointer return arguments may be NULL (useful for syntax checking) + * but the scheme must always be specified. + * If user was not specified then *userp will be set to NULL. + * If port was not specified then *portp will be -1. + * If path was not specified then *pathp will be set to NULL. + * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri. + */ +int +parse_uri(const char *scheme, const char *uri, char **userp, char **hostp, + int *portp, char **pathp) +{ + char *uridup, *cp, *tmp, ch; + char *user = NULL, *host = NULL, *path = NULL; + int port = -1, ret = -1; + size_t len; + + len = strlen(scheme); + if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0) + return 1; + uri += len + 3; + + if (userp != NULL) + *userp = NULL; + if (hostp != NULL) + *hostp = NULL; + if (portp != NULL) + *portp = -1; + if (pathp != NULL) + *pathp = NULL; + + uridup = tmp = xstrdup(uri); + + /* Extract optional ssh-info (username + connection params) */ + if ((cp = strchr(tmp, '@')) != NULL) { + char *delim; + + *cp = '\0'; + /* Extract username and connection params */ + if ((delim = strchr(tmp, ';')) != NULL) { + /* Just ignore connection params for now */ + *delim = '\0'; + } + if (*tmp == '\0') { + /* Empty username */ + goto out; + } + if ((user = urldecode(tmp)) == NULL) + goto out; + tmp = cp + 1; + } + + /* Extract mandatory hostname */ + if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0') + goto out; + host = xstrdup(cleanhostname(cp)); + if (!valid_domain(host, 0, NULL)) + goto out; + + if (tmp != NULL && *tmp != '\0') { + if (ch == ':') { + /* Convert and verify port. */ + if ((cp = strchr(tmp, '/')) != NULL) + *cp = '\0'; + if ((port = a2port(tmp)) <= 0) + goto out; + tmp = cp ? cp + 1 : NULL; + } + if (tmp != NULL && *tmp != '\0') { + /* Extract optional path */ + if ((path = urldecode(tmp)) == NULL) + goto out; + } + } + + /* Success */ + if (userp != NULL) { + *userp = user; + user = NULL; + } + if (hostp != NULL) { + *hostp = host; + host = NULL; + } + if (portp != NULL) + *portp = port; + if (pathp != NULL) { + *pathp = path; + path = NULL; + } + ret = 0; + out: + free(uridup); + free(user); + free(host); + free(path); + return ret; +} + +/* function to assist building execv() arguments */ +void +addargs(arglist *args, char *fmt, ...) +{ + va_list ap; + char *cp; + u_int nalloc; + int r; + + va_start(ap, fmt); + r = vasprintf(&cp, fmt, ap); + va_end(ap); + if (r == -1) + fatal_f("argument too long"); + + nalloc = args->nalloc; + if (args->list == NULL) { + nalloc = 32; + args->num = 0; + } else if (args->num > (256 * 1024)) + fatal_f("too many arguments"); + else if (args->num >= args->nalloc) + fatal_f("arglist corrupt"); + else if (args->num+2 >= nalloc) + nalloc *= 2; + + args->list = xrecallocarray(args->list, args->nalloc, + nalloc, sizeof(char *)); + args->nalloc = nalloc; + args->list[args->num++] = cp; + args->list[args->num] = NULL; +} + +void +replacearg(arglist *args, u_int which, char *fmt, ...) +{ + va_list ap; + char *cp; + int r; + + va_start(ap, fmt); + r = vasprintf(&cp, fmt, ap); + va_end(ap); + if (r == -1) + fatal_f("argument too long"); + if (args->list == NULL || args->num >= args->nalloc) + fatal_f("arglist corrupt"); + + if (which >= args->num) + fatal_f("tried to replace invalid arg %d >= %d", + which, args->num); + free(args->list[which]); + args->list[which] = cp; +} + +void +freeargs(arglist *args) +{ + u_int i; + + if (args == NULL) + return; + if (args->list != NULL && args->num < args->nalloc) { + for (i = 0; i < args->num; i++) + free(args->list[i]); + free(args->list); + } + args->nalloc = args->num = 0; + args->list = NULL; +} + +/* + * Expands tildes in the file name. Returns data allocated by xmalloc. + * Warning: this calls getpw*. + */ +int +tilde_expand(const char *filename, uid_t uid, char **retp) +{ + char *ocopy = NULL, *copy, *s = NULL; + const char *path = NULL, *user = NULL; + struct passwd *pw; + size_t len; + int ret = -1, r, slash; + + *retp = NULL; + if (*filename != '~') { + *retp = xstrdup(filename); + return 0; + } + ocopy = copy = xstrdup(filename + 1); + + if (*copy == '\0') /* ~ */ + path = NULL; + else if (*copy == '/') { + copy += strspn(copy, "/"); + if (*copy == '\0') + path = NULL; /* ~/ */ + else + path = copy; /* ~/path */ + } else { + user = copy; + if ((path = strchr(copy, '/')) != NULL) { + copy[path - copy] = '\0'; + path++; + path += strspn(path, "/"); + if (*path == '\0') /* ~user/ */ + path = NULL; + /* else ~user/path */ + } + /* else ~user */ + } + if (user != NULL) { + if ((pw = getpwnam(user)) == NULL) { + error_f("No such user %s", user); + goto out; + } + } else if ((pw = getpwuid(uid)) == NULL) { + error_f("No such uid %ld", (long)uid); + goto out; + } + + /* Make sure directory has a trailing '/' */ + slash = (len = strlen(pw->pw_dir)) == 0 || pw->pw_dir[len - 1] != '/'; + + if ((r = xasprintf(&s, "%s%s%s", pw->pw_dir, + slash ? "/" : "", path != NULL ? path : "")) <= 0) { + error_f("xasprintf failed"); + goto out; + } + if (r >= PATH_MAX) { + error_f("Path too long"); + goto out; + } + /* success */ + ret = 0; + *retp = s; + s = NULL; + out: + free(s); + free(ocopy); + return ret; +} + +char * +tilde_expand_filename(const char *filename, uid_t uid) +{ + char *ret; + + if (tilde_expand(filename, uid, &ret) != 0) + cleanup_exit(255); + return ret; +} + +/* + * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT} + * substitutions. A number of escapes may be specified as + * (char *escape_chars, char *replacement) pairs. The list must be terminated + * by a NULL escape_char. Returns replaced string in memory allocated by + * xmalloc which the caller must free. + */ +static char * +vdollar_percent_expand(int *parseerror, int dollar, int percent, + const char *string, va_list ap) +{ +#define EXPAND_MAX_KEYS 16 + u_int num_keys = 0, i; + struct { + const char *key; + const char *repl; + } keys[EXPAND_MAX_KEYS]; + struct sshbuf *buf; + int r, missingvar = 0; + char *ret = NULL, *var, *varend, *val; + size_t len; + + if ((buf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (parseerror == NULL) + fatal_f("null parseerror arg"); + *parseerror = 1; + + /* Gather keys if we're doing percent expansion. */ + if (percent) { + for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { + keys[num_keys].key = va_arg(ap, char *); + if (keys[num_keys].key == NULL) + break; + keys[num_keys].repl = va_arg(ap, char *); + if (keys[num_keys].repl == NULL) { + fatal_f("NULL replacement for token %s", + keys[num_keys].key); + } + } + if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) + fatal_f("too many keys"); + if (num_keys == 0) + fatal_f("percent expansion without token list"); + } + + /* Expand string */ + for (i = 0; *string != '\0'; string++) { + /* Optionally process ${ENVIRONMENT} expansions. */ + if (dollar && string[0] == '$' && string[1] == '{') { + string += 2; /* skip over '${' */ + if ((varend = strchr(string, '}')) == NULL) { + error_f("environment variable '%s' missing " + "closing '}'", string); + goto out; + } + len = varend - string; + if (len == 0) { + error_f("zero-length environment variable"); + goto out; + } + var = xmalloc(len + 1); + (void)strlcpy(var, string, len + 1); + if ((val = getenv(var)) == NULL) { + error_f("env var ${%s} has no value", var); + missingvar = 1; + } else { + debug3_f("expand ${%s} -> '%s'", var, val); + if ((r = sshbuf_put(buf, val, strlen(val))) !=0) + fatal_fr(r, "sshbuf_put ${}"); + } + free(var); + string += len; + continue; + } + + /* + * Process percent expansions if we have a list of TOKENs. + * If we're not doing percent expansion everything just gets + * appended here. + */ + if (*string != '%' || !percent) { + append: + if ((r = sshbuf_put_u8(buf, *string)) != 0) + fatal_fr(r, "sshbuf_put_u8 %%"); + continue; + } + string++; + /* %% case */ + if (*string == '%') + goto append; + if (*string == '\0') { + error_f("invalid format"); + goto out; + } + for (i = 0; i < num_keys; i++) { + if (strchr(keys[i].key, *string) != NULL) { + if ((r = sshbuf_put(buf, keys[i].repl, + strlen(keys[i].repl))) != 0) + fatal_fr(r, "sshbuf_put %%-repl"); + break; + } + } + if (i >= num_keys) { + error_f("unknown key %%%c", *string); + goto out; + } + } + if (!missingvar && (ret = sshbuf_dup_string(buf)) == NULL) + fatal_f("sshbuf_dup_string failed"); + *parseerror = 0; + out: + sshbuf_free(buf); + return *parseerror ? NULL : ret; +#undef EXPAND_MAX_KEYS +} + +/* + * Expand only environment variables. + * Note that although this function is variadic like the other similar + * functions, any such arguments will be unused. + */ + +char * +dollar_expand(int *parseerr, const char *string, ...) +{ + char *ret; + int err; + va_list ap; + + va_start(ap, string); + ret = vdollar_percent_expand(&err, 1, 0, string, ap); + va_end(ap); + if (parseerr != NULL) + *parseerr = err; + return ret; +} + +/* + * Returns expanded string or NULL if a specified environment variable is + * not defined, or calls fatal if the string is invalid. + */ +char * +percent_expand(const char *string, ...) +{ + char *ret; + int err; + va_list ap; + + va_start(ap, string); + ret = vdollar_percent_expand(&err, 0, 1, string, ap); + va_end(ap); + if (err) + fatal_f("failed"); + return ret; +} + +/* + * Returns expanded string or NULL if a specified environment variable is + * not defined, or calls fatal if the string is invalid. + */ +char * +percent_dollar_expand(const char *string, ...) +{ + char *ret; + int err; + va_list ap; + + va_start(ap, string); + ret = vdollar_percent_expand(&err, 1, 1, string, ap); + va_end(ap); + if (err) + fatal_f("failed"); + return ret; +} + +int +tun_open(int tun, int mode, char **ifname) +{ +#if defined(CUSTOM_SYS_TUN_OPEN) + return (sys_tun_open(tun, mode, ifname)); +#elif defined(SSH_TUN_OPENBSD) + struct ifreq ifr; + char name[100]; + int fd = -1, sock; + const char *tunbase = "tun"; + + if (ifname != NULL) + *ifname = NULL; + + if (mode == SSH_TUNMODE_ETHERNET) + tunbase = "tap"; + + /* Open the tunnel device */ + if (tun <= SSH_TUNID_MAX) { + snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); + fd = open(name, O_RDWR); + } else if (tun == SSH_TUNID_ANY) { + for (tun = 100; tun >= 0; tun--) { + snprintf(name, sizeof(name), "/dev/%s%d", + tunbase, tun); + if ((fd = open(name, O_RDWR)) >= 0) + break; + } + } else { + debug_f("invalid tunnel %u", tun); + return -1; + } + + if (fd == -1) { + debug_f("%s open: %s", name, strerror(errno)); + return -1; + } + + debug_f("%s mode %d fd %d", name, mode, fd); + + /* Bring interface up if it is not already */ + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); + if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + goto failed; + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { + debug_f("get interface %s flags: %s", ifr.ifr_name, + strerror(errno)); + goto failed; + } + + if (!(ifr.ifr_flags & IFF_UP)) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) { + debug_f("activate interface %s: %s", ifr.ifr_name, + strerror(errno)); + goto failed; + } + } + + if (ifname != NULL) + *ifname = xstrdup(ifr.ifr_name); + + close(sock); + return fd; + + failed: + if (fd >= 0) + close(fd); + if (sock >= 0) + close(sock); + return -1; +#else + error("Tunnel interfaces are not supported on this platform"); + return (-1); +#endif +} + +void +sanitise_stdfd(void) +{ + int nullfd, dupfd; + + if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { + fprintf(stderr, "Couldn't open /dev/null: %s\n", + strerror(errno)); + exit(1); + } + while (++dupfd <= STDERR_FILENO) { + /* Only populate closed fds. */ + if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) { + if (dup2(nullfd, dupfd) == -1) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + exit(1); + } + } + } + if (nullfd > STDERR_FILENO) + close(nullfd); +} + +char * +tohex(const void *vp, size_t l) +{ + const u_char *p = (const u_char *)vp; + char b[3], *r; + size_t i, hl; + + if (l > 65536) + return xstrdup("tohex: length > 65536"); + + hl = l * 2 + 1; + r = xcalloc(1, hl); + for (i = 0; i < l; i++) { + snprintf(b, sizeof(b), "%02x", p[i]); + strlcat(r, b, hl); + } + return (r); +} + +/* + * Extend string *sp by the specified format. If *sp is not NULL (or empty), + * then the separator 'sep' will be prepended before the formatted arguments. + * Extended strings are heap allocated. + */ +void +xextendf(char **sp, const char *sep, const char *fmt, ...) +{ + va_list ap; + char *tmp1, *tmp2; + + va_start(ap, fmt); + xvasprintf(&tmp1, fmt, ap); + va_end(ap); + + if (*sp == NULL || **sp == '\0') { + free(*sp); + *sp = tmp1; + return; + } + xasprintf(&tmp2, "%s%s%s", *sp, sep == NULL ? "" : sep, tmp1); + free(tmp1); + free(*sp); + *sp = tmp2; +} + + +u_int64_t +get_u64(const void *vp) +{ + const u_char *p = (const u_char *)vp; + u_int64_t v; + + v = (u_int64_t)p[0] << 56; + v |= (u_int64_t)p[1] << 48; + v |= (u_int64_t)p[2] << 40; + v |= (u_int64_t)p[3] << 32; + v |= (u_int64_t)p[4] << 24; + v |= (u_int64_t)p[5] << 16; + v |= (u_int64_t)p[6] << 8; + v |= (u_int64_t)p[7]; + + return (v); +} + +u_int32_t +get_u32(const void *vp) +{ + const u_char *p = (const u_char *)vp; + u_int32_t v; + + v = (u_int32_t)p[0] << 24; + v |= (u_int32_t)p[1] << 16; + v |= (u_int32_t)p[2] << 8; + v |= (u_int32_t)p[3]; + + return (v); +} + +u_int32_t +get_u32_le(const void *vp) +{ + const u_char *p = (const u_char *)vp; + u_int32_t v; + + v = (u_int32_t)p[0]; + v |= (u_int32_t)p[1] << 8; + v |= (u_int32_t)p[2] << 16; + v |= (u_int32_t)p[3] << 24; + + return (v); +} + +u_int16_t +get_u16(const void *vp) +{ + const u_char *p = (const u_char *)vp; + u_int16_t v; + + v = (u_int16_t)p[0] << 8; + v |= (u_int16_t)p[1]; + + return (v); +} + +void +put_u64(void *vp, u_int64_t v) +{ + u_char *p = (u_char *)vp; + + p[0] = (u_char)(v >> 56) & 0xff; + p[1] = (u_char)(v >> 48) & 0xff; + p[2] = (u_char)(v >> 40) & 0xff; + p[3] = (u_char)(v >> 32) & 0xff; + p[4] = (u_char)(v >> 24) & 0xff; + p[5] = (u_char)(v >> 16) & 0xff; + p[6] = (u_char)(v >> 8) & 0xff; + p[7] = (u_char)v & 0xff; +} + +void +put_u32(void *vp, u_int32_t v) +{ + u_char *p = (u_char *)vp; + + p[0] = (u_char)(v >> 24) & 0xff; + p[1] = (u_char)(v >> 16) & 0xff; + p[2] = (u_char)(v >> 8) & 0xff; + p[3] = (u_char)v & 0xff; +} + +void +put_u32_le(void *vp, u_int32_t v) +{ + u_char *p = (u_char *)vp; + + p[0] = (u_char)v & 0xff; + p[1] = (u_char)(v >> 8) & 0xff; + p[2] = (u_char)(v >> 16) & 0xff; + p[3] = (u_char)(v >> 24) & 0xff; +} + +void +put_u16(void *vp, u_int16_t v) +{ + u_char *p = (u_char *)vp; + + p[0] = (u_char)(v >> 8) & 0xff; + p[1] = (u_char)v & 0xff; +} + +void +ms_subtract_diff(struct timeval *start, int *ms) +{ + struct timeval diff, finish; + + monotime_tv(&finish); + timersub(&finish, start, &diff); + *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); +} + +void +ms_to_timespec(struct timespec *ts, int ms) +{ + if (ms < 0) + ms = 0; + ts->tv_sec = ms / 1000; + ts->tv_nsec = (ms % 1000) * 1000 * 1000; +} + +void +monotime_ts(struct timespec *ts) +{ + struct timeval tv; +#if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \ + defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME)) + static int gettime_failed = 0; + + if (!gettime_failed) { +# ifdef CLOCK_BOOTTIME + if (clock_gettime(CLOCK_BOOTTIME, ts) == 0) + return; +# endif /* CLOCK_BOOTTIME */ +# ifdef CLOCK_MONOTONIC + if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) + return; +# endif /* CLOCK_MONOTONIC */ +# ifdef CLOCK_REALTIME + /* Not monotonic, but we're almost out of options here. */ + if (clock_gettime(CLOCK_REALTIME, ts) == 0) + return; +# endif /* CLOCK_REALTIME */ + debug3("clock_gettime: %s", strerror(errno)); + gettime_failed = 1; + } +#endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */ + gettimeofday(&tv, NULL); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = (long)tv.tv_usec * 1000; +} + +void +monotime_tv(struct timeval *tv) +{ + struct timespec ts; + + monotime_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +time_t +monotime(void) +{ + struct timespec ts; + + monotime_ts(&ts); + return ts.tv_sec; +} + +double +monotime_double(void) +{ + struct timespec ts; + + monotime_ts(&ts); + return ts.tv_sec + ((double)ts.tv_nsec / 1000000000); +} + +void +bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) +{ + bw->buflen = buflen; + bw->rate = kbps; + bw->thresh = buflen; + bw->lamt = 0; + timerclear(&bw->bwstart); + timerclear(&bw->bwend); +} + +/* Callback from read/write loop to insert bandwidth-limiting delays */ +void +bandwidth_limit(struct bwlimit *bw, size_t read_len) +{ + u_int64_t waitlen; + struct timespec ts, rm; + + bw->lamt += read_len; + if (!timerisset(&bw->bwstart)) { + monotime_tv(&bw->bwstart); + return; + } + if (bw->lamt < bw->thresh) + return; + + monotime_tv(&bw->bwend); + timersub(&bw->bwend, &bw->bwstart, &bw->bwend); + if (!timerisset(&bw->bwend)) + return; + + bw->lamt *= 8; + waitlen = (double)1000000L * bw->lamt / bw->rate; + + bw->bwstart.tv_sec = waitlen / 1000000L; + bw->bwstart.tv_usec = waitlen % 1000000L; + + if (timercmp(&bw->bwstart, &bw->bwend, >)) { + timersub(&bw->bwstart, &bw->bwend, &bw->bwend); + + /* Adjust the wait time */ + if (bw->bwend.tv_sec) { + bw->thresh /= 2; + if (bw->thresh < bw->buflen / 4) + bw->thresh = bw->buflen / 4; + } else if (bw->bwend.tv_usec < 10000) { + bw->thresh *= 2; + if (bw->thresh > bw->buflen * 8) + bw->thresh = bw->buflen * 8; + } + + TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); + while (nanosleep(&ts, &rm) == -1) { + if (errno != EINTR) + break; + ts = rm; + } + } + + bw->lamt = 0; + monotime_tv(&bw->bwstart); +} + +/* Make a template filename for mk[sd]temp() */ +void +mktemp_proto(char *s, size_t len) +{ + const char *tmpdir; + int r; + + if ((tmpdir = getenv("TMPDIR")) != NULL) { + r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); + if (r > 0 && (size_t)r < len) + return; + } + r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); + if (r < 0 || (size_t)r >= len) + fatal_f("template string too short"); +} + +static const struct { + const char *name; + int value; +} ipqos[] = { + { "none", INT_MAX }, /* can't use 0 here; that's CS0 */ + { "af11", IPTOS_DSCP_AF11 }, + { "af12", IPTOS_DSCP_AF12 }, + { "af13", IPTOS_DSCP_AF13 }, + { "af21", IPTOS_DSCP_AF21 }, + { "af22", IPTOS_DSCP_AF22 }, + { "af23", IPTOS_DSCP_AF23 }, + { "af31", IPTOS_DSCP_AF31 }, + { "af32", IPTOS_DSCP_AF32 }, + { "af33", IPTOS_DSCP_AF33 }, + { "af41", IPTOS_DSCP_AF41 }, + { "af42", IPTOS_DSCP_AF42 }, + { "af43", IPTOS_DSCP_AF43 }, + { "cs0", IPTOS_DSCP_CS0 }, + { "cs1", IPTOS_DSCP_CS1 }, + { "cs2", IPTOS_DSCP_CS2 }, + { "cs3", IPTOS_DSCP_CS3 }, + { "cs4", IPTOS_DSCP_CS4 }, + { "cs5", IPTOS_DSCP_CS5 }, + { "cs6", IPTOS_DSCP_CS6 }, + { "cs7", IPTOS_DSCP_CS7 }, + { "ef", IPTOS_DSCP_EF }, + { "le", IPTOS_DSCP_LE }, + { "lowdelay", IPTOS_LOWDELAY }, + { "throughput", IPTOS_THROUGHPUT }, + { "reliability", IPTOS_RELIABILITY }, + { NULL, -1 } +}; + +int +parse_ipqos(const char *cp) +{ + u_int i; + char *ep; + long val; + + if (cp == NULL) + return -1; + for (i = 0; ipqos[i].name != NULL; i++) { + if (strcasecmp(cp, ipqos[i].name) == 0) + return ipqos[i].value; + } + /* Try parsing as an integer */ + val = strtol(cp, &ep, 0); + if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) + return -1; + return val; +} + +const char * +iptos2str(int iptos) +{ + int i; + static char iptos_str[sizeof "0xff"]; + + for (i = 0; ipqos[i].name != NULL; i++) { + if (ipqos[i].value == iptos) + return ipqos[i].name; + } + snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); + return iptos_str; +} + +void +lowercase(char *s) +{ + for (; *s; s++) + *s = tolower((u_char)*s); +} + +int +unix_listener(const char *path, int backlog, int unlink_first) +{ + struct sockaddr_un sunaddr; + int saved_errno, sock; + + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + if (strlcpy(sunaddr.sun_path, path, + sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { + error_f("path \"%s\" too long for Unix domain socket", path); + errno = ENAMETOOLONG; + return -1; + } + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + saved_errno = errno; + error_f("socket: %.100s", strerror(errno)); + errno = saved_errno; + return -1; + } + if (unlink_first == 1) { + if (unlink(path) != 0 && errno != ENOENT) + error("unlink(%s): %.100s", path, strerror(errno)); + } + if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { + saved_errno = errno; + error_f("cannot bind to path %s: %s", path, strerror(errno)); + close(sock); + errno = saved_errno; + return -1; + } + if (listen(sock, backlog) == -1) { + saved_errno = errno; + error_f("cannot listen on path %s: %s", path, strerror(errno)); + close(sock); + unlink(path); + errno = saved_errno; + return -1; + } + return sock; +} + +void +sock_set_v6only(int s) +{ +#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) + int on = 1; + + debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) + error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); +#endif +} + +/* + * Compares two strings that maybe be NULL. Returns non-zero if strings + * are both NULL or are identical, returns zero otherwise. + */ +static int +strcmp_maybe_null(const char *a, const char *b) +{ + if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) + return 0; + if (a != NULL && strcmp(a, b) != 0) + return 0; + return 1; +} + +/* + * Compare two forwards, returning non-zero if they are identical or + * zero otherwise. + */ +int +forward_equals(const struct Forward *a, const struct Forward *b) +{ + if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0) + return 0; + if (a->listen_port != b->listen_port) + return 0; + if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0) + return 0; + if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0) + return 0; + if (a->connect_port != b->connect_port) + return 0; + if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0) + return 0; + /* allocated_port and handle are not checked */ + return 1; +} + +/* returns 1 if process is already daemonized, 0 otherwise */ +int +daemonized(void) +{ + int fd; + + if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) { + close(fd); + return 0; /* have controlling terminal */ + } + if (getppid() != 1) + return 0; /* parent is not init */ + if (getsid(0) != getpid()) + return 0; /* not session leader */ + debug3("already daemonized"); + return 1; +} + +/* + * Splits 's' into an argument vector. Handles quoted string and basic + * escape characters (\\, \", \'). Caller must free the argument vector + * and its members. + */ +int +argv_split(const char *s, int *argcp, char ***argvp, int terminate_on_comment) +{ + int r = SSH_ERR_INTERNAL_ERROR; + int argc = 0, quote, i, j; + char *arg, **argv = xcalloc(1, sizeof(*argv)); + + *argvp = NULL; + *argcp = 0; + + for (i = 0; s[i] != '\0'; i++) { + /* Skip leading whitespace */ + if (s[i] == ' ' || s[i] == '\t') + continue; + if (terminate_on_comment && s[i] == '#') + break; + /* Start of a token */ + quote = 0; + + argv = xreallocarray(argv, (argc + 2), sizeof(*argv)); + arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1); + argv[argc] = NULL; + + /* Copy the token in, removing escapes */ + for (j = 0; s[i] != '\0'; i++) { + if (s[i] == '\\') { + if (s[i + 1] == '\'' || + s[i + 1] == '\"' || + s[i + 1] == '\\' || + (quote == 0 && s[i + 1] == ' ')) { + i++; /* Skip '\' */ + arg[j++] = s[i]; + } else { + /* Unrecognised escape */ + arg[j++] = s[i]; + } + } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t')) + break; /* done */ + else if (quote == 0 && (s[i] == '\"' || s[i] == '\'')) + quote = s[i]; /* quote start */ + else if (quote != 0 && s[i] == quote) + quote = 0; /* quote end */ + else + arg[j++] = s[i]; + } + if (s[i] == '\0') { + if (quote != 0) { + /* Ran out of string looking for close quote */ + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + break; + } + } + /* Success */ + *argcp = argc; + *argvp = argv; + argc = 0; + argv = NULL; + r = 0; + out: + if (argc != 0 && argv != NULL) { + for (i = 0; i < argc; i++) + free(argv[i]); + free(argv); + } + return r; +} + +/* + * Reassemble an argument vector into a string, quoting and escaping as + * necessary. Caller must free returned string. + */ +char * +argv_assemble(int argc, char **argv) +{ + int i, j, ws, r; + char c, *ret; + struct sshbuf *buf, *arg; + + if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + for (i = 0; i < argc; i++) { + ws = 0; + sshbuf_reset(arg); + for (j = 0; argv[i][j] != '\0'; j++) { + r = 0; + c = argv[i][j]; + switch (c) { + case ' ': + case '\t': + ws = 1; + r = sshbuf_put_u8(arg, c); + break; + case '\\': + case '\'': + case '"': + if ((r = sshbuf_put_u8(arg, '\\')) != 0) + break; + /* FALLTHROUGH */ + default: + r = sshbuf_put_u8(arg, c); + break; + } + if (r != 0) + fatal_fr(r, "sshbuf_put_u8"); + } + if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) || + (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) || + (r = sshbuf_putb(buf, arg)) != 0 || + (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0)) + fatal_fr(r, "assemble"); + } + if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL) + fatal_f("malloc failed"); + memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf)); + ret[sshbuf_len(buf)] = '\0'; + sshbuf_free(buf); + sshbuf_free(arg); + return ret; +} + +char * +argv_next(int *argcp, char ***argvp) +{ + char *ret = (*argvp)[0]; + + if (*argcp > 0 && ret != NULL) { + (*argcp)--; + (*argvp)++; + } + return ret; +} + +void +argv_consume(int *argcp) +{ + *argcp = 0; +} + +void +argv_free(char **av, int ac) +{ + int i; + + if (av == NULL) + return; + for (i = 0; i < ac; i++) + free(av[i]); + free(av); +} + +/* Returns 0 if pid exited cleanly, non-zero otherwise */ +int +exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) +{ + int status; + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + error("%s waitpid: %s", tag, strerror(errno)); + return -1; + } + } + if (WIFSIGNALED(status)) { + error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status)); + return -1; + } else if (WEXITSTATUS(status) != 0) { + do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, + "%s %s failed, status %d", tag, cmd, WEXITSTATUS(status)); + return -1; + } + return 0; +} + +/* + * Check a given path for security. This is defined as all components + * of the path to the file must be owned by either the owner of + * of the file or root and no directories must be group or world writable. + * + * XXX Should any specific check be done for sym links ? + * + * Takes a file name, its stat information (preferably from fstat() to + * avoid races), the uid of the expected owner, their home directory and an + * error buffer plus max size as arguments. + * + * Returns 0 on success and -1 on failure + */ +int +safe_path(const char *name, struct stat *stp, const char *pw_dir, + uid_t uid, char *err, size_t errlen) +{ + char buf[PATH_MAX], homedir[PATH_MAX]; + char *cp; + int comparehome = 0; +#if !defined(ANDROID) + struct stat st; +#endif + + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, + strerror(errno)); + return -1; + } + if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) + comparehome = 1; + + if (!S_ISREG(stp->st_mode)) { + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } + if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || + (stp->st_mode & 022) != 0) { + snprintf(err, errlen, "bad ownership or modes for file %s", + buf); + return -1; + } + + /* for each component of the canonical path, walking upwards */ + for (;;) { + if ((cp = dirname(buf)) == NULL) { + snprintf(err, errlen, "dirname() failed"); + return -1; + } + strlcpy(buf, cp, sizeof(buf)); + +#if !defined(ANDROID) + /* /data is owned by system user, which causes this check to fail */ + if (stat(buf, &st) == -1 || + (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || + (st.st_mode & 022) != 0) { + snprintf(err, errlen, + "bad ownership or modes for directory %s", buf); + return -1; + } +#endif + + /* If are past the homedir then we can stop */ + if (comparehome && strcmp(homedir, buf) == 0) + break; + + /* + * dirname should always complete with a "/" path, + * but we can be paranoid and check for "." too + */ + if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) + break; + } + return 0; +} + +/* + * Version of safe_path() that accepts an open file descriptor to + * avoid races. + * + * Returns 0 on success and -1 on failure + */ +int +safe_path_fd(int fd, const char *file, struct passwd *pw, + char *err, size_t errlen) +{ + struct stat st; + + /* check the open file to avoid races */ + if (fstat(fd, &st) == -1) { + snprintf(err, errlen, "cannot stat file %s: %s", + file, strerror(errno)); + return -1; + } + return safe_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); +} + +/* + * Sets the value of the given variable in the environment. If the variable + * already exists, its value is overridden. + */ +void +child_set_env(char ***envp, u_int *envsizep, const char *name, + const char *value) +{ + char **env; + u_int envsize; + u_int i, namelen; + + if (strchr(name, '=') != NULL) { + error("Invalid environment variable \"%.100s\"", name); + return; + } + + /* + * If we're passed an uninitialized list, allocate a single null + * entry before continuing. + */ + if (*envp == NULL && *envsizep == 0) { + *envp = xmalloc(sizeof(char *)); + *envp[0] = NULL; + *envsizep = 1; + } + + /* + * Find the slot where the value should be stored. If the variable + * already exists, we reuse the slot; otherwise we append a new slot + * at the end of the array, expanding if necessary. + */ + env = *envp; + namelen = strlen(name); + for (i = 0; env[i]; i++) + if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') + break; + if (env[i]) { + /* Reuse the slot. */ + free(env[i]); + } else { + /* New variable. Expand if necessary. */ + envsize = *envsizep; + if (i >= envsize - 1) { + if (envsize >= 1000) + fatal("child_set_env: too many env vars"); + envsize += 50; + env = (*envp) = xreallocarray(env, envsize, sizeof(char *)); + *envsizep = envsize; + } + /* Need to set the NULL pointer at end of array beyond the new slot. */ + env[i + 1] = NULL; + } + + /* Allocate space and format the variable in the appropriate slot. */ + /* XXX xasprintf */ + env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); + snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); +} + +/* + * Check and optionally lowercase a domain name, also removes trailing '.' + * Returns 1 on success and 0 on failure, storing an error message in errstr. + */ +int +valid_domain(char *name, int makelower, const char **errstr) +{ + size_t i, l = strlen(name); + u_char c, last = '\0'; + static char errbuf[256]; + + if (l == 0) { + strlcpy(errbuf, "empty domain name", sizeof(errbuf)); + goto bad; + } + if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) { + snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" " + "starts with invalid character", name); + goto bad; + } + for (i = 0; i < l; i++) { + c = tolower((u_char)name[i]); + if (makelower) + name[i] = (char)c; + if (last == '.' && c == '.') { + snprintf(errbuf, sizeof(errbuf), "domain name " + "\"%.100s\" contains consecutive separators", name); + goto bad; + } + if (c != '.' && c != '-' && !isalnum(c) && + c != '_') /* technically invalid, but common */ { + snprintf(errbuf, sizeof(errbuf), "domain name " + "\"%.100s\" contains invalid characters", name); + goto bad; + } + last = c; + } + if (name[l - 1] == '.') + name[l - 1] = '\0'; + if (errstr != NULL) + *errstr = NULL; + return 1; +bad: + if (errstr != NULL) + *errstr = errbuf; + return 0; +} + +/* + * Verify that a environment variable name (not including initial '$') is + * valid; consisting of one or more alphanumeric or underscore characters only. + * Returns 1 on valid, 0 otherwise. + */ +int +valid_env_name(const char *name) +{ + const char *cp; + + if (name[0] == '\0') + return 0; + for (cp = name; *cp != '\0'; cp++) { + if (!isalnum((u_char)*cp) && *cp != '_') + return 0; + } + return 1; +} + +const char * +atoi_err(const char *nptr, int *val) +{ + const char *errstr = NULL; + long long num; + + if (nptr == NULL || *nptr == '\0') + return "missing"; + num = strtonum(nptr, 0, INT_MAX, &errstr); + if (errstr == NULL) + *val = (int)num; + return errstr; +} + +int +parse_absolute_time(const char *s, uint64_t *tp) +{ + struct tm tm; + time_t tt; + char buf[32], *fmt; + + *tp = 0; + + /* + * POSIX strptime says "The application shall ensure that there + * is white-space or other non-alphanumeric characters between + * any two conversion specifications" so arrange things this way. + */ + switch (strlen(s)) { + case 8: /* YYYYMMDD */ + fmt = "%Y-%m-%d"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); + break; + case 12: /* YYYYMMDDHHMM */ + fmt = "%Y-%m-%dT%H:%M"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s", + s, s + 4, s + 6, s + 8, s + 10); + break; + case 14: /* YYYYMMDDHHMMSS */ + fmt = "%Y-%m-%dT%H:%M:%S"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", + s, s + 4, s + 6, s + 8, s + 10, s + 12); + break; + default: + return SSH_ERR_INVALID_FORMAT; + } + + memset(&tm, 0, sizeof(tm)); + if (strptime(buf, fmt, &tm) == NULL) + return SSH_ERR_INVALID_FORMAT; + if ((tt = mktime(&tm)) < 0) + return SSH_ERR_INVALID_FORMAT; + /* success */ + *tp = (uint64_t)tt; + return 0; +} + +/* On OpenBSD time_t is int64_t which is long long. */ +/* #define SSH_TIME_T_MAX LLONG_MAX */ + +void +format_absolute_time(uint64_t t, char *buf, size_t len) +{ + time_t tt = t > SSH_TIME_T_MAX ? SSH_TIME_T_MAX : t; + struct tm tm; + + localtime_r(&tt, &tm); + strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); +} + +/* check if path is absolute */ +int +path_absolute(const char *path) +{ + return (*path == '/') ? 1 : 0; +} + +void +skip_space(char **cpp) +{ + char *cp; + + for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) + ; + *cpp = cp; +} + +/* authorized_key-style options parsing helpers */ + +/* + * Match flag 'opt' in *optsp, and if allow_negate is set then also match + * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 + * if negated option matches. + * If the option or negated option matches, then *optsp is updated to + * point to the first character after the option. + */ +int +opt_flag(const char *opt, int allow_negate, const char **optsp) +{ + size_t opt_len = strlen(opt); + const char *opts = *optsp; + int negate = 0; + + if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { + opts += 3; + negate = 1; + } + if (strncasecmp(opts, opt, opt_len) == 0) { + *optsp = opts + opt_len; + return negate ? 0 : 1; + } + return -1; +} + +char * +opt_dequote(const char **sp, const char **errstrp) +{ + const char *s = *sp; + char *ret; + size_t i; + + *errstrp = NULL; + if (*s != '"') { + *errstrp = "missing start quote"; + return NULL; + } + s++; + if ((ret = malloc(strlen((s)) + 1)) == NULL) { + *errstrp = "memory allocation failed"; + return NULL; + } + for (i = 0; *s != '\0' && *s != '"';) { + if (s[0] == '\\' && s[1] == '"') + s++; + ret[i++] = *s++; + } + if (*s == '\0') { + *errstrp = "missing end quote"; + free(ret); + return NULL; + } + ret[i] = '\0'; + s++; + *sp = s; + return ret; +} + +int +opt_match(const char **opts, const char *term) +{ + if (strncasecmp((*opts), term, strlen(term)) == 0 && + (*opts)[strlen(term)] == '=') { + *opts += strlen(term) + 1; + return 1; + } + return 0; +} + +void +opt_array_append2(const char *file, const int line, const char *directive, + char ***array, int **iarray, u_int *lp, const char *s, int i) +{ + + if (*lp >= INT_MAX) + fatal("%s line %d: Too many %s entries", file, line, directive); + + if (iarray != NULL) { + *iarray = xrecallocarray(*iarray, *lp, *lp + 1, + sizeof(**iarray)); + (*iarray)[*lp] = i; + } + + *array = xrecallocarray(*array, *lp, *lp + 1, sizeof(**array)); + (*array)[*lp] = xstrdup(s); + (*lp)++; +} + +void +opt_array_append(const char *file, const int line, const char *directive, + char ***array, u_int *lp, const char *s) +{ + opt_array_append2(file, line, directive, array, NULL, lp, s, 0); +} + +sshsig_t +ssh_signal(int signum, sshsig_t handler) +{ + struct sigaction sa, osa; + + /* mask all other signals while in handler */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigfillset(&sa.sa_mask); +#if defined(SA_RESTART) && !defined(NO_SA_RESTART) + if (signum != SIGALRM) + sa.sa_flags = SA_RESTART; +#endif + if (sigaction(signum, &sa, &osa) == -1) { + debug3("sigaction(%s): %s", strsignal(signum), strerror(errno)); + return SIG_ERR; + } + return osa.sa_handler; +} + +int +stdfd_devnull(int do_stdin, int do_stdout, int do_stderr) +{ + int devnull, ret = 0; + + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error_f("open %s: %s", _PATH_DEVNULL, + strerror(errno)); + return -1; + } + if ((do_stdin && dup2(devnull, STDIN_FILENO) == -1) || + (do_stdout && dup2(devnull, STDOUT_FILENO) == -1) || + (do_stderr && dup2(devnull, STDERR_FILENO) == -1)) { + error_f("dup2: %s", strerror(errno)); + ret = -1; + } + if (devnull > STDERR_FILENO) + close(devnull); + return ret; +} + +/* + * Runs command in a subprocess with a minimal environment. + * Returns pid on success, 0 on failure. + * The child stdout and stderr maybe captured, left attached or sent to + * /dev/null depending on the contents of flags. + * "tag" is prepended to log messages. + * NB. "command" is only used for logging; the actual command executed is + * av[0]. + */ +pid_t +subprocess(const char *tag, const char *command, + int ac, char **av, FILE **child, u_int flags, + struct passwd *pw, privdrop_fn *drop_privs, privrestore_fn *restore_privs) +{ + FILE *f = NULL; + struct stat st; + int fd, devnull, p[2], i; + pid_t pid; + char *cp, errmsg[512]; + u_int nenv = 0; + char **env = NULL; + + /* If dropping privs, then must specify user and restore function */ + if (drop_privs != NULL && (pw == NULL || restore_privs == NULL)) { + error("%s: inconsistent arguments", tag); /* XXX fatal? */ + return 0; + } + if (pw == NULL && (pw = getpwuid(getuid())) == NULL) { + error("%s: no user for current uid", tag); + return 0; + } + if (child != NULL) + *child = NULL; + + debug3_f("%s command \"%s\" running as %s (flags 0x%x)", + tag, command, pw->pw_name, flags); + + /* Check consistency */ + if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && + (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { + error_f("inconsistent flags"); + return 0; + } + if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { + error_f("inconsistent flags/output"); + return 0; + } + + /* + * If executing an explicit binary, then verify the it exists + * and appears safe-ish to execute + */ + if (!path_absolute(av[0])) { + error("%s path is not absolute", tag); + return 0; + } + if (drop_privs != NULL) + drop_privs(pw); + if (stat(av[0], &st) == -1) { + error("Could not stat %s \"%s\": %s", tag, + av[0], strerror(errno)); + goto restore_return; + } + if ((flags & SSH_SUBPROCESS_UNSAFE_PATH) == 0 && + safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { + error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); + goto restore_return; + } + /* Prepare to keep the child's stdout if requested */ + if (pipe(p) == -1) { + error("%s: pipe: %s", tag, strerror(errno)); + restore_return: + if (restore_privs != NULL) + restore_privs(); + return 0; + } + if (restore_privs != NULL) + restore_privs(); + + switch ((pid = fork())) { + case -1: /* error */ + error("%s: fork: %s", tag, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; + case 0: /* child */ + /* Prepare a minimal environment for the child. */ + if ((flags & SSH_SUBPROCESS_PRESERVE_ENV) == 0) { + nenv = 5; + env = xcalloc(sizeof(*env), nenv); + child_set_env(&env, &nenv, "PATH", _PATH_STDPATH); + child_set_env(&env, &nenv, "USER", pw->pw_name); + child_set_env(&env, &nenv, "LOGNAME", pw->pw_name); + child_set_env(&env, &nenv, "HOME", pw->pw_dir); + if ((cp = getenv("LANG")) != NULL) + child_set_env(&env, &nenv, "LANG", cp); + } + + for (i = 1; i < NSIG; i++) + ssh_signal(i, SIG_DFL); + + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", tag, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if (dup2(devnull, STDIN_FILENO) == -1) { + error("%s: dup2: %s", tag, strerror(errno)); + _exit(1); + } + + /* Set up stdout as requested; leave stderr in place for now. */ + fd = -1; + if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) + fd = p[1]; + else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) + fd = devnull; + if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { + error("%s: dup2: %s", tag, strerror(errno)); + _exit(1); + } + closefrom(STDERR_FILENO + 1); + + if (geteuid() == 0 && + initgroups(pw->pw_name, pw->pw_gid) == -1) { + error("%s: initgroups(%s, %u): %s", tag, + pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); + _exit(1); + } + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) { + error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, + strerror(errno)); + _exit(1); + } + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) { + error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, + strerror(errno)); + _exit(1); + } + /* stdin is pointed to /dev/null at this point */ + if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && + dup2(STDIN_FILENO, STDERR_FILENO) == -1) { + error("%s: dup2: %s", tag, strerror(errno)); + _exit(1); + } + if (env != NULL) + execve(av[0], av, env); + else + execv(av[0], av); + error("%s %s \"%s\": %s", tag, env == NULL ? "execv" : "execve", + command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + + close(p[1]); + if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) + close(p[0]); + else if ((f = fdopen(p[0], "r")) == NULL) { + error("%s: fdopen: %s", tag, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + kill(pid, SIGTERM); + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) + ; + return 0; + } + /* Success */ + debug3_f("%s pid %ld", tag, (long)pid); + if (child != NULL) + *child = f; + return pid; +} + +const char * +lookup_env_in_list(const char *env, char * const *envs, size_t nenvs) +{ + size_t i, envlen; + + envlen = strlen(env); + for (i = 0; i < nenvs; i++) { + if (strncmp(envs[i], env, envlen) == 0 && + envs[i][envlen] == '=') { + return envs[i] + envlen + 1; + } + } + return NULL; +} diff --git a/aosp/external/openssh/misc.h b/aosp/external/openssh/misc.h new file mode 100644 index 0000000000000000000000000000000000000000..2e1b5fecaa017912ea3d7a5e92aaa1103c80cce3 --- /dev/null +++ b/aosp/external/openssh/misc.h @@ -0,0 +1,232 @@ +/* $OpenBSD: misc.h,v 1.99 2021/11/13 21:14:13 deraadt Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef _MISC_H +#define _MISC_H + +#include +#include +#include +#include + +/* Data structure for representing a forwarding request. */ +struct Forward { + char *listen_host; /* Host (address) to listen on. */ + int listen_port; /* Port to forward. */ + char *listen_path; /* Path to bind domain socket. */ + char *connect_host; /* Host to connect. */ + int connect_port; /* Port to connect on connect_host. */ + char *connect_path; /* Path to connect domain socket. */ + int allocated_port; /* Dynamically allocated listen port */ + int handle; /* Handle for dynamic listen ports */ +}; + +int forward_equals(const struct Forward *, const struct Forward *); +int daemonized(void); + +/* Common server and client forwarding options. */ +struct ForwardOptions { + int gateway_ports; /* Allow remote connects to forwarded ports. */ + mode_t streamlocal_bind_mask; /* umask for streamlocal binds */ + int streamlocal_bind_unlink; /* unlink socket before bind */ +}; + +/* misc.c */ + +char *chop(char *); +void rtrim(char *); +void skip_space(char **); +char *strdelim(char **); +char *strdelimw(char **); +int set_nonblock(int); +int unset_nonblock(int); +void set_nodelay(int); +int set_reuseaddr(int); +char *get_rdomain(int); +int set_rdomain(int, const char *); +int get_sock_af(int); +void set_sock_tos(int, int); +int waitrfd(int, int *); +int timeout_connect(int, const struct sockaddr *, socklen_t, int *); +int a2port(const char *); +int a2tun(const char *, int *); +char *put_host_port(const char *, u_short); +char *hpdelim2(char **, char *); +char *hpdelim(char **); +char *cleanhostname(char *); +char *colon(char *); +int parse_user_host_path(const char *, char **, char **, char **); +int parse_user_host_port(const char *, char **, char **, int *); +int parse_uri(const char *, const char *, char **, char **, int *, char **); +int convtime(const char *); +const char *fmt_timeframe(time_t t); +int tilde_expand(const char *, uid_t, char **); +char *tilde_expand_filename(const char *, uid_t); + +char *dollar_expand(int *, const char *string, ...); +char *percent_expand(const char *, ...) __attribute__((__sentinel__)); +char *percent_dollar_expand(const char *, ...) __attribute__((__sentinel__)); +char *tohex(const void *, size_t); +void xextendf(char **s, const char *sep, const char *fmt, ...) + __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); +void sanitise_stdfd(void); +void ms_subtract_diff(struct timeval *, int *); +void ms_to_timespec(struct timespec *, int); +void monotime_ts(struct timespec *); +void monotime_tv(struct timeval *); +time_t monotime(void); +double monotime_double(void); +void lowercase(char *s); +int unix_listener(const char *, int, int); +int valid_domain(char *, int, const char **); +int valid_env_name(const char *); +const char *atoi_err(const char *, int *); +int parse_absolute_time(const char *, uint64_t *); +void format_absolute_time(uint64_t, char *, size_t); +int path_absolute(const char *); +int stdfd_devnull(int, int, int); + +void sock_set_v6only(int); + +struct passwd *pwcopy(struct passwd *); +const char *ssh_gai_strerror(int); + +typedef void privdrop_fn(struct passwd *); +typedef void privrestore_fn(void); +#define SSH_SUBPROCESS_STDOUT_DISCARD (1) /* Discard stdout */ +#define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ +#define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ +#define SSH_SUBPROCESS_UNSAFE_PATH (1<<3) /* Don't check for safe cmd */ +#define SSH_SUBPROCESS_PRESERVE_ENV (1<<4) /* Keep parent environment */ +pid_t subprocess(const char *, const char *, int, char **, FILE **, u_int, + struct passwd *, privdrop_fn *, privrestore_fn *); + +typedef struct arglist arglist; +struct arglist { + char **list; + u_int num; + u_int nalloc; +}; +void addargs(arglist *, char *, ...) + __attribute__((format(printf, 2, 3))); +void replacearg(arglist *, u_int, char *, ...) + __attribute__((format(printf, 3, 4))); +void freeargs(arglist *); + +int tun_open(int, int, char **); + +/* Common definitions for ssh tunnel device forwarding */ +#define SSH_TUNMODE_NO 0x00 +#define SSH_TUNMODE_POINTOPOINT 0x01 +#define SSH_TUNMODE_ETHERNET 0x02 +#define SSH_TUNMODE_DEFAULT SSH_TUNMODE_POINTOPOINT +#define SSH_TUNMODE_YES (SSH_TUNMODE_POINTOPOINT|SSH_TUNMODE_ETHERNET) + +#define SSH_TUNID_ANY 0x7fffffff +#define SSH_TUNID_ERR (SSH_TUNID_ANY - 1) +#define SSH_TUNID_MAX (SSH_TUNID_ANY - 2) + +/* Fake port to indicate that host field is really a path. */ +#define PORT_STREAMLOCAL -2 + +/* Functions to extract or store big-endian words of various sizes */ +u_int64_t get_u64(const void *) + __attribute__((__bounded__( __minbytes__, 1, 8))); +u_int32_t get_u32(const void *) + __attribute__((__bounded__( __minbytes__, 1, 4))); +u_int16_t get_u16(const void *) + __attribute__((__bounded__( __minbytes__, 1, 2))); +void put_u64(void *, u_int64_t) + __attribute__((__bounded__( __minbytes__, 1, 8))); +void put_u32(void *, u_int32_t) + __attribute__((__bounded__( __minbytes__, 1, 4))); +void put_u16(void *, u_int16_t) + __attribute__((__bounded__( __minbytes__, 1, 2))); + +/* Little-endian store/load, used by umac.c */ +u_int32_t get_u32_le(const void *) + __attribute__((__bounded__(__minbytes__, 1, 4))); +void put_u32_le(void *, u_int32_t) + __attribute__((__bounded__(__minbytes__, 1, 4))); + +struct bwlimit { + size_t buflen; + u_int64_t rate; /* desired rate in kbit/s */ + u_int64_t thresh; /* threshold after which we'll check timers */ + u_int64_t lamt; /* amount written in last timer interval */ + struct timeval bwstart, bwend; +}; + +void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t); +void bandwidth_limit(struct bwlimit *, size_t); + +int parse_ipqos(const char *); +const char *iptos2str(int); +void mktemp_proto(char *, size_t); + +void child_set_env(char ***envp, u_int *envsizep, const char *name, + const char *value); +const char *lookup_env_in_list(const char *env, + char * const *envs, size_t nenvs); + +int argv_split(const char *, int *, char ***, int); +char *argv_assemble(int, char **argv); +char *argv_next(int *, char ***); +void argv_consume(int *); +void argv_free(char **, int); + +int exited_cleanly(pid_t, const char *, const char *, int); + +struct stat; +int safe_path(const char *, struct stat *, const char *, uid_t, + char *, size_t); +int safe_path_fd(int, const char *, struct passwd *, + char *err, size_t errlen); + +/* authorized_key-style options parsing helpers */ +int opt_flag(const char *opt, int allow_negate, const char **optsp); +char *opt_dequote(const char **sp, const char **errstrp); +int opt_match(const char **opts, const char *term); + +/* readconf/servconf option lists */ +void opt_array_append(const char *file, const int line, + const char *directive, char ***array, u_int *lp, const char *s); +void opt_array_append2(const char *file, const int line, + const char *directive, char ***array, int **iarray, u_int *lp, + const char *s, int i); + +/* readpass.c */ + +#define RP_ECHO 0x0001 +#define RP_ALLOW_STDIN 0x0002 +#define RP_ALLOW_EOF 0x0004 +#define RP_USE_ASKPASS 0x0008 + +struct notifier_ctx; + +char *read_passphrase(const char *, int); +int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); +struct notifier_ctx *notify_start(int, const char *, ...) + __attribute__((format(printf, 2, 3))); +void notify_complete(struct notifier_ctx *, const char *, ...) + __attribute__((format(printf, 2, 3))); + +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) +#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) + +typedef void (*sshsig_t)(int); +sshsig_t ssh_signal(int, sshsig_t); + +#endif /* _MISC_H */ diff --git a/aosp/external/openssh/mkinstalldirs b/aosp/external/openssh/mkinstalldirs new file mode 100644 index 0000000000000000000000000000000000000000..399f40925ac7ab9d7d803c805c7f136a134d95f2 --- /dev/null +++ b/aosp/external/openssh/mkinstalldirs @@ -0,0 +1,38 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/aosp/external/openssh/moduli b/aosp/external/openssh/moduli new file mode 100644 index 0000000000000000000000000000000000000000..1362f20e1bd1a8e9ed88a9333e7cdddcd59b5c18 --- /dev/null +++ b/aosp/external/openssh/moduli @@ -0,0 +1,383 @@ +# $OpenBSD: moduli,v 1.31 2021/09/28 11:10:05 dtucker Exp $ +# Time Type Tests Tries Size Generator Modulus +20210524220022 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F806ABBEB +20210524220025 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F807D248B +20210524220034 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F80D9BC9F +20210524220041 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F812A0A37 +20210524220046 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F8155AB97 +20210524220054 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F81B2848B +20210524220056 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F81C13007 +20210524220118 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F82930D03 +20210524220122 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F82BE877B +20210524220127 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F82EBBF97 +20210524220132 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F8320C043 +20210524220137 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F83476BCB +20210524220154 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F83FA204B +20210524220159 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F842A67EB +20210524220217 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F84E7FEDB +20210524220251 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F86503997 +20210524220256 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F867E7587 +20210524220301 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F86A47F7B +20210524220302 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F86A754D3 +20210524220304 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F86BFB23F +20210524220310 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F86F871FB +20210524220313 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87125B7F +20210524220316 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87290AD7 +20210524220323 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F876DECFB +20210524220330 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87B31B33 +20210524220332 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87BD0977 +20210524220333 2 6 100 2047 5 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87C5168F +20210524220335 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87D5CCB3 +20210524220337 2 6 100 2047 2 C117F4B631CA032FD2F00AC0D9A5473D8E56DA246FC44FD594BF5657D399E453728341CC920EE9127729637683D268CA3B62F5CB61C7D4F08D3F202D67DBBBD88498043861190549649D82E7793A1874FE0E8ED0B460E3442DD4DFC2301A1B6D8FA36C7CAD084B2FFEF2205E3CE46B030E4618C7B50656BF9FB60592B1FA32E91E0D536A12E601317F0562F547FF44DF33377ABAB2A2991EC99887712BFD9C78BBAA8759B09577706493F50A416F472D6F7B9532959A8899FACEB55B012E8FE8131AA45E1851FFAA3572E489FF4AAE89ECB583C98CC29ADBB1E9677AE2517D6BC1BC13C5A18A232FAD4CDC6E580FEC730D070D49E88A23C528A4985F87DBEA23 +20210524220355 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A44F8E93 +20210524220404 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A4A0C6AF +20210524220412 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A4EF1E03 +20210524220414 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A4F93C23 +20210524220420 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A535963B +20210524220424 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A5566833 +20210524220425 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A5599617 +20210524220426 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A55EC4A7 +20210524220436 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A5BD5EB7 +20210524220445 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A619258B +20210524220451 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A650122B +20210524220501 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A6B04DA7 +20210524220513 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A725B467 +20210524220528 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A7C40A4B +20210524220537 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A825DA4F +20210524220559 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A906849F +20210524220603 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A92B2543 +20210524220606 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A946A62B +20210524220611 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A96E9F73 +20210524220612 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A9741957 +20210524220613 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A977BF53 +20210524220614 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A9808FE3 +20210524220616 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A994D8CB +20210524220618 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A99CAF0B +20210524220624 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A9D53ABF +20210524220627 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01A9F1F75B +20210524220644 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AAB771E7 +20210524220645 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AAB8D6C3 +20210524220650 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AAE8CD27 +20210524220652 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AAFAE557 +20210524220656 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AB231D03 +20210524220658 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AB2F512F +20210524220700 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AB3ED6AF +20210524220706 2 6 100 2047 5 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01AB7A3DDF +20210524220712 2 6 100 2047 2 CDA33E2E7179C731632DFF6272815DAAC78E971B46095FA70FD7153B8FEC3061C37064D6D6961EA44D04D20222E7071AF123319EE4B70C6C6C7F5EC5BF6F4556A9636794FE249A109E9292CAE57FE40829089D99A5731AC08639F090500DAA9ECD8D56C83368EA05DEB9A1C37C82C0E84396128BF47B2222A7312DC06F7220DB671F16302E8C9ACEB9034E4955EC08E27C9E708FF81884B81CFB3DD2D662D0A60A5DAA91EBD69F564B8B2A565637663A4D444BA4BBEE7F029BB44AE1EA2182F39A1E2ABFDE297432932295FCF1DB704EFCD3A30A9EF881D6090480232D8893B9F7608037E1B5A2A09DD9E590C63ED28A82EFF89135E05E010710FC01ABB48B1B +20210524221547 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505902F721263 +20210524221723 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059030ED9F9B +20210524221800 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903179F84F +20210524221940 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB850590331E399B +20210524222007 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB850590338E4BEB +20210524222013 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB850590339E72DB +20210524222057 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903458ADC7 +20210524222125 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059034C60DC3 +20210524222149 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903527AD67 +20210524222206 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB850590356715BF +20210524222226 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059035B2AF7F +20210524222240 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059035E27D63 +20210524222530 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059038AD337F +20210524222550 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059038FF50F7 +20210524222711 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903A587DBB +20210524222722 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903A7E2593 +20210524222813 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903B518C67 +20210524222821 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903B6BA4B3 +20210524222927 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903C7ADF53 +20210524222944 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903CB9378F +20210524223124 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903E4E0F63 +20210524223132 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505903E680D03 +20210524223318 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505904017708B +20210524223328 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059040391D6B +20210524223340 2 6 100 3071 5 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB8505904064990F +20210524223418 2 6 100 3071 2 C1064E3791D96D66CA94320FDDA0DD731891FE4D4E4BCB5C8C2A22E33BE0C071BDD6B55DF0A474F1123F6AA3CDBB043EE9972052E3422BAAF9CBB6C1E77E2817F0266F0018351110EE2017EDE4312F0E0ABECA6585F15630FE2228BC03E4DEC84F31650177D712CDE77D6D6D2B0B391F64CD792A6C14BF9A16011EE673183465882595613087535464AC29A1138E1FB2B1024693A368700447307B43435E1BC718CD239396943F22BBF861EA0098069CAB7976B13C60A9CDD4BEDE6DF3659D61188E161D2D5A02CADDCCB547319E02211D00445BE47F5484F673239D7796CB1E1D3FF1558DA0ACD3AB17C6B3D8C3F00DFBBBF69F3B600E98DDC1FB0C0F79FC2E2F88C891F7BACAD4023331B623DF6835B2A7E881E59F0B7CE676208717E3AA51FBEDD7E9EE62527F253762C7B6A3D987DF800D2FB66DFE4F9588BEAA938DD9FAA5916186746FBC25A357F57D0E03442EEA6D067F19ABB96865474A250C73F66FE0FC5B5847CAB8F2CE25765A52A27223BD725C7ECFCF55EAAB85059040FCE473 +20210524223453 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB455657B +20210524223634 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB5FCC833 +20210524223659 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB65DD6F3 +20210524223732 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB6E02573 +20210524223739 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB6F4ED87 +20210524223745 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB7068FBB +20210524223826 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB7A2859B +20210524223848 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB7EE3B1B +20210524223936 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB8AE2333 +20210524224015 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB93EC75B +20210524224018 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EB944D10F +20210524224136 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBA7C4FC7 +20210524224213 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBB023E7F +20210524224228 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBB36560F +20210524224236 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBB4F6853 +20210524224323 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBC0F75BB +20210524224346 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBC62CB7B +20210524224413 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBCC9AD0B +20210524224441 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBD2F9967 +20210524224508 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBD92D627 +20210524224722 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBF92432B +20210524224733 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EBFB7A837 +20210524224832 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC09EA30F +20210524224841 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC0B86AD3 +20210524224843 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC0B88DBF +20210524225050 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC2C17DEB +20210524225054 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC2C967DB +20210524225112 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC30CDA43 +20210524225123 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC3311D0F +20210524225247 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC4813D2B +20210524225254 2 6 100 3071 2 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC4923E73 +20210524225346 2 6 100 3071 5 C6D582E2863DD842704EDE65181452A2A54FD6112537B792F2AF066D30656086948D10BD502D46881AA1766656F36A4BDDCCAF0893BA661A1E7952ED0CC9FD481640C27E1513322DA51E6CF68B2659BFED70ACA5F3D45188E2DCE57C05479C7564EB1573D3A039E815105E5BED13E0810D70E388766005C2DE1ADA9855EB53BF83EFABFCB67D8BDC4156C639A547EBDCB3EB980DCD81A44255F1EFCEAE2367482CD7AC7C7B68CA9536B6548F5323C1F46C9ADB084274D82387B7005D7D964CF53F4FCD2EF3337466DEB47BF46DA8501BCDAC4579FE0512B1D6C2AFDF933E9C6B89B25CCD5066B103D59FF1CB96DD4E427520CC06DEE74325885FD48E20E6DA45009CFD337511AF8081C2BC4341306DB2F2BFEB6912F7176D07161162D1BF8B603B0500EDAE3985D29BE88F3F94E98F91A73BA3CCAD9F35EBF6F95F179DA44AD4255AE983FBF8A8BD9458ED7D3AC7E410735FF90ECB32029C5A7D562A33C295F6262E6E91AA15444B971D565A6F90CD898E700B4EE15FE1ADABD3EA6EC562D397 +20210524230435 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375A8B66EEB +20210524230743 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375AA1B04D7 +20210524231138 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375ABDA60AB +20210524231337 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375ACBB313B +20210524231610 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375ADDB3D53 +20210524231810 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375AEB7413F +20210524231830 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375AED5921B +20210524231850 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375AEF2A0D7 +20210524232438 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B19B5B17 +20210524232556 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B23127DB +20210524233441 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B624C5D3 +20210524233915 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B81FBBEF +20210524234022 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B89ABD27 +20210524234251 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B9AA7603 +20210524234341 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375B9FF196B +20210524234859 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375BC541C47 +20210524234945 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375BCA418EB +20210524235116 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375BD49C877 +20210524235639 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375BFB1B0BB +20210524235928 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C0F703EB +20210525000202 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C21C5393 +20210525000215 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C22B072B +20210525000220 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C22DB943 +20210525000241 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C24E57CF +20210525000519 2 6 100 4095 5 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C37CDA8F +20210525000617 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C3E42EBB +20210525000910 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C52F623B +20210525000950 2 6 100 4095 2 DF313B012B87BCB41D4138171AA4A3ACBBCE90E58F3142966241384A0C0FFB78A3AAF92DFD19BAEF944D6323266A2BF7DAD5B917587B37D446A09EC84E519C10C981F1F59D7BF098A174589ACE1272D2BEDBC6703AE8F728E2EEC81F0C8F5E3E77991C632A664FAB45A584FD7A34133DB362EC04B2EFC6EC3F6C609C3ECEFB6223BA4CF06792D3A623F8FA4E1C78603B22B8AF856C8435AA6B25EEEB5C90F24572236182A79EE6A31331B4A78F1EFDE526AE53EA6C30F93DC2AEB3790E8AA728CE26D13080F84CC81ABB03E8E8E653368825D488461676BACF9BAAEDDA84A2E0E3524F407DD717A1DEC0D46C8FCF63F6F8952BCF1B19C03425F5AAB759ECD3C1E943756694A0F32CB0F765FB5EE410AFA6BAA30B0C96F4E65C76741BD067048DFF657B4C7C43336F200951448F3348AD241D9EBA14EE8CD8C023EF049835B6F6A6A4BA8B45CFF29C5252521494750C51972D2D448E357EA1B5A7230DCEFE9F2404E1D1C857E2DBFA18ABEA6369D958870D5657E3C6465748E2E10F851F9301B26F0A4AEF77F134C52A9391F7C92783FD7A2E378B5CE9F6D0ADA2B1EADC9BC9959E30E3821B2473DC1E2F3529DAE96B9E09BB0BC7806F41D7F39CEC690DB9F3DCCDFD92FA5DBF29FA26E5F55D87ED627E7788120A76ADDF2F2F28323F0950E1D9EAAC4270A6D66269978BB7382055BE7C36CE63446899C7ADF725F375C573F39B +20210525001816 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FBF7D2A3 +20210525001921 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FC6DAC53 +20210525001926 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FC6F3373 +20210525002309 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FE12C677 +20210525002320 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FE1E59AB +20210525002338 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FE3AB02F +20210525002359 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FE5BF4C3 +20210525002601 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB091FF3C869B +20210525002851 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09200739083 +20210525003041 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920140797B +20210525003049 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092014502F3 +20210525003141 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09201A2B9FF +20210525003223 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09201EA8A9B +20210525003231 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09201F326F3 +20210525003352 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092028EFD83 +20210525003809 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920482BA13 +20210525003923 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09205072CA3 +20210525004024 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09205780163 +20210525004148 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920617E947 +20210525004457 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092077D9FDB +20210525004558 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09207ED990B +20210525004731 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092089EB6C3 +20210525004805 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09208D77E3B +20210525004822 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09208EF6DA3 +20210525005103 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920A21B82B +20210525005232 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920AC6072B +20210525005335 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920B39092B +20210525005400 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920B611A73 +20210525005804 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920D2EF2A7 +20210525005836 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0920D66062F +20210525010525 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921066C17F +20210525010559 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09210A11893 +20210525010655 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921101CC03 +20210525010715 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092111F033F +20210525011027 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921284AEC3 +20210525011247 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092138697D7 +20210525011345 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09213EAC66B +20210525011758 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09215CA5E7F +20210525011827 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09215F9F6DB +20210525011950 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092168F25FB +20210525012059 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09217019A9B +20210525012441 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB092189F6357 +20210525012731 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB09219DE687B +20210525012806 2 6 100 4095 5 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921A19486F +20210525012959 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921AE8C22B +20210525013036 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921B277E7B +20210525013100 2 6 100 4095 2 D26A87E5A681F2A0E0231EC9449860176D2F9282C1D6E791D912B84987AAEF8BBCDE27FCC3A31FD76A78509ACB3673A0091330E077333D6682329E65ED16DAB1DC98ED93D911BCC3B63FF1271937AE5CE43688C0322530126BCC852EBA388F6B5B9AFF6808DD0B17790AD76F462EA9F0438F1E5A622C921E50A0358335806DD4A574C8117028E6DD07A42F09C3CDE7B5B9CD8914CEE9116E2F952862EFDB94F826D826C8EF15C468E80F916E09F4C411483E2C3CBDBF1CEE282F1144D17F9F681B0B238337941C1C5DEAB7DD52F0EC84A5C8B9FEEF5AA7EEA118FDF857B8BC0190C81A8D2E446E58C5F18DE1CFDD4680E04C89D421E1DEF67BC5A6FBA6B525FB06C263624A5D668A36F2254F9ADC7366544CE3A388B31675E3886801F9DFB571BA159C7D20B6175711387D696741F64FD689DC61F775028924BCCFC0197844340F61BB7EF0370B3FA53CFEEF65B4058B4CA1B25290DEB7E9ED829154430B7008A5E4174FCCAD783F3C8D1CE61014AF4299527B81B9F4D6EA4DEDEE89DD042FBDB6F0ED861CD7073F59AE181C5922BF8B480F7C71E2EC6CB6EA922FBA8C7383AA72001AA4A0AF633CD82D423B880E4AA3B96921BA8CE3EAB0337BAF49F37576905D811D657E3420C8C41C9BC693ACA6C94B34102F54FB370D597A1F9C00ED602927871F79C396D778FA4C968D2CD48788196EFB9511315087B9AEB0921B4B1893 +20210525015106 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081BF2FBF4B +20210525015719 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081C01C9317 +20210525022943 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081C51E7ABF +20210525025621 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081C92AC0FF +20210525030139 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081C9F26EFB +20210525030310 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081CA22ECE3 +20210525033123 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081CE69BC83 +20210525035425 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081D1EA340F +20210525042046 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081D5E1F147 +20210525044151 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081D906177B +20210525045703 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081DB34C7EB +20210525050443 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081DC53F8C7 +20210525050557 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081DC7921BB +20210525051327 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081DD90DF33 +20210525051635 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081DE04D46F +20210525052745 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081DFAB207B +20210525053701 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081E102FFA3 +20210525060524 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081E525193F +20210525061916 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081E723D123 +20210525062421 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081E7DAEAA3 +20210525062728 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081E8449753 +20210525064313 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081EA8CAE5B +20210525072251 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081F04FCEC3 +20210525082731 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081F9AA3067 +20210525090241 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB3081FEACB11B +20210525093810 2 6 100 6143 2 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB308203BF9E43 +20210525094831 2 6 100 6143 5 CAED937BA3084DFE7B4C1B59AC23C7EA9F28E677B8AFA536E9079DECED93EDAAEFB2C757F9E7AE4E90C4C68C8C937CAA8A0FB92A36FBBD24F777D579E264E8750B365A59AF83C5925262981B301C94CA57CE2A4825438EE089B2E0A5A8D90B51B7DB6044383542685F2D543030403741A6337C0280866A08D15BBFF32D9326073E6F8BAE510E66CF5A5DC6C7D96EFA13984EA383224CD59C7F8A49BA170EBD0BCF896FAD0FD67AB88D4D25CC81D29FFB7062328C9BA3BCD7EB107B74FAC05412D7677C058604F544B2EE8548E9372064C0CEB79BADEF8755F86E8DC894BD043DE697ECE323D624156B731D99C12FD1F42BB2FCADF8CFFEAB8DB59F9CB5843C1A01018DB3E415B4BF00D7F3F20FAAD86938C6B3061BC2A4ED0D18FAD01B1E53D0C8D41D208189D6E341E167B4B975236701B76C72097A34AAC46F4781185A7A63E4FB4A3CDDAB793EE4DFBA3D57481AF41ACD06A45F8D59D33FEBABCD3B98675A9C3F54B0AB42CFCABCA47021C709867AB12F830C4E93AE48AE5EBDD109E3F672AAE6A2EFFEAF07648D3110EE52627D44C8EA35482502C1CDC7B655F4D4A5E7DA1A064E138D00A9CC85DA5E833221AC5FF62F7871B53A8CE279AD441A5795D092F257C9BD2A871D098B3583ED9876342351C063BC2EA9FF111D245FE68A6A087B9E0B7AB0C47F2C6BD98A5CB4DFC57B71A1CF13A83308DA1E3BEB572AFDA9B88F05CCE1DBE359A44E849CAE4AB6196262877B119EDF241326C0F523ACF04DDF90601DC0D4107B935444D5C9E019B340227A386996D071BAA64D62FA049F285F12366ACDD439FCD3D0BAF9C37519177AAC6CF3B6986BAB5F68BDC9FB6E3B82FA157BE36D452FC0EDC4CDB95F9677F97C0F98725F275EAE0FF15C53957D1EFA9CABCE7B68FC186F117C40C5D24BD982A7F21AD6FE4A9CAC6C763FB68DD0B1C725E5DBC4522B86A42B02E3F5E704595977829607FDEB0922C497A9A93F327FA921D8BFBCD9C18272506A1A9F23171787FCBA72E4670C857D089DB5A22AE801EEBD567D48D697EBF25E6183374FC4C47C1747C3F69E650333F6F7ABEB30820532247F +20210525101346 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7A84C1DCF +20210525102328 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7A9C0F78B +20210525102507 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7A9F844D3 +20210525105019 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7ADB803E7 +20210525110206 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7AF734957 +20210525110823 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7B0605EAB +20210525113051 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7B3C762EB +20210525114458 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7B5E9D203 +20210525120436 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7B8DCCD03 +20210525120944 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7B9A11BE3 +20210525122534 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7BBFCC14F +20210525140523 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7CAD4CEEB +20210525141051 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7CB96ECE3 +20210525141705 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7CC76C197 +20210525145447 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7D1FC10C3 +20210525152246 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7D605451B +20210525153139 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7D73E819B +20210525153550 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7D7D225C3 +20210525161421 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7DD5B0F27 +20210525163744 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7E0BFFE7B +20210525165044 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7E29682EB +20210525172301 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7E730C6C3 +20210525172835 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7E7F17D9F +20210525173319 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7E89CD397 +20210525174552 2 6 100 6143 2 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7EA6442E3 +20210525175415 2 6 100 6143 5 E14FDFEE7787F1C28112A16D64FE7C953E3A963B98675E5C078465D774DDD60D0689616D88E223B1D75C4D124BD32324D828137D8AF145AED00162977FA394F7D9657F5E892CDAC98461E41CCF76B3F42471E8DC112FB82F65AD55AE55ADE7F3B85C9EE6E914F2DEE618D55A9FA0F72F6C03664BCDAEDC64ACD9DAE85B0396321F79565522DE272B5D041155C8949BE08B5B09712887BE1E20C31FE5A5A90E0F9610000EF7E1BBA8C7EF3A9F7E3BD2710BCA36C5390673FC55FC47CF63A356472C491229E283A236DB444E75BCD2F43B0FF35B809E6336314945B725912A1C3423141007FFC129C65CCD772966D9E394AECC3252F9C9AD019950C188FE8922A959E19F226B33235BA30D0F3B163F21BE8964C67D0CD118EB7EDBE0DB21762006A115ECCF829452B51E533AC29A836DC6CF40135FCB31B77A287E5CD377A0A80D7F2683A9D9E760F3EA479CFCE9627AFF5BF6AEC257043D71E0CC695D2DF4589C3255DBA1A2F58FA3F673C23FEF80366042BC0DBD7AE2556E3EA38577FC0CA9D56324CB084E6EBE01ADFAD00AB41CF94822FC7680A9007A6BA804A2242E8EDE7D599A46F59886D36E6B7F4AA20D92E682AFC1BB0BD5F5AC0203185B978F438508933EE2A85666467A95051DF1EA0C6366CC28D36030242A4B51820D043190B310D5D51DCE6A6AF333629FAEE5F3A6D88DDC325321A3767CF1C97B6B5FD8E2AC2E95C54F142BD4F5A5F5609E6D4BC09C55FA03F92BA35C006662FC08BEE197777F3E8CC1730F754447D349BF93AA8683D226EBD2FD872CB8383A03058AE9210891886DB9E4DB2300D109B2C32E26C460DA6E362F72214999EFDEED5E7299AA4C6A9DC9A9BB5A8D59374F92C339314BCEDFE44E5F805F37934C94FB6D8C8E2C9045BF0C1CE9A659A40937870F51F36947A89EA941990F743666D6B27C71CDF6366666E3623471945AB3F52F98C6034CE808AF2F10F21541A3B3C1CBF2A204770E10953F63913D1A5695C1A68E48D21F65C210097F931E4DD82A31BEE9EC842D8898A73CB84E236B8A10D3B7D8BE6097E4679CC8BE8C77A1CB824F6231E7EB8F5A57 +20210525183229 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816BFE560FB +20210525190732 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816C2E82C7B +20210525191937 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816C3EC147F +20210525193159 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816C4F60C8B +20210525194246 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816C5D97313 +20210525203443 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816CA459133 +20210525215204 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816D0DE4FDB +20210525233543 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816D9A29577 +20210525234619 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816DA7ADFAB +20210525235329 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816DB0B2673 +20210525235513 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816DB26C1CB +20210526022844 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816E7D6D73B +20210526023323 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816E82F077F +20210526062933 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FB41731B +20210526065151 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FCF0DCD3 +20210526070545 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FDF8DB8F +20210526071052 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FE572613 +20210526071809 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FEDF35F3 +20210526072107 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FF13DABF +20210526072624 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37816FF7318C7 +20210526081148 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F3781702F3953F +20210526081939 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378170385D3EF +20210526100857 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378170BEE7A0B +20210526104252 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378170E7ACB07 +20210526104946 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378170EFA920B +20210526105532 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378170F653A13 +20210526110401 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378171004A14B +20210526110925 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378171065B293 +20210526123113 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F37817168DDC17 +20210526123356 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F3781716B7D3FB +20210526135059 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378171C86775F +20210526162058 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F3781727B688EB +20210526173849 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378172D7A5213 +20210526175432 2 6 100 7679 2 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378172E9D4DF3 +20210526175746 2 6 100 7679 5 E3F4DC1A68758284B2950A16204A7D9CB3972B0E74E2CDF12518B48DA3F349640BFEDA5162CB2EC47E2593EDB3BB51D743F3F4948A4D940B37B724B6D7E5F5BFD44F9D3CFB0F6E2CC1B33AEB7FCA7C2E26EFC55208D15DD12B08925383346148CA7CEEE874499CBE6E5D34AF7EF47742F683398B5418E3FA261013922180C6EEB4250632A2F93CDC281E6E76A488FEB4CE7B4C9783A37201CE4B551B53EEBF291234454CD0A4DC1B78C2D0F1BF72EC5D64EDBE8CB662F373ED861194F02AFBC8B7133ED9BDDFEEBA8E7C19C58505DF691960346B4F9E0FC1076DE872E6CB4323B8AD7FB4B086B26E10A55ECEB23F6FE78A5224A0679E5EA529C1327D38EDCCD340788D6216D1388B33A8E93B41B0B13ECAB9B7D647BBFBA2181E752B9DE7230C13B38E0988CAD88E2B1F8B8B1C938B93BAD62F250289CE33E3BE77C04B8090B806B031C23413EAE7799E6FB6DFB02ACCD308EA8688245527ED4918ACE575748DC01C02A6420C9CEECCAF8CBA73F16282DEEC6697CF82C880E3FFC8FD149411C6AD19AAF3B30BF2EE89977B7536C9607777F6E4F6E83B84BB4D9B359EDA6A5A1167B6D352F4D6CCE10F2C974EB81CB1C75280406B2F891A7E500BD58648A5A88ACDD1A6D7A2D80690BB441E533171930ABA557A52BD2512E3FD9A3E5DE6A378E3FED42D6ED5480093D3B0E068F2A24B54FF43BEE12BDD5A646B4A2C1746C27328792A4B9528FB45F6AF56C6F917978B535D98C2441EE79D825E73BF97A7D49B5DC85B12B0C257DF6AF0907B771C5EA582C9DECDDC3CC1852AE403FDCA636DB4B111747646CF893260651567C4C63B391013CA9047AE7F77BCE46A16FBE54054CA8D74AE248A5C5A22B841D31B89C82C8D9567A03B249A07FC1CEFB03B569AB328B0028DA5F3D4D2E2BBE076D9DAAE0DA86DA2648E7311709D0A8C6083C42F9FE430418C2597BF533D294E15E53D5E045A95222A23F4CA7AC9FF35A0E1CBFB80227D800ADA8F79FE463F1C9FA0D8A4E9F6C42ACAB07B5A7CE6E1BB649264EF1EBBDE073A1892B3EDA5CF60D0921754A5848FE47F32168A094CAB1D242EF095BAAD113824511114B4714DDAAA585E3A653DEF46A70DB8D43E4813E6A03E5779B9A9194925CC967DF08F1EB9CEC35EA96BB3D78C40B0A4B8AA79D101CD8110E37119267CC3F2ADA62E3CA0F444C22CF5A73D46BBFDE56BBD1E4FC397A9D6975E77923B6E3AD4CAB4A4E3C5EEB01DC1B7690BF44197AAD7D1B802BB4F6494095E708F4A7F9642AAD64B0511037DC9A65663C4AA6046575A56226103E282B61286CFE6BD8672A3C297DB544E3CF08013BD2B32130D21E521351E9AA9F378172ED281E7 +20210526194658 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC72DF2567 +20210526205542 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC789BCF5B +20210526211855 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC7A83E6AB +20210526212819 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC7B4A5E7B +20210526214431 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC7CA4162F +20210526221404 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC7F1A2A03 +20210526224339 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC817DD633 +20210526232709 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC850A6253 +20210526233209 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC8568303F +20210527003359 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC8A7766A3 +20210527004505 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC8B60FE03 +20210527005150 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC8BE3BDCF +20210527010445 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC8CE83F03 +20210527012302 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC8E564BAB +20210527031004 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC96F6A7D3 +20210527042035 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDC9C9071D3 +20210527051239 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCA0A5F80F +20210527054907 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCA382DD3B +20210527055430 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCA3E850B3 +20210527063343 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCA6FD9EFB +20210527071327 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCAA0EBEC7 +20210527071630 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCAA41FB57 +20210527080058 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCADACEDC3 +20210527085945 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCB22EE113 +20210527101619 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCB8016A93 +20210527102548 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCB8B44C67 +20210527105317 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCBAC26CAF +20210527111021 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCBC0189BB +20210527120037 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCBFCB4D7B +20210527120149 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCBFDA08E7 +20210527125327 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCC3B60853 +20210527125755 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCC4035243 +20210527133418 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCC6B03E83 +20210527141546 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCC9BF3B53 +20210527142810 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCCAA21133 +20210527143703 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCCB42B1A3 +20210527150004 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCCCEFC0DF +20210527152242 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCCE94F76B +20210527153438 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCCF7614B7 +20210527153537 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCCF7F4B23 +20210527155914 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCD13EEF3F +20210527170116 2 6 100 7679 2 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCD5E5A47B +20210527171119 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCD6975A67 +20210527173152 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCD815F687 +20210527180427 2 6 100 7679 5 C32E7FEED6DD76E659C6961141AEC4C2E086F62A1B6FBB851A142301550CCFC2E424C3D4B1B518D97BB7DBA9B7E0E40408EA5CE767F9276D112391D7D777FC14537F787F3ADD0DA7A45B64E1330E8F7A0D23C5AFB283E766F2BD69FD286599C68CFF239900CB849E720800D52B4D7FDFBE94C6B920AFD4FE3BAB7C9F19193F96ADC0EDE471FCFCD27ABFF54EFD8F589FD4FD5DF45FC03F832C4B137F4AEAE448E9FEC83A8E1BE47FA202A243BE467219C94A5B28908635E9794C4A4D471E17297E5584CAC109082787C01B8C32C3B0D89006EABD02F17D8BAA792C71A3CA3563501206E70786FC09605B91365EBC2F31EAA94778BB63895FE13ECEBDD005255672ED2768ACAA24E317FC4D4204AEF9B69C0A7EE845B3C228A878D385BE603145DFA087D480A41AEDFA887F7B058BBBEE7D702D83611132FDDF37D0ED1D1731B905D73CDCEF06F9F2BE2A1D01099A915F545F8FFE9B42B66C5FF5DDF2CFCD38B50D45620A364650F12A6E9E94D398FBA738ED66C764901F475CC0DB8B328343180DC70E77BAFC3D8F5CD168A9D9985BA98DD632FB6691B178DE4C276FD5EADABB891680118D0F1234C21AE7788981A33AEEB5D357C28B87F82E4D7314C2A5C9C756541C9CA0C038CA03EB4A75324BBD8DEB5817EDBE6EC15FEEE0E94DBADB19DB0C53467935672C9E8DA879D83619277D37109F4002884B45A69693E5E5C0919BB3A11C89D0BDE5B944602222405EA03F1CB8B8C171C1BD01489E5690EFBFD69CE50A3C7D57FB83857B18B98FFC999D0CA1CA5BFE196485D03E6CD9DE2964091827CC7FBD6D4708D6E8C27BD38A40CF06F713544A1A862DC650F13148A039BE09E55765E27E70490EA8D5AB67009A5834ED6972FF8D76F57B4EEE311E2F395C12DF65FB0DB8F9DD405F0CFEDF3BF08C391E628A0230B6F0ABBB4D07EC644DBBFBD4F48F9A89756F1004873C3D9F20E3566B6B1A2E1ABB6C7152EFB099723469A3D4408D0C27106E09D483C705E50556A35691C99B108D5629A3BFF434A196868E460046B26F4D1FC9725A3470229BF840C86EE0FE1239AF1992C6F7E960CAD0E0D82839CB67318A084AE5BBD8CE513C4DE6EC95B74F8CABADCD825775C36247B07F2CBDCF45B6C3ABF0C4EFED04733309D6CFEBE37871B9A4180A84A822742CD47496C310BCECB574EBED53C0BFDE574D49F3E180EF575B1BFB8799434305FAB7CB970AF999407F262FDBC13CC1809B1F34A962FE8D1348D52780C4AA173E769E70CAC6D8C2E97BF92F7624B4631952FC07FAF93E43A1A8C869C3423BD9A9387CFB4B5B8B2D8475174CCA1AF38CC64E514F0281D65A9AEE4876EB7CDCDA776E0F +20210527185319 2 6 100 8191 5 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F513E1A12F +20210527185735 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F51429194B +20210527213255 2 6 100 8191 5 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F51F664F27 +20210527214610 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F52058B933 +20210528000741 2 6 100 8191 5 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F52A61610F +20210528003717 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F52C738E73 +20210528014022 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F530CE1FFB +20210528020015 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F5322397AB +20210528050850 2 6 100 8191 5 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F53F20EBA7 +20210528054438 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F5418B5C4B +20210528084340 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F54D8C79F3 +20210528091530 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F54FA20AAB +20210528094806 2 6 100 8191 5 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F551BB35BF +20210528123059 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F55C74EE33 +20210528123356 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F55C9BEE33 +20210528131007 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F55EF35FB3 +20210528131122 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F55EFEBC1B +20210528133019 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F560345AEB +20210528174944 2 6 100 8191 5 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F570F3AF57 +20210528180427 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F571DF3C6B +20210528235428 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F587F3BDB3 +20210529021000 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F590536F63 +20210529024531 2 6 100 8191 2 F08476CF6891E7C1CE5A873D1D744FC41D579F53B1F877B0F280D44DF57BEBB3EFDEBC7B8DA48C885005E0F3D8167BE79FAA61DA67EAB112C599882D369A7446FFB888D483317B6A7E0A6AC0CCB6ADD70D511AD81FDCDD71D40C5A40B3627872CE2EDD092B2ED810F6F5F3AE3C02D97856002D646C750910C9B6917615BA8D8D0345514B5F7B37EF910BE98C8E42FF9F9C2FE04F3252C2894D67A033AB22CA3B1BFA9FEB208509654755F3B83121AB827BC481F2E435F68EA144ECCC19670F5C234369F97461A9AFEAE48917B2D767E925D3B94C6380F4A1891EE20A1413C358EC93C8D0D34E90C424BCA367AB4B7C63FDFA0931FB8735ECC5FE22694F8E2784389AC68974BBF1FB5196A5CFA63D30464968927752F1F8F216A07A467EFBCFD4078203DA384AC63A6A304AC56114D2BBDF9212F58BDE3AC556E168ACBF0AB63370D44E90D63E5CEDACA9D94F248B74A783E3852BE5BFF73E27F6FAE6BD0BE5A7A014262CBFB7ADB6A29477D9F2F5FB45E9AD5F16C07B06CA166455C83377C52BF61D06D8CCA11B692F7F05E96264C52295C73D6CD991190A54A2B097BFDBBA8B89A5DFCD1FF9DAFD24542B78FF5F00AF78890EA8297223CCAD28DEE98D829750A17298ABE7F3ED70E7370B1CDE1B4F23C24D69F0F4EA9F821B89BAA2D4577A247118EEF70F780FEF57DB587E1F3EAE54748615BE921CA766FC7EC2961E36EFB41CFC607B989ED0C4254057FD9D39715A35C872ED17407856C80C35F65BD181B181942CB5D62710FE60D0347FEEC2AC3ACE45EA72FCB4256EC07C0383B6CF930CF56CDB9803CEE74A66F83ABEF9C75A0D222BF02C3535B6E3C0DC3124A0E603B9E6A89A3BF636305D2174A2B379C4E36685993C8E8B4E44905D2D352B9EE9101910DCB6931C904D16CB0A079B6669DA3FF8F925A3119EF27F4164DA23405F8F135E454D09BEA876E27449E5884884A24833F3BE93165240285E43A3E4B296CAB04891F574787335DDF17085F77B37AFCD8221AB748FCB28CA2C664931C397E2CD70F8EC8D21C467A16A1F33EB1A5E9C0CA80EC81697A40FE6B01753AC39216301D9763D401DE1460DEC6BC546FDB648C79F9A7B80D6A8CD2AC8E09970D52B1FD4D780A51DBDDC4FDCB53A3D6DB50C6959C78AE8E1912BBD9B259159BEFE965834EB8F17F00A10DE857A49A6E94BA5E930998F283CB035C83174B17596B4C3ECFAC1912AB9799A92E2A01D6A0004753E3D2E5F8748A697D4007DF99C32C3E6C1C3DDDDEF90B9539D716DCF3F95CFC4CA0A9D9AD13E570C55A708986D1304740AAE543C4506D96DB45AC0547854735EFAB7814C8FF5029B256DA0C58E6BC618008117E92228B618103642E8AE533D5859178AB2B39E3B0D044195F531E8304DCA4B883E1B2DFDA12663E9D5FE976935CCC1594C6284A68C4CECAAF842F5927CEA53 +20210529031713 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CA36AA3B7 +20210529041139 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CA75D422B +20210529050432 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CAB1C588B +20210529050545 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CAB28E9FF +20210529063055 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CB131E3CF +20210529074241 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CB6403B37 +20210529084711 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CBAB7BD1B +20210529091139 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CBC6896B3 +20210529091418 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CBC8EBF67 +20210529104434 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CC2D31FCB +20210529114106 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CC6BA9507 +20210529124457 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CCB1266F7 +20210529130632 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CCC8BF0A7 +20210529154413 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CD7337777 +20210529171849 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CDD794B1B +20210529201318 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CE8F228D7 +20210529210612 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CEC4AD6BB +20210529211901 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CED1A6127 +20210529213817 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CEE51861B +20210529215309 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CEF3757F3 +20210529215904 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CEF8E8187 +20210530010351 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CFB5346BB +20210530015721 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CFE9B3287 +20210530020811 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2CFF416103 +20210530062143 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2D0EB89917 +20210530071701 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2D1208E667 +20210530095307 2 6 100 8191 2 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2D1B675F3B +20210530111421 2 6 100 8191 5 D8A2E2F024645B5A3FB339A2EF06A3E46CD2AA808530C320F121CE1EEBEE128D6DB4C49492881B35E5AA8C43CAA07E7F69A5C4ECD0EE62A29FC191B9C0DD6955E23E831A0573135B9931D03A5BBCA961410CA287D08D53C0E8CFEC19534C4FECBAD0F2BE625802AF1B504F59A4A88DAA5F52F221E0E9E0DB1396F07EAF2CA88B79D8CF35EF154A69569C545CA1D04A07ADE3628243039985880B79972FB9F5070951D40C9D21C1729348152724531B270C9DEBB5B2C730C72F4EA3932DB44BD3C746E082856A4109A37BA979164881D5B8007807FF1A7E0C48F99DBBE1709585203E579AE201D295E720CDD45B3A8652A7CE878765E699727641410F840871B4362DC51A3D4D15980876649089CE845B5733497FB8E7B2568EF8627EC1BEE0434A3A72094132A0BA0CA102A440952285823FB09684A1C52468193F6E8A6CC9D935C13FCC661DED5CB84B5D5DF721447531787F78AB31AA1D6342460A8B896518640B7DCF3825B52D46469351EF17E586B661D5C0E792C0AA0B0309B8A4BECB3B267BB459C7452387FF6028EEC1C6DFAE3EE9452EB7A7CFB9CDD361B45FF29E6648BD94712BEF29A1C86C492231F710BC66BF8CF83F8765EF6DBA6857C733789DD71245F17C6FA03DD07FCFFBC450029776FDB4F4B411D4B4A98E090CB79B4CF990398FE1C147CE959C8C11D5DE9A4271EA4CA3D8A50A6F7081A2B0F95AA4AD80081511C357D9F15B7547F0A725EED4EE7FDDAE32A3D00512984928679C51375777E968FE3363DCC495300762C7C6D32F4088DB4D4D4449610D05E87CECADA742827EF2B4EF1D064CEB0EEBB01264306F3E24D6260483829576F88B8346764429722BFD0FE3845F541837ACFFEDA101E0E082F618630B741BE1A9E44C677A947FEC31F08EB338798617A09C38EF3FF31A677E62DCDBA2C56D37B1F16AD2B06F41D3E5CDE9A9B7179B6EC46DF99D857521DF29E8012A1AFE6F2F0658171F56EEA20E531C3B2E02864B130BB0BA476F23024DCCA6411CE1BBD1D5C9F5346DB36334117AAF7A1B4ABE86470CABDF862010F5AE3129CA17F36BA89EE3DB83641804EC2455ED75686342E9F339068A77258BBC292E05AF5C233F5B74CA83080CC5DAE01E977E2F7D1ADC36803398B81DAC22DE391F512912148392371C658C416C96C8C3035928EDBB418CF249C9C02793AE6661AB04B4C6C944F55B23CB5621511DACD8EC5C1ECD463620CAC79C38B6CC95FA8993482EA255246D30DA6EA67C677889F56CDF4466F5D968069E152B0BE30B64E1B18607035D7D15503D364B73BAB5582CDF290FE30D2EB7A4F6F797D80702CC448E1153A3A9D882445C73BA490388588F31FA6C14BA7DBFAD39E1CE6B0E2FAD69015F81FF6DAAC56420A478CF717DD952A0DC51B1E38113CF5EF2AC2EC08B6F6CB427A06AD990213C09DB2D2044A90F diff --git a/aosp/external/openssh/moduli.5 b/aosp/external/openssh/moduli.5 new file mode 100644 index 0000000000000000000000000000000000000000..ef0de08506b17cc339904ad951d747435e6cb081 --- /dev/null +++ b/aosp/external/openssh/moduli.5 @@ -0,0 +1,127 @@ +.\" $OpenBSD: moduli.5,v 1.17 2012/09/26 17:34:38 jmc Exp $ +.\" +.\" Copyright (c) 2008 Damien Miller +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.Dd $Mdocdate: September 26 2012 $ +.Dt MODULI 5 +.Os +.Sh NAME +.Nm moduli +.Nd Diffie-Hellman moduli +.Sh DESCRIPTION +The +.Pa /etc/moduli +file contains prime numbers and generators for use by +.Xr sshd 8 +in the Diffie-Hellman Group Exchange key exchange method. +.Pp +New moduli may be generated with +.Xr ssh-keygen 1 +using a two-step process. +An initial +.Em candidate generation +pass, using +.Ic ssh-keygen -G , +calculates numbers that are likely to be useful. +A second +.Em primality testing +pass, using +.Ic ssh-keygen -T , +provides a high degree of assurance that the numbers are prime and are +safe for use in Diffie-Hellman operations by +.Xr sshd 8 . +This +.Nm +format is used as the output from each pass. +.Pp +The file consists of newline-separated records, one per modulus, +containing seven space-separated fields. +These fields are as follows: +.Bl -tag -width Description -offset indent +.It timestamp +The time that the modulus was last processed as YYYYMMDDHHMMSS. +.It type +Decimal number specifying the internal structure of the prime modulus. +Supported types are: +.Pp +.Bl -tag -width 0x00 -compact +.It 0 +Unknown, not tested. +.It 2 +"Safe" prime; (p-1)/2 is also prime. +.It 4 +Sophie Germain; 2p+1 is also prime. +.El +.Pp +Moduli candidates initially produced by +.Xr ssh-keygen 1 +are Sophie Germain primes (type 4). +Further primality testing with +.Xr ssh-keygen 1 +produces safe prime moduli (type 2) that are ready for use in +.Xr sshd 8 . +Other types are not used by OpenSSH. +.It tests +Decimal number indicating the type of primality tests that the number +has been subjected to represented as a bitmask of the following values: +.Pp +.Bl -tag -width 0x00 -compact +.It 0x00 +Not tested. +.It 0x01 +Composite number \(en not prime. +.It 0x02 +Sieve of Eratosthenes. +.It 0x04 +Probabilistic Miller-Rabin primality tests. +.El +.Pp +The +.Xr ssh-keygen 1 +moduli candidate generation uses the Sieve of Eratosthenes (flag 0x02). +Subsequent +.Xr ssh-keygen 1 +primality tests are Miller-Rabin tests (flag 0x04). +.It trials +Decimal number indicating the number of primality trials +that have been performed on the modulus. +.It size +Decimal number indicating the size of the prime in bits. +.It generator +The recommended generator for use with this modulus (hexadecimal). +.It modulus +The modulus itself in hexadecimal. +.El +.Pp +When performing Diffie-Hellman Group Exchange, +.Xr sshd 8 +first estimates the size of the modulus required to produce enough +Diffie-Hellman output to sufficiently key the selected symmetric cipher. +.Xr sshd 8 +then randomly selects a modulus from +.Fa /etc/moduli +that best meets the size requirement. +.Sh SEE ALSO +.Xr ssh-keygen 1 , +.Xr sshd 8 +.Sh STANDARDS +.Rs +.%A M. Friedl +.%A N. Provos +.%A W. Simpson +.%D March 2006 +.%R RFC 4419 +.%T Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol +.%D 2006 +.Re diff --git a/aosp/external/openssh/moduli.c b/aosp/external/openssh/moduli.c new file mode 100644 index 0000000000000000000000000000000000000000..8dd36b1cf231c8e1183fad050c7915b9d8f9f76f --- /dev/null +++ b/aosp/external/openssh/moduli.c @@ -0,0 +1,813 @@ +/* $OpenBSD: moduli.c,v 1.37 2019/11/15 06:00:20 djm Exp $ */ +/* + * Copyright 1994 Phil Karn + * Copyright 1996-1998, 2003 William Allen Simpson + * Copyright 2000 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Two-step process to generate safe primes for DHGEX + * + * Sieve candidates for "safe" primes, + * suitable for use as Diffie-Hellman moduli; + * that is, where q = (p-1)/2 is also prime. + * + * First step: generate candidate primes (memory intensive) + * Second step: test primes' safety (processor intensive) + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "dh.h" +#include "log.h" +#include "misc.h" + +#include "openbsd-compat/openssl-compat.h" + +/* + * File output defines + */ + +/* need line long enough for largest moduli plus headers */ +#define QLINESIZE (100+8192) + +/* + * Size: decimal. + * Specifies the number of the most significant bit (0 to M). + * WARNING: internally, usually 1 to N. + */ +#define QSIZE_MINIMUM (511) + +/* + * Prime sieving defines + */ + +/* Constant: assuming 8 bit bytes and 32 bit words */ +#define SHIFT_BIT (3) +#define SHIFT_BYTE (2) +#define SHIFT_WORD (SHIFT_BIT+SHIFT_BYTE) +#define SHIFT_MEGABYTE (20) +#define SHIFT_MEGAWORD (SHIFT_MEGABYTE-SHIFT_BYTE) + +/* + * Using virtual memory can cause thrashing. This should be the largest + * number that is supported without a large amount of disk activity -- + * that would increase the run time from hours to days or weeks! + */ +#define LARGE_MINIMUM (8UL) /* megabytes */ + +/* + * Do not increase this number beyond the unsigned integer bit size. + * Due to a multiple of 4, it must be LESS than 128 (yielding 2**30 bits). + */ +#define LARGE_MAXIMUM (127UL) /* megabytes */ + +/* + * Constant: when used with 32-bit integers, the largest sieve prime + * has to be less than 2**32. + */ +#define SMALL_MAXIMUM (0xffffffffUL) + +/* Constant: can sieve all primes less than 2**32, as 65537**2 > 2**32-1. */ +#define TINY_NUMBER (1UL<<16) + +/* Ensure enough bit space for testing 2*q. */ +#define TEST_MAXIMUM (1UL<<16) +#define TEST_MINIMUM (QSIZE_MINIMUM + 1) +/* real TEST_MINIMUM (1UL << (SHIFT_WORD - TEST_POWER)) */ +#define TEST_POWER (3) /* 2**n, n < SHIFT_WORD */ + +/* bit operations on 32-bit words */ +#define BIT_CLEAR(a,n) ((a)[(n)>>SHIFT_WORD] &= ~(1L << ((n) & 31))) +#define BIT_SET(a,n) ((a)[(n)>>SHIFT_WORD] |= (1L << ((n) & 31))) +#define BIT_TEST(a,n) ((a)[(n)>>SHIFT_WORD] & (1L << ((n) & 31))) + +/* + * Prime testing defines + */ + +/* Minimum number of primality tests to perform */ +#define TRIAL_MINIMUM (4) + +/* + * Sieving data (XXX - move to struct) + */ + +/* sieve 2**16 */ +static u_int32_t *TinySieve, tinybits; + +/* sieve 2**30 in 2**16 parts */ +static u_int32_t *SmallSieve, smallbits, smallbase; + +/* sieve relative to the initial value */ +static u_int32_t *LargeSieve, largewords, largetries, largenumbers; +static u_int32_t largebits, largememory; /* megabytes */ +static BIGNUM *largebase; + +int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); +int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, + unsigned long); + +/* + * print moduli out in consistent form, + */ +static int +qfileout(FILE * ofile, u_int32_t otype, u_int32_t otests, u_int32_t otries, + u_int32_t osize, u_int32_t ogenerator, BIGNUM * omodulus) +{ + struct tm *gtm; + time_t time_now; + int res; + + time(&time_now); + gtm = gmtime(&time_now); + if (gtm == NULL) + return -1; + + res = fprintf(ofile, "%04d%02d%02d%02d%02d%02d %u %u %u %u %x ", + gtm->tm_year + 1900, gtm->tm_mon + 1, gtm->tm_mday, + gtm->tm_hour, gtm->tm_min, gtm->tm_sec, + otype, otests, otries, osize, ogenerator); + + if (res < 0) + return (-1); + + if (BN_print_fp(ofile, omodulus) < 1) + return (-1); + + res = fprintf(ofile, "\n"); + fflush(ofile); + + return (res > 0 ? 0 : -1); +} + + +/* + ** Sieve p's and q's with small factors + */ +static void +sieve_large(u_int32_t s) +{ + u_int32_t r, u; + + debug3("sieve_large %u", s); + largetries++; + /* r = largebase mod s */ + r = BN_mod_word(largebase, s); + if (r == 0) + u = 0; /* s divides into largebase exactly */ + else + u = s - r; /* largebase+u is first entry divisible by s */ + + if (u < largebits * 2) { + /* + * The sieve omits p's and q's divisible by 2, so ensure that + * largebase+u is odd. Then, step through the sieve in + * increments of 2*s + */ + if (u & 0x1) + u += s; /* Make largebase+u odd, and u even */ + + /* Mark all multiples of 2*s */ + for (u /= 2; u < largebits; u += s) + BIT_SET(LargeSieve, u); + } + + /* r = p mod s */ + r = (2 * r + 1) % s; + if (r == 0) + u = 0; /* s divides p exactly */ + else + u = s - r; /* p+u is first entry divisible by s */ + + if (u < largebits * 4) { + /* + * The sieve omits p's divisible by 4, so ensure that + * largebase+u is not. Then, step through the sieve in + * increments of 4*s + */ + while (u & 0x3) { + if (SMALL_MAXIMUM - u < s) + return; + u += s; + } + + /* Mark all multiples of 4*s */ + for (u /= 4; u < largebits; u += s) + BIT_SET(LargeSieve, u); + } +} + +/* + * list candidates for Sophie-Germain primes (where q = (p-1)/2) + * to standard output. + * The list is checked against small known primes (less than 2**30). + */ +int +gen_candidates(FILE *out, u_int32_t memory, u_int32_t power, BIGNUM *start) +{ + BIGNUM *q; + u_int32_t j, r, s, t; + u_int32_t smallwords = TINY_NUMBER >> 6; + u_int32_t tinywords = TINY_NUMBER >> 6; + time_t time_start, time_stop; + u_int32_t i; + int ret = 0; + + largememory = memory; + + if (memory != 0 && + (memory < LARGE_MINIMUM || memory > LARGE_MAXIMUM)) { + error("Invalid memory amount (min %ld, max %ld)", + LARGE_MINIMUM, LARGE_MAXIMUM); + return (-1); + } + + /* + * Set power to the length in bits of the prime to be generated. + * This is changed to 1 less than the desired safe prime moduli p. + */ + if (power > TEST_MAXIMUM) { + error("Too many bits: %u > %lu", power, TEST_MAXIMUM); + return (-1); + } else if (power < TEST_MINIMUM) { + error("Too few bits: %u < %u", power, TEST_MINIMUM); + return (-1); + } + power--; /* decrement before squaring */ + + /* + * The density of ordinary primes is on the order of 1/bits, so the + * density of safe primes should be about (1/bits)**2. Set test range + * to something well above bits**2 to be reasonably sure (but not + * guaranteed) of catching at least one safe prime. + */ + largewords = ((power * power) >> (SHIFT_WORD - TEST_POWER)); + + /* + * Need idea of how much memory is available. We don't have to use all + * of it. + */ + if (largememory > LARGE_MAXIMUM) { + logit("Limited memory: %u MB; limit %lu MB", + largememory, LARGE_MAXIMUM); + largememory = LARGE_MAXIMUM; + } + + if (largewords <= (largememory << SHIFT_MEGAWORD)) { + logit("Increased memory: %u MB; need %u bytes", + largememory, (largewords << SHIFT_BYTE)); + largewords = (largememory << SHIFT_MEGAWORD); + } else if (largememory > 0) { + logit("Decreased memory: %u MB; want %u bytes", + largememory, (largewords << SHIFT_BYTE)); + largewords = (largememory << SHIFT_MEGAWORD); + } + + TinySieve = xcalloc(tinywords, sizeof(u_int32_t)); + tinybits = tinywords << SHIFT_WORD; + + SmallSieve = xcalloc(smallwords, sizeof(u_int32_t)); + smallbits = smallwords << SHIFT_WORD; + + /* + * dynamically determine available memory + */ + while ((LargeSieve = calloc(largewords, sizeof(u_int32_t))) == NULL) + largewords -= (1L << (SHIFT_MEGAWORD - 2)); /* 1/4 MB chunks */ + + largebits = largewords << SHIFT_WORD; + largenumbers = largebits * 2; /* even numbers excluded */ + + /* validation check: count the number of primes tried */ + largetries = 0; + if ((q = BN_new()) == NULL) + fatal("BN_new failed"); + + /* + * Generate random starting point for subprime search, or use + * specified parameter. + */ + if ((largebase = BN_new()) == NULL) + fatal("BN_new failed"); + if (start == NULL) { + if (BN_rand(largebase, power, 1, 1) == 0) + fatal("BN_rand failed"); + } else { + if (BN_copy(largebase, start) == NULL) + fatal("BN_copy: failed"); + } + + /* ensure odd */ + if (BN_set_bit(largebase, 0) == 0) + fatal("BN_set_bit: failed"); + + time(&time_start); + + logit("%.24s Sieve next %u plus %u-bit", ctime(&time_start), + largenumbers, power); + debug2("start point: 0x%s", BN_bn2hex(largebase)); + + /* + * TinySieve + */ + for (i = 0; i < tinybits; i++) { + if (BIT_TEST(TinySieve, i)) + continue; /* 2*i+3 is composite */ + + /* The next tiny prime */ + t = 2 * i + 3; + + /* Mark all multiples of t */ + for (j = i + t; j < tinybits; j += t) + BIT_SET(TinySieve, j); + + sieve_large(t); + } + + /* + * Start the small block search at the next possible prime. To avoid + * fencepost errors, the last pass is skipped. + */ + for (smallbase = TINY_NUMBER + 3; + smallbase < (SMALL_MAXIMUM - TINY_NUMBER); + smallbase += TINY_NUMBER) { + for (i = 0; i < tinybits; i++) { + if (BIT_TEST(TinySieve, i)) + continue; /* 2*i+3 is composite */ + + /* The next tiny prime */ + t = 2 * i + 3; + r = smallbase % t; + + if (r == 0) { + s = 0; /* t divides into smallbase exactly */ + } else { + /* smallbase+s is first entry divisible by t */ + s = t - r; + } + + /* + * The sieve omits even numbers, so ensure that + * smallbase+s is odd. Then, step through the sieve + * in increments of 2*t + */ + if (s & 1) + s += t; /* Make smallbase+s odd, and s even */ + + /* Mark all multiples of 2*t */ + for (s /= 2; s < smallbits; s += t) + BIT_SET(SmallSieve, s); + } + + /* + * SmallSieve + */ + for (i = 0; i < smallbits; i++) { + if (BIT_TEST(SmallSieve, i)) + continue; /* 2*i+smallbase is composite */ + + /* The next small prime */ + sieve_large((2 * i) + smallbase); + } + + memset(SmallSieve, 0, smallwords << SHIFT_BYTE); + } + + time(&time_stop); + + logit("%.24s Sieved with %u small primes in %lld seconds", + ctime(&time_stop), largetries, (long long)(time_stop - time_start)); + + for (j = r = 0; j < largebits; j++) { + if (BIT_TEST(LargeSieve, j)) + continue; /* Definitely composite, skip */ + + debug2("test q = largebase+%u", 2 * j); + if (BN_set_word(q, 2 * j) == 0) + fatal("BN_set_word failed"); + if (BN_add(q, q, largebase) == 0) + fatal("BN_add failed"); + if (qfileout(out, MODULI_TYPE_SOPHIE_GERMAIN, + MODULI_TESTS_SIEVE, largetries, + (power - 1) /* MSB */, (0), q) == -1) { + ret = -1; + break; + } + + r++; /* count q */ + } + + time(&time_stop); + + free(LargeSieve); + free(SmallSieve); + free(TinySieve); + + logit("%.24s Found %u candidates", ctime(&time_stop), r); + + return (ret); +} + +static void +write_checkpoint(char *cpfile, u_int32_t lineno) +{ + FILE *fp; + char tmp[PATH_MAX]; + int r; + + r = snprintf(tmp, sizeof(tmp), "%s.XXXXXXXXXX", cpfile); + if (r < 0 || r >= PATH_MAX) { + logit("write_checkpoint: temp pathname too long"); + return; + } + if ((r = mkstemp(tmp)) == -1) { + logit("mkstemp(%s): %s", tmp, strerror(errno)); + return; + } + if ((fp = fdopen(r, "w")) == NULL) { + logit("write_checkpoint: fdopen: %s", strerror(errno)); + unlink(tmp); + close(r); + return; + } + if (fprintf(fp, "%lu\n", (unsigned long)lineno) > 0 && fclose(fp) == 0 + && rename(tmp, cpfile) == 0) + debug3("wrote checkpoint line %lu to '%s'", + (unsigned long)lineno, cpfile); + else + logit("failed to write to checkpoint file '%s': %s", cpfile, + strerror(errno)); +} + +static unsigned long +read_checkpoint(char *cpfile) +{ + FILE *fp; + unsigned long lineno = 0; + + if ((fp = fopen(cpfile, "r")) == NULL) + return 0; + if (fscanf(fp, "%lu\n", &lineno) < 1) + logit("Failed to load checkpoint from '%s'", cpfile); + else + logit("Loaded checkpoint from '%s' line %lu", cpfile, lineno); + fclose(fp); + return lineno; +} + +static unsigned long +count_lines(FILE *f) +{ + unsigned long count = 0; + char lp[QLINESIZE + 1]; + + if (fseek(f, 0, SEEK_SET) != 0) { + debug("input file is not seekable"); + return ULONG_MAX; + } + while (fgets(lp, QLINESIZE + 1, f) != NULL) + count++; + rewind(f); + debug("input file has %lu lines", count); + return count; +} + +static char * +fmt_time(time_t seconds) +{ + int day, hr, min; + static char buf[128]; + + min = (seconds / 60) % 60; + hr = (seconds / 60 / 60) % 24; + day = seconds / 60 / 60 / 24; + if (day > 0) + snprintf(buf, sizeof buf, "%dd %d:%02d", day, hr, min); + else + snprintf(buf, sizeof buf, "%d:%02d", hr, min); + return buf; +} + +static void +print_progress(unsigned long start_lineno, unsigned long current_lineno, + unsigned long end_lineno) +{ + static time_t time_start, time_prev; + time_t time_now, elapsed; + unsigned long num_to_process, processed, remaining, percent, eta; + double time_per_line; + char *eta_str; + + time_now = monotime(); + if (time_start == 0) { + time_start = time_prev = time_now; + return; + } + /* print progress after 1m then once per 5m */ + if (time_now - time_prev < 5 * 60) + return; + time_prev = time_now; + elapsed = time_now - time_start; + processed = current_lineno - start_lineno; + remaining = end_lineno - current_lineno; + num_to_process = end_lineno - start_lineno; + time_per_line = (double)elapsed / processed; + /* if we don't know how many we're processing just report count+time */ + time(&time_now); + if (end_lineno == ULONG_MAX) { + logit("%.24s processed %lu in %s", ctime(&time_now), + processed, fmt_time(elapsed)); + return; + } + percent = 100 * processed / num_to_process; + eta = time_per_line * remaining; + eta_str = xstrdup(fmt_time(eta)); + logit("%.24s processed %lu of %lu (%lu%%) in %s, ETA %s", + ctime(&time_now), processed, num_to_process, percent, + fmt_time(elapsed), eta_str); + free(eta_str); +} + +/* + * perform a Miller-Rabin primality test + * on the list of candidates + * (checking both q and p) + * The result is a list of so-call "safe" primes + */ +int +prime_test(FILE *in, FILE *out, u_int32_t trials, u_int32_t generator_wanted, + char *checkpoint_file, unsigned long start_lineno, unsigned long num_lines) +{ + BIGNUM *q, *p, *a; + char *cp, *lp; + u_int32_t count_in = 0, count_out = 0, count_possible = 0; + u_int32_t generator_known, in_tests, in_tries, in_type, in_size; + unsigned long last_processed = 0, end_lineno; + time_t time_start, time_stop; + int res, is_prime; + + if (trials < TRIAL_MINIMUM) { + error("Minimum primality trials is %d", TRIAL_MINIMUM); + return (-1); + } + + if (num_lines == 0) + end_lineno = count_lines(in); + else + end_lineno = start_lineno + num_lines; + + time(&time_start); + + if ((p = BN_new()) == NULL) + fatal("BN_new failed"); + if ((q = BN_new()) == NULL) + fatal("BN_new failed"); + + debug2("%.24s Final %u Miller-Rabin trials (%x generator)", + ctime(&time_start), trials, generator_wanted); + + if (checkpoint_file != NULL) + last_processed = read_checkpoint(checkpoint_file); + last_processed = start_lineno = MAXIMUM(last_processed, start_lineno); + if (end_lineno == ULONG_MAX) + debug("process from line %lu from pipe", last_processed); + else + debug("process from line %lu to line %lu", last_processed, + end_lineno); + + res = 0; + lp = xmalloc(QLINESIZE + 1); + while (fgets(lp, QLINESIZE + 1, in) != NULL && count_in < end_lineno) { + count_in++; + if (count_in <= last_processed) { + debug3("skipping line %u, before checkpoint or " + "specified start line", count_in); + continue; + } + if (checkpoint_file != NULL) + write_checkpoint(checkpoint_file, count_in); + print_progress(start_lineno, count_in, end_lineno); + if (strlen(lp) < 14 || *lp == '!' || *lp == '#') { + debug2("%10u: comment or short line", count_in); + continue; + } + + /* XXX - fragile parser */ + /* time */ + cp = &lp[14]; /* (skip) */ + + /* type */ + in_type = strtoul(cp, &cp, 10); + + /* tests */ + in_tests = strtoul(cp, &cp, 10); + + if (in_tests & MODULI_TESTS_COMPOSITE) { + debug2("%10u: known composite", count_in); + continue; + } + + /* tries */ + in_tries = strtoul(cp, &cp, 10); + + /* size (most significant bit) */ + in_size = strtoul(cp, &cp, 10); + + /* generator (hex) */ + generator_known = strtoul(cp, &cp, 16); + + /* Skip white space */ + cp += strspn(cp, " "); + + /* modulus (hex) */ + switch (in_type) { + case MODULI_TYPE_SOPHIE_GERMAIN: + debug2("%10u: (%u) Sophie-Germain", count_in, in_type); + a = q; + if (BN_hex2bn(&a, cp) == 0) + fatal("BN_hex2bn failed"); + /* p = 2*q + 1 */ + if (BN_lshift(p, q, 1) == 0) + fatal("BN_lshift failed"); + if (BN_add_word(p, 1) == 0) + fatal("BN_add_word failed"); + in_size += 1; + generator_known = 0; + break; + case MODULI_TYPE_UNSTRUCTURED: + case MODULI_TYPE_SAFE: + case MODULI_TYPE_SCHNORR: + case MODULI_TYPE_STRONG: + case MODULI_TYPE_UNKNOWN: + debug2("%10u: (%u)", count_in, in_type); + a = p; + if (BN_hex2bn(&a, cp) == 0) + fatal("BN_hex2bn failed"); + /* q = (p-1) / 2 */ + if (BN_rshift(q, p, 1) == 0) + fatal("BN_rshift failed"); + break; + default: + debug2("Unknown prime type"); + break; + } + + /* + * due to earlier inconsistencies in interpretation, check + * the proposed bit size. + */ + if ((u_int32_t)BN_num_bits(p) != (in_size + 1)) { + debug2("%10u: bit size %u mismatch", count_in, in_size); + continue; + } + if (in_size < QSIZE_MINIMUM) { + debug2("%10u: bit size %u too short", count_in, in_size); + continue; + } + + if (in_tests & MODULI_TESTS_MILLER_RABIN) + in_tries += trials; + else + in_tries = trials; + + /* + * guess unknown generator + */ + if (generator_known == 0) { + if (BN_mod_word(p, 24) == 11) + generator_known = 2; + else { + u_int32_t r = BN_mod_word(p, 10); + + if (r == 3 || r == 7) + generator_known = 5; + } + } + /* + * skip tests when desired generator doesn't match + */ + if (generator_wanted > 0 && + generator_wanted != generator_known) { + debug2("%10u: generator %d != %d", + count_in, generator_known, generator_wanted); + continue; + } + + /* + * Primes with no known generator are useless for DH, so + * skip those. + */ + if (generator_known == 0) { + debug2("%10u: no known generator", count_in); + continue; + } + + count_possible++; + + /* + * The (1/4)^N performance bound on Miller-Rabin is + * extremely pessimistic, so don't spend a lot of time + * really verifying that q is prime until after we know + * that p is also prime. A single pass will weed out the + * vast majority of composite q's. + */ + is_prime = BN_is_prime_ex(q, 1, NULL, NULL); + if (is_prime < 0) + fatal("BN_is_prime_ex failed"); + if (is_prime == 0) { + debug("%10u: q failed first possible prime test", + count_in); + continue; + } + + /* + * q is possibly prime, so go ahead and really make sure + * that p is prime. If it is, then we can go back and do + * the same for q. If p is composite, chances are that + * will show up on the first Rabin-Miller iteration so it + * doesn't hurt to specify a high iteration count. + */ + is_prime = BN_is_prime_ex(p, trials, NULL, NULL); + if (is_prime < 0) + fatal("BN_is_prime_ex failed"); + if (is_prime == 0) { + debug("%10u: p is not prime", count_in); + continue; + } + debug("%10u: p is almost certainly prime", count_in); + + /* recheck q more rigorously */ + is_prime = BN_is_prime_ex(q, trials - 1, NULL, NULL); + if (is_prime < 0) + fatal("BN_is_prime_ex failed"); + if (is_prime == 0) { + debug("%10u: q is not prime", count_in); + continue; + } + debug("%10u: q is almost certainly prime", count_in); + + if (qfileout(out, MODULI_TYPE_SAFE, + in_tests | MODULI_TESTS_MILLER_RABIN, + in_tries, in_size, generator_known, p)) { + res = -1; + break; + } + + count_out++; + } + + time(&time_stop); + free(lp); + BN_free(p); + BN_free(q); + + if (checkpoint_file != NULL) + unlink(checkpoint_file); + + logit("%.24s Found %u safe primes of %u candidates in %ld seconds", + ctime(&time_stop), count_out, count_possible, + (long) (time_stop - time_start)); + + return (res); +} + +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/monitor.c b/aosp/external/openssh/monitor.c new file mode 100644 index 0000000000000000000000000000000000000000..4f16f9d6c00ef1193807572d28ca76af1af12310 --- /dev/null +++ b/aosp/external/openssh/monitor.c @@ -0,0 +1,1962 @@ +/* $OpenBSD: monitor.c,v 1.232 2022/02/25 02:09:27 djm Exp $ */ +/* + * Copyright 2002 Niels Provos + * Copyright 2002 Markus Friedl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif + +#ifdef WITH_OPENSSL +#include +#endif + +#include "openbsd-compat/sys-tree.h" +#include "openbsd-compat/sys-queue.h" +#include "openbsd-compat/openssl-compat.h" + +#include "atomicio.h" +#include "xmalloc.h" +#include "ssh.h" +#include "sshkey.h" +#include "sshbuf.h" +#include "hostfile.h" +#include "auth.h" +#include "cipher.h" +#include "kex.h" +#include "dh.h" +#include "auth-pam.h" +#include "packet.h" +#include "auth-options.h" +#include "sshpty.h" +#include "channels.h" +#include "session.h" +#include "sshlogin.h" +#include "canohost.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "monitor.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "monitor_fdpass.h" +#include "compat.h" +#include "ssh2.h" +#include "authfd.h" +#include "match.h" +#include "ssherr.h" +#include "sk-api.h" + +#ifdef GSSAPI +static Gssctxt *gsscontext = NULL; +#endif + +/* Imports */ +extern ServerOptions options; +extern u_int utmp_len; +extern struct sshbuf *loginmsg; +extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ + +/* State exported from the child */ +static struct sshbuf *child_state; + +/* Functions on the monitor that answer unprivileged requests */ + +int mm_answer_moduli(struct ssh *, int, struct sshbuf *); +int mm_answer_sign(struct ssh *, int, struct sshbuf *); +int mm_answer_pwnamallow(struct ssh *, int, struct sshbuf *); +int mm_answer_auth2_read_banner(struct ssh *, int, struct sshbuf *); +int mm_answer_authserv(struct ssh *, int, struct sshbuf *); +int mm_answer_authpassword(struct ssh *, int, struct sshbuf *); +int mm_answer_bsdauthquery(struct ssh *, int, struct sshbuf *); +int mm_answer_bsdauthrespond(struct ssh *, int, struct sshbuf *); +int mm_answer_keyallowed(struct ssh *, int, struct sshbuf *); +int mm_answer_keyverify(struct ssh *, int, struct sshbuf *); +int mm_answer_pty(struct ssh *, int, struct sshbuf *); +int mm_answer_pty_cleanup(struct ssh *, int, struct sshbuf *); +int mm_answer_term(struct ssh *, int, struct sshbuf *); +int mm_answer_rsa_keyallowed(struct ssh *, int, struct sshbuf *); +int mm_answer_rsa_challenge(struct ssh *, int, struct sshbuf *); +int mm_answer_rsa_response(struct ssh *, int, struct sshbuf *); +int mm_answer_sesskey(struct ssh *, int, struct sshbuf *); +int mm_answer_sessid(struct ssh *, int, struct sshbuf *); + +#ifdef USE_PAM +int mm_answer_pam_start(struct ssh *, int, struct sshbuf *); +int mm_answer_pam_account(struct ssh *, int, struct sshbuf *); +int mm_answer_pam_init_ctx(struct ssh *, int, struct sshbuf *); +int mm_answer_pam_query(struct ssh *, int, struct sshbuf *); +int mm_answer_pam_respond(struct ssh *, int, struct sshbuf *); +int mm_answer_pam_free_ctx(struct ssh *, int, struct sshbuf *); +#endif + +#ifdef GSSAPI +int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *); +int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *); +int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); +int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); +#endif + +#ifdef SSH_AUDIT_EVENTS +int mm_answer_audit_event(struct ssh *, int, struct sshbuf *); +int mm_answer_audit_command(struct ssh *, int, struct sshbuf *); +#endif + +static Authctxt *authctxt; + +/* local state for key verify */ +static u_char *key_blob = NULL; +static size_t key_bloblen = 0; +static u_int key_blobtype = MM_NOKEY; +static struct sshauthopt *key_opts = NULL; +static char *hostbased_cuser = NULL; +static char *hostbased_chost = NULL; +static char *auth_method = "unknown"; +static char *auth_submethod = NULL; +static u_int session_id2_len = 0; +static u_char *session_id2 = NULL; +static pid_t monitor_child_pid; + +struct mon_table { + enum monitor_reqtype type; + int flags; + int (*f)(struct ssh *, int, struct sshbuf *); +}; + +#define MON_ISAUTH 0x0004 /* Required for Authentication */ +#define MON_AUTHDECIDE 0x0008 /* Decides Authentication */ +#define MON_ONCE 0x0010 /* Disable after calling */ +#define MON_ALOG 0x0020 /* Log auth attempt without authenticating */ + +#define MON_AUTH (MON_ISAUTH|MON_AUTHDECIDE) + +#define MON_PERMIT 0x1000 /* Request is permitted */ + +static int monitor_read(struct ssh *, struct monitor *, struct mon_table *, + struct mon_table **); +static int monitor_read_log(struct monitor *); + +struct mon_table mon_dispatch_proto20[] = { +#ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, +#endif + {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, + {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, + {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, +#ifdef USE_PAM + {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, + {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, + {MONITOR_REQ_PAM_INIT_CTX, MON_ONCE, mm_answer_pam_init_ctx}, + {MONITOR_REQ_PAM_QUERY, 0, mm_answer_pam_query}, + {MONITOR_REQ_PAM_RESPOND, MON_ONCE, mm_answer_pam_respond}, + {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, +#endif +#ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, +#endif +#ifdef BSD_AUTH + {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, + {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, +#endif + {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, + {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, +#ifdef GSSAPI + {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, +#endif + {0, 0, NULL} +}; + +struct mon_table mon_dispatch_postauth20[] = { +#ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, +#endif + {MONITOR_REQ_SIGN, 0, mm_answer_sign}, + {MONITOR_REQ_PTY, 0, mm_answer_pty}, + {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, + {MONITOR_REQ_TERM, 0, mm_answer_term}, +#ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, + {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, +#endif + {0, 0, NULL} +}; + +struct mon_table *mon_dispatch; + +/* Specifies if a certain message is allowed at the moment */ +static void +monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) +{ + while (ent->f != NULL) { + if (ent->type == type) { + ent->flags &= ~MON_PERMIT; + ent->flags |= permit ? MON_PERMIT : 0; + return; + } + ent++; + } +} + +static void +monitor_permit_authentications(int permit) +{ + struct mon_table *ent = mon_dispatch; + + while (ent->f != NULL) { + if (ent->flags & MON_AUTH) { + ent->flags &= ~MON_PERMIT; + ent->flags |= permit ? MON_PERMIT : 0; + } + ent++; + } +} + +void +monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor) +{ + struct mon_table *ent; + int authenticated = 0, partial = 0; + + debug3("preauth child monitor started"); + + if (pmonitor->m_recvfd >= 0) + close(pmonitor->m_recvfd); + if (pmonitor->m_log_sendfd >= 0) + close(pmonitor->m_log_sendfd); + pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1; + + authctxt = (Authctxt *)ssh->authctxt; + memset(authctxt, 0, sizeof(*authctxt)); + ssh->authctxt = authctxt; + + authctxt->loginmsg = loginmsg; + + mon_dispatch = mon_dispatch_proto20; + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + + /* The first few requests do not require asynchronous access */ + while (!authenticated) { + partial = 0; + auth_method = "unknown"; + auth_submethod = NULL; + auth2_authctxt_reset_info(authctxt); + + authenticated = (monitor_read(ssh, pmonitor, + mon_dispatch, &ent) == 1); + + /* Special handling for multiple required authentications */ + if (options.num_auth_methods != 0) { + if (authenticated && + !auth2_update_methods_lists(authctxt, + auth_method, auth_submethod)) { + debug3_f("method %s: partial", auth_method); + authenticated = 0; + partial = 1; + } + } + + if (authenticated) { + if (!(ent->flags & MON_AUTHDECIDE)) + fatal_f("unexpected authentication from %d", + ent->type); + if (authctxt->pw->pw_uid == 0 && + !auth_root_allowed(ssh, auth_method)) + authenticated = 0; +#ifdef USE_PAM + /* PAM needs to perform account checks after auth */ + if (options.use_pam && authenticated) { + struct sshbuf *m; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", + __func__); + mm_request_receive_expect(pmonitor->m_sendfd, + MONITOR_REQ_PAM_ACCOUNT, m); + authenticated = mm_answer_pam_account( + ssh, pmonitor->m_sendfd, m); + sshbuf_free(m); + } +#endif + } + if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { + auth_log(ssh, authenticated, partial, + auth_method, auth_submethod); + if (!partial && !authenticated) + authctxt->failures++; + if (authenticated || partial) { + auth2_update_session_info(authctxt, + auth_method, auth_submethod); + } + } + } + + if (!authctxt->valid) + fatal_f("authenticated invalid user"); + if (strcmp(auth_method, "unknown") == 0) + fatal_f("authentication method name unknown"); + + debug_f("user %s authenticated by privileged process", authctxt->user); + ssh->authctxt = NULL; + ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); + + mm_get_keystate(ssh, pmonitor); + + /* Drain any buffered messages from the child */ + while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0) + ; + + if (pmonitor->m_recvfd >= 0) + close(pmonitor->m_recvfd); + if (pmonitor->m_log_sendfd >= 0) + close(pmonitor->m_log_sendfd); + pmonitor->m_sendfd = pmonitor->m_log_recvfd = -1; +} + +static void +monitor_set_child_handler(pid_t pid) +{ + monitor_child_pid = pid; +} + +static void +monitor_child_handler(int sig) +{ + kill(monitor_child_pid, sig); +} + +void +monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor) +{ + close(pmonitor->m_recvfd); + pmonitor->m_recvfd = -1; + + monitor_set_child_handler(pmonitor->m_pid); + ssh_signal(SIGHUP, &monitor_child_handler); + ssh_signal(SIGTERM, &monitor_child_handler); + ssh_signal(SIGINT, &monitor_child_handler); +#ifdef SIGXFSZ + ssh_signal(SIGXFSZ, SIG_IGN); +#endif + + mon_dispatch = mon_dispatch_postauth20; + + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); + + if (auth_opts->permit_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); + } + + for (;;) + monitor_read(ssh, pmonitor, mon_dispatch, NULL); +} + +static int +monitor_read_log(struct monitor *pmonitor) +{ + struct sshbuf *logmsg; + u_int len, level, forced; + char *msg; + u_char *p; + int r; + + if ((logmsg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + + /* Read length */ + if ((r = sshbuf_reserve(logmsg, 4, &p)) != 0) + fatal_fr(r, "reserve len"); + if (atomicio(read, pmonitor->m_log_recvfd, p, 4) != 4) { + if (errno == EPIPE) { + sshbuf_free(logmsg); + debug_f("child log fd closed"); + close(pmonitor->m_log_recvfd); + pmonitor->m_log_recvfd = -1; + return -1; + } + fatal_f("log fd read: %s", strerror(errno)); + } + if ((r = sshbuf_get_u32(logmsg, &len)) != 0) + fatal_fr(r, "parse len"); + if (len <= 4 || len > 8192) + fatal_f("invalid log message length %u", len); + + /* Read severity, message */ + sshbuf_reset(logmsg); + if ((r = sshbuf_reserve(logmsg, len, &p)) != 0) + fatal_fr(r, "reserve msg"); + if (atomicio(read, pmonitor->m_log_recvfd, p, len) != len) + fatal_f("log fd read: %s", strerror(errno)); + if ((r = sshbuf_get_u32(logmsg, &level)) != 0 || + (r = sshbuf_get_u32(logmsg, &forced)) != 0 || + (r = sshbuf_get_cstring(logmsg, &msg, NULL)) != 0) + fatal_fr(r, "parse"); + + /* Log it */ + if (log_level_name(level) == NULL) + fatal_f("invalid log level %u (corrupted message?)", level); + sshlogdirect(level, forced, "%s [preauth]", msg); + + sshbuf_free(logmsg); + free(msg); + + return 0; +} + +static int +monitor_read(struct ssh *ssh, struct monitor *pmonitor, struct mon_table *ent, + struct mon_table **pent) +{ + struct sshbuf *m; + int r, ret; + u_char type; + struct pollfd pfd[2]; + + for (;;) { + memset(&pfd, 0, sizeof(pfd)); + pfd[0].fd = pmonitor->m_sendfd; + pfd[0].events = POLLIN; + pfd[1].fd = pmonitor->m_log_recvfd; + pfd[1].events = pfd[1].fd == -1 ? 0 : POLLIN; + if (poll(pfd, pfd[1].fd == -1 ? 1 : 2, -1) == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + fatal_f("poll: %s", strerror(errno)); + } + if (pfd[1].revents) { + /* + * Drain all log messages before processing next + * monitor request. + */ + monitor_read_log(pmonitor); + continue; + } + if (pfd[0].revents) + break; /* Continues below */ + } + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + + mm_request_receive(pmonitor->m_sendfd, m); + if ((r = sshbuf_get_u8(m, &type)) != 0) + fatal_fr(r, "parse type"); + + debug3_f("checking request %d", type); + + while (ent->f != NULL) { + if (ent->type == type) + break; + ent++; + } + + if (ent->f != NULL) { + if (!(ent->flags & MON_PERMIT)) + fatal_f("unpermitted request %d", type); + ret = (*ent->f)(ssh, pmonitor->m_sendfd, m); + sshbuf_free(m); + + /* The child may use this request only once, disable it */ + if (ent->flags & MON_ONCE) { + debug2_f("%d used once, disabling now", type); + ent->flags &= ~MON_PERMIT; + } + + if (pent != NULL) + *pent = ent; + + return ret; + } + + fatal_f("unsupported request: %d", type); + + /* NOTREACHED */ + return (-1); +} + +/* allowed key state */ +static int +monitor_allowed_key(const u_char *blob, u_int bloblen) +{ + /* make sure key is allowed */ + if (key_blob == NULL || key_bloblen != bloblen || + timingsafe_bcmp(key_blob, blob, key_bloblen)) + return (0); + return (1); +} + +static void +monitor_reset_key_state(void) +{ + /* reset state */ + free(key_blob); + free(hostbased_cuser); + free(hostbased_chost); + sshauthopt_free(key_opts); + key_blob = NULL; + key_bloblen = 0; + key_blobtype = MM_NOKEY; + key_opts = NULL; + hostbased_cuser = NULL; + hostbased_chost = NULL; +} + +#ifdef WITH_OPENSSL +int +mm_answer_moduli(struct ssh *ssh, int sock, struct sshbuf *m) +{ + DH *dh; + const BIGNUM *dh_p, *dh_g; + int r; + u_int min, want, max; + + if ((r = sshbuf_get_u32(m, &min)) != 0 || + (r = sshbuf_get_u32(m, &want)) != 0 || + (r = sshbuf_get_u32(m, &max)) != 0) + fatal_fr(r, "parse"); + + debug3_f("got parameters: %d %d %d", min, want, max); + /* We need to check here, too, in case the child got corrupted */ + if (max < min || want < min || max < want) + fatal_f("bad parameters: %d %d %d", min, want, max); + + sshbuf_reset(m); + + dh = choose_dh(min, want, max); + if (dh == NULL) { + if ((r = sshbuf_put_u8(m, 0)) != 0) + fatal_fr(r, "assemble empty"); + return (0); + } else { + /* Send first bignum */ + DH_get0_pqg(dh, &dh_p, NULL, &dh_g); + if ((r = sshbuf_put_u8(m, 1)) != 0 || + (r = sshbuf_put_bignum2(m, dh_p)) != 0 || + (r = sshbuf_put_bignum2(m, dh_g)) != 0) + fatal_fr(r, "assemble"); + + DH_free(dh); + } + mm_request_send(sock, MONITOR_ANS_MODULI, m); + return (0); +} +#endif + +int +mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m) +{ + extern int auth_sock; /* XXX move to state struct? */ + struct sshkey *key; + struct sshbuf *sigbuf = NULL; + u_char *p = NULL, *signature = NULL; + char *alg = NULL; + size_t datlen, siglen, alglen; + int r, is_proof = 0; + u_int keyid, compat; + const char proof_req[] = "hostkeys-prove-00@openssh.com"; + + debug3_f("entering"); + + if ((r = sshbuf_get_u32(m, &keyid)) != 0 || + (r = sshbuf_get_string(m, &p, &datlen)) != 0 || + (r = sshbuf_get_cstring(m, &alg, &alglen)) != 0 || + (r = sshbuf_get_u32(m, &compat)) != 0) + fatal_fr(r, "parse"); + if (keyid > INT_MAX) + fatal_f("invalid key ID"); + + /* + * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), + * SHA384 (48 bytes) and SHA512 (64 bytes). + * + * Otherwise, verify the signature request is for a hostkey + * proof. + * + * XXX perform similar check for KEX signature requests too? + * it's not trivial, since what is signed is the hash, rather + * than the full kex structure... + */ + if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) { + /* + * Construct expected hostkey proof and compare it to what + * the client sent us. + */ + if (session_id2_len == 0) /* hostkeys is never first */ + fatal_f("bad data length: %zu", datlen); + if ((key = get_hostkey_public_by_index(keyid, ssh)) == NULL) + fatal_f("no hostkey for index %d", keyid); + if ((sigbuf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_cstring(sigbuf, proof_req)) != 0 || + (r = sshbuf_put_string(sigbuf, session_id2, + session_id2_len)) != 0 || + (r = sshkey_puts(key, sigbuf)) != 0) + fatal_fr(r, "assemble private key proof"); + if (datlen != sshbuf_len(sigbuf) || + memcmp(p, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf)) != 0) + fatal_f("bad data length: %zu, hostkey proof len %zu", + datlen, sshbuf_len(sigbuf)); + sshbuf_free(sigbuf); + is_proof = 1; + } + + /* save session id, it will be passed on the first call */ + if (session_id2_len == 0) { + session_id2_len = datlen; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, p, session_id2_len); + } + + if ((key = get_hostkey_by_index(keyid)) != NULL) { + if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg, + options.sk_provider, NULL, compat)) != 0) + fatal_fr(r, "sign"); + } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && + auth_sock > 0) { + if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, + p, datlen, alg, compat)) != 0) + fatal_fr(r, "agent sign"); + } else + fatal_f("no hostkey from index %d", keyid); + + debug3_f("%s %s signature len=%zu", alg, + is_proof ? "hostkey proof" : "KEX", siglen); + + sshbuf_reset(m); + if ((r = sshbuf_put_string(m, signature, siglen)) != 0) + fatal_fr(r, "assemble"); + + free(alg); + free(p); + free(signature); + + mm_request_send(sock, MONITOR_ANS_SIGN, m); + + /* Turn on permissions for getpwnam */ + monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); + + return (0); +} + +#define PUTPW(b, id) \ + do { \ + if ((r = sshbuf_put_string(b, \ + &pwent->id, sizeof(pwent->id))) != 0) \ + fatal_fr(r, "assemble %s", #id); \ + } while (0) + +/* Retrieves the password entry and also checks if the user is permitted */ +int +mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m) +{ + struct passwd *pwent; + int r, allowed = 0; + u_int i; + + debug3_f("entering"); + + if (authctxt->attempt++ != 0) + fatal_f("multiple attempts for getpwnam"); + + if ((r = sshbuf_get_cstring(m, &authctxt->user, NULL)) != 0) + fatal_fr(r, "parse"); + + pwent = getpwnamallow(ssh, authctxt->user); + + setproctitle("%s [priv]", pwent ? authctxt->user : "unknown"); + + sshbuf_reset(m); + + if (pwent == NULL) { + if ((r = sshbuf_put_u8(m, 0)) != 0) + fatal_fr(r, "assemble fakepw"); + authctxt->pw = fakepw(); + goto out; + } + + allowed = 1; + authctxt->pw = pwent; + authctxt->valid = 1; + + /* XXX send fake class/dir/shell, etc. */ + if ((r = sshbuf_put_u8(m, 1)) != 0) + fatal_fr(r, "assemble ok"); + PUTPW(m, pw_uid); + PUTPW(m, pw_gid); +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + PUTPW(m, pw_change); +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + PUTPW(m, pw_expire); +#endif + if ((r = sshbuf_put_cstring(m, pwent->pw_name)) != 0 || + (r = sshbuf_put_cstring(m, "*")) != 0 || +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + (r = sshbuf_put_cstring(m, pwent->pw_gecos)) != 0 || +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + (r = sshbuf_put_cstring(m, pwent->pw_class)) != 0 || +#endif + (r = sshbuf_put_cstring(m, pwent->pw_dir)) != 0 || + (r = sshbuf_put_cstring(m, pwent->pw_shell)) != 0) + fatal_fr(r, "assemble pw"); + + out: + ssh_packet_set_log_preamble(ssh, "%suser %s", + authctxt->valid ? "authenticating" : "invalid ", authctxt->user); + if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 0) + fatal_fr(r, "assemble options"); + +#define M_CP_STROPT(x) do { \ + if (options.x != NULL && \ + (r = sshbuf_put_cstring(m, options.x)) != 0) \ + fatal_fr(r, "assemble %s", #x); \ + } while (0) +#define M_CP_STRARRAYOPT(x, nx) do { \ + for (i = 0; i < options.nx; i++) { \ + if ((r = sshbuf_put_cstring(m, options.x[i])) != 0) \ + fatal_fr(r, "assemble %s", #x); \ + } \ + } while (0) + /* See comment in servconf.h */ + COPY_MATCH_STRING_OPTS(); +#undef M_CP_STROPT +#undef M_CP_STRARRAYOPT + + /* Create valid auth method lists */ + if (auth2_setup_methods_lists(authctxt) != 0) { + /* + * The monitor will continue long enough to let the child + * run to its packet_disconnect(), but it must not allow any + * authentication to succeed. + */ + debug_f("no valid authentication method lists"); + } + + debug3_f("sending MONITOR_ANS_PWNAM: %d", allowed); + mm_request_send(sock, MONITOR_ANS_PWNAM, m); + + /* Allow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); + +#ifdef USE_PAM + if (options.use_pam) + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1); +#endif + + return (0); +} + +int mm_answer_auth2_read_banner(struct ssh *ssh, int sock, struct sshbuf *m) +{ + char *banner; + int r; + + sshbuf_reset(m); + banner = auth2_read_banner(); + if ((r = sshbuf_put_cstring(m, banner != NULL ? banner : "")) != 0) + fatal_fr(r, "assemble"); + mm_request_send(sock, MONITOR_ANS_AUTH2_READ_BANNER, m); + free(banner); + + return (0); +} + +int +mm_answer_authserv(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int r; + + monitor_permit_authentications(1); + + if ((r = sshbuf_get_cstring(m, &authctxt->service, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &authctxt->style, NULL)) != 0) + fatal_fr(r, "parse"); + debug3_f("service=%s, style=%s", authctxt->service, authctxt->style); + + if (strlen(authctxt->style) == 0) { + free(authctxt->style); + authctxt->style = NULL; + } + + return (0); +} + +/* + * Check that the key type appears in the supplied pattern list, ignoring + * mismatches in the signature algorithm. (Signature algorithm checks are + * performed in the unprivileged authentication code). + * Returns 1 on success, 0 otherwise. + */ +static int +key_base_type_match(const char *method, const struct sshkey *key, + const char *list) +{ + char *s, *l, *ol = xstrdup(list); + int found = 0; + + l = ol; + for ((s = strsep(&l, ",")); s && *s != '\0'; (s = strsep(&l, ","))) { + if (sshkey_type_from_name(s) == key->type) { + found = 1; + break; + } + } + if (!found) { + error("%s key type %s is not in permitted list %s", method, + sshkey_ssh_name(key), list); + } + + free(ol); + return found; +} + +int +mm_answer_authpassword(struct ssh *ssh, int sock, struct sshbuf *m) +{ + static int call_count; +#if !defined(ANDROID) + char *passwd; +#endif + int r, authenticated; + size_t plen = 0; + + if (!options.password_authentication) + fatal_f("password authentication not enabled"); +#if !defined(ANDROID) + if ((r = sshbuf_get_cstring(m, &passwd, &plen)) != 0) + fatal_fr(r, "parse"); + /* Only authenticate if the context is valid */ + authenticated = options.password_authentication && + auth_password(ssh, passwd); + freezero(passwd, plen); +#else + /* no password authentication in Android. */ + authenticated = 0; +#endif + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) + fatal_fr(r, "assemble"); +#ifdef USE_PAM + if ((r = sshbuf_put_u32(m, sshpam_get_maxtries_reached())) != 0) + fatal_fr(r, "assemble PAM"); +#endif + + debug3("%s: sending result %d", __func__, authenticated); + debug3_f("sending result %d", authenticated); + mm_request_send(sock, MONITOR_ANS_AUTHPASSWORD, m); + + call_count++; + if (plen == 0 && call_count == 1) + auth_method = "none"; + else + auth_method = "password"; + + /* Causes monitor loop to terminate if authenticated */ + return (authenticated); +} + +#ifdef BSD_AUTH +int +mm_answer_bsdauthquery(struct ssh *ssh, int sock, struct sshbuf *m) +{ + char *name, *infotxt; + u_int numprompts, *echo_on, success; + char **prompts; + int r; + + if (!options.kbd_interactive_authentication) + fatal_f("kbd-int authentication not enabled"); + success = bsdauth_query(authctxt, &name, &infotxt, &numprompts, + &prompts, &echo_on) < 0 ? 0 : 1; + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, success)) != 0) + fatal_fr(r, "assemble"); + if (success) { + if ((r = sshbuf_put_cstring(m, prompts[0])) != 0) + fatal_fr(r, "assemble prompt"); + } + + debug3_f("sending challenge success: %u", success); + mm_request_send(sock, MONITOR_ANS_BSDAUTHQUERY, m); + + if (success) { + free(name); + free(infotxt); + free(prompts); + free(echo_on); + } + + return (0); +} + +int +mm_answer_bsdauthrespond(struct ssh *ssh, int sock, struct sshbuf *m) +{ + char *response; + int r, authok; + + if (!options.kbd_interactive_authentication) + fatal_f("kbd-int authentication not enabled"); + if (authctxt->as == NULL) + fatal_f("no bsd auth session"); + + if ((r = sshbuf_get_cstring(m, &response, NULL)) != 0) + fatal_fr(r, "parse"); + authok = options.kbd_interactive_authentication && + auth_userresponse(authctxt->as, response, 0); + authctxt->as = NULL; + debug3_f("<%s> = <%d>", response, authok); + free(response); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authok)) != 0) + fatal_fr(r, "assemble"); + + debug3_f("sending authenticated: %d", authok); + mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); + + auth_method = "keyboard-interactive"; + auth_submethod = "bsdauth"; + + return (authok != 0); +} +#endif + +#ifdef USE_PAM +int +mm_answer_pam_start(struct ssh *ssh, int sock, struct sshbuf *m) +{ + if (!options.use_pam) + fatal("UsePAM not set, but ended up in %s anyway", __func__); + + start_pam(ssh); + + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_ACCOUNT, 1); + if (options.kbd_interactive_authentication) + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_INIT_CTX, 1); + + return (0); +} + +int +mm_answer_pam_account(struct ssh *ssh, int sock, struct sshbuf *m) +{ + u_int ret; + int r; + + if (!options.use_pam) + fatal("%s: PAM not enabled", __func__); + + ret = do_pam_account(); + + if ((r = sshbuf_put_u32(m, ret)) != 0 || + (r = sshbuf_put_stringb(m, loginmsg)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(sock, MONITOR_ANS_PAM_ACCOUNT, m); + + return (ret); +} + +static void *sshpam_ctxt, *sshpam_authok; +extern KbdintDevice sshpam_device; + +int +mm_answer_pam_init_ctx(struct ssh *ssh, int sock, struct sshbuf *m) +{ + u_int ok = 0; + int r; + + debug3("%s", __func__); + if (!options.kbd_interactive_authentication) + fatal("%s: kbd-int authentication not enabled", __func__); + if (sshpam_ctxt != NULL) + fatal("%s: already called", __func__); + sshpam_ctxt = (sshpam_device.init_ctx)(authctxt); + sshpam_authok = NULL; + sshbuf_reset(m); + if (sshpam_ctxt != NULL) { + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_FREE_CTX, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_QUERY, 1); + ok = 1; + } + if ((r = sshbuf_put_u32(m, ok)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + mm_request_send(sock, MONITOR_ANS_PAM_INIT_CTX, m); + return (0); +} + +int +mm_answer_pam_query(struct ssh *ssh, int sock, struct sshbuf *m) +{ + char *name = NULL, *info = NULL, **prompts = NULL; + u_int i, num = 0, *echo_on = 0; + int r, ret; + + debug3("%s", __func__); + sshpam_authok = NULL; + if (sshpam_ctxt == NULL) + fatal("%s: no context", __func__); + ret = (sshpam_device.query)(sshpam_ctxt, &name, &info, + &num, &prompts, &echo_on); + if (ret == 0 && num == 0) + sshpam_authok = sshpam_ctxt; + if (num > 1 || name == NULL || info == NULL) + fatal("sshpam_device.query failed"); + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_RESPOND, 1); + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, ret)) != 0 || + (r = sshbuf_put_cstring(m, name)) != 0 || + (r = sshbuf_put_cstring(m, info)) != 0 || + (r = sshbuf_put_u32(m, sshpam_get_maxtries_reached())) != 0 || + (r = sshbuf_put_u32(m, num)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + free(name); + free(info); + for (i = 0; i < num; ++i) { + if ((r = sshbuf_put_cstring(m, prompts[i])) != 0 || + (r = sshbuf_put_u32(m, echo_on[i])) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + free(prompts[i]); + } + free(prompts); + free(echo_on); + auth_method = "keyboard-interactive"; + auth_submethod = "pam"; + mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m); + return (0); +} + +int +mm_answer_pam_respond(struct ssh *ssh, int sock, struct sshbuf *m) +{ + char **resp; + u_int i, num; + int r, ret; + + debug3("%s", __func__); + if (sshpam_ctxt == NULL) + fatal("%s: no context", __func__); + sshpam_authok = NULL; + if ((r = sshbuf_get_u32(m, &num)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (num > 0) { + resp = xcalloc(num, sizeof(char *)); + for (i = 0; i < num; ++i) { + if ((r = sshbuf_get_cstring(m, &(resp[i]), NULL)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + } + ret = (sshpam_device.respond)(sshpam_ctxt, num, resp); + for (i = 0; i < num; ++i) + free(resp[i]); + free(resp); + } else { + ret = (sshpam_device.respond)(sshpam_ctxt, num, NULL); + } + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, ret)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m); + auth_method = "keyboard-interactive"; + auth_submethod = "pam"; + if (ret == 0) + sshpam_authok = sshpam_ctxt; + return (0); +} + +int +mm_answer_pam_free_ctx(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int r = sshpam_authok != NULL && sshpam_authok == sshpam_ctxt; + + debug3("%s", __func__); + if (sshpam_ctxt == NULL) + fatal("%s: no context", __func__); + (sshpam_device.free_ctx)(sshpam_ctxt); + sshpam_ctxt = sshpam_authok = NULL; + sshbuf_reset(m); + mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m); + /* Allow another attempt */ + monitor_permit(mon_dispatch, MONITOR_REQ_PAM_INIT_CTX, 1); + auth_method = "keyboard-interactive"; + auth_submethod = "pam"; + return r; +} +#endif + +int +mm_answer_keyallowed(struct ssh *ssh, int sock, struct sshbuf *m) +{ + struct sshkey *key = NULL; + char *cuser, *chost; + u_int pubkey_auth_attempt; + u_int type = 0; + int r, allowed = 0; + struct sshauthopt *opts = NULL; + + debug3_f("entering"); + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_cstring(m, &cuser, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || + (r = sshkey_froms(m, &key)) != 0 || + (r = sshbuf_get_u32(m, &pubkey_auth_attempt)) != 0) + fatal_fr(r, "parse"); + + if (key != NULL && authctxt->valid) { + /* These should not make it past the privsep child */ + if (sshkey_type_plain(key->type) == KEY_RSA && + (ssh->compat & SSH_BUG_RSASIGMD5) != 0) + fatal_f("passed a SSH_BUG_RSASIGMD5 key"); + + switch (type) { + case MM_USERKEY: + auth_method = "publickey"; + if (!options.pubkey_authentication) + break; + if (auth2_key_already_used(authctxt, key)) + break; + if (!key_base_type_match(auth_method, key, + options.pubkey_accepted_algos)) + break; + allowed = user_key_allowed(ssh, authctxt->pw, key, + pubkey_auth_attempt, &opts); + break; + case MM_HOSTKEY: + auth_method = "hostbased"; + if (!options.hostbased_authentication) + break; + if (auth2_key_already_used(authctxt, key)) + break; + if (!key_base_type_match(auth_method, key, + options.hostbased_accepted_algos)) + break; + allowed = hostbased_key_allowed(ssh, authctxt->pw, + cuser, chost, key); + auth2_record_info(authctxt, + "client user \"%.100s\", client host \"%.100s\"", + cuser, chost); + break; + default: + fatal_f("unknown key type %u", type); + break; + } + } + + debug3_f("%s authentication%s: %s key is %s", auth_method, + pubkey_auth_attempt ? "" : " test", + (key == NULL || !authctxt->valid) ? "invalid" : sshkey_type(key), + allowed ? "allowed" : "not allowed"); + + auth2_record_key(authctxt, 0, key); + + /* clear temporarily storage (used by verify) */ + monitor_reset_key_state(); + + if (allowed) { + /* Save temporarily for comparison in verify */ + if ((r = sshkey_to_blob(key, &key_blob, &key_bloblen)) != 0) + fatal_fr(r, "sshkey_to_blob"); + key_blobtype = type; + key_opts = opts; + hostbased_cuser = cuser; + hostbased_chost = chost; + } else { + /* Log failed attempt */ + auth_log(ssh, 0, 0, auth_method, NULL); + free(cuser); + free(chost); + } + sshkey_free(key); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, allowed)) != 0) + fatal_fr(r, "assemble"); + if (opts != NULL && (r = sshauthopt_serialise(opts, m, 1)) != 0) + fatal_fr(r, "sshauthopt_serialise"); + mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); + + if (!allowed) + sshauthopt_free(opts); + + return (0); +} + +static int +monitor_valid_userblob(struct ssh *ssh, const u_char *data, u_int datalen) +{ + struct sshbuf *b; + struct sshkey *hostkey = NULL; + const u_char *p; + char *userstyle, *cp; + size_t len; + u_char type; + int hostbound = 0, r, fail = 0; + + if ((b = sshbuf_from(data, datalen)) == NULL) + fatal_f("sshbuf_from"); + + if (ssh->compat & SSH_OLD_SESSIONID) { + p = sshbuf_ptr(b); + len = sshbuf_len(b); + if ((session_id2 == NULL) || + (len < session_id2_len) || + (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) + fail++; + if ((r = sshbuf_consume(b, session_id2_len)) != 0) + fatal_fr(r, "consume"); + } else { + if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) + fatal_fr(r, "parse sessionid"); + if ((session_id2 == NULL) || + (len != session_id2_len) || + (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) + fail++; + } + if ((r = sshbuf_get_u8(b, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != SSH2_MSG_USERAUTH_REQUEST) + fail++; + if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal_fr(r, "parse userstyle"); + xasprintf(&userstyle, "%s%s%s", authctxt->user, + authctxt->style ? ":" : "", + authctxt->style ? authctxt->style : ""); + if (strcmp(userstyle, cp) != 0) { + logit("wrong user name passed to monitor: " + "expected %s != %.100s", userstyle, cp); + fail++; + } + free(userstyle); + free(cp); + if ((r = sshbuf_skip_string(b)) != 0 || /* service */ + (r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal_fr(r, "parse method"); + if (strcmp("publickey", cp) != 0) { + if (strcmp("publickey-hostbound-v00@openssh.com", cp) == 0) + hostbound = 1; + else + fail++; + } + free(cp); + if ((r = sshbuf_get_u8(b, &type)) != 0) + fatal_fr(r, "parse pktype"); + if (type == 0) + fail++; + if ((r = sshbuf_skip_string(b)) != 0 || /* pkalg */ + (r = sshbuf_skip_string(b)) != 0 || /* pkblob */ + (hostbound && (r = sshkey_froms(b, &hostkey)) != 0)) + fatal_fr(r, "parse pk"); + if (sshbuf_len(b) != 0) + fail++; + sshbuf_free(b); + if (hostkey != NULL) { + /* + * Ensure this is actually one of our hostkeys; unfortunately + * can't check ssh->kex->initial_hostkey directly at this point + * as packet state has not yet been exported to monitor. + */ + if (get_hostkey_index(hostkey, 1, ssh) == -1) + fatal_f("hostbound hostkey does not match"); + sshkey_free(hostkey); + } + return (fail == 0); +} + +static int +monitor_valid_hostbasedblob(const u_char *data, u_int datalen, + const char *cuser, const char *chost) +{ + struct sshbuf *b; + const u_char *p; + char *cp, *userstyle; + size_t len; + int r, fail = 0; + u_char type; + + if ((b = sshbuf_from(data, datalen)) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) + fatal_fr(r, "parse sessionid"); + + if ((session_id2 == NULL) || + (len != session_id2_len) || + (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) + fail++; + + if ((r = sshbuf_get_u8(b, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != SSH2_MSG_USERAUTH_REQUEST) + fail++; + if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal_fr(r, "parse userstyle"); + xasprintf(&userstyle, "%s%s%s", authctxt->user, + authctxt->style ? ":" : "", + authctxt->style ? authctxt->style : ""); + if (strcmp(userstyle, cp) != 0) { + logit("wrong user name passed to monitor: " + "expected %s != %.100s", userstyle, cp); + fail++; + } + free(userstyle); + free(cp); + if ((r = sshbuf_skip_string(b)) != 0 || /* service */ + (r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal_fr(r, "parse method"); + if (strcmp(cp, "hostbased") != 0) + fail++; + free(cp); + if ((r = sshbuf_skip_string(b)) != 0 || /* pkalg */ + (r = sshbuf_skip_string(b)) != 0) /* pkblob */ + fatal_fr(r, "parse pk"); + + /* verify client host, strip trailing dot if necessary */ + if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal_fr(r, "parse host"); + if (((len = strlen(cp)) > 0) && cp[len - 1] == '.') + cp[len - 1] = '\0'; + if (strcmp(cp, chost) != 0) + fail++; + free(cp); + + /* verify client user */ + if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal_fr(r, "parse ruser"); + if (strcmp(cp, cuser) != 0) + fail++; + free(cp); + + if (sshbuf_len(b) != 0) + fail++; + sshbuf_free(b); + return (fail == 0); +} + +int +mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m) +{ + struct sshkey *key; + const u_char *signature, *data, *blob; + char *sigalg = NULL, *fp = NULL; + size_t signaturelen, datalen, bloblen; + int r, ret, req_presence = 0, req_verify = 0, valid_data = 0; + int encoded_ret; + struct sshkey_sig_details *sig_details = NULL; + + if ((r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 || + (r = sshbuf_get_string_direct(m, &signature, &signaturelen)) != 0 || + (r = sshbuf_get_string_direct(m, &data, &datalen)) != 0 || + (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0) + fatal_fr(r, "parse"); + + if (hostbased_cuser == NULL || hostbased_chost == NULL || + !monitor_allowed_key(blob, bloblen)) + fatal_f("bad key, not previously allowed"); + + /* Empty signature algorithm means NULL. */ + if (*sigalg == '\0') { + free(sigalg); + sigalg = NULL; + } + + /* XXX use sshkey_froms here; need to change key_blob, etc. */ + if ((r = sshkey_from_blob(blob, bloblen, &key)) != 0) + fatal_fr(r, "parse key"); + + switch (key_blobtype) { + case MM_USERKEY: + valid_data = monitor_valid_userblob(ssh, data, datalen); + auth_method = "publickey"; + break; + case MM_HOSTKEY: + valid_data = monitor_valid_hostbasedblob(data, datalen, + hostbased_cuser, hostbased_chost); + auth_method = "hostbased"; + break; + default: + valid_data = 0; + break; + } + if (!valid_data) + fatal_f("bad %s signature data blob", + key_blobtype == MM_USERKEY ? "userkey" : + (key_blobtype == MM_HOSTKEY ? "hostkey" : "unknown")); + + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + + ret = sshkey_verify(key, signature, signaturelen, data, datalen, + sigalg, ssh->compat, &sig_details); + debug3_f("%s %s signature using %s %s%s%s", auth_method, + sshkey_type(key), sigalg == NULL ? "default" : sigalg, + (ret == 0) ? "verified" : "unverified", + (ret != 0) ? ": " : "", (ret != 0) ? ssh_err(ret) : ""); + + if (ret == 0 && key_blobtype == MM_USERKEY && sig_details != NULL) { + req_presence = (options.pubkey_auth_options & + PUBKEYAUTH_TOUCH_REQUIRED) || + !key_opts->no_require_user_presence; + if (req_presence && + (sig_details->sk_flags & SSH_SK_USER_PRESENCE_REQD) == 0) { + error("public key %s %s signature for %s%s from %.128s " + "port %d rejected: user presence " + "(authenticator touch) requirement not met ", + sshkey_type(key), fp, + authctxt->valid ? "" : "invalid user ", + authctxt->user, ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh)); + ret = SSH_ERR_SIGNATURE_INVALID; + } + req_verify = (options.pubkey_auth_options & + PUBKEYAUTH_VERIFY_REQUIRED) || key_opts->require_verify; + if (req_verify && + (sig_details->sk_flags & SSH_SK_USER_VERIFICATION_REQD) == 0) { + error("public key %s %s signature for %s%s from %.128s " + "port %d rejected: user verification requirement " + "not met ", sshkey_type(key), fp, + authctxt->valid ? "" : "invalid user ", + authctxt->user, ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh)); + ret = SSH_ERR_SIGNATURE_INVALID; + } + } + auth2_record_key(authctxt, ret == 0, key); + + if (key_blobtype == MM_USERKEY) + auth_activate_options(ssh, key_opts); + monitor_reset_key_state(); + + sshbuf_reset(m); + + /* encode ret != 0 as positive integer, since we're sending u32 */ + encoded_ret = (ret != 0); + if ((r = sshbuf_put_u32(m, encoded_ret)) != 0 || + (r = sshbuf_put_u8(m, sig_details != NULL)) != 0) + fatal_fr(r, "assemble"); + if (sig_details != NULL) { + if ((r = sshbuf_put_u32(m, sig_details->sk_counter)) != 0 || + (r = sshbuf_put_u8(m, sig_details->sk_flags)) != 0) + fatal_fr(r, "assemble sk"); + } + sshkey_sig_details_free(sig_details); + mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m); + + free(sigalg); + free(fp); + sshkey_free(key); + + return ret == 0; +} + +static void +mm_record_login(struct ssh *ssh, Session *s, struct passwd *pw) +{ + socklen_t fromlen; + struct sockaddr_storage from; + + /* + * Get IP address of client. If the connection is not a socket, let + * the address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); + if (ssh_packet_connection_is_on_socket(ssh)) { + if (getpeername(ssh_packet_get_connection_in(ssh), + (struct sockaddr *)&from, &fromlen) == -1) { + debug("getpeername: %.100s", strerror(errno)); + cleanup_exit(255); + } + } + /* Record that there was a login on that tty from the remote host. */ + record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, + session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), + (struct sockaddr *)&from, fromlen); +} + +static void +mm_session_close(Session *s) +{ + debug3_f("session %d pid %ld", s->self, (long)s->pid); + if (s->ttyfd != -1) { + debug3_f("tty %s ptyfd %d", s->tty, s->ptyfd); + session_pty_cleanup2(s); + } + session_unused(s->self); +} + +int +mm_answer_pty(struct ssh *ssh, int sock, struct sshbuf *m) +{ + extern struct monitor *pmonitor; + Session *s; + int r, res, fd0; + + debug3_f("entering"); + + sshbuf_reset(m); + s = session_new(); + if (s == NULL) + goto error; + s->authctxt = authctxt; + s->pw = authctxt->pw; + s->pid = pmonitor->m_pid; + res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); + if (res == 0) + goto error; + pty_setowner(authctxt->pw, s->tty); + + if ((r = sshbuf_put_u32(m, 1)) != 0 || + (r = sshbuf_put_cstring(m, s->tty)) != 0) + fatal_fr(r, "assemble"); + + /* We need to trick ttyslot */ + if (dup2(s->ttyfd, 0) == -1) + fatal_f("dup2"); + + mm_record_login(ssh, s, authctxt->pw); + + /* Now we can close the file descriptor again */ + close(0); + + /* send messages generated by record_login */ + if ((r = sshbuf_put_stringb(m, loginmsg)) != 0) + fatal_fr(r, "assemble loginmsg"); + sshbuf_reset(loginmsg); + + mm_request_send(sock, MONITOR_ANS_PTY, m); + + if (mm_send_fd(sock, s->ptyfd) == -1 || + mm_send_fd(sock, s->ttyfd) == -1) + fatal_f("send fds failed"); + + /* make sure nothing uses fd 0 */ + if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) == -1) + fatal_f("open(/dev/null): %s", strerror(errno)); + if (fd0 != 0) + error_f("fd0 %d != 0", fd0); + + /* slave side of pty is not needed */ + close(s->ttyfd); + s->ttyfd = s->ptyfd; + /* no need to dup() because nobody closes ptyfd */ + s->ptymaster = s->ptyfd; + + debug3_f("tty %s ptyfd %d", s->tty, s->ttyfd); + + return (0); + + error: + if (s != NULL) + mm_session_close(s); + if ((r = sshbuf_put_u32(m, 0)) != 0) + fatal_fr(r, "assemble 0"); + mm_request_send(sock, MONITOR_ANS_PTY, m); + return (0); +} + +int +mm_answer_pty_cleanup(struct ssh *ssh, int sock, struct sshbuf *m) +{ + Session *s; + char *tty; + int r; + + debug3_f("entering"); + + if ((r = sshbuf_get_cstring(m, &tty, NULL)) != 0) + fatal_fr(r, "parse tty"); + if ((s = session_by_tty(tty)) != NULL) + mm_session_close(s); + sshbuf_reset(m); + free(tty); + return (0); +} + +int +mm_answer_term(struct ssh *ssh, int sock, struct sshbuf *req) +{ + extern struct monitor *pmonitor; + int res, status; + + debug3_f("tearing down sessions"); + + /* The child is terminating */ + session_destroy_all(ssh, &mm_session_close); + +#ifdef USE_PAM + if (options.use_pam) + sshpam_cleanup(); +#endif + + while (waitpid(pmonitor->m_pid, &status, 0) == -1) + if (errno != EINTR) + exit(1); + + res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; + + /* Terminate process */ + exit(res); +} + +#ifdef SSH_AUDIT_EVENTS +/* Report that an audit event occurred */ +int +mm_answer_audit_event(struct ssh *ssh, int socket, struct sshbuf *m) +{ + u_int n; + ssh_audit_event_t event; + int r; + + debug3("%s entering", __func__); + + if ((r = sshbuf_get_u32(m, &n)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + event = (ssh_audit_event_t)n; + switch (event) { + case SSH_AUTH_FAIL_PUBKEY: + case SSH_AUTH_FAIL_HOSTBASED: + case SSH_AUTH_FAIL_GSSAPI: + case SSH_LOGIN_EXCEED_MAXTRIES: + case SSH_LOGIN_ROOT_DENIED: + case SSH_CONNECTION_CLOSE: + case SSH_INVALID_USER: + audit_event(ssh, event); + break; + default: + fatal("Audit event type %d not permitted", event); + } + + return (0); +} + +int +mm_answer_audit_command(struct ssh *ssh, int socket, struct sshbuf *m) +{ + char *cmd; + int r; + + debug3("%s entering", __func__); + if ((r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + /* sanity check command, if so how? */ + audit_run_command(cmd); + free(cmd); + return (0); +} +#endif /* SSH_AUDIT_EVENTS */ + +void +monitor_clear_keystate(struct ssh *ssh, struct monitor *pmonitor) +{ + ssh_clear_newkeys(ssh, MODE_IN); + ssh_clear_newkeys(ssh, MODE_OUT); + sshbuf_free(child_state); + child_state = NULL; +} + +void +monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor) +{ + struct kex *kex; + int r; + + debug3_f("packet_set_state"); + if ((r = ssh_packet_set_state(ssh, child_state)) != 0) + fatal_fr(r, "packet_set_state"); + sshbuf_free(child_state); + child_state = NULL; + if ((kex = ssh->kex) == NULL) + fatal_f("internal error: ssh->kex == NULL"); + if (session_id2_len != sshbuf_len(ssh->kex->session_id)) { + fatal_f("incorrect session id length %zu (expected %u)", + sshbuf_len(ssh->kex->session_id), session_id2_len); + } + if (memcmp(sshbuf_ptr(ssh->kex->session_id), session_id2, + session_id2_len) != 0) + fatal_f("session ID mismatch"); + /* XXX set callbacks */ +#ifdef WITH_OPENSSL + kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; + kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; + kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; + kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; + kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; +# ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kex_gen_server; +# endif +#endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kex_gen_server; + kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; + kex->host_key_index=&get_hostkey_index; + kex->sign = sshd_hostkey_sign; +} + +/* This function requires careful sanity checking */ + +void +mm_get_keystate(struct ssh *ssh, struct monitor *pmonitor) +{ + debug3_f("Waiting for new keys"); + + if ((child_state = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, + child_state); + debug3_f("GOT new keys"); +} + + +/* XXX */ + +#define FD_CLOSEONEXEC(x) do { \ + if (fcntl(x, F_SETFD, FD_CLOEXEC) == -1) \ + fatal("fcntl(%d, F_SETFD)", x); \ +} while (0) + +static void +monitor_openfds(struct monitor *mon, int do_logfds) +{ + int pair[2]; +#ifdef SO_ZEROIZE + int on = 1; +#endif + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + fatal_f("socketpair: %s", strerror(errno)); +#ifdef SO_ZEROIZE + if (setsockopt(pair[0], SOL_SOCKET, SO_ZEROIZE, &on, sizeof(on)) == -1) + error("setsockopt SO_ZEROIZE(0): %.100s", strerror(errno)); + if (setsockopt(pair[1], SOL_SOCKET, SO_ZEROIZE, &on, sizeof(on)) == -1) + error("setsockopt SO_ZEROIZE(1): %.100s", strerror(errno)); +#endif + FD_CLOSEONEXEC(pair[0]); + FD_CLOSEONEXEC(pair[1]); + mon->m_recvfd = pair[0]; + mon->m_sendfd = pair[1]; + + if (do_logfds) { + if (pipe(pair) == -1) + fatal_f("pipe: %s", strerror(errno)); + FD_CLOSEONEXEC(pair[0]); + FD_CLOSEONEXEC(pair[1]); + mon->m_log_recvfd = pair[0]; + mon->m_log_sendfd = pair[1]; + } else + mon->m_log_recvfd = mon->m_log_sendfd = -1; +} + +#define MM_MEMSIZE 65536 + +struct monitor * +monitor_init(void) +{ + struct monitor *mon; + + mon = xcalloc(1, sizeof(*mon)); + monitor_openfds(mon, 1); + + return mon; +} + +void +monitor_reinit(struct monitor *mon) +{ + monitor_openfds(mon, 0); +} + +#ifdef GSSAPI +int +mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m) +{ + gss_OID_desc goid; + OM_uint32 major; + size_t len; + u_char *p; + int r; + + if (!options.gss_authentication) + fatal_f("GSSAPI authentication not enabled"); + + if ((r = sshbuf_get_string(m, &p, &len)) != 0) + fatal_fr(r, "parse"); + goid.elements = p; + goid.length = len; + + major = ssh_gssapi_server_ctx(&gsscontext, &goid); + + free(goid.elements); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, major)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(sock, MONITOR_ANS_GSSSETUP, m); + + /* Now we have a context, enable the step */ + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 1); + + return (0); +} + +int +mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m) +{ + gss_buffer_desc in; + gss_buffer_desc out = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + OM_uint32 flags = 0; /* GSI needs this */ + int r; + + if (!options.gss_authentication) + fatal_f("GSSAPI authentication not enabled"); + + if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) + fatal_fr(r, "ssh_gssapi_get_buffer_desc"); + major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); + free(in.value); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, major)) != 0 || + (r = sshbuf_put_string(m, out.value, out.length)) != 0 || + (r = sshbuf_put_u32(m, flags)) != 0) + fatal_fr(r, "assemble"); + mm_request_send(sock, MONITOR_ANS_GSSSTEP, m); + + gss_release_buffer(&minor, &out); + + if (major == GSS_S_COMPLETE) { + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); + } + return (0); +} + +int +mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) +{ + gss_buffer_desc gssbuf, mic; + OM_uint32 ret; + int r; + + if (!options.gss_authentication) + fatal_f("GSSAPI authentication not enabled"); + + if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || + (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) + fatal_fr(r, "ssh_gssapi_get_buffer_desc"); + + ret = ssh_gssapi_checkmic(gsscontext, &gssbuf, &mic); + + free(gssbuf.value); + free(mic.value); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, ret)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(sock, MONITOR_ANS_GSSCHECKMIC, m); + + if (!GSS_ERROR(ret)) + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + + return (0); +} + +int +mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int r, authenticated; + const char *displayname; + + if (!options.gss_authentication) + fatal_f("GSSAPI authentication not enabled"); + + authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) + fatal_fr(r, "assemble"); + + debug3_f("sending result %d", authenticated); + mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); + + auth_method = "gssapi-with-mic"; + + if ((displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); + + /* Monitor loop will terminate if authenticated */ + return (authenticated); +} +#endif /* GSSAPI */ + diff --git a/aosp/external/openssh/monitor.h b/aosp/external/openssh/monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..683e5e07163e18828fe9285f383ca63080d7b41e --- /dev/null +++ b/aosp/external/openssh/monitor.h @@ -0,0 +1,95 @@ +/* $OpenBSD: monitor.h,v 1.23 2019/01/19 21:43:56 djm Exp $ */ + +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MONITOR_H_ +#define _MONITOR_H_ + +/* Please keep *_REQ_* values on even numbers and *_ANS_* on odd numbers */ +enum monitor_reqtype { + MONITOR_REQ_MODULI = 0, MONITOR_ANS_MODULI = 1, + MONITOR_REQ_FREE = 2, + MONITOR_REQ_AUTHSERV = 4, + MONITOR_REQ_SIGN = 6, MONITOR_ANS_SIGN = 7, + MONITOR_REQ_PWNAM = 8, MONITOR_ANS_PWNAM = 9, + MONITOR_REQ_AUTH2_READ_BANNER = 10, MONITOR_ANS_AUTH2_READ_BANNER = 11, + MONITOR_REQ_AUTHPASSWORD = 12, MONITOR_ANS_AUTHPASSWORD = 13, + MONITOR_REQ_BSDAUTHQUERY = 14, MONITOR_ANS_BSDAUTHQUERY = 15, + MONITOR_REQ_BSDAUTHRESPOND = 16, MONITOR_ANS_BSDAUTHRESPOND = 17, + MONITOR_REQ_KEYALLOWED = 22, MONITOR_ANS_KEYALLOWED = 23, + MONITOR_REQ_KEYVERIFY = 24, MONITOR_ANS_KEYVERIFY = 25, + MONITOR_REQ_KEYEXPORT = 26, + MONITOR_REQ_PTY = 28, MONITOR_ANS_PTY = 29, + MONITOR_REQ_PTYCLEANUP = 30, + MONITOR_REQ_SESSKEY = 32, MONITOR_ANS_SESSKEY = 33, + MONITOR_REQ_SESSID = 34, + MONITOR_REQ_RSAKEYALLOWED = 36, MONITOR_ANS_RSAKEYALLOWED = 37, + MONITOR_REQ_RSACHALLENGE = 38, MONITOR_ANS_RSACHALLENGE = 39, + MONITOR_REQ_RSARESPONSE = 40, MONITOR_ANS_RSARESPONSE = 41, + MONITOR_REQ_GSSSETUP = 42, MONITOR_ANS_GSSSETUP = 43, + MONITOR_REQ_GSSSTEP = 44, MONITOR_ANS_GSSSTEP = 45, + MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47, + MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49, + MONITOR_REQ_TERM = 50, + + MONITOR_REQ_PAM_START = 100, + MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, + MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105, + MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107, + MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109, + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, + MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, + +}; + +struct ssh; + +struct monitor { + int m_recvfd; + int m_sendfd; + int m_log_recvfd; + int m_log_sendfd; + struct kex **m_pkex; + pid_t m_pid; +}; + +struct monitor *monitor_init(void); +void monitor_reinit(struct monitor *); + +struct Authctxt; +void monitor_child_preauth(struct ssh *, struct monitor *); +void monitor_child_postauth(struct ssh *, struct monitor *); + +void monitor_clear_keystate(struct ssh *, struct monitor *); +void monitor_apply_keystate(struct ssh *, struct monitor *); + +/* Prototypes for request sending and receiving */ +void mm_request_send(int, enum monitor_reqtype, struct sshbuf *); +void mm_request_receive(int, struct sshbuf *); +void mm_request_receive_expect(int, enum monitor_reqtype, struct sshbuf *); +void mm_get_keystate(struct ssh *, struct monitor *); + +#endif /* _MONITOR_H_ */ diff --git a/aosp/external/openssh/monitor_fdpass.c b/aosp/external/openssh/monitor_fdpass.c new file mode 100644 index 0000000000000000000000000000000000000000..a07727a8e743c4159955a995d5a9c1a939dc4881 --- /dev/null +++ b/aosp/external/openssh/monitor_fdpass.c @@ -0,0 +1,185 @@ +/* $OpenBSD: monitor_fdpass.c,v 1.22 2020/10/18 11:32:01 djm Exp $ */ +/* + * Copyright 2001 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_POLL_H +# include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif + +#include "log.h" +#include "monitor_fdpass.h" + +int +mm_send_fd(int sock, int fd) +{ +#if defined(HAVE_SENDMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) + struct msghdr msg; +#ifndef HAVE_ACCRIGHTS_IN_MSGHDR + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; +#endif + struct iovec vec; + char ch = '\0'; + ssize_t n; + struct pollfd pfd; + + memset(&msg, 0, sizeof(msg)); +#ifdef HAVE_ACCRIGHTS_IN_MSGHDR + msg.msg_accrights = (caddr_t)&fd; + msg.msg_accrightslen = sizeof(fd); +#else + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = (caddr_t)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = fd; +#endif + + vec.iov_base = &ch; + vec.iov_len = 1; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + pfd.fd = sock; + pfd.events = POLLOUT; + while ((n = sendmsg(sock, &msg, 0)) == -1 && + (errno == EAGAIN || errno == EINTR)) { + debug3_f("sendmsg(%d): %s", fd, strerror(errno)); + (void)poll(&pfd, 1, -1); + } + if (n == -1) { + error_f("sendmsg(%d): %s", fd, strerror(errno)); + return -1; + } + + if (n != 1) { + error_f("sendmsg: expected sent 1 got %zd", n); + return -1; + } + return 0; +#else + error("%s: file descriptor passing not supported", __func__); + return -1; +#endif +} + +int +mm_receive_fd(int sock) +{ +#if defined(HAVE_RECVMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) + struct msghdr msg; +#ifndef HAVE_ACCRIGHTS_IN_MSGHDR + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; +#endif + struct iovec vec; + ssize_t n; + char ch; + int fd; + struct pollfd pfd; + + memset(&msg, 0, sizeof(msg)); + vec.iov_base = &ch; + vec.iov_len = 1; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; +#ifdef HAVE_ACCRIGHTS_IN_MSGHDR + msg.msg_accrights = (caddr_t)&fd; + msg.msg_accrightslen = sizeof(fd); +#else + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); +#endif + + pfd.fd = sock; + pfd.events = POLLIN; + while ((n = recvmsg(sock, &msg, 0)) == -1 && + (errno == EAGAIN || errno == EINTR)) { + debug3_f("recvmsg: %s", strerror(errno)); + (void)poll(&pfd, 1, -1); + } + if (n == -1) { + error_f("recvmsg: %s", strerror(errno)); + return -1; + } + + if (n != 1) { + error_f("recvmsg: expected received 1 got %zd", n); + return -1; + } + +#ifdef HAVE_ACCRIGHTS_IN_MSGHDR + if (msg.msg_accrightslen != sizeof(fd)) { + error_f("no fd"); + return -1; + } +#else + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL) { + error_f("no message header"); + return -1; + } + +#ifndef BROKEN_CMSG_TYPE + if (cmsg->cmsg_type != SCM_RIGHTS) { + error_f("expected %d got %d", SCM_RIGHTS, cmsg->cmsg_type); + return -1; + } +#endif + fd = (*(int *)CMSG_DATA(cmsg)); +#endif + return fd; +#else + error_f("file descriptor passing not supported"); + return -1; +#endif +} diff --git a/aosp/external/openssh/monitor_fdpass.h b/aosp/external/openssh/monitor_fdpass.h new file mode 100644 index 0000000000000000000000000000000000000000..a4b1f6358969d41fc32a62362b72b2fc8fa48357 --- /dev/null +++ b/aosp/external/openssh/monitor_fdpass.h @@ -0,0 +1,34 @@ +/* $OpenBSD: monitor_fdpass.h,v 1.4 2007/09/04 03:21:03 djm Exp $ */ + +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MM_FDPASS_H_ +#define _MM_FDPASS_H_ + +int mm_send_fd(int, int); +int mm_receive_fd(int); + +#endif /* _MM_FDPASS_H_ */ diff --git a/aosp/external/openssh/monitor_wrap.c b/aosp/external/openssh/monitor_wrap.c new file mode 100644 index 0000000000000000000000000000000000000000..748333c75e598d464eb85b436d60f3daadc1686a --- /dev/null +++ b/aosp/external/openssh/monitor_wrap.c @@ -0,0 +1,1021 @@ +/* $OpenBSD: monitor_wrap.c,v 1.123 2021/04/15 16:24:31 markus Exp $ */ +/* + * Copyright 2002 Niels Provos + * Copyright 2002 Markus Friedl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#endif + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#ifdef WITH_OPENSSL +#include "dh.h" +#endif +#include "sshbuf.h" +#include "sshkey.h" +#include "cipher.h" +#include "kex.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-options.h" +#include "packet.h" +#include "mac.h" +#include "log.h" +#include "auth-pam.h" +#include "monitor.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "atomicio.h" +#include "monitor_fdpass.h" +#include "misc.h" + +#include "channels.h" +#include "session.h" +#include "servconf.h" + +#include "ssherr.h" + +/* Imports */ +extern struct monitor *pmonitor; +extern struct sshbuf *loginmsg; +extern ServerOptions options; + +void +mm_log_handler(LogLevel level, int forced, const char *msg, void *ctx) +{ + struct sshbuf *log_msg; + struct monitor *mon = (struct monitor *)ctx; + int r; + size_t len; + + if (mon->m_log_sendfd == -1) + fatal_f("no log channel"); + + if ((log_msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + if ((r = sshbuf_put_u32(log_msg, 0)) != 0 || /* length; filled below */ + (r = sshbuf_put_u32(log_msg, level)) != 0 || + (r = sshbuf_put_u32(log_msg, forced)) != 0 || + (r = sshbuf_put_cstring(log_msg, msg)) != 0) + fatal_fr(r, "assemble"); + if ((len = sshbuf_len(log_msg)) < 4 || len > 0xffffffff) + fatal_f("bad length %zu", len); + POKE_U32(sshbuf_mutable_ptr(log_msg), len - 4); + if (atomicio(vwrite, mon->m_log_sendfd, + sshbuf_mutable_ptr(log_msg), len) != len) + fatal_f("write: %s", strerror(errno)); + sshbuf_free(log_msg); +} + +int +mm_is_monitor(void) +{ + /* + * m_pid is only set in the privileged part, and + * points to the unprivileged child. + */ + return (pmonitor && pmonitor->m_pid > 0); +} + +void +mm_request_send(int sock, enum monitor_reqtype type, struct sshbuf *m) +{ + size_t mlen = sshbuf_len(m); + u_char buf[5]; + + debug3_f("entering, type %d", type); + + if (mlen >= 0xffffffff) + fatal_f("bad length %zu", mlen); + POKE_U32(buf, mlen + 1); + buf[4] = (u_char) type; /* 1st byte of payload is mesg-type */ + if (atomicio(vwrite, sock, buf, sizeof(buf)) != sizeof(buf)) + fatal_f("write: %s", strerror(errno)); + if (atomicio(vwrite, sock, sshbuf_mutable_ptr(m), mlen) != mlen) + fatal_f("write: %s", strerror(errno)); +} + +void +mm_request_receive(int sock, struct sshbuf *m) +{ + u_char buf[4], *p = NULL; + u_int msg_len; + int r; + + debug3_f("entering"); + + if (atomicio(read, sock, buf, sizeof(buf)) != sizeof(buf)) { + if (errno == EPIPE) + cleanup_exit(255); + fatal_f("read: %s", strerror(errno)); + } + msg_len = PEEK_U32(buf); + if (msg_len > 256 * 1024) + fatal_f("read: bad msg_len %d", msg_len); + sshbuf_reset(m); + if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) + fatal_fr(r, "reserve"); + if (atomicio(read, sock, p, msg_len) != msg_len) + fatal_f("read: %s", strerror(errno)); +} + +void +mm_request_receive_expect(int sock, enum monitor_reqtype type, struct sshbuf *m) +{ + u_char rtype; + int r; + + debug3_f("entering, type %d", type); + + mm_request_receive(sock, m); + if ((r = sshbuf_get_u8(m, &rtype)) != 0) + fatal_fr(r, "parse"); + if (rtype != type) + fatal_f("read: rtype %d != type %d", rtype, type); +} + +#ifdef WITH_OPENSSL +DH * +mm_choose_dh(int min, int nbits, int max) +{ + BIGNUM *p, *g; + int r; + u_char success = 0; + struct sshbuf *m; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u32(m, min)) != 0 || + (r = sshbuf_put_u32(m, nbits)) != 0 || + (r = sshbuf_put_u32(m, max)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_MODULI, m); + + debug3_f("waiting for MONITOR_ANS_MODULI"); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_MODULI, m); + + if ((r = sshbuf_get_u8(m, &success)) != 0) + fatal_fr(r, "parse success"); + if (success == 0) + fatal_f("MONITOR_ANS_MODULI failed"); + + if ((r = sshbuf_get_bignum2(m, &p)) != 0 || + (r = sshbuf_get_bignum2(m, &g)) != 0) + fatal_fr(r, "parse group"); + + debug3_f("remaining %zu", sshbuf_len(m)); + sshbuf_free(m); + + return (dh_new_group(g, p)); +} +#endif + +int +mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, const char *hostkey_alg, + const char *sk_provider, const char *sk_pin, u_int compat) +{ + struct kex *kex = *pmonitor->m_pkex; + struct sshbuf *m; + u_int ndx = kex->host_key_index(key, 0, ssh); + int r; + + debug3_f("entering"); + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u32(m, ndx)) != 0 || + (r = sshbuf_put_string(m, data, datalen)) != 0 || + (r = sshbuf_put_cstring(m, hostkey_alg)) != 0 || + (r = sshbuf_put_u32(m, compat)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SIGN, m); + + debug3_f("waiting for MONITOR_ANS_SIGN"); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, m); + if ((r = sshbuf_get_string(m, sigp, lenp)) != 0) + fatal_fr(r, "parse"); + sshbuf_free(m); + + return (0); +} + +#define GETPW(b, id) \ + do { \ + if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) \ + fatal_fr(r, "parse pw %s", #id); \ + if (len != sizeof(pw->id)) \ + fatal_fr(r, "bad length for %s", #id); \ + memcpy(&pw->id, p, len); \ + } while (0) + +struct passwd * +mm_getpwnamallow(struct ssh *ssh, const char *username) +{ + struct sshbuf *m; + struct passwd *pw; + size_t len; + u_int i; + ServerOptions *newopts; + int r; + u_char ok; + const u_char *p; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_cstring(m, username)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PWNAM, m); + + debug3_f("waiting for MONITOR_ANS_PWNAM"); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PWNAM, m); + + if ((r = sshbuf_get_u8(m, &ok)) != 0) + fatal_fr(r, "parse success"); + if (ok == 0) { + pw = NULL; + goto out; + } + + /* XXX don't like passing struct passwd like this */ + pw = xcalloc(sizeof(*pw), 1); + GETPW(m, pw_uid); + GETPW(m, pw_gid); +#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE + GETPW(m, pw_change); +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + GETPW(m, pw_expire); +#endif + if ((r = sshbuf_get_cstring(m, &pw->pw_name, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &pw->pw_passwd, NULL)) != 0 || +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + (r = sshbuf_get_cstring(m, &pw->pw_gecos, NULL)) != 0 || +#endif +#ifdef HAVE_STRUCT_PASSWD_PW_CLASS + (r = sshbuf_get_cstring(m, &pw->pw_class, NULL)) != 0 || +#endif + (r = sshbuf_get_cstring(m, &pw->pw_dir, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &pw->pw_shell, NULL)) != 0) + fatal_fr(r, "parse pw"); + +out: + /* copy options block as a Match directive may have changed some */ + if ((r = sshbuf_get_string_direct(m, &p, &len)) != 0) + fatal_fr(r, "parse opts"); + if (len != sizeof(*newopts)) + fatal_f("option block size mismatch"); + newopts = xcalloc(sizeof(*newopts), 1); + memcpy(newopts, p, sizeof(*newopts)); + +#define M_CP_STROPT(x) do { \ + if (newopts->x != NULL && \ + (r = sshbuf_get_cstring(m, &newopts->x, NULL)) != 0) \ + fatal_fr(r, "parse %s", #x); \ + } while (0) +#define M_CP_STRARRAYOPT(x, nx) do { \ + newopts->x = newopts->nx == 0 ? \ + NULL : xcalloc(newopts->nx, sizeof(*newopts->x)); \ + for (i = 0; i < newopts->nx; i++) { \ + if ((r = sshbuf_get_cstring(m, \ + &newopts->x[i], NULL)) != 0) \ + fatal_fr(r, "parse %s", #x); \ + } \ + } while (0) + /* See comment in servconf.h */ + COPY_MATCH_STRING_OPTS(); +#undef M_CP_STROPT +#undef M_CP_STRARRAYOPT + + copy_set_server_options(&options, newopts, 1); + log_change_level(options.log_level); + log_verbose_reset(); + for (i = 0; i < options.num_log_verbose; i++) + log_verbose_add(options.log_verbose[i]); + process_permitopen(ssh, &options); + free(newopts); + + sshbuf_free(m); + + return (pw); +} + +char * +mm_auth2_read_banner(void) +{ + struct sshbuf *m; + char *banner; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTH2_READ_BANNER, m); + sshbuf_reset(m); + + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_AUTH2_READ_BANNER, m); + if ((r = sshbuf_get_cstring(m, &banner, NULL)) != 0) + fatal_fr(r, "parse"); + sshbuf_free(m); + + /* treat empty banner as missing banner */ + if (strlen(banner) == 0) { + free(banner); + banner = NULL; + } + return (banner); +} + +/* Inform the privileged process about service and style */ + +void +mm_inform_authserv(char *service, char *style) +{ + struct sshbuf *m; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_cstring(m, service)) != 0 || + (r = sshbuf_put_cstring(m, style ? style : "")) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, m); + + sshbuf_free(m); +} + +/* Do the password authentication */ +int +mm_auth_password(struct ssh *ssh, char *password) +{ + struct sshbuf *m; + int r, authenticated = 0; +#ifdef USE_PAM + u_int maxtries = 0; +#endif + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_cstring(m, password)) != 0) + fatal_fr(r, "assemble"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHPASSWORD, m); + + debug3_f("waiting for MONITOR_ANS_AUTHPASSWORD"); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_AUTHPASSWORD, m); + + if ((r = sshbuf_get_u32(m, &authenticated)) != 0) + fatal_fr(r, "parse"); +#ifdef USE_PAM + if ((r = sshbuf_get_u32(m, &maxtries)) != 0) + fatal_fr(r, "parse PAM"); + if (maxtries > INT_MAX) + fatal_fr(r, "bad maxtries"); + sshpam_set_maxtries_reached(maxtries); +#endif + + sshbuf_free(m); + + debug3_f("user %sauthenticated", authenticated ? "" : "not "); + return (authenticated); +} + +int +mm_user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, + int pubkey_auth_attempt, struct sshauthopt **authoptp) +{ + return (mm_key_allowed(MM_USERKEY, NULL, NULL, key, + pubkey_auth_attempt, authoptp)); +} + +int +mm_hostbased_key_allowed(struct ssh *ssh, struct passwd *pw, + const char *user, const char *host, struct sshkey *key) +{ + return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0, NULL)); +} + +int +mm_key_allowed(enum mm_keytype type, const char *user, const char *host, + struct sshkey *key, int pubkey_auth_attempt, struct sshauthopt **authoptp) +{ + struct sshbuf *m; + int r, allowed = 0; + struct sshauthopt *opts = NULL; + + debug3_f("entering"); + + if (authoptp != NULL) + *authoptp = NULL; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u32(m, type)) != 0 || + (r = sshbuf_put_cstring(m, user ? user : "")) != 0 || + (r = sshbuf_put_cstring(m, host ? host : "")) != 0 || + (r = sshkey_puts(key, m)) != 0 || + (r = sshbuf_put_u32(m, pubkey_auth_attempt)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYALLOWED, m); + + debug3_f("waiting for MONITOR_ANS_KEYALLOWED"); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_KEYALLOWED, m); + + if ((r = sshbuf_get_u32(m, &allowed)) != 0) + fatal_fr(r, "parse"); + if (allowed && type == MM_USERKEY && + (r = sshauthopt_deserialise(m, &opts)) != 0) + fatal_fr(r, "sshauthopt_deserialise"); + sshbuf_free(m); + + if (authoptp != NULL) { + *authoptp = opts; + opts = NULL; + } + sshauthopt_free(opts); + + return allowed; +} + +/* + * This key verify needs to send the key type along, because the + * privileged parent makes the decision if the key is allowed + * for authentication. + */ + +int +mm_sshkey_verify(const struct sshkey *key, const u_char *sig, size_t siglen, + const u_char *data, size_t datalen, const char *sigalg, u_int compat, + struct sshkey_sig_details **sig_detailsp) +{ + struct sshbuf *m; + u_int encoded_ret = 0; + int r; + u_char sig_details_present, flags; + u_int counter; + + debug3_f("entering"); + + if (sig_detailsp != NULL) + *sig_detailsp = NULL; + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshkey_puts(key, m)) != 0 || + (r = sshbuf_put_string(m, sig, siglen)) != 0 || + (r = sshbuf_put_string(m, data, datalen)) != 0 || + (r = sshbuf_put_cstring(m, sigalg == NULL ? "" : sigalg)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYVERIFY, m); + + debug3_f("waiting for MONITOR_ANS_KEYVERIFY"); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_KEYVERIFY, m); + + if ((r = sshbuf_get_u32(m, &encoded_ret)) != 0 || + (r = sshbuf_get_u8(m, &sig_details_present)) != 0) + fatal_fr(r, "parse"); + if (sig_details_present && encoded_ret == 0) { + if ((r = sshbuf_get_u32(m, &counter)) != 0 || + (r = sshbuf_get_u8(m, &flags)) != 0) + fatal_fr(r, "parse sig_details"); + if (sig_detailsp != NULL) { + *sig_detailsp = xcalloc(1, sizeof(**sig_detailsp)); + (*sig_detailsp)->sk_counter = counter; + (*sig_detailsp)->sk_flags = flags; + } + } + + sshbuf_free(m); + + if (encoded_ret != 0) + return SSH_ERR_SIGNATURE_INVALID; + return 0; +} + +void +mm_send_keystate(struct ssh *ssh, struct monitor *monitor) +{ + struct sshbuf *m; + int r; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = ssh_packet_get_state(ssh, m)) != 0) + fatal_fr(r, "ssh_packet_get_state"); + mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, m); + debug3_f("Finished sending state"); + sshbuf_free(m); +} + +int +mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen) +{ + struct sshbuf *m; + char *p, *msg; + int success = 0, tmp1 = -1, tmp2 = -1, r; + + /* Kludge: ensure there are fds free to receive the pty/tty */ + if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 || + (tmp2 = dup(pmonitor->m_recvfd)) == -1) { + error_f("cannot allocate fds for pty"); + if (tmp1 > 0) + close(tmp1); + if (tmp2 > 0) + close(tmp2); + return 0; + } + close(tmp1); + close(tmp2); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, m); + + debug3_f("waiting for MONITOR_ANS_PTY"); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PTY, m); + + if ((r = sshbuf_get_u32(m, &success)) != 0) + fatal_fr(r, "parse success"); + if (success == 0) { + debug3_f("pty alloc failed"); + sshbuf_free(m); + return (0); + } + if ((r = sshbuf_get_cstring(m, &p, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &msg, NULL)) != 0) + fatal_fr(r, "parse"); + sshbuf_free(m); + + strlcpy(namebuf, p, namebuflen); /* Possible truncation */ + free(p); + + if ((r = sshbuf_put(loginmsg, msg, strlen(msg))) != 0) + fatal_fr(r, "put loginmsg"); + free(msg); + + if ((*ptyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1 || + (*ttyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1) + fatal_f("receive fds failed"); + + /* Success */ + return (1); +} + +void +mm_session_pty_cleanup2(Session *s) +{ + struct sshbuf *m; + int r; + + if (s->ttyfd == -1) + return; + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_cstring(m, s->tty)) != 0) + fatal_fr(r, "assmble"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTYCLEANUP, m); + sshbuf_free(m); + + /* closed dup'ed master */ + if (s->ptymaster != -1 && close(s->ptymaster) == -1) + error("close(s->ptymaster/%d): %s", + s->ptymaster, strerror(errno)); + + /* unlink pty from session */ + s->ttyfd = -1; +} + +#ifdef USE_PAM +void +mm_start_pam(struct ssh *ssh) +{ + struct sshbuf *m; + + debug3("%s entering", __func__); + if (!options.use_pam) + fatal("UsePAM=no, but ended up in %s anyway", __func__); + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_START, m); + + sshbuf_free(m); +} + +u_int +mm_do_pam_account(void) +{ + struct sshbuf *m; + u_int ret; + char *msg; + size_t msglen; + int r; + + debug3("%s entering", __func__); + if (!options.use_pam) + fatal("UsePAM=no, but ended up in %s anyway", __func__); + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_ACCOUNT, m); + + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_PAM_ACCOUNT, m); + if ((r = sshbuf_get_u32(m, &ret)) != 0 || + (r = sshbuf_get_cstring(m, &msg, &msglen)) != 0 || + (r = sshbuf_put(loginmsg, msg, msglen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + free(msg); + sshbuf_free(m); + + debug3("%s returning %d", __func__, ret); + + return (ret); +} + +void * +mm_sshpam_init_ctx(Authctxt *authctxt) +{ + struct sshbuf *m; + int r, success; + + debug3("%s", __func__); + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_INIT_CTX, m); + debug3("%s: waiting for MONITOR_ANS_PAM_INIT_CTX", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_PAM_INIT_CTX, m); + if ((r = sshbuf_get_u32(m, &success)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (success == 0) { + debug3("%s: pam_init_ctx failed", __func__); + sshbuf_free(m); + return (NULL); + } + sshbuf_free(m); + return (authctxt); +} + +int +mm_sshpam_query(void *ctx, char **name, char **info, + u_int *num, char ***prompts, u_int **echo_on) +{ + struct sshbuf *m; + u_int i, n; + int r, ret; + + debug3("%s", __func__); + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_QUERY, m); + debug3("%s: waiting for MONITOR_ANS_PAM_QUERY", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_QUERY, m); + if ((r = sshbuf_get_u32(m, &ret)) != 0 || + (r = sshbuf_get_cstring(m, name, NULL)) != 0 || + (r = sshbuf_get_cstring(m, info, NULL)) != 0 || + (r = sshbuf_get_u32(m, &n)) != 0 || + (r = sshbuf_get_u32(m, num)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + debug3("%s: pam_query returned %d", __func__, ret); + sshpam_set_maxtries_reached(n); + if (*num > PAM_MAX_NUM_MSG) + fatal("%s: received %u PAM messages, expected <= %u", + __func__, *num, PAM_MAX_NUM_MSG); + *prompts = xcalloc((*num + 1), sizeof(char *)); + *echo_on = xcalloc((*num + 1), sizeof(u_int)); + for (i = 0; i < *num; ++i) { + if ((r = sshbuf_get_cstring(m, &((*prompts)[i]), NULL)) != 0 || + (r = sshbuf_get_u32(m, &((*echo_on)[i]))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + sshbuf_free(m); + return (ret); +} + +int +mm_sshpam_respond(void *ctx, u_int num, char **resp) +{ + struct sshbuf *m; + u_int n, i; + int r, ret; + + debug3("%s", __func__); + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshbuf_put_u32(m, num)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + for (i = 0; i < num; ++i) { + if ((r = sshbuf_put_cstring(m, resp[i])) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_RESPOND, m); + debug3("%s: waiting for MONITOR_ANS_PAM_RESPOND", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_PAM_RESPOND, m); + if ((r = sshbuf_get_u32(m, &n)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + ret = (int)n; /* XXX */ + debug3("%s: pam_respond returned %d", __func__, ret); + sshbuf_free(m); + return (ret); +} + +void +mm_sshpam_free_ctx(void *ctxtp) +{ + struct sshbuf *m; + + debug3("%s", __func__); + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_FREE_CTX, m); + debug3("%s: waiting for MONITOR_ANS_PAM_FREE_CTX", __func__); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_PAM_FREE_CTX, m); + sshbuf_free(m); +} +#endif /* USE_PAM */ + +/* Request process termination */ + +void +mm_terminate(void) +{ + struct sshbuf *m; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_TERM, m); + sshbuf_free(m); +} + +static void +mm_chall_setup(char **name, char **infotxt, u_int *numprompts, + char ***prompts, u_int **echo_on) +{ + *name = xstrdup(""); + *infotxt = xstrdup(""); + *numprompts = 1; + *prompts = xcalloc(*numprompts, sizeof(char *)); + *echo_on = xcalloc(*numprompts, sizeof(u_int)); + (*echo_on)[0] = 0; +} + +int +mm_bsdauth_query(void *ctx, char **name, char **infotxt, + u_int *numprompts, char ***prompts, u_int **echo_on) +{ + struct sshbuf *m; + u_int success; + char *challenge; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHQUERY, m); + + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_BSDAUTHQUERY, m); + if ((r = sshbuf_get_u32(m, &success)) != 0) + fatal_fr(r, "parse success"); + if (success == 0) { + debug3_f("no challenge"); + sshbuf_free(m); + return (-1); + } + + /* Get the challenge, and format the response */ + if ((r = sshbuf_get_cstring(m, &challenge, NULL)) != 0) + fatal_fr(r, "parse challenge"); + sshbuf_free(m); + + mm_chall_setup(name, infotxt, numprompts, prompts, echo_on); + (*prompts)[0] = challenge; + + debug3_f("received challenge: %s", challenge); + + return (0); +} + +int +mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses) +{ + struct sshbuf *m; + int r, authok; + + debug3_f("entering"); + if (numresponses != 1) + return (-1); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_cstring(m, responses[0])) != 0) + fatal_fr(r, "assemble"); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHRESPOND, m); + + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_BSDAUTHRESPOND, m); + + if ((r = sshbuf_get_u32(m, &authok)) != 0) + fatal_fr(r, "parse"); + sshbuf_free(m); + + return ((authok == 0) ? -1 : 0); +} + +#ifdef SSH_AUDIT_EVENTS +void +mm_audit_event(struct ssh *ssh, ssh_audit_event_t event) +{ + struct sshbuf *m; + int r; + + debug3("%s entering", __func__); + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshbuf_put_u32(m, event)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, m); + sshbuf_free(m); +} + +void +mm_audit_run_command(const char *command) +{ + struct sshbuf *m; + int r; + + debug3("%s entering command %s", __func__, command); + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshbuf_put_cstring(m, command)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, m); + sshbuf_free(m); +} +#endif /* SSH_AUDIT_EVENTS */ + +#ifdef GSSAPI +OM_uint32 +mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid) +{ + struct sshbuf *m; + OM_uint32 major; + int r; + + /* Client doesn't get to see the context */ + *ctx = NULL; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_string(m, goid->elements, goid->length)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSETUP, m); + + if ((r = sshbuf_get_u32(m, &major)) != 0) + fatal_fr(r, "parse"); + + sshbuf_free(m); + return (major); +} + +OM_uint32 +mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in, + gss_buffer_desc *out, OM_uint32 *flagsp) +{ + struct sshbuf *m; + OM_uint32 major; + u_int flags; + int r; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_string(m, in->value, in->length)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, m); + + if ((r = sshbuf_get_u32(m, &major)) != 0 || + (r = ssh_gssapi_get_buffer_desc(m, out)) != 0) + fatal_fr(r, "parse"); + if (flagsp != NULL) { + if ((r = sshbuf_get_u32(m, &flags)) != 0) + fatal_fr(r, "parse flags"); + *flagsp = flags; + } + + sshbuf_free(m); + + return (major); +} + +OM_uint32 +mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) +{ + struct sshbuf *m; + OM_uint32 major; + int r; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_string(m, gssbuf->value, gssbuf->length)) != 0 || + (r = sshbuf_put_string(m, gssmic->value, gssmic->length)) != 0) + fatal_fr(r, "assemble"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSCHECKMIC, m); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_GSSCHECKMIC, m); + + if ((r = sshbuf_get_u32(m, &major)) != 0) + fatal_fr(r, "parse"); + sshbuf_free(m); + return(major); +} + +int +mm_ssh_gssapi_userok(char *user) +{ + struct sshbuf *m; + int r, authenticated = 0; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); + mm_request_receive_expect(pmonitor->m_recvfd, + MONITOR_ANS_GSSUSEROK, m); + + if ((r = sshbuf_get_u32(m, &authenticated)) != 0) + fatal_fr(r, "parse"); + + sshbuf_free(m); + debug3_f("user %sauthenticated", authenticated ? "" : "not "); + return (authenticated); +} +#endif /* GSSAPI */ diff --git a/aosp/external/openssh/monitor_wrap.h b/aosp/external/openssh/monitor_wrap.h new file mode 100644 index 0000000000000000000000000000000000000000..a163b67d2878cdfd4b5caef1265e7ed179850a6c --- /dev/null +++ b/aosp/external/openssh/monitor_wrap.h @@ -0,0 +1,102 @@ +/* $OpenBSD: monitor_wrap.h,v 1.47 2021/04/15 16:24:31 markus Exp $ */ + +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MM_WRAP_H_ +#define _MM_WRAP_H_ + +extern int use_privsep; +#define PRIVSEP(x) (use_privsep ? mm_##x : x) + +enum mm_keytype { MM_NOKEY, MM_HOSTKEY, MM_USERKEY }; + +struct ssh; +struct monitor; +struct Authctxt; +struct sshkey; +struct sshauthopt; +struct sshkey_sig_details; + +void mm_log_handler(LogLevel, int, const char *, void *); +int mm_is_monitor(void); +#ifdef WITH_OPENSSL +DH *mm_choose_dh(int, int, int); +#endif +int mm_sshkey_sign(struct ssh *, struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, const char *, + const char *, u_int compat); +void mm_inform_authserv(char *, char *); +struct passwd *mm_getpwnamallow(struct ssh *, const char *); +char *mm_auth2_read_banner(void); +int mm_auth_password(struct ssh *, char *); +int mm_key_allowed(enum mm_keytype, const char *, const char *, struct sshkey *, + int, struct sshauthopt **); +int mm_user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int, + struct sshauthopt **); +int mm_hostbased_key_allowed(struct ssh *, struct passwd *, const char *, + const char *, struct sshkey *); +int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **); + +#ifdef GSSAPI +OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +int mm_ssh_gssapi_userok(char *user); +OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); +#endif + +#ifdef USE_PAM +void mm_start_pam(struct ssh *ssh); +u_int mm_do_pam_account(void); +void *mm_sshpam_init_ctx(struct Authctxt *); +int mm_sshpam_query(void *, char **, char **, u_int *, char ***, u_int **); +int mm_sshpam_respond(void *, u_int, char **); +void mm_sshpam_free_ctx(void *); +#endif + +#ifdef SSH_AUDIT_EVENTS +#include "audit.h" +void mm_audit_event(struct ssh *, ssh_audit_event_t); +void mm_audit_run_command(const char *); +#endif + +struct Session; +void mm_terminate(void); +int mm_pty_allocate(int *, int *, char *, size_t); +void mm_session_pty_cleanup2(struct Session *); + +/* Key export functions */ +struct newkeys *mm_newkeys_from_blob(u_char *, int); +int mm_newkeys_to_blob(int, u_char **, u_int *); + +void mm_send_keystate(struct ssh *, struct monitor*); + +/* bsdauth */ +int mm_bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **); +int mm_bsdauth_respond(void *, u_int, char **); + +#endif /* _MM_WRAP_H_ */ diff --git a/aosp/external/openssh/msg.c b/aosp/external/openssh/msg.c new file mode 100644 index 0000000000000000000000000000000000000000..d22c4e477d1c836389fff6c0b126251dfbbb7713 --- /dev/null +++ b/aosp/external/openssh/msg.c @@ -0,0 +1,94 @@ +/* $OpenBSD: msg.c,v 1.20 2020/10/18 11:32:01 djm Exp $ */ +/* + * Copyright (c) 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "sshbuf.h" +#include "ssherr.h" +#include "log.h" +#include "atomicio.h" +#include "msg.h" +#include "misc.h" + +int +ssh_msg_send(int fd, u_char type, struct sshbuf *m) +{ + u_char buf[5]; + u_int mlen = sshbuf_len(m); + + debug3_f("type %u", (unsigned int)type & 0xff); + + put_u32(buf, mlen + 1); + buf[4] = type; /* 1st byte of payload is mesg-type */ + if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) { + error_f("write: %s", strerror(errno)); + return (-1); + } + if (atomicio(vwrite, fd, sshbuf_mutable_ptr(m), mlen) != mlen) { + error_f("write: %s", strerror(errno)); + return (-1); + } + return (0); +} + +int +ssh_msg_recv(int fd, struct sshbuf *m) +{ + u_char buf[4], *p; + u_int msg_len; + int r; + + debug3("ssh_msg_recv entering"); + + if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) { + if (errno != EPIPE) + error_f("read header: %s", strerror(errno)); + return (-1); + } + msg_len = get_u32(buf); + if (msg_len > sshbuf_max_size(m)) { + error_f("read: bad msg_len %u", msg_len); + return (-1); + } + sshbuf_reset(m); + if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) { + error_fr(r, "reserve"); + return -1; + } + if (atomicio(read, fd, p, msg_len) != msg_len) { + error_f("read: %s", strerror(errno)); + return (-1); + } + return (0); +} diff --git a/aosp/external/openssh/msg.h b/aosp/external/openssh/msg.h new file mode 100644 index 0000000000000000000000000000000000000000..dfb34247c6e960d0ebb003f36d170506c557f87d --- /dev/null +++ b/aosp/external/openssh/msg.h @@ -0,0 +1,32 @@ +/* $OpenBSD: msg.h,v 1.5 2015/01/15 09:40:00 djm Exp $ */ +/* + * Copyright (c) 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SSH_MSG_H +#define SSH_MSG_H + +struct sshbuf; +int ssh_msg_send(int, u_char, struct sshbuf *); +int ssh_msg_recv(int, struct sshbuf *); + +#endif diff --git a/aosp/external/openssh/mux.c b/aosp/external/openssh/mux.c new file mode 100644 index 0000000000000000000000000000000000000000..176f035c86f5cf4049cc0dc039e8802488a9b7c5 --- /dev/null +++ b/aosp/external/openssh/mux.c @@ -0,0 +1,2343 @@ +/* $OpenBSD: mux.c,v 1.92 2022/01/11 01:26:47 djm Exp $ */ +/* + * Copyright (c) 2002-2008 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ssh session multiplexing support */ + +#include "includes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif + +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif + +#ifdef HAVE_UTIL_H +# include +#endif + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "log.h" +#include "ssh.h" +#include "ssh2.h" +#include "pathnames.h" +#include "misc.h" +#include "match.h" +#include "sshbuf.h" +#include "channels.h" +#include "msg.h" +#include "packet.h" +#include "monitor_fdpass.h" +#include "sshpty.h" +#include "sshkey.h" +#include "readconf.h" +#include "clientloop.h" +#include "ssherr.h" + +/* from ssh.c */ +extern int tty_flag; +extern Options options; +extern char *host; +extern struct sshbuf *command; +extern volatile sig_atomic_t quit_pending; + +/* Context for session open confirmation callback */ +struct mux_session_confirm_ctx { + u_int want_tty; + u_int want_subsys; + u_int want_x_fwd; + u_int want_agent_fwd; + struct sshbuf *cmd; + char *term; + struct termios tio; + char **env; + u_int rid; +}; + +/* Context for stdio fwd open confirmation callback */ +struct mux_stdio_confirm_ctx { + u_int rid; +}; + +/* Context for global channel callback */ +struct mux_channel_confirm_ctx { + u_int cid; /* channel id */ + u_int rid; /* request id */ + int fid; /* forward id */ +}; + +/* fd to control socket */ +int muxserver_sock = -1; + +/* client request id */ +u_int muxclient_request_id = 0; + +/* Multiplexing control command */ +u_int muxclient_command = 0; + +/* Set when signalled. */ +static volatile sig_atomic_t muxclient_terminate = 0; + +/* PID of multiplex server */ +static u_int muxserver_pid = 0; + +static Channel *mux_listener_channel = NULL; + +struct mux_master_state { + int hello_rcvd; +}; + +/* mux protocol messages */ +#define MUX_MSG_HELLO 0x00000001 +#define MUX_C_NEW_SESSION 0x10000002 +#define MUX_C_ALIVE_CHECK 0x10000004 +#define MUX_C_TERMINATE 0x10000005 +#define MUX_C_OPEN_FWD 0x10000006 +#define MUX_C_CLOSE_FWD 0x10000007 +#define MUX_C_NEW_STDIO_FWD 0x10000008 +#define MUX_C_STOP_LISTENING 0x10000009 +#define MUX_C_PROXY 0x1000000f +#define MUX_S_OK 0x80000001 +#define MUX_S_PERMISSION_DENIED 0x80000002 +#define MUX_S_FAILURE 0x80000003 +#define MUX_S_EXIT_MESSAGE 0x80000004 +#define MUX_S_ALIVE 0x80000005 +#define MUX_S_SESSION_OPENED 0x80000006 +#define MUX_S_REMOTE_PORT 0x80000007 +#define MUX_S_TTY_ALLOC_FAIL 0x80000008 +#define MUX_S_PROXY 0x8000000f + +/* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ +#define MUX_FWD_LOCAL 1 +#define MUX_FWD_REMOTE 2 +#define MUX_FWD_DYNAMIC 3 + +static void mux_session_confirm(struct ssh *, int, int, void *); +static void mux_stdio_confirm(struct ssh *, int, int, void *); + +static int mux_master_process_hello(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_new_session(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_alive_check(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_terminate(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_open_fwd(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_close_fwd(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_stdio_fwd(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_stop_listening(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_proxy(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); + +static const struct { + u_int type; + int (*handler)(struct ssh *, u_int, Channel *, + struct sshbuf *, struct sshbuf *); +} mux_master_handlers[] = { + { MUX_MSG_HELLO, mux_master_process_hello }, + { MUX_C_NEW_SESSION, mux_master_process_new_session }, + { MUX_C_ALIVE_CHECK, mux_master_process_alive_check }, + { MUX_C_TERMINATE, mux_master_process_terminate }, + { MUX_C_OPEN_FWD, mux_master_process_open_fwd }, + { MUX_C_CLOSE_FWD, mux_master_process_close_fwd }, + { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd }, + { MUX_C_STOP_LISTENING, mux_master_process_stop_listening }, + { MUX_C_PROXY, mux_master_process_proxy }, + { 0, NULL } +}; + +/* Cleanup callback fired on closure of mux client _session_ channel */ +/* ARGSUSED */ +static void +mux_master_session_cleanup_cb(struct ssh *ssh, int cid, void *unused) +{ + Channel *cc, *c = channel_by_id(ssh, cid); + + debug3_f("entering for channel %d", cid); + if (c == NULL) + fatal_f("channel_by_id(%i) == NULL", cid); + if (c->ctl_chan != -1) { + if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) + fatal_f("channel %d missing control channel %d", + c->self, c->ctl_chan); + c->ctl_chan = -1; + cc->remote_id = 0; + cc->have_remote_id = 0; + chan_rcvd_oclose(ssh, cc); + } + channel_cancel_cleanup(ssh, c->self); +} + +/* Cleanup callback fired on closure of mux client _control_ channel */ +/* ARGSUSED */ +static void +mux_master_control_cleanup_cb(struct ssh *ssh, int cid, void *unused) +{ + Channel *sc, *c = channel_by_id(ssh, cid); + + debug3_f("entering for channel %d", cid); + if (c == NULL) + fatal_f("channel_by_id(%i) == NULL", cid); + if (c->have_remote_id) { + if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) + fatal_f("channel %d missing session channel %u", + c->self, c->remote_id); + c->remote_id = 0; + c->have_remote_id = 0; + sc->ctl_chan = -1; + if (sc->type != SSH_CHANNEL_OPEN && + sc->type != SSH_CHANNEL_OPENING) { + debug2_f("channel %d: not open", sc->self); + chan_mark_dead(ssh, sc); + } else { + if (sc->istate == CHAN_INPUT_OPEN) + chan_read_failed(ssh, sc); + if (sc->ostate == CHAN_OUTPUT_OPEN) + chan_write_failed(ssh, sc); + } + } + channel_cancel_cleanup(ssh, c->self); +} + +/* Check mux client environment variables before passing them to mux master. */ +static int +env_permitted(char *env) +{ + int i, ret; + char name[1024], *cp; + + if ((cp = strchr(env, '=')) == NULL || cp == env) + return 0; + ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); + if (ret <= 0 || (size_t)ret >= sizeof(name)) { + error_f("name '%.100s...' too long", env); + return 0; + } + + for (i = 0; i < options.num_send_env; i++) + if (match_pattern(name, options.send_env[i])) + return 1; + + return 0; +} + +/* Mux master protocol message handlers */ + +static int +mux_master_process_hello(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + u_int ver; + struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; + int r; + + if (state == NULL) + fatal_f("channel %d: c->mux_ctx == NULL", c->self); + if (state->hello_rcvd) { + error_f("HELLO received twice"); + return -1; + } + if ((r = sshbuf_get_u32(m, &ver)) != 0) { + error_fr(r, "parse"); + return -1; + } + if (ver != SSHMUX_VER) { + error_f("unsupported multiplexing protocol version %u " + "(expected %u)", ver, SSHMUX_VER); + return -1; + } + debug2_f("channel %d client version %u", c->self, ver); + + /* No extensions are presently defined */ + while (sshbuf_len(m) > 0) { + char *name = NULL; + size_t value_len = 0; + + if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || + (r = sshbuf_get_string_direct(m, NULL, &value_len)) != 0) { + error_fr(r, "parse extension"); + return -1; + } + debug2_f("Unrecognised extension \"%s\" length %zu", + name, value_len); + free(name); + } + state->hello_rcvd = 1; + return 0; +} + +/* Enqueue a "ok" response to the reply buffer */ +static void +reply_ok(struct sshbuf *reply, u_int rid) +{ + int r; + + if ((r = sshbuf_put_u32(reply, MUX_S_OK)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0) + fatal_fr(r, "reply"); +} + +/* Enqueue an error response to the reply buffer */ +static void +reply_error(struct sshbuf *reply, u_int type, u_int rid, const char *msg) +{ + int r; + + if ((r = sshbuf_put_u32(reply, type)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0 || + (r = sshbuf_put_cstring(reply, msg)) != 0) + fatal_fr(r, "reply"); +} + +static int +mux_master_process_new_session(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + Channel *nc; + struct mux_session_confirm_ctx *cctx; + char *cmd, *cp; + u_int i, j, env_len, escape_char, window, packetmax; + int r, new_fd[3]; + + /* Reply for SSHMUX_COMMAND_OPEN */ + cctx = xcalloc(1, sizeof(*cctx)); + cctx->term = NULL; + cctx->rid = rid; + cmd = NULL; + cctx->env = NULL; + env_len = 0; + if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ + (r = sshbuf_get_u32(m, &cctx->want_tty)) != 0 || + (r = sshbuf_get_u32(m, &cctx->want_x_fwd)) != 0 || + (r = sshbuf_get_u32(m, &cctx->want_agent_fwd)) != 0 || + (r = sshbuf_get_u32(m, &cctx->want_subsys)) != 0 || + (r = sshbuf_get_u32(m, &escape_char)) != 0 || + (r = sshbuf_get_cstring(m, &cctx->term, NULL)) != 0 || + (r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) { + malf: + free(cmd); + for (j = 0; j < env_len; j++) + free(cctx->env[j]); + free(cctx->env); + free(cctx->term); + free(cctx); + error_f("malformed message"); + return -1; + } + +#define MUX_MAX_ENV_VARS 4096 + while (sshbuf_len(m) > 0) { + if ((r = sshbuf_get_cstring(m, &cp, NULL)) != 0) + goto malf; + if (!env_permitted(cp)) { + free(cp); + continue; + } + cctx->env = xreallocarray(cctx->env, env_len + 2, + sizeof(*cctx->env)); + cctx->env[env_len++] = cp; + cctx->env[env_len] = NULL; + if (env_len > MUX_MAX_ENV_VARS) { + error_f(">%d environment variables received, " + "ignoring additional", MUX_MAX_ENV_VARS); + break; + } + } + + debug2_f("channel %d: request tty %d, X %d, agent %d, subsys %d, " + "term \"%s\", cmd \"%s\", env %u", c->self, + cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, + cctx->want_subsys, cctx->term, cmd, env_len); + + if ((cctx->cmd = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put(cctx->cmd, cmd, strlen(cmd))) != 0) + fatal_fr(r, "sshbuf_put"); + free(cmd); + cmd = NULL; + + /* Gather fds from client */ + for(i = 0; i < 3; i++) { + if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { + error_f("failed to receive fd %d from client", i); + for (j = 0; j < i; j++) + close(new_fd[j]); + for (j = 0; j < env_len; j++) + free(cctx->env[j]); + free(cctx->env); + free(cctx->term); + sshbuf_free(cctx->cmd); + free(cctx); + reply_error(reply, MUX_S_FAILURE, rid, + "did not receive file descriptors"); + return -1; + } + } + + debug3_f("got fds stdin %d, stdout %d, stderr %d", + new_fd[0], new_fd[1], new_fd[2]); + + /* XXX support multiple child sessions in future */ + if (c->have_remote_id) { + debug2_f("session already open"); + reply_error(reply, MUX_S_FAILURE, rid, + "Multiple sessions not supported"); + cleanup: + close(new_fd[0]); + close(new_fd[1]); + close(new_fd[2]); + free(cctx->term); + if (env_len != 0) { + for (i = 0; i < env_len; i++) + free(cctx->env[i]); + free(cctx->env); + } + sshbuf_free(cctx->cmd); + free(cctx); + return 0; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Allow shared connection to %s? ", host)) { + debug2_f("session refused by user"); + reply_error(reply, MUX_S_PERMISSION_DENIED, rid, + "Permission denied"); + goto cleanup; + } + } + + /* Try to pick up ttymodes from client before it goes raw */ + if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) + error_f("tcgetattr: %s", strerror(errno)); + + window = CHAN_SES_WINDOW_DEFAULT; + packetmax = CHAN_SES_PACKET_DEFAULT; + if (cctx->want_tty) { + window >>= 1; + packetmax >>= 1; + } + + nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, + new_fd[0], new_fd[1], new_fd[2], window, packetmax, + CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO); + + nc->ctl_chan = c->self; /* link session -> control channel */ + c->remote_id = nc->self; /* link control -> session channel */ + c->have_remote_id = 1; + + if (cctx->want_tty && escape_char != 0xffffffff) { + channel_register_filter(ssh, nc->self, + client_simple_escape_filter, NULL, + client_filter_cleanup, + client_new_escape_filter_ctx((int)escape_char)); + } + + debug2_f("channel_new: %d linked to control channel %d", + nc->self, nc->ctl_chan); + + channel_send_open(ssh, nc->self); + channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); + c->mux_pause = 1; /* stop handling messages until open_confirm done */ + channel_register_cleanup(ssh, nc->self, + mux_master_session_cleanup_cb, 1); + + /* reply is deferred, sent by mux_session_confirm */ + return 0; +} + +static int +mux_master_process_alive_check(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + int r; + + debug2_f("channel %d: alive check", c->self); + + /* prepare reply */ + if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0 || + (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0) + fatal_fr(r, "reply"); + + return 0; +} + +static int +mux_master_process_terminate(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + debug2_f("channel %d: terminate request", c->self); + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Terminate shared connection to %s? ", + host)) { + debug2_f("termination refused by user"); + reply_error(reply, MUX_S_PERMISSION_DENIED, rid, + "Permission denied"); + return 0; + } + } + + quit_pending = 1; + reply_ok(reply, rid); + /* XXX exit happens too soon - message never makes it to client */ + return 0; +} + +static char * +format_forward(u_int ftype, struct Forward *fwd) +{ + char *ret; + + switch (ftype) { + case MUX_FWD_LOCAL: + xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", + (fwd->listen_path != NULL) ? fwd->listen_path : + (fwd->listen_host == NULL) ? + (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : + fwd->listen_host, fwd->listen_port, + (fwd->connect_path != NULL) ? fwd->connect_path : + fwd->connect_host, fwd->connect_port); + break; + case MUX_FWD_DYNAMIC: + xasprintf(&ret, "dynamic forward %.200s:%d -> *", + (fwd->listen_host == NULL) ? + (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : + fwd->listen_host, fwd->listen_port); + break; + case MUX_FWD_REMOTE: + xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", + (fwd->listen_path != NULL) ? fwd->listen_path : + (fwd->listen_host == NULL) ? + "LOCALHOST" : fwd->listen_host, + fwd->listen_port, + (fwd->connect_path != NULL) ? fwd->connect_path : + fwd->connect_host, fwd->connect_port); + break; + default: + fatal_f("unknown forward type %u", ftype); + } + return ret; +} + +static int +compare_host(const char *a, const char *b) +{ + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + return strcmp(a, b) == 0; +} + +static int +compare_forward(struct Forward *a, struct Forward *b) +{ + if (!compare_host(a->listen_host, b->listen_host)) + return 0; + if (!compare_host(a->listen_path, b->listen_path)) + return 0; + if (a->listen_port != b->listen_port) + return 0; + if (!compare_host(a->connect_host, b->connect_host)) + return 0; + if (!compare_host(a->connect_path, b->connect_path)) + return 0; + if (a->connect_port != b->connect_port) + return 0; + + return 1; +} + +static void +mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) +{ + struct mux_channel_confirm_ctx *fctx = ctxt; + char *failmsg = NULL; + struct Forward *rfwd; + Channel *c; + struct sshbuf *out; + u_int port; + int r; + + if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { + /* no channel for reply */ + error_f("unknown channel"); + return; + } + if ((out = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if (fctx->fid >= options.num_remote_forwards || + (options.remote_forwards[fctx->fid].connect_path == NULL && + options.remote_forwards[fctx->fid].connect_host == NULL)) { + xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); + goto fail; + } + rfwd = &options.remote_forwards[fctx->fid]; + debug_f("%s for: listen %d, connect %s:%d", + type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", + rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : + rfwd->connect_host, rfwd->connect_port); + if (type == SSH2_MSG_REQUEST_SUCCESS) { + if (rfwd->listen_port == 0) { + if ((r = sshpkt_get_u32(ssh, &port)) != 0) + fatal_fr(r, "parse port"); + if (port > 65535) { + fatal("Invalid allocated port %u for " + "mux remote forward to %s:%d", port, + rfwd->connect_host, rfwd->connect_port); + } + rfwd->allocated_port = (int)port; + debug("Allocated port %u for mux remote forward" + " to %s:%d", rfwd->allocated_port, + rfwd->connect_host, rfwd->connect_port); + if ((r = sshbuf_put_u32(out, + MUX_S_REMOTE_PORT)) != 0 || + (r = sshbuf_put_u32(out, fctx->rid)) != 0 || + (r = sshbuf_put_u32(out, + rfwd->allocated_port)) != 0) + fatal_fr(r, "reply"); + channel_update_permission(ssh, rfwd->handle, + rfwd->allocated_port); + } else { + reply_ok(out, fctx->rid); + } + goto out; + } else { + if (rfwd->listen_port == 0) + channel_update_permission(ssh, rfwd->handle, -1); + if (rfwd->listen_path != NULL) + xasprintf(&failmsg, "remote port forwarding failed for " + "listen path %s", rfwd->listen_path); + else + xasprintf(&failmsg, "remote port forwarding failed for " + "listen port %d", rfwd->listen_port); + + debug2_f("clearing registered forwarding for listen %d, " + "connect %s:%d", rfwd->listen_port, + rfwd->connect_path ? rfwd->connect_path : + rfwd->connect_host, rfwd->connect_port); + + free(rfwd->listen_host); + free(rfwd->listen_path); + free(rfwd->connect_host); + free(rfwd->connect_path); + memset(rfwd, 0, sizeof(*rfwd)); + } + fail: + error_f("%s", failmsg); + reply_error(out, MUX_S_FAILURE, fctx->rid, failmsg); + free(failmsg); + out: + if ((r = sshbuf_put_stringb(c->output, out)) != 0) + fatal_fr(r, "enqueue"); + sshbuf_free(out); + if (c->mux_pause <= 0) + fatal_f("mux_pause %d", c->mux_pause); + c->mux_pause = 0; /* start processing messages again */ +} + +static int +mux_master_process_open_fwd(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + struct Forward fwd; + char *fwd_desc = NULL; + char *listen_addr, *connect_addr; + u_int ftype; + u_int lport, cport; + int r, i, ret = 0, freefwd = 1; + + memset(&fwd, 0, sizeof(fwd)); + + /* XXX - lport/cport check redundant */ + if ((r = sshbuf_get_u32(m, &ftype)) != 0 || + (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || + (r = sshbuf_get_u32(m, &lport)) != 0 || + (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || + (r = sshbuf_get_u32(m, &cport)) != 0 || + (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || + (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { + error_f("malformed message"); + ret = -1; + goto out; + } + if (*listen_addr == '\0') { + free(listen_addr); + listen_addr = NULL; + } + if (*connect_addr == '\0') { + free(connect_addr); + connect_addr = NULL; + } + + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_port = lport; + if (fwd.listen_port == PORT_STREAMLOCAL) + fwd.listen_path = listen_addr; + else + fwd.listen_host = listen_addr; + fwd.connect_port = cport; + if (fwd.connect_port == PORT_STREAMLOCAL) + fwd.connect_path = connect_addr; + else + fwd.connect_host = connect_addr; + + debug2_f("channel %d: request %s", c->self, + (fwd_desc = format_forward(ftype, &fwd))); + + if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && + ftype != MUX_FWD_DYNAMIC) { + logit_f("invalid forwarding type %u", ftype); + invalid: + free(listen_addr); + free(connect_addr); + reply_error(reply, MUX_S_FAILURE, rid, + "Invalid forwarding request"); + return 0; + } + if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { + logit_f("streamlocal and dynamic forwards " + "are mutually exclusive"); + goto invalid; + } + if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { + logit_f("invalid listen port %u", fwd.listen_port); + goto invalid; + } + if ((fwd.connect_port != PORT_STREAMLOCAL && + fwd.connect_port >= 65536) || + (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && + fwd.connect_port == 0)) { + logit_f("invalid connect port %u", + fwd.connect_port); + goto invalid; + } + if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && + fwd.connect_path == NULL) { + logit_f("missing connect host"); + goto invalid; + } + + /* Skip forwards that have already been requested */ + switch (ftype) { + case MUX_FWD_LOCAL: + case MUX_FWD_DYNAMIC: + for (i = 0; i < options.num_local_forwards; i++) { + if (compare_forward(&fwd, + options.local_forwards + i)) { + exists: + debug2_f("found existing forwarding"); + reply_ok(reply, rid); + goto out; + } + } + break; + case MUX_FWD_REMOTE: + for (i = 0; i < options.num_remote_forwards; i++) { + if (!compare_forward(&fwd, options.remote_forwards + i)) + continue; + if (fwd.listen_port != 0) + goto exists; + debug2_f("found allocated port"); + if ((r = sshbuf_put_u32(reply, + MUX_S_REMOTE_PORT)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0 || + (r = sshbuf_put_u32(reply, + options.remote_forwards[i].allocated_port)) != 0) + fatal_fr(r, "reply FWD_REMOTE"); + goto out; + } + break; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Open %s on %s?", fwd_desc, host)) { + debug2_f("forwarding refused by user"); + reply_error(reply, MUX_S_PERMISSION_DENIED, rid, + "Permission denied"); + goto out; + } + } + + if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { + if (!channel_setup_local_fwd_listener(ssh, &fwd, + &options.fwd_opts)) { + fail: + logit_f("requested %s failed", fwd_desc); + reply_error(reply, MUX_S_FAILURE, rid, + "Port forwarding failed"); + goto out; + } + add_local_forward(&options, &fwd); + freefwd = 0; + } else { + struct mux_channel_confirm_ctx *fctx; + + fwd.handle = channel_request_remote_forwarding(ssh, &fwd); + if (fwd.handle < 0) + goto fail; + add_remote_forward(&options, &fwd); + fctx = xcalloc(1, sizeof(*fctx)); + fctx->cid = c->self; + fctx->rid = rid; + fctx->fid = options.num_remote_forwards - 1; + client_register_global_confirm(mux_confirm_remote_forward, + fctx); + freefwd = 0; + c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ + /* delayed reply in mux_confirm_remote_forward */ + goto out; + } + reply_ok(reply, rid); + out: + free(fwd_desc); + if (freefwd) { + free(fwd.listen_host); + free(fwd.listen_path); + free(fwd.connect_host); + free(fwd.connect_path); + } + return ret; +} + +static int +mux_master_process_close_fwd(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + struct Forward fwd, *found_fwd; + char *fwd_desc = NULL; + const char *error_reason = NULL; + char *listen_addr = NULL, *connect_addr = NULL; + u_int ftype; + int r, i, ret = 0; + u_int lport, cport; + + memset(&fwd, 0, sizeof(fwd)); + + if ((r = sshbuf_get_u32(m, &ftype)) != 0 || + (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || + (r = sshbuf_get_u32(m, &lport)) != 0 || + (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || + (r = sshbuf_get_u32(m, &cport)) != 0 || + (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || + (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { + error_f("malformed message"); + ret = -1; + goto out; + } + + if (*listen_addr == '\0') { + free(listen_addr); + listen_addr = NULL; + } + if (*connect_addr == '\0') { + free(connect_addr); + connect_addr = NULL; + } + + memset(&fwd, 0, sizeof(fwd)); + fwd.listen_port = lport; + if (fwd.listen_port == PORT_STREAMLOCAL) + fwd.listen_path = listen_addr; + else + fwd.listen_host = listen_addr; + fwd.connect_port = cport; + if (fwd.connect_port == PORT_STREAMLOCAL) + fwd.connect_path = connect_addr; + else + fwd.connect_host = connect_addr; + + debug2_f("channel %d: request cancel %s", c->self, + (fwd_desc = format_forward(ftype, &fwd))); + + /* make sure this has been requested */ + found_fwd = NULL; + switch (ftype) { + case MUX_FWD_LOCAL: + case MUX_FWD_DYNAMIC: + for (i = 0; i < options.num_local_forwards; i++) { + if (compare_forward(&fwd, + options.local_forwards + i)) { + found_fwd = options.local_forwards + i; + break; + } + } + break; + case MUX_FWD_REMOTE: + for (i = 0; i < options.num_remote_forwards; i++) { + if (compare_forward(&fwd, + options.remote_forwards + i)) { + found_fwd = options.remote_forwards + i; + break; + } + } + break; + } + + if (found_fwd == NULL) + error_reason = "port not forwarded"; + else if (ftype == MUX_FWD_REMOTE) { + /* + * This shouldn't fail unless we confused the host/port + * between options.remote_forwards and permitted_opens. + * However, for dynamic allocated listen ports we need + * to use the actual listen port. + */ + if (channel_request_rforward_cancel(ssh, found_fwd) == -1) + error_reason = "port not in permitted opens"; + } else { /* local and dynamic forwards */ + /* Ditto */ + if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, + &options.fwd_opts) == -1) + error_reason = "port not found"; + } + + if (error_reason != NULL) + reply_error(reply, MUX_S_FAILURE, rid, error_reason); + else { + reply_ok(reply, rid); + free(found_fwd->listen_host); + free(found_fwd->listen_path); + free(found_fwd->connect_host); + free(found_fwd->connect_path); + found_fwd->listen_host = found_fwd->connect_host = NULL; + found_fwd->listen_path = found_fwd->connect_path = NULL; + found_fwd->listen_port = found_fwd->connect_port = 0; + } + out: + free(fwd_desc); + free(listen_addr); + free(connect_addr); + + return ret; +} + +static int +mux_master_process_stdio_fwd(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + Channel *nc; + char *chost = NULL; + u_int cport, i, j; + int r, new_fd[2]; + struct mux_stdio_confirm_ctx *cctx; + + if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ + (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || + (r = sshbuf_get_u32(m, &cport)) != 0) { + free(chost); + error_f("malformed message"); + return -1; + } + + debug2_f("channel %d: stdio fwd to %s:%u", c->self, chost, cport); + + /* Gather fds from client */ + for(i = 0; i < 2; i++) { + if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { + error_f("failed to receive fd %d from client", i); + for (j = 0; j < i; j++) + close(new_fd[j]); + free(chost); + + /* prepare reply */ + reply_error(reply, MUX_S_FAILURE, rid, + "did not receive file descriptors"); + return -1; + } + } + + debug3_f("got fds stdin %d, stdout %d", new_fd[0], new_fd[1]); + + /* XXX support multiple child sessions in future */ + if (c->have_remote_id) { + debug2_f("session already open"); + reply_error(reply, MUX_S_FAILURE, rid, + "Multiple sessions not supported"); + cleanup: + close(new_fd[0]); + close(new_fd[1]); + free(chost); + return 0; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Allow forward to %s:%u? ", + chost, cport)) { + debug2_f("stdio fwd refused by user"); + reply_error(reply, MUX_S_PERMISSION_DENIED, rid, + "Permission denied"); + goto cleanup; + } + } + + nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1], + CHANNEL_NONBLOCK_STDIO); + free(chost); + + nc->ctl_chan = c->self; /* link session -> control channel */ + c->remote_id = nc->self; /* link control -> session channel */ + c->have_remote_id = 1; + + debug2_f("channel_new: %d control %d", nc->self, nc->ctl_chan); + + channel_register_cleanup(ssh, nc->self, + mux_master_session_cleanup_cb, 1); + + cctx = xcalloc(1, sizeof(*cctx)); + cctx->rid = rid; + channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); + c->mux_pause = 1; /* stop handling messages until open_confirm done */ + + /* reply is deferred, sent by mux_session_confirm */ + return 0; +} + +/* Callback on open confirmation in mux master for a mux stdio fwd session. */ +static void +mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) +{ + struct mux_stdio_confirm_ctx *cctx = arg; + Channel *c, *cc; + struct sshbuf *reply; + int r; + + if (cctx == NULL) + fatal_f("cctx == NULL"); + if ((c = channel_by_id(ssh, id)) == NULL) + fatal_f("no channel for id %d", id); + if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) + fatal_f("channel %d lacks control channel %d", + id, c->ctl_chan); + if ((reply = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + + if (!success) { + debug3_f("sending failure reply"); + reply_error(reply, MUX_S_FAILURE, cctx->rid, + "Session open refused by peer"); + /* prepare reply */ + goto done; + } + + debug3_f("sending success reply"); + /* prepare reply */ + if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || + (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || + (r = sshbuf_put_u32(reply, c->self)) != 0) + fatal_fr(r, "reply"); + + done: + /* Send reply */ + if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) + fatal_fr(r, "enqueue"); + sshbuf_free(reply); + + if (cc->mux_pause <= 0) + fatal_f("mux_pause %d", cc->mux_pause); + cc->mux_pause = 0; /* start processing messages again */ + c->open_confirm_ctx = NULL; + free(cctx); +} + +static int +mux_master_process_stop_listening(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + debug_f("channel %d: stop listening", c->self); + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Disable further multiplexing on shared " + "connection to %s? ", host)) { + debug2_f("stop listen refused by user"); + reply_error(reply, MUX_S_PERMISSION_DENIED, rid, + "Permission denied"); + return 0; + } + } + + if (mux_listener_channel != NULL) { + channel_free(ssh, mux_listener_channel); + client_stop_mux(); + free(options.control_path); + options.control_path = NULL; + mux_listener_channel = NULL; + muxserver_sock = -1; + } + + reply_ok(reply, rid); + return 0; +} + +static int +mux_master_process_proxy(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + int r; + + debug_f("channel %d: proxy request", c->self); + + c->mux_rcb = channel_proxy_downstream; + if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0) + fatal_fr(r, "reply"); + + return 0; +} + +/* Channel callbacks fired on read/write from mux client fd */ +static int +mux_master_read_cb(struct ssh *ssh, Channel *c) +{ + struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; + struct sshbuf *in = NULL, *out = NULL; + u_int type, rid, i; + int r, ret = -1; + + if ((out = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + + /* Setup ctx and */ + if (c->mux_ctx == NULL) { + state = xcalloc(1, sizeof(*state)); + c->mux_ctx = state; + channel_register_cleanup(ssh, c->self, + mux_master_control_cleanup_cb, 0); + + /* Send hello */ + if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 || + (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0) + fatal_fr(r, "reply"); + /* no extensions */ + if ((r = sshbuf_put_stringb(c->output, out)) != 0) + fatal_fr(r, "enqueue"); + debug3_f("channel %d: hello sent", c->self); + ret = 0; + goto out; + } + + /* Channel code ensures that we receive whole packets */ + if ((r = sshbuf_froms(c->input, &in)) != 0) { + malf: + error_f("malformed message"); + goto out; + } + + if ((r = sshbuf_get_u32(in, &type)) != 0) + goto malf; + debug3_f("channel %d packet type 0x%08x len %zu", c->self, + type, sshbuf_len(in)); + + if (type == MUX_MSG_HELLO) + rid = 0; + else { + if (!state->hello_rcvd) { + error_f("expected MUX_MSG_HELLO(0x%08x), " + "received 0x%08x", MUX_MSG_HELLO, type); + goto out; + } + if ((r = sshbuf_get_u32(in, &rid)) != 0) + goto malf; + } + + for (i = 0; mux_master_handlers[i].handler != NULL; i++) { + if (type == mux_master_handlers[i].type) { + ret = mux_master_handlers[i].handler(ssh, rid, + c, in, out); + break; + } + } + if (mux_master_handlers[i].handler == NULL) { + error_f("unsupported mux message 0x%08x", type); + reply_error(out, MUX_S_FAILURE, rid, "unsupported request"); + ret = 0; + } + /* Enqueue reply packet */ + if (sshbuf_len(out) != 0 && + (r = sshbuf_put_stringb(c->output, out)) != 0) + fatal_fr(r, "enqueue"); + out: + sshbuf_free(in); + sshbuf_free(out); + return ret; +} + +void +mux_exit_message(struct ssh *ssh, Channel *c, int exitval) +{ + struct sshbuf *m; + Channel *mux_chan; + int r; + + debug3_f("channel %d: exit message, exitval %d", c->self, exitval); + + if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) + fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); + + /* Append exit message packet to control socket output queue */ + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 || + (r = sshbuf_put_u32(m, c->self)) != 0 || + (r = sshbuf_put_u32(m, exitval)) != 0 || + (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) + fatal_fr(r, "reply"); + sshbuf_free(m); +} + +void +mux_tty_alloc_failed(struct ssh *ssh, Channel *c) +{ + struct sshbuf *m; + Channel *mux_chan; + int r; + + debug3_f("channel %d: TTY alloc failed", c->self); + + if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) + fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); + + /* Append exit message packet to control socket output queue */ + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 || + (r = sshbuf_put_u32(m, c->self)) != 0 || + (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) + fatal_fr(r, "reply"); + sshbuf_free(m); +} + +/* Prepare a mux master to listen on a Unix domain socket. */ +void +muxserver_listen(struct ssh *ssh) +{ + mode_t old_umask; + char *orig_control_path = options.control_path; + char rbuf[16+1]; + u_int i, r; + int oerrno; + + if (options.control_path == NULL || + options.control_master == SSHCTL_MASTER_NO) + return; + + debug("setting up multiplex master socket"); + + /* + * Use a temporary path before listen so we can pseudo-atomically + * establish the listening socket in its final location to avoid + * other processes racing in between bind() and listen() and hitting + * an unready socket. + */ + for (i = 0; i < sizeof(rbuf) - 1; i++) { + r = arc4random_uniform(26+26+10); + rbuf[i] = (r < 26) ? 'a' + r : + (r < 26*2) ? 'A' + r - 26 : + '0' + r - 26 - 26; + } + rbuf[sizeof(rbuf) - 1] = '\0'; + options.control_path = NULL; + xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); + debug3_f("temporary control path %s", options.control_path); + + old_umask = umask(0177); + muxserver_sock = unix_listener(options.control_path, 64, 0); + oerrno = errno; + umask(old_umask); + if (muxserver_sock < 0) { + if (oerrno == EINVAL || oerrno == EADDRINUSE) { + error("ControlSocket %s already exists, " + "disabling multiplexing", options.control_path); + disable_mux_master: + if (muxserver_sock != -1) { + close(muxserver_sock); + muxserver_sock = -1; + } + free(orig_control_path); + free(options.control_path); + options.control_path = NULL; + options.control_master = SSHCTL_MASTER_NO; + return; + } else { + /* unix_listener() logs the error */ + cleanup_exit(255); + } + } + + /* Now atomically "move" the mux socket into position */ + if (link(options.control_path, orig_control_path) != 0) { + if (errno != EEXIST) { + fatal_f("link mux listener %s => %s: %s", + options.control_path, orig_control_path, + strerror(errno)); + } + error("ControlSocket %s already exists, disabling multiplexing", + orig_control_path); + unlink(options.control_path); + goto disable_mux_master; + } + unlink(options.control_path); + free(options.control_path); + options.control_path = orig_control_path; + + set_nonblock(muxserver_sock); + + mux_listener_channel = channel_new(ssh, "mux listener", + SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, options.control_path, 1); + mux_listener_channel->mux_rcb = mux_master_read_cb; + debug3_f("mux listener channel %d fd %d", + mux_listener_channel->self, mux_listener_channel->sock); +} + +/* Callback on open confirmation in mux master for a mux client session. */ +static void +mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) +{ + struct mux_session_confirm_ctx *cctx = arg; + const char *display; + Channel *c, *cc; + int i, r; + struct sshbuf *reply; + + if (cctx == NULL) + fatal_f("cctx == NULL"); + if ((c = channel_by_id(ssh, id)) == NULL) + fatal_f("no channel for id %d", id); + if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) + fatal_f("channel %d lacks control channel %d", + id, c->ctl_chan); + if ((reply = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + + if (!success) { + debug3_f("sending failure reply"); + reply_error(reply, MUX_S_FAILURE, cctx->rid, + "Session open refused by peer"); + goto done; + } + + display = getenv("DISPLAY"); + if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { + char *proto, *data; + + /* Get reasonable local authentication information. */ + if (client_x11_get_proto(ssh, display, options.xauth_location, + options.forward_x11_trusted, options.forward_x11_timeout, + &proto, &data) == 0) { + /* Request forwarding with authentication spoofing. */ + debug("Requesting X11 forwarding with authentication " + "spoofing."); + x11_request_forwarding_with_spoofing(ssh, id, + display, proto, data, 1); + /* XXX exit_on_forward_failure */ + client_expect_confirm(ssh, id, "X11 forwarding", + CONFIRM_WARN); + } + } + + if (cctx->want_agent_fwd && options.forward_agent) { + debug("Requesting authentication agent forwarding."); + channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send"); + } + + client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, + cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env); + + debug3_f("sending success reply"); + /* prepare reply */ + if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || + (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || + (r = sshbuf_put_u32(reply, c->self)) != 0) + fatal_fr(r, "reply"); + + done: + /* Send reply */ + if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) + fatal_fr(r, "enqueue"); + sshbuf_free(reply); + + if (cc->mux_pause <= 0) + fatal_f("mux_pause %d", cc->mux_pause); + cc->mux_pause = 0; /* start processing messages again */ + c->open_confirm_ctx = NULL; + sshbuf_free(cctx->cmd); + free(cctx->term); + if (cctx->env != NULL) { + for (i = 0; cctx->env[i] != NULL; i++) + free(cctx->env[i]); + free(cctx->env); + } + free(cctx); +} + +/* ** Multiplexing client support */ + +/* Exit signal handler */ +static void +control_client_sighandler(int signo) +{ + muxclient_terminate = signo; +} + +/* + * Relay signal handler - used to pass some signals from mux client to + * mux master. + */ +static void +control_client_sigrelay(int signo) +{ + int save_errno = errno; + + if (muxserver_pid > 1) + kill(muxserver_pid, signo); + + errno = save_errno; +} + +static int +mux_client_read(int fd, struct sshbuf *b, size_t need) +{ + size_t have; + ssize_t len; + u_char *p; + struct pollfd pfd; + int r; + + pfd.fd = fd; + pfd.events = POLLIN; + if ((r = sshbuf_reserve(b, need, &p)) != 0) + fatal_fr(r, "reserve"); + for (have = 0; have < need; ) { + if (muxclient_terminate) { + errno = EINTR; + return -1; + } + len = read(fd, p + have, need - have); + if (len == -1) { + switch (errno) { +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EAGAIN: + (void)poll(&pfd, 1, -1); + /* FALLTHROUGH */ + case EINTR: + continue; + default: + return -1; + } + } + if (len == 0) { + errno = EPIPE; + return -1; + } + have += (size_t)len; + } + return 0; +} + +static int +mux_client_write_packet(int fd, struct sshbuf *m) +{ + struct sshbuf *queue; + u_int have, need; + int r, oerrno, len; + const u_char *ptr; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLOUT; + if ((queue = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_stringb(queue, m)) != 0) + fatal_fr(r, "enqueue"); + + need = sshbuf_len(queue); + ptr = sshbuf_ptr(queue); + + for (have = 0; have < need; ) { + if (muxclient_terminate) { + sshbuf_free(queue); + errno = EINTR; + return -1; + } + len = write(fd, ptr + have, need - have); + if (len == -1) { + switch (errno) { +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EAGAIN: + (void)poll(&pfd, 1, -1); + /* FALLTHROUGH */ + case EINTR: + continue; + default: + oerrno = errno; + sshbuf_free(queue); + errno = oerrno; + return -1; + } + } + if (len == 0) { + sshbuf_free(queue); + errno = EPIPE; + return -1; + } + have += (u_int)len; + } + sshbuf_free(queue); + return 0; +} + +static int +mux_client_read_packet(int fd, struct sshbuf *m) +{ + struct sshbuf *queue; + size_t need, have; + const u_char *ptr; + int r, oerrno; + + if ((queue = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if (mux_client_read(fd, queue, 4) != 0) { + if ((oerrno = errno) == EPIPE) + debug3_f("read header failed: %s", + strerror(errno)); + sshbuf_free(queue); + errno = oerrno; + return -1; + } + need = PEEK_U32(sshbuf_ptr(queue)); + if (mux_client_read(fd, queue, need) != 0) { + oerrno = errno; + debug3_f("read body failed: %s", strerror(errno)); + sshbuf_free(queue); + errno = oerrno; + return -1; + } + if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 || + (r = sshbuf_put(m, ptr, have)) != 0) + fatal_fr(r, "dequeue"); + sshbuf_free(queue); + return 0; +} + +static int +mux_client_hello_exchange(int fd) +{ + struct sshbuf *m; + u_int type, ver; + int r, ret = -1; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_MSG_HELLO)) != 0 || + (r = sshbuf_put_u32(m, SSHMUX_VER)) != 0) + fatal_fr(r, "assemble hello"); + /* no extensions */ + + if (mux_client_write_packet(fd, m) != 0) { + debug_f("write packet: %s", strerror(errno)); + goto out; + } + + sshbuf_reset(m); + + /* Read their HELLO */ + if (mux_client_read_packet(fd, m) != 0) { + debug_f("read packet failed"); + goto out; + } + + if ((r = sshbuf_get_u32(m, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != MUX_MSG_HELLO) { + error_f("expected HELLO (%u) got %u", MUX_MSG_HELLO, type); + goto out; + } + if ((r = sshbuf_get_u32(m, &ver)) != 0) + fatal_fr(r, "parse version"); + if (ver != SSHMUX_VER) { + error("Unsupported multiplexing protocol version %d " + "(expected %d)", ver, SSHMUX_VER); + goto out; + } + debug2_f("master version %u", ver); + /* No extensions are presently defined */ + while (sshbuf_len(m) > 0) { + char *name = NULL; + + if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || + (r = sshbuf_skip_string(m)) != 0) { /* value */ + error_fr(r, "parse extension"); + goto out; + } + debug2("Unrecognised master extension \"%s\"", name); + free(name); + } + /* success */ + ret = 0; + out: + sshbuf_free(m); + return ret; +} + +static u_int +mux_client_request_alive(int fd) +{ + struct sshbuf *m; + char *e; + u_int pid, type, rid; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) + fatal_fr(r, "assemble"); + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + sshbuf_reset(m); + + /* Read their reply */ + if (mux_client_read_packet(fd, m) != 0) { + sshbuf_free(m); + return 0; + } + + if ((r = sshbuf_get_u32(m, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != MUX_S_ALIVE) { + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal_f("master returned error: %s", e); + } + + if ((r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse remote ID"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + if ((r = sshbuf_get_u32(m, &pid)) != 0) + fatal_fr(r, "parse PID"); + sshbuf_free(m); + + debug3_f("done pid = %u", pid); + + muxclient_request_id++; + + return pid; +} + +static void +mux_client_request_terminate(int fd) +{ + struct sshbuf *m; + char *e; + u_int type, rid; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) + fatal_fr(r, "request"); + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + sshbuf_reset(m); + + /* Read their reply */ + if (mux_client_read_packet(fd, m) != 0) { + /* Remote end exited already */ + if (errno == EPIPE) { + sshbuf_free(m); + return; + } + fatal_f("read from master failed: %s", strerror(errno)); + } + + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + switch (type) { + case MUX_S_OK: + break; + case MUX_S_PERMISSION_DENIED: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal("Master refused termination request: %s", e); + case MUX_S_FAILURE: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal_f("termination request failed: %s", e); + default: + fatal_f("unexpected response from master 0x%08x", type); + } + sshbuf_free(m); + muxclient_request_id++; +} + +static int +mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) +{ + struct sshbuf *m; + char *e, *fwd_desc; + const char *lhost, *chost; + u_int type, rid; + int r; + + fwd_desc = format_forward(ftype, fwd); + debug("Requesting %s %s", + cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); + free(fwd_desc); + + type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD; + if (fwd->listen_path != NULL) + lhost = fwd->listen_path; + else if (fwd->listen_host == NULL) + lhost = ""; + else if (*fwd->listen_host == '\0') + lhost = "*"; + else + lhost = fwd->listen_host; + + if (fwd->connect_path != NULL) + chost = fwd->connect_path; + else if (fwd->connect_host == NULL) + chost = ""; + else + chost = fwd->connect_host; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, type)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || + (r = sshbuf_put_u32(m, ftype)) != 0 || + (r = sshbuf_put_cstring(m, lhost)) != 0 || + (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 || + (r = sshbuf_put_cstring(m, chost)) != 0 || + (r = sshbuf_put_u32(m, fwd->connect_port)) != 0) + fatal_fr(r, "request"); + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + sshbuf_reset(m); + + /* Read their reply */ + if (mux_client_read_packet(fd, m) != 0) { + sshbuf_free(m); + return -1; + } + + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + + switch (type) { + case MUX_S_OK: + break; + case MUX_S_REMOTE_PORT: + if (cancel_flag) + fatal_f("got MUX_S_REMOTE_PORT for cancel"); + if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0) + fatal_fr(r, "parse port"); + verbose("Allocated port %u for remote forward to %s:%d", + fwd->allocated_port, + fwd->connect_host ? fwd->connect_host : "", + fwd->connect_port); + if (muxclient_command == SSHMUX_COMMAND_FORWARD) + fprintf(stdout, "%i\n", fwd->allocated_port); + break; + case MUX_S_PERMISSION_DENIED: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + sshbuf_free(m); + error("Master refused forwarding request: %s", e); + return -1; + case MUX_S_FAILURE: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + sshbuf_free(m); + error_f("forwarding request failed: %s", e); + return -1; + default: + fatal_f("unexpected response from master 0x%08x", type); + } + sshbuf_free(m); + + muxclient_request_id++; + return 0; +} + +static int +mux_client_forwards(int fd, int cancel_flag) +{ + int i, ret = 0; + + debug3_f("%s forwardings: %d local, %d remote", + cancel_flag ? "cancel" : "request", + options.num_local_forwards, options.num_remote_forwards); + + /* XXX ExitOnForwardingFailure */ + for (i = 0; i < options.num_local_forwards; i++) { + if (mux_client_forward(fd, cancel_flag, + options.local_forwards[i].connect_port == 0 ? + MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, + options.local_forwards + i) != 0) + ret = -1; + } + for (i = 0; i < options.num_remote_forwards; i++) { + if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, + options.remote_forwards + i) != 0) + ret = -1; + } + return ret; +} + +static int +mux_client_request_session(int fd) +{ + struct sshbuf *m; + char *e; + const char *term = NULL; + u_int echar, rid, sid, esid, exitval, type, exitval_seen; + extern char **environ; + int r, i, rawmode; + + debug3_f("entering"); + + if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { + error_f("master alive request failed"); + return -1; + } + + ssh_signal(SIGPIPE, SIG_IGN); + + if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) + fatal_f("stdfd_devnull failed"); + + if ((term = lookup_env_in_list("TERM", options.setenv, + options.num_setenv)) == NULL || *term == '\0') + term = getenv("TERM"); + + echar = 0xffffffff; + if (options.escape_char != SSH_ESCAPECHAR_NONE) + echar = (u_int)options.escape_char; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || + (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ + (r = sshbuf_put_u32(m, tty_flag)) != 0 || + (r = sshbuf_put_u32(m, options.forward_x11)) != 0 || + (r = sshbuf_put_u32(m, options.forward_agent)) != 0 || + (r = sshbuf_put_u32(m, options.session_type == SESSION_TYPE_SUBSYSTEM)) != 0 || + (r = sshbuf_put_u32(m, echar)) != 0 || + (r = sshbuf_put_cstring(m, term == NULL ? "" : term)) != 0 || + (r = sshbuf_put_stringb(m, command)) != 0) + fatal_fr(r, "request"); + + /* Pass environment */ + if (options.num_send_env > 0 && environ != NULL) { + for (i = 0; environ[i] != NULL; i++) { + if (!env_permitted(environ[i])) + continue; + if ((r = sshbuf_put_cstring(m, environ[i])) != 0) + fatal_fr(r, "request sendenv"); + } + } + for (i = 0; i < options.num_setenv; i++) { + if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0) + fatal_fr(r, "request setenv"); + } + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + /* Send the stdio file descriptors */ + if (mm_send_fd(fd, STDIN_FILENO) == -1 || + mm_send_fd(fd, STDOUT_FILENO) == -1 || + mm_send_fd(fd, STDERR_FILENO) == -1) + fatal_f("send fds failed"); + + debug3_f("session request sent"); + + /* Read their reply */ + sshbuf_reset(m); + if (mux_client_read_packet(fd, m) != 0) { + error_f("read from master failed: %s", strerror(errno)); + sshbuf_free(m); + return -1; + } + + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + + switch (type) { + case MUX_S_SESSION_OPENED: + if ((r = sshbuf_get_u32(m, &sid)) != 0) + fatal_fr(r, "parse session ID"); + debug_f("master session id: %u", sid); + break; + case MUX_S_PERMISSION_DENIED: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + error("Master refused session request: %s", e); + sshbuf_free(m); + return -1; + case MUX_S_FAILURE: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + error_f("session request failed: %s", e); + sshbuf_free(m); + return -1; + default: + sshbuf_free(m); + error_f("unexpected response from master 0x%08x", type); + return -1; + } + muxclient_request_id++; + + if (pledge("stdio proc tty", NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + platform_pledge_mux(); + + ssh_signal(SIGHUP, control_client_sighandler); + ssh_signal(SIGINT, control_client_sighandler); + ssh_signal(SIGTERM, control_client_sighandler); + ssh_signal(SIGWINCH, control_client_sigrelay); + + rawmode = tty_flag; + if (tty_flag) + enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + + /* + * Stick around until the controlee closes the client_fd. + * Before it does, it is expected to write an exit message. + * This process must read the value and wait for the closure of + * the client_fd; if this one closes early, the multiplex master will + * terminate early too (possibly losing data). + */ + for (exitval = 255, exitval_seen = 0;;) { + sshbuf_reset(m); + if (mux_client_read_packet(fd, m) != 0) + break; + if ((r = sshbuf_get_u32(m, &type)) != 0) + fatal_fr(r, "parse type"); + switch (type) { + case MUX_S_TTY_ALLOC_FAIL: + if ((r = sshbuf_get_u32(m, &esid)) != 0) + fatal_fr(r, "parse session ID"); + if (esid != sid) + fatal_f("tty alloc fail on unknown session: " + "my id %u theirs %u", sid, esid); + leave_raw_mode(options.request_tty == + REQUEST_TTY_FORCE); + rawmode = 0; + continue; + case MUX_S_EXIT_MESSAGE: + if ((r = sshbuf_get_u32(m, &esid)) != 0) + fatal_fr(r, "parse session ID"); + if (esid != sid) + fatal_f("exit on unknown session: " + "my id %u theirs %u", sid, esid); + if (exitval_seen) + fatal_f("exitval sent twice"); + if ((r = sshbuf_get_u32(m, &exitval)) != 0) + fatal_fr(r, "parse exitval"); + exitval_seen = 1; + continue; + default: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal_f("master returned error: %s", e); + } + } + + close(fd); + if (rawmode) + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + + if (muxclient_terminate) { + debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); + exitval = 255; + } else if (!exitval_seen) { + debug2("Control master terminated unexpectedly"); + exitval = 255; + } else + debug2("Received exit status from master %d", exitval); + + if (tty_flag && options.log_level >= SYSLOG_LEVEL_INFO) + fprintf(stderr, "Shared connection to %s closed.\r\n", host); + + exit(exitval); +} + +static int +mux_client_proxy(int fd) +{ + struct sshbuf *m; + char *e; + u_int type, rid; + int r; + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) + fatal_fr(r, "request"); + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + sshbuf_reset(m); + + /* Read their reply */ + if (mux_client_read_packet(fd, m) != 0) { + sshbuf_free(m); + return 0; + } + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + if (type != MUX_S_PROXY) { + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal_f("master returned error: %s", e); + } + sshbuf_free(m); + + debug3_f("done"); + muxclient_request_id++; + return 0; +} + +static int +mux_client_request_stdio_fwd(int fd) +{ + struct sshbuf *m; + char *e; + u_int type, rid, sid; + int r; + + debug3_f("entering"); + + if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { + error_f("master alive request failed"); + return -1; + } + + ssh_signal(SIGPIPE, SIG_IGN); + + if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) + fatal_f("stdfd_devnull failed"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || + (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ + (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 || + (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0) + fatal_fr(r, "request"); + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + /* Send the stdio file descriptors */ + if (mm_send_fd(fd, STDIN_FILENO) == -1 || + mm_send_fd(fd, STDOUT_FILENO) == -1) + fatal_f("send fds failed"); + + if (pledge("stdio proc tty", NULL) == -1) + fatal_f("pledge(): %s", strerror(errno)); + platform_pledge_mux(); + + debug3_f("stdio forward request sent"); + + /* Read their reply */ + sshbuf_reset(m); + + if (mux_client_read_packet(fd, m) != 0) { + error_f("read from master failed: %s", strerror(errno)); + sshbuf_free(m); + return -1; + } + + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + switch (type) { + case MUX_S_SESSION_OPENED: + if ((r = sshbuf_get_u32(m, &sid)) != 0) + fatal_fr(r, "parse session ID"); + debug_f("master session id: %u", sid); + break; + case MUX_S_PERMISSION_DENIED: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + sshbuf_free(m); + fatal("Master refused stdio forwarding request: %s", e); + case MUX_S_FAILURE: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + sshbuf_free(m); + fatal("Stdio forwarding request failed: %s", e); + default: + sshbuf_free(m); + error_f("unexpected response from master 0x%08x", type); + return -1; + } + muxclient_request_id++; + + ssh_signal(SIGHUP, control_client_sighandler); + ssh_signal(SIGINT, control_client_sighandler); + ssh_signal(SIGTERM, control_client_sighandler); + ssh_signal(SIGWINCH, control_client_sigrelay); + + /* + * Stick around until the controlee closes the client_fd. + */ + sshbuf_reset(m); + if (mux_client_read_packet(fd, m) != 0) { + if (errno == EPIPE || + (errno == EINTR && muxclient_terminate != 0)) + return 0; + fatal_f("mux_client_read_packet: %s", strerror(errno)); + } + fatal_f("master returned unexpected message %u", type); +} + +static void +mux_client_request_stop_listening(int fd) +{ + struct sshbuf *m; + char *e; + u_int type, rid; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) + fatal_fr(r, "request"); + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + sshbuf_reset(m); + + /* Read their reply */ + if (mux_client_read_packet(fd, m) != 0) + fatal_f("read from master failed: %s", strerror(errno)); + + if ((r = sshbuf_get_u32(m, &type)) != 0 || + (r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + + switch (type) { + case MUX_S_OK: + break; + case MUX_S_PERMISSION_DENIED: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal("Master refused stop listening request: %s", e); + case MUX_S_FAILURE: + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal_f("stop listening request failed: %s", e); + default: + fatal_f("unexpected response from master 0x%08x", type); + } + sshbuf_free(m); + muxclient_request_id++; +} + +/* Multiplex client main loop. */ +int +muxclient(const char *path) +{ + struct sockaddr_un addr; + int sock; + u_int pid; + + if (muxclient_command == 0) { + if (options.stdio_forward_host != NULL) + muxclient_command = SSHMUX_COMMAND_STDIO_FWD; + else + muxclient_command = SSHMUX_COMMAND_OPEN; + } + + switch (options.control_master) { + case SSHCTL_MASTER_AUTO: + case SSHCTL_MASTER_AUTO_ASK: + debug("auto-mux: Trying existing master"); + /* FALLTHROUGH */ + case SSHCTL_MASTER_NO: + break; + default: + return -1; + } + + memset(&addr, '\0', sizeof(addr)); + addr.sun_family = AF_UNIX; + + if (strlcpy(addr.sun_path, path, + sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) + fatal("ControlPath too long ('%s' >= %u bytes)", path, + (unsigned int)sizeof(addr.sun_path)); + + if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + fatal_f("socket(): %s", strerror(errno)); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + switch (muxclient_command) { + case SSHMUX_COMMAND_OPEN: + case SSHMUX_COMMAND_STDIO_FWD: + break; + default: + fatal("Control socket connect(%.100s): %s", path, + strerror(errno)); + } + if (errno == ECONNREFUSED && + options.control_master != SSHCTL_MASTER_NO) { + debug("Stale control socket %.100s, unlinking", path); + unlink(path); + } else if (errno == ENOENT) { + debug("Control socket \"%.100s\" does not exist", path); + } else { + error("Control socket connect(%.100s): %s", path, + strerror(errno)); + } + close(sock); + return -1; + } + set_nonblock(sock); + + if (mux_client_hello_exchange(sock) != 0) { + error_f("master hello exchange failed"); + close(sock); + return -1; + } + + switch (muxclient_command) { + case SSHMUX_COMMAND_ALIVE_CHECK: + if ((pid = mux_client_request_alive(sock)) == 0) + fatal_f("master alive check failed"); + fprintf(stderr, "Master running (pid=%u)\r\n", pid); + exit(0); + case SSHMUX_COMMAND_TERMINATE: + mux_client_request_terminate(sock); + if (options.log_level != SYSLOG_LEVEL_QUIET) + fprintf(stderr, "Exit request sent.\r\n"); + exit(0); + case SSHMUX_COMMAND_FORWARD: + if (mux_client_forwards(sock, 0) != 0) + fatal_f("master forward request failed"); + exit(0); + case SSHMUX_COMMAND_OPEN: + if (mux_client_forwards(sock, 0) != 0) { + error_f("master forward request failed"); + return -1; + } + mux_client_request_session(sock); + return -1; + case SSHMUX_COMMAND_STDIO_FWD: + mux_client_request_stdio_fwd(sock); + exit(0); + case SSHMUX_COMMAND_STOP: + mux_client_request_stop_listening(sock); + if (options.log_level != SYSLOG_LEVEL_QUIET) + fprintf(stderr, "Stop listening request sent.\r\n"); + exit(0); + case SSHMUX_COMMAND_CANCEL_FWD: + if (mux_client_forwards(sock, 1) != 0) + error_f("master cancel forward request failed"); + exit(0); + case SSHMUX_COMMAND_PROXY: + mux_client_proxy(sock); + return (sock); + default: + fatal("unrecognised muxclient_command %d", muxclient_command); + } +} diff --git a/aosp/external/openssh/myproposal.h b/aosp/external/openssh/myproposal.h new file mode 100644 index 0000000000000000000000000000000000000000..ee6e9f7415261c9e7a9d6ad64a2eccf26ec1d45a --- /dev/null +++ b/aosp/external/openssh/myproposal.h @@ -0,0 +1,116 @@ +/* $OpenBSD: myproposal.h,v 1.71 2022/03/30 21:13:23 djm Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define KEX_SERVER_KEX \ + "sntrup761x25519-sha512@openssh.com," \ + "curve25519-sha256," \ + "curve25519-sha256@libssh.org," \ + "ecdh-sha2-nistp256," \ + "ecdh-sha2-nistp384," \ + "ecdh-sha2-nistp521," \ + "diffie-hellman-group-exchange-sha256," \ + "diffie-hellman-group16-sha512," \ + "diffie-hellman-group18-sha512," \ + "diffie-hellman-group14-sha256" + +#define KEX_CLIENT_KEX KEX_SERVER_KEX + +#define KEX_DEFAULT_PK_ALG \ + "ssh-ed25519-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp256-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ + "ecdsa-sha2-nistp521-cert-v01@openssh.com," \ + "sk-ssh-ed25519-cert-v01@openssh.com," \ + "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com," \ + "rsa-sha2-512-cert-v01@openssh.com," \ + "rsa-sha2-256-cert-v01@openssh.com," \ + "ssh-ed25519," \ + "ecdsa-sha2-nistp256," \ + "ecdsa-sha2-nistp384," \ + "ecdsa-sha2-nistp521," \ + "sk-ssh-ed25519@openssh.com," \ + "sk-ecdsa-sha2-nistp256@openssh.com," \ + "rsa-sha2-512," \ + "rsa-sha2-256" + +#define KEX_SERVER_ENCRYPT \ + "chacha20-poly1305@openssh.com," \ + "aes128-ctr,aes192-ctr,aes256-ctr," \ + "aes128-gcm@openssh.com,aes256-gcm@openssh.com" + +#define KEX_CLIENT_ENCRYPT KEX_SERVER_ENCRYPT + +#define KEX_SERVER_MAC \ + "umac-64-etm@openssh.com," \ + "umac-128-etm@openssh.com," \ + "hmac-sha2-256-etm@openssh.com," \ + "hmac-sha2-512-etm@openssh.com," \ + "hmac-sha1-etm@openssh.com," \ + "umac-64@openssh.com," \ + "umac-128@openssh.com," \ + "hmac-sha2-256," \ + "hmac-sha2-512," \ + "hmac-sha1" + +#define KEX_CLIENT_MAC KEX_SERVER_MAC + +/* Not a KEX value, but here so all the algorithm defaults are together */ +#define SSH_ALLOWED_CA_SIGALGS \ + "ssh-ed25519," \ + "ecdsa-sha2-nistp256," \ + "ecdsa-sha2-nistp384," \ + "ecdsa-sha2-nistp521," \ + "sk-ssh-ed25519@openssh.com," \ + "sk-ecdsa-sha2-nistp256@openssh.com," \ + "rsa-sha2-512," \ + "rsa-sha2-256" + +#define KEX_DEFAULT_COMP "none,zlib@openssh.com" +#define KEX_DEFAULT_LANG "" + +#define KEX_CLIENT \ + KEX_CLIENT_KEX, \ + KEX_DEFAULT_PK_ALG, \ + KEX_CLIENT_ENCRYPT, \ + KEX_CLIENT_ENCRYPT, \ + KEX_CLIENT_MAC, \ + KEX_CLIENT_MAC, \ + KEX_DEFAULT_COMP, \ + KEX_DEFAULT_COMP, \ + KEX_DEFAULT_LANG, \ + KEX_DEFAULT_LANG + +#define KEX_SERVER \ + KEX_SERVER_KEX, \ + KEX_DEFAULT_PK_ALG, \ + KEX_SERVER_ENCRYPT, \ + KEX_SERVER_ENCRYPT, \ + KEX_SERVER_MAC, \ + KEX_SERVER_MAC, \ + KEX_DEFAULT_COMP, \ + KEX_DEFAULT_COMP, \ + KEX_DEFAULT_LANG, \ + KEX_DEFAULT_LANG diff --git a/aosp/external/openssh/nchan.c b/aosp/external/openssh/nchan.c new file mode 100644 index 0000000000000000000000000000000000000000..d33426fedf91f139c1788290e1b64d7bc01ab4c8 --- /dev/null +++ b/aosp/external/openssh/nchan.c @@ -0,0 +1,443 @@ +/* $OpenBSD: nchan.c,v 1.74 2022/02/01 23:32:51 djm Exp $ */ +/* + * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "ssh2.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "packet.h" +#include "channels.h" +#include "compat.h" +#include "log.h" + +/* + * SSH Protocol 1.5 aka New Channel Protocol + * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. + * Written by Markus Friedl in October 1999 + * + * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the + * tear down of channels: + * + * 1.3: strict request-ack-protocol: + * CLOSE -> + * <- CLOSE_CONFIRM + * + * 1.5: uses variations of: + * IEOF -> + * <- OCLOSE + * <- IEOF + * OCLOSE -> + * i.e. both sides have to close the channel + * + * 2.0: the EOF messages are optional + * + * See the debugging output from 'ssh -v' and 'sshd -d' of + * ssh-1.2.27 as an example. + * + */ + +/* functions manipulating channel states */ +/* + * EVENTS update channel input/output states execute ACTIONS + */ +/* + * ACTIONS: should never update the channel states + */ +static void chan_send_eof2(struct ssh *, Channel *); +static void chan_send_eow2(struct ssh *, Channel *); + +/* helper */ +static void chan_shutdown_write(struct ssh *, Channel *); +static void chan_shutdown_read(struct ssh *, Channel *); +static void chan_shutdown_extended_read(struct ssh *, Channel *); + +static const char * const ostates[] = { + "open", "drain", "wait_ieof", "closed", +}; +static const char * const istates[] = { + "open", "drain", "wait_oclose", "closed", +}; + +static void +chan_set_istate(Channel *c, u_int next) +{ + if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED) + fatal("chan_set_istate: bad state %d -> %d", c->istate, next); + debug2("channel %d: input %s -> %s", c->self, istates[c->istate], + istates[next]); + c->istate = next; +} + +static void +chan_set_ostate(Channel *c, u_int next) +{ + if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED) + fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next); + debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate], + ostates[next]); + c->ostate = next; +} + +void +chan_read_failed(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: read failed", c->self); + switch (c->istate) { + case CHAN_INPUT_OPEN: + chan_shutdown_read(ssh, c); + chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN); + break; + default: + error("channel %d: chan_read_failed for istate %d", + c->self, c->istate); + break; + } +} + +void +chan_ibuf_empty(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: ibuf empty", c->self); + if (sshbuf_len(c->input)) { + error("channel %d: chan_ibuf_empty for non empty buffer", + c->self); + return; + } + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL))) + chan_send_eof2(ssh, c); + chan_set_istate(c, CHAN_INPUT_CLOSED); + break; + default: + error("channel %d: chan_ibuf_empty for istate %d", + c->self, c->istate); + break; + } +} + +void +chan_obuf_empty(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: obuf empty", c->self); + if (sshbuf_len(c->output)) { + error("channel %d: chan_obuf_empty for non empty buffer", + c->self); + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_WAIT_DRAIN: + chan_shutdown_write(ssh, c); + chan_set_ostate(c, CHAN_OUTPUT_CLOSED); + break; + default: + error("channel %d: internal error: obuf_empty for ostate %d", + c->self, c->ostate); + break; + } +} + +void +chan_rcvd_eow(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: rcvd eow", c->self); + switch (c->istate) { + case CHAN_INPUT_OPEN: + chan_shutdown_read(ssh, c); + chan_set_istate(c, CHAN_INPUT_CLOSED); + break; + } +} + +static void +chan_send_eof2(struct ssh *ssh, Channel *c) +{ + int r; + + debug2("channel %d: send eof", c->self); + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + if (!c->have_remote_id) + fatal_f("channel %d: no remote_id", c->self); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EOF)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send CHANNEL_EOF"); + c->flags |= CHAN_EOF_SENT; + break; + default: + error("channel %d: cannot send eof for istate %d", + c->self, c->istate); + break; + } +} + +static void +chan_send_close2(struct ssh *ssh, Channel *c) +{ + int r; + + debug2("channel %d: send close", c->self); + if (c->ostate != CHAN_OUTPUT_CLOSED || + c->istate != CHAN_INPUT_CLOSED) { + error("channel %d: cannot send close for istate/ostate %d/%d", + c->self, c->istate, c->ostate); + } else if (c->flags & CHAN_CLOSE_SENT) { + error("channel %d: already sent close", c->self); + } else { + if (!c->have_remote_id) + fatal_f("channel %d: no remote_id", c->self); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_CLOSE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send CHANNEL_EOF"); + c->flags |= CHAN_CLOSE_SENT; + } +} + +static void +chan_send_eow2(struct ssh *ssh, Channel *c) +{ + int r; + + debug2("channel %d: send eow", c->self); + if (c->ostate == CHAN_OUTPUT_CLOSED) { + error("channel %d: must not sent eow on closed output", + c->self); + return; + } + if (!(ssh->compat & SSH_NEW_OPENSSH)) + return; + if (!c->have_remote_id) + fatal_f("channel %d: no remote_id", c->self); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_cstring(ssh, "eow@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send CHANNEL_EOF"); +} + +/* shared */ + +void +chan_rcvd_ieof(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: rcvd eof", c->self); + c->flags |= CHAN_EOF_RCVD; + if (c->ostate == CHAN_OUTPUT_OPEN) + chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); + if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN && + sshbuf_len(c->output) == 0 && + !CHANNEL_EFD_OUTPUT_ACTIVE(c)) + chan_obuf_empty(ssh, c); +} + +void +chan_rcvd_oclose(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: rcvd close", c->self); + if (!(c->flags & CHAN_LOCAL)) { + if (c->flags & CHAN_CLOSE_RCVD) + error("channel %d: protocol error: close rcvd twice", + c->self); + c->flags |= CHAN_CLOSE_RCVD; + } + if (c->type == SSH_CHANNEL_LARVAL) { + /* tear down larval channels immediately */ + chan_set_ostate(c, CHAN_OUTPUT_CLOSED); + chan_set_istate(c, CHAN_INPUT_CLOSED); + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + /* + * wait until a data from the channel is consumed if a CLOSE + * is received + */ + chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); + break; + } + switch (c->istate) { + case CHAN_INPUT_OPEN: + chan_shutdown_read(ssh, c); + chan_shutdown_extended_read(ssh, c); + chan_set_istate(c, CHAN_INPUT_CLOSED); + break; + case CHAN_INPUT_WAIT_DRAIN: + if (!(c->flags & CHAN_LOCAL)) + chan_send_eof2(ssh, c); + chan_shutdown_extended_read(ssh, c); + chan_set_istate(c, CHAN_INPUT_CLOSED); + break; + } +} + +void +chan_write_failed(struct ssh *ssh, Channel *c) +{ + debug2("channel %d: write failed", c->self); + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + case CHAN_OUTPUT_WAIT_DRAIN: + chan_shutdown_write(ssh, c); + if (strcmp(c->ctype, "session") == 0) + chan_send_eow2(ssh, c); + chan_set_ostate(c, CHAN_OUTPUT_CLOSED); + break; + default: + error("channel %d: chan_write_failed for ostate %d", + c->self, c->ostate); + break; + } +} + +void +chan_mark_dead(struct ssh *ssh, Channel *c) +{ + c->type = SSH_CHANNEL_ZOMBIE; +} + +int +chan_is_dead(struct ssh *ssh, Channel *c, int do_send) +{ + if (c->type == SSH_CHANNEL_ZOMBIE) { + debug2("channel %d: zombie", c->self); + return 1; + } + if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED) + return 0; + if ((ssh->compat & SSH_BUG_EXTEOF) && + c->extended_usage == CHAN_EXTENDED_WRITE && + c->efd != -1 && + sshbuf_len(c->extended) > 0) { + debug2("channel %d: active efd: %d len %zu", + c->self, c->efd, sshbuf_len(c->extended)); + return 0; + } + if (c->flags & CHAN_LOCAL) { + debug2("channel %d: is dead (local)", c->self); + return 1; + } + if (!(c->flags & CHAN_CLOSE_SENT)) { + if (do_send) { + chan_send_close2(ssh, c); + } else { + /* channel would be dead if we sent a close */ + if (c->flags & CHAN_CLOSE_RCVD) { + debug2("channel %d: almost dead", + c->self); + return 1; + } + } + } + if ((c->flags & CHAN_CLOSE_SENT) && + (c->flags & CHAN_CLOSE_RCVD)) { + debug2("channel %d: is dead", c->self); + return 1; + } + return 0; +} + +/* helper */ +static void +chan_shutdown_write(struct ssh *ssh, Channel *c) +{ + sshbuf_reset(c->output); + if (c->type == SSH_CHANNEL_LARVAL) + return; + /* shutdown failure is allowed if write failed already */ + debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])", + c->self, c->istate, c->ostate, c->sock, c->wfd, c->efd, + channel_format_extended_usage(c)); + if (c->sock != -1) { + if (shutdown(c->sock, SHUT_WR) == -1) { + debug2_f("channel %d: shutdown() failed for " + "fd %d [i%d o%d]: %.100s", c->self, c->sock, + c->istate, c->ostate, strerror(errno)); + } + } else { + if (channel_close_fd(ssh, c, &c->wfd) < 0) { + logit_f("channel %d: close() failed for " + "fd %d [i%d o%d]: %.100s", c->self, c->wfd, + c->istate, c->ostate, strerror(errno)); + } + } +} + +static void +chan_shutdown_read(struct ssh *ssh, Channel *c) +{ + if (c->type == SSH_CHANNEL_LARVAL) + return; + debug2_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])", + c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd, + channel_format_extended_usage(c)); + if (c->sock != -1) { + /* + * shutdown(sock, SHUT_READ) may return ENOTCONN if the + * write side has been closed already. (bug on Linux) + * HP-UX may return ENOTCONN also. + */ + if (shutdown(c->sock, SHUT_RD) == -1 && errno != ENOTCONN) { + error_f("channel %d: shutdown() failed for " + "fd %d [i%d o%d]: %.100s", c->self, c->sock, + c->istate, c->ostate, strerror(errno)); + } + } else { + if (channel_close_fd(ssh, c, &c->rfd) < 0) { + logit_f("channel %d: close() failed for " + "fd %d [i%d o%d]: %.100s", c->self, c->rfd, + c->istate, c->ostate, strerror(errno)); + } + } +} + +static void +chan_shutdown_extended_read(struct ssh *ssh, Channel *c) +{ + if (c->type == SSH_CHANNEL_LARVAL || c->efd == -1) + return; + if (c->extended_usage != CHAN_EXTENDED_READ && + c->extended_usage != CHAN_EXTENDED_IGNORE) + return; + debug_f("channel %d: (i%d o%d sock %d wfd %d efd %d [%s])", + c->self, c->istate, c->ostate, c->sock, c->rfd, c->efd, + channel_format_extended_usage(c)); + if (channel_close_fd(ssh, c, &c->efd) < 0) { + logit_f("channel %d: close() failed for " + "extended fd %d [i%d o%d]: %.100s", c->self, c->efd, + c->istate, c->ostate, strerror(errno)); + } +} diff --git a/aosp/external/openssh/nchan.ms b/aosp/external/openssh/nchan.ms new file mode 100644 index 0000000000000000000000000000000000000000..57576017b3646387fe3d0611d041f6c29c4041fd --- /dev/null +++ b/aosp/external/openssh/nchan.ms @@ -0,0 +1,99 @@ +.\" $OpenBSD: nchan.ms,v 1.8 2003/11/21 11:57:03 djm Exp $ +.\" +.\" +.\" Copyright (c) 1999 Markus Friedl. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.TL +OpenSSH Channel Close Protocol 1.5 Implementation +.SH +Channel Input State Diagram +.PS +reset +l=1 +s=1.2 +ellipsewid=s*ellipsewid +boxwid=s*boxwid +ellipseht=s*ellipseht +S1: ellipse "INPUT" "OPEN" +move right 2*l from last ellipse.e +S4: ellipse "INPUT" "CLOSED" +move down l from last ellipse.s +S3: ellipse "INPUT" "WAIT" "OCLOSED" +move down l from 1st ellipse.s +S2: ellipse "INPUT" "WAIT" "DRAIN" +arrow "" "rcvd OCLOSE/" "shutdown_read" "send IEOF" from S1.e to S4.w +arrow "ibuf_empty/" "send IEOF" from S2.e to S3.w +arrow from S1.s to S2.n +box invis "read_failed/" "shutdown_read" with .e at last arrow.c +arrow from S3.n to S4.s +box invis "rcvd OCLOSE/" "-" with .w at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +arrow from S2.ne to S4.sw +box invis "rcvd OCLOSE/ " with .e at last arrow.c +box invis " send IEOF" with .w at last arrow.c +.PE +.SH +Channel Output State Diagram +.PS +S1: ellipse "OUTPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse "OUTPUT" "WAIT" "IEOF" +move down l from last ellipse.s +S4: ellipse "OUTPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "OUTPUT" "WAIT" "DRAIN" +arrow "" "write_failed/" "shutdown_write" "send OCLOSE" from S1.e to S3.w +arrow "obuf_empty ||" "write_failed/" "shutdown_write" "send OCLOSE" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "rcvd IEOF/" "-" with .e at last arrow.c +arrow from S3.s to S4.n +box invis "rcvd IEOF/" "-" with .w at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Notes +.PP +The input buffer is filled with data from the socket +(the socket represents the local consumer/producer of the +forwarded channel). +The data is then sent over the INPUT-end (transmit-end) of the channel to the +remote peer. +Data sent by the peer is received on the OUTPUT-end (receive-end), +saved in the output buffer and written to the socket. +.PP +If the local protocol instance has forwarded all data on the +INPUT-end of the channel, it sends an IEOF message to the peer. +If the peer receives the IEOF and has consumed all +data he replies with an OCLOSE. +When the local instance receives the OCLOSE +he considers the INPUT-half of the channel closed. +The peer has his OUTOUT-half closed. +.PP +A channel can be deallocated by a protocol instance +if both the INPUT- and the OUTOUT-half on his +side of the channel are closed. +Note that when an instance is unable to consume the +received data, he is permitted to send an OCLOSE +before the matching IEOF is received. diff --git a/aosp/external/openssh/nchan2.ms b/aosp/external/openssh/nchan2.ms new file mode 100644 index 0000000000000000000000000000000000000000..700150450adc06255361614dd7b06e7948dee037 --- /dev/null +++ b/aosp/external/openssh/nchan2.ms @@ -0,0 +1,88 @@ +.\" $OpenBSD: nchan2.ms,v 1.4 2008/05/15 23:52:24 djm Exp $ +.\" +.\" Copyright (c) 2000 Markus Friedl. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.TL +OpenSSH Channel Close Protocol 2.0 Implementation +.SH +Channel Input State Diagram +.PS +reset +l=1 +s=1.2 +ellipsewid=s*ellipsewid +boxwid=s*boxwid +ellipseht=s*ellipseht +S1: ellipse "INPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse invis +move down l from last ellipse.s +S4: ellipse "INPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "INPUT" "WAIT" "DRAIN" +arrow from S1.e to S4.n +box invis "rcvd CLOSE/" "shutdown_read" with .sw at last arrow.c +arrow "ibuf_empty ||" "rcvd CLOSE/" "send EOF" "" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "read_failed ||" "rcvd EOW/" "shutdown_read" with .e at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Channel Output State Diagram +.PS +S1: ellipse "OUTPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse invis +move down l from last ellipse.s +S4: ellipse "OUTPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "OUTPUT" "WAIT" "DRAIN" +arrow from S1.e to S4.n +box invis "write_failed/" "shutdown_write" "send EOW" with .sw at last arrow.c +arrow "obuf_empty ||" "write_failed/" "shutdown_write" "" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "rcvd EOF ||" "rcvd CLOSE/" "-" with .e at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Notes +.PP +The input buffer is filled with data from the socket +(the socket represents the local consumer/producer of the +forwarded channel). +The data is then sent over the INPUT-end (transmit-end) of the channel to the +remote peer. +Data sent by the peer is received on the OUTPUT-end (receive-end), +saved in the output buffer and written to the socket. +.PP +If the local protocol instance has forwarded all data on the +INPUT-end of the channel, it sends an EOF message to the peer. +.PP +A CLOSE message is sent to the peer if +both the INPUT- and the OUTOUT-half of the local +end of the channel are closed. +.PP +The channel can be deallocated by a protocol instance +if a CLOSE message he been both sent and received. diff --git a/aosp/external/openssh/openbsd-compat/Makefile.in b/aosp/external/openssh/openbsd-compat/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..5d53bef5757fb6d49f5d013c3f34bf79b90a5171 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/Makefile.in @@ -0,0 +1,120 @@ +sysconfdir=@sysconfdir@ +piddir=@piddir@ +srcdir=@srcdir@ +top_srcdir=@top_srcdir@ + +VPATH=@srcdir@ +CC=@CC@ +LD=@LD@ +CFLAGS=@CFLAGS@ +CFLAGS_NOPIE=@CFLAGS_NOPIE@ +CPPFLAGS=-I. -I.. -I$(srcdir) -I$(srcdir)/.. @CPPFLAGS@ @DEFS@ +PICFLAG=@PICFLAG@ +LIBS=@LIBS@ +AR=@AR@ +RANLIB=@RANLIB@ +INSTALL=@INSTALL@ +LDFLAGS=-L. @LDFLAGS@ +LDFLAGS_NOPIE=-L. -Lopenbsd-compat/ @LDFLAGS_NOPIE@ + +OPENBSD=base64.o \ + basename.o \ + bcrypt_pbkdf.o \ + bcrypt_pbkdf.o \ + bindresvport.o \ + blowfish.o \ + daemon.o \ + dirname.o \ + explicit_bzero.o \ + fmt_scaled.o \ + freezero.o \ + fnmatch.o \ + getcwd.o \ + getgrouplist.o \ + getopt_long.o \ + getrrsetbyname.o \ + glob.o \ + inet_aton.o \ + inet_ntoa.o \ + inet_ntop.o \ + md5.o \ + memmem.o \ + mktemp.o \ + pwcache.o \ + readpassphrase.o \ + reallocarray.o \ + recallocarray.o \ + rresvport.o \ + setenv.o \ + setproctitle.o \ + sha1.o \ + sha2.o \ + sigact.o \ + strcasestr.o \ + strlcat.o \ + strlcpy.o \ + strmode.o \ + strndup.o \ + strnlen.o \ + strptime.o \ + strsep.o \ + strtoll.o \ + strtonum.o \ + strtoull.o \ + strtoul.o \ + timingsafe_bcmp.o \ + vis.o + +COMPAT= arc4random.o \ + bsd-asprintf.o \ + bsd-closefrom.o \ + bsd-cygwin_util.o \ + bsd-err.o \ + bsd-flock.o \ + bsd-getline.o \ + bsd-getpagesize.o \ + bsd-getpeereid.o \ + bsd-malloc.o \ + bsd-misc.o \ + bsd-nextstep.o \ + bsd-openpty.o \ + bsd-poll.o \ + bsd-pselect.o \ + bsd-setres_id.o \ + bsd-signal.o \ + bsd-snprintf.o \ + bsd-statvfs.o \ + bsd-waitpid.o \ + fake-rfc2553.o \ + getrrsetbyname-ldns.o \ + kludge-fd_set.o \ + openssl-compat.o \ + libressl-api-compat.o \ + xcrypt.o + +PORTS= port-aix.o \ + port-irix.o \ + port-linux.o \ + port-prngd.o \ + port-solaris.o \ + port-net.o \ + port-uw.o + +.c.o: + $(CC) $(CFLAGS_NOPIE) $(PICFLAG) $(CPPFLAGS) -c $< + +all: libopenbsd-compat.a + +$(COMPAT): ../config.h +$(OPENBSD): ../config.h +$(PORTS): ../config.h + +libopenbsd-compat.a: $(COMPAT) $(OPENBSD) $(PORTS) + $(AR) rv $@ $(COMPAT) $(OPENBSD) $(PORTS) + $(RANLIB) $@ + +clean: + rm -f *.o *.a core + +distclean: clean + rm -f Makefile *~ diff --git a/aosp/external/openssh/openbsd-compat/arc4random.c b/aosp/external/openssh/openbsd-compat/arc4random.c new file mode 100644 index 0000000000000000000000000000000000000000..ce5f054f1e2353239206972594943b4b6ba68039 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/arc4random.c @@ -0,0 +1,346 @@ +/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */ + +/* $OpenBSD: arc4random.c,v 1.25 2013/10/01 18:34:57 markus Exp $ */ + +/* + * Copyright (c) 1996, David Mazieres + * Copyright (c) 2008, Damien Miller + * Copyright (c) 2013, Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * ChaCha based random number generator for OpenBSD. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#ifdef HAVE_SYS_RANDOM_H +# include +#endif + +#ifndef HAVE_ARC4RANDOM + +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + +#ifdef WITH_OPENSSL +#include +#include +#endif + +#include "log.h" + +#define KEYSTREAM_ONLY +#include "chacha_private.h" + +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +/* OpenSSH isn't multithreaded */ +#define _ARC4_LOCK() +#define _ARC4_UNLOCK() + +#define KEYSZ 32 +#define IVSZ 8 +#define BLOCKSZ 64 +#define RSBUFSZ (16*BLOCKSZ) +static int rs_initialized; +static pid_t rs_stir_pid; +static chacha_ctx rs; /* chacha context for random keystream */ +static u_char rs_buf[RSBUFSZ]; /* keystream blocks */ +static size_t rs_have; /* valid bytes at end of rs_buf */ +static size_t rs_count; /* bytes till reseed */ + +static inline void _rs_rekey(u_char *dat, size_t datlen); + +static inline void +_rs_init(u_char *buf, size_t n) +{ + if (n < KEYSZ + IVSZ) + return; + chacha_keysetup(&rs, buf, KEYSZ * 8); + chacha_ivsetup(&rs, buf + KEYSZ); +} + +#ifndef WITH_OPENSSL +# ifndef SSH_RANDOM_DEV +# define SSH_RANDOM_DEV "/dev/urandom" +# endif /* SSH_RANDOM_DEV */ +static void +getrnd(u_char *s, size_t len) +{ + int fd, save_errno; + ssize_t r; + size_t o = 0; + +#ifdef HAVE_GETRANDOM + if ((r = getrandom(s, len, 0)) > 0 && (size_t)r == len) + return; +#endif /* HAVE_GETRANDOM */ + + if ((fd = open(SSH_RANDOM_DEV, O_RDONLY)) == -1) { + save_errno = errno; + /* Try egd/prngd before giving up. */ + if (seed_from_prngd(s, len) == 0) + return; + fatal("Couldn't open %s: %s", SSH_RANDOM_DEV, + strerror(save_errno)); + } + while (o < len) { + r = read(fd, s + o, len - o); + if (r < 0) { + if (errno == EAGAIN || errno == EINTR || + errno == EWOULDBLOCK) + continue; + fatal("read %s: %s", SSH_RANDOM_DEV, strerror(errno)); + } + o += r; + } + close(fd); +} +#endif /* WITH_OPENSSL */ + +static void +_rs_stir(void) +{ + u_char rnd[KEYSZ + IVSZ]; + +#ifdef WITH_OPENSSL + if (RAND_bytes(rnd, sizeof(rnd)) <= 0) + fatal("Couldn't obtain random bytes (error 0x%lx)", + (unsigned long)ERR_get_error()); +#else + getrnd(rnd, sizeof(rnd)); +#endif + + if (!rs_initialized) { + rs_initialized = 1; + _rs_init(rnd, sizeof(rnd)); + } else + _rs_rekey(rnd, sizeof(rnd)); + explicit_bzero(rnd, sizeof(rnd)); + + /* invalidate rs_buf */ + rs_have = 0; + memset(rs_buf, 0, RSBUFSZ); + + rs_count = 1600000; +} + +static inline void +_rs_stir_if_needed(size_t len) +{ + pid_t pid = getpid(); + + if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) { + rs_stir_pid = pid; + _rs_stir(); + } else + rs_count -= len; +} + +static inline void +_rs_rekey(u_char *dat, size_t datlen) +{ +#ifndef KEYSTREAM_ONLY + memset(rs_buf, 0,RSBUFSZ); +#endif + /* fill rs_buf with the keystream */ + chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ); + /* mix in optional user provided data */ + if (dat) { + size_t i, m; + + m = MINIMUM(datlen, KEYSZ + IVSZ); + for (i = 0; i < m; i++) + rs_buf[i] ^= dat[i]; + } + /* immediately reinit for backtracking resistance */ + _rs_init(rs_buf, KEYSZ + IVSZ); + memset(rs_buf, 0, KEYSZ + IVSZ); + rs_have = RSBUFSZ - KEYSZ - IVSZ; +} + +static inline void +_rs_random_buf(void *_buf, size_t n) +{ + u_char *buf = (u_char *)_buf; + size_t m; + + _rs_stir_if_needed(n); + while (n > 0) { + if (rs_have > 0) { + m = MINIMUM(n, rs_have); + memcpy(buf, rs_buf + RSBUFSZ - rs_have, m); + memset(rs_buf + RSBUFSZ - rs_have, 0, m); + buf += m; + n -= m; + rs_have -= m; + } + if (rs_have == 0) + _rs_rekey(NULL, 0); + } +} + +static inline void +_rs_random_u32(u_int32_t *val) +{ + _rs_stir_if_needed(sizeof(*val)); + if (rs_have < sizeof(*val)) + _rs_rekey(NULL, 0); + memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val)); + memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val)); + rs_have -= sizeof(*val); + return; +} + +void +arc4random_stir(void) +{ + _ARC4_LOCK(); + _rs_stir(); + _ARC4_UNLOCK(); +} + +void +arc4random_addrandom(u_char *dat, int datlen) +{ + int m; + + _ARC4_LOCK(); + if (!rs_initialized) + _rs_stir(); + while (datlen > 0) { + m = MINIMUM(datlen, KEYSZ + IVSZ); + _rs_rekey(dat, m); + dat += m; + datlen -= m; + } + _ARC4_UNLOCK(); +} + +u_int32_t +arc4random(void) +{ + u_int32_t val; + + _ARC4_LOCK(); + _rs_random_u32(&val); + _ARC4_UNLOCK(); + return val; +} + +/* + * If we are providing arc4random, then we can provide a more efficient + * arc4random_buf(). + */ +# ifndef HAVE_ARC4RANDOM_BUF +void +arc4random_buf(void *buf, size_t n) +{ + _ARC4_LOCK(); + _rs_random_buf(buf, n); + _ARC4_UNLOCK(); +} +# endif /* !HAVE_ARC4RANDOM_BUF */ +#endif /* !HAVE_ARC4RANDOM */ + +/* arc4random_buf() that uses platform arc4random() */ +#if !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) +void +arc4random_buf(void *_buf, size_t n) +{ + size_t i; + u_int32_t r = 0; + char *buf = (char *)_buf; + + for (i = 0; i < n; i++) { + if (i % 4 == 0) + r = arc4random(); + buf[i] = r & 0xff; + r >>= 8; + } + explicit_bzero(&r, sizeof(r)); +} +#endif /* !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) */ + +#ifndef HAVE_ARC4RANDOM_UNIFORM +/* + * Calculate a uniformly distributed random number less than upper_bound + * avoiding "modulo bias". + * + * Uniformity is achieved by generating new random numbers until the one + * returned is outside the range [0, 2**32 % upper_bound). This + * guarantees the selected random number will be inside + * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) + * after reduction modulo upper_bound. + */ +u_int32_t +arc4random_uniform(u_int32_t upper_bound) +{ + u_int32_t r, min; + + if (upper_bound < 2) + return 0; + + /* 2**32 % x == (2**32 - x) % x */ + min = -upper_bound % upper_bound; + + /* + * This could theoretically loop forever but each retry has + * p > 0.5 (worst case, usually far better) of selecting a + * number inside the range we need, so it should rarely need + * to re-roll. + */ + for (;;) { + r = arc4random(); + if (r >= min) + break; + } + + return r % upper_bound; +} +#endif /* !HAVE_ARC4RANDOM_UNIFORM */ + +#if 0 +/*-------- Test code for i386 --------*/ +#include +#include +int +main(int argc, char **argv) +{ + const int iter = 1000000; + int i; + pctrval v; + + v = rdtsc(); + for (i = 0; i < iter; i++) + arc4random(); + v = rdtsc() - v; + v /= iter; + + printf("%qd cycles\n", v); + exit(0); +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/base64.c b/aosp/external/openssh/openbsd-compat/base64.c new file mode 100644 index 0000000000000000000000000000000000000000..e5faba3c52b956967456235f8daed06918c2f146 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/base64.c @@ -0,0 +1,314 @@ +/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + +#include "includes.h" + +#if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "base64.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +#if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) +{ + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + u_int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} +#endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ + +#if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(char const *src, u_char *target, size_t targsize) +{ + u_int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + +#endif /* !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) */ +#endif diff --git a/aosp/external/openssh/openbsd-compat/base64.h b/aosp/external/openssh/openbsd-compat/base64.h new file mode 100644 index 0000000000000000000000000000000000000000..bd772931b46df83110b327e95113865697adf436 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/base64.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _BSD_BASE64_H +#define _BSD_BASE64_H + +#include "includes.h" + +#ifndef HAVE___B64_NTOP +# ifndef HAVE_B64_NTOP +int b64_ntop(u_char const *src, size_t srclength, char *target, + size_t targsize); +# endif /* !HAVE_B64_NTOP */ +# define __b64_ntop(a,b,c,d) b64_ntop(a,b,c,d) +#endif /* HAVE___B64_NTOP */ + +#ifndef HAVE___B64_PTON +# ifndef HAVE_B64_PTON +int b64_pton(char const *src, u_char *target, size_t targsize); +# endif /* !HAVE_B64_PTON */ +# define __b64_pton(a,b,c) b64_pton(a,b,c) +#endif /* HAVE___B64_PTON */ + +#endif /* _BSD_BASE64_H */ diff --git a/aosp/external/openssh/openbsd-compat/basename.c b/aosp/external/openssh/openbsd-compat/basename.c new file mode 100644 index 0000000000000000000000000000000000000000..ffa5c898461ef1772948ef97f5d8aa02af79ad61 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/basename.c @@ -0,0 +1,67 @@ +/* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $ */ + +/* + * Copyright (c) 1997, 2004 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/basename.c */ + +#include "includes.h" +#ifndef HAVE_BASENAME +#include +#include + +char * +basename(const char *path) +{ + static char bname[MAXPATHLEN]; + size_t len; + const char *endp, *startp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + bname[0] = '.'; + bname[1] = '\0'; + return (bname); + } + + /* Strip any trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* All slashes becomes "/" */ + if (endp == path && *endp == '/') { + bname[0] = '/'; + bname[1] = '\0'; + return (bname); + } + + /* Find the start of the base */ + startp = endp; + while (startp > path && *(startp - 1) != '/') + startp--; + + len = endp - startp + 1; + if (len >= sizeof(bname)) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(bname, startp, len); + bname[len] = '\0'; + return (bname); +} + +#endif /* !defined(HAVE_BASENAME) */ diff --git a/aosp/external/openssh/openbsd-compat/bcrypt_pbkdf.c b/aosp/external/openssh/openbsd-compat/bcrypt_pbkdf.c new file mode 100644 index 0000000000000000000000000000000000000000..5a22ba3b4258d61b236840719f046e5ed2df6c3a --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bcrypt_pbkdf.c @@ -0,0 +1,188 @@ +/* $OpenBSD: bcrypt_pbkdf.c,v 1.16 2020/08/02 18:35:48 tb Exp $ */ +/* + * Copyright (c) 2013 Ted Unangst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libutil/bcrypt_pbkdf.c */ + +/* This version has been modified to use SHA512 from SUPERCOP */ + +#include "includes.h" + +#ifndef HAVE_BCRYPT_PBKDF + +#include + +#ifdef HAVE_STDLIB_H +# include +#endif +#include + +#ifdef HAVE_BLF_H +# include +#endif + +#include "crypto_api.h" +#ifdef SHA512_DIGEST_LENGTH +# undef SHA512_DIGEST_LENGTH +#endif +#define SHA512_DIGEST_LENGTH crypto_hash_sha512_BYTES + +#define MINIMUM(a,b) (((a) < (b)) ? (a) : (b)) + +/* + * pkcs #5 pbkdf2 implementation using the "bcrypt" hash + * + * The bcrypt hash function is derived from the bcrypt password hashing + * function with the following modifications: + * 1. The input password and salt are preprocessed with SHA512. + * 2. The output length is expanded to 256 bits. + * 3. Subsequently the magic string to be encrypted is lengthened and modifed + * to "OxychromaticBlowfishSwatDynamite" + * 4. The hash function is defined to perform 64 rounds of initial state + * expansion. (More rounds are performed by iterating the hash.) + * + * Note that this implementation pulls the SHA512 operations into the caller + * as a performance optimization. + * + * One modification from official pbkdf2. Instead of outputting key material + * linearly, we mix it. pbkdf2 has a known weakness where if one uses it to + * generate (e.g.) 512 bits of key material for use as two 256 bit keys, an + * attacker can merely run once through the outer loop, but the user + * always runs it twice. Shuffling output bytes requires computing the + * entirety of the key material to assemble any subkey. This is something a + * wise caller could do; we just do it for you. + */ + +#define BCRYPT_WORDS 8 +#define BCRYPT_HASHSIZE (BCRYPT_WORDS * 4) + +static void +bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out) +{ + blf_ctx state; + uint8_t ciphertext[BCRYPT_HASHSIZE] = + "OxychromaticBlowfishSwatDynamite"; + uint32_t cdata[BCRYPT_WORDS]; + int i; + uint16_t j; + size_t shalen = SHA512_DIGEST_LENGTH; + + /* key expansion */ + Blowfish_initstate(&state); + Blowfish_expandstate(&state, sha2salt, shalen, sha2pass, shalen); + for (i = 0; i < 64; i++) { + Blowfish_expand0state(&state, sha2salt, shalen); + Blowfish_expand0state(&state, sha2pass, shalen); + } + + /* encryption */ + j = 0; + for (i = 0; i < BCRYPT_WORDS; i++) + cdata[i] = Blowfish_stream2word(ciphertext, sizeof(ciphertext), + &j); + for (i = 0; i < 64; i++) + blf_enc(&state, cdata, BCRYPT_WORDS / 2); + + /* copy out */ + for (i = 0; i < BCRYPT_WORDS; i++) { + out[4 * i + 3] = (cdata[i] >> 24) & 0xff; + out[4 * i + 2] = (cdata[i] >> 16) & 0xff; + out[4 * i + 1] = (cdata[i] >> 8) & 0xff; + out[4 * i + 0] = cdata[i] & 0xff; + } + + /* zap */ + explicit_bzero(ciphertext, sizeof(ciphertext)); + explicit_bzero(cdata, sizeof(cdata)); + explicit_bzero(&state, sizeof(state)); +} + +int +bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltlen, + uint8_t *key, size_t keylen, unsigned int rounds) +{ + uint8_t sha2pass[SHA512_DIGEST_LENGTH]; + uint8_t sha2salt[SHA512_DIGEST_LENGTH]; + uint8_t out[BCRYPT_HASHSIZE]; + uint8_t tmpout[BCRYPT_HASHSIZE]; + uint8_t *countsalt; + size_t i, j, amt, stride; + uint32_t count; + size_t origkeylen = keylen; + + /* nothing crazy */ + if (rounds < 1) + goto bad; + if (passlen == 0 || saltlen == 0 || keylen == 0 || + keylen > sizeof(out) * sizeof(out) || saltlen > 1<<20) + goto bad; + if ((countsalt = calloc(1, saltlen + 4)) == NULL) + goto bad; + stride = (keylen + sizeof(out) - 1) / sizeof(out); + amt = (keylen + stride - 1) / stride; + + memcpy(countsalt, salt, saltlen); + + /* collapse password */ + crypto_hash_sha512(sha2pass, pass, passlen); + + /* generate key, sizeof(out) at a time */ + for (count = 1; keylen > 0; count++) { + countsalt[saltlen + 0] = (count >> 24) & 0xff; + countsalt[saltlen + 1] = (count >> 16) & 0xff; + countsalt[saltlen + 2] = (count >> 8) & 0xff; + countsalt[saltlen + 3] = count & 0xff; + + /* first round, salt is salt */ + crypto_hash_sha512(sha2salt, countsalt, saltlen + 4); + + bcrypt_hash(sha2pass, sha2salt, tmpout); + memcpy(out, tmpout, sizeof(out)); + + for (i = 1; i < rounds; i++) { + /* subsequent rounds, salt is previous output */ + crypto_hash_sha512(sha2salt, tmpout, sizeof(tmpout)); + bcrypt_hash(sha2pass, sha2salt, tmpout); + for (j = 0; j < sizeof(out); j++) + out[j] ^= tmpout[j]; + } + + /* + * pbkdf2 deviation: output the key material non-linearly. + */ + amt = MINIMUM(amt, keylen); + for (i = 0; i < amt; i++) { + size_t dest = i * stride + (count - 1); + if (dest >= origkeylen) + break; + key[dest] = out[i]; + } + keylen -= i; + } + + /* zap */ + freezero(countsalt, saltlen + 4); + explicit_bzero(out, sizeof(out)); + explicit_bzero(tmpout, sizeof(tmpout)); + + return 0; + +bad: + /* overwrite with random in case caller doesn't check return code */ + arc4random_buf(key, keylen); + return -1; +} +#endif /* HAVE_BCRYPT_PBKDF */ diff --git a/aosp/external/openssh/openbsd-compat/bindresvport.c b/aosp/external/openssh/openbsd-compat/bindresvport.c new file mode 100644 index 0000000000000000000000000000000000000000..346c7fe5654a10c94aa63588d46c2ef2547e779c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bindresvport.c @@ -0,0 +1,120 @@ +/* This file has be substantially modified from the original OpenBSD source */ + +/* $OpenBSD: bindresvport.c,v 1.17 2005/12/21 01:40:22 millert Exp $ */ + +/* + * Copyright 1996, Jason Downs. All rights reserved. + * Copyright 1998, Theo de Raadt. All rights reserved. + * Copyright 2000, Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/rpc/bindresvport.c */ + +#include "includes.h" + +#ifndef HAVE_BINDRESVPORT_SA +#include +#include + +#include +#include + +#include +#include +#include + +#define STARTPORT 600 +#define ENDPORT (IPPORT_RESERVED - 1) +#define NPORTS (ENDPORT - STARTPORT + 1) + +/* + * Bind a socket to a privileged IP port + */ +int +bindresvport_sa(int sd, struct sockaddr *sa) +{ + int error, af; + struct sockaddr_storage myaddr; + struct sockaddr_in *in; + struct sockaddr_in6 *in6; + u_int16_t *portp; + u_int16_t port; + socklen_t salen; + int i; + + if (sa == NULL) { + memset(&myaddr, 0, sizeof(myaddr)); + sa = (struct sockaddr *)&myaddr; + salen = sizeof(myaddr); + + if (getsockname(sd, sa, &salen) == -1) + return -1; /* errno is correctly set */ + + af = sa->sa_family; + memset(&myaddr, 0, salen); + } else + af = sa->sa_family; + + if (af == AF_INET) { + in = (struct sockaddr_in *)sa; + salen = sizeof(struct sockaddr_in); + portp = &in->sin_port; + } else if (af == AF_INET6) { + in6 = (struct sockaddr_in6 *)sa; + salen = sizeof(struct sockaddr_in6); + portp = &in6->sin6_port; + } else { + errno = EPFNOSUPPORT; + return (-1); + } + sa->sa_family = af; + + port = ntohs(*portp); + if (port == 0) + port = arc4random_uniform(NPORTS) + STARTPORT; + + /* Avoid warning */ + error = -1; + + for(i = 0; i < NPORTS; i++) { + *portp = htons(port); + + error = bind(sd, sa, salen); + + /* Terminate on success */ + if (error == 0) + break; + + /* Terminate on errors, except "address already in use" */ + if ((error < 0) && !((errno == EADDRINUSE) || (errno == EINVAL))) + break; + + port++; + if (port > ENDPORT) + port = STARTPORT; + } + + return (error); +} + +#endif /* HAVE_BINDRESVPORT_SA */ diff --git a/aosp/external/openssh/openbsd-compat/blf.h b/aosp/external/openssh/openbsd-compat/blf.h new file mode 100644 index 0000000000000000000000000000000000000000..5b8a73e55aca6fb222a62b3bdb8daa8f21a1ae32 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/blf.h @@ -0,0 +1,85 @@ +/* $OpenBSD: blf.h,v 1.8 2021/11/29 01:04:45 djm Exp $ */ +/* + * Blowfish - a fast block cipher designed by Bruce Schneier + * + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BLF_H_ +#define _BLF_H_ + +#include "includes.h" + +#if !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H) + +/* Schneier specifies a maximum key length of 56 bytes. + * This ensures that every key bit affects every cipher + * bit. However, the subkeys can hold up to 72 bytes. + * Warning: For normal blowfish encryption only 56 bytes + * of the key affect all cipherbits. + */ + +#define BLF_N 16 /* Number of Subkeys */ +#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */ +#define BLF_MAXUTILIZED ((BLF_N+2)*4) /* 576 bits */ + +/* Blowfish context */ +typedef struct BlowfishContext { + u_int32_t S[4][256]; /* S-Boxes */ + u_int32_t P[BLF_N + 2]; /* Subkeys */ +} blf_ctx; + +/* Raw access to customized Blowfish + * blf_key is just: + * Blowfish_initstate( state ) + * Blowfish_expand0state( state, key, keylen ) + */ + +void Blowfish_encipher(blf_ctx *, u_int32_t *, u_int32_t *); +void Blowfish_decipher(blf_ctx *, u_int32_t *, u_int32_t *); +void Blowfish_initstate(blf_ctx *); +void Blowfish_expand0state(blf_ctx *, const u_int8_t *, u_int16_t); +void Blowfish_expandstate +(blf_ctx *, const u_int8_t *, u_int16_t, const u_int8_t *, u_int16_t); + +/* Standard Blowfish */ + +void blf_key(blf_ctx *, const u_int8_t *, u_int16_t); +void blf_enc(blf_ctx *, u_int32_t *, u_int16_t); +void blf_dec(blf_ctx *, u_int32_t *, u_int16_t); + +void blf_ecb_encrypt(blf_ctx *, u_int8_t *, u_int32_t); +void blf_ecb_decrypt(blf_ctx *, u_int8_t *, u_int32_t); + +void blf_cbc_encrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); +void blf_cbc_decrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); + +/* Converts u_int8_t to u_int32_t */ +u_int32_t Blowfish_stream2word(const u_int8_t *, u_int16_t , u_int16_t *); + +#endif /* !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H) */ +#endif /* _BLF_H */ + diff --git a/aosp/external/openssh/openbsd-compat/blowfish.c b/aosp/external/openssh/openbsd-compat/blowfish.c new file mode 100644 index 0000000000000000000000000000000000000000..bfeba47c0db3ea94c4b546b1a8cdbef5dde7b53b --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/blowfish.c @@ -0,0 +1,693 @@ +/* $OpenBSD: blowfish.c,v 1.20 2021/11/29 01:04:45 djm Exp $ */ +/* + * Blowfish block cipher for OpenBSD + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Implementation advice by David Mazieres . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code is derived from section 14.3 and the given source + * in section V of Applied Cryptography, second edition. + * Blowfish is an unpatented fast block cipher designed by + * Bruce Schneier. + */ + +#include "includes.h" + +#if !defined(HAVE_BCRYPT_PBKDF) && (!defined(HAVE_BLOWFISH_INITSTATE) || \ + !defined(HAVE_BLOWFISH_EXPAND0STATE) || !defined(HAVE_BLF_ENC)) + +#if 0 +#include /* used for debugging */ +#include +#endif + +#include +#ifdef HAVE_BLF_H +#include +#endif + +#undef inline +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +/* Function for Feistel Networks */ + +#define F(s, x) ((((s)[ (((x)>>24)&0xFF)] \ + + (s)[0x100 + (((x)>>16)&0xFF)]) \ + ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \ + + (s)[0x300 + ( (x) &0xFF)]) + +#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n]) + +void +Blowfish_encipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) +{ + u_int32_t Xl; + u_int32_t Xr; + u_int32_t *s = c->S[0]; + u_int32_t *p = c->P; + + Xl = *xl; + Xr = *xr; + + Xl ^= p[0]; + BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2); + BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4); + BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6); + BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8); + BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10); + BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12); + BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14); + BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16); + + *xl = Xr ^ p[17]; + *xr = Xl; +} + +void +Blowfish_decipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) +{ + u_int32_t Xl; + u_int32_t Xr; + u_int32_t *s = c->S[0]; + u_int32_t *p = c->P; + + Xl = *xl; + Xr = *xr; + + Xl ^= p[17]; + BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15); + BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13); + BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11); + BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9); + BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7); + BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5); + BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3); + BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1); + + *xl = Xr ^ p[0]; + *xr = Xl; +} + +void +Blowfish_initstate(blf_ctx *c) +{ + /* P-box and S-box tables initialized with digits of Pi */ + + static const blf_ctx initstate = + { { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a}, + { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7}, + { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0}, + { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6} + }, + { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + } }; + + *c = initstate; +} + +u_int32_t +Blowfish_stream2word(const u_int8_t *data, u_int16_t databytes, + u_int16_t *current) +{ + u_int8_t i; + u_int16_t j; + u_int32_t temp; + + temp = 0x00000000; + j = *current; + + for (i = 0; i < 4; i++, j++) { + if (j >= databytes) + j = 0; + temp = (temp << 8) | data[j]; + } + + *current = j; + return temp; +} + +void +Blowfish_expand0state(blf_ctx *c, const u_int8_t *key, u_int16_t keybytes) +{ + u_int16_t i; + u_int16_t j; + u_int16_t k; + u_int32_t temp; + u_int32_t datal; + u_int32_t datar; + + j = 0; + for (i = 0; i < BLF_N + 2; i++) { + /* Extract 4 int8 to 1 int32 from keystream */ + temp = Blowfish_stream2word(key, keybytes, &j); + c->P[i] = c->P[i] ^ temp; + } + + j = 0; + datal = 0x00000000; + datar = 0x00000000; + for (i = 0; i < BLF_N + 2; i += 2) { + Blowfish_encipher(c, &datal, &datar); + + c->P[i] = datal; + c->P[i + 1] = datar; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + Blowfish_encipher(c, &datal, &datar); + + c->S[i][k] = datal; + c->S[i][k + 1] = datar; + } + } +} + + +void +Blowfish_expandstate(blf_ctx *c, const u_int8_t *data, u_int16_t databytes, + const u_int8_t *key, u_int16_t keybytes) +{ + u_int16_t i; + u_int16_t j; + u_int16_t k; + u_int32_t temp; + u_int32_t datal; + u_int32_t datar; + + j = 0; + for (i = 0; i < BLF_N + 2; i++) { + /* Extract 4 int8 to 1 int32 from keystream */ + temp = Blowfish_stream2word(key, keybytes, &j); + c->P[i] = c->P[i] ^ temp; + } + + j = 0; + datal = 0x00000000; + datar = 0x00000000; + for (i = 0; i < BLF_N + 2; i += 2) { + datal ^= Blowfish_stream2word(data, databytes, &j); + datar ^= Blowfish_stream2word(data, databytes, &j); + Blowfish_encipher(c, &datal, &datar); + + c->P[i] = datal; + c->P[i + 1] = datar; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + datal ^= Blowfish_stream2word(data, databytes, &j); + datar ^= Blowfish_stream2word(data, databytes, &j); + Blowfish_encipher(c, &datal, &datar); + + c->S[i][k] = datal; + c->S[i][k + 1] = datar; + } + } + +} + +void +blf_key(blf_ctx *c, const u_int8_t *k, u_int16_t len) +{ + /* Initialize S-boxes and subkeys with Pi */ + Blowfish_initstate(c); + + /* Transform S-boxes and subkeys with key */ + Blowfish_expand0state(c, k, len); +} + +void +blf_enc(blf_ctx *c, u_int32_t *data, u_int16_t blocks) +{ + u_int32_t *d; + u_int16_t i; + + d = data; + for (i = 0; i < blocks; i++) { + Blowfish_encipher(c, d, d + 1); + d += 2; + } +} + +void +blf_dec(blf_ctx *c, u_int32_t *data, u_int16_t blocks) +{ + u_int32_t *d; + u_int16_t i; + + d = data; + for (i = 0; i < blocks; i++) { + Blowfish_decipher(c, d, d + 1); + d += 2; + } +} + +void +blf_ecb_encrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i; + + for (i = 0; i < len; i += 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_encipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + data += 8; + } +} + +void +blf_ecb_decrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i; + + for (i = 0; i < len; i += 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + data += 8; + } +} + +void +blf_cbc_encrypt(blf_ctx *c, u_int8_t *iv, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i, j; + + for (i = 0; i < len; i += 8) { + for (j = 0; j < 8; j++) + data[j] ^= iv[j]; + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_encipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + iv = data; + data += 8; + } +} + +void +blf_cbc_decrypt(blf_ctx *c, u_int8_t *iva, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int8_t *iv; + u_int32_t i, j; + + iv = data + len - 16; + data = data + len - 8; + for (i = len - 8; i >= 8; i -= 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + for (j = 0; j < 8; j++) + data[j] ^= iv[j]; + iv -= 8; + data -= 8; + } + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + for (j = 0; j < 8; j++) + data[j] ^= iva[j]; +} + +#if 0 +void +report(u_int32_t data[], u_int16_t len) +{ + u_int16_t i; + for (i = 0; i < len; i += 2) + printf("Block %0hd: %08lx %08lx.\n", + i / 2, data[i], data[i + 1]); +} +void +main(void) +{ + + blf_ctx c; + char key[] = "AAAAA"; + char key2[] = "abcdefghijklmnopqrstuvwxyz"; + + u_int32_t data[10]; + u_int32_t data2[] = + {0x424c4f57l, 0x46495348l}; + + u_int16_t i; + + /* First test */ + for (i = 0; i < 10; i++) + data[i] = i; + + blf_key(&c, (u_int8_t *) key, 5); + blf_enc(&c, data, 5); + blf_dec(&c, data, 1); + blf_dec(&c, data + 2, 4); + printf("Should read as 0 - 9.\n"); + report(data, 10); + + /* Second test */ + blf_key(&c, (u_int8_t *) key2, strlen(key2)); + blf_enc(&c, data2, 1); + printf("\nShould read as: 0x324ed0fe 0xf413a203.\n"); + report(data2, 2); + blf_dec(&c, data2, 1); + report(data2, 2); +} +#endif + +#endif /* !defined(HAVE_BCRYPT_PBKDF) && (!defined(HAVE_BLOWFISH_INITSTATE) || \ + !defined(HAVE_BLOWFISH_EXPAND0STATE) || !defined(HAVE_BLF_ENC)) */ + diff --git a/aosp/external/openssh/openbsd-compat/bsd-asprintf.c b/aosp/external/openssh/openbsd-compat/bsd-asprintf.c new file mode 100644 index 0000000000000000000000000000000000000000..1092772717fdcaef26bc8f8f7f4155a7bb7805a8 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-asprintf.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2004 Darren Tucker. + * + * Based originally on asprintf.c from OpenBSD: + * Copyright (c) 1997 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +/* + * Don't let systems with broken printf(3) avoid our replacements + * via asprintf(3)/vasprintf(3) calling libc internally. + */ +#if defined(BROKEN_SNPRINTF) +# undef HAVE_VASPRINTF +# undef HAVE_ASPRINTF +#endif + +#ifndef HAVE_VASPRINTF + +#include +#include +#include + +#define INIT_SZ 128 + +int +vasprintf(char **str, const char *fmt, va_list ap) +{ + int ret = -1; + va_list ap2; + char *string, *newstr; + size_t len; + + VA_COPY(ap2, ap); + if ((string = malloc(INIT_SZ)) == NULL) + goto fail; + + ret = vsnprintf(string, INIT_SZ, fmt, ap2); + if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */ + *str = string; + } else if (ret == INT_MAX || ret < 0) { /* Bad length */ + free(string); + goto fail; + } else { /* bigger than initial, realloc allowing for nul */ + len = (size_t)ret + 1; + if ((newstr = realloc(string, len)) == NULL) { + free(string); + goto fail; + } else { + va_end(ap2); + VA_COPY(ap2, ap); + ret = vsnprintf(newstr, len, fmt, ap2); + if (ret >= 0 && (size_t)ret < len) { + *str = newstr; + } else { /* failed with realloc'ed string, give up */ + free(newstr); + goto fail; + } + } + } + va_end(ap2); + return (ret); + +fail: + *str = NULL; + errno = ENOMEM; + va_end(ap2); + return (-1); +} +#endif + +#ifndef HAVE_ASPRINTF +int asprintf(char **str, const char *fmt, ...) +{ + va_list ap; + int ret; + + *str = NULL; + va_start(ap, fmt); + ret = vasprintf(str, fmt, ap); + va_end(ap); + + return ret; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-closefrom.c b/aosp/external/openssh/openbsd-compat/bsd-closefrom.c new file mode 100644 index 0000000000000000000000000000000000000000..704da531fef949c4e8cb28d12b445c37ffbd4d4e --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-closefrom.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2004-2005 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#if !defined(HAVE_CLOSEFROM) || defined(BROKEN_CLOSEFROM) + +#include +#include +#include +#ifdef HAVE_FCNTL_H +# include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif +#if defined(HAVE_LIBPROC_H) +# include +#endif + +#ifndef OPEN_MAX +# define OPEN_MAX 256 +#endif + +#if 0 +__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; +#endif /* lint */ + +#ifndef HAVE_FCNTL_CLOSEM +/* + * Close all file descriptors greater than or equal to lowfd. + */ +static void +closefrom_fallback(int lowfd) +{ + long fd, maxfd; + + /* + * Fall back on sysconf() or getdtablesize(). We avoid checking + * resource limits since it is possible to open a file descriptor + * and then drop the rlimit such that it is below the open fd. + */ +#ifdef HAVE_SYSCONF + maxfd = sysconf(_SC_OPEN_MAX); +#else + maxfd = getdtablesize(); +#endif /* HAVE_SYSCONF */ + if (maxfd < 0) + maxfd = OPEN_MAX; + + for (fd = lowfd; fd < maxfd; fd++) + (void) close((int) fd); +} +#endif /* HAVE_FCNTL_CLOSEM */ + +#ifdef HAVE_FCNTL_CLOSEM +void +closefrom(int lowfd) +{ + (void) fcntl(lowfd, F_CLOSEM, 0); +} +#elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO) +void +closefrom(int lowfd) +{ + int i, r, sz; + pid_t pid = getpid(); + struct proc_fdinfo *fdinfo_buf = NULL; + + sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (sz == 0) + return; /* no fds, really? */ + else if (sz == -1) + goto fallback; + if ((fdinfo_buf = malloc(sz)) == NULL) + goto fallback; + r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz); + if (r < 0 || r > sz) + goto fallback; + for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) { + if (fdinfo_buf[i].proc_fd >= lowfd) + close(fdinfo_buf[i].proc_fd); + } + free(fdinfo_buf); + return; + fallback: + free(fdinfo_buf); + closefrom_fallback(lowfd); + return; +} +#elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) +void +closefrom(int lowfd) +{ + long fd; + char fdpath[PATH_MAX], *endp; + struct dirent *dent; + DIR *dirp; + int len; + +#ifdef HAVE_CLOSE_RANGE + if (close_range(lowfd, INT_MAX, 0) == 0) + return; +#endif + + /* Check for a /proc/$$/fd directory. */ + len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); + if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { + while ((dent = readdir(dirp)) != NULL) { + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) + (void) close((int) fd); + } + (void) closedir(dirp); + return; + } + /* /proc/$$/fd strategy failed, fall back to brute force closure */ + closefrom_fallback(lowfd); +} +#else +void +closefrom(int lowfd) +{ + closefrom_fallback(lowfd); +} +#endif /* !HAVE_FCNTL_CLOSEM */ +#endif /* HAVE_CLOSEFROM */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-cygwin_util.c b/aosp/external/openssh/openbsd-compat/bsd-cygwin_util.c new file mode 100644 index 0000000000000000000000000000000000000000..9ede21d243a210658f0331535f0f6dce602ca2cc --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-cygwin_util.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2000, 2001, 2011, 2013 Corinna Vinschen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Created: Sat Sep 02 12:17:00 2000 cv + * + * This file contains functions for forcing opened file descriptors to + * binary mode on Windows systems. + */ + +#define NO_BINARY_OPEN /* Avoid redefining open to binary_open for this file */ +#include "includes.h" + +#ifdef HAVE_CYGWIN + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" + +int +binary_open(const char *filename, int flags, ...) +{ + va_list ap; + mode_t mode; + + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + return (open(filename, flags | O_BINARY, mode)); +} + +int +check_ntsec(const char *filename) +{ + return (pathconf(filename, _PC_POSIX_PERMISSIONS)); +} + +const char * +cygwin_ssh_privsep_user() +{ + static char cyg_privsep_user[DNLEN + UNLEN + 2]; + + if (!cyg_privsep_user[0]) + { +#ifdef CW_CYGNAME_FROM_WINNAME + if (cygwin_internal (CW_CYGNAME_FROM_WINNAME, "sshd", cyg_privsep_user, + sizeof cyg_privsep_user) != 0) +#endif + strlcpy(cyg_privsep_user, "sshd", sizeof(cyg_privsep_user)); + } + return cyg_privsep_user; +} + +#define NL(x) x, (sizeof (x) - 1) +#define WENV_SIZ (sizeof (wenv_arr) / sizeof (wenv_arr[0])) + +static struct wenv { + const char *name; + size_t namelen; +} wenv_arr[] = { + { NL("ALLUSERSPROFILE=") }, + { NL("COMPUTERNAME=") }, + { NL("COMSPEC=") }, + { NL("CYGWIN=") }, + { NL("OS=") }, + { NL("PATH=") }, + { NL("PATHEXT=") }, + { NL("PROGRAMFILES=") }, + { NL("SYSTEMDRIVE=") }, + { NL("SYSTEMROOT=") }, + { NL("WINDIR=") } +}; + +char ** +fetch_windows_environment(void) +{ + char **e, **p; + unsigned int i, idx = 0; + + p = xcalloc(WENV_SIZ + 1, sizeof(char *)); + for (e = environ; *e != NULL; ++e) { + for (i = 0; i < WENV_SIZ; ++i) { + if (!strncmp(*e, wenv_arr[i].name, wenv_arr[i].namelen)) + p[idx++] = *e; + } + } + p[idx] = NULL; + return p; +} + +void +free_windows_environment(char **p) +{ + free(p); +} + +/* + * Returns true if the given string matches the pattern (which may contain ? + * and * as wildcards), and zero if it does not match. + * + * The Cygwin version of this function must be case-insensitive and take + * Unicode characters into account. + */ + +static int +__match_pattern (const wchar_t *s, const wchar_t *pattern) +{ + for (;;) { + /* If at end of pattern, accept if also at end of string. */ + if (!*pattern) + return !*s; + + if (*pattern == '*') { + /* Skip the asterisk. */ + pattern++; + + /* If at end of pattern, accept immediately. */ + if (!*pattern) + return 1; + + /* If next character in pattern is known, optimize. */ + if (*pattern != '?' && *pattern != '*') { + /* + * Look instances of the next character in + * pattern, and try to match starting from + * those. + */ + for (; *s; s++) + if (*s == *pattern && + __match_pattern(s + 1, pattern + 1)) + return 1; + /* Failed. */ + return 0; + } + /* + * Move ahead one character at a time and try to + * match at each position. + */ + for (; *s; s++) + if (__match_pattern(s, pattern)) + return 1; + /* Failed. */ + return 0; + } + /* + * There must be at least one more character in the string. + * If we are at the end, fail. + */ + if (!*s) + return 0; + + /* Check if the next character of the string is acceptable. */ + if (*pattern != '?' && towlower(*pattern) != towlower(*s)) + return 0; + + /* Move to the next character, both in string and in pattern. */ + s++; + pattern++; + } + /* NOTREACHED */ +} + +static int +_match_pattern(const char *s, const char *pattern) +{ + wchar_t *ws; + wchar_t *wpattern; + size_t len; + int ret; + + if ((len = mbstowcs(NULL, s, 0)) == (size_t) -1) + return 0; + ws = (wchar_t *) xcalloc(len + 1, sizeof (wchar_t)); + mbstowcs(ws, s, len + 1); + if ((len = mbstowcs(NULL, pattern, 0)) == (size_t) -1) + return 0; + wpattern = (wchar_t *) xcalloc(len + 1, sizeof (wchar_t)); + mbstowcs(wpattern, pattern, len + 1); + ret = __match_pattern (ws, wpattern); + free(ws); + free(wpattern); + return ret; +} + +/* + * Tries to match the string against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns -1 if negation matches, 1 if there is + * a positive match, 0 if there is no match at all. + */ +int +cygwin_ug_match_pattern_list(const char *string, const char *pattern) +{ + char sub[1024]; + int negated; + int got_positive; + u_int i, subi, len = strlen(pattern); + + got_positive = 0; + for (i = 0; i < len;) { + /* Check if the subpattern is negated. */ + if (pattern[i] == '!') { + negated = 1; + i++; + } else + negated = 0; + + /* + * Extract the subpattern up to a comma or end. Convert the + * subpattern to lowercase. + */ + for (subi = 0; + i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; + subi++, i++) + sub[subi] = pattern[i]; + /* If subpattern too long, return failure (no match). */ + if (subi >= sizeof(sub) - 1) + return 0; + + /* If the subpattern was terminated by a comma, then skip it. */ + if (i < len && pattern[i] == ',') + i++; + + /* Null-terminate the subpattern. */ + sub[subi] = '\0'; + + /* Try to match the subpattern against the string. */ + if (_match_pattern(string, sub)) { + if (negated) + return -1; /* Negative */ + else + got_positive = 1; /* Positive */ + } + } + + /* + * Return success if got a positive match. If there was a negative + * match, we have already returned -1 and never get here. + */ + return got_positive; +} + +#endif /* HAVE_CYGWIN */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-cygwin_util.h b/aosp/external/openssh/openbsd-compat/bsd-cygwin_util.h new file mode 100644 index 0000000000000000000000000000000000000000..55c5a5b81b4418255127eeeff88528e076701967 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-cygwin_util.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2000, 2001, 2011, 2013 Corinna Vinschen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Created: Sat Sep 02 12:17:00 2000 cv + * + * This file contains functions for forcing opened file descriptors to + * binary mode on Windows systems. + */ + +#ifndef _BSD_CYGWIN_UTIL_H +#define _BSD_CYGWIN_UTIL_H + +#ifdef HAVE_CYGWIN + +#undef ERROR + +/* Avoid including windows headers. */ +typedef void *HANDLE; +#define INVALID_HANDLE_VALUE ((HANDLE) -1) +#define DNLEN 16 +#define UNLEN 256 + +/* Cygwin functions for which declarations are only available when including + windows headers, so we have to define them here explicitly. */ +extern HANDLE cygwin_logon_user (const struct passwd *, const char *); +extern void cygwin_set_impersonation_token (const HANDLE); + +#include +#include + +#define CYGWIN_SSH_PRIVSEP_USER (cygwin_ssh_privsep_user()) +const char *cygwin_ssh_privsep_user(); + +int binary_open(const char *, int , ...); +int check_ntsec(const char *); +char **fetch_windows_environment(void); +void free_windows_environment(char **); +int cygwin_ug_match_pattern_list(const char *, const char *); + +#ifndef NO_BINARY_OPEN +#define open binary_open +#endif + +#endif /* HAVE_CYGWIN */ + +#endif /* _BSD_CYGWIN_UTIL_H */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-err.c b/aosp/external/openssh/openbsd-compat/bsd-err.c new file mode 100644 index 0000000000000000000000000000000000000000..e4ed22b86a2594374d7d10a9a8da30b300e5d998 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-err.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 Tim Rice + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include + +#ifndef HAVE_ERR +void +err(int r, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", strerror(errno)); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); + exit(r); +} +#endif + +#ifndef HAVE_ERRX +void +errx(int r, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); + exit(r); +} +#endif + +#ifndef HAVE_WARN +void +warn(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", strerror(errno)); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-flock.c b/aosp/external/openssh/openbsd-compat/bsd-flock.c new file mode 100644 index 0000000000000000000000000000000000000000..9b15d1eaf49ec07348cc702e9f1ed2fb91cd6876 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-flock.c @@ -0,0 +1,81 @@ +/* $NetBSD: flock.c,v 1.6 2008/04/28 20:24:12 martin Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Todd Vierling. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Emulate flock() with fcntl(), where available. + * Otherwise, don't do locking; just pretend success. + */ + +#include "includes.h" + +#ifndef HAVE_FLOCK +#include +#include + +int +flock(int fd, int op) +{ + int rc = 0; + +#if defined(F_SETLK) && defined(F_SETLKW) + struct flock fl = {0}; + + switch (op & (LOCK_EX|LOCK_SH|LOCK_UN)) { + case LOCK_EX: + fl.l_type = F_WRLCK; + break; + + case LOCK_SH: + fl.l_type = F_RDLCK; + break; + + case LOCK_UN: + fl.l_type = F_UNLCK; + break; + + default: + errno = EINVAL; + return -1; + } + + fl.l_whence = SEEK_SET; + rc = fcntl(fd, op & LOCK_NB ? F_SETLK : F_SETLKW, &fl); + + if (rc && (errno == EAGAIN)) + errno = EWOULDBLOCK; +#else + rc = -1; + errno = ENOSYS; +#endif + + return rc; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-getline.c b/aosp/external/openssh/openbsd-compat/bsd-getline.c new file mode 100644 index 0000000000000000000000000000000000000000..e51bd7a1313db990e2242b0d9621e9a43a15260b --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-getline.c @@ -0,0 +1,113 @@ +/* $NetBSD: getline.c,v 1.1.1.6 2015/01/02 20:34:27 christos Exp $ */ + +/* NetBSD: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* NETBSD ORIGINAL: external/bsd/file/dist/src/getline.c */ + +#include "includes.h" + +#if 0 +#include "file.h" +#endif + +#if !defined(HAVE_GETLINE) || defined(BROKEN_GETLINE) +#include +#include +#include +#include +#include + +static ssize_t +getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +{ + char *ptr, *eptr; + + + if (*buf == NULL || *bufsiz == 0) { + if ((*buf = malloc(BUFSIZ)) == NULL) + return -1; + *bufsiz = BUFSIZ; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - *buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +ssize_t +getline(char **buf, size_t *bufsiz, FILE *fp) +{ + return getdelim(buf, bufsiz, '\n', fp); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + char *p = NULL; + ssize_t len; + size_t n = 0; + + while ((len = getline(&p, &n, stdin)) != -1) + (void)printf("%" SIZE_T_FORMAT "d %s", len, p); + free(p); + return 0; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-getpagesize.c b/aosp/external/openssh/openbsd-compat/bsd-getpagesize.c new file mode 100644 index 0000000000000000000000000000000000000000..416a8d4cb72f19abce96e28fdeac386e920d0ef7 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-getpagesize.c @@ -0,0 +1,25 @@ +/* Placed in the public domain */ + +#include "includes.h" + +#ifndef HAVE_GETPAGESIZE + +#include +#include + +int +getpagesize(void) +{ +#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + long r = sysconf(_SC_PAGESIZE); + if (r > 0 && r < INT_MAX) + return (int)r; +#endif + /* + * This is at the lower end of common values and appropriate for + * our current use of getpagesize() in recallocarray(). + */ + return 4096; +} + +#endif /* HAVE_GETPAGESIZE */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-getpeereid.c b/aosp/external/openssh/openbsd-compat/bsd-getpeereid.c new file mode 100644 index 0000000000000000000000000000000000000000..5f7e677e5cdf3e75fc73d3b6c462346aae4ec572 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-getpeereid.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002,2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#if !defined(HAVE_GETPEEREID) + +#include +#include + +#include + +#if defined(SO_PEERCRED) +int +getpeereid(int s, uid_t *euid, gid_t *gid) +{ + struct ucred cred; + socklen_t len = sizeof(cred); + + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0) + return (-1); + *euid = cred.uid; + *gid = cred.gid; + + return (0); +} +#elif defined(HAVE_GETPEERUCRED) + +#ifdef HAVE_UCRED_H +# include +#endif + +int +getpeereid(int s, uid_t *euid, gid_t *gid) +{ + ucred_t *ucred = NULL; + + if (getpeerucred(s, &ucred) == -1) + return (-1); + if ((*euid = ucred_geteuid(ucred)) == -1) + return (-1); + if ((*gid = ucred_getrgid(ucred)) == -1) + return (-1); + + ucred_free(ucred); + + return (0); +} +#else +int +getpeereid(int s, uid_t *euid, gid_t *gid) +{ + *euid = geteuid(); + *gid = getgid(); + + return (0); +} +#endif /* defined(SO_PEERCRED) */ + +#endif /* !defined(HAVE_GETPEEREID) */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-malloc.c b/aosp/external/openssh/openbsd-compat/bsd-malloc.c new file mode 100644 index 0000000000000000000000000000000000000000..482facdc9a34420eb3ba0be90dc4c25b5e9e0988 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-malloc.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" +#undef malloc +#undef calloc +#undef realloc + +#include +#include + +#if defined(HAVE_MALLOC) && HAVE_MALLOC == 0 +void * +rpl_malloc(size_t size) +{ + if (size == 0) + size = 1; + return malloc(size); +} +#endif + +#if defined(HAVE_CALLOC) && HAVE_CALLOC == 0 +void * +rpl_calloc(size_t nmemb, size_t size) +{ + if (nmemb == 0) + nmemb = 1; + if (size == 0) + size = 1; + return calloc(nmemb, size); +} +#endif + +#if defined (HAVE_REALLOC) && HAVE_REALLOC == 0 +void * +rpl_realloc(void *ptr, size_t size) +{ + if (size == 0) + size = 1; + if (ptr == 0) + return malloc(size); + return realloc(ptr, size); +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-misc.c b/aosp/external/openssh/openbsd-compat/bsd-misc.c new file mode 100644 index 0000000000000000000000000000000000000000..3b00ef6d240ea4ed96cb5cee0b9d2a8d02a7687e --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-misc.c @@ -0,0 +1,448 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_SELECT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifndef HAVE___PROGNAME +char *__progname; +#endif + +/* + * NB. duplicate __progname in case it is an alias for argv[0] + * Otherwise it may get clobbered by setproctitle() + */ +char *ssh_get_progname(char *argv0) +{ + char *p, *q; +#ifdef HAVE___PROGNAME + extern char *__progname; + + p = __progname; +#else + if (argv0 == NULL) + return ("unknown"); /* XXX */ + p = strrchr(argv0, '/'); + if (p == NULL) + p = argv0; + else + p++; +#endif + if ((q = strdup(p)) == NULL) { + perror("strdup"); + exit(1); + } + return q; +} + +#ifndef HAVE_SETLOGIN +int setlogin(const char *name) +{ + return (0); +} +#endif /* !HAVE_SETLOGIN */ + +#ifndef HAVE_INNETGR +int innetgr(const char *netgroup, const char *host, + const char *user, const char *domain) +{ + return (0); +} +#endif /* HAVE_INNETGR */ + +#if !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) +int seteuid(uid_t euid) +{ + return (setreuid(-1, euid)); +} +#endif /* !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) */ + +#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) +int setegid(uid_t egid) +{ + return(setresgid(-1, egid, -1)); +} +#endif /* !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */ + +#if !defined(HAVE_STRERROR) && defined(HAVE_SYS_ERRLIST) && defined(HAVE_SYS_NERR) +const char *strerror(int e) +{ + extern int sys_nerr; + extern char *sys_errlist[]; + + if ((e >= 0) && (e < sys_nerr)) + return (sys_errlist[e]); + + return ("unlisted error"); +} +#endif + +#ifndef HAVE_UTIMES +int utimes(const char *filename, struct timeval *tvp) +{ + struct utimbuf ub; + + ub.actime = tvp[0].tv_sec; + ub.modtime = tvp[1].tv_sec; + + return (utime(filename, &ub)); +} +#endif + +#ifndef HAVE_UTIMENSAT +/* + * A limited implementation of utimensat() that only implements the + * functionality used by OpenSSH, currently only AT_FDCWD and + * AT_SYMLINK_NOFOLLOW. + */ +int +utimensat(int fd, const char *path, const struct timespec times[2], + int flag) +{ + struct timeval tv[2]; +# ifdef HAVE_FUTIMES + int ret, oflags = O_WRONLY; +# endif + + tv[0].tv_sec = times[0].tv_sec; + tv[0].tv_usec = times[0].tv_nsec / 1000; + tv[1].tv_sec = times[1].tv_sec; + tv[1].tv_usec = times[1].tv_nsec / 1000; + + if (fd != AT_FDCWD) { + errno = ENOSYS; + return -1; + } +# ifndef HAVE_FUTIMES + return utimes(path, tv); +# else +# ifdef O_NOFOLLOW + if (flag & AT_SYMLINK_NOFOLLOW) + oflags |= O_NOFOLLOW; +# endif /* O_NOFOLLOW */ + if ((fd = open(path, oflags)) == -1) + return -1; + ret = futimes(fd, tv); + close(fd); + return ret; +# endif +} +#endif + +#ifndef HAVE_FCHOWNAT +/* + * A limited implementation of fchownat() that only implements the + * functionality used by OpenSSH, currently only AT_FDCWD and + * AT_SYMLINK_NOFOLLOW. + */ +int +fchownat(int fd, const char *path, uid_t owner, gid_t group, int flag) +{ + int ret, oflags = O_WRONLY; + + if (fd != AT_FDCWD) { + errno = ENOSYS; + return -1; + } +# ifndef HAVE_FCHOWN + return chown(path, owner, group); +# else +# ifdef O_NOFOLLOW + if (flag & AT_SYMLINK_NOFOLLOW) + oflags |= O_NOFOLLOW; +# endif /* O_NOFOLLOW */ + if ((fd = open(path, oflags)) == -1) + return -1; + ret = fchown(fd, owner, group); + close(fd); + return ret; +# endif +} +#endif + +#ifndef HAVE_FCHMODAT +/* + * A limited implementation of fchmodat() that only implements the + * functionality used by OpenSSH, currently only AT_FDCWD and + * AT_SYMLINK_NOFOLLOW. + */ +int +fchmodat(int fd, const char *path, mode_t mode, int flag) +{ + int ret, oflags = O_WRONLY; + + if (fd != AT_FDCWD) { + errno = ENOSYS; + return -1; + } +# ifndef HAVE_FCHMOD + return chmod(path, mode); +# else +# ifdef O_NOFOLLOW + if (flag & AT_SYMLINK_NOFOLLOW) + oflags |= O_NOFOLLOW; +# endif /* O_NOFOLLOW */ + if ((fd = open(path, oflags)) == -1) + return -1; + ret = fchmod(fd, mode); + close(fd); + return ret; +# endif +} +#endif + +#ifndef HAVE_TRUNCATE +int truncate(const char *path, off_t length) +{ + int fd, ret, saverrno; + + fd = open(path, O_WRONLY); + if (fd < 0) + return (-1); + + ret = ftruncate(fd, length); + saverrno = errno; + close(fd); + if (ret == -1) + errno = saverrno; + + return(ret); +} +#endif /* HAVE_TRUNCATE */ + +#if !defined(HAVE_NANOSLEEP) && !defined(HAVE_NSLEEP) +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + int rc, saverrno; + extern int errno; + struct timeval tstart, tstop, tremain, time2wait; + + TIMESPEC_TO_TIMEVAL(&time2wait, req) + (void) gettimeofday(&tstart, NULL); + rc = select(0, NULL, NULL, NULL, &time2wait); + if (rc == -1) { + saverrno = errno; + (void) gettimeofday (&tstop, NULL); + errno = saverrno; + tremain.tv_sec = time2wait.tv_sec - + (tstop.tv_sec - tstart.tv_sec); + tremain.tv_usec = time2wait.tv_usec - + (tstop.tv_usec - tstart.tv_usec); + tremain.tv_sec += tremain.tv_usec / 1000000L; + tremain.tv_usec %= 1000000L; + } else { + tremain.tv_sec = 0; + tremain.tv_usec = 0; + } + if (rem != NULL) + TIMEVAL_TO_TIMESPEC(&tremain, rem) + + return(rc); +} +#endif + +#if !defined(HAVE_USLEEP) +int usleep(unsigned int useconds) +{ + struct timespec ts; + + ts.tv_sec = useconds / 1000000; + ts.tv_nsec = (useconds % 1000000) * 1000; + return nanosleep(&ts, NULL); +} +#endif + +#ifndef HAVE_TCGETPGRP +pid_t +tcgetpgrp(int fd) +{ + int ctty_pgrp; + + if (ioctl(fd, TIOCGPGRP, &ctty_pgrp) == -1) + return(-1); + else + return(ctty_pgrp); +} +#endif /* HAVE_TCGETPGRP */ + +#ifndef HAVE_TCSENDBREAK +int +tcsendbreak(int fd, int duration) +{ +# if defined(TIOCSBRK) && defined(TIOCCBRK) + struct timeval sleepytime; + + sleepytime.tv_sec = 0; + sleepytime.tv_usec = 400000; + if (ioctl(fd, TIOCSBRK, 0) == -1) + return (-1); + (void)select(0, 0, 0, 0, &sleepytime); + if (ioctl(fd, TIOCCBRK, 0) == -1) + return (-1); + return (0); +# else + return -1; +# endif +} +#endif /* HAVE_TCSENDBREAK */ + +#ifndef HAVE_STRDUP +char * +strdup(const char *str) +{ + size_t len; + char *cp; + + len = strlen(str) + 1; + cp = malloc(len); + if (cp != NULL) + return(memcpy(cp, str, len)); + return NULL; +} +#endif + +#ifndef HAVE_ISBLANK +int +isblank(int c) +{ + return (c == ' ' || c == '\t'); +} +#endif + +#ifndef HAVE_GETPGID +pid_t +getpgid(pid_t pid) +{ +#if defined(HAVE_GETPGRP) && !defined(GETPGRP_VOID) && GETPGRP_VOID == 0 + return getpgrp(pid); +#elif defined(HAVE_GETPGRP) + if (pid == 0) + return getpgrp(); +#endif + + errno = ESRCH; + return -1; +} +#endif + +#ifndef HAVE_PLEDGE +int +pledge(const char *promises, const char *paths[]) +{ + return 0; +} +#endif + +#ifndef HAVE_MBTOWC +/* a mbtowc that only supports ASCII */ +int +mbtowc(wchar_t *pwc, const char *s, size_t n) +{ + if (s == NULL || *s == '\0') + return 0; /* ASCII is not state-dependent */ + if (*s < 0 || *s > 0x7f || n < 1) { + errno = EOPNOTSUPP; + return -1; + } + if (pwc != NULL) + *pwc = *s; + return 1; +} +#endif + +#ifndef HAVE_LLABS +long long +llabs(long long j) +{ + return (j < 0 ? -j : j); +} +#endif + +#ifndef HAVE_BZERO +void +bzero(void *b, size_t n) +{ + (void)memset(b, 0, n); +} +#endif + +#ifndef HAVE_RAISE +int +raise(int sig) +{ + kill(getpid(), sig); +} +#endif + +#ifndef HAVE_GETSID +pid_t +getsid(pid_t pid) +{ + errno = ENOSYS; + return -1; +} +#endif + +#ifndef HAVE_KILLPG +int +killpg(pid_t pgrp, int sig) +{ + return kill(pgrp, sig); +} +#endif + +#ifdef FFLUSH_NULL_BUG +#undef fflush +int _ssh_compat_fflush(FILE *f) +{ + int r1, r2; + + if (f == NULL) { + r1 = fflush(stdout); + r2 = fflush(stderr); + if (r1 == -1 || r2 == -1) + return -1; + return 0; + } + return fflush(f); +} +#endif + +#ifndef HAVE_LOCALTIME_R +struct tm * +localtime_r(const time_t *timep, struct tm *result) +{ + struct tm *tm = localtime(timep); + *result = *tm; + return result; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-misc.h b/aosp/external/openssh/openbsd-compat/bsd-misc.h new file mode 100644 index 0000000000000000000000000000000000000000..61ead1b7fad0ad871fa54c4cf833a51d7a9573ba --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-misc.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 1999-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BSD_MISC_H +#define _BSD_MISC_H + +#include "includes.h" + +char *ssh_get_progname(char *); +int seed_from_prngd(unsigned char *, size_t); + +#ifndef HAVE_SETSID +#define setsid() setpgrp(0, getpid()) +#endif /* !HAVE_SETSID */ + +#ifndef HAVE_SETENV +int setenv(const char *, const char *, int); +#endif /* !HAVE_SETENV */ + +#ifndef HAVE_SETLOGIN +int setlogin(const char *); +#endif /* !HAVE_SETLOGIN */ + +#ifndef HAVE_INNETGR +int innetgr(const char *, const char *, const char *, const char *); +#endif /* HAVE_INNETGR */ + +#if !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) +int seteuid(uid_t); +#endif /* !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) */ + +#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) +int setegid(uid_t); +#endif /* !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */ + +#if !defined(HAVE_STRERROR) && defined(HAVE_SYS_ERRLIST) && defined(HAVE_SYS_NERR) +const char *strerror(int); +#endif + +#if !defined(HAVE_SETLINEBUF) +#define setlinebuf(a) (setvbuf((a), NULL, _IOLBF, 0)) +#endif + +#ifndef HAVE_UTIMES +#ifndef HAVE_STRUCT_TIMEVAL +struct timeval { + long tv_sec; + long tv_usec; +} +#endif /* HAVE_STRUCT_TIMEVAL */ + +int utimes(const char *, struct timeval *); +#endif /* HAVE_UTIMES */ + +#ifndef AT_FDCWD +# define AT_FDCWD (-2) +#endif + +#ifndef HAVE_FCHMODAT +int fchmodat(int, const char *, mode_t, int); +#endif + +#ifndef HAVE_FCHOWNAT +int fchownat(int, const char *, uid_t, gid_t, int); +#endif + +#ifndef HAVE_TRUNCATE +int truncate (const char *, off_t); +#endif /* HAVE_TRUNCATE */ + +#ifndef HAVE_STRUCT_TIMESPEC +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif /* !HAVE_STRUCT_TIMESPEC */ + +#if !defined(HAVE_NANOSLEEP) && !defined(HAVE_NSLEEP) +# include +int nanosleep(const struct timespec *, struct timespec *); +#endif + +#ifndef HAVE_UTIMENSAT +# include +/* start with the high bits and work down to minimise risk of overlap */ +# ifndef AT_SYMLINK_NOFOLLOW +# define AT_SYMLINK_NOFOLLOW 0x80000000 +# endif +int utimensat(int, const char *, const struct timespec[2], int); +#endif /* !HAVE_UTIMENSAT */ + +#ifndef HAVE_USLEEP +int usleep(unsigned int useconds); +#endif + +#ifndef HAVE_TCGETPGRP +pid_t tcgetpgrp(int); +#endif + +#ifndef HAVE_TCSENDBREAK +int tcsendbreak(int, int); +#endif + +#ifndef HAVE_UNSETENV +int unsetenv(const char *); +#endif + +#ifndef HAVE_ISBLANK +int isblank(int); +#endif + +#ifndef HAVE_GETPGID +pid_t getpgid(pid_t); +#endif + +#ifndef HAVE_PSELECT +int pselect(int, fd_set *, fd_set *, fd_set *, const struct timespec *, + const sigset_t *); +#endif + +#ifndef HAVE_ENDGRENT +# define endgrent() do { } while(0) +#endif + +#ifndef HAVE_KRB5_GET_ERROR_MESSAGE +# define krb5_get_error_message krb5_get_err_text +#endif + +#ifndef HAVE_KRB5_FREE_ERROR_MESSAGE +# define krb5_free_error_message(a,b) do { } while(0) +#endif + +#ifndef HAVE_PLEDGE +int pledge(const char *promises, const char *paths[]); +#endif + +/* bsd-err.h */ +#ifndef HAVE_ERR +void err(int, const char *, ...) __attribute__((format(printf, 2, 3))); +#endif +#ifndef HAVE_ERRX +void errx(int, const char *, ...) __attribute__((format(printf, 2, 3))); +#endif +#ifndef HAVE_WARN +void warn(const char *, ...) __attribute__((format(printf, 1, 2))); +#endif + +#ifndef HAVE_LLABS +long long llabs(long long); +#endif + +#if defined(HAVE_DECL_BZERO) && HAVE_DECL_BZERO == 0 +void bzero(void *, size_t); +#endif + +#ifndef HAVE_RAISE +int raise(int); +#endif + +#ifndef HAVE_GETSID +pid_t getsid(pid_t); +#endif + +#ifndef HAVE_FLOCK +# define LOCK_SH 0x01 +# define LOCK_EX 0x02 +# define LOCK_NB 0x04 +# define LOCK_UN 0x08 +int flock(int, int); +#endif + +#ifdef FFLUSH_NULL_BUG +# define fflush(x) (_ssh_compat_fflush(x)) +#endif + +#ifndef HAVE_LOCALTIME_R +struct tm *localtime_r(const time_t *, struct tm *); +#endif + +#ifndef HAVE_REALPATH +#define realpath(x, y) (sftp_realpath((x), (y))) +#endif + +#endif /* _BSD_MISC_H */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-nextstep.c b/aosp/external/openssh/openbsd-compat/bsd-nextstep.c new file mode 100644 index 0000000000000000000000000000000000000000..d52443f6d2614baf7c7eae30cc1b9aec66520594 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-nextstep.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2000,2001 Ben Lindstrom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef HAVE_NEXT +#include +#include +#include "bsd-nextstep.h" + +pid_t +posix_wait(int *status) +{ + union wait statusp; + pid_t wait_pid; + + #undef wait /* Use NeXT's wait() function */ + wait_pid = wait(&statusp); + if (status) + *status = (int) statusp.w_status; + + return (wait_pid); +} + +int +tcgetattr(int fd, struct termios *t) +{ + return (ioctl(fd, TIOCGETA, t)); +} + +int +tcsetattr(int fd, int opt, const struct termios *t) +{ + struct termios localterm; + + if (opt & TCSASOFT) { + localterm = *t; + localterm.c_cflag |= CIGNORE; + t = &localterm; + } + switch (opt & ~TCSASOFT) { + case TCSANOW: + return (ioctl(fd, TIOCSETA, t)); + case TCSADRAIN: + return (ioctl(fd, TIOCSETAW, t)); + case TCSAFLUSH: + return (ioctl(fd, TIOCSETAF, t)); + default: + errno = EINVAL; + return (-1); + } +} + +int tcsetpgrp(int fd, pid_t pgrp) +{ + return (ioctl(fd, TIOCSPGRP, &pgrp)); +} + +speed_t cfgetospeed(const struct termios *t) +{ + return (t->c_ospeed); +} + +speed_t cfgetispeed(const struct termios *t) +{ + return (t->c_ispeed); +} + +int +cfsetospeed(struct termios *t,int speed) +{ + t->c_ospeed = speed; + return (0); +} + +int +cfsetispeed(struct termios *t, int speed) +{ + t->c_ispeed = speed; + return (0); +} +#endif /* HAVE_NEXT */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-nextstep.h b/aosp/external/openssh/openbsd-compat/bsd-nextstep.h new file mode 100644 index 0000000000000000000000000000000000000000..4a45b15af0e069344008eebe56bf9f9e5a86d27b --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-nextstep.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2000,2001 Ben Lindstrom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _NEXT_POSIX_H +#define _NEXT_POSIX_H + +#ifdef HAVE_NEXT +#include + +/* NGROUPS_MAX is behind -lposix. Use the BSD version which is NGROUPS */ +#undef NGROUPS_MAX +#define NGROUPS_MAX NGROUPS + +/* NeXT's readdir() is BSD (struct direct) not POSIX (struct dirent) */ +#define dirent direct + +/* Swap out NeXT's BSD wait() for a more POSIX compliant one */ +pid_t posix_wait(int *); +#define wait(a) posix_wait(a) + +/* #ifdef wrapped functions that need defining for clean compiling */ +pid_t getppid(void); +void vhangup(void); +int innetgr(const char *, const char *, const char *, const char *); + +/* TERMCAP */ +int tcgetattr(int, struct termios *); +int tcsetattr(int, int, const struct termios *); +int tcsetpgrp(int, pid_t); +speed_t cfgetospeed(const struct termios *); +speed_t cfgetispeed(const struct termios *); +int cfsetospeed(struct termios *, int); +int cfsetispeed(struct termios *, int); +#endif /* HAVE_NEXT */ +#endif /* _NEXT_POSIX_H */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-openpty.c b/aosp/external/openssh/openbsd-compat/bsd-openpty.c new file mode 100644 index 0000000000000000000000000000000000000000..5a20794ab69bcc5e4d2399ba19e93bd8a974b8a4 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-openpty.c @@ -0,0 +1,242 @@ +/* + * Please note: this implementation of openpty() is far from complete. + * it is just enough for portable OpenSSH's needs. + */ + +/* + * Copyright (c) 2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Allocating a pseudo-terminal, and making it the controlling tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" +#if !defined(HAVE_OPENPTY) + +#include + +#include + +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifdef HAVE_UTIL_H +# include +#endif /* HAVE_UTIL_H */ + +#ifdef HAVE_PTY_H +# include +#endif +#if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H) +# include +#endif + +#include +#include +#include + +#include "misc.h" +#include "log.h" + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#if defined(HAVE_DEV_PTMX) && !defined(HAVE__GETPTY) +static int +openpty_streams(int *amaster, int *aslave) +{ + /* + * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 + * also has bsd-style ptys, but they simply do not work.) + */ + int ptm; + char *pts; + sshsig_t old_signal; + + if ((ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY)) == -1) + return (-1); + + /* XXX: need to close ptm on error? */ + old_signal = ssh_signal(SIGCHLD, SIG_DFL); + if (grantpt(ptm) < 0) + return (-1); + ssh_signal(SIGCHLD, old_signal); + + if (unlockpt(ptm) < 0) + return (-1); + + if ((pts = ptsname(ptm)) == NULL) + return (-1); + *amaster = ptm; + + /* Open the slave side. */ + if ((*aslave = open(pts, O_RDWR | O_NOCTTY)) == -1) { + close(*amaster); + return (-1); + } + +#if !defined(ANDROID) +# if defined(I_FIND) && defined(__SVR4) + /* + * If the streams modules have already been pushed then there + * is no more work to do here. + */ + if (ioctl(*aslave, I_FIND, "ptem") != 0) + return 0; +# endif + + /* + * Try to push the appropriate streams modules, as described + * in Solaris pts(7). + */ + ioctl(*aslave, I_PUSH, "ptem"); + ioctl(*aslave, I_PUSH, "ldterm"); +# ifndef __hpux + ioctl(*aslave, I_PUSH, "ttcompat"); +# endif /* __hpux */ +#endif + return (0); +} +#endif + +int +openpty(int *amaster, int *aslave, char *name, struct termios *termp, + struct winsize *winp) +{ +#if defined(HAVE__GETPTY) + /* + * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more + * pty's automagically when needed + */ + char *slave; + + if ((slave = _getpty(amaster, O_RDWR, 0622, 0)) == NULL) + return (-1); + + /* Open the slave side. */ + if ((*aslave = open(slave, O_RDWR | O_NOCTTY)) == -1) { + close(*amaster); + return (-1); + } + return (0); + +#elif defined(HAVE_DEV_PTMX) + +#ifdef SSHD_ACQUIRES_CTTY + /* + * On some (most? all?) SysV based systems with STREAMS based terminals, + * sshd will acquire a controlling terminal when it pushes the "ptem" + * module. On such platforms, first allocate a sacrificial pty so + * that sshd already has a controlling terminal before allocating the + * one that will be passed back to the user process. This ensures + * the second pty is not already the controlling terminal for a + * different session and is available to become controlling terminal + * for the client's subprocess. See bugzilla #245 for details. + */ + int r, fd; + static int junk_ptyfd = -1, junk_ttyfd; + + r = openpty_streams(amaster, aslave); + if (junk_ptyfd == -1 && (fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) >= 0) { + close(fd); + junk_ptyfd = *amaster; + junk_ttyfd = *aslave; + debug("STREAMS bug workaround pty %d tty %d name %s", + junk_ptyfd, junk_ttyfd, ttyname(junk_ttyfd)); + } else + return r; +#endif + + return openpty_streams(amaster, aslave); + +#elif defined(HAVE_DEV_PTS_AND_PTC) + /* AIX-style pty code. */ + const char *ttname; + + if ((*amaster = open("/dev/ptc", O_RDWR | O_NOCTTY)) == -1) + return (-1); + if ((ttname = ttyname(*amaster)) == NULL) + return (-1); + if ((*aslave = open(ttname, O_RDWR | O_NOCTTY)) == -1) { + close(*amaster); + return (-1); + } + return (0); + +#else + /* BSD-style pty code. */ + char ptbuf[64], ttbuf[64]; + int i; + const char *ptymajors = "pqrstuvwxyzabcdefghijklmno" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *ptyminors = "0123456789abcdef"; + int num_minors = strlen(ptyminors); + int num_ptys = strlen(ptymajors) * num_minors; + struct termios tio; + + for (i = 0; i < num_ptys; i++) { + snprintf(ptbuf, sizeof(ptbuf), "/dev/pty%c%c", + ptymajors[i / num_minors], ptyminors[i % num_minors]); + snprintf(ttbuf, sizeof(ttbuf), "/dev/tty%c%c", + ptymajors[i / num_minors], ptyminors[i % num_minors]); + + if ((*amaster = open(ptbuf, O_RDWR | O_NOCTTY)) == -1) { + /* Try SCO style naming */ + snprintf(ptbuf, sizeof(ptbuf), "/dev/ptyp%d", i); + snprintf(ttbuf, sizeof(ttbuf), "/dev/ttyp%d", i); + if ((*amaster = open(ptbuf, O_RDWR | O_NOCTTY)) == -1) + continue; + } + + /* Open the slave side. */ + if ((*aslave = open(ttbuf, O_RDWR | O_NOCTTY)) == -1) { + close(*amaster); + return (-1); + } + /* set tty modes to a sane state for broken clients */ + if (tcgetattr(*amaster, &tio) != -1) { + tio.c_lflag |= (ECHO | ISIG | ICANON); + tio.c_oflag |= (OPOST | ONLCR); + tio.c_iflag |= ICRNL; + tcsetattr(*amaster, TCSANOW, &tio); + } + + return (0); + } + return (-1); +#endif +} + +#endif /* !defined(HAVE_OPENPTY) */ + diff --git a/aosp/external/openssh/openbsd-compat/bsd-poll.c b/aosp/external/openssh/openbsd-compat/bsd-poll.c new file mode 100644 index 0000000000000000000000000000000000000000..9a9794f5863be1509765a9f7c6e1cc9a936444df --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-poll.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2004, 2005, 2007 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" +#if !defined(HAVE_PPOLL) || !defined(HAVE_POLL) || defined(BROKEN_POLL) + +#include +#include +#ifdef HAVE_SYS_PARAM_H +# include +#endif +#ifdef HAVE_SYS_SELECT_H +# include +#endif + +#include +#include +#include +#include +#include "bsd-poll.h" + +#if !defined(HAVE_PPOLL) || defined(BROKEN_POLL) +/* + * A minimal implementation of ppoll(2), built on top of pselect(2). + * + * Only supports POLLIN, POLLOUT and POLLPRI flags in pfd.events and + * revents. Notably POLLERR, POLLHUP and POLLNVAL are not supported. + * + * Supports pfd.fd = -1 meaning "unused" although it's not standard. + */ + +int +ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmoutp, + const sigset_t *sigmask) +{ + nfds_t i; + int saved_errno, ret, fd, maxfd = 0; + fd_set *readfds = NULL, *writefds = NULL, *exceptfds = NULL; + size_t nmemb; + + for (i = 0; i < nfds; i++) { + fd = fds[i].fd; + if (fd != -1 && fd >= FD_SETSIZE) { + errno = EINVAL; + return -1; + } + maxfd = MAX(maxfd, fd); + } + + nmemb = howmany(maxfd + 1 , NFDBITS); + if ((readfds = calloc(nmemb, sizeof(fd_mask))) == NULL || + (writefds = calloc(nmemb, sizeof(fd_mask))) == NULL || + (exceptfds = calloc(nmemb, sizeof(fd_mask))) == NULL) { + saved_errno = ENOMEM; + ret = -1; + goto out; + } + + /* populate event bit vectors for the events we're interested in */ + for (i = 0; i < nfds; i++) { + fd = fds[i].fd; + if (fd == -1) + continue; + if (fds[i].events & POLLIN) + FD_SET(fd, readfds); + if (fds[i].events & POLLOUT) + FD_SET(fd, writefds); + if (fds[i].events & POLLPRI) + FD_SET(fd, exceptfds); + } + + ret = pselect(maxfd + 1, readfds, writefds, exceptfds, tmoutp, sigmask); + saved_errno = errno; + + /* scan through select results and set poll() flags */ + for (i = 0; i < nfds; i++) { + fd = fds[i].fd; + fds[i].revents = 0; + if (fd == -1) + continue; + if ((fds[i].events & POLLIN) && FD_ISSET(fd, readfds)) + fds[i].revents |= POLLIN; + if ((fds[i].events & POLLOUT) && FD_ISSET(fd, writefds)) + fds[i].revents |= POLLOUT; + if ((fds[i].events & POLLPRI) && FD_ISSET(fd, exceptfds)) + fds[i].revents |= POLLPRI; + } + +out: + free(readfds); + free(writefds); + free(exceptfds); + if (ret == -1) + errno = saved_errno; + return ret; +} +#endif /* !HAVE_PPOLL || BROKEN_POLL */ + +#if !defined(HAVE_POLL) || defined(BROKEN_POLL) +int +poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + struct timespec ts, *tsp = NULL; + + /* poll timeout is msec, ppoll is timespec (sec + nsec) */ + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + tsp = &ts; + } + + return ppoll(fds, nfds, tsp, NULL); +} +#endif /* !HAVE_POLL || BROKEN_POLL */ + +#endif /* !HAVE_PPOLL || !HAVE_POLL || BROKEN_POLL */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-poll.h b/aosp/external/openssh/openbsd-compat/bsd-poll.h new file mode 100644 index 0000000000000000000000000000000000000000..586647ee1aff244930212cdac62cf068fd6083b2 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-poll.h @@ -0,0 +1,77 @@ +/* $OpenBSD: poll.h,v 1.11 2003/12/10 23:10:08 millert Exp $ */ + +/* + * Copyright (c) 1996 Theo de Raadt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: sys/sys/poll.h */ + +#ifndef _COMPAT_POLL_H_ +#define _COMPAT_POLL_H_ + +#include +#ifdef HAVE_POLL_H +# include +#elif HAVE_SYS_POLL_H +# include +#endif + +#ifndef HAVE_STRUCT_POLLFD_FD +typedef struct pollfd { + int fd; + short events; + short revents; +} pollfd_t; + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 +#if 0 +/* the following are currently not implemented */ +#define POLLRDNORM 0x0040 +#define POLLNORM POLLRDNORM +#define POLLWRNORM POLLOUT +#define POLLRDBAND 0x0080 +#define POLLWRBAND 0x0100 +#endif + +#define INFTIM (-1) /* not standard */ +#endif /* !HAVE_STRUCT_POLLFD_FD */ + +#ifndef HAVE_NFDS_T +typedef unsigned int nfds_t; +#endif + +#ifndef HAVE_POLL +int poll(struct pollfd *, nfds_t, int); +#endif + +#ifndef HAVE_PPOLL +int ppoll(struct pollfd *, nfds_t, const struct timespec *, const sigset_t *); +#endif + +#endif /* !_COMPAT_POLL_H_ */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-pselect.c b/aosp/external/openssh/openbsd-compat/bsd-pselect.c new file mode 100644 index 0000000000000000000000000000000000000000..b3632086368a6b5cc639a8e57c2d08006af440f4 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-pselect.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2021 Darren Tucker (dtucker at dtucker net). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +#ifndef HAVE_PSELECT + +#include +#include +#ifdef HAVE_SYS_SELECT_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "misc.h" /* for set_nonblock */ + +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); +#endif + +static sighandler_t saved_sighandler[_NSIG]; + +/* + * Set up the descriptors. Because they are close-on-exec, in the case + * where sshd's re-exec fails notify_pipe will still point to a descriptor + * that was closed by the exec attempt but if that descriptor has been + * reopened then we'll attempt to use that. Ensure that notify_pipe is + * outside of the range used by sshd re-exec but within NFDBITS (so we don't + * need to expand the fd_sets). + */ +#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) +static int +pselect_notify_setup_fd(int *fd) +{ + int r; + + if ((r = fcntl(*fd, F_DUPFD, REEXEC_MIN_FREE_FD)) < 0 || + fcntl(r, F_SETFD, FD_CLOEXEC) < 0 || r >= FD_SETSIZE) + return -1; + (void)close(*fd); + return (*fd = r); +} + +/* + * we write to this pipe if a SIGCHLD is caught in order to avoid + * the race between select() and child_terminated + */ +static pid_t notify_pid; +static int notify_pipe[2]; +static void +pselect_notify_setup(void) +{ + static int initialized; + + if (initialized && notify_pid == getpid()) + return; + if (notify_pid == 0) + debug3_f("initializing"); + else { + debug3_f("pid changed, reinitializing"); + if (notify_pipe[0] != -1) + close(notify_pipe[0]); + if (notify_pipe[1] != -1) + close(notify_pipe[1]); + } + if (pipe(notify_pipe) == -1) { + error("pipe(notify_pipe) failed %s", strerror(errno)); + } else if (pselect_notify_setup_fd(¬ify_pipe[0]) == -1 || + pselect_notify_setup_fd(¬ify_pipe[1]) == -1) { + error("fcntl(notify_pipe, ...) failed %s", strerror(errno)); + close(notify_pipe[0]); + close(notify_pipe[1]); + } else { + set_nonblock(notify_pipe[0]); + set_nonblock(notify_pipe[1]); + notify_pid = getpid(); + debug3_f("pid %d saved %d pipe0 %d pipe1 %d", getpid(), + notify_pid, notify_pipe[0], notify_pipe[1]); + initialized = 1; + return; + } + notify_pipe[0] = -1; /* read end */ + notify_pipe[1] = -1; /* write end */ +} +static void +pselect_notify_parent(void) +{ + if (notify_pipe[1] != -1) + (void)write(notify_pipe[1], "", 1); +} +static void +pselect_notify_prepare(fd_set *readset) +{ + if (notify_pipe[0] != -1) + FD_SET(notify_pipe[0], readset); +} +static void +pselect_notify_done(fd_set *readset) +{ + char c; + + if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) { + while (read(notify_pipe[0], &c, 1) != -1) + debug2_f("reading"); + FD_CLR(notify_pipe[0], readset); + } +} + +/*ARGSUSED*/ +static void +pselect_sig_handler(int sig) +{ + int save_errno = errno; + + pselect_notify_parent(); + if (saved_sighandler[sig] != NULL) + (*saved_sighandler[sig])(sig); /* call original handler */ + errno = save_errno; +} + +/* + * A minimal implementation of pselect(2), built on top of select(2). + */ + +int +pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + const struct timespec *timeout, const sigset_t *mask) +{ + int ret, sig, saved_errno, unmasked = 0; + sigset_t osig; + struct sigaction sa, osa; + struct timeval tv, *tvp = NULL; + + if (timeout != NULL) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_nsec / 1000; + tvp = &tv; + } + if (mask == NULL) /* no signal mask, just call select */ + return select(nfds, readfds, writefds, exceptfds, tvp); + + /* For each signal we're unmasking, install our handler if needed. */ + for (sig = 0; sig < _NSIG; sig++) { + if (sig == SIGKILL || sig == SIGSTOP || sigismember(mask, sig)) + continue; + if (sigaction(sig, NULL, &sa) == 0 && + sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) { + unmasked = 1; + if (sa.sa_handler == pselect_sig_handler) + continue; + sa.sa_handler = pselect_sig_handler; + if (sigaction(sig, &sa, &osa) == 0) { + debug3_f("installing signal handler for %s, " + "previous %p", strsignal(sig), + osa.sa_handler); + saved_sighandler[sig] = osa.sa_handler; + } + } + } + if (unmasked) { + pselect_notify_setup(); + pselect_notify_prepare(readfds); + nfds = MAX(nfds, notify_pipe[0] + 1); + } + + /* Unmask signals, call select then restore signal mask. */ + sigprocmask(SIG_SETMASK, mask, &osig); + ret = select(nfds, readfds, writefds, exceptfds, tvp); + saved_errno = errno; + sigprocmask(SIG_SETMASK, &osig, NULL); + + if (unmasked) + pselect_notify_done(readfds); + errno = saved_errno; + return ret; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-setres_id.c b/aosp/external/openssh/openbsd-compat/bsd-setres_id.c new file mode 100644 index 0000000000000000000000000000000000000000..04752d5afa08caa0fee0a10e70fc740101813f20 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-setres_id.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include +#include +#include + +#include "log.h" + +#if !defined(HAVE_SETRESGID) || defined(BROKEN_SETRESGID) +int +setresgid(gid_t rgid, gid_t egid, gid_t sgid) +{ + int ret = 0, saved_errno; + + if (rgid != sgid) { + errno = ENOSYS; + return -1; + } +#if defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID) + if (setregid(rgid, egid) < 0) { + saved_errno = errno; + error("setregid %lu: %.100s", (u_long)rgid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +#else + if (setegid(egid) < 0) { + saved_errno = errno; + error("setegid %lu: %.100s", (u_long)egid, strerror(errno)); + errno = saved_errno; + ret = -1; + } + if (setgid(rgid) < 0) { + saved_errno = errno; + error("setgid %lu: %.100s", (u_long)rgid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +#endif + return ret; +} +#endif + +#if !defined(HAVE_SETRESUID) || defined(BROKEN_SETRESUID) +int +setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + int ret = 0, saved_errno; + + if (ruid != suid) { + errno = ENOSYS; + return -1; + } +#if defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID) + if (setreuid(ruid, euid) < 0) { + saved_errno = errno; + error("setreuid %lu: %.100s", (u_long)ruid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +#else + +# ifndef SETEUID_BREAKS_SETUID + if (seteuid(euid) < 0) { + saved_errno = errno; + error("seteuid %lu: %.100s", (u_long)euid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +# endif + if (setuid(ruid) < 0) { + saved_errno = errno; + error("setuid %lu: %.100s", (u_long)ruid, strerror(errno)); + errno = saved_errno; + ret = -1; + } +#endif + return ret; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-setres_id.h b/aosp/external/openssh/openbsd-compat/bsd-setres_id.h new file mode 100644 index 0000000000000000000000000000000000000000..0350a596e9ac90af44ebf46783f006f042f3a686 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-setres_id.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef HAVE_SETRESGID +int setresgid(gid_t, gid_t, gid_t); +#endif +#ifndef HAVE_SETRESUID +int setresuid(uid_t, uid_t, uid_t); +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-signal.c b/aosp/external/openssh/openbsd-compat/bsd-signal.c new file mode 100644 index 0000000000000000000000000000000000000000..38d5e972e9e422e065fc0a858b7eaf33b6892fa7 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-signal.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1999-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include "openbsd-compat/bsd-signal.h" + +#if !defined(HAVE_STRSIGNAL) +char *strsignal(int sig) +{ + static char buf[16]; + + (void)snprintf(buf, sizeof(buf), "%d", sig); + return buf; +} +#endif + diff --git a/aosp/external/openssh/openbsd-compat/bsd-signal.h b/aosp/external/openssh/openbsd-compat/bsd-signal.h new file mode 100644 index 0000000000000000000000000000000000000000..8d8c4441968d59f47bf2ec774be04c83df0a281e --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-signal.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1999-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BSD_SIGNAL_H +#define _BSD_SIGNAL_H + +#include "includes.h" + +#include + +#ifndef _NSIG +# ifdef NSIG +# define _NSIG NSIG +# else +# define _NSIG 128 +# endif +#endif + +#if !defined(HAVE_STRSIGNAL) +char *strsignal(int); +#endif + +#endif /* _BSD_SIGNAL_H */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-snprintf.c b/aosp/external/openssh/openbsd-compat/bsd-snprintf.c new file mode 100644 index 0000000000000000000000000000000000000000..b9eaee14f3c0f9ffa20b62ec577a5d6343f25b33 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-snprintf.c @@ -0,0 +1,880 @@ +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formatted the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * tridge@samba.org, idra@samba.org, April 2001 + * got rid of fcvt code (twas buggy and made testing harder) + * added C99 semantics + * + * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0 + * actually print args for %g and %e + * + * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0 + * Since includes.h isn't included here, VA_COPY has to be defined here. I don't + * see any include file that is guaranteed to be here, so I'm defining it + * locally. Fixes AIX and Solaris builds. + * + * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13 + * put the ifdef for HAVE_VA_COPY in one place rather than in lots of + * functions + * + * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4 + * Fix usage of va_list passed as an arg. Use __va_copy before using it + * when it exists. + * + * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14 + * Fix incorrect zpadlen handling in fmtfp. + * Thanks to Ollie Oldham for spotting it. + * few mods to make it easier to compile the tests. + * added the "Ollie" test to the floating point ones. + * + * Martin Pool (mbp@samba.org) April 2003 + * Remove NO_CONFIG_H so that the test case can be built within a source + * tree with less trouble. + * Remove unnecessary SAFE_FREE() definition. + * + * Martin Pool (mbp@samba.org) May 2003 + * Put in a prototype for dummy_snprintf() to quiet compiler warnings. + * + * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even + * if the C library has some snprintf functions already. + * + * Damien Miller (djm@mindrot.org) Jan 2007 + * Fix integer overflows in return value. + * Make formatting quite a bit faster by inlining dopr_outch() + * + **************************************************************/ + +#include "includes.h" + +#if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */ +# undef HAVE_SNPRINTF +# undef HAVE_VSNPRINTF +#endif + +#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LONG_DOUBLE +# define LDOUBLE long double +#else +# define LDOUBLE double +#endif + +#ifdef HAVE_LONG_LONG +# define LLONG long long +#else +# define LLONG long +#endif + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 +#define DP_C_LLONG 4 +#define DP_C_SIZE 5 +#define DP_C_INTMAX 6 + +#define char_to_int(p) ((p)- '0') +#ifndef MAX +# define MAX(p,q) (((p) >= (q)) ? (p) : (q)) +#endif + +#define DOPR_OUTCH(buf, pos, buflen, thechar) \ + do { \ + if (pos + 1 >= INT_MAX) { \ + errno = ERANGE; \ + return -1; \ + } \ + if (pos < buflen) \ + buf[pos] = thechar; \ + (pos)++; \ + } while (0) + +static int dopr(char *buffer, size_t maxlen, const char *format, + va_list args_in); +static int fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static int fmtint(char *buffer, size_t *currlen, size_t maxlen, + intmax_t value, int base, int min, int max, int flags); +static int fmtfp(char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); + +static int +dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) +{ + char ch; + intmax_t value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + va_list args; + + VA_COPY(args, args_in); + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + + while (state != DP_S_DONE) { + if (ch == '\0') + state = DP_S_DONE; + + switch(state) { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + DOPR_OUTCH(buffer, currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char)ch)) { + min = 10*min + char_to_int (ch); + ch = *format++; + } else if (ch == '*') { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } else { + state = DP_S_DOT; + } + break; + case DP_S_DOT: + if (ch == '.') { + state = DP_S_MAX; + ch = *format++; + } else { + state = DP_S_MOD; + } + break; + case DP_S_MAX: + if (isdigit((unsigned char)ch)) { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } else if (ch == '*') { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } else { + state = DP_S_MOD; + } + break; + case DP_S_MOD: + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'j': + cflags = DP_C_INTMAX; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + if (ch == 'l') { /* It's a long long */ + cflags = DP_C_LLONG; + ch = *format++; + } + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + case 'z': + cflags = DP_C_SIZE; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, LLONG); + else if (cflags == DP_C_SIZE) + value = va_arg (args, ssize_t); + else if (cflags == DP_C_INTMAX) + value = va_arg (args, intmax_t); + else + value = va_arg (args, int); + if (fmtint(buffer, &currlen, maxlen, + value, 10, min, max, flags) == -1) + return -1; + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (long)va_arg (args, unsigned LLONG); + else if (cflags == DP_C_SIZE) + value = va_arg (args, size_t); +#ifdef notyet + else if (cflags == DP_C_INTMAX) + value = va_arg (args, uintmax_t); +#endif + else + value = (long)va_arg (args, unsigned int); + if (fmtint(buffer, &currlen, maxlen, value, + 8, min, max, flags) == -1) + return -1; + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); + else if (cflags == DP_C_SIZE) + value = va_arg (args, size_t); +#ifdef notyet + else if (cflags == DP_C_INTMAX) + value = va_arg (args, uintmax_t); +#endif + else + value = (long)va_arg (args, unsigned int); + if (fmtint(buffer, &currlen, maxlen, value, + 10, min, max, flags) == -1) + return -1; + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); + else if (cflags == DP_C_SIZE) + value = va_arg (args, size_t); +#ifdef notyet + else if (cflags == DP_C_INTMAX) + value = va_arg (args, uintmax_t); +#endif + else + value = (long)va_arg (args, unsigned int); + if (fmtint(buffer, &currlen, maxlen, value, + 16, min, max, flags) == -1) + return -1; + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + if (fmtfp(buffer, &currlen, maxlen, fvalue, + min, max, flags) == -1) + return -1; + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + if (fmtfp(buffer, &currlen, maxlen, fvalue, + min, max, flags) == -1) + return -1; + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + if (fmtfp(buffer, &currlen, maxlen, fvalue, + min, max, flags) == -1) + return -1; + break; + case 'c': + DOPR_OUTCH(buffer, currlen, maxlen, + va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + if (!strvalue) strvalue = "(NULL)"; + if (max == -1) { + max = strlen(strvalue); + } + if (min > 0 && max >= 0 && min > max) max = min; + if (fmtstr(buffer, &currlen, maxlen, + strvalue, flags, min, max) == -1) + return -1; + break; + case 'p': + strvalue = va_arg (args, void *); + if (fmtint(buffer, &currlen, maxlen, + (long) strvalue, 16, min, max, flags) == -1) + return -1; + break; +#if we_dont_want_this_in_openssh + case 'n': + if (cflags == DP_C_SHORT) { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } else if (cflags == DP_C_LONG) { + long int *num; + num = va_arg (args, long int *); + *num = (long int)currlen; + } else if (cflags == DP_C_LLONG) { + LLONG *num; + num = va_arg (args, LLONG *); + *num = (LLONG)currlen; + } else if (cflags == DP_C_SIZE) { + ssize_t *num; + num = va_arg (args, ssize_t *); + *num = (ssize_t)currlen; + } else if (cflags == DP_C_INTMAX) { + intmax_t *num; + num = va_arg (args, intmax_t *); + *num = (intmax_t)currlen; + } else { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; +#endif + case '%': + DOPR_OUTCH(buffer, currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (maxlen != 0) { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else if (maxlen > 0) + buffer[maxlen - 1] = '\0'; + } + + return currlen < INT_MAX ? (int)currlen : -1; +} + +static int +fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + +#ifdef DEBUG_SNPRINTF + printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); +#endif + if (value == 0) { + value = ""; + } + + for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while ((padlen > 0) && (cnt < max)) { + DOPR_OUTCH(buffer, *currlen, maxlen, ' '); + --padlen; + ++cnt; + } + while (*value && (cnt < max)) { + DOPR_OUTCH(buffer, *currlen, maxlen, *value); + value++; + ++cnt; + } + while ((padlen < 0) && (cnt < max)) { + DOPR_OUTCH(buffer, *currlen, maxlen, ' '); + ++padlen; + ++cnt; + } + return 0; +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static int +fmtint(char *buffer, size_t *currlen, size_t maxlen, + intmax_t value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned LLONG uvalue; + char convert[20]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + int caps = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } else { + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + } + + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ + + do { + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < 20)); + if (place == 20) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place); +#endif + + /* Spaces */ + while (spadlen > 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) { + while (zpadlen > 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) { + --place; + DOPR_OUTCH(buffer, *currlen, maxlen, convert[place]); + } + + /* Left Justified spaces */ + while (spadlen < 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, ' '); + ++spadlen; + } + return 0; +} + +static LDOUBLE abs_val(LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE POW10(int val) +{ + LDOUBLE result = 1; + + while (val) { + result *= 10; + val--; + } + + return result; +} + +static LLONG ROUND(LDOUBLE value) +{ + LLONG intpart; + + intpart = (LLONG)value; + value = value - intpart; + if (value >= 0.5) intpart++; + + return intpart; +} + +/* a replacement for modf that doesn't need the math library. Should + be portable, but slow */ +static double my_modf(double x0, double *iptr) +{ + int i; + long l; + double x = x0; + double f = 1.0; + + for (i=0;i<100;i++) { + l = (long)x; + if (l <= (x+1) && l >= (x-1)) break; + x *= 0.1; + f *= 10.0; + } + + if (i == 100) { + /* + * yikes! the number is beyond what we can handle. + * What do we do? + */ + (*iptr) = 0; + return 0; + } + + if (i != 0) { + double i2; + double ret; + + ret = my_modf(x0-l*f, &i2); + (*iptr) = l*f + i2; + return ret; + } + + (*iptr) = l; + return x - (*iptr); +} + + +static int +fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + double ufvalue; + char iconvert[311]; + char fconvert[311]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + int idx; + double intpart; + double fracpart; + double temp; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) { + signvalue = '-'; + } else { + if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ + signvalue = '+'; + } else { + if (flags & DP_F_SPACE) + signvalue = ' '; + } + } + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + +#if 0 + if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ +#endif + + /* + * Sorry, we only support 16 digits past the decimal because of our + * conversion method + */ + if (max > 16) + max = 16; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + + temp = ufvalue; + my_modf(temp, &intpart); + + fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); + + if (fracpart >= POW10(max)) { + intpart++; + fracpart -= POW10(max); + } + + /* Convert integer part */ + do { + temp = intpart*0.1; + my_modf(temp, &intpart); + idx = (int) ((temp -intpart +0.05)* 10.0); + /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ + /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; + } while (intpart && (iplace < 311)); + if (iplace == 311) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + if (fracpart) + { + do { + temp = fracpart*0.1; + my_modf(temp, &fracpart); + idx = (int) ((temp -fracpart +0.05)* 10.0); + /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ + /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; + } while(fracpart && (fplace < 311)); + if (fplace == 311) fplace--; + } + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) { + if (signvalue) { + DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); + + while (iplace > 0) { + --iplace; + DOPR_OUTCH(buffer, *currlen, maxlen, iconvert[iplace]); + } + +#ifdef DEBUG_SNPRINTF + printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); +#endif + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, '.'); + + while (zpadlen > 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, '0'); + --zpadlen; + } + + while (fplace > 0) { + --fplace; + DOPR_OUTCH(buffer, *currlen, maxlen, fconvert[fplace]); + } + } + + while (padlen < 0) { + DOPR_OUTCH(buffer, *currlen, maxlen, ' '); + ++padlen; + } + return 0; +} +#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ + +#if !defined(HAVE_VSNPRINTF) +int +vsnprintf (char *str, size_t count, const char *fmt, va_list args) +{ + return dopr(str, count, fmt, args); +} +#endif + +#if !defined(HAVE_SNPRINTF) +int +snprintf(char *str, size_t count, SNPRINTF_CONST char *fmt, ...) +{ + size_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vsnprintf(str, count, fmt, ap); + va_end(ap); + return ret; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-statvfs.c b/aosp/external/openssh/openbsd-compat/bsd-statvfs.c new file mode 100644 index 0000000000000000000000000000000000000000..cddc20efd7b3873492e86713e61e6239f49486de --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-statvfs.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2008,2014 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#if !defined(HAVE_STATVFS) || !defined(HAVE_FSTATVFS) + +#ifdef HAVE_SYS_MOUNT_H +# include +#endif + +#if defined(ANDROID) +#include +#define MNAMELEN PATH_MAX +#endif + +#include + +#ifndef MNAMELEN +# define MNAMELEN 32 +#endif + +#ifdef HAVE_STRUCT_STATFS_F_FILES +# define HAVE_STRUCT_STATFS +#endif + +#ifdef HAVE_STRUCT_STATFS +static void +copy_statfs_to_statvfs(struct statvfs *to, struct statfs *from) +{ + to->f_bsize = from->f_bsize; + to->f_frsize = from->f_bsize; /* no exact equivalent */ + to->f_blocks = from->f_blocks; + to->f_bfree = from->f_bfree; + to->f_bavail = from->f_bavail; + to->f_files = from->f_files; + to->f_ffree = from->f_ffree; + to->f_favail = from->f_ffree; /* no exact equivalent */ + to->f_fsid = 0; /* XXX fix me */ +#ifdef HAVE_STRUCT_STATFS_F_FLAGS + to->f_flag = from->f_flags; +#else + to->f_flag = 0; +#endif + to->f_namemax = MNAMELEN; +} +#endif + +# ifndef HAVE_STATVFS +int statvfs(const char *path, struct statvfs *buf) +{ +# if defined(HAVE_STATFS) && defined(HAVE_STRUCT_STATFS) + struct statfs fs; + + memset(&fs, 0, sizeof(fs)); + if (statfs(path, &fs) == -1) + return -1; + copy_statfs_to_statvfs(buf, &fs); + return 0; +# else + errno = ENOSYS; + return -1; +# endif +} +# endif + +# ifndef HAVE_FSTATVFS +int fstatvfs(int fd, struct statvfs *buf) +{ +# if defined(HAVE_FSTATFS) && defined(HAVE_STRUCT_STATFS) + struct statfs fs; + + memset(&fs, 0, sizeof(fs)); + if (fstatfs(fd, &fs) == -1) + return -1; + copy_statfs_to_statvfs(buf, &fs); + return 0; +# else + errno = ENOSYS; + return -1; +# endif +} +# endif + +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-statvfs.h b/aosp/external/openssh/openbsd-compat/bsd-statvfs.h new file mode 100644 index 0000000000000000000000000000000000000000..e2a4c15f70395b6ee772b9dd0021e281d8b3702a --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-statvfs.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2008,2014 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#if !defined(HAVE_STATVFS) || !defined(HAVE_FSTATVFS) + +#include + +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_STATFS_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif + +#ifndef HAVE_FSBLKCNT_T +typedef unsigned long fsblkcnt_t; +#endif +#ifndef HAVE_FSFILCNT_T +typedef unsigned long fsfilcnt_t; +#endif + +#ifndef ST_RDONLY +#define ST_RDONLY 1 +#endif +#ifndef ST_NOSUID +#define ST_NOSUID 2 +#endif + + /* as defined in IEEE Std 1003.1, 2004 Edition */ +struct statvfs { + unsigned long f_bsize; /* File system block size. */ + unsigned long f_frsize; /* Fundamental file system block size. */ + fsblkcnt_t f_blocks; /* Total number of blocks on file system in */ + /* units of f_frsize. */ + fsblkcnt_t f_bfree; /* Total number of free blocks. */ + fsblkcnt_t f_bavail; /* Number of free blocks available to */ + /* non-privileged process. */ + fsfilcnt_t f_files; /* Total number of file serial numbers. */ + fsfilcnt_t f_ffree; /* Total number of free file serial numbers. */ + fsfilcnt_t f_favail; /* Number of file serial numbers available to */ + /* non-privileged process. */ + unsigned long f_fsid; /* File system ID. */ + unsigned long f_flag; /* BBit mask of f_flag values. */ + unsigned long f_namemax;/* Maximum filename length. */ +}; +#endif + +#ifndef HAVE_STATVFS +int statvfs(const char *, struct statvfs *); +#endif + +#ifndef HAVE_FSTATVFS +int fstatvfs(int, struct statvfs *); +#endif diff --git a/aosp/external/openssh/openbsd-compat/bsd-waitpid.c b/aosp/external/openssh/openbsd-compat/bsd-waitpid.c new file mode 100644 index 0000000000000000000000000000000000000000..113fb1ea91ae84a19eff98312ccde436b4b2f0c8 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-waitpid.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2000 Ben Lindstrom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifndef HAVE_WAITPID +#include +#include +#include "bsd-waitpid.h" + +pid_t +waitpid(int pid, int *stat_loc, int options) +{ + union wait statusp; + pid_t wait_pid; + + if (pid <= 0) { + if (pid != -1) { + errno = EINVAL; + return (-1); + } + /* wait4() wants pid=0 for indiscriminate wait. */ + pid = 0; + } + wait_pid = wait4(pid, &statusp, options, NULL); + if (stat_loc) + *stat_loc = (int) statusp.w_status; + + return (wait_pid); +} + +#endif /* !HAVE_WAITPID */ diff --git a/aosp/external/openssh/openbsd-compat/bsd-waitpid.h b/aosp/external/openssh/openbsd-compat/bsd-waitpid.h new file mode 100644 index 0000000000000000000000000000000000000000..bd61b69092407d1d2fd042bc69a59e882b9dbb9c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/bsd-waitpid.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2000 Ben Lindstrom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _BSD_WAITPID_H +#define _BSD_WAITPID_H + +#ifndef HAVE_WAITPID +/* Clean out any potential issues */ +#undef WIFEXITED +#undef WIFSTOPPED +#undef WIFSIGNALED + +/* Define required functions to mimic a POSIX look and feel */ +#define _W_INT(w) (*(int*)&(w)) /* convert union wait to int */ +#define WIFEXITED(w) (!((_W_INT(w)) & 0377)) +#define WIFSTOPPED(w) ((_W_INT(w)) & 0100) +#define WIFSIGNALED(w) (!WIFEXITED(w) && !WIFSTOPPED(w)) +#define WEXITSTATUS(w) (int)(WIFEXITED(w) ? ((_W_INT(w) >> 8) & 0377) : -1) +#define WTERMSIG(w) (int)(WIFSIGNALED(w) ? (_W_INT(w) & 0177) : -1) +#define WCOREFLAG 0x80 +#define WCOREDUMP(w) ((_W_INT(w)) & WCOREFLAG) + +/* Prototype */ +pid_t waitpid(int, int *, int); + +#endif /* !HAVE_WAITPID */ +#endif /* _BSD_WAITPID_H */ diff --git a/aosp/external/openssh/openbsd-compat/chacha_private.h b/aosp/external/openssh/openbsd-compat/chacha_private.h new file mode 100644 index 0000000000000000000000000000000000000000..cdcb785608254d80bdf2c59dea05b251982ff358 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/chacha_private.h @@ -0,0 +1,224 @@ +/* OPENBSD ORIGINAL: lib/libc/crypt/chacha_private.h */ + +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +/* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */ + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct +{ + u32 input[16]; /* could be compressed */ +} chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +static void +chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits) +{ + const char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +static void +chacha_ivsetup(chacha_ctx *x,const u8 *iv) +{ + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +static void +chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + +#ifndef KEYSTREAM_ONLY + x0 = XOR(x0,U8TO32_LITTLE(m + 0)); + x1 = XOR(x1,U8TO32_LITTLE(m + 4)); + x2 = XOR(x2,U8TO32_LITTLE(m + 8)); + x3 = XOR(x3,U8TO32_LITTLE(m + 12)); + x4 = XOR(x4,U8TO32_LITTLE(m + 16)); + x5 = XOR(x5,U8TO32_LITTLE(m + 20)); + x6 = XOR(x6,U8TO32_LITTLE(m + 24)); + x7 = XOR(x7,U8TO32_LITTLE(m + 28)); + x8 = XOR(x8,U8TO32_LITTLE(m + 32)); + x9 = XOR(x9,U8TO32_LITTLE(m + 36)); + x10 = XOR(x10,U8TO32_LITTLE(m + 40)); + x11 = XOR(x11,U8TO32_LITTLE(m + 44)); + x12 = XOR(x12,U8TO32_LITTLE(m + 48)); + x13 = XOR(x13,U8TO32_LITTLE(m + 52)); + x14 = XOR(x14,U8TO32_LITTLE(m + 56)); + x15 = XOR(x15,U8TO32_LITTLE(m + 60)); +#endif + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; +#ifndef KEYSTREAM_ONLY + m += 64; +#endif + } +} diff --git a/aosp/external/openssh/openbsd-compat/charclass.h b/aosp/external/openssh/openbsd-compat/charclass.h new file mode 100644 index 0000000000000000000000000000000000000000..91f517447b8f18f7595ef9906aecc74ffd1c2bef --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/charclass.h @@ -0,0 +1,31 @@ +/* + * Public domain, 2008, Todd C. Miller + * + * $OpenBSD: charclass.h,v 1.1 2008/10/01 23:04:13 millert Exp $ + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/charclass.h */ + +/* + * POSIX character class support for fnmatch() and glob(). + */ +static struct cclass { + const char *name; + int (*isctype)(int); +} cclasses[] = { + { "alnum", isalnum }, + { "alpha", isalpha }, + { "blank", isblank }, + { "cntrl", iscntrl }, + { "digit", isdigit }, + { "graph", isgraph }, + { "lower", islower }, + { "print", isprint }, + { "punct", ispunct }, + { "space", isspace }, + { "upper", isupper }, + { "xdigit", isxdigit }, + { NULL, NULL } +}; + +#define NCCLASSES (sizeof(cclasses) / sizeof(cclasses[0]) - 1) diff --git a/aosp/external/openssh/openbsd-compat/daemon.c b/aosp/external/openssh/openbsd-compat/daemon.c new file mode 100644 index 0000000000000000000000000000000000000000..3efe14c68c415b1639244e33b988b2c8bf9b94b3 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/daemon.c @@ -0,0 +1,82 @@ +/* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */ + +#include "includes.h" + +#ifndef HAVE_DAEMON + +#include + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +int +daemon(int nochdir, int noclose) +{ + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close (fd); + } + return (0); +} + +#endif /* !HAVE_DAEMON */ + diff --git a/aosp/external/openssh/openbsd-compat/dirname.c b/aosp/external/openssh/openbsd-compat/dirname.c new file mode 100644 index 0000000000000000000000000000000000000000..127bc2ae16f1b0ed2615c3d83912f0e20ced002a --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/dirname.c @@ -0,0 +1,71 @@ +/* $OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $ */ + +/* + * Copyright (c) 1997, 2004 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/dirname.c */ + +#include "includes.h" +#ifndef HAVE_DIRNAME + +#include +#include + +char * +dirname(const char *path) +{ + static char dname[MAXPATHLEN]; + size_t len; + const char *endp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + dname[0] = '.'; + dname[1] = '\0'; + return (dname); + } + + /* Strip any trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* Find the start of the dir */ + while (endp > path && *endp != '/') + endp--; + + /* Either the dir is "/" or there are no slashes */ + if (endp == path) { + dname[0] = *endp == '/' ? '/' : '.'; + dname[1] = '\0'; + return (dname); + } else { + /* Move forward past the separating slashes */ + do { + endp--; + } while (endp > path && *endp == '/'); + } + + len = endp - path + 1; + if (len >= sizeof(dname)) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(dname, path, len); + dname[len] = '\0'; + return (dname); +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/explicit_bzero.c b/aosp/external/openssh/openbsd-compat/explicit_bzero.c new file mode 100644 index 0000000000000000000000000000000000000000..4ffc6553a3563e74ba8c58aba15d7656097ba5fb --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/explicit_bzero.c @@ -0,0 +1,69 @@ +/* OPENBSD ORIGINAL: lib/libc/string/explicit_bzero.c */ +/* $OpenBSD: explicit_bzero.c,v 1.1 2014/01/22 21:06:45 tedu Exp $ */ +/* + * Public domain. + * Written by Ted Unangst + */ + +#include "includes.h" + +#include + +/* + * explicit_bzero - don't let the compiler optimize away bzero + */ + +#ifndef HAVE_EXPLICIT_BZERO + +#ifdef HAVE_EXPLICIT_MEMSET + +void +explicit_bzero(void *p, size_t n) +{ + (void)explicit_memset(p, 0, n); +} + +#elif defined(HAVE_MEMSET_S) + +void +explicit_bzero(void *p, size_t n) +{ + if (n == 0) + return; + (void)memset_s(p, n, 0, n); +} + +#else /* HAVE_MEMSET_S */ + +/* + * Indirect bzero through a volatile pointer to hopefully avoid + * dead-store optimisation eliminating the call. + */ +#if defined(ANDROID) +static void (* volatile ssh_bzero)(void *, size_t) = __bionic_bzero; +#else +static void (* volatile ssh_bzero)(void *, size_t) = bzero; +#endif + +void +explicit_bzero(void *p, size_t n) +{ + if (n == 0) + return; + /* + * clang -fsanitize=memory needs to intercept memset-like functions + * to correctly detect memory initialisation. Make sure one is called + * directly since our indirection trick above successfully confuses it. + */ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) + memset(p, 0, n); +# endif +#endif + + ssh_bzero(p, n); +} + +#endif /* HAVE_MEMSET_S */ + +#endif /* HAVE_EXPLICIT_BZERO */ diff --git a/aosp/external/openssh/openbsd-compat/fake-rfc2553.c b/aosp/external/openssh/openbsd-compat/fake-rfc2553.c new file mode 100644 index 0000000000000000000000000000000000000000..d5a62975aa468a91de9a515520c6ecfb34433aab --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/fake-rfc2553.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#include "includes.h" + +#include +#include + +#include +#include + +#ifndef HAVE_GETNAMEINFO +int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + struct hostent *hp; + char tmpserv[16]; + + if (sa->sa_family != AF_UNSPEC && sa->sa_family != AF_INET) + return (EAI_FAMILY); + if (serv != NULL) { + snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port)); + if (strlcpy(serv, tmpserv, servlen) >= servlen) + return (EAI_MEMORY); + } + + if (host != NULL) { + if (flags & NI_NUMERICHOST) { + if (strlcpy(host, inet_ntoa(sin->sin_addr), + hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } else { + hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == NULL) + return (EAI_NODATA); + + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) + return (EAI_MEMORY); + else + return (0); + } + } + return (0); +} +#endif /* !HAVE_GETNAMEINFO */ + +#ifndef HAVE_GAI_STRERROR +#ifdef HAVE_CONST_GAI_STRERROR_PROTO +const char * +#else +char * +#endif +gai_strerror(int err) +{ + switch (err) { + case EAI_NODATA: + return ("no address associated with name"); + case EAI_MEMORY: + return ("memory allocation failure."); + case EAI_NONAME: + return ("nodename nor servname provided, or not known"); + case EAI_FAMILY: + return ("ai_family not supported"); + default: + return ("unknown/invalid error."); + } +} +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + for(; ai != NULL;) { + next = ai->ai_next; + free(ai); + ai = next; + } +} +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETADDRINFO +static struct +addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints) +{ + struct addrinfo *ai; + + ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in)); + if (ai == NULL) + return (NULL); + + memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in)); + + ai->ai_addr = (struct sockaddr *)(ai + 1); + /* XXX -- ssh doesn't use sa_len */ + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr->sa_family = ai->ai_family = AF_INET; + + ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port; + ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr; + + /* XXX: the following is not generally correct, but does what we want */ + if (hints->ai_socktype) + ai->ai_socktype = hints->ai_socktype; + else + ai->ai_socktype = SOCK_STREAM; + + if (hints->ai_protocol) + ai->ai_protocol = hints->ai_protocol; + + return (ai); +} + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct hostent *hp; + struct servent *sp; + struct in_addr in; + int i; + long int port; + u_long addr; + + port = 0; + if (hints && hints->ai_family != AF_UNSPEC && + hints->ai_family != AF_INET) + return (EAI_FAMILY); + if (servname != NULL) { + char *cp; + + port = strtol(servname, &cp, 10); + if (port > 0 && port <= 65535 && *cp == '\0') + port = htons(port); + else if ((sp = getservbyname(servname, NULL)) != NULL) + port = sp->s_port; + else + port = 0; + } + + if (hints && hints->ai_flags & AI_PASSIVE) { + addr = htonl(0x00000000); + if (hostname && inet_aton(hostname, &in) != 0) + addr = in.s_addr; + *res = malloc_ai(port, addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (!hostname) { + *res = malloc_ai(port, htonl(0x7f000001), hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + if (inet_aton(hostname, &in)) { + *res = malloc_ai(port, in.s_addr, hints); + if (*res == NULL) + return (EAI_MEMORY); + return (0); + } + + /* Don't try DNS if AI_NUMERICHOST is set */ + if (hints && hints->ai_flags & AI_NUMERICHOST) + return (EAI_NONAME); + + hp = gethostbyname(hostname); + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + struct addrinfo *cur, *prev; + + cur = prev = *res = NULL; + for (i = 0; hp->h_addr_list[i]; i++) { + struct in_addr *in = (struct in_addr *)hp->h_addr_list[i]; + + cur = malloc_ai(port, in->s_addr, hints); + if (cur == NULL) { + if (*res != NULL) + freeaddrinfo(*res); + return (EAI_MEMORY); + } + if (prev) + prev->ai_next = cur; + else + *res = cur; + + prev = cur; + } + return (0); + } + + return (EAI_NODATA); +} +#endif /* !HAVE_GETADDRINFO */ diff --git a/aosp/external/openssh/openbsd-compat/fake-rfc2553.h b/aosp/external/openssh/openbsd-compat/fake-rfc2553.h new file mode 100644 index 0000000000000000000000000000000000000000..f913617fe7d904b0c1456944d216c1b32519f0e9 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/fake-rfc2553.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2000-2003 Damien Miller. All rights reserved. + * Copyright (C) 1999 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Pseudo-implementation of RFC2553 name / address resolution functions + * + * But these functions are not implemented correctly. The minimum subset + * is implemented for ssh use only. For example, this routine assumes + * that ai_family is AF_INET. Don't use it for another purpose. + */ + +#ifndef _FAKE_RFC2553_H +#define _FAKE_RFC2553_H + +#include "includes.h" +#include +#if defined(HAVE_NETDB_H) +# include +#endif + +/* + * First, socket and INET6 related definitions + */ +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE +# define _SS_MAXSIZE 128 /* Implementation specific max size */ +# define _SS_PADSIZE (_SS_MAXSIZE - sizeof (struct sockaddr)) +struct sockaddr_storage { + struct sockaddr ss_sa; + char __ss_pad2[_SS_PADSIZE]; +}; +# define ss_family ss_sa.sa_family +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +#ifndef IN6_IS_ADDR_LOOPBACK +# define IN6_IS_ADDR_LOOPBACK(a) \ + (((u_int32_t *)(a))[0] == 0 && ((u_int32_t *)(a))[1] == 0 && \ + ((u_int32_t *)(a))[2] == 0 && ((u_int32_t *)(a))[3] == htonl(1)) +#endif /* !IN6_IS_ADDR_LOOPBACK */ + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + u_int8_t s6_addr[16]; +}; +#endif /* !HAVE_STRUCT_IN6_ADDR */ + +#ifndef HAVE_STRUCT_SOCKADDR_IN6 +struct sockaddr_in6 { + unsigned short sin6_family; + u_int16_t sin6_port; + u_int32_t sin6_flowinfo; + struct in6_addr sin6_addr; + u_int32_t sin6_scope_id; +}; +#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */ + +#ifndef AF_INET6 +/* Define it to something that should never appear */ +#define AF_INET6 AF_MAX +#endif + +/* + * Next, RFC2553 name / address resolution API + */ + +#ifndef NI_NUMERICHOST +# define NI_NUMERICHOST (1) +#endif +#ifndef NI_NAMEREQD +# define NI_NAMEREQD (1<<1) +#endif +#ifndef NI_NUMERICSERV +# define NI_NUMERICSERV (1<<2) +#endif + +#ifndef AI_PASSIVE +# define AI_PASSIVE (1) +#endif +#ifndef AI_CANONNAME +# define AI_CANONNAME (1<<1) +#endif +#ifndef AI_NUMERICHOST +# define AI_NUMERICHOST (1<<2) +#endif +#ifndef AI_NUMERICSERV +# define AI_NUMERICSERV (1<<3) +#endif + +#ifndef NI_MAXSERV +# define NI_MAXSERV 32 +#endif /* !NI_MAXSERV */ +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif /* !NI_MAXHOST */ + +#ifndef EAI_NODATA +# define EAI_NODATA (INT_MAX - 1) +#endif +#ifndef EAI_MEMORY +# define EAI_MEMORY (INT_MAX - 2) +#endif +#ifndef EAI_NONAME +# define EAI_NONAME (INT_MAX - 3) +#endif +#ifndef EAI_SYSTEM +# define EAI_SYSTEM (INT_MAX - 4) +#endif +#ifndef EAI_FAMILY +# define EAI_FAMILY (INT_MAX - 5) +#endif + +#ifndef HAVE_STRUCT_ADDRINFO +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif /* !HAVE_STRUCT_ADDRINFO */ + +#ifndef HAVE_GETADDRINFO +#ifdef getaddrinfo +# undef getaddrinfo +#endif +#define getaddrinfo(a,b,c,d) (ssh_getaddrinfo(a,b,c,d)) +int getaddrinfo(const char *, const char *, + const struct addrinfo *, struct addrinfo **); +#endif /* !HAVE_GETADDRINFO */ + +#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO) +#define gai_strerror(a) (_ssh_compat_gai_strerror(a)) +char *gai_strerror(int); +#endif /* !HAVE_GAI_STRERROR */ + +#ifndef HAVE_FREEADDRINFO +#define freeaddrinfo(a) (ssh_freeaddrinfo(a)) +void freeaddrinfo(struct addrinfo *); +#endif /* !HAVE_FREEADDRINFO */ + +#ifndef HAVE_GETNAMEINFO +#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g)) +int getnameinfo(const struct sockaddr *, size_t, char *, size_t, + char *, size_t, int); +#endif /* !HAVE_GETNAMEINFO */ + +#endif /* !_FAKE_RFC2553_H */ + diff --git a/aosp/external/openssh/openbsd-compat/fmt_scaled.c b/aosp/external/openssh/openbsd-compat/fmt_scaled.c new file mode 100644 index 0000000000000000000000000000000000000000..87d40d2d38c06efd571ace98ec5632a65d8ffc52 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/fmt_scaled.c @@ -0,0 +1,309 @@ +/* $OpenBSD: fmt_scaled.c,v 1.21 2022/03/11 07:29:53 dtucker Exp $ */ + +/* + * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libutil/fmt_scaled.c */ + +/* + * fmt_scaled: Format numbers scaled for human comprehension + * scan_scaled: Scan numbers in this format. + * + * "Human-readable" output uses 4 digits max, and puts a unit suffix at + * the end. Makes output compact and easy-to-read esp. on huge disks. + * Formatting code was originally in OpenBSD "df", converted to library routine. + * Scanning code written for OpenBSD libutil. + */ + +#include "includes.h" + +#ifndef HAVE_FMT_SCALED + +#include +#include +#include +#include +#include +#include + +typedef enum { + NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6 +} unit_type; + +/* These three arrays MUST be in sync! XXX make a struct */ +static const unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA }; +static const char scale_chars[] = "BKMGTPE"; +static const long long scale_factors[] = { + 1LL, + 1024LL, + 1024LL*1024, + 1024LL*1024*1024, + 1024LL*1024*1024*1024, + 1024LL*1024*1024*1024*1024, + 1024LL*1024*1024*1024*1024*1024, +}; +#define SCALE_LENGTH (sizeof(units)/sizeof(units[0])) + +#define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */ + +/* Convert the given input string "scaled" into numeric in "result". + * Return 0 on success, -1 and errno set on error. + */ +int +scan_scaled(char *scaled, long long *result) +{ + char *p = scaled; + int sign = 0; + unsigned int i, ndigits = 0, fract_digits = 0; + long long scale_fact = 1, whole = 0, fpart = 0; + + /* Skip leading whitespace */ + while (isascii((unsigned char)*p) && isspace((unsigned char)*p)) + ++p; + + /* Then at most one leading + or - */ + while (*p == '-' || *p == '+') { + if (*p == '-') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = -1; + ++p; + } else if (*p == '+') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = +1; + ++p; + } + } + + /* Main loop: Scan digits, find decimal point, if present. + * We don't allow exponentials, so no scientific notation + * (but note that E for Exa might look like e to some!). + * Advance 'p' to end, to get scale factor. + */ + for (; isascii((unsigned char)*p) && + (isdigit((unsigned char)*p) || *p=='.'); ++p) { + if (*p == '.') { + if (fract_digits > 0) { /* oops, more than one '.' */ + errno = EINVAL; + return -1; + } + fract_digits = 1; + continue; + } + + i = (*p) - '0'; /* whew! finally a digit we can use */ + if (fract_digits > 0) { + if (fract_digits >= MAX_DIGITS-1) + /* ignore extra fractional digits */ + continue; + fract_digits++; /* for later scaling */ + if (fpart > LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } + fpart *= 10; + if (i > LLONG_MAX - fpart) { + errno = ERANGE; + return -1; + } + fpart += i; + } else { /* normal digit */ + if (++ndigits >= MAX_DIGITS) { + errno = ERANGE; + return -1; + } + if (whole > LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } + whole *= 10; + if (i > LLONG_MAX - whole) { + errno = ERANGE; + return -1; + } + whole += i; + } + } + + if (sign) + whole *= sign; + + /* If no scale factor given, we're done. fraction is discarded. */ + if (!*p) { + *result = whole; + return 0; + } + + /* Validate scale factor, and scale whole and fraction by it. */ + for (i = 0; i < SCALE_LENGTH; i++) { + + /* Are we there yet? */ + if (*p == scale_chars[i] || + *p == tolower((unsigned char)scale_chars[i])) { + + /* If it ends with alphanumerics after the scale char, bad. */ + if (isalnum((unsigned char)*(p+1))) { + errno = EINVAL; + return -1; + } + scale_fact = scale_factors[i]; + + /* check for overflow and underflow after scaling */ + if (whole > LLONG_MAX / scale_fact || + whole < LLONG_MIN / scale_fact) { + errno = ERANGE; + return -1; + } + + /* scale whole part */ + whole *= scale_fact; + + /* truncate fpart so it doesn't overflow. + * then scale fractional part. + */ + while (fpart >= LLONG_MAX / scale_fact || + fpart <= LLONG_MIN / scale_fact) { + fpart /= 10; + fract_digits--; + } + fpart *= scale_fact; + if (fract_digits > 0) { + for (i = 0; i < fract_digits -1; i++) + fpart /= 10; + } + if (sign == -1) + whole -= fpart; + else + whole += fpart; + *result = whole; + return 0; + } + } + + /* Invalid unit or character */ + errno = EINVAL; + return -1; +} + +/* Format the given "number" into human-readable form in "result". + * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE. + * Return 0 on success, -1 and errno set if error. + */ +int +fmt_scaled(long long number, char *result) +{ + long long abval, fract = 0; + unsigned int i; + unit_type unit = NONE; + + /* Not every negative long long has a positive representation. */ + if (number == LLONG_MIN) { + errno = ERANGE; + return -1; + } + + abval = llabs(number); + + /* Also check for numbers that are just too darned big to format. */ + if (abval / 1024 >= scale_factors[SCALE_LENGTH-1]) { + errno = ERANGE; + return -1; + } + + /* scale whole part; get unscaled fraction */ + for (i = 0; i < SCALE_LENGTH; i++) { + if (abval/1024 < scale_factors[i]) { + unit = units[i]; + fract = (i == 0) ? 0 : abval % scale_factors[i]; + number /= scale_factors[i]; + if (i > 0) + fract /= scale_factors[i - 1]; + break; + } + } + + fract = (10 * fract + 512) / 1024; + /* if the result would be >= 10, round main number */ + if (fract >= 10) { + if (number >= 0) + number++; + else + number--; + fract = 0; + } else if (fract < 0) { + /* shouldn't happen */ + fract = 0; + } + + if (number == 0) + strlcpy(result, "0B", FMT_SCALED_STRSIZE); + else if (unit == NONE || number >= 100 || number <= -100) { + if (fract >= 5) { + if (number >= 0) + number++; + else + number--; + } + (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c", + number, scale_chars[unit]); + } else + (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c", + number, fract, scale_chars[unit]); + + return 0; +} + +#ifdef MAIN +/* + * This is the original version of the program in the man page. + * Copy-and-paste whatever you need from it. + */ +int +main(int argc, char **argv) +{ + char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE]; + long long ninput = 10483892, result; + + if (scan_scaled(cinput, &result) == 0) + printf("\"%s\" -> %lld\n", cinput, result); + else + perror(cinput); + + if (fmt_scaled(ninput, buf) == 0) + printf("%lld -> \"%s\"\n", ninput, buf); + else + fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno)); + + return 0; +} +#endif + +#endif /* HAVE_FMT_SCALED */ diff --git a/aosp/external/openssh/openbsd-compat/fnmatch.c b/aosp/external/openssh/openbsd-compat/fnmatch.c new file mode 100644 index 0000000000000000000000000000000000000000..b5641a0916326c0a38183adab0c61813556dad9c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/fnmatch.c @@ -0,0 +1,495 @@ +/* $OpenBSD: fnmatch.c,v 1.22 2020/03/13 03:25:45 djm Exp $ */ + +/* Copyright (c) 2011, VMware, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the VMware, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 2008, 2016 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Authored by William A. Rowe Jr. , April 2011 + * + * Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008 + * as described in; + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html + * + * Filename pattern matches defined in section 2.13, "Pattern Matching Notation" + * from chapter 2. "Shell Command Language" + * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 + * where; 1. A bracket expression starting with an unquoted '^' + * character CONTINUES to specify a non-matching list; 2. an explicit '.' + * in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading + * in a filename; 3. a '[' which does not introduce + * a valid bracket expression is treated as an ordinary character; 4. a differing + * number of consecutive slashes within pattern and string will NOT match; + * 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character. + * + * Bracket expansion defined in section 9.3.5, "RE Bracket Expression", + * from chapter 9, "Regular Expressions" + * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05 + * with no support for collating symbols, equivalence class expressions or + * character class expressions. A partial range expression with a leading + * hyphen following a valid range expression will match only the ordinary + * and the ending character (e.g. "[a-m-z]" will match characters + * 'a' through 'm', a '-', or a 'z'). + * + * Supports BSD extensions FNM_LEADING_DIR to match pattern to the end of one + * path segment of string, and FNM_CASEFOLD to ignore alpha case. + * + * NOTE: Only POSIX/C single byte locales are correctly supported at this time. + * Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results, + * particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and + * nonalpha characters within a range. + * + * XXX comments below indicate porting required for multi-byte character sets + * and non-POSIX locale collation orders; requires mbr* APIs to track shift + * state of pattern and string (rewinding pattern and string repeatedly). + * + * Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g. + * UTF-8, SHIFT-JIS, etc). Any implementation allowing '\' as an alternate + * path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/fnmatch.c */ + +#include "includes.h" +#ifndef HAVE_FNMATCH + +#include +#include +#include + +#include "charclass.h" + +#define RANGE_MATCH 1 +#define RANGE_NOMATCH 0 +#define RANGE_ERROR (-1) + +static int +classmatch(const char *pattern, char test, int foldcase, const char **ep) +{ + const char * const mismatch = pattern; + const char *colon; + struct cclass *cc; + int rval = RANGE_NOMATCH; + size_t len; + + if (pattern[0] != '[' || pattern[1] != ':') { + *ep = mismatch; + return RANGE_ERROR; + } + pattern += 2; + + if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') { + *ep = mismatch; + return RANGE_ERROR; + } + *ep = colon + 2; + len = (size_t)(colon - pattern); + + if (foldcase && strncmp(pattern, "upper:]", 7) == 0) + pattern = "lower:]"; + for (cc = cclasses; cc->name != NULL; cc++) { + if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { + if (cc->isctype((unsigned char)test)) + rval = RANGE_MATCH; + break; + } + } + if (cc->name == NULL) { + /* invalid character class, treat as normal text */ + *ep = mismatch; + rval = RANGE_ERROR; + } + return rval; +} + +/* Most MBCS/collation/case issues handled here. Wildcard '*' is not handled. + * EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over, + * however the "\/" sequence is advanced to '/'. + * + * Both pattern and string are **char to support pointer increment of arbitrary + * multibyte characters for the given locale, in a later iteration of this code + */ +static int fnmatch_ch(const char **pattern, const char **string, int flags) +{ + const char * const mismatch = *pattern; + const int nocase = !!(flags & FNM_CASEFOLD); + const int escape = !(flags & FNM_NOESCAPE); + const int slash = !!(flags & FNM_PATHNAME); + int result = FNM_NOMATCH; + const char *startch; + int negate; + + if (**pattern == '[') { + ++*pattern; + + /* Handle negation, either leading ! or ^ operators */ + negate = (**pattern == '!') || (**pattern == '^'); + if (negate) + ++*pattern; + + /* ']' is an ordinary char at the start of the range pattern */ + if (**pattern == ']') + goto leadingclosebrace; + + while (**pattern) { + if (**pattern == ']') { + ++*pattern; + /* XXX: Fix for MBCS character width */ + ++*string; + return (result ^ negate); + } + + if (escape && (**pattern == '\\')) { + ++*pattern; + + /* Patterns must terminate with ']', not EOS */ + if (!**pattern) + break; + } + + /* Patterns must terminate with ']' not '/' */ + if (slash && (**pattern == '/')) + break; + + /* Match character classes. */ + switch (classmatch(*pattern, **string, nocase, pattern)) { + case RANGE_MATCH: + result = 0; + continue; + case RANGE_NOMATCH: + /* Valid character class but no match. */ + continue; + default: + /* Not a valid character class. */ + break; + } + if (!**pattern) + break; + +leadingclosebrace: + /* Look at only well-formed range patterns; + * "x-]" is not allowed unless escaped ("x-\]") + * XXX: Fix for locale/MBCS character width + */ + if (((*pattern)[1] == '-') && ((*pattern)[2] != ']')) { + startch = *pattern; + *pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2; + + /* + * NOT a properly balanced [expr] pattern, EOS + * terminated or ranges containing a slash in + * FNM_PATHNAME mode pattern fall out to to the + * rewind and test '[' literal code path. + */ + if (!**pattern || (slash && (**pattern == '/'))) + break; + + /* XXX: handle locale/MBCS comparison, advance by MBCS char width */ + if ((**string >= *startch) && (**string <= **pattern)) + result = 0; + else if (nocase && + (isupper((unsigned char)**string) || + isupper((unsigned char)*startch) || + isupper((unsigned char)**pattern)) && + (tolower((unsigned char)**string) >= + tolower((unsigned char)*startch)) && + (tolower((unsigned char)**string) <= + tolower((unsigned char)**pattern))) + result = 0; + + ++*pattern; + continue; + } + + /* XXX: handle locale/MBCS comparison, advance by MBCS char width */ + if ((**string == **pattern)) + result = 0; + else if (nocase && (isupper((unsigned char)**string) || + isupper((unsigned char)**pattern)) && + (tolower((unsigned char)**string) == + tolower((unsigned char)**pattern))) + result = 0; + + ++*pattern; + } + /* + * NOT a properly balanced [expr] pattern; + * Rewind and reset result to test '[' literal + */ + *pattern = mismatch; + result = FNM_NOMATCH; + } else if (**pattern == '?') { + /* Optimize '?' match before unescaping **pattern */ + if (!**string || (slash && (**string == '/'))) + return FNM_NOMATCH; + result = 0; + goto fnmatch_ch_success; + } else if (escape && (**pattern == '\\') && (*pattern)[1]) { + ++*pattern; + } + + /* XXX: handle locale/MBCS comparison, advance by the MBCS char width */ + if (**string == **pattern) + result = 0; + else if (nocase && (isupper((unsigned char)**string) || + isupper((unsigned char)**pattern)) && + (tolower((unsigned char)**string) == + tolower((unsigned char)**pattern))) + result = 0; + + /* Refuse to advance over trailing slash or NULs */ + if (**string == '\0' || **pattern == '\0' || + (slash && ((**string == '/') || (**pattern == '/')))) + return result; + +fnmatch_ch_success: + ++*pattern; + ++*string; + return result; +} + + +int fnmatch(const char *pattern, const char *string, int flags) +{ + static const char dummystring[2] = {' ', 0}; + const int escape = !(flags & FNM_NOESCAPE); + const int slash = !!(flags & FNM_PATHNAME); + const int leading_dir = !!(flags & FNM_LEADING_DIR); + const char *dummyptr, *matchptr, *strendseg; + int wild; + /* For '*' wild processing only; suppress 'used before initialization' + * warnings with dummy initialization values; + */ + const char *strstartseg = NULL; + const char *mismatch = NULL; + int matchlen = 0; + + if (*pattern == '*') + goto firstsegment; + + while (*pattern && *string) { + /* + * Pre-decode "\/" which has no special significance, and + * match balanced slashes, starting a new segment pattern. + */ + if (slash && escape && (*pattern == '\\') && (pattern[1] == '/')) + ++pattern; + if (slash && (*pattern == '/') && (*string == '/')) { + ++pattern; + ++string; + } + +firstsegment: + /* + * At the beginning of each segment, validate leading period + * behavior. + */ + if ((flags & FNM_PERIOD) && (*string == '.')) { + if (*pattern == '.') + ++pattern; + else if (escape && (*pattern == '\\') && (pattern[1] == '.')) + pattern += 2; + else + return FNM_NOMATCH; + ++string; + } + + /* + * Determine the end of string segment. Presumes '/' + * character is unique, not composite in any MBCS encoding + */ + if (slash) { + strendseg = strchr(string, '/'); + if (!strendseg) + strendseg = strchr(string, '\0'); + } else { + strendseg = strchr(string, '\0'); + } + + /* + * Allow pattern '*' to be consumed even with no remaining + * string to match. + */ + while (*pattern) { + if ((string > strendseg) || + ((string == strendseg) && (*pattern != '*'))) + break; + + if (slash && ((*pattern == '/') || + (escape && (*pattern == '\\') && (pattern[1] == '/')))) + break; + + /* + * Reduce groups of '*' and '?' to n '?' matches + * followed by one '*' test for simplicity. + */ + for (wild = 0; (*pattern == '*') || (*pattern == '?'); ++pattern) { + if (*pattern == '*') { + wild = 1; + } else if (string < strendseg) { /* && (*pattern == '?') */ + /* XXX: Advance 1 char for MBCS locale */ + ++string; + } + else { /* (string >= strendseg) && (*pattern == '?') */ + return FNM_NOMATCH; + } + } + + if (wild) { + strstartseg = string; + mismatch = pattern; + + /* + * Count fixed (non '*') char matches remaining + * in pattern * excluding '/' (or "\/") and '*'. + */ + for (matchptr = pattern, matchlen = 0; 1; ++matchlen) { + if ((*matchptr == '\0') || + (slash && ((*matchptr == '/') || + (escape && (*matchptr == '\\') && + (matchptr[1] == '/'))))) { + /* Compare precisely this many + * trailing string chars, the + * resulting match needs no + * wildcard loop. + */ + /* XXX: Adjust for MBCS */ + if (string + matchlen > strendseg) + return FNM_NOMATCH; + + string = strendseg - matchlen; + wild = 0; + break; + } + + if (*matchptr == '*') { + /* + * Ensure at least this many + * trailing string chars remain + * for the first comparison. + */ + /* XXX: Adjust for MBCS */ + if (string + matchlen > strendseg) + return FNM_NOMATCH; + + /* + * Begin first wild comparison + * at the current position. + */ + break; + } + + /* + * Skip forward in pattern by a single + * character match Use a dummy + * fnmatch_ch() test to count one + * "[range]" escape. + */ + /* XXX: Adjust for MBCS */ + if (escape && (*matchptr == '\\') && + matchptr[1]) { + matchptr += 2; + } else if (*matchptr == '[') { + dummyptr = dummystring; + fnmatch_ch(&matchptr, &dummyptr, + flags); + } else { + ++matchptr; + } + } + } + + /* Incrementally match string against the pattern. */ + while (*pattern && (string < strendseg)) { + /* Success; begin a new wild pattern search. */ + if (*pattern == '*') + break; + + if (slash && ((*string == '/') || + (*pattern == '/') || (escape && + (*pattern == '\\') && (pattern[1] == '/')))) + break; + + /* + * Compare ch's (the pattern is advanced over + * "\/" to the '/', but slashes will mismatch, + * and are not consumed). + */ + if (!fnmatch_ch(&pattern, &string, flags)) + continue; + + /* + * Failed to match, loop against next char + * offset of string segment until not enough + * string chars remain to match the fixed + * pattern. + */ + if (wild) { + /* XXX: Advance 1 char for MBCS locale */ + string = ++strstartseg; + if (string + matchlen > strendseg) + return FNM_NOMATCH; + + pattern = mismatch; + continue; + } else + return FNM_NOMATCH; + } + } + + if (*string && !((slash || leading_dir) && (*string == '/'))) + return FNM_NOMATCH; + + if (*pattern && !(slash && ((*pattern == '/') || + (escape && (*pattern == '\\') && (pattern[1] == '/'))))) + return FNM_NOMATCH; + + if (leading_dir && !*pattern && *string == '/') + return 0; + } + + /* Where both pattern and string are at EOS, declare success. */ + if (!*string && !*pattern) + return 0; + + /* Pattern didn't match to the end of string. */ + return FNM_NOMATCH; +} +#endif /* HAVE_FNMATCH */ diff --git a/aosp/external/openssh/openbsd-compat/fnmatch.h b/aosp/external/openssh/openbsd-compat/fnmatch.h new file mode 100644 index 0000000000000000000000000000000000000000..d3bc4a8634f190a5da5b94cc97e97d13d3e23164 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/fnmatch.h @@ -0,0 +1,66 @@ +/* $OpenBSD: fnmatch.h,v 1.8 2005/12/13 00:35:22 millert Exp $ */ +/* $NetBSD: fnmatch.h,v 1.5 1994/10/26 00:55:53 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93 + */ + +/* OPENBSD ORIGINAL: include/fnmatch.h */ + +#ifndef HAVE_FNMATCH_H +/* Ensure we define FNM_CASEFOLD */ +#define __BSD_VISIBLE 1 + +#ifndef _FNMATCH_H_ +#define _FNMATCH_H_ + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +#define FNM_NOMATCH 1 /* Match failed. */ +#define FNM_NOSYS 2 /* Function not supported (unused). */ + +#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +#define FNM_PERIOD 0x04 /* Period must be matched by period. */ +#if __BSD_VISIBLE +#define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ +#define FNM_CASEFOLD 0x10 /* Case insensitive search. */ +#define FNM_IGNORECASE FNM_CASEFOLD +#define FNM_FILE_NAME FNM_PATHNAME +#endif + +/* __BEGIN_DECLS */ +int fnmatch(const char *, const char *, int); +/* __END_DECLS */ + +#endif /* !_FNMATCH_H_ */ +#endif /* ! HAVE_FNMATCH_H */ diff --git a/aosp/external/openssh/openbsd-compat/freezero.c b/aosp/external/openssh/openbsd-compat/freezero.c new file mode 100644 index 0000000000000000000000000000000000000000..bad018ff0329051a6588d548e7ed9de8cbe17bae --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/freezero.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#ifndef HAVE_FREEZERO + +void +freezero(void *ptr, size_t sz) +{ + if (ptr == NULL) + return; + explicit_bzero(ptr, sz); + free(ptr); +} + +#endif /* HAVE_FREEZERO */ + diff --git a/aosp/external/openssh/openbsd-compat/getcwd.c b/aosp/external/openssh/openbsd-compat/getcwd.c new file mode 100644 index 0000000000000000000000000000000000000000..2d56bae19dd6a755a866af3fd91bbfde0279d2bb --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getcwd.c @@ -0,0 +1,239 @@ +/* $OpenBSD: getcwd.c,v 1.14 2005/08/08 08:05:34 espie Exp */ +/* + * Copyright (c) 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/getcwd.c */ + +#include "includes.h" + +#if !defined(HAVE_GETCWD) + +#include +#include +#include +#include +#include +#include +#include +#include "includes.h" + +#define ISDOT(dp) \ + (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + +char * +getcwd(char *pt, size_t size) +{ + struct dirent *dp; + DIR *dir = NULL; + dev_t dev; + ino_t ino; + int first; + char *bpt, *bup; + struct stat s; + dev_t root_dev; + ino_t root_ino; + size_t ptsize, upsize; + int save_errno; + char *ept, *eup, *up; + + /* + * If no buffer specified by the user, allocate one as necessary. + * If a buffer is specified, the size has to be non-zero. The path + * is built from the end of the buffer backwards. + */ + if (pt) { + ptsize = 0; + if (!size) { + errno = EINVAL; + return (NULL); + } + ept = pt + size; + } else { + if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL) + return (NULL); + ept = pt + ptsize; + } + bpt = ept - 1; + *bpt = '\0'; + + /* + * Allocate bytes for the string of "../"'s. + * Should always be enough (it's 340 levels). If it's not, allocate + * as necessary. Special * case the first stat, it's ".", not "..". + */ + if ((up = malloc(upsize = MAXPATHLEN)) == NULL) + goto err; + eup = up + upsize; + bup = up; + up[0] = '.'; + up[1] = '\0'; + + /* Save root values, so know when to stop. */ + if (stat("/", &s)) + goto err; + root_dev = s.st_dev; + root_ino = s.st_ino; + + errno = 0; /* XXX readdir has no error return. */ + + for (first = 1;; first = 0) { + /* Stat the current level. */ + if (lstat(up, &s)) + goto err; + + /* Save current node values. */ + ino = s.st_ino; + dev = s.st_dev; + + /* Check for reaching root. */ + if (root_dev == dev && root_ino == ino) { + *--bpt = '/'; + /* + * It's unclear that it's a requirement to copy the + * path to the beginning of the buffer, but it's always + * been that way and stuff would probably break. + */ + memmove(pt, bpt, ept - bpt); + free(up); + return (pt); + } + + /* + * Build pointer to the parent directory, allocating memory + * as necessary. Max length is 3 for "../", the largest + * possible component name, plus a trailing NUL. + */ + if (bup + 3 + MAXNAMLEN + 1 >= eup) { + char *nup; + + if ((nup = realloc(up, upsize *= 2)) == NULL) + goto err; + bup = nup + (bup - up); + up = nup; + eup = up + upsize; + } + *bup++ = '.'; + *bup++ = '.'; + *bup = '\0'; + + /* Open and stat parent directory. */ + if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) + goto err; + + /* Add trailing slash for next directory. */ + *bup++ = '/'; + + /* + * If it's a mount point, have to stat each element because + * the inode number in the directory is for the entry in the + * parent directory, not the inode number of the mounted file. + */ + save_errno = 0; + if (s.st_dev == dev) { + for (;;) { + if (!(dp = readdir(dir))) + goto notfound; + if (dp->d_fileno == ino) + break; + } + } else + for (;;) { + if (!(dp = readdir(dir))) + goto notfound; + if (ISDOT(dp)) + continue; + memcpy(bup, dp->d_name, dp->d_namlen + 1); + + /* Save the first error for later. */ + if (lstat(up, &s)) { + if (!save_errno) + save_errno = errno; + errno = 0; + continue; + } + if (s.st_dev == dev && s.st_ino == ino) + break; + } + + /* + * Check for length of the current name, preceding slash, + * leading slash. + */ + if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { + size_t len; + char *npt; + + if (!ptsize) { + errno = ERANGE; + goto err; + } + len = ept - bpt; + if ((npt = realloc(pt, ptsize *= 2)) == NULL) + goto err; + bpt = npt + (bpt - pt); + pt = npt; + ept = pt + ptsize; + memmove(ept - len, bpt, len); + bpt = ept - len; + } + if (!first) + *--bpt = '/'; + bpt -= dp->d_namlen; + memcpy(bpt, dp->d_name, dp->d_namlen); + (void)closedir(dir); + + /* Truncate any file name. */ + *bup = '\0'; + } + +notfound: + /* + * If readdir set errno, use it, not any saved error; otherwise, + * didn't find the current directory in its parent directory, set + * errno to ENOENT. + */ + if (!errno) + errno = save_errno ? save_errno : ENOENT; + /* FALLTHROUGH */ +err: + save_errno = errno; + + if (ptsize) + free(pt); + free(up); + if (dir) + (void)closedir(dir); + + errno = save_errno; + + return (NULL); +} + +#endif /* !defined(HAVE_GETCWD) */ diff --git a/aosp/external/openssh/openbsd-compat/getgrouplist.c b/aosp/external/openssh/openbsd-compat/getgrouplist.c new file mode 100644 index 0000000000000000000000000000000000000000..3906cd62970c6b79f09a7fbb2bdcd7dfa3eb30fc --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getgrouplist.c @@ -0,0 +1,95 @@ +/* $OpenBSD: getgrouplist.c,v 1.12 2005/08/08 08:05:34 espie Exp */ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/getgrouplist.c */ + +#include "includes.h" + +#ifndef HAVE_GETGROUPLIST + +/* + * get credential + */ +#include +#include +#include +#include + +int +getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *grpcnt) +{ + struct group *grp; + int i, ngroups; + int ret, maxgroups; + int bail; + + ret = 0; + ngroups = 0; + maxgroups = *grpcnt; + + /* + * install primary group + */ + if (ngroups >= maxgroups) { + *grpcnt = ngroups; + return (-1); + } + groups[ngroups++] = agroup; + + /* + * Scan the group file to find additional groups. + */ + setgrent(); + while ((grp = getgrent())) { + if (grp->gr_gid == agroup) + continue; + for (bail = 0, i = 0; bail == 0 && i < ngroups; i++) + if (groups[i] == grp->gr_gid) + bail = 1; + if (bail) + continue; + for (i = 0; grp->gr_mem[i]; i++) { + if (!strcmp(grp->gr_mem[i], uname)) { + if (ngroups >= maxgroups) { + ret = -1; + goto out; + } + groups[ngroups++] = grp->gr_gid; + break; + } + } + } +out: + endgrent(); + *grpcnt = ngroups; + return (ret); +} + +#endif /* HAVE_GETGROUPLIST */ diff --git a/aosp/external/openssh/openbsd-compat/getopt.h b/aosp/external/openssh/openbsd-compat/getopt.h new file mode 100644 index 0000000000000000000000000000000000000000..8eb12447ed6433df6dce8d2cc54c51bdfd8d35be --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getopt.h @@ -0,0 +1,74 @@ +/* $OpenBSD: getopt.h,v 1.2 2008/06/26 05:42:04 ray Exp $ */ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +/* + * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DEFINED_ +#define _GETOPT_DEFINED_ +int getopt(int, char * const *, const char *); +int getsubopt(char **, char * const *, char **); + +extern char *optarg; /* getopt(3) external variables */ +extern int opterr; +extern int optind; +extern int optopt; +extern int optreset; +extern char *suboptarg; /* getsubopt(3) external variable */ +#endif + +#endif /* !_GETOPT_H_ */ diff --git a/aosp/external/openssh/openbsd-compat/getopt_long.c b/aosp/external/openssh/openbsd-compat/getopt_long.c new file mode 100644 index 0000000000000000000000000000000000000000..1a5001f7d98a7b96efbaaa0e457f7ea4794b373a --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getopt_long.c @@ -0,0 +1,532 @@ +/* $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */ +#include "includes.h" + +#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET) + +/* + * Some defines to make it easier to keep the code in sync with upstream. + * getopt opterr optind optopt optreset optarg are all in defines.h which is + * pulled in by includes.h. + */ +#define warnx logit + +#if 0 +#include +#include +#endif +#include +#include +#include +#include + +#include "log.h" + +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1 || optreset) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} + +#if 0 +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} +#endif + +#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */ diff --git a/aosp/external/openssh/openbsd-compat/getrrsetbyname-ldns.c b/aosp/external/openssh/openbsd-compat/getrrsetbyname-ldns.c new file mode 100644 index 0000000000000000000000000000000000000000..4647b623b8621a97b6790504e20b3fca7b3f84b7 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getrrsetbyname-ldns.c @@ -0,0 +1,284 @@ +/* $OpenBSD: getrrsetbyname.c,v 1.10 2005/03/30 02:58:28 tedu Exp $ */ + +/* + * Copyright (c) 2007 Simon Vallet / Genoscope + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#if !defined (HAVE_GETRRSETBYNAME) && defined (HAVE_LDNS) + +#include +#include + +#include + +#include "getrrsetbyname.h" +#include "log.h" +#include "xmalloc.h" + +#define malloc(x) (xmalloc(x)) +#define calloc(x, y) (xcalloc((x),(y))) + +int +getrrsetbyname(const char *hostname, unsigned int rdclass, + unsigned int rdtype, unsigned int flags, + struct rrsetinfo **res) +{ + int result; + unsigned int i, j, index_ans, index_sig; + struct rrsetinfo *rrset = NULL; + struct rdatainfo *rdata; + size_t len; + ldns_resolver *ldns_res = NULL; + ldns_rdf *domain = NULL; + ldns_pkt *pkt = NULL; + ldns_rr_list *rrsigs = NULL, *rrdata = NULL; + ldns_status err; + ldns_rr *rr; + + /* check for invalid class and type */ + if (rdclass > 0xffff || rdtype > 0xffff) { + result = ERRSET_INVAL; + goto fail; + } + + /* don't allow queries of class or type ANY */ + if (rdclass == 0xff || rdtype == 0xff) { + result = ERRSET_INVAL; + goto fail; + } + + /* don't allow flags yet, unimplemented */ + if (flags) { + result = ERRSET_INVAL; + goto fail; + } + + /* Initialize resolver from resolv.conf */ + domain = ldns_dname_new_frm_str(hostname); + if ((err = ldns_resolver_new_frm_file(&ldns_res, NULL)) != \ + LDNS_STATUS_OK) { + result = ERRSET_FAIL; + goto fail; + } + +#ifdef LDNS_DEBUG + ldns_resolver_set_debug(ldns_res, true); +#endif /* LDNS_DEBUG */ + + ldns_resolver_set_dnssec(ldns_res, true); /* Use DNSSEC */ + + /* make query */ + pkt = ldns_resolver_query(ldns_res, domain, rdtype, rdclass, LDNS_RD); + + /*** TODO: finer errcodes -- see original **/ + if (!pkt || ldns_pkt_ancount(pkt) < 1) { + result = ERRSET_FAIL; + goto fail; + } + + /* initialize rrset */ + rrset = calloc(1, sizeof(struct rrsetinfo)); + if (rrset == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + rrdata = ldns_pkt_rr_list_by_type(pkt, rdtype, LDNS_SECTION_ANSWER); + rrset->rri_nrdatas = ldns_rr_list_rr_count(rrdata); + if (!rrset->rri_nrdatas) { + result = ERRSET_NODATA; + goto fail; + } + + /* copy name from answer section */ + len = ldns_rdf_size(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))); + if ((rrset->rri_name = malloc(len)) == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + memcpy(rrset->rri_name, + ldns_rdf_data(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))), len); + + rrset->rri_rdclass = ldns_rr_get_class(ldns_rr_list_rr(rrdata, 0)); + rrset->rri_rdtype = ldns_rr_get_type(ldns_rr_list_rr(rrdata, 0)); + rrset->rri_ttl = ldns_rr_ttl(ldns_rr_list_rr(rrdata, 0)); + + debug2("ldns: got %u answers from DNS", rrset->rri_nrdatas); + + /* Check for authenticated data */ + if (ldns_pkt_ad(pkt)) { + rrset->rri_flags |= RRSET_VALIDATED; + } else { /* AD is not set, try autonomous validation */ + ldns_rr_list * trusted_keys = ldns_rr_list_new(); + + debug2("ldns: trying to validate RRset"); + /* Get eventual sigs */ + rrsigs = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_RRSIG, + LDNS_SECTION_ANSWER); + + rrset->rri_nsigs = ldns_rr_list_rr_count(rrsigs); + debug2("ldns: got %u signature(s) (RRTYPE %u) from DNS", + rrset->rri_nsigs, LDNS_RR_TYPE_RRSIG); + + if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs, + trusted_keys)) == LDNS_STATUS_OK) { + rrset->rri_flags |= RRSET_VALIDATED; + debug2("ldns: RRset is signed with a valid key"); + } else { + debug2("ldns: RRset validation failed: %s", + ldns_get_errorstr_by_id(err)); + } + + ldns_rr_list_deep_free(trusted_keys); + } + + /* allocate memory for answers */ + rrset->rri_rdatas = calloc(rrset->rri_nrdatas, + sizeof(struct rdatainfo)); + + if (rrset->rri_rdatas == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + /* allocate memory for signatures */ + if (rrset->rri_nsigs > 0) { + rrset->rri_sigs = calloc(rrset->rri_nsigs, + sizeof(struct rdatainfo)); + + if (rrset->rri_sigs == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + } + + /* copy answers & signatures */ + for (i=0, index_ans=0, index_sig=0; i< pkt->_header->_ancount; i++) { + rdata = NULL; + rr = ldns_rr_list_rr(ldns_pkt_answer(pkt), i); + + if (ldns_rr_get_class(rr) == rrset->rri_rdclass && + ldns_rr_get_type(rr) == rrset->rri_rdtype) { + rdata = &rrset->rri_rdatas[index_ans++]; + } + + if (rr->_rr_class == rrset->rri_rdclass && + rr->_rr_type == LDNS_RR_TYPE_RRSIG && + rrset->rri_sigs) { + rdata = &rrset->rri_sigs[index_sig++]; + } + + if (rdata) { + size_t rdata_offset = 0; + + rdata->rdi_length = 0; + for (j=0; j< rr->_rd_count; j++) { + rdata->rdi_length += + ldns_rdf_size(ldns_rr_rdf(rr, j)); + } + + rdata->rdi_data = malloc(rdata->rdi_length); + if (rdata->rdi_data == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + /* Re-create the raw DNS RDATA */ + for (j=0; j< rr->_rd_count; j++) { + len = ldns_rdf_size(ldns_rr_rdf(rr, j)); + memcpy(rdata->rdi_data + rdata_offset, + ldns_rdf_data(ldns_rr_rdf(rr, j)), len); + rdata_offset += len; + } + } + } + + *res = rrset; + result = ERRSET_SUCCESS; + +fail: + /* freerrset(rrset); */ + ldns_rdf_deep_free(domain); + ldns_pkt_free(pkt); + ldns_rr_list_deep_free(rrsigs); + ldns_rr_list_deep_free(rrdata); + ldns_resolver_deep_free(ldns_res); + + return result; +} + + +void +freerrset(struct rrsetinfo *rrset) +{ + u_int16_t i; + + if (rrset == NULL) + return; + + if (rrset->rri_rdatas) { + for (i = 0; i < rrset->rri_nrdatas; i++) { + if (rrset->rri_rdatas[i].rdi_data == NULL) + break; + free(rrset->rri_rdatas[i].rdi_data); + } + free(rrset->rri_rdatas); + } + + if (rrset->rri_sigs) { + for (i = 0; i < rrset->rri_nsigs; i++) { + if (rrset->rri_sigs[i].rdi_data == NULL) + break; + free(rrset->rri_sigs[i].rdi_data); + } + free(rrset->rri_sigs); + } + + if (rrset->rri_name) + free(rrset->rri_name); + free(rrset); +} + + +#endif /* !defined (HAVE_GETRRSETBYNAME) && defined (HAVE_LDNS) */ diff --git a/aosp/external/openssh/openbsd-compat/getrrsetbyname.c b/aosp/external/openssh/openbsd-compat/getrrsetbyname.c new file mode 100644 index 0000000000000000000000000000000000000000..cc1f8ae519ebe8747acd2189fa6cf081f4c3f84b --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getrrsetbyname.c @@ -0,0 +1,609 @@ +/* $OpenBSD: getrrsetbyname.c,v 1.11 2007/10/11 18:36:41 jakob Exp $ */ + +/* + * Copyright (c) 2001 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/getrrsetbyname.c */ + +#include "includes.h" + +#if !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) + +#include +#include + +#include +#include + +#include "getrrsetbyname.h" + +#if defined(HAVE_DECL_H_ERRNO) && !HAVE_DECL_H_ERRNO +extern int h_errno; +#endif + +/* We don't need multithread support here */ +#ifdef _THREAD_PRIVATE +# undef _THREAD_PRIVATE +#endif +#define _THREAD_PRIVATE(a,b,c) (c) + +#ifndef HAVE__RES_EXTERN +struct __res_state _res; +#endif + +/* Necessary functions and macros */ + +/* + * Inline versions of get/put short/long. Pointer is advanced. + * + * These macros demonstrate the property of C whereby it can be + * portable or it can be elegant but rarely both. + */ + +#ifndef INT32SZ +# define INT32SZ 4 +#endif +#ifndef INT16SZ +# define INT16SZ 2 +#endif + +#ifndef GETSHORT +#define GETSHORT(s, cp) { \ + u_char *t_cp = (u_char *)(cp); \ + (s) = ((u_int16_t)t_cp[0] << 8) \ + | ((u_int16_t)t_cp[1]) \ + ; \ + (cp) += INT16SZ; \ +} +#endif + +#ifndef GETLONG +#define GETLONG(l, cp) { \ + u_char *t_cp = (u_char *)(cp); \ + (l) = ((u_int32_t)t_cp[0] << 24) \ + | ((u_int32_t)t_cp[1] << 16) \ + | ((u_int32_t)t_cp[2] << 8) \ + | ((u_int32_t)t_cp[3]) \ + ; \ + (cp) += INT32SZ; \ +} +#endif + +/* + * If the system doesn't have _getshort/_getlong or that are not exactly what + * we need then use local replacements, avoiding name collisions. + */ +#if !defined(HAVE__GETSHORT) || !defined(HAVE__GETLONG) || \ + !defined(HAVE_DECL__GETSHORT) || HAVE_DECL__GETSHORT == 0 || \ + !defined(HAVE_DECL__GETLONG) || HAVE_DECL__GETLONG == 0 +#define _getshort(x) (_ssh_compat_getshort(x)) +#define _getlong(x) (_ssh_compat_getlong(x)) +/* + * Routines to insert/extract short/long's. + */ +static u_int16_t +_getshort(const u_char *msgp) +{ + u_int16_t u; + + GETSHORT(u, msgp); + return (u); +} + +static u_int32_t +_getlong(const u_char *msgp) +{ + u_int32_t u; + + GETLONG(u, msgp); + return (u); +} +#endif + +/* ************** */ + +#define ANSWER_BUFFER_SIZE 0xffff + +struct dns_query { + char *name; + u_int16_t type; + u_int16_t class; + struct dns_query *next; +}; + +struct dns_rr { + char *name; + u_int16_t type; + u_int16_t class; + u_int16_t ttl; + u_int16_t size; + void *rdata; + struct dns_rr *next; +}; + +struct dns_response { + HEADER header; + struct dns_query *query; + struct dns_rr *answer; + struct dns_rr *authority; + struct dns_rr *additional; +}; + +static struct dns_response *parse_dns_response(const u_char *, int); +static struct dns_query *parse_dns_qsection(const u_char *, int, + const u_char **, int); +static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **, + int); + +static void free_dns_query(struct dns_query *); +static void free_dns_rr(struct dns_rr *); +static void free_dns_response(struct dns_response *); + +static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t); + +int +getrrsetbyname(const char *hostname, unsigned int rdclass, + unsigned int rdtype, unsigned int flags, + struct rrsetinfo **res) +{ + struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res); + int result; + struct rrsetinfo *rrset = NULL; + struct dns_response *response = NULL; + struct dns_rr *rr; + struct rdatainfo *rdata; + int length; + unsigned int index_ans, index_sig; + u_char answer[ANSWER_BUFFER_SIZE]; + + /* check for invalid class and type */ + if (rdclass > 0xffff || rdtype > 0xffff) { + result = ERRSET_INVAL; + goto fail; + } + + /* don't allow queries of class or type ANY */ + if (rdclass == 0xff || rdtype == 0xff) { + result = ERRSET_INVAL; + goto fail; + } + + /* don't allow flags yet, unimplemented */ + if (flags) { + result = ERRSET_INVAL; + goto fail; + } + + /* initialize resolver */ + if ((_resp->options & RES_INIT) == 0 && res_init() == -1) { + result = ERRSET_FAIL; + goto fail; + } + +#ifdef DEBUG + _resp->options |= RES_DEBUG; +#endif /* DEBUG */ + +#ifdef RES_USE_DNSSEC + /* turn on DNSSEC if EDNS0 is configured */ + if (_resp->options & RES_USE_EDNS0) + _resp->options |= RES_USE_DNSSEC; +#endif /* RES_USE_DNSEC */ + + /* make query */ + length = res_query(hostname, (signed int) rdclass, (signed int) rdtype, + answer, sizeof(answer)); + if (length < 0) { + switch(h_errno) { + case HOST_NOT_FOUND: + result = ERRSET_NONAME; + goto fail; + case NO_DATA: + result = ERRSET_NODATA; + goto fail; + default: + result = ERRSET_FAIL; + goto fail; + } + } + + /* parse result */ + response = parse_dns_response(answer, length); + if (response == NULL) { + result = ERRSET_FAIL; + goto fail; + } + + if (response->header.qdcount != 1) { + result = ERRSET_FAIL; + goto fail; + } + + /* initialize rrset */ + rrset = calloc(1, sizeof(struct rrsetinfo)); + if (rrset == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + rrset->rri_rdclass = response->query->class; + rrset->rri_rdtype = response->query->type; + rrset->rri_ttl = response->answer->ttl; + rrset->rri_nrdatas = response->header.ancount; + +#ifdef HAVE_HEADER_AD + /* check for authenticated data */ + if (response->header.ad == 1) + rrset->rri_flags |= RRSET_VALIDATED; +#endif + + /* copy name from answer section */ + rrset->rri_name = strdup(response->answer->name); + if (rrset->rri_name == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + /* count answers */ + rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass, + rrset->rri_rdtype); + rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass, + T_RRSIG); + + /* allocate memory for answers */ + rrset->rri_rdatas = calloc(rrset->rri_nrdatas, + sizeof(struct rdatainfo)); + if (rrset->rri_rdatas == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + + /* allocate memory for signatures */ + if (rrset->rri_nsigs > 0) { + rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo)); + if (rrset->rri_sigs == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + } + + /* copy answers & signatures */ + for (rr = response->answer, index_ans = 0, index_sig = 0; + rr; rr = rr->next) { + + rdata = NULL; + + if (rr->class == rrset->rri_rdclass && + rr->type == rrset->rri_rdtype) + rdata = &rrset->rri_rdatas[index_ans++]; + + if (rr->class == rrset->rri_rdclass && + rr->type == T_RRSIG) + rdata = &rrset->rri_sigs[index_sig++]; + + if (rdata) { + rdata->rdi_length = rr->size; + rdata->rdi_data = malloc(rr->size); + + if (rdata->rdi_data == NULL) { + result = ERRSET_NOMEMORY; + goto fail; + } + memcpy(rdata->rdi_data, rr->rdata, rr->size); + } + } + free_dns_response(response); + + *res = rrset; + return (ERRSET_SUCCESS); + +fail: + if (rrset != NULL) + freerrset(rrset); + if (response != NULL) + free_dns_response(response); + return (result); +} + +void +freerrset(struct rrsetinfo *rrset) +{ + u_int16_t i; + + if (rrset == NULL) + return; + + if (rrset->rri_rdatas) { + for (i = 0; i < rrset->rri_nrdatas; i++) { + if (rrset->rri_rdatas[i].rdi_data == NULL) + break; + free(rrset->rri_rdatas[i].rdi_data); + } + free(rrset->rri_rdatas); + } + + if (rrset->rri_sigs) { + for (i = 0; i < rrset->rri_nsigs; i++) { + if (rrset->rri_sigs[i].rdi_data == NULL) + break; + free(rrset->rri_sigs[i].rdi_data); + } + free(rrset->rri_sigs); + } + + if (rrset->rri_name) + free(rrset->rri_name); + free(rrset); +} + +/* + * DNS response parsing routines + */ +static struct dns_response * +parse_dns_response(const u_char *answer, int size) +{ + struct dns_response *resp; + const u_char *cp; + + /* allocate memory for the response */ + resp = calloc(1, sizeof(*resp)); + if (resp == NULL) + return (NULL); + + /* initialize current pointer */ + cp = answer; + + /* copy header */ + memcpy(&resp->header, cp, HFIXEDSZ); + cp += HFIXEDSZ; + + /* fix header byte order */ + resp->header.qdcount = ntohs(resp->header.qdcount); + resp->header.ancount = ntohs(resp->header.ancount); + resp->header.nscount = ntohs(resp->header.nscount); + resp->header.arcount = ntohs(resp->header.arcount); + + /* there must be at least one query */ + if (resp->header.qdcount < 1) { + free_dns_response(resp); + return (NULL); + } + + /* parse query section */ + resp->query = parse_dns_qsection(answer, size, &cp, + resp->header.qdcount); + if (resp->header.qdcount && resp->query == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse answer section */ + resp->answer = parse_dns_rrsection(answer, size, &cp, + resp->header.ancount); + if (resp->header.ancount && resp->answer == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse authority section */ + resp->authority = parse_dns_rrsection(answer, size, &cp, + resp->header.nscount); + if (resp->header.nscount && resp->authority == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse additional section */ + resp->additional = parse_dns_rrsection(answer, size, &cp, + resp->header.arcount); + if (resp->header.arcount && resp->additional == NULL) { + free_dns_response(resp); + return (NULL); + } + + return (resp); +} + +static struct dns_query * +parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count) +{ + struct dns_query *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_query)); + if (curr == NULL) { + free_dns_query(head); + return (NULL); + } + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_query(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_query(head); + return (NULL); + } + *cp += length; + + /* type */ + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + curr->class = _getshort(*cp); + *cp += INT16SZ; + } + + return (head); +} + +static struct dns_rr * +parse_dns_rrsection(const u_char *answer, int size, const u_char **cp, + int count) +{ + struct dns_rr *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_rr)); + if (curr == NULL) { + free_dns_rr(head); + return (NULL); + } + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_rr(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_rr(head); + return (NULL); + } + *cp += length; + + /* type */ + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + curr->class = _getshort(*cp); + *cp += INT16SZ; + + /* ttl */ + curr->ttl = _getlong(*cp); + *cp += INT32SZ; + + /* rdata size */ + curr->size = _getshort(*cp); + *cp += INT16SZ; + + /* rdata itself */ + curr->rdata = malloc(curr->size); + if (curr->rdata == NULL) { + free_dns_rr(head); + return (NULL); + } + memcpy(curr->rdata, *cp, curr->size); + *cp += curr->size; + } + + return (head); +} + +static void +free_dns_query(struct dns_query *p) +{ + if (p == NULL) + return; + + if (p->name) + free(p->name); + free_dns_query(p->next); + free(p); +} + +static void +free_dns_rr(struct dns_rr *p) +{ + if (p == NULL) + return; + + if (p->name) + free(p->name); + if (p->rdata) + free(p->rdata); + free_dns_rr(p->next); + free(p); +} + +static void +free_dns_response(struct dns_response *p) +{ + if (p == NULL) + return; + + free_dns_query(p->query); + free_dns_rr(p->answer); + free_dns_rr(p->authority); + free_dns_rr(p->additional); + free(p); +} + +static int +count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) +{ + int n = 0; + + while(p) { + if (p->class == class && p->type == type) + n++; + p = p->next; + } + + return (n); +} + +#endif /* !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) */ diff --git a/aosp/external/openssh/openbsd-compat/getrrsetbyname.h b/aosp/external/openssh/openbsd-compat/getrrsetbyname.h new file mode 100644 index 0000000000000000000000000000000000000000..1283f55062e0cc5ebdb323cb68c4b02d125d2be5 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/getrrsetbyname.h @@ -0,0 +1,110 @@ +/* OPENBSD BASED ON : include/netdb.h */ + +/* $OpenBSD: getrrsetbyname.c,v 1.4 2001/08/16 18:16:43 ho Exp $ */ + +/* + * Copyright (c) 2001 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GETRRSETBYNAME_H +#define _GETRRSETBYNAME_H + +#include "includes.h" + +#ifndef HAVE_GETRRSETBYNAME + +#include +#include +#include +#include +#include + +#ifndef HFIXEDSZ +#define HFIXEDSZ 12 +#endif + +#ifndef T_RRSIG +#define T_RRSIG 46 +#endif + +/* + * Flags for getrrsetbyname() + */ +#ifndef RRSET_VALIDATED +# define RRSET_VALIDATED 1 +#endif + +/* + * Return codes for getrrsetbyname() + */ +#ifndef ERRSET_SUCCESS +# define ERRSET_SUCCESS 0 +# define ERRSET_NOMEMORY 1 +# define ERRSET_FAIL 2 +# define ERRSET_INVAL 3 +# define ERRSET_NONAME 4 +# define ERRSET_NODATA 5 +#endif + +struct rdatainfo { + unsigned int rdi_length; /* length of data */ + unsigned char *rdi_data; /* record data */ +}; + +struct rrsetinfo { + unsigned int rri_flags; /* RRSET_VALIDATED ... */ + unsigned int rri_rdclass; /* class number */ + unsigned int rri_rdtype; /* RR type number */ + unsigned int rri_ttl; /* time to live */ + unsigned int rri_nrdatas; /* size of rdatas array */ + unsigned int rri_nsigs; /* size of sigs array */ + char *rri_name; /* canonical name */ + struct rdatainfo *rri_rdatas; /* individual records */ + struct rdatainfo *rri_sigs; /* individual signatures */ +}; + +int getrrsetbyname(const char *, unsigned int, unsigned int, unsigned int, struct rrsetinfo **); +void freerrset(struct rrsetinfo *); + +#endif /* !defined(HAVE_GETRRSETBYNAME) */ + +#endif /* _GETRRSETBYNAME_H */ diff --git a/aosp/external/openssh/openbsd-compat/glob.c b/aosp/external/openssh/openbsd-compat/glob.c new file mode 100644 index 0000000000000000000000000000000000000000..e89151789aa3e9c799ad35963a447a782e8ca9f9 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/glob.c @@ -0,0 +1,1079 @@ +/* $OpenBSD: glob.c,v 1.49 2020/04/21 08:25:22 dtucker Exp $ */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/glob.c */ + +/* + * glob(3) -- a superset of the one defined in POSIX 1003.2. + * + * The [!...] convention to negate a range is supported (SysV, Posix, ksh). + * + * Optional extra services, controlled by flags not defined by POSIX: + * + * GLOB_QUOTE: + * Escaping convention: \ inhibits any special meaning the following + * character might have (except \ at end of string is retained). + * GLOB_MAGCHAR: + * Set in gl_flags if pattern contained a globbing character. + * GLOB_NOMAGIC: + * Same as GLOB_NOCHECK, but it will only append pattern if it did + * not contain any magic characters. [Used in csh style globbing] + * GLOB_ALTDIRFUNC: + * Use alternately specified directory access functions. + * GLOB_TILDE: + * expand ~user/foo to the /home/dir/of/user/foo + * GLOB_BRACE: + * expand {1,2}{a,b} to 1a 1b 2a 2b + * gl_matchc: + * Number of matches in the current invocation of glob. + */ + +#include "includes.h" +#include "glob.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#if !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || \ + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \ + !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ + defined(BROKEN_GLOB) + +#include "charclass.h" + +#ifdef TILDE +# undef TILDE +#endif + +#define DOLLAR '$' +#define DOT '.' +#define EOS '\0' +#define LBRACKET '[' +#define NOT '!' +#define QUESTION '?' +#define QUOTE '\\' +#define RANGE '-' +#define RBRACKET ']' +#define SEP '/' +#define STAR '*' +#define TILDE '~' +#define UNDERSCORE '_' +#define LBRACE '{' +#define RBRACE '}' +#define SLASH '/' +#define COMMA ',' + +#ifndef DEBUG + +#define M_QUOTE 0x8000 +#define M_PROTECT 0x4000 +#define M_MASK 0xffff +#define M_ASCII 0x00ff + +typedef u_short Char; + +#else + +#define M_QUOTE 0x80 +#define M_PROTECT 0x40 +#define M_MASK 0xff +#define M_ASCII 0x7f + +typedef char Char; + +#endif + + +#define CHAR(c) ((Char)((c)&M_ASCII)) +#define META(c) ((Char)((c)|M_QUOTE)) +#define M_ALL META('*') +#define M_END META(']') +#define M_NOT META('!') +#define M_ONE META('?') +#define M_RNG META('-') +#define M_SET META('[') +#define M_CLASS META(':') +#define ismeta(c) (((c)&M_QUOTE) != 0) + +#define GLOB_LIMIT_MALLOC 65536 +#define GLOB_LIMIT_STAT 2048 +#define GLOB_LIMIT_READDIR 16384 + +struct glob_lim { + size_t glim_malloc; + size_t glim_stat; + size_t glim_readdir; +}; + +struct glob_path_stat { + char *gps_path; + struct stat *gps_stat; +}; + +static int compare(const void *, const void *); +static int compare_gps(const void *, const void *); +static int g_Ctoc(const Char *, char *, size_t); +static int g_lstat(Char *, struct stat *, glob_t *); +static DIR *g_opendir(Char *, glob_t *); +static Char *g_strchr(const Char *, int); +static int g_strncmp(const Char *, const char *, size_t); +static int g_stat(Char *, struct stat *, glob_t *); +static int glob0(const Char *, glob_t *, struct glob_lim *); +static int glob1(Char *, Char *, glob_t *, struct glob_lim *); +static int glob2(Char *, Char *, Char *, Char *, Char *, Char *, + glob_t *, struct glob_lim *); +static int glob3(Char *, Char *, Char *, Char *, Char *, + Char *, Char *, glob_t *, struct glob_lim *); +static int globextend(const Char *, glob_t *, struct glob_lim *, + struct stat *); +static const Char * + globtilde(const Char *, Char *, size_t, glob_t *); +static int globexp1(const Char *, glob_t *, struct glob_lim *); +static int globexp2(const Char *, const Char *, glob_t *, + struct glob_lim *); +static int match(Char *, Char *, Char *); +#ifdef DEBUG +static void qprintf(const char *, Char *); +#endif + +int +glob(const char *pattern, int flags, int (*errfunc)(const char *, int), + glob_t *pglob) +{ + const u_char *patnext; + int c; + Char *bufnext, *bufend, patbuf[PATH_MAX]; + struct glob_lim limit = { 0, 0, 0 }; + + patnext = (u_char *) pattern; + if (!(flags & GLOB_APPEND)) { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + pglob->gl_statv = NULL; + if (!(flags & GLOB_DOOFFS)) + pglob->gl_offs = 0; + } + pglob->gl_flags = flags & ~GLOB_MAGCHAR; + pglob->gl_errfunc = errfunc; + pglob->gl_matchc = 0; + + if (strnlen(pattern, PATH_MAX) == PATH_MAX) + return(GLOB_NOMATCH); + + if (pglob->gl_offs >= SSIZE_MAX || pglob->gl_pathc >= SSIZE_MAX || + pglob->gl_pathc >= SSIZE_MAX - pglob->gl_offs - 1) + return GLOB_NOSPACE; + + bufnext = patbuf; + bufend = bufnext + PATH_MAX - 1; + if (flags & GLOB_NOESCAPE) + while (bufnext < bufend && (c = *patnext++) != EOS) + *bufnext++ = c; + else { + /* Protect the quoted characters. */ + while (bufnext < bufend && (c = *patnext++) != EOS) + if (c == QUOTE) { + if ((c = *patnext++) == EOS) { + c = QUOTE; + --patnext; + } + *bufnext++ = c | M_PROTECT; + } else + *bufnext++ = c; + } + *bufnext = EOS; + + if (flags & GLOB_BRACE) + return globexp1(patbuf, pglob, &limit); + else + return glob0(patbuf, pglob, &limit); +} + +/* + * Expand recursively a glob {} pattern. When there is no more expansion + * invoke the standard globbing routine to glob the rest of the magic + * characters + */ +static int +globexp1(const Char *pattern, glob_t *pglob, struct glob_lim *limitp) +{ + const Char* ptr = pattern; + + /* Protect a single {}, for find(1), like csh */ + if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) + return glob0(pattern, pglob, limitp); + + if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL) + return globexp2(ptr, pattern, pglob, limitp); + + return glob0(pattern, pglob, limitp); +} + + +/* + * Recursive brace globbing helper. Tries to expand a single brace. + * If it succeeds then it invokes globexp1 with the new pattern. + * If it fails then it tries to glob the rest of the pattern and returns. + */ +static int +globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, + struct glob_lim *limitp) +{ + int i, rv; + Char *lm, *ls; + const Char *pe, *pm, *pl; + Char patbuf[PATH_MAX]; + + /* copy part up to the brace */ + for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) + ; + *lm = EOS; + ls = lm; + + /* Find the balanced brace */ + for (i = 0, pe = ++ptr; *pe; pe++) + if (*pe == LBRACKET) { + /* Ignore everything between [] */ + for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) + ; + if (*pe == EOS) { + /* + * We could not find a matching RBRACKET. + * Ignore and just look for RBRACE + */ + pe = pm; + } + } else if (*pe == LBRACE) + i++; + else if (*pe == RBRACE) { + if (i == 0) + break; + i--; + } + + /* Non matching braces; just glob the pattern */ + if (i != 0 || *pe == EOS) + return glob0(patbuf, pglob, limitp); + + for (i = 0, pl = pm = ptr; pm <= pe; pm++) { + switch (*pm) { + case LBRACKET: + /* Ignore everything between [] */ + for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++) + ; + if (*pm == EOS) { + /* + * We could not find a matching RBRACKET. + * Ignore and just look for RBRACE + */ + pm = pl; + } + break; + + case LBRACE: + i++; + break; + + case RBRACE: + if (i) { + i--; + break; + } + /* FALLTHROUGH */ + case COMMA: + if (i && *pm == COMMA) + break; + else { + /* Append the current string */ + for (lm = ls; (pl < pm); *lm++ = *pl++) + ; + + /* + * Append the rest of the pattern after the + * closing brace + */ + for (pl = pe + 1; (*lm++ = *pl++) != EOS; ) + ; + + /* Expand the current pattern */ +#ifdef DEBUG + qprintf("globexp2:", patbuf); +#endif + rv = globexp1(patbuf, pglob, limitp); + if (rv && rv != GLOB_NOMATCH) + return rv; + + /* move after the comma, to the next string */ + pl = pm + 1; + } + break; + + default: + break; + } + } + return 0; +} + + + +/* + * expand tilde from the passwd file. + */ +static const Char * +globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) +{ + struct passwd *pwd; + char *h; + const Char *p; + Char *b, *eb; + + if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) + return pattern; + + /* Copy up to the end of the string or / */ + eb = &patbuf[patbuf_len - 1]; + for (p = pattern + 1, h = (char *) patbuf; + h < (char *)eb && *p && *p != SLASH; *h++ = *p++) + ; + + *h = EOS; + +#if 0 + if (h == (char *)eb) + return what; +#endif + + if (((char *) patbuf)[0] == EOS) { + /* + * handle a plain ~ or ~/ by expanding $HOME + * first and then trying the password file + */ +#if 0 + if (issetugid() != 0 || (h = getenv("HOME")) == NULL) { +#endif + if ((getuid() != geteuid()) || (h = getenv("HOME")) == NULL) { + if ((pwd = getpwuid(getuid())) == NULL) + return pattern; + else + h = pwd->pw_dir; + } + } else { + /* + * Expand a ~user + */ + if ((pwd = getpwnam((char*) patbuf)) == NULL) + return pattern; + else + h = pwd->pw_dir; + } + + /* Copy the home directory */ + for (b = patbuf; b < eb && *h; *b++ = *h++) + ; + + /* Append the rest of the pattern */ + while (b < eb && (*b++ = *p++) != EOS) + ; + *b = EOS; + + return patbuf; +} + +static int +g_strncmp(const Char *s1, const char *s2, size_t n) +{ + int rv = 0; + + while (n--) { + rv = *(Char *)s1 - *(const unsigned char *)s2++; + if (rv) + break; + if (*s1++ == '\0') + break; + } + return rv; +} + +static int +g_charclass(const Char **patternp, Char **bufnextp) +{ + const Char *pattern = *patternp + 1; + Char *bufnext = *bufnextp; + const Char *colon; + struct cclass *cc; + size_t len; + + if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']') + return 1; /* not a character class */ + + len = (size_t)(colon - pattern); + for (cc = cclasses; cc->name != NULL; cc++) { + if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0') + break; + } + if (cc->name == NULL) + return -1; /* invalid character class */ + *bufnext++ = M_CLASS; + *bufnext++ = (Char)(cc - &cclasses[0]); + *bufnextp = bufnext; + *patternp += len + 3; + + return 0; +} + +/* + * The main glob() routine: compiles the pattern (optionally processing + * quotes), calls glob1() to do the real pattern matching, and finally + * sorts the list (unless unsorted operation is requested). Returns 0 + * if things went well, nonzero if errors occurred. It is not an error + * to find no matches. + */ +static int +glob0(const Char *pattern, glob_t *pglob, struct glob_lim *limitp) +{ + const Char *qpatnext; + int c, err; + size_t oldpathc; + Char *bufnext, patbuf[PATH_MAX]; + + qpatnext = globtilde(pattern, patbuf, PATH_MAX, pglob); + oldpathc = pglob->gl_pathc; + bufnext = patbuf; + + /* We don't need to check for buffer overflow any more. */ + while ((c = *qpatnext++) != EOS) { + switch (c) { + case LBRACKET: + c = *qpatnext; + if (c == NOT) + ++qpatnext; + if (*qpatnext == EOS || + g_strchr(qpatnext+1, RBRACKET) == NULL) { + *bufnext++ = LBRACKET; + if (c == NOT) + --qpatnext; + break; + } + *bufnext++ = M_SET; + if (c == NOT) + *bufnext++ = M_NOT; + c = *qpatnext++; + do { + if (c == LBRACKET && *qpatnext == ':') { + do { + err = g_charclass(&qpatnext, + &bufnext); + if (err) + break; + c = *qpatnext++; + } while (c == LBRACKET && *qpatnext == ':'); + if (err == -1 && + !(pglob->gl_flags & GLOB_NOCHECK)) + return GLOB_NOMATCH; + if (c == RBRACKET) + break; + } + *bufnext++ = CHAR(c); + if (*qpatnext == RANGE && + (c = qpatnext[1]) != RBRACKET) { + *bufnext++ = M_RNG; + *bufnext++ = CHAR(c); + qpatnext += 2; + } + } while ((c = *qpatnext++) != RBRACKET); + pglob->gl_flags |= GLOB_MAGCHAR; + *bufnext++ = M_END; + break; + case QUESTION: + pglob->gl_flags |= GLOB_MAGCHAR; + *bufnext++ = M_ONE; + break; + case STAR: + pglob->gl_flags |= GLOB_MAGCHAR; + /* collapse adjacent stars to one, + * to avoid exponential behavior + */ + if (bufnext == patbuf || bufnext[-1] != M_ALL) + *bufnext++ = M_ALL; + break; + default: + *bufnext++ = CHAR(c); + break; + } + } + *bufnext = EOS; +#ifdef DEBUG + qprintf("glob0:", patbuf); +#endif + + if ((err = glob1(patbuf, patbuf+PATH_MAX-1, pglob, limitp)) != 0) + return(err); + + /* + * If there was no match we are going to append the pattern + * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified + * and the pattern did not contain any magic characters + * GLOB_NOMAGIC is there just for compatibility with csh. + */ + if (pglob->gl_pathc == oldpathc) { + if ((pglob->gl_flags & GLOB_NOCHECK) || + ((pglob->gl_flags & GLOB_NOMAGIC) && + !(pglob->gl_flags & GLOB_MAGCHAR))) + return(globextend(pattern, pglob, limitp, NULL)); + else + return(GLOB_NOMATCH); + } + if (!(pglob->gl_flags & GLOB_NOSORT)) { + if ((pglob->gl_flags & GLOB_KEEPSTAT)) { + /* Keep the paths and stat info synced during sort */ + struct glob_path_stat *path_stat; + size_t i; + size_t n = pglob->gl_pathc - oldpathc; + size_t o = pglob->gl_offs + oldpathc; + + if ((path_stat = calloc(n, sizeof(*path_stat))) == NULL) + return GLOB_NOSPACE; + for (i = 0; i < n; i++) { + path_stat[i].gps_path = pglob->gl_pathv[o + i]; + path_stat[i].gps_stat = pglob->gl_statv[o + i]; + } + qsort(path_stat, n, sizeof(*path_stat), compare_gps); + for (i = 0; i < n; i++) { + pglob->gl_pathv[o + i] = path_stat[i].gps_path; + pglob->gl_statv[o + i] = path_stat[i].gps_stat; + } + free(path_stat); + } else { + qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, + pglob->gl_pathc - oldpathc, sizeof(char *), + compare); + } + } + return(0); +} + +static int +compare(const void *p, const void *q) +{ + return(strcmp(*(char **)p, *(char **)q)); +} + +static int +compare_gps(const void *_p, const void *_q) +{ + const struct glob_path_stat *p = (const struct glob_path_stat *)_p; + const struct glob_path_stat *q = (const struct glob_path_stat *)_q; + + return(strcmp(p->gps_path, q->gps_path)); +} + +static int +glob1(Char *pattern, Char *pattern_last, glob_t *pglob, struct glob_lim *limitp) +{ + Char pathbuf[PATH_MAX]; + + /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ + if (*pattern == EOS) + return(0); + return(glob2(pathbuf, pathbuf+PATH_MAX-1, + pathbuf, pathbuf+PATH_MAX-1, + pattern, pattern_last, pglob, limitp)); +} + +/* + * The functions glob2 and glob3 are mutually recursive; there is one level + * of recursion for each segment in the pattern that contains one or more + * meta characters. + */ +static int +glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, + Char *pattern, Char *pattern_last, glob_t *pglob, struct glob_lim *limitp) +{ + struct stat sb; + Char *p, *q; + int anymeta; + + /* + * Loop over pattern segments until end of pattern or until + * segment with meta character found. + */ + for (anymeta = 0;;) { + if (*pattern == EOS) { /* End of pattern? */ + *pathend = EOS; + + if ((pglob->gl_flags & GLOB_LIMIT) && + limitp->glim_stat++ >= GLOB_LIMIT_STAT) { + errno = 0; + *pathend++ = SEP; + *pathend = EOS; + return(GLOB_NOSPACE); + } + if (g_lstat(pathbuf, &sb, pglob)) + return(0); + + if (((pglob->gl_flags & GLOB_MARK) && + pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) || + (S_ISLNK(sb.st_mode) && + (g_stat(pathbuf, &sb, pglob) == 0) && + S_ISDIR(sb.st_mode)))) { + if (pathend+1 > pathend_last) + return (1); + *pathend++ = SEP; + *pathend = EOS; + } + ++pglob->gl_matchc; + return(globextend(pathbuf, pglob, limitp, &sb)); + } + + /* Find end of next segment, copy tentatively to pathend. */ + q = pathend; + p = pattern; + while (*p != EOS && *p != SEP) { + if (ismeta(*p)) + anymeta = 1; + if (q+1 > pathend_last) + return (1); + *q++ = *p++; + } + + if (!anymeta) { /* No expansion, do next segment. */ + pathend = q; + pattern = p; + while (*pattern == SEP) { + if (pathend+1 > pathend_last) + return (1); + *pathend++ = *pattern++; + } + } else + /* Need expansion, recurse. */ + return(glob3(pathbuf, pathbuf_last, pathend, + pathend_last, pattern, p, pattern_last, + pglob, limitp)); + } + /* NOTREACHED */ +} + +static int +glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, + Char *pattern, Char *restpattern, Char *restpattern_last, glob_t *pglob, + struct glob_lim *limitp) +{ + struct dirent *dp; + DIR *dirp; + int err; + char buf[PATH_MAX]; + + /* + * The readdirfunc declaration can't be prototyped, because it is + * assigned, below, to two functions which are prototyped in glob.h + * and dirent.h as taking pointers to differently typed opaque + * structures. + */ + struct dirent *(*readdirfunc)(void *); + + if (pathend > pathend_last) + return (1); + *pathend = EOS; + errno = 0; + + if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { + /* TODO: don't call for ENOENT or ENOTDIR? */ + if (pglob->gl_errfunc) { + if (g_Ctoc(pathbuf, buf, sizeof(buf))) + return(GLOB_ABORTED); + if (pglob->gl_errfunc(buf, errno) || + pglob->gl_flags & GLOB_ERR) + return(GLOB_ABORTED); + } + return(0); + } + + err = 0; + + /* Search directory for matching names. */ + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + readdirfunc = pglob->gl_readdir; + else + readdirfunc = (struct dirent *(*)(void *))readdir; + while ((dp = (*readdirfunc)(dirp))) { + u_char *sc; + Char *dc; + + if ((pglob->gl_flags & GLOB_LIMIT) && + limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) { + errno = 0; + *pathend++ = SEP; + *pathend = EOS; + err = GLOB_NOSPACE; + break; + } + + /* Initial DOT must be matched literally. */ + if (dp->d_name[0] == DOT && *pattern != DOT) + continue; + dc = pathend; + sc = (u_char *) dp->d_name; + while (dc < pathend_last && (*dc++ = *sc++) != EOS) + ; + if (dc >= pathend_last) { + *dc = EOS; + err = 1; + break; + } + + if (!match(pathend, pattern, restpattern)) { + *pathend = EOS; + continue; + } + err = glob2(pathbuf, pathbuf_last, --dc, pathend_last, + restpattern, restpattern_last, pglob, limitp); + if (err) + break; + } + + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + (*pglob->gl_closedir)(dirp); + else + closedir(dirp); + return(err); +} + + +/* + * Extend the gl_pathv member of a glob_t structure to accommodate a new item, + * add the new item, and update gl_pathc. + * + * This assumes the BSD realloc, which only copies the block when its size + * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic + * behavior. + * + * Return 0 if new item added, error code if memory couldn't be allocated. + * + * Invariant of the glob_t structure: + * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and + * gl_pathv points to (gl_offs + gl_pathc + 1) items. + */ +static int +globextend(const Char *path, glob_t *pglob, struct glob_lim *limitp, + struct stat *sb) +{ + char **pathv; + size_t i, newn, len; + char *copy = NULL; + const Char *p; + struct stat **statv; + + newn = 2 + pglob->gl_pathc + pglob->gl_offs; + if (pglob->gl_offs >= SSIZE_MAX || + pglob->gl_pathc >= SSIZE_MAX || + newn >= SSIZE_MAX || + SIZE_MAX / sizeof(*pathv) <= newn || + SIZE_MAX / sizeof(*statv) <= newn) { + nospace: + for (i = pglob->gl_offs; i < newn - 2; i++) { + if (pglob->gl_pathv && pglob->gl_pathv[i]) + free(pglob->gl_pathv[i]); + if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0 && + pglob->gl_pathv && pglob->gl_pathv[i]) + free(pglob->gl_statv[i]); + } + free(pglob->gl_pathv); + pglob->gl_pathv = NULL; + free(pglob->gl_statv); + pglob->gl_statv = NULL; + return(GLOB_NOSPACE); + } + + pathv = reallocarray(pglob->gl_pathv, newn, sizeof(*pathv)); + if (pathv == NULL) + goto nospace; + if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + pathv += pglob->gl_offs; + for (i = pglob->gl_offs; i > 0; i--) + *--pathv = NULL; + } + pglob->gl_pathv = pathv; + + if ((pglob->gl_flags & GLOB_KEEPSTAT) != 0) { + statv = reallocarray(pglob->gl_statv, newn, sizeof(*statv)); + if (statv == NULL) + goto nospace; + if (pglob->gl_statv == NULL && pglob->gl_offs > 0) { + /* first time around -- clear initial gl_offs items */ + statv += pglob->gl_offs; + for (i = pglob->gl_offs; i > 0; i--) + *--statv = NULL; + } + pglob->gl_statv = statv; + if (sb == NULL) + statv[pglob->gl_offs + pglob->gl_pathc] = NULL; + else { + limitp->glim_malloc += sizeof(**statv); + if ((pglob->gl_flags & GLOB_LIMIT) && + limitp->glim_malloc >= GLOB_LIMIT_MALLOC) { + errno = 0; + return(GLOB_NOSPACE); + } + if ((statv[pglob->gl_offs + pglob->gl_pathc] = + malloc(sizeof(**statv))) == NULL) + goto copy_error; + memcpy(statv[pglob->gl_offs + pglob->gl_pathc], sb, + sizeof(*sb)); + } + statv[pglob->gl_offs + pglob->gl_pathc + 1] = NULL; + } + + for (p = path; *p++;) + ; + len = (size_t)(p - path); + limitp->glim_malloc += len; + if ((copy = malloc(len)) != NULL) { + if (g_Ctoc(path, copy, len)) { + free(copy); + return(GLOB_NOSPACE); + } + pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; + } + pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; + + if ((pglob->gl_flags & GLOB_LIMIT) && + (newn * sizeof(*pathv)) + limitp->glim_malloc > + GLOB_LIMIT_MALLOC) { + errno = 0; + return(GLOB_NOSPACE); + } + copy_error: + return(copy == NULL ? GLOB_NOSPACE : 0); +} + + +/* + * pattern matching function for filenames. Each occurrence of the * + * pattern causes an iteration. + * + * Note, this function differs from the original as per the discussion + * here: https://research.swtch.com/glob + * + * Basically we removed the recursion and made it use the algorithm + * from Russ Cox to not go quadratic on cases like a file called + * ("a" x 100) . "x" matched against a pattern like "a*a*a*a*a*a*a*y". + */ +static int +match(Char *name, Char *pat, Char *patend) +{ + int ok, negate_range; + Char c, k; + Char *nextp = NULL; + Char *nextn = NULL; + +loop: + while (pat < patend) { + c = *pat++; + switch (c & M_MASK) { + case M_ALL: + while (pat < patend && (*pat & M_MASK) == M_ALL) + pat++; /* eat consecutive '*' */ + if (pat == patend) + return(1); + if (*name == EOS) + return(0); + nextn = name + 1; + nextp = pat - 1; + break; + case M_ONE: + if (*name++ == EOS) + goto fail; + break; + case M_SET: + ok = 0; + if ((k = *name++) == EOS) + goto fail; + if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS) + ++pat; + while (((c = *pat++) & M_MASK) != M_END) { + if ((c & M_MASK) == M_CLASS) { + Char idx = *pat & M_MASK; + if (idx < NCCLASSES && + cclasses[idx].isctype(k)) + ok = 1; + ++pat; + } + if ((*pat & M_MASK) == M_RNG) { + if (c <= k && k <= pat[1]) + ok = 1; + pat += 2; + } else if (c == k) + ok = 1; + } + if (ok == negate_range) + goto fail; + break; + default: + if (*name++ != c) + goto fail; + break; + } + } + if (*name == EOS) + return(1); + +fail: + if (nextn) { + pat = nextp; + name = nextn; + goto loop; + } + return(0); +} + +/* Free allocated data belonging to a glob_t structure. */ +void +globfree(glob_t *pglob) +{ + size_t i; + char **pp; + + if (pglob->gl_pathv != NULL) { + pp = pglob->gl_pathv + pglob->gl_offs; + for (i = pglob->gl_pathc; i--; ++pp) + free(*pp); + free(pglob->gl_pathv); + pglob->gl_pathv = NULL; + } + if (pglob->gl_statv != NULL) { + for (i = 0; i < pglob->gl_pathc; i++) { + free(pglob->gl_statv[i]); + } + free(pglob->gl_statv); + pglob->gl_statv = NULL; + } +} + +static DIR * +g_opendir(Char *str, glob_t *pglob) +{ + char buf[PATH_MAX]; + + if (!*str) + strlcpy(buf, ".", sizeof buf); + else { + if (g_Ctoc(str, buf, sizeof(buf))) + return(NULL); + } + + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_opendir)(buf)); + + return(opendir(buf)); +} + +static int +g_lstat(Char *fn, struct stat *sb, glob_t *pglob) +{ + char buf[PATH_MAX]; + + if (g_Ctoc(fn, buf, sizeof(buf))) + return(-1); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_lstat)(buf, sb)); + return(lstat(buf, sb)); +} + +static int +g_stat(Char *fn, struct stat *sb, glob_t *pglob) +{ + char buf[PATH_MAX]; + + if (g_Ctoc(fn, buf, sizeof(buf))) + return(-1); + if (pglob->gl_flags & GLOB_ALTDIRFUNC) + return((*pglob->gl_stat)(buf, sb)); + return(stat(buf, sb)); +} + +static Char * +g_strchr(const Char *str, int ch) +{ + do { + if (*str == ch) + return ((Char *)str); + } while (*str++); + return (NULL); +} + +static int +g_Ctoc(const Char *str, char *buf, size_t len) +{ + + while (len--) { + if ((*buf++ = *str++) == EOS) + return (0); + } + return (1); +} + +#ifdef DEBUG +static void +qprintf(const char *str, Char *s) +{ + Char *p; + + (void)printf("%s:\n", str); + for (p = s; *p; p++) + (void)printf("%c", CHAR(*p)); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", *p & M_PROTECT ? '"' : ' '); + (void)printf("\n"); + for (p = s; *p; p++) + (void)printf("%c", ismeta(*p) ? '_' : ' '); + (void)printf("\n"); +} +#endif + +#endif /* !defined(HAVE_GLOB) || !defined(GLOB_HAS_ALTDIRFUNC) || + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) */ diff --git a/aosp/external/openssh/openbsd-compat/glob.h b/aosp/external/openssh/openbsd-compat/glob.h new file mode 100644 index 0000000000000000000000000000000000000000..1692d36cc25adf4a95deeebe42e5ccab62c57c13 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/glob.h @@ -0,0 +1,108 @@ +/* $OpenBSD: glob.h,v 1.14 2019/02/04 16:45:40 millert Exp $ */ +/* $NetBSD: glob.h,v 1.5 1994/10/26 00:55:56 cgd Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)glob.h 8.1 (Berkeley) 6/2/93 + */ + +/* OPENBSD ORIGINAL: include/glob.h */ + +#if !defined(HAVE_GLOB_H) || !defined(GLOB_HAS_ALTDIRFUNC) || \ + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOB_HAS_GL_STATV) || \ + !defined(HAVE_DECL_GLOB_NOMATCH) || HAVE_DECL_GLOB_NOMATCH == 0 || \ + defined(BROKEN_GLOB) + +#ifndef _COMPAT_GLOB_H_ +#define _COMPAT_GLOB_H_ + +#include +#include + +# define glob_t _ssh_compat_glob_t +# define glob(a, b, c, d) _ssh__compat_glob(a, b, c, d) +# define globfree(a) _ssh__compat_globfree(a) + +struct stat; +typedef struct { + size_t gl_pathc; /* Count of total paths so far. */ + size_t gl_matchc; /* Count of paths matching pattern. */ + size_t gl_offs; /* Reserved at beginning of gl_pathv. */ + int gl_flags; /* Copy of flags parameter to glob. */ + char **gl_pathv; /* List of paths matching pattern. */ + struct stat **gl_statv; /* Stat entries corresponding to gl_pathv */ + /* Copy of errfunc parameter to glob. */ + int (*gl_errfunc)(const char *, int); + + /* + * Alternate filesystem access methods for glob; replacement + * versions of closedir(3), readdir(3), opendir(3), stat(2) + * and lstat(2). + */ + void (*gl_closedir)(void *); + struct dirent *(*gl_readdir)(void *); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *, struct stat *); + int (*gl_stat)(const char *, struct stat *); +} glob_t; + +#define GLOB_APPEND 0x0001 /* Append to output from previous call. */ +#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */ +#define GLOB_ERR 0x0004 /* Return on error. */ +#define GLOB_MARK 0x0008 /* Append / to matching directories. */ +#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */ +#define GLOB_NOSORT 0x0020 /* Don't sort. */ +#define GLOB_NOESCAPE 0x1000 /* Disable backslash escaping. */ + +#define GLOB_NOSPACE (-1) /* Malloc call failed. */ +#define GLOB_ABORTED (-2) /* Unignored error. */ +#define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK not set. */ +#define GLOB_NOSYS (-4) /* Function not supported. */ + +#define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */ +#define GLOB_BRACE 0x0080 /* Expand braces ala csh. */ +#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */ +#define GLOB_NOMAGIC 0x0200 /* GLOB_NOCHECK without magic chars (csh). */ +#define GLOB_QUOTE 0x0400 /* Quote special chars with \. */ +#define GLOB_TILDE 0x0800 /* Expand tilde names from the passwd file. */ +#define GLOB_LIMIT 0x2000 /* Limit pattern match output to ARG_MAX */ +#define GLOB_KEEPSTAT 0x4000 /* Retain stat data for paths in gl_statv. */ +#define GLOB_ABEND GLOB_ABORTED /* backward compatibility */ + +int glob(const char *, int, int (*)(const char *, int), glob_t *); +void globfree(glob_t *); + +#endif /* !_GLOB_H_ */ + +#endif /* !defined(HAVE_GLOB_H) || !defined(GLOB_HAS_ALTDIRFUNC) || + !defined(GLOB_HAS_GL_MATCHC) || !defined(GLOH_HAS_GL_STATV) */ + diff --git a/aosp/external/openssh/openbsd-compat/inet_aton.c b/aosp/external/openssh/openbsd-compat/inet_aton.c new file mode 100644 index 0000000000000000000000000000000000000000..5efcc5f0374a02821415af7d9e98ae6388bbb158 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/inet_aton.c @@ -0,0 +1,178 @@ +/* $OpenBSD: inet_addr.c,v 1.9 2005/08/06 20:30:03 espie Exp $ */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +/* OPENBSD ORIGINAL: lib/libc/net/inet_addr.c */ + +#include "includes.h" + +#if !defined(HAVE_INET_ATON) + +#include +#include +#include +#include + +#if 0 +/* + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ +in_addr_t +inet_addr(const char *cp) +{ + struct in_addr val; + + if (inet_aton(cp, &val)) + return (val.s_addr); + return (INADDR_NONE); +} +#endif + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr *addr) +{ + u_int32_t val; + int base, n; + char c; + u_int parts[4]; + u_int *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + for (;;) { + if (isascii(c) && isdigit(c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base == 16 && isascii(c) && isxdigit(c)) { + val = (val << 4) | + (c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isascii(c) || !isspace(c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if ((val > 0xffffff) || (parts[0] > 0xff)) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if ((val > 0xffff) || (parts[0] > 0xff) || (parts[1] > 0xff)) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if ((val > 0xff) || (parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); +} + +#endif /* !defined(HAVE_INET_ATON) */ diff --git a/aosp/external/openssh/openbsd-compat/inet_ntoa.c b/aosp/external/openssh/openbsd-compat/inet_ntoa.c new file mode 100644 index 0000000000000000000000000000000000000000..0eb7b3bd76c4fc522f154da17c56e429d2fb7b7f --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/inet_ntoa.c @@ -0,0 +1,59 @@ +/* $OpenBSD: inet_ntoa.c,v 1.6 2005/08/06 20:30:03 espie Exp $ */ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/inet_ntoa.c */ + +#include "includes.h" + +#if defined(BROKEN_INET_NTOA) || !defined(HAVE_INET_NTOA) + +/* + * Convert network-format internet address + * to base 256 d.d.d.d representation. + */ +#include +#include +#include +#include + +char * +inet_ntoa(struct in_addr in) +{ + static char b[18]; + char *p; + + p = (char *)∈ +#define UC(b) (((int)b)&0xff) + (void)snprintf(b, sizeof(b), + "%u.%u.%u.%u", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); + return (b); +} + +#endif /* defined(BROKEN_INET_NTOA) || !defined(HAVE_INET_NTOA) */ diff --git a/aosp/external/openssh/openbsd-compat/inet_ntop.c b/aosp/external/openssh/openbsd-compat/inet_ntop.c new file mode 100644 index 0000000000000000000000000000000000000000..c037f0805a6f70eb417a2d100e02a17894c668b6 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/inet_ntop.c @@ -0,0 +1,210 @@ +/* $OpenBSD: inet_ntop.c,v 1.8 2008/12/09 19:38:38 otto Exp $ */ + +/* Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/inet_ntop.c */ + +#include "includes.h" + +#ifndef HAVE_INET_NTOP + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 /* IPv6 T_AAAA */ +#endif + +#ifndef INT16SZ +#define INT16SZ 2 /* for systems without 16-bit ints */ +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static const char *inet_ntop4(const u_char *src, char *dst, size_t size); +static const char *inet_ntop6(const u_char *src, char *dst, size_t size); + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char * +inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, (size_t)size)); + case AF_INET6: + return (inet_ntop6(src, dst, (size_t)size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address, more or less like inet_ntoa() + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const u_char *src, char *dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]); + if (l <= 0 || l >= size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(const u_char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + char *tp, *ep; + struct { int base, len; } best, cur; + u_int words[IN6ADDRSZ / INT16SZ]; + int i; + int advance; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + ep = tmp + sizeof(tmp); + for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) + return (NULL); + tp += strlen(tp); + break; + } + advance = snprintf(tp, ep - tp, "%x", words[i]); + if (advance <= 0 || advance >= ep - tp) + return (NULL); + tp += advance; + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + if (tp + 1 >= ep) + return (NULL); + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} + +#endif /* !HAVE_INET_NTOP */ diff --git a/aosp/external/openssh/openbsd-compat/kludge-fd_set.c b/aosp/external/openssh/openbsd-compat/kludge-fd_set.c new file mode 100644 index 0000000000000000000000000000000000000000..6c2ffb64bddb0699edebd3aeebeb2882bfb899aa --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/kludge-fd_set.c @@ -0,0 +1,28 @@ +/* Placed in the public domain. */ + +/* + * _FORTIFY_SOURCE includes a misguided check for FD_SET(n)/FD_ISSET(b) + * where n > FD_SETSIZE. This breaks OpenSSH and other programs that + * explicitly allocate fd_sets. To avoid this, we wrap FD_SET in a + * function compiled without _FORTIFY_SOURCE. + */ + +#include "config.h" + +#if defined(HAVE_FEATURES_H) && defined(_FORTIFY_SOURCE) +# include +# if defined(__GNU_LIBRARY__) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2, 15) && (_FORTIFY_SOURCE > 0) +# undef _FORTIFY_SOURCE +# undef __USE_FORTIFY_LEVEL +# include +void kludge_FD_SET(int n, fd_set *set) { + FD_SET(n, set); +} +int kludge_FD_ISSET(int n, fd_set *set) { + return FD_ISSET(n, set); +} +# endif /* __GLIBC_PREREQ(2, 15) && (_FORTIFY_SOURCE > 0) */ +# endif /* __GNU_LIBRARY__ && __GLIBC_PREREQ */ +#endif /* HAVE_FEATURES_H && _FORTIFY_SOURCE */ + diff --git a/aosp/external/openssh/openbsd-compat/libressl-api-compat.c b/aosp/external/openssh/openbsd-compat/libressl-api-compat.c new file mode 100755 index 0000000000000000000000000000000000000000..4eb7227f68bc85bd8d917e1d22bba45da58dc933 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/libressl-api-compat.c @@ -0,0 +1,642 @@ +/* $OpenBSD: dsa_lib.c,v 1.29 2018/04/14 07:09:21 tb Exp $ */ +/* $OpenBSD: rsa_lib.c,v 1.37 2018/04/14 07:09:21 tb Exp $ */ +/* $OpenBSD: evp_lib.c,v 1.17 2018/09/12 06:35:38 djm Exp $ */ +/* $OpenBSD: dh_lib.c,v 1.32 2018/05/02 15:48:38 tb Exp $ */ +/* $OpenBSD: p_lib.c,v 1.24 2018/05/30 15:40:50 tb Exp $ */ +/* $OpenBSD: digest.c,v 1.30 2018/04/14 07:09:21 tb Exp $ */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* $OpenBSD: dsa_asn1.c,v 1.22 2018/06/14 17:03:19 jsing Exp $ */ +/* $OpenBSD: ecs_asn1.c,v 1.9 2018/03/17 15:24:44 tb Exp $ */ +/* $OpenBSD: digest.c,v 1.30 2018/04/14 07:09:21 tb Exp $ */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project 2000. + */ +/* ==================================================================== + * Copyright (c) 2000-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +/* $OpenBSD: rsa_meth.c,v 1.2 2018/09/12 06:35:38 djm Exp $ */ +/* + * Copyright (c) 2018 Theo Buehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include + +#include +#include +#include +#include +#include +#ifdef OPENSSL_HAS_ECC +#include +#endif +#include + +#ifndef HAVE_DSA_GET0_PQG +void +DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + if (p != NULL) + *p = d->p; + if (q != NULL) + *q = d->q; + if (g != NULL) + *g = d->g; +} +#endif /* HAVE_DSA_GET0_PQG */ + +#ifndef HAVE_DSA_SET0_PQG +int +DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + if ((d->p == NULL && p == NULL) || (d->q == NULL && q == NULL) || + (d->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(d->p); + d->p = p; + } + if (q != NULL) { + BN_free(d->q); + d->q = q; + } + if (g != NULL) { + BN_free(d->g); + d->g = g; + } + + return 1; +} +#endif /* HAVE_DSA_SET0_PQG */ + +#ifndef HAVE_DSA_GET0_KEY +void +DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key != NULL) + *pub_key = d->pub_key; + if (priv_key != NULL) + *priv_key = d->priv_key; +} +#endif /* HAVE_DSA_GET0_KEY */ + +#ifndef HAVE_DSA_SET0_KEY +int +DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) +{ + if (d->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(d->pub_key); + d->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(d->priv_key); + d->priv_key = priv_key; + } + + return 1; +} +#endif /* HAVE_DSA_SET0_KEY */ + +#ifndef HAVE_RSA_GET0_KEY +void +RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} +#endif /* HAVE_RSA_GET0_KEY */ + +#ifndef HAVE_RSA_SET0_KEY +int +RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} +#endif /* HAVE_RSA_SET0_KEY */ + +#ifndef HAVE_RSA_GET0_CRT_PARAMS +void +RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, + const BIGNUM **iqmp) +{ + if (dmp1 != NULL) + *dmp1 = r->dmp1; + if (dmq1 != NULL) + *dmq1 = r->dmq1; + if (iqmp != NULL) + *iqmp = r->iqmp; +} +#endif /* HAVE_RSA_GET0_CRT_PARAMS */ + +#ifndef HAVE_RSA_SET0_CRT_PARAMS +int +RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +{ + if ((r->dmp1 == NULL && dmp1 == NULL) || + (r->dmq1 == NULL && dmq1 == NULL) || + (r->iqmp == NULL && iqmp == NULL)) + return 0; + + if (dmp1 != NULL) { + BN_free(r->dmp1); + r->dmp1 = dmp1; + } + if (dmq1 != NULL) { + BN_free(r->dmq1); + r->dmq1 = dmq1; + } + if (iqmp != NULL) { + BN_free(r->iqmp); + r->iqmp = iqmp; + } + + return 1; +} +#endif /* HAVE_RSA_SET0_CRT_PARAMS */ + +#ifndef HAVE_RSA_GET0_FACTORS +void +RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + if (p != NULL) + *p = r->p; + if (q != NULL) + *q = r->q; +} +#endif /* HAVE_RSA_GET0_FACTORS */ + +#ifndef HAVE_RSA_SET0_FACTORS +int +RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +{ + if ((r->p == NULL && p == NULL) || (r->q == NULL && q == NULL)) + return 0; + + if (p != NULL) { + BN_free(r->p); + r->p = p; + } + if (q != NULL) { + BN_free(r->q); + r->q = q; + } + + return 1; +} +#endif /* HAVE_RSA_SET0_FACTORS */ + +#ifndef HAVE_EVP_CIPHER_CTX_GET_IV +int +EVP_CIPHER_CTX_get_iv(const EVP_CIPHER_CTX *ctx, unsigned char *iv, size_t len) +{ + if (ctx == NULL) + return 0; + if (EVP_CIPHER_CTX_iv_length(ctx) < 0) + return 0; + if (len != (size_t)EVP_CIPHER_CTX_iv_length(ctx)) + return 0; + if (len > EVP_MAX_IV_LENGTH) + return 0; /* sanity check; shouldn't happen */ + /* + * Skip the memcpy entirely when the requested IV length is zero, + * since the iv pointer may be NULL or invalid. + */ + if (len != 0) { + if (iv == NULL) + return 0; +# ifdef HAVE_EVP_CIPHER_CTX_IV + memcpy(iv, EVP_CIPHER_CTX_iv(ctx), len); +# else + memcpy(iv, ctx->iv, len); +# endif /* HAVE_EVP_CIPHER_CTX_IV */ + } + return 1; +} +#endif /* HAVE_EVP_CIPHER_CTX_GET_IV */ + +#ifndef HAVE_EVP_CIPHER_CTX_SET_IV +int +EVP_CIPHER_CTX_set_iv(EVP_CIPHER_CTX *ctx, const unsigned char *iv, size_t len) +{ + if (ctx == NULL) + return 0; + if (EVP_CIPHER_CTX_iv_length(ctx) < 0) + return 0; + if (len != (size_t)EVP_CIPHER_CTX_iv_length(ctx)) + return 0; + if (len > EVP_MAX_IV_LENGTH) + return 0; /* sanity check; shouldn't happen */ + /* + * Skip the memcpy entirely when the requested IV length is zero, + * since the iv pointer may be NULL or invalid. + */ + if (len != 0) { + if (iv == NULL) + return 0; +# ifdef HAVE_EVP_CIPHER_CTX_IV_NOCONST + memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, len); +# else + memcpy(ctx->iv, iv, len); +# endif /* HAVE_EVP_CIPHER_CTX_IV_NOCONST */ + } + return 1; +} +#endif /* HAVE_EVP_CIPHER_CTX_SET_IV */ + +#ifndef HAVE_DSA_SIG_GET0 +void +DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} +#endif /* HAVE_DSA_SIG_GET0 */ + +#ifndef HAVE_DSA_SIG_SET0 +int +DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + + BN_clear_free(sig->r); + sig->r = r; + BN_clear_free(sig->s); + sig->s = s; + + return 1; +} +#endif /* HAVE_DSA_SIG_SET0 */ + +#ifdef OPENSSL_HAS_ECC +#ifndef HAVE_ECDSA_SIG_GET0 +void +ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} +#endif /* HAVE_ECDSA_SIG_GET0 */ + +#ifndef HAVE_ECDSA_SIG_SET0 +int +ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + return 1; +} +#endif /* HAVE_ECDSA_SIG_SET0 */ +#endif /* OPENSSL_HAS_ECC */ + +#ifndef HAVE_DH_GET0_PQG +void +DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + if (p != NULL) + *p = dh->p; + if (q != NULL) + *q = dh->q; + if (g != NULL) + *g = dh->g; +} +#endif /* HAVE_DH_GET0_PQG */ + +#ifndef HAVE_DH_SET0_PQG +int +DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(dh->p); + dh->p = p; + } + if (q != NULL) { + BN_free(dh->q); + dh->q = q; + } + if (g != NULL) { + BN_free(dh->g); + dh->g = g; + } + + return 1; +} +#endif /* HAVE_DH_SET0_PQG */ + +#ifndef HAVE_DH_GET0_KEY +void +DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key != NULL) + *pub_key = dh->pub_key; + if (priv_key != NULL) + *priv_key = dh->priv_key; +} +#endif /* HAVE_DH_GET0_KEY */ + +#ifndef HAVE_DH_SET0_KEY +int +DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) +{ + if (pub_key != NULL) { + BN_free(dh->pub_key); + dh->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(dh->priv_key); + dh->priv_key = priv_key; + } + + return 1; +} +#endif /* HAVE_DH_SET0_KEY */ + +#ifndef HAVE_DH_SET_LENGTH +int +DH_set_length1(DH *dh, long length) +{ + if (length < 0 || length > INT_MAX) + return 0; + +#if !defined(OPENSSL_IS_BORINGSSL) + dh->length = length; +#endif + return 1; +} +#endif /* HAVE_DH_SET_LENGTH */ + +#ifndef HAVE_RSA_METH_FREE +void +RSA_meth_free(RSA_METHOD *meth) +{ + if (meth != NULL) { + free((char *)meth->name); + free(meth); + } +} +#endif /* HAVE_RSA_METH_FREE */ + +#ifndef HAVE_RSA_METH_DUP +RSA_METHOD * +RSA_meth_dup(const RSA_METHOD *meth) +{ + RSA_METHOD *copy; + + if ((copy = calloc(1, sizeof(*copy))) == NULL) + return NULL; + memcpy(copy, meth, sizeof(*copy)); + if ((copy->name = strdup(meth->name)) == NULL) { + free(copy); + return NULL; + } + + return copy; +} +#endif /* HAVE_RSA_METH_DUP */ + +#ifndef HAVE_RSA_METH_SET1_NAME +int +RSA_meth_set1_name(RSA_METHOD *meth, const char *name) +{ + char *copy; + + if ((copy = strdup(name)) == NULL) + return 0; + free((char *)meth->name); + meth->name = copy; + return 1; +} +#endif /* HAVE_RSA_METH_SET1_NAME */ + +#ifndef HAVE_RSA_METH_GET_FINISH +int +(*RSA_meth_get_finish(const RSA_METHOD *meth))(RSA *rsa) +{ + return meth->finish; +} +#endif /* HAVE_RSA_METH_GET_FINISH */ + +#ifndef HAVE_RSA_METH_SET_PRIV_ENC +int +RSA_meth_set_priv_enc(RSA_METHOD *meth, int (*priv_enc)(int flen, + const unsigned char *from, unsigned char *to, RSA *rsa, int padding)) +{ + meth->rsa_priv_enc = priv_enc; + return 1; +} +#endif /* HAVE_RSA_METH_SET_PRIV_ENC */ + +#ifndef HAVE_RSA_METH_SET_PRIV_DEC +int +RSA_meth_set_priv_dec(RSA_METHOD *meth, int (*priv_dec)(int flen, + const unsigned char *from, unsigned char *to, RSA *rsa, int padding)) +{ + meth->rsa_priv_dec = priv_dec; + return 1; +} +#endif /* HAVE_RSA_METH_SET_PRIV_DEC */ + +#ifndef HAVE_RSA_METH_SET_FINISH +int +RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa)) +{ + meth->finish = finish; + return 1; +} +#endif /* HAVE_RSA_METH_SET_FINISH */ + +#ifndef HAVE_EVP_PKEY_GET0_RSA +RSA * +EVP_PKEY_get0_RSA(EVP_PKEY *pkey) +{ + if (pkey->type != EVP_PKEY_RSA) { + /* EVPerror(EVP_R_EXPECTING_AN_RSA_KEY); */ + return NULL; + } + return pkey->pkey.rsa; +} +#endif /* HAVE_EVP_PKEY_GET0_RSA */ + +#ifndef HAVE_EVP_MD_CTX_NEW +EVP_MD_CTX * +EVP_MD_CTX_new(void) +{ + return calloc(1, sizeof(EVP_MD_CTX)); +} +#endif /* HAVE_EVP_MD_CTX_NEW */ + +#ifndef HAVE_EVP_MD_CTX_FREE +void +EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + if (ctx == NULL) + return; + + EVP_MD_CTX_cleanup(ctx); + + free(ctx); +} +#endif /* HAVE_EVP_MD_CTX_FREE */ + +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/openbsd-compat/md5.c b/aosp/external/openssh/openbsd-compat/md5.c new file mode 100644 index 0000000000000000000000000000000000000000..195ab515d1ba763b0d487e925ecc3fe880eec2d1 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/md5.c @@ -0,0 +1,251 @@ +/* $OpenBSD: md5.c,v 1.9 2014/01/08 06:14:57 tedu Exp $ */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "includes.h" + +#ifndef WITH_OPENSSL + +#include +#include +#include "md5.h" + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static u_int8_t PADDING[MD5_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(MD5_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + need = MD5_BLOCK_LENGTH - have; + + /* Update bitcount */ + ctx->count += (u_int64_t)len << 3; + + if (len >= need) { + if (have != 0) { + memcpy(ctx->buffer + have, input, need); + MD5Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ + while (len >= MD5_BLOCK_LENGTH) { + MD5Transform(ctx->state, input); + input += MD5_BLOCK_LENGTH; + len -= MD5_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + memcpy(ctx->buffer + have, input, len); +} + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Pad(MD5_CTX *ctx) +{ + u_int8_t count[8]; + size_t padlen; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD5_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD5_BLOCK_LENGTH; + MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD5Update(ctx, count, 8); +} + +/* + * Final wrapup--call MD5Pad, fill in digest and zero out ctx. + */ +void +MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) +{ + int i; + + MD5Pad(ctx); + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + memset(ctx, 0, sizeof(*ctx)); +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; + +#if BYTE_ORDER == LITTLE_ENDIAN + memcpy(in, block, sizeof(in)); +#else + for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { + in[a] = (u_int32_t)( + (u_int32_t)(block[a * 4 + 0]) | + (u_int32_t)(block[a * 4 + 1]) << 8 | + (u_int32_t)(block[a * 4 + 2]) << 16 | + (u_int32_t)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} +#endif /* !WITH_OPENSSL */ diff --git a/aosp/external/openssh/openbsd-compat/md5.h b/aosp/external/openssh/openbsd-compat/md5.h new file mode 100644 index 0000000000000000000000000000000000000000..c83c19dcac6068630610304b8960cea1569b4410 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/md5.h @@ -0,0 +1,51 @@ +/* $OpenBSD: md5.h,v 1.17 2012/12/05 23:19:57 deraadt Exp $ */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + */ + +#ifndef _MD5_H_ +#define _MD5_H_ + +#ifndef WITH_OPENSSL + +#define MD5_BLOCK_LENGTH 64 +#define MD5_DIGEST_LENGTH 16 +#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) + +typedef struct MD5Context { + u_int32_t state[4]; /* state */ + u_int64_t count; /* number of bits, mod 2^64 */ + u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ +} MD5_CTX; + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void MD5Pad(MD5_CTX *); +void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *) + __attribute__((__bounded__(__minbytes__,1,MD5_DIGEST_LENGTH))); +void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]) + __attribute__((__bounded__(__minbytes__,1,4))) + __attribute__((__bounded__(__minbytes__,2,MD5_BLOCK_LENGTH))); +char *MD5End(MD5_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,MD5_DIGEST_STRING_LENGTH))); +char *MD5File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,MD5_DIGEST_STRING_LENGTH))); +char *MD5FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,MD5_DIGEST_STRING_LENGTH))); +char *MD5Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,MD5_DIGEST_STRING_LENGTH))); + +#endif /* !WITH_OPENSSL */ + +#endif /* _MD5_H_ */ diff --git a/aosp/external/openssh/openbsd-compat/memmem.c b/aosp/external/openssh/openbsd-compat/memmem.c new file mode 100644 index 0000000000000000000000000000000000000000..2637401d7c6e3098fce7d8d611fb7702f181481c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/memmem.c @@ -0,0 +1,196 @@ +/* $OpenBSD: memmem.c,v 1.5 2020/04/16 12:39:28 claudio Exp $ */ + +/* + * Copyright (c) 2005-2020 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/memmem.c */ + +#include "includes.h" + +#ifndef HAVE_MEMMEM + +#include +#ifdef HAVE_STDINT_H +#include +#endif + +static char * +twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n) +{ + uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1]; + for (h+=2, k-=2; k; k--, hw = hw<<8 | *h++) + if (hw == nw) return (char *)h-2; + return hw == nw ? (char *)h-2 : 0; +} + +static char * +threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n) +{ + uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8; + uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8; + for (h+=3, k-=3; k; k--, hw = (hw|*h++)<<8) + if (hw == nw) return (char *)h-3; + return hw == nw ? (char *)h-3 : 0; +} + +static char * +fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n) +{ + uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3]; + uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3]; + for (h+=4, k-=4; k; k--, hw = hw<<8 | *h++) + if (hw == nw) return (char *)h-4; + return hw == nw ? (char *)h-4 : 0; +} + +#if 0 +/* In -portable, defines.h ensures that these are already defined. */ +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#define BITOP(a,b,op) \ + ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a)))) + +/* + * Maxime Crochemore and Dominique Perrin, Two-way string-matching, + * Journal of the ACM, 38(3):651-675, July 1991. + */ +static char * +twoway_memmem(const unsigned char *h, const unsigned char *z, + const unsigned char *n, size_t l) +{ + size_t i, ip, jp, k, p, ms, p0, mem, mem0; + size_t byteset[32 / sizeof(size_t)] = { 0 }; + size_t shift[256]; + + /* Computing length of needle and fill shift table */ + for (i=0; i n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + ms = ip; + p0 = p; + + /* And with the opposite comparison */ + ip = -1; jp = 0; k = p = 1; + while (jp+k ms+1) ms = ip; + else p = p0; + + /* Periodic needle? */ + if (memcmp(n, n+p, ms+1)) { + mem0 = 0; + p = MAX(ms, l-ms-1) + 1; + } else mem0 = l-p; + mem = 0; + + /* Search loop */ + for (;;) { + /* If remainder of haystack is shorter than needle, done */ + if (z-h < l) return 0; + + /* Check last byte first; advance by shift on mismatch */ + if (BITOP(byteset, h[l-1], &)) { + k = l-shift[h[l-1]]; + if (k) { + if (k < mem) k = mem; + h += k; + mem = 0; + continue; + } + } else { + h += l; + mem = 0; + continue; + } + + /* Compare right half */ + for (k=MAX(ms+1,mem); kmem && n[k-1] == h[k-1]; k--); + if (k <= mem) return (char *)h; + h += p; + mem = mem0; + } +} + +void * +memmem(const void *h0, size_t k, const void *n0, size_t l) +{ + const unsigned char *h = h0, *n = n0; + + /* Return immediately on empty needle */ + if (!l) return (void *)h; + + /* Return immediately when needle is longer than haystack */ + if (k +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(HAVE_MKDTEMP) + +#define MKTEMP_NAME 0 +#define MKTEMP_FILE 1 +#define MKTEMP_DIR 2 + +#define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +#define NUM_CHARS (sizeof(TEMPCHARS) - 1) + +static int +mktemp_internal(char *path, int slen, int mode) +{ + char *start, *cp, *ep; + const char *tempchars = TEMPCHARS; + unsigned int r, tries; + struct stat sb; + size_t len; + int fd; + + len = strlen(path); + if (len == 0 || slen < 0 || (size_t)slen >= len) { + errno = EINVAL; + return(-1); + } + ep = path + len - slen; + + tries = 1; + for (start = ep; start > path && start[-1] == 'X'; start--) { + if (tries < INT_MAX / NUM_CHARS) + tries *= NUM_CHARS; + } + tries *= 2; + + do { + for (cp = start; cp != ep; cp++) { + r = arc4random_uniform(NUM_CHARS); + *cp = tempchars[r]; + } + + switch (mode) { + case MKTEMP_NAME: + if (lstat(path, &sb) != 0) + return(errno == ENOENT ? 0 : -1); + break; + case MKTEMP_FILE: + fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); + if (fd != -1 || errno != EEXIST) + return(fd); + break; + case MKTEMP_DIR: + if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0) + return(0); + if (errno != EEXIST) + return(-1); + break; + } + } while (--tries); + + errno = EEXIST; + return(-1); +} + +#if 0 +char *_mktemp(char *); + +char * +_mktemp(char *path) +{ + if (mktemp_internal(path, 0, MKTEMP_NAME) == -1) + return(NULL); + return(path); +} + +__warn_references(mktemp, + "warning: mktemp() possibly used unsafely; consider using mkstemp()"); + +char * +mktemp(char *path) +{ + return(_mktemp(path)); +} +#endif + +int +mkstemp(char *path) +{ + return(mktemp_internal(path, 0, MKTEMP_FILE)); +} + +int +mkstemps(char *path, int slen) +{ + return(mktemp_internal(path, slen, MKTEMP_FILE)); +} + +char * +mkdtemp(char *path) +{ + int error; + + error = mktemp_internal(path, 0, MKTEMP_DIR); + return(error ? NULL : path); +} + +#endif /* !defined(HAVE_MKDTEMP) */ diff --git a/aosp/external/openssh/openbsd-compat/openbsd-compat.h b/aosp/external/openssh/openbsd-compat/openbsd-compat.h new file mode 100644 index 0000000000000000000000000000000000000000..4316ab84bf8979e66c6548357bef058444765e76 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/openbsd-compat.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 1999-2003 Damien Miller. All rights reserved. + * Copyright (c) 2003 Ben Lindstrom. All rights reserved. + * Copyright (c) 2002 Tim Rice. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OPENBSD_COMPAT_H +#define _OPENBSD_COMPAT_H + +#include "includes.h" + +#include +#include + +#include + +#include /* for wchar_t */ + +/* OpenBSD function replacements */ +#include "base64.h" +#include "sigact.h" +#include "readpassphrase.h" +#include "vis.h" +#include "getrrsetbyname.h" +#include "sha1.h" +#include "sha2.h" +#include "md5.h" +#include "blf.h" +#include "fnmatch.h" + +#if defined(HAVE_LOGIN_CAP) && !defined(HAVE_LOGIN_GETPWCLASS) +# include +# define login_getpwclass(pw) login_getclass(pw->pw_class) +#endif + +#ifndef HAVE_BASENAME +char *basename(const char *path); +#endif + +#ifndef HAVE_BINDRESVPORT_SA +int bindresvport_sa(int sd, struct sockaddr *sa); +#endif + +#ifndef HAVE_CLOSEFROM +void closefrom(int); +#endif + +#if defined(HAVE_DECL_FTRUNCATE) && HAVE_DECL_FTRUNCATE == 0 +int ftruncate(int filedes, off_t length); +#endif + +#ifndef HAVE_GETLINE +#include +ssize_t getline(char **, size_t *, FILE *); +#endif + +#ifndef HAVE_GETPAGESIZE +int getpagesize(void); +#endif + +#ifndef HAVE_GETCWD +char *getcwd(char *pt, size_t size); +#endif + +#ifndef HAVE_KILLPG +int killpg(pid_t, int); +#endif + +#if defined(HAVE_DECL_MEMMEM) && HAVE_DECL_MEMMEM == 0 +void *memmem(const void *, size_t, const void *, size_t); +#endif + +#ifndef HAVE_REALLOCARRAY +void *reallocarray(void *, size_t, size_t); +#endif + +#ifndef HAVE_RECALLOCARRAY +void *recallocarray(void *, size_t, size_t, size_t); +#endif + +#ifndef HAVE_RRESVPORT_AF +int rresvport_af(int *alport, sa_family_t af); +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_STRLCAT +size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_STRCASESTR +char *strcasestr(const char *, const char *); +#endif + +#ifndef HAVE_STRNLEN +size_t strnlen(const char *, size_t); +#endif + +#ifndef HAVE_STRNDUP +char *strndup(const char *s, size_t n); +#endif + +#ifndef HAVE_SETENV +int setenv(register const char *name, register const char *value, int rewrite); +#endif + +#ifndef HAVE_STRMODE +void strmode(int mode, char *p); +#endif + +#ifndef HAVE_STRPTIME +#include +char *strptime(const char *buf, const char *fmt, struct tm *tm); +#endif + +#if !defined(HAVE_MKDTEMP) +int mkstemps(char *path, int slen); +int mkstemp(char *path); +char *mkdtemp(char *path); +#endif + +#ifndef HAVE_DAEMON +int daemon(int nochdir, int noclose); +#endif + +#ifndef HAVE_DIRNAME +char *dirname(const char *path); +#endif + +#ifndef HAVE_FMT_SCALED +#define FMT_SCALED_STRSIZE 7 +int fmt_scaled(long long number, char *result); +#endif + +#ifndef HAVE_SCAN_SCALED +int scan_scaled(char *, long long *); +#endif + +#if defined(BROKEN_INET_NTOA) || !defined(HAVE_INET_NTOA) +char *inet_ntoa(struct in_addr in); +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +#endif + +#ifndef HAVE_INET_ATON +int inet_aton(const char *cp, struct in_addr *addr); +#endif + +#ifndef HAVE_STRSEP +char *strsep(char **stringp, const char *delim); +#endif + +#ifndef HAVE_SETPROCTITLE +void setproctitle(const char *fmt, ...); +void compat_init_setproctitle(int argc, char *argv[]); +#endif + +#ifndef HAVE_GETGROUPLIST +int getgrouplist(const char *, gid_t, gid_t *, int *); +#endif + +#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET) +int BSDgetopt(int argc, char * const *argv, const char *opts); +#include "openbsd-compat/getopt.h" +#endif + +#if ((defined(HAVE_DECL_READV) && HAVE_DECL_READV == 0) || \ + (defined(HAVE_DECL_WRITEV) && HAVE_DECL_WRITEV == 0)) +# include +# include + +# if defined(HAVE_DECL_READV) && HAVE_DECL_READV == 0 +int readv(int, struct iovec *, int); +# endif + +# if defined(HAVE_DECL_WRITEV) && HAVE_DECL_WRITEV == 0 +int writev(int, struct iovec *, int); +# endif +#endif + +/* Home grown routines */ +#include "bsd-signal.h" +#include "bsd-misc.h" +#include "bsd-setres_id.h" +#include "bsd-statvfs.h" +#include "bsd-waitpid.h" +#include "bsd-poll.h" + +#if defined(HAVE_DECL_GETPEEREID) && HAVE_DECL_GETPEEREID == 0 +int getpeereid(int , uid_t *, gid_t *); +#endif + +#ifdef HAVE_ARC4RANDOM +# ifndef HAVE_ARC4RANDOM_STIR +# define arc4random_stir() +# endif +#else +unsigned int arc4random(void); +void arc4random_stir(void); +#endif /* !HAVE_ARC4RANDOM */ + +#ifndef HAVE_ARC4RANDOM_BUF +void arc4random_buf(void *, size_t); +#endif + +#ifndef HAVE_ARC4RANDOM_UNIFORM +u_int32_t arc4random_uniform(u_int32_t); +#endif + +#ifndef HAVE_ASPRINTF +int asprintf(char **, const char *, ...); +#endif + +#ifndef HAVE_OPENPTY +# include /* for struct winsize */ +int openpty(int *, int *, char *, struct termios *, struct winsize *); +#endif /* HAVE_OPENPTY */ + +#ifndef HAVE_SNPRINTF +int snprintf(char *, size_t, SNPRINTF_CONST char *, ...); +#endif + +#ifndef HAVE_STRTOLL +long long strtoll(const char *, char **, int); +#endif + +#ifndef HAVE_STRTOUL +unsigned long strtoul(const char *, char **, int); +#endif + +#ifndef HAVE_STRTOULL +unsigned long long strtoull(const char *, char **, int); +#endif + +#ifndef HAVE_STRTONUM +long long strtonum(const char *, long long, long long, const char **); +#endif + +/* multibyte character support */ +#ifndef HAVE_MBLEN +# define mblen(x, y) (1) +#endif + +#ifndef HAVE_WCWIDTH +# define wcwidth(x) (((x) >= 0x20 && (x) <= 0x7e) ? 1 : -1) +/* force our no-op nl_langinfo and mbtowc */ +# undef HAVE_NL_LANGINFO +# undef HAVE_MBTOWC +# undef HAVE_LANGINFO_H +#endif + +#ifndef HAVE_NL_LANGINFO +# define nl_langinfo(x) "" +#endif + +#ifndef HAVE_MBTOWC +int mbtowc(wchar_t *, const char*, size_t); +#endif + +#if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF) +# include +#endif + +/* + * Some platforms unconditionally undefine va_copy() so we define VA_COPY() + * instead. This is known to be the case on at least some configurations of + * AIX with the xlc compiler. + */ +#ifndef VA_COPY +# ifdef HAVE_VA_COPY +# define VA_COPY(dest, src) va_copy(dest, src) +# else +# ifdef HAVE___VA_COPY +# define VA_COPY(dest, src) __va_copy(dest, src) +# else +# define VA_COPY(dest, src) (dest) = (src) +# endif +# endif +#endif + +#ifndef HAVE_VASPRINTF +int vasprintf(char **, const char *, va_list); +#endif + +#ifndef HAVE_VSNPRINTF +int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#ifndef HAVE_USER_FROM_UID +char *user_from_uid(uid_t, int); +#endif + +#ifndef HAVE_GROUP_FROM_GID +char *group_from_gid(gid_t, int); +#endif + +#ifndef HAVE_TIMINGSAFE_BCMP +int timingsafe_bcmp(const void *, const void *, size_t); +#endif + +#ifndef HAVE_BCRYPT_PBKDF +int bcrypt_pbkdf(const char *, size_t, const uint8_t *, size_t, + uint8_t *, size_t, unsigned int); +#endif + +#ifndef HAVE_EXPLICIT_BZERO +void explicit_bzero(void *p, size_t n); +#endif + +#ifndef HAVE_FREEZERO +void freezero(void *, size_t); +#endif + +#ifndef HAVE_LOCALTIME_R +struct tm *localtime_r(const time_t *, struct tm *); +#endif + +char *xcrypt(const char *password, const char *salt); +char *shadow_pw(struct passwd *pw); + +/* rfc2553 socket API replacements */ +#include "fake-rfc2553.h" + +/* Routines for a single OS platform */ +#include "bsd-cygwin_util.h" + +#include "port-aix.h" +#include "port-irix.h" +#include "port-linux.h" +#include "port-solaris.h" +#include "port-net.h" +#include "port-uw.h" + +/* _FORTIFY_SOURCE breaks FD_ISSET(n)/FD_SET(n) for n > FD_SETSIZE. Avoid. */ +#if defined(HAVE_FEATURES_H) && defined(_FORTIFY_SOURCE) +# include +# if defined(__GNU_LIBRARY__) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2, 15) && (_FORTIFY_SOURCE > 0) +# include /* Ensure include guard is defined */ +# undef FD_SET +# undef FD_ISSET +# define FD_SET(n, set) kludge_FD_SET(n, set) +# define FD_ISSET(n, set) kludge_FD_ISSET(n, set) +void kludge_FD_SET(int, fd_set *); +int kludge_FD_ISSET(int, fd_set *); +# endif /* __GLIBC_PREREQ(2, 15) && (_FORTIFY_SOURCE > 0) */ +# endif /* __GNU_LIBRARY__ && __GLIBC_PREREQ */ +#endif /* HAVE_FEATURES_H && _FORTIFY_SOURCE */ + +#endif /* _OPENBSD_COMPAT_H */ diff --git a/aosp/external/openssh/openbsd-compat/openssl-compat.c b/aosp/external/openssh/openbsd-compat/openssl-compat.c new file mode 100644 index 0000000000000000000000000000000000000000..a37ca61bf2d427b82017dea770feb81d73ad70ed --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/openssl-compat.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2005 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define SSH_DONT_OVERLOAD_OPENSSL_FUNCS +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include +#include + +#ifdef USE_OPENSSL_ENGINE +# include +# include +#endif + +#include "log.h" + +#include "openssl-compat.h" + +/* + * OpenSSL version numbers: MNNFFPPS: major minor fix patch status + * We match major, minor, fix and status (not patch) for <1.0.0. + * After that, we acceptable compatible fix versions (so we + * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed + * within a patch series. + */ + +int +ssh_compatible_openssl(long headerver, long libver) +{ + long mask, hfix, lfix; + + /* exact match is always OK */ + if (headerver == libver) + return 1; + + /* for versions < 1.0.0, major,minor,fix,status must match */ + if (headerver < 0x1000000f) { + mask = 0xfffff00fL; /* major,minor,fix,status */ + return (headerver & mask) == (libver & mask); + } + + /* + * For versions >= 1.0.0, major,minor,status must match and library + * fix version must be equal to or newer than the header. + */ + mask = 0xfff0000fL; /* major,minor,status */ + hfix = (headerver & 0x000ff000) >> 12; + lfix = (libver & 0x000ff000) >> 12; + if ( (headerver & mask) == (libver & mask) && lfix >= hfix) + return 1; + return 0; +} + +void +ssh_libcrypto_init(void) +{ +#if defined(HAVE_OPENSSL_INIT_CRYPTO) && \ + defined(OPENSSL_INIT_ADD_ALL_CIPHERS) && \ + defined(OPENSSL_INIT_ADD_ALL_DIGESTS) + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | + OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); +#elif defined(HAVE_OPENSSL_ADD_ALL_ALGORITHMS) + OpenSSL_add_all_algorithms(); +#endif + +#ifdef USE_OPENSSL_ENGINE + /* Enable use of crypto hardware */ + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + + /* Load the libcrypto config file to pick up engines defined there */ +# if defined(HAVE_OPENSSL_INIT_CRYPTO) && defined(OPENSSL_INIT_LOAD_CONFIG) + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | + OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CONFIG, NULL); +# else + OPENSSL_config(NULL); +# endif +#endif /* USE_OPENSSL_ENGINE */ +} + +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/openbsd-compat/openssl-compat.h b/aosp/external/openssh/openbsd-compat/openssl-compat.h new file mode 100755 index 0000000000000000000000000000000000000000..8f8a6030e0e3d91270c2663fe6b28f2bd77030da --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/openssl-compat.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2005 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OPENSSL_COMPAT_H +#define _OPENSSL_COMPAT_H + +#include "includes.h" +#ifdef WITH_OPENSSL + +#include +#include +#include +#include +#include +#ifdef OPENSSL_HAS_ECC +#include +#endif +#include + +int ssh_compatible_openssl(long, long); +void ssh_libcrypto_init(void); + +#if (OPENSSL_VERSION_NUMBER < 0x1000100fL) +# error OpenSSL 1.0.1 or greater is required +#endif + +#ifndef OPENSSL_VERSION +# define OPENSSL_VERSION SSLEAY_VERSION +#endif + +#ifndef HAVE_OPENSSL_VERSION +# define OpenSSL_version(x) SSLeay_version(x) +#endif + +#ifndef HAVE_OPENSSL_VERSION_NUM +# define OpenSSL_version_num SSLeay +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10000001L +# define LIBCRYPTO_EVP_INL_TYPE unsigned int +#else +# define LIBCRYPTO_EVP_INL_TYPE size_t +#endif + +#ifndef OPENSSL_RSA_MAX_MODULUS_BITS +# define OPENSSL_RSA_MAX_MODULUS_BITS 16384 +#endif +#ifndef OPENSSL_DSA_MAX_MODULUS_BITS +# define OPENSSL_DSA_MAX_MODULUS_BITS 10000 +#endif + +#ifdef LIBRESSL_VERSION_NUMBER +# if LIBRESSL_VERSION_NUMBER < 0x3010000fL +# define HAVE_BROKEN_CHACHA20 +# endif +#endif + +#ifndef OPENSSL_HAVE_EVPCTR +# define EVP_aes_128_ctr evp_aes_128_ctr +# define EVP_aes_192_ctr evp_aes_128_ctr +# define EVP_aes_256_ctr evp_aes_128_ctr +const EVP_CIPHER *evp_aes_128_ctr(void); +void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t); +#endif + +/* Avoid some #ifdef. Code that uses these is unreachable without GCM */ +#if !defined(OPENSSL_HAVE_EVPGCM) && !defined(EVP_CTRL_GCM_SET_IV_FIXED) +# define EVP_CTRL_GCM_SET_IV_FIXED -1 +# define EVP_CTRL_GCM_IV_GEN -1 +# define EVP_CTRL_GCM_SET_TAG -1 +# define EVP_CTRL_GCM_GET_TAG -1 +#endif + +/* Replace missing EVP_CIPHER_CTX_ctrl() with something that returns failure */ +#ifndef HAVE_EVP_CIPHER_CTX_CTRL +# ifdef OPENSSL_HAVE_EVPGCM +# error AES-GCM enabled without EVP_CIPHER_CTX_ctrl /* shouldn't happen */ +# else +# define EVP_CIPHER_CTX_ctrl(a,b,c,d) (0) +# endif +#endif + +/* LibreSSL/OpenSSL 1.1x API compat */ +#ifndef HAVE_DSA_GET0_PQG +void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, + const BIGNUM **g); +#endif /* HAVE_DSA_GET0_PQG */ + +#ifndef HAVE_DSA_SET0_PQG +int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); +#endif /* HAVE_DSA_SET0_PQG */ + +#ifndef HAVE_DSA_GET0_KEY +void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, + const BIGNUM **priv_key); +#endif /* HAVE_DSA_GET0_KEY */ + +#ifndef HAVE_DSA_SET0_KEY +int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); +#endif /* HAVE_DSA_SET0_KEY */ + +#ifndef HAVE_EVP_CIPHER_CTX_GET_IV +# ifdef HAVE_EVP_CIPHER_CTX_GET_UPDATED_IV +# define EVP_CIPHER_CTX_get_iv EVP_CIPHER_CTX_get_updated_iv +# else /* HAVE_EVP_CIPHER_CTX_GET_UPDATED_IV */ +int EVP_CIPHER_CTX_get_iv(const EVP_CIPHER_CTX *ctx, + unsigned char *iv, size_t len); +# endif /* HAVE_EVP_CIPHER_CTX_GET_UPDATED_IV */ +#endif /* HAVE_EVP_CIPHER_CTX_GET_IV */ + +#ifndef HAVE_EVP_CIPHER_CTX_SET_IV +int EVP_CIPHER_CTX_set_iv(EVP_CIPHER_CTX *ctx, + const unsigned char *iv, size_t len); +#endif /* HAVE_EVP_CIPHER_CTX_SET_IV */ + +#ifndef HAVE_RSA_GET0_KEY +void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, + const BIGNUM **d); +#endif /* HAVE_RSA_GET0_KEY */ + +#ifndef HAVE_RSA_SET0_KEY +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); +#endif /* HAVE_RSA_SET0_KEY */ + +#ifndef HAVE_RSA_GET0_CRT_PARAMS +void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, + const BIGNUM **iqmp); +#endif /* HAVE_RSA_GET0_CRT_PARAMS */ + +#ifndef HAVE_RSA_SET0_CRT_PARAMS +int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); +#endif /* HAVE_RSA_SET0_CRT_PARAMS */ + +#ifndef HAVE_RSA_GET0_FACTORS +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); +#endif /* HAVE_RSA_GET0_FACTORS */ + +#ifndef HAVE_RSA_SET0_FACTORS +int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); +#endif /* HAVE_RSA_SET0_FACTORS */ + +#ifndef DSA_SIG_GET0 +void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); +#endif /* DSA_SIG_GET0 */ + +#ifndef DSA_SIG_SET0 +int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s); +#endif /* DSA_SIG_SET0 */ + +#ifdef OPENSSL_HAS_ECC +#ifndef HAVE_ECDSA_SIG_GET0 +void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); +#endif /* HAVE_ECDSA_SIG_GET0 */ + +#ifndef HAVE_ECDSA_SIG_SET0 +int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); +#endif /* HAVE_ECDSA_SIG_SET0 */ +#endif /* OPENSSL_HAS_ECC */ + +#ifndef HAVE_DH_GET0_PQG +void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, + const BIGNUM **g); +#endif /* HAVE_DH_GET0_PQG */ + +#ifndef HAVE_DH_SET0_PQG +int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); +#endif /* HAVE_DH_SET0_PQG */ + +#ifndef HAVE_DH_GET0_KEY +void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key); +#endif /* HAVE_DH_GET0_KEY */ + +#ifndef HAVE_DH_SET0_KEY +int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key); +#endif /* HAVE_DH_SET0_KEY */ + +#ifndef HAVE_DH_SET_LENGTH +int DH_set_length1(DH *dh, long length); +#endif /* HAVE_DH_SET_LENGTH */ + +#ifndef HAVE_RSA_METH_FREE +void RSA_meth_free(RSA_METHOD *meth); +#endif /* HAVE_RSA_METH_FREE */ + +#ifndef HAVE_RSA_METH_DUP +RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth); +#endif /* HAVE_RSA_METH_DUP */ + +#ifndef HAVE_RSA_METH_SET1_NAME +int RSA_meth_set1_name(RSA_METHOD *meth, const char *name); +#endif /* HAVE_RSA_METH_SET1_NAME */ + +#ifndef HAVE_RSA_METH_GET_FINISH +int (*RSA_meth_get_finish(const RSA_METHOD *meth))(RSA *rsa); +#endif /* HAVE_RSA_METH_GET_FINISH */ + +#ifndef HAVE_RSA_METH_SET_PRIV_ENC +int RSA_meth_set_priv_enc(RSA_METHOD *meth, int (*priv_enc)(int flen, + const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); +#endif /* HAVE_RSA_METH_SET_PRIV_ENC */ + +#ifndef HAVE_RSA_METH_SET_PRIV_DEC +int RSA_meth_set_priv_dec(RSA_METHOD *meth, int (*priv_dec)(int flen, + const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); +#endif /* HAVE_RSA_METH_SET_PRIV_DEC */ + +#ifndef HAVE_RSA_METH_SET_FINISH +int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa)); +#endif /* HAVE_RSA_METH_SET_FINISH */ + +#ifndef HAVE_EVP_PKEY_GET0_RSA +RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey); +#endif /* HAVE_EVP_PKEY_GET0_RSA */ + +#ifndef HAVE_EVP_MD_CTX_new +EVP_MD_CTX *EVP_MD_CTX_new(void); +#endif /* HAVE_EVP_MD_CTX_new */ + +#ifndef HAVE_EVP_MD_CTX_free +void EVP_MD_CTX_free(EVP_MD_CTX *ctx); +#endif /* HAVE_EVP_MD_CTX_free */ + +#endif /* WITH_OPENSSL */ +#endif /* _OPENSSL_COMPAT_H */ diff --git a/aosp/external/openssh/openbsd-compat/port-aix.c b/aosp/external/openssh/openbsd-compat/port-aix.c new file mode 100644 index 0000000000000000000000000000000000000000..2ac9bad09834e3812114ad0e4e04f9e7dfe2afd5 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-aix.c @@ -0,0 +1,483 @@ +/* + * + * Copyright (c) 2001 Gert Doering. All rights reserved. + * Copyright (c) 2003,2004,2005,2006 Darren Tucker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "includes.h" + +#ifdef _AIX + +#include "xmalloc.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "ssh.h" +#include "ssh_api.h" +#include "log.h" + +#include +#if defined(HAVE_NETDB_H) +# include +#endif +#include +#include +#include +#include +#include + +#ifdef WITH_AIXAUTHENTICATE +# include +# include +# if defined(HAVE_SYS_AUDIT_H) && defined(AIX_LOGINFAILED_4ARG) +# include +# endif +# include +#endif + +#include "port-aix.h" + +static char *lastlogin_msg = NULL; + +# ifdef HAVE_SETAUTHDB +static char old_registry[REGISTRY_SIZE] = ""; +# endif + +/* + * AIX has a "usrinfo" area where logname and other stuff is stored - + * a few applications actually use this and die if it's not set + * + * NOTE: TTY= should be set, but since no one uses it and it's hard to + * acquire due to privsep code. We will just drop support. + */ +void +aix_usrinfo(struct passwd *pw) +{ + u_int i; + size_t len; + char *cp; + + len = sizeof("LOGNAME= NAME= ") + (2 * strlen(pw->pw_name)); + cp = xmalloc(len); + + i = snprintf(cp, len, "LOGNAME=%s%cNAME=%s%c", pw->pw_name, '\0', + pw->pw_name, '\0'); + if (usrinfo(SETUINFO, cp, i) == -1) + fatal("Couldn't set usrinfo: %s", strerror(errno)); + debug3("AIX/UsrInfo: set len %d", i); + + free(cp); +} + +# ifdef WITH_AIXAUTHENTICATE +/* + * Remove embedded newlines in string (if any). + * Used before logging messages returned by AIX authentication functions + * so the message is logged on one line. + */ +void +aix_remove_embedded_newlines(char *p) +{ + if (p == NULL) + return; + + for (; *p; p++) { + if (*p == '\n') + *p = ' '; + } + /* Remove trailing whitespace */ + if (*--p == ' ') + *p = '\0'; +} + +/* + * Test specifically for the case where SYSTEM == NONE and AUTH1 contains + * anything other than NONE or SYSTEM, which indicates that the admin has + * configured the account for purely AUTH1-type authentication. + * + * Since authenticate() doesn't check AUTH1, and sshd can't sanely support + * AUTH1 itself, in such a case authenticate() will allow access without + * authentation, which is almost certainly not what the admin intends. + * + * (The native tools, eg login, will process the AUTH1 list in addition to + * the SYSTEM list by using ckuserID(), however ckuserID() and AUTH1 methods + * have been deprecated since AIX 4.2.x and would be very difficult for sshd + * to support. + * + * Returns 0 if an unsupportable combination is found, 1 otherwise. + */ +static int +aix_valid_authentications(const char *user) +{ + char *auth1, *sys, *p; + int valid = 1; + + if (getuserattr((char *)user, S_AUTHSYSTEM, &sys, SEC_CHAR) != 0) { + logit("Can't retrieve attribute SYSTEM for %s: %.100s", + user, strerror(errno)); + return 0; + } + + debug3("AIX SYSTEM attribute %s", sys); + if (strcmp(sys, "NONE") != 0) + return 1; /* not "NONE", so is OK */ + + if (getuserattr((char *)user, S_AUTH1, &auth1, SEC_LIST) != 0) { + logit("Can't retrieve attribute auth1 for %s: %.100s", + user, strerror(errno)); + return 0; + } + + p = auth1; + /* A SEC_LIST is concatenated strings, ending with two NULs. */ + while (p[0] != '\0' && p[1] != '\0') { + debug3("AIX auth1 attribute list member %s", p); + if (strcmp(p, "NONE") != 0 && strcmp(p, "SYSTEM")) { + logit("Account %s has unsupported auth1 value '%s'", + user, p); + valid = 0; + } + p += strlen(p) + 1; + } + + return (valid); +} + +/* + * Do authentication via AIX's authenticate routine. We loop until the + * reenter parameter is 0, but normally authenticate is called only once. + * + * Note: this function returns 1 on success, whereas AIX's authenticate() + * returns 0. + */ +int +sys_auth_passwd(struct ssh *ssh, const char *password) +{ + Authctxt *ctxt = ssh->authctxt; + char *authmsg = NULL, *msg = NULL, *name = ctxt->pw->pw_name; + int r, authsuccess = 0, expired, reenter, result; + + do { + result = authenticate((char *)name, (char *)password, &reenter, + &authmsg); + aix_remove_embedded_newlines(authmsg); + debug3("AIX/authenticate result %d, authmsg %.100s", result, + authmsg); + } while (reenter); + + if (!aix_valid_authentications(name)) + result = -1; + + if (result == 0) { + authsuccess = 1; + + /* + * Record successful login. We don't have a pty yet, so just + * label the line as "ssh" + */ + aix_setauthdb(name); + + /* + * Check if the user's password is expired. + */ + expired = passwdexpired(name, &msg); + if (msg && *msg) { + if ((r = sshbuf_put(ctxt->loginmsg, + msg, strlen(msg))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + aix_remove_embedded_newlines(msg); + } + debug3("AIX/passwdexpired returned %d msg %.100s", expired, msg); + + switch (expired) { + case 0: /* password not expired */ + break; + case 1: /* expired, password change required */ + ctxt->force_pwchange = 1; + break; + default: /* user can't change(2) or other error (-1) */ + logit("Password can't be changed for user %s: %.100s", + name, msg); + free(msg); + authsuccess = 0; + } + + aix_restoreauthdb(); + } + + free(authmsg); + + return authsuccess; +} + +/* + * Check if specified account is permitted to log in. + * Returns 1 if login is allowed, 0 if not allowed. + */ +int +sys_auth_allowed_user(struct passwd *pw, struct sshbuf *loginmsg) +{ + char *msg = NULL; + int r, result, permitted = 0; + struct stat st; + + /* + * Don't perform checks for root account (PermitRootLogin controls + * logins via ssh) or if running as non-root user (since + * loginrestrictions will always fail due to insufficient privilege). + */ + if (pw->pw_uid == 0 || geteuid() != 0) { + debug3("%s: not checking", __func__); + return 1; + } + + result = loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg); + if (result == 0) + permitted = 1; + /* + * If restricted because /etc/nologin exists, the login will be denied + * in session.c after the nologin message is sent, so allow for now + * and do not append the returned message. + */ + if (result == -1 && errno == EPERM && stat(_PATH_NOLOGIN, &st) == 0) + permitted = 1; + else if (msg != NULL) { + if ((r = sshbuf_put(loginmsg, msg, strlen(msg))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + if (msg == NULL) + msg = xstrdup("(none)"); + aix_remove_embedded_newlines(msg); + debug3("AIX/loginrestrictions returned %d msg %.100s", result, msg); + + if (!permitted) + logit("Login restricted for %s: %.100s", pw->pw_name, msg); + free(msg); + return permitted; +} + +int +sys_auth_record_login(const char *user, const char *host, const char *ttynm, + struct sshbuf *loginmsg) +{ + char *msg = NULL; + int success = 0; + + aix_setauthdb(user); + if (loginsuccess((char *)user, (char *)host, (char *)ttynm, &msg) == 0) { + success = 1; + if (msg != NULL) { + debug("AIX/loginsuccess: msg %s", msg); + if (lastlogin_msg == NULL) + lastlogin_msg = msg; + } + } + aix_restoreauthdb(); + return (success); +} + +char * +sys_auth_get_lastlogin_msg(const char *user, uid_t uid) +{ + char *msg = lastlogin_msg; + + lastlogin_msg = NULL; + return msg; +} + +# ifdef CUSTOM_FAILED_LOGIN +/* + * record_failed_login: generic "login failed" interface function + */ +void +record_failed_login(struct ssh *ssh, const char *user, const char *hostname, + const char *ttyname) +{ + if (geteuid() != 0) + return; + + aix_setauthdb(user); +# ifdef AIX_LOGINFAILED_4ARG + loginfailed((char *)user, (char *)hostname, (char *)ttyname, + AUDIT_FAIL_AUTH); +# else + loginfailed((char *)user, (char *)hostname, (char *)ttyname); +# endif + aix_restoreauthdb(); +} +# endif /* CUSTOM_FAILED_LOGIN */ + +/* + * If we have setauthdb, retrieve the password registry for the user's + * account then feed it to setauthdb. This will mean that subsequent AIX auth + * functions will only use the specified loadable module. If we don't have + * setauthdb this is a no-op. + */ +void +aix_setauthdb(const char *user) +{ +# ifdef HAVE_SETAUTHDB + char *registry; + + if (setuserdb(S_READ) == -1) { + debug3("%s: Could not open userdb to read", __func__); + return; + } + + if (getuserattr((char *)user, S_REGISTRY, ®istry, SEC_CHAR) == 0) { + if (setauthdb(registry, old_registry) == 0) + debug3("AIX/setauthdb set registry '%s'", registry); + else + debug3("AIX/setauthdb set registry '%s' failed: %s", + registry, strerror(errno)); + } else + debug3("%s: Could not read S_REGISTRY for user: %s", __func__, + strerror(errno)); + enduserdb(); +# endif /* HAVE_SETAUTHDB */ +} + +/* + * Restore the user's registry settings from old_registry. + * Note that if the first aix_setauthdb fails, setauthdb("") is still safe + * (it restores the system default behaviour). If we don't have setauthdb, + * this is a no-op. + */ +void +aix_restoreauthdb(void) +{ +# ifdef HAVE_SETAUTHDB + if (setauthdb(old_registry, NULL) == 0) + debug3("%s: restoring old registry '%s'", __func__, + old_registry); + else + debug3("%s: failed to restore old registry %s", __func__, + old_registry); +# endif /* HAVE_SETAUTHDB */ +} + +# endif /* WITH_AIXAUTHENTICATE */ + +# ifdef USE_AIX_KRB_NAME +/* + * aix_krb5_get_principal_name: returns the user's kerberos client principal + * name if configured, otherwise NULL. Caller must free returned string. + */ +char * +aix_krb5_get_principal_name(const char *const_pw_name) +{ + char *pw_name = (char *)const_pw_name; + char *authname = NULL, *authdomain = NULL, *principal = NULL; + + setuserdb(S_READ); + if (getuserattr(pw_name, S_AUTHDOMAIN, &authdomain, SEC_CHAR) != 0) + debug("AIX getuserattr S_AUTHDOMAIN: %s", strerror(errno)); + if (getuserattr(pw_name, S_AUTHNAME, &authname, SEC_CHAR) != 0) + debug("AIX getuserattr S_AUTHNAME: %s", strerror(errno)); + + if (authdomain != NULL) + xasprintf(&principal, "%s@%s", authname ? authname : pw_name, + authdomain); + else if (authname != NULL) + principal = xstrdup(authname); + enduserdb(); + return principal; +} +# endif /* USE_AIX_KRB_NAME */ + +# if defined(AIX_GETNAMEINFO_HACK) && !defined(BROKEN_ADDRINFO) +# undef getnameinfo +/* + * For some reason, AIX's getnameinfo will refuse to resolve the all-zeros + * IPv6 address into its textual representation ("::"), so we wrap it + * with a function that will. + */ +int +sshaix_getnameinfo(const struct sockaddr *sa, size_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in6 *sa6; + u_int32_t *a6; + + if (flags & (NI_NUMERICHOST|NI_NUMERICSERV) && + sa->sa_family == AF_INET6) { + sa6 = (struct sockaddr_in6 *)sa; + a6 = sa6->sin6_addr.u6_addr.u6_addr32; + + if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) { + strlcpy(host, "::", hostlen); + snprintf(serv, servlen, "%d", sa6->sin6_port); + return 0; + } + } + return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); +} +# endif /* AIX_GETNAMEINFO_HACK */ + +# if defined(USE_GETGRSET) +# include +int +getgrouplist(const char *user, gid_t pgid, gid_t *groups, int *grpcnt) +{ + char *cp, *grplist, *grp; + gid_t gid; + int ret = 0, ngroups = 0, maxgroups; + long long ll; + + maxgroups = *grpcnt; + + if ((cp = grplist = getgrset(user)) == NULL) + return -1; + + /* handle zero-length case */ + if (maxgroups <= 0) { + *grpcnt = 0; + return -1; + } + + /* copy primary group */ + groups[ngroups++] = pgid; + + /* copy each entry from getgrset into group list */ + while ((grp = strsep(&grplist, ",")) != NULL) { + ll = strtoll(grp, NULL, 10); + if (ngroups >= maxgroups || ll < 0 || ll > UID_MAX) { + ret = -1; + goto out; + } + gid = (gid_t)ll; + if (gid == pgid) + continue; /* we have already added primary gid */ + groups[ngroups++] = gid; + } +out: + free(cp); + *grpcnt = ngroups; + return ret; +} +# endif /* USE_GETGRSET */ + +#endif /* _AIX */ diff --git a/aosp/external/openssh/openbsd-compat/port-aix.h b/aosp/external/openssh/openbsd-compat/port-aix.h new file mode 100644 index 0000000000000000000000000000000000000000..0ee366140b5b4c017e99316d2ed56d97a94a1a6e --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-aix.h @@ -0,0 +1,127 @@ +/* + * + * Copyright (c) 2001 Gert Doering. All rights reserved. + * Copyright (c) 2004,2005,2006 Darren Tucker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _AIX + +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +struct ssh; +struct sshbuf; + +/* These should be in the system headers but are not. */ +int usrinfo(int, char *, int); +#if defined(HAVE_DECL_SETAUTHDB) && (HAVE_DECL_SETAUTHDB == 0) +int setauthdb(const char *, char *); +#endif +/* these may or may not be in the headers depending on the version */ +#if defined(HAVE_DECL_AUTHENTICATE) && (HAVE_DECL_AUTHENTICATE == 0) +int authenticate(char *, char *, int *, char **); +#endif +#if defined(HAVE_DECL_LOGINFAILED) && (HAVE_DECL_LOGINFAILED == 0) +int loginfailed(char *, char *, char *); +#endif +#if defined(HAVE_DECL_LOGINRESTRICTIONS) && (HAVE_DECL_LOGINRESTRICTIONS == 0) +int loginrestrictions(char *, int, char *, char **); +#endif +#if defined(HAVE_DECL_LOGINSUCCESS) && (HAVE_DECL_LOGINSUCCESS == 0) +int loginsuccess(char *, char *, char *, char **); +#endif +#if defined(HAVE_DECL_PASSWDEXPIRED) && (HAVE_DECL_PASSWDEXPIRED == 0) +int passwdexpired(char *, char **); +#endif + +/* Some versions define r_type in the above headers, which causes a conflict */ +#ifdef r_type +# undef r_type +#endif + +/* AIX 4.2.x doesn't have nanosleep but does have nsleep which is equivalent */ +#if !defined(HAVE_NANOSLEEP) && defined(HAVE_NSLEEP) +# define nanosleep(a,b) nsleep(a,b) +#endif + +/* For struct timespec on AIX 4.2.x */ +#ifdef HAVE_SYS_TIMERS_H +# include +#endif + +/* for setpcred and friends */ +#ifdef HAVE_USERSEC_H +# include +#endif + +/* + * According to the setauthdb man page, AIX password registries must be 15 + * chars or less plus terminating NUL. + */ +#ifdef HAVE_SETAUTHDB +# define REGISTRY_SIZE 16 +#endif + +void aix_usrinfo(struct passwd *); + +#ifdef WITH_AIXAUTHENTICATE +# define CUSTOM_SYS_AUTH_PASSWD 1 +# define CUSTOM_SYS_AUTH_ALLOWED_USER 1 +int sys_auth_allowed_user(struct passwd *, struct sshbuf *); +# define CUSTOM_SYS_AUTH_RECORD_LOGIN 1 +int sys_auth_record_login(const char *, const char *, const char *, + struct sshbuf *); +# define CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG +char *sys_auth_get_lastlogin_msg(const char *, uid_t); +# define CUSTOM_FAILED_LOGIN 1 +# if defined(S_AUTHDOMAIN) && defined (S_AUTHNAME) +# define USE_AIX_KRB_NAME +char *aix_krb5_get_principal_name(const char *); +# endif +#endif + +void aix_setauthdb(const char *); +void aix_restoreauthdb(void); +void aix_remove_embedded_newlines(char *); + +#if defined(AIX_GETNAMEINFO_HACK) && !defined(BROKEN_GETADDRINFO) +# ifdef getnameinfo +# undef getnameinfo +# endif +int sshaix_getnameinfo(const struct sockaddr *, size_t, char *, size_t, + char *, size_t, int); +# define getnameinfo(a,b,c,d,e,f,g) (sshaix_getnameinfo(a,b,c,d,e,f,g)) +#endif + +/* + * We use getgrset in preference to multiple getgrent calls for efficiency + * plus it supports NIS and LDAP groups. + */ +#if !defined(HAVE_GETGROUPLIST) && defined(HAVE_GETGRSET) +# define HAVE_GETGROUPLIST +# define USE_GETGRSET +int getgrouplist(const char *, gid_t, gid_t *, int *); +#endif + +#endif /* _AIX */ diff --git a/aosp/external/openssh/openbsd-compat/port-irix.c b/aosp/external/openssh/openbsd-compat/port-irix.c new file mode 100644 index 0000000000000000000000000000000000000000..aebffb0143b9a9132a17fa60877a29c98c755f39 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-irix.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2000 Denis Parker. All rights reserved. + * Copyright (c) 2000 Michael Stone. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#if defined(WITH_IRIX_PROJECT) || \ + defined(WITH_IRIX_JOBS) || \ + defined(WITH_IRIX_ARRAY) + +#include +#include +#include + +#ifdef WITH_IRIX_PROJECT +# include +#endif /* WITH_IRIX_PROJECT */ +#ifdef WITH_IRIX_JOBS +# include +#endif +#ifdef WITH_IRIX_AUDIT +# include +#endif /* WITH_IRIX_AUDIT */ + +#include "log.h" + +void +irix_setusercontext(struct passwd *pw) +{ +#ifdef WITH_IRIX_PROJECT + prid_t projid; +#endif +#ifdef WITH_IRIX_JOBS + jid_t jid = 0; +#elif defined(WITH_IRIX_ARRAY) + int jid = 0; +#endif + +#ifdef WITH_IRIX_JOBS + jid = jlimit_startjob(pw->pw_name, pw->pw_uid, "interactive"); + if (jid == -1) + fatal("Failed to create job container: %.100s", + strerror(errno)); +#endif /* WITH_IRIX_JOBS */ +#ifdef WITH_IRIX_ARRAY + /* initialize array session */ + if (jid == 0 && newarraysess() != 0) + fatal("Failed to set up new array session: %.100s", + strerror(errno)); +#endif /* WITH_IRIX_ARRAY */ +#ifdef WITH_IRIX_PROJECT + /* initialize irix project info */ + if ((projid = getdfltprojuser(pw->pw_name)) == -1) { + debug("Failed to get project id, using projid 0"); + projid = 0; + } + if (setprid(projid)) + fatal("Failed to initialize project %d for %s: %.100s", + (int)projid, pw->pw_name, strerror(errno)); +#endif /* WITH_IRIX_PROJECT */ +#ifdef WITH_IRIX_AUDIT + if (sysconf(_SC_AUDIT)) { + debug("Setting sat id to %d", (int) pw->pw_uid); + if (satsetid(pw->pw_uid)) + debug("error setting satid: %.100s", strerror(errno)); + } +#endif /* WITH_IRIX_AUDIT */ +} + + +#endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ diff --git a/aosp/external/openssh/openbsd-compat/port-irix.h b/aosp/external/openssh/openbsd-compat/port-irix.h new file mode 100644 index 0000000000000000000000000000000000000000..bc8cc44ac5a49f1826bf29b5c0addd08664629f6 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-irix.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2000 Denis Parker. All rights reserved. + * Copyright (c) 2000 Michael Stone. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PORT_IRIX_H +#define _PORT_IRIX_H + +#if defined(WITH_IRIX_PROJECT) || \ + defined(WITH_IRIX_JOBS) || \ + defined(WITH_IRIX_ARRAY) + +void irix_setusercontext(struct passwd *pw); + +#endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ + +#endif /* ! _PORT_IRIX_H */ diff --git a/aosp/external/openssh/openbsd-compat/port-linux.c b/aosp/external/openssh/openbsd-compat/port-linux.c new file mode 100644 index 0000000000000000000000000000000000000000..77cb8213a12ebb08c294e1f6109219a85acd1e38 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-linux.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2005 Daniel Walsh + * Copyright (c) 2006 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Linux-specific portability code - just SELinux support at present + */ + +#include "includes.h" + +#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) +#include +#include +#include +#include +#include + +#include "log.h" +#include "xmalloc.h" +#include "port-linux.h" + +#ifdef WITH_SELINUX +#include +#include + +#ifndef SSH_SELINUX_UNCONFINED_TYPE +# define SSH_SELINUX_UNCONFINED_TYPE ":unconfined_t:" +#endif + +/* Wrapper around is_selinux_enabled() to log its return value once only */ +int +ssh_selinux_enabled(void) +{ + static int enabled = -1; + + if (enabled == -1) { + enabled = (is_selinux_enabled() == 1); + debug("SELinux support %s", enabled ? "enabled" : "disabled"); + } + + return (enabled); +} + +/* Return the default security context for the given username */ +static char * +ssh_selinux_getctxbyname(char *pwname) +{ + char *sc = NULL, *sename = NULL, *lvl = NULL; + int r; + +#ifdef HAVE_GETSEUSERBYNAME + if (getseuserbyname(pwname, &sename, &lvl) != 0) + return NULL; +#else + sename = pwname; + lvl = NULL; +#endif + +#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL + r = get_default_context_with_level(sename, lvl, NULL, &sc); +#else + r = get_default_context(sename, NULL, &sc); +#endif + + if (r != 0) { + switch (security_getenforce()) { + case -1: + fatal("%s: ssh_selinux_getctxbyname: " + "security_getenforce() failed", __func__); + case 0: + error("%s: Failed to get default SELinux security " + "context for %s", __func__, pwname); + sc = NULL; + break; + default: + fatal("%s: Failed to get default SELinux security " + "context for %s (in enforcing mode)", + __func__, pwname); + } + } + +#ifdef HAVE_GETSEUSERBYNAME + free(sename); + free(lvl); +#endif + + return sc; +} + +/* Set the execution context to the default for the specified user */ +void +ssh_selinux_setup_exec_context(char *pwname) +{ + char *user_ctx = NULL; + + if (!ssh_selinux_enabled()) + return; + + debug3("%s: setting execution context", __func__); + + user_ctx = ssh_selinux_getctxbyname(pwname); + if (setexeccon(user_ctx) != 0) { + switch (security_getenforce()) { + case -1: + fatal("%s: security_getenforce() failed", __func__); + case 0: + error("%s: Failed to set SELinux execution " + "context for %s", __func__, pwname); + break; + default: + fatal("%s: Failed to set SELinux execution context " + "for %s (in enforcing mode)", __func__, pwname); + } + } + if (user_ctx != NULL) + freecon(user_ctx); + + debug3("%s: done", __func__); +} + +/* Set the TTY context for the specified user */ +void +ssh_selinux_setup_pty(char *pwname, const char *tty) +{ + char *new_tty_ctx = NULL, *user_ctx = NULL, *old_tty_ctx = NULL; + security_class_t chrclass; + + if (!ssh_selinux_enabled()) + return; + + debug3("%s: setting TTY context on %s", __func__, tty); + + user_ctx = ssh_selinux_getctxbyname(pwname); + + /* XXX: should these calls fatal() upon failure in enforcing mode? */ + + if (getfilecon(tty, &old_tty_ctx) == -1) { + error("%s: getfilecon: %s", __func__, strerror(errno)); + goto out; + } + if ((chrclass = string_to_security_class("chr_file")) == 0) { + error("%s: couldn't get security class for chr_file", __func__); + goto out; + } + if (security_compute_relabel(user_ctx, old_tty_ctx, + chrclass, &new_tty_ctx) != 0) { + error("%s: security_compute_relabel: %s", + __func__, strerror(errno)); + goto out; + } + + if (setfilecon(tty, new_tty_ctx) != 0) + error("%s: setfilecon: %s", __func__, strerror(errno)); + out: + if (new_tty_ctx != NULL) + freecon(new_tty_ctx); + if (old_tty_ctx != NULL) + freecon(old_tty_ctx); + if (user_ctx != NULL) + freecon(user_ctx); + debug3("%s: done", __func__); +} + +void +ssh_selinux_change_context(const char *newname) +{ + int len, newlen; + char *oldctx, *newctx, *cx; + LogLevel log_level = SYSLOG_LEVEL_INFO; + + if (!ssh_selinux_enabled()) + return; + + if (getcon(&oldctx) < 0) { + logit("%s: getcon failed with %s", __func__, strerror(errno)); + return; + } + if ((cx = index(oldctx, ':')) == NULL || (cx = index(cx + 1, ':')) == + NULL) { + logit("%s: unparsable context %s", __func__, oldctx); + return; + } + + /* + * Check whether we are attempting to switch away from an unconfined + * security context. + */ + if (strncmp(cx, SSH_SELINUX_UNCONFINED_TYPE, + sizeof(SSH_SELINUX_UNCONFINED_TYPE) - 1) == 0) + log_level = SYSLOG_LEVEL_DEBUG3; + + newlen = strlen(oldctx) + strlen(newname) + 1; + newctx = xmalloc(newlen); + len = cx - oldctx + 1; + memcpy(newctx, oldctx, len); + strlcpy(newctx + len, newname, newlen - len); + if ((cx = index(cx + 1, ':'))) + strlcat(newctx, cx, newlen); + debug3("%s: setting context from '%s' to '%s'", __func__, + oldctx, newctx); + if (setcon(newctx) < 0) + do_log2(log_level, "%s: setcon %s from %s failed with %s", + __func__, newctx, oldctx, strerror(errno)); + free(oldctx); + free(newctx); +} + +void +ssh_selinux_setfscreatecon(const char *path) +{ + char *context; + + if (!ssh_selinux_enabled()) + return; + if (path == NULL) { + setfscreatecon(NULL); + return; + } + if (matchpathcon(path, 0700, &context) == 0) + setfscreatecon(context); +} + +#endif /* WITH_SELINUX */ + +#ifdef LINUX_OOM_ADJUST +/* + * The magic "don't kill me" values, old and new, as documented in eg: + * http://lxr.linux.no/#linux+v2.6.32/Documentation/filesystems/proc.txt + * http://lxr.linux.no/#linux+v2.6.36/Documentation/filesystems/proc.txt + */ + +static int oom_adj_save = INT_MIN; +static char *oom_adj_path = NULL; +struct { + char *path; + int value; +} oom_adjust[] = { + {"/proc/self/oom_score_adj", -1000}, /* kernels >= 2.6.36 */ + {"/proc/self/oom_adj", -17}, /* kernels <= 2.6.35 */ + {NULL, 0}, +}; + +/* + * Tell the kernel's out-of-memory killer to avoid sshd. + * Returns the previous oom_adj value or zero. + */ +void +oom_adjust_setup(void) +{ + int i, value; + FILE *fp; + + debug3("%s", __func__); + for (i = 0; oom_adjust[i].path != NULL; i++) { + oom_adj_path = oom_adjust[i].path; + value = oom_adjust[i].value; + if ((fp = fopen(oom_adj_path, "r+")) != NULL) { + if (fscanf(fp, "%d", &oom_adj_save) != 1) + verbose("error reading %s: %s", oom_adj_path, + strerror(errno)); + else { + rewind(fp); + if (fprintf(fp, "%d\n", value) <= 0) + verbose("error writing %s: %s", + oom_adj_path, strerror(errno)); + else + debug("Set %s from %d to %d", + oom_adj_path, oom_adj_save, value); + } + fclose(fp); + return; + } + } + oom_adj_path = NULL; +} + +/* Restore the saved OOM adjustment */ +void +oom_adjust_restore(void) +{ + FILE *fp; + + debug3("%s", __func__); + if (oom_adj_save == INT_MIN || oom_adj_path == NULL || + (fp = fopen(oom_adj_path, "w")) == NULL) + return; + + if (fprintf(fp, "%d\n", oom_adj_save) <= 0) + verbose("error writing %s: %s", oom_adj_path, strerror(errno)); + else + debug("Set %s to %d", oom_adj_path, oom_adj_save); + + fclose(fp); + return; +} +#endif /* LINUX_OOM_ADJUST */ +#endif /* WITH_SELINUX || LINUX_OOM_ADJUST */ diff --git a/aosp/external/openssh/openbsd-compat/port-linux.h b/aosp/external/openssh/openbsd-compat/port-linux.h new file mode 100644 index 0000000000000000000000000000000000000000..3c22a854dbce4770d3f465cb9498164275a6cfc2 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-linux.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2006 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PORT_LINUX_H +#define _PORT_LINUX_H + +#ifdef WITH_SELINUX +int ssh_selinux_enabled(void); +void ssh_selinux_setup_pty(char *, const char *); +void ssh_selinux_setup_exec_context(char *); +void ssh_selinux_change_context(const char *); +void ssh_selinux_setfscreatecon(const char *); +#endif + +#ifdef LINUX_OOM_ADJUST +void oom_adjust_restore(void); +void oom_adjust_setup(void); +#endif + +#endif /* ! _PORT_LINUX_H */ diff --git a/aosp/external/openssh/openbsd-compat/port-net.c b/aosp/external/openssh/openbsd-compat/port-net.c new file mode 100644 index 0000000000000000000000000000000000000000..198e73f0de2067131d367247fe235071fc5461a1 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-net.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2005 Reyk Floeter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "log.h" +#include "misc.h" +#include "sshbuf.h" +#include "channels.h" +#include "ssherr.h" + +/* + * This file contains various portability code for network support, + * including tun/tap forwarding and routing domains. + */ + +#if defined(SYS_RDOMAIN_LINUX) || defined(SSH_TUN_LINUX) +#include +#endif + +#if defined(SYS_RDOMAIN_LINUX) +char * +sys_get_rdomain(int fd) +{ + char dev[IFNAMSIZ + 1]; + socklen_t len = sizeof(dev) - 1; + + if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, dev, &len) == -1) { + error("%s: cannot determine VRF for fd=%d : %s", + __func__, fd, strerror(errno)); + return NULL; + } + dev[len] = '\0'; + return strdup(dev); +} + +int +sys_set_rdomain(int fd, const char *name) +{ + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, + name, strlen(name)) == -1) { + error("%s: setsockopt(%d, SO_BINDTODEVICE, %s): %s", + __func__, fd, name, strerror(errno)); + return -1; + } + return 0; +} + +int +sys_valid_rdomain(const char *name) +{ + int fd; + + /* + * This is a pretty crappy way to test. It would be better to + * check whether "name" represents a VRF device, but apparently + * that requires an rtnetlink transaction. + */ + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) + return 0; + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, + name, strlen(name)) == -1) { + close(fd); + return 0; + } + close(fd); + return 1; +} +#elif defined(SYS_RDOMAIN_XXX) +/* XXX examples */ +char * +sys_get_rdomain(int fd) +{ + return NULL; +} + +int +sys_set_rdomain(int fd, const char *name) +{ + return -1; +} + +int +valid_rdomain(const char *name) +{ + return 0; +} + +void +sys_set_process_rdomain(const char *name) +{ + fatal("%s: not supported", __func__); +} +#endif /* defined(SYS_RDOMAIN_XXX) */ + +/* + * This is the portable version of the SSH tunnel forwarding, it + * uses some preprocessor definitions for various platform-specific + * settings. + * + * SSH_TUN_LINUX Use the (newer) Linux tun/tap device + * SSH_TUN_FREEBSD Use the FreeBSD tun/tap device + * SSH_TUN_COMPAT_AF Translate the OpenBSD address family + * SSH_TUN_PREPEND_AF Prepend/remove the address family + */ + +/* + * System-specific tunnel open function + */ + +#if defined(SSH_TUN_LINUX) +#include +#define TUN_CTRL_DEV "/dev/net/tun" + +int +sys_tun_open(int tun, int mode, char **ifname) +{ + struct ifreq ifr; + int fd = -1; + const char *name = NULL; + + if (ifname != NULL) + *ifname = NULL; + if ((fd = open(TUN_CTRL_DEV, O_RDWR)) == -1) { + debug("%s: failed to open tunnel control device \"%s\": %s", + __func__, TUN_CTRL_DEV, strerror(errno)); + return (-1); + } + + bzero(&ifr, sizeof(ifr)); + + if (mode == SSH_TUNMODE_ETHERNET) { + ifr.ifr_flags = IFF_TAP; + name = "tap%d"; + } else { + ifr.ifr_flags = IFF_TUN; + name = "tun%d"; + } + ifr.ifr_flags |= IFF_NO_PI; + + if (tun != SSH_TUNID_ANY) { + if (tun > SSH_TUNID_MAX) { + debug("%s: invalid tunnel id %x: %s", __func__, + tun, strerror(errno)); + goto failed; + } + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun); + } + + if (ioctl(fd, TUNSETIFF, &ifr) == -1) { + debug("%s: failed to configure tunnel (mode %d): %s", __func__, + mode, strerror(errno)); + goto failed; + } + + if (tun == SSH_TUNID_ANY) + debug("%s: tunnel mode %d fd %d", __func__, mode, fd); + else + debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd); + + if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL) + goto failed; + + return (fd); + + failed: + close(fd); + return (-1); +} +#endif /* SSH_TUN_LINUX */ + +#ifdef SSH_TUN_FREEBSD +#include +#include + +#ifdef HAVE_NET_IF_TUN_H +#include +#endif + +int +sys_tun_open(int tun, int mode, char **ifname) +{ + struct ifreq ifr; + char name[100]; + int fd = -1, sock; + const char *tunbase = "tun"; +#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF) + int flag; +#endif + + if (ifname != NULL) + *ifname = NULL; + + if (mode == SSH_TUNMODE_ETHERNET) { +#ifdef SSH_TUN_NO_L2 + debug("%s: no layer 2 tunnelling support", __func__); + return (-1); +#else + tunbase = "tap"; +#endif + } + + /* Open the tunnel device */ + if (tun <= SSH_TUNID_MAX) { + snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); + fd = open(name, O_RDWR); + } else if (tun == SSH_TUNID_ANY) { + for (tun = 100; tun >= 0; tun--) { + snprintf(name, sizeof(name), "/dev/%s%d", + tunbase, tun); + if ((fd = open(name, O_RDWR)) >= 0) + break; + } + } else { + debug("%s: invalid tunnel %u\n", __func__, tun); + return (-1); + } + + if (fd < 0) { + debug("%s: %s open failed: %s", __func__, name, + strerror(errno)); + return (-1); + } + + /* Turn on tunnel headers */ +#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF) + flag = 1; + if (mode != SSH_TUNMODE_ETHERNET && + ioctl(fd, TUNSIFHEAD, &flag) == -1) { + debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd, + strerror(errno)); + close(fd); + } +#endif + + debug("%s: %s mode %d fd %d", __func__, name, mode, fd); + + /* Set the tunnel device operation mode */ + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); + if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + goto failed; + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) + goto failed; + if ((ifr.ifr_flags & IFF_UP) == 0) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) + goto failed; + } + + if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL) + goto failed; + + close(sock); + return (fd); + + failed: + if (fd >= 0) + close(fd); + if (sock >= 0) + close(sock); + debug("%s: failed to set %s mode %d: %s", __func__, name, + mode, strerror(errno)); + return (-1); +} +#endif /* SSH_TUN_FREEBSD */ + +/* + * System-specific channel filters + */ + +#if defined(SSH_TUN_FILTER) +/* + * The tunnel forwarding protocol prepends the address family of forwarded + * IP packets using OpenBSD's numbers. + */ +#define OPENBSD_AF_INET 2 +#define OPENBSD_AF_INET6 24 + +int +sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len) +{ + int r; + size_t len; + char *ptr = buf; +#if defined(SSH_TUN_PREPEND_AF) + char rbuf[CHAN_RBUF]; + struct ip iph; +#endif +#if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF) + u_int32_t af; +#endif + + /* XXX update channel input filter API to use unsigned length */ + if (_len < 0) + return -1; + len = _len; + +#if defined(SSH_TUN_PREPEND_AF) + if (len <= sizeof(iph) || len > sizeof(rbuf) - 4) + return -1; + /* Determine address family from packet IP header. */ + memcpy(&iph, buf, sizeof(iph)); + af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET; + /* Prepend address family to packet using OpenBSD constants */ + memcpy(rbuf + 4, buf, len); + len += 4; + POKE_U32(rbuf, af); + ptr = rbuf; +#elif defined(SSH_TUN_COMPAT_AF) + /* Convert existing address family header to OpenBSD value */ + if (len <= 4) + return -1; + af = PEEK_U32(buf); + /* Put it back */ + POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET); +#endif + + if ((r = sshbuf_put_string(c->input, ptr, len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + return (0); +} + +u_char * +sys_tun_outfilter(struct ssh *ssh, struct Channel *c, + u_char **data, size_t *dlen) +{ + u_char *buf; + u_int32_t af; + int r; + + /* XXX new API is incompatible with this signature. */ + if ((r = sshbuf_get_string(c->output, data, dlen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (*dlen < sizeof(af)) + return (NULL); + buf = *data; + +#if defined(SSH_TUN_PREPEND_AF) + /* skip address family */ + *dlen -= sizeof(af); + buf = *data + sizeof(af); +#elif defined(SSH_TUN_COMPAT_AF) + /* translate address family */ + af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET; + POKE_U32(buf, af); +#endif + return (buf); +} +#endif /* SSH_TUN_FILTER */ diff --git a/aosp/external/openssh/openbsd-compat/port-net.h b/aosp/external/openssh/openbsd-compat/port-net.h new file mode 100644 index 0000000000000000000000000000000000000000..3a0d1104bf6d091aee9d5f7ab45f9126e4e5ece3 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-net.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Reyk Floeter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PORT_TUN_H +#define _PORT_TUN_H + +struct Channel; +struct ssh; + +#if defined(SSH_TUN_LINUX) || defined(SSH_TUN_FREEBSD) +# define CUSTOM_SYS_TUN_OPEN +int sys_tun_open(int, int, char **); +#endif + +#if defined(SSH_TUN_COMPAT_AF) || defined(SSH_TUN_PREPEND_AF) +# define SSH_TUN_FILTER +int sys_tun_infilter(struct ssh *, struct Channel *, char *, int); +u_char *sys_tun_outfilter(struct ssh *, struct Channel *, u_char **, size_t *); +#endif + +#if defined(SYS_RDOMAIN_LINUX) +# define HAVE_SYS_GET_RDOMAIN +# define HAVE_SYS_SET_RDOMAIN +# define HAVE_SYS_VALID_RDOMAIN +char *sys_get_rdomain(int fd); +int sys_set_rdomain(int fd, const char *name); +int sys_valid_rdomain(const char *name); +#endif + +#if defined(SYS_RDOMAIN_XXX) +# define HAVE_SYS_SET_PROCESS_RDOMAIN +void sys_set_process_rdomain(const char *name); +#endif + +#endif diff --git a/aosp/external/openssh/openbsd-compat/port-prngd.c b/aosp/external/openssh/openbsd-compat/port-prngd.c new file mode 100644 index 0000000000000000000000000000000000000000..6afa8f913ae30acfe439079a2317843f88be66e3 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-prngd.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_SYS_UN_H +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include /* for offsetof */ + +#include "atomicio.h" +#include "misc.h" +#include "log.h" + +#if defined(PRNGD_PORT) || defined(PRNGD_SOCKET) +/* + * EGD/PRNGD interface. + * + * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon + * listening either on 'tcp_port', or via Unix domain socket at * + * 'socket_path'. + * Either a non-zero tcp_port or a non-null socket_path must be + * supplied. + * Returns 0 on success, -1 on error + */ +static int +get_random_bytes_prngd(unsigned char *buf, int len, + unsigned short tcp_port, char *socket_path) +{ + int fd, addr_len, rval, errors; + u_char msg[2]; + struct sockaddr_storage addr; + struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; + struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr; + sshsig_t old_sigpipe; + + /* Sanity checks */ + if (socket_path == NULL && tcp_port == 0) + fatal("You must specify a port or a socket"); + if (socket_path != NULL && + strlen(socket_path) >= sizeof(addr_un->sun_path)) + fatal("Random pool path is too long"); + if (len <= 0 || len > 255) + fatal("Too many bytes (%d) to read from PRNGD", len); + + memset(&addr, '\0', sizeof(addr)); + + if (tcp_port != 0) { + addr_in->sin_family = AF_INET; + addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr_in->sin_port = htons(tcp_port); + addr_len = sizeof(*addr_in); + } else { + addr_un->sun_family = AF_UNIX; + strlcpy(addr_un->sun_path, socket_path, + sizeof(addr_un->sun_path)); + addr_len = offsetof(struct sockaddr_un, sun_path) + + strlen(socket_path) + 1; + } + + old_sigpipe = ssh_signal(SIGPIPE, SIG_IGN); + + errors = 0; + rval = -1; +reopen: + fd = socket(addr.ss_family, SOCK_STREAM, 0); + if (fd == -1) { + error("Couldn't create socket: %s", strerror(errno)); + goto done; + } + + if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) { + if (tcp_port != 0) { + error("Couldn't connect to PRNGD port %d: %s", + tcp_port, strerror(errno)); + } else { + error("Couldn't connect to PRNGD socket \"%s\": %s", + addr_un->sun_path, strerror(errno)); + } + goto done; + } + + /* Send blocking read request to PRNGD */ + msg[0] = 0x02; + msg[1] = len; + + if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) { + if (errno == EPIPE && errors < 10) { + close(fd); + errors++; + goto reopen; + } + error("Couldn't write to PRNGD socket: %s", + strerror(errno)); + goto done; + } + + if (atomicio(read, fd, buf, len) != (size_t)len) { + if (errno == EPIPE && errors < 10) { + close(fd); + errors++; + goto reopen; + } + error("Couldn't read from PRNGD socket: %s", + strerror(errno)); + goto done; + } + + rval = 0; +done: + ssh_signal(SIGPIPE, old_sigpipe); + if (fd != -1) + close(fd); + return rval; +} +#endif /* PRNGD_PORT || PRNGD_SOCKET */ + +int +seed_from_prngd(unsigned char *buf, size_t bytes) +{ +#ifdef PRNGD_PORT + debug("trying egd/prngd port %d", PRNGD_PORT); + if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0) + return 0; +#endif +#ifdef PRNGD_SOCKET + debug("trying egd/prngd socket %s", PRNGD_SOCKET); + if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0) + return 0; +#endif + return -1; +} diff --git a/aosp/external/openssh/openbsd-compat/port-solaris.c b/aosp/external/openssh/openbsd-compat/port-solaris.c new file mode 100644 index 0000000000000000000000000000000000000000..10c2d6b7fa5aeb228788164f5f3dc5f551860b37 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-solaris.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2006 Chad Mynhier. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" +#include "includes.h" + +#include +#include + +#include +#ifdef HAVE_FCNTL_H +# include +#endif +#include +#include +#include + +#include "log.h" + +#ifdef USE_SOLARIS_PROCESS_CONTRACTS + +#include +#include +#include + +#define CT_TEMPLATE CTFS_ROOT "/process/template" +#define CT_LATEST CTFS_ROOT "/process/latest" + +static int tmpl_fd = -1; + +/* Lookup the latest process contract */ +static ctid_t +get_active_process_contract_id(void) +{ + int stat_fd; + ctid_t ctid = -1; + ct_stathdl_t stathdl; + + if ((stat_fd = open64(CT_LATEST, O_RDONLY)) == -1) { + error("%s: Error opening 'latest' process " + "contract: %s", __func__, strerror(errno)); + return -1; + } + if (ct_status_read(stat_fd, CTD_COMMON, &stathdl) != 0) { + error("%s: Error reading process contract " + "status: %s", __func__, strerror(errno)); + goto out; + } + if ((ctid = ct_status_get_id(stathdl)) < 0) { + error("%s: Error getting process contract id: %s", + __func__, strerror(errno)); + goto out; + } + + ct_status_free(stathdl); + out: + close(stat_fd); + return ctid; +} + +void +solaris_contract_pre_fork(void) +{ + if ((tmpl_fd = open64(CT_TEMPLATE, O_RDWR)) == -1) { + error("%s: open %s: %s", __func__, + CT_TEMPLATE, strerror(errno)); + return; + } + + debug2("%s: setting up process contract template on fd %d", + __func__, tmpl_fd); + + /* First we set the template parameters and event sets. */ + if (ct_pr_tmpl_set_param(tmpl_fd, CT_PR_PGRPONLY) != 0) { + error("%s: Error setting process contract parameter set " + "(pgrponly): %s", __func__, strerror(errno)); + goto fail; + } + if (ct_pr_tmpl_set_fatal(tmpl_fd, CT_PR_EV_HWERR) != 0) { + error("%s: Error setting process contract template " + "fatal events: %s", __func__, strerror(errno)); + goto fail; + } + if (ct_tmpl_set_critical(tmpl_fd, 0) != 0) { + error("%s: Error setting process contract template " + "critical events: %s", __func__, strerror(errno)); + goto fail; + } + if (ct_tmpl_set_informative(tmpl_fd, CT_PR_EV_HWERR) != 0) { + error("%s: Error setting process contract template " + "informative events: %s", __func__, strerror(errno)); + goto fail; + } + + /* Now make this the active template for this process. */ + if (ct_tmpl_activate(tmpl_fd) != 0) { + error("%s: Error activating process contract " + "template: %s", __func__, strerror(errno)); + goto fail; + } + return; + + fail: + if (tmpl_fd != -1) { + close(tmpl_fd); + tmpl_fd = -1; + } +} + +void +solaris_contract_post_fork_child() +{ + debug2("%s: clearing process contract template on fd %d", + __func__, tmpl_fd); + + /* Clear the active template. */ + if (ct_tmpl_clear(tmpl_fd) != 0) + error("%s: Error clearing active process contract " + "template: %s", __func__, strerror(errno)); + + close(tmpl_fd); + tmpl_fd = -1; +} + +void +solaris_contract_post_fork_parent(pid_t pid) +{ + ctid_t ctid; + char ctl_path[256]; + int r, ctl_fd = -1, stat_fd = -1; + + debug2("%s: clearing template (fd %d)", __func__, tmpl_fd); + + if (tmpl_fd == -1) + return; + + /* First clear the active template. */ + if ((r = ct_tmpl_clear(tmpl_fd)) != 0) + error("%s: Error clearing active process contract " + "template: %s", __func__, strerror(errno)); + + close(tmpl_fd); + tmpl_fd = -1; + + /* + * If either the fork didn't succeed (pid < 0), or clearing + * th active contract failed (r != 0), then we have nothing + * more do. + */ + if (r != 0 || pid <= 0) + return; + + /* Now lookup and abandon the contract we've created. */ + ctid = get_active_process_contract_id(); + + debug2("%s: abandoning contract id %ld", __func__, ctid); + + snprintf(ctl_path, sizeof(ctl_path), + CTFS_ROOT "/process/%ld/ctl", ctid); + if ((ctl_fd = open64(ctl_path, O_WRONLY)) < 0) { + error("%s: Error opening process contract " + "ctl file: %s", __func__, strerror(errno)); + goto fail; + } + if (ct_ctl_abandon(ctl_fd) < 0) { + error("%s: Error abandoning process contract: %s", + __func__, strerror(errno)); + goto fail; + } + close(ctl_fd); + return; + + fail: + if (tmpl_fd != -1) { + close(tmpl_fd); + tmpl_fd = -1; + } + if (stat_fd != -1) + close(stat_fd); + if (ctl_fd != -1) + close(ctl_fd); +} +#endif + +#ifdef USE_SOLARIS_PROJECTS +#include +#include + +/* + * Get/set solaris default project. + * If we fail, just run along gracefully. + */ +void +solaris_set_default_project(struct passwd *pw) +{ + struct project *defaultproject; + struct project tempproject; + char buf[1024]; + + /* get default project, if we fail just return gracefully */ + if ((defaultproject = getdefaultproj(pw->pw_name, &tempproject, &buf, + sizeof(buf))) != NULL) { + /* set default project */ + if (setproject(defaultproject->pj_name, pw->pw_name, + TASK_NORMAL) != 0) + debug("setproject(%s): %s", defaultproject->pj_name, + strerror(errno)); + } else { + /* debug on getdefaultproj() error */ + debug("getdefaultproj(%s): %s", pw->pw_name, strerror(errno)); + } +} +#endif /* USE_SOLARIS_PROJECTS */ + +#ifdef USE_SOLARIS_PRIVS +# ifdef HAVE_PRIV_H +# include +# endif + +priv_set_t * +solaris_basic_privset(void) +{ + priv_set_t *pset; + +#ifdef HAVE_PRIV_BASICSET + if ((pset = priv_allocset()) == NULL) { + error("priv_allocset: %s", strerror(errno)); + return NULL; + } + priv_basicset(pset); +#else + if ((pset = priv_str_to_set("basic", ",", NULL)) == NULL) { + error("priv_str_to_set: %s", strerror(errno)); + return NULL; + } +#endif + return pset; +} + +void +solaris_drop_privs_pinfo_net_fork_exec(void) +{ + priv_set_t *pset = NULL, *npset = NULL; + + /* + * Note: this variant avoids dropping DAC filesystem rights, in case + * the process calling it is running as root and should have the + * ability to read/write/chown any file on the system. + * + * We start with the basic set, then *add* the DAC rights to it while + * taking away other parts of BASIC we don't need. Then we intersect + * this with our existing PERMITTED set. In this way we keep any + * DAC rights we had before, while otherwise reducing ourselves to + * the minimum set of privileges we need to proceed. + * + * This also means we drop any other parts of "root" that we don't + * need (e.g. the ability to kill any process, create new device nodes + * etc etc). + */ + + if ((pset = priv_allocset()) == NULL) + fatal("priv_allocset: %s", strerror(errno)); + if ((npset = solaris_basic_privset()) == NULL) + fatal("solaris_basic_privset: %s", strerror(errno)); + + if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 || + priv_addset(npset, PRIV_FILE_DAC_READ) != 0 || + priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 || + priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 || + priv_addset(npset, PRIV_FILE_OWNER) != 0) + fatal("priv_addset: %s", strerror(errno)); + + if (priv_delset(npset, PRIV_PROC_EXEC) != 0 || +#ifdef PRIV_NET_ACCESS + priv_delset(npset, PRIV_NET_ACCESS) != 0 || +#endif + priv_delset(npset, PRIV_PROC_FORK) != 0 || + priv_delset(npset, PRIV_PROC_INFO) != 0 || + priv_delset(npset, PRIV_PROC_SESSION) != 0) + fatal("priv_delset: %s", strerror(errno)); + + if (getppriv(PRIV_PERMITTED, pset) != 0) + fatal("getppriv: %s", strerror(errno)); + + priv_intersect(pset, npset); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0) + fatal("setppriv: %s", strerror(errno)); + + priv_freeset(pset); + priv_freeset(npset); +} + +void +solaris_drop_privs_root_pinfo_net(void) +{ + priv_set_t *pset = NULL; + + /* Start with "basic" and drop everything we don't need. */ + if ((pset = solaris_basic_privset()) == NULL) + fatal("solaris_basic_privset: %s", strerror(errno)); + + if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS + priv_delset(pset, PRIV_NET_ACCESS) != 0 || +#endif + priv_delset(pset, PRIV_PROC_INFO) != 0 || + priv_delset(pset, PRIV_PROC_SESSION) != 0) + fatal("priv_delset: %s", strerror(errno)); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0) + fatal("setppriv: %s", strerror(errno)); + + priv_freeset(pset); +} + +void +solaris_drop_privs_root_pinfo_net_exec(void) +{ + priv_set_t *pset = NULL; + + + /* Start with "basic" and drop everything we don't need. */ + if ((pset = solaris_basic_privset()) == NULL) + fatal("solaris_basic_privset: %s", strerror(errno)); + + if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS + priv_delset(pset, PRIV_NET_ACCESS) != 0 || +#endif + priv_delset(pset, PRIV_PROC_EXEC) != 0 || + priv_delset(pset, PRIV_PROC_INFO) != 0) + fatal("priv_delset: %s", strerror(errno)); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0) + fatal("setppriv: %s", strerror(errno)); + + priv_freeset(pset); +} + +#endif diff --git a/aosp/external/openssh/openbsd-compat/port-solaris.h b/aosp/external/openssh/openbsd-compat/port-solaris.h new file mode 100644 index 0000000000000000000000000000000000000000..dde1a5b8bd378375a3bbcc44607cc7eebb2ca00a --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-solaris.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 Chad Mynhier. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PORT_SOLARIS_H + +#include + +#include + +void solaris_contract_pre_fork(void); +void solaris_contract_post_fork_child(void); +void solaris_contract_post_fork_parent(pid_t pid); +void solaris_set_default_project(struct passwd *); +# ifdef USE_SOLARIS_PRIVS +#include +priv_set_t *solaris_basic_privset(void); +void solaris_drop_privs_pinfo_net_fork_exec(void); +void solaris_drop_privs_root_pinfo_net(void); +void solaris_drop_privs_root_pinfo_net_exec(void); +# endif /* USE_SOLARIS_PRIVS */ + +#endif diff --git a/aosp/external/openssh/openbsd-compat/port-uw.c b/aosp/external/openssh/openbsd-compat/port-uw.c new file mode 100644 index 0000000000000000000000000000000000000000..074f80c8d3d55891a00c6ddbb0a2f7866d58c16c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-uw.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2005 The SCO Group. All rights reserved. + * Copyright (c) 2005 Tim Rice. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#if defined(HAVE_LIBIAF) && !defined(HAVE_SECUREWARE) +#include +#ifdef HAVE_CRYPT_H +# include +#endif +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "packet.h" +#include "auth-options.h" +#include "log.h" +#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ +#include "servconf.h" +#include "hostfile.h" +#include "auth.h" +#include "ssh.h" +#include "ssh_api.h" + +int nischeck(char *); + +int +sys_auth_passwd(struct ssh *ssh, const char *password) +{ + Authctxt *authctxt = ssh->authctxt; + struct passwd *pw = authctxt->pw; + char *salt; + int result; + + /* Just use the supplied fake password if authctxt is invalid */ + char *pw_password = authctxt->valid ? shadow_pw(pw) : pw->pw_passwd; + + if (pw_password == NULL) + return 0; + + /* Check for users with no password. */ + if (strcmp(pw_password, "") == 0 && strcmp(password, "") == 0) + return (1); + + /* Encrypt the candidate password using the proper salt. */ + salt = (pw_password[0] && pw_password[1]) ? pw_password : "xx"; + + /* + * Authentication is accepted if the encrypted passwords + * are identical. + */ +#ifdef UNIXWARE_LONG_PASSWORDS + if (!nischeck(pw->pw_name)) { + result = ((strcmp(bigcrypt(password, salt), pw_password) == 0) + || (strcmp(osr5bigcrypt(password, salt), pw_password) == 0)); + } + else +#endif /* UNIXWARE_LONG_PASSWORDS */ + result = (strcmp(xcrypt(password, salt), pw_password) == 0); + +#ifdef USE_LIBIAF + if (authctxt->valid) + free(pw_password); +#endif + return(result); +} + +#ifdef UNIXWARE_LONG_PASSWORDS +int +nischeck(char *namep) +{ + char password_file[] = "/etc/passwd"; + FILE *fd; + struct passwd *ent = NULL; + + if ((fd = fopen (password_file, "r")) == NULL) { + /* + * If the passwd file has disappeared we are in a bad state. + * However, returning 0 will send us back through the + * authentication scheme that has checked the ia database for + * passwords earlier. + */ + return(0); + } + + /* + * fgetpwent() only reads from password file, so we know for certain + * that the user is local. + */ + while (ent = fgetpwent(fd)) { + if (strcmp (ent->pw_name, namep) == 0) { + /* Local user */ + fclose (fd); + return(0); + } + } + + fclose (fd); + return (1); +} + +#endif /* UNIXWARE_LONG_PASSWORDS */ + +/* + NOTE: ia_get_logpwd() allocates memory for arg 2 + functions that call shadow_pw() will need to free + */ + +#ifdef USE_LIBIAF +char * +get_iaf_password(struct passwd *pw) +{ + char *pw_password = NULL; + + uinfo_t uinfo; + if (!ia_openinfo(pw->pw_name,&uinfo)) { + ia_get_logpwd(uinfo, &pw_password); + if (pw_password == NULL) + fatal("ia_get_logpwd: Unable to get the shadow passwd"); + ia_closeinfo(uinfo); + return pw_password; + } + else + fatal("ia_openinfo: Unable to open the shadow passwd file"); +} +#endif /* USE_LIBIAF */ +#endif /* HAVE_LIBIAF and not HAVE_SECUREWARE */ + diff --git a/aosp/external/openssh/openbsd-compat/port-uw.h b/aosp/external/openssh/openbsd-compat/port-uw.h new file mode 100644 index 0000000000000000000000000000000000000000..263d8b5a754b329def0f03eceee88f1f7190f097 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/port-uw.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Tim Rice. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef USE_LIBIAF +char * get_iaf_password(struct passwd *pw); +#endif + diff --git a/aosp/external/openssh/openbsd-compat/pwcache.c b/aosp/external/openssh/openbsd-compat/pwcache.c new file mode 100644 index 0000000000000000000000000000000000000000..826c2378ba258072269995549e3c9ab98ee2f332 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/pwcache.c @@ -0,0 +1,114 @@ +/* $OpenBSD: pwcache.c,v 1.9 2005/08/08 08:05:34 espie Exp $ */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/pwcache.c */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include + +#define NCACHE 64 /* power of 2 */ +#define MASK (NCACHE - 1) /* bits to store with */ + +#ifndef HAVE_USER_FROM_UID +char * +user_from_uid(uid_t uid, int nouser) +{ + static struct ncache { + uid_t uid; + char *name; + } c_uid[NCACHE]; + static int pwopen; + static char nbuf[15]; /* 32 bits == 10 digits */ + struct passwd *pw; + struct ncache *cp; + + cp = c_uid + (uid & MASK); + if (cp->uid != uid || cp->name == NULL) { + if (pwopen == 0) { +#ifdef HAVE_SETPASSENT + setpassent(1); +#endif + pwopen = 1; + } + if ((pw = getpwuid(uid)) == NULL) { + if (nouser) + return (NULL); + (void)snprintf(nbuf, sizeof(nbuf), "%lu", (u_long)uid); + } + cp->uid = uid; + if (cp->name != NULL) + free(cp->name); + cp->name = strdup(pw ? pw->pw_name : nbuf); + } + return (cp->name); +} +#endif + +#ifndef HAVE_GROUP_FROM_GID +char * +group_from_gid(gid_t gid, int nogroup) +{ + static struct ncache { + gid_t gid; + char *name; + } c_gid[NCACHE]; + static int gropen; + static char nbuf[15]; /* 32 bits == 10 digits */ + struct group *gr; + struct ncache *cp; + + cp = c_gid + (gid & MASK); + if (cp->gid != gid || cp->name == NULL) { + if (gropen == 0) { +#ifdef HAVE_SETGROUPENT + setgroupent(1); +#endif + gropen = 1; + } + if ((gr = getgrgid(gid)) == NULL) { + if (nogroup) + return (NULL); + (void)snprintf(nbuf, sizeof(nbuf), "%lu", (u_long)gid); + } + cp->gid = gid; + if (cp->name != NULL) + free(cp->name); + cp->name = strdup(gr ? gr->gr_name : nbuf); + } + return (cp->name); +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/readpassphrase.c b/aosp/external/openssh/openbsd-compat/readpassphrase.c new file mode 100644 index 0000000000000000000000000000000000000000..ff8ff3dec77f55daf428a11247a83a25db8af64d --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/readpassphrase.c @@ -0,0 +1,211 @@ +/* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */ + +/* + * Copyright (c) 2000-2002, 2007, 2010 + * Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ + +#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TCSASOFT +/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */ +# define TCSASOFT 0 +#endif + +/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ +#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) +# define _POSIX_VDISABLE VDISABLE +#endif + +static volatile sig_atomic_t signo[_NSIG]; + +static void handler(int); + +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + ssize_t nr; + int input, output, save_errno, i, need_restart; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou, savepipe; + + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return(NULL); + } + +restart: + for (i = 0; i < _NSIG; i++) + signo[i] = 0; + nr = -1; + save_errno = 0; + need_restart = 0; + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((flags & RPP_STDIN) || + (input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return(NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * Turn off echo if possible. + * If we are using a tty but are not the foreground pgrp this will + * generate SIGTTOU, so do it *before* installing the signal handlers. + */ + if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); +#ifdef VSTATUS + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; +#endif + (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); + } else { + memset(&term, 0, sizeof(term)); + term.c_lflag |= ECHO; + memset(&oterm, 0, sizeof(oterm)); + oterm.c_lflag |= ECHO; + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGXCPU and SIGVTALRM for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGALRM, &sa, &savealrm); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGPIPE, &sa, &savepipe); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); + + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha((unsigned char)ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower((unsigned char)ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper((unsigned char)ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); + + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) { + const int sigttou = signo[SIGTTOU]; + + /* Ignore SIGTTOU generated when we are not the fg pgrp. */ + while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && + errno == EINTR && !signo[SIGTTOU]) + continue; + signo[SIGTTOU] = sigttou; + } + (void)sigaction(SIGALRM, &savealrm, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGPIPE, &savepipe, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) + (void)close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + for (i = 0; i < _NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + } + } + } + if (need_restart) + goto restart; + + if (save_errno) + errno = save_errno; + return(nr == -1 ? NULL : buf); +} +DEF_WEAK(readpassphrase); + +#if 0 +char * +getpass(const char *prompt) +{ + static char buf[_PASSWORD_LEN + 1]; + + return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); +} +#endif + +static void handler(int s) +{ + + signo[s] = 1; +} +#endif /* HAVE_READPASSPHRASE */ diff --git a/aosp/external/openssh/openbsd-compat/readpassphrase.h b/aosp/external/openssh/openbsd-compat/readpassphrase.h new file mode 100644 index 0000000000000000000000000000000000000000..5fd7c5d77abae60c782359031f79956f10248840 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/readpassphrase.h @@ -0,0 +1,44 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: include/readpassphrase.h */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ +#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* HAVE_READPASSPHRASE */ + +#endif /* !_READPASSPHRASE_H_ */ diff --git a/aosp/external/openssh/openbsd-compat/reallocarray.c b/aosp/external/openssh/openbsd-compat/reallocarray.c new file mode 100644 index 0000000000000000000000000000000000000000..1a52acc623fecd3d5e9496d7daae720894bcf9f3 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/reallocarray.c @@ -0,0 +1,46 @@ +/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/reallocarray.c */ + +#include "includes.h" +#ifndef HAVE_REALLOCARRAY + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} +#endif /* HAVE_REALLOCARRAY */ diff --git a/aosp/external/openssh/openbsd-compat/recallocarray.c b/aosp/external/openssh/openbsd-compat/recallocarray.c new file mode 100644 index 0000000000000000000000000000000000000000..3e1156ce2d95985b5cebef86b94043050034c525 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/recallocarray.c @@ -0,0 +1,90 @@ +/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */ +/* + * Copyright (c) 2008, 2017 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */ + +#include "includes.h" +#ifndef HAVE_RECALLOCARRAY + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) +{ + size_t oldsize, newsize; + void *newptr; + + if (ptr == NULL) + return calloc(newnmemb, size); + + if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + newnmemb > 0 && SIZE_MAX / newnmemb < size) { + errno = ENOMEM; + return NULL; + } + newsize = newnmemb * size; + + if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { + errno = EINVAL; + return NULL; + } + oldsize = oldnmemb * size; + + /* + * Don't bother too much if we're shrinking just a bit, + * we do not shrink for series of small steps, oh well. + */ + if (newsize <= oldsize) { + size_t d = oldsize - newsize; + + if (d < oldsize / 2 && d < (size_t)getpagesize()) { + memset((char *)ptr + newsize, 0, d); + return ptr; + } + } + + newptr = malloc(newsize); + if (newptr == NULL) + return NULL; + + if (newsize > oldsize) { + memcpy(newptr, ptr, oldsize); + memset((char *)newptr + oldsize, 0, newsize - oldsize); + } else + memcpy(newptr, ptr, newsize); + + explicit_bzero(ptr, oldsize); + free(ptr); + + return newptr; +} +/* DEF_WEAK(recallocarray); */ + +#endif /* HAVE_RECALLOCARRAY */ diff --git a/aosp/external/openssh/openbsd-compat/regress/Makefile.in b/aosp/external/openssh/openbsd-compat/regress/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..dd8cdc4b7e7adbcd30ed96fdcbf229f454314c05 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/Makefile.in @@ -0,0 +1,36 @@ +sysconfdir=@sysconfdir@ +piddir=@piddir@ +srcdir=@srcdir@ +top_srcdir=@top_srcdir@ + +VPATH=@srcdir@ +CC=@CC@ +LD=@LD@ +CFLAGS=@CFLAGS@ +CPPFLAGS=-I. -I.. -I../.. -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. @CPPFLAGS@ @DEFS@ +EXEEXT=@EXEEXT@ +LIBCOMPAT=../libopenbsd-compat.a +LIBS=@LIBS@ +LDFLAGS=@LDFLAGS@ $(LIBCOMPAT) + +TESTPROGS=closefromtest$(EXEEXT) snprintftest$(EXEEXT) strduptest$(EXEEXT) \ + strtonumtest$(EXEEXT) opensslvertest$(EXEEXT) utimensattest$(EXEEXT) + +all: t-exec ${OTHERTESTS} + +%$(EXEEXT): %.c $(LIBCOMPAT) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< $(LIBCOMPAT) $(LIBS) + +t-exec: $(TESTPROGS) + @echo running compat regress tests + @for TEST in ""$?; do \ + echo "run test $${TEST}" ... 1>&2; \ + ./$${TEST}$(EXEEXT) || exit $$? ; \ + done + @echo finished compat regress tests + +clean: + rm -f *.o *.a core $(TESTPROGS) valid.out + +distclean: clean + rm -f Makefile *~ diff --git a/aosp/external/openssh/openbsd-compat/regress/closefromtest.c b/aosp/external/openssh/openbsd-compat/regress/closefromtest.c new file mode 100644 index 0000000000000000000000000000000000000000..7a69fb2b1a4e6fa6ffc520825acba348627cc9b5 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/closefromtest.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include + +#define NUM_OPENS 10 + +void +fail(char *msg) +{ + fprintf(stderr, "closefrom: %s\n", msg); + exit(1); +} + +int +main(void) +{ + int i, max, fds[NUM_OPENS]; + char buf[512]; + + for (i = 0; i < NUM_OPENS; i++) + if ((fds[i] = open("/dev/null", O_RDONLY)) == -1) + exit(0); /* can't test */ + max = i - 1; + + /* should close last fd only */ + closefrom(fds[max]); + if (close(fds[max]) != -1) + fail("failed to close highest fd"); + + /* make sure we can still use remaining descriptors */ + for (i = 0; i < max; i++) + if (read(fds[i], buf, sizeof(buf)) == -1) + fail("closed descriptors it should not have"); + + /* should close all fds */ + closefrom(fds[0]); + for (i = 0; i < NUM_OPENS; i++) + if (close(fds[i]) != -1) + fail("failed to close from lowest fd"); + return 0; +} diff --git a/aosp/external/openssh/openbsd-compat/regress/opensslvertest.c b/aosp/external/openssh/openbsd-compat/regress/opensslvertest.c new file mode 100644 index 0000000000000000000000000000000000000000..43825b24c3eb04cd59f328afe58dd44ee914e9c5 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/opensslvertest.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +int ssh_compatible_openssl(long, long); + +struct version_test { + long headerver; + long libver; + int result; +} version_tests[] = { + /* built with 0.9.8b release headers */ + { 0x0090802fL, 0x0090802fL, 1}, /* exact match */ + { 0x0090802fL, 0x0090804fL, 1}, /* newer library fix version: ok */ + { 0x0090802fL, 0x0090801fL, 1}, /* older library fix version: ok */ + { 0x0090802fL, 0x0090702fL, 0}, /* older library minor version: NO */ + { 0x0090802fL, 0x0090902fL, 0}, /* newer library minor version: NO */ + { 0x0090802fL, 0x0080802fL, 0}, /* older library major version: NO */ + { 0x0090802fL, 0x1000100fL, 0}, /* newer library major version: NO */ + + /* built with 1.0.1b release headers */ + { 0x1000101fL, 0x1000101fL, 1},/* exact match */ + { 0x1000101fL, 0x1000102fL, 1}, /* newer library patch version: ok */ + { 0x1000101fL, 0x1000100fL, 1}, /* older library patch version: ok */ + { 0x1000101fL, 0x1000201fL, 1}, /* newer library fix version: ok */ + { 0x1000101fL, 0x1000001fL, 0}, /* older library fix version: NO */ + { 0x1000101fL, 0x1010101fL, 0}, /* newer library minor version: NO */ + { 0x1000101fL, 0x0000101fL, 0}, /* older library major version: NO */ + { 0x1000101fL, 0x2000101fL, 0}, /* newer library major version: NO */ +}; + +void +fail(long hver, long lver, int result) +{ + fprintf(stderr, "opensslver: header %lx library %lx != %d \n", hver, lver, result); + exit(1); +} + +int +main(void) +{ + unsigned int i; + int res; + long hver, lver; + + for (i = 0; i < sizeof(version_tests) / sizeof(version_tests[0]); i++) { + hver = version_tests[i].headerver; + lver = version_tests[i].libver; + res = version_tests[i].result; + if (ssh_compatible_openssl(hver, lver) != res) + fail(hver, lver, res); + } + exit(0); +} diff --git a/aosp/external/openssh/openbsd-compat/regress/snprintftest.c b/aosp/external/openssh/openbsd-compat/regress/snprintftest.c new file mode 100644 index 0000000000000000000000000000000000000000..a3134db1ca947423d77d168e62a5858f64f35f14 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/snprintftest.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005 Darren Tucker + * Copyright (c) 2005 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BUFSZ 2048 + +#include "includes.h" + +#include +#include +#include +#include +#include + +static int failed = 0; + +static void +fail(const char *m) +{ + fprintf(stderr, "snprintftest: %s\n", m); + failed = 1; +} + +int x_snprintf(char *str, size_t count, const char *fmt, ...) +{ + size_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vsnprintf(str, count, fmt, ap); + va_end(ap); + return ret; +} + +int +main(void) +{ + char b[5]; + char *src = NULL; + + snprintf(b,5,"123456789"); + if (b[4] != '\0') + fail("snprintf does not correctly terminate long strings"); + + /* check for read overrun on unterminated string */ + if ((src = malloc(BUFSZ)) == NULL) { + fail("malloc failed"); + } else { + memset(src, 'a', BUFSZ); + snprintf(b, sizeof(b), "%.*s", 1, src); + if (strcmp(b, "a") != 0) + fail("failed with length limit '%%.s'"); + } + + /* check that snprintf and vsnprintf return sane values */ + if (snprintf(b, 1, "%s %d", "hello", 12345) != 11) + fail("snprintf does not return required length"); + if (x_snprintf(b, 1, "%s %d", "hello", 12345) != 11) + fail("vsnprintf does not return required length"); + + free(src); + return failed; +} diff --git a/aosp/external/openssh/openbsd-compat/regress/strduptest.c b/aosp/external/openssh/openbsd-compat/regress/strduptest.c new file mode 100644 index 0000000000000000000000000000000000000000..8a3ccf77169f9b6dee3c2cdb36cd2d49ca6550a1 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/strduptest.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +static int fail = 0; + +void +test(const char *a) +{ + char *b; + + b = strdup(a); + if (b == 0) { + fail = 1; + return; + } + if (strcmp(a, b) != 0) + fail = 1; + free(b); +} + +int +main(void) +{ + test(""); + test("a"); + test("\0"); + test("abcdefghijklmnopqrstuvwxyz"); + return fail; +} diff --git a/aosp/external/openssh/openbsd-compat/regress/strtonumtest.c b/aosp/external/openssh/openbsd-compat/regress/strtonumtest.c new file mode 100644 index 0000000000000000000000000000000000000000..46bd2b916494d113839a14f0ebd1ae89a1931b81 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/strtonumtest.c @@ -0,0 +1,82 @@ +/* $OpenBSD: strtonumtest.c,v 1.1 2004/08/03 20:38:36 otto Exp $ */ +/* + * Copyright (c) 2004 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: regress/lib/libc/strtonum/strtonumtest.c */ + +#include "includes.h" + +#include +#include +#include + +/* LLONG_MAX is known as LONGLONG_MAX on AIX */ +#if defined(LONGLONG_MAX) && !defined(LLONG_MAX) +# define LLONG_MAX LONGLONG_MAX +# define LLONG_MIN LONGLONG_MIN +#endif + +/* LLONG_MAX is known as LONG_LONG_MAX on HP-UX */ +#if defined(LONG_LONG_MAX) && !defined(LLONG_MAX) +# define LLONG_MAX LONG_LONG_MAX +# define LLONG_MIN LONG_LONG_MIN +#endif + +long long strtonum(const char *, long long, long long, const char **); + +int fail; + +void +test(const char *p, long long lb, long long ub, int ok) +{ + long long val; + const char *q; + + val = strtonum(p, lb, ub, &q); + if (ok && q != NULL) { + fprintf(stderr, "%s [%lld-%lld] ", p, lb, ub); + fprintf(stderr, "NUMBER NOT ACCEPTED %s\n", q); + fail = 1; + } else if (!ok && q == NULL) { + fprintf(stderr, "%s [%lld-%lld] %lld ", p, lb, ub, val); + fprintf(stderr, "NUMBER ACCEPTED\n"); + fail = 1; + } +} + +int main(int argc, char *argv[]) +{ + test("1", 0, 10, 1); + test("0", -2, 5, 1); + test("0", 2, 5, 0); + test("0", 2, LLONG_MAX, 0); + test("-2", 0, LLONG_MAX, 0); + test("0", -5, LLONG_MAX, 1); + test("-3", -3, LLONG_MAX, 1); + test("-9223372036854775808", LLONG_MIN, LLONG_MAX, 1); + test("9223372036854775807", LLONG_MIN, LLONG_MAX, 1); + test("-9223372036854775809", LLONG_MIN, LLONG_MAX, 0); + test("9223372036854775808", LLONG_MIN, LLONG_MAX, 0); + test("1000000000000000000000000", LLONG_MIN, LLONG_MAX, 0); + test("-1000000000000000000000000", LLONG_MIN, LLONG_MAX, 0); + test("-2", 10, -1, 0); + test("-2", -10, -1, 1); + test("-20", -10, -1, 0); + test("20", -10, -1, 0); + + return (fail); +} + diff --git a/aosp/external/openssh/openbsd-compat/regress/utimensattest.c b/aosp/external/openssh/openbsd-compat/regress/utimensattest.c new file mode 100644 index 0000000000000000000000000000000000000000..bbc66c48523e1caa46c55cb293bdf9b167538e61 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/regress/utimensattest.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define TMPFILE "utimensat.tmp" +#define TMPFILE2 "utimensat.tmp2" + +#ifndef AT_SYMLINK_NOFOLLOW +# define AT_SYMLINK_NOFOLLOW 0x80000000 +#endif + +int utimensat(int, const char *, const struct timespec[2], int); + +static void +cleanup(void) +{ + (void)unlink(TMPFILE); + (void)unlink(TMPFILE2); +} + +static void +fail(char *msg, long expect, long got) +{ + int saved_errno = errno; + + if (expect == got && got == 0) + fprintf(stderr, "utimensat: %s: %s\n", msg, + strerror(saved_errno)); + else + fprintf(stderr, "utimensat: %s: expected %ld got %ld\n", + msg, expect, got); + cleanup(); + exit(1); +} + +int +main(void) +{ + int fd; + struct stat sb; + struct timespec ts[2]; + + cleanup(); + if ((fd = open(TMPFILE, O_CREAT, 0600)) == -1) + fail("open", 0, 0); + close(fd); + + ts[0].tv_sec = 12345678; + ts[0].tv_nsec = 23456789; + ts[1].tv_sec = 34567890; + ts[1].tv_nsec = 45678901; + if (utimensat(AT_FDCWD, TMPFILE, ts, AT_SYMLINK_NOFOLLOW) == -1) + fail("utimensat", 0, 0); + + if (stat(TMPFILE, &sb) == -1) + fail("stat", 0, 0 ); + if (sb.st_atime != 12345678) + fail("st_atime", 0, 0 ); + if (sb.st_mtime != 34567890) + fail("st_mtime", 0, 0 ); +#if 0 + /* + * Results expected to be rounded to the nearest microsecond. + * Depends on timestamp precision in kernel and filesystem so + * disabled by default. + */ + if (sb.st_atim.tv_nsec != 23456000) + fail("atim.tv_nsec", 23456000, sb.st_atim.tv_nsec); + if (sb.st_mtim.tv_nsec != 45678000) + fail("mtim.tv_nsec", 45678000, sb.st_mtim.tv_nsec); +#endif + + /* + * POSIX specifies that when given a symlink, AT_SYMLINK_NOFOLLOW + * should update the symlink and not the destination. The compat + * code doesn't have a way to do this, so where possible it fails + * with instead of following a symlink when explicitly asked not to. + * Here we just test that it does not update the destination. + */ + if (rename(TMPFILE, TMPFILE2) == -1) + fail("rename", 0, 0); + if (symlink(TMPFILE2, TMPFILE) == -1) + fail("symlink", 0, 0); + ts[0].tv_sec = 11223344; + ts[1].tv_sec = 55667788; + (void)utimensat(AT_FDCWD, TMPFILE, ts, AT_SYMLINK_NOFOLLOW); + if (stat(TMPFILE2, &sb) == -1) + fail("stat", 0, 0 ); + if (sb.st_atime == 11223344) + fail("utimensat symlink st_atime", 0, 0 ); + if (sb.st_mtime == 55667788) + fail("utimensat symlink st_mtime", 0, 0 ); + + cleanup(); + exit(0); +} diff --git a/aosp/external/openssh/openbsd-compat/rresvport.c b/aosp/external/openssh/openbsd-compat/rresvport.c new file mode 100644 index 0000000000000000000000000000000000000000..1cd61e58dbad7ae6ec27046e2b76d5d4ce7b3454 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/rresvport.c @@ -0,0 +1,108 @@ +/* $OpenBSD: rresvport.c,v 1.9 2005/11/10 10:00:17 espie Exp $ */ +/* + * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved. + * Copyright (c) 1983, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/rresvport.c */ + +#include "includes.h" + +#ifndef HAVE_RRESVPORT_AF + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#if 0 +int +rresvport(int *alport) +{ + return rresvport_af(alport, AF_INET); +} +#endif + +int +rresvport_af(int *alport, sa_family_t af) +{ + struct sockaddr_storage ss; + struct sockaddr *sa; + u_int16_t *portp; + int s; + socklen_t salen; + + memset(&ss, '\0', sizeof ss); + sa = (struct sockaddr *)&ss; + + switch (af) { + case AF_INET: + salen = sizeof(struct sockaddr_in); + portp = &((struct sockaddr_in *)sa)->sin_port; + break; + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + portp = &((struct sockaddr_in6 *)sa)->sin6_port; + break; + default: + errno = EPFNOSUPPORT; + return (-1); + } + sa->sa_family = af; + + s = socket(af, SOCK_STREAM, 0); + if (s < 0) + return (-1); + + *portp = htons(*alport); + if (*alport < IPPORT_RESERVED - 1) { + if (bind(s, sa, salen) >= 0) + return (s); + if (errno != EADDRINUSE) { + (void)close(s); + return (-1); + } + } + + *portp = 0; + sa->sa_family = af; + if (bindresvport_sa(s, sa) == -1) { + (void)close(s); + return (-1); + } + *alport = ntohs(*portp); + return (s); +} + +#endif /* HAVE_RRESVPORT_AF */ diff --git a/aosp/external/openssh/openbsd-compat/setenv.c b/aosp/external/openssh/openbsd-compat/setenv.c new file mode 100644 index 0000000000000000000000000000000000000000..86954c284ee3b74e8fe93c63793c64fc04c0317c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/setenv.c @@ -0,0 +1,228 @@ +/* $OpenBSD: setenv.c,v 1.13 2010/08/23 22:31:50 millert Exp $ */ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/setenv.c */ + +#include "includes.h" + +#if !defined(HAVE_SETENV) || !defined(HAVE_UNSETENV) + +#include +#include +#include + +extern char **environ; +#ifndef HAVE_SETENV +static char **lastenv; /* last value of environ */ +#endif + +/* OpenSSH Portable: __findenv is from getenv.c rev 1.8, made static */ +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Starts searching within the environmental array at offset. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by putenv(3), setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + * + * This routine *should* be a static; don't use it. + */ +static char * +__findenv(const char *name, int len, int *offset) +{ + extern char **environ; + int i; + const char *np; + char **p, *cp; + + if (name == NULL || environ == NULL) + return (NULL); + for (p = environ + *offset; (cp = *p) != NULL; ++p) { + for (np = name, i = len; i && *cp; i--) + if (*cp++ != *np++) + break; + if (i == 0 && *cp++ == '=') { + *offset = p - environ; + return (cp); + } + } + return (NULL); +} + +#if 0 /* nothing uses putenv */ +/* + * putenv -- + * Add a name=value string directly to the environmental, replacing + * any current value. + */ +int +putenv(char *str) +{ + char **P, *cp; + size_t cnt; + int offset = 0; + + for (cp = str; *cp && *cp != '='; ++cp) + ; + if (*cp != '=') { + errno = EINVAL; + return (-1); /* missing `=' in string */ + } + + if (__findenv(str, (int)(cp - str), &offset) != NULL) { + environ[offset++] = str; + /* could be set multiple times */ + while (__findenv(str, (int)(cp - str), &offset)) { + for (P = &environ[offset];; ++P) + if (!(*P = *(P + 1))) + break; + } + return (0); + } + + /* create new slot for string */ + for (P = environ; *P != NULL; P++) + ; + cnt = P - environ; + P = (char **)realloc(lastenv, sizeof(char *) * (cnt + 2)); + if (!P) + return (-1); + if (lastenv != environ) + memcpy(P, environ, cnt * sizeof(char *)); + lastenv = environ = P; + environ[cnt] = str; + environ[cnt + 1] = NULL; + return (0); +} + +#endif + +#ifndef HAVE_SETENV +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +int +setenv(const char *name, const char *value, int rewrite) +{ + char *C, **P; + const char *np; + int l_value, offset = 0; + + for (np = name; *np && *np != '='; ++np) + ; +#ifdef notyet + if (*np) { + errno = EINVAL; + return (-1); /* has `=' in name */ + } +#endif + + l_value = strlen(value); + if ((C = __findenv(name, (int)(np - name), &offset)) != NULL) { + int tmpoff = offset + 1; + if (!rewrite) + return (0); +#if 0 /* XXX - existing entry may not be writable */ + if (strlen(C) >= l_value) { /* old larger; copy over */ + while ((*C++ = *value++)) + ; + return (0); + } +#endif + /* could be set multiple times */ + while (__findenv(name, (int)(np - name), &tmpoff)) { + for (P = &environ[tmpoff];; ++P) + if (!(*P = *(P + 1))) + break; + } + } else { /* create new slot */ + size_t cnt; + + for (P = environ; *P != NULL; P++) + ; + cnt = P - environ; + P = (char **)realloc(lastenv, sizeof(char *) * (cnt + 2)); + if (!P) + return (-1); + if (lastenv != environ) + memcpy(P, environ, cnt * sizeof(char *)); + lastenv = environ = P; + offset = cnt; + environ[cnt + 1] = NULL; + } + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(np - name) + l_value + 2)))) + return (-1); + for (C = environ[offset]; (*C = *name++) && *C != '='; ++C) + ; + for (*C++ = '='; (*C++ = *value++); ) + ; + return (0); +} + +#endif /* HAVE_SETENV */ + +#ifndef HAVE_UNSETENV +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +int +unsetenv(const char *name) +{ + char **P; + const char *np; + int offset = 0; + + if (!name || !*name) { + errno = EINVAL; + return (-1); + } + for (np = name; *np && *np != '='; ++np) + ; + if (*np) { + errno = EINVAL; + return (-1); /* has `=' in name */ + } + + /* could be set multiple times */ + while (__findenv(name, (int)(np - name), &offset)) { + for (P = &environ[offset];; ++P) + if (!(*P = *(P + 1))) + break; + } + return (0); +} +#endif /* HAVE_UNSETENV */ + +#endif /* !defined(HAVE_SETENV) || !defined(HAVE_UNSETENV) */ + diff --git a/aosp/external/openssh/openbsd-compat/setproctitle.c b/aosp/external/openssh/openbsd-compat/setproctitle.c new file mode 100644 index 0000000000000000000000000000000000000000..e4064323a63060d34633630467e4cc28d15f9080 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/setproctitle.c @@ -0,0 +1,170 @@ +/* Based on conf.c from UCB sendmail 8.8.8 */ + +/* + * Copyright 2003 Damien Miller + * Copyright (c) 1983, 1995-1997 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "includes.h" + +#ifndef HAVE_SETPROCTITLE + +#include +#include +#include +#include +#ifdef HAVE_SYS_PSTAT_H +#include +#endif +#include + +#include + +#define SPT_NONE 0 /* don't use it at all */ +#define SPT_PSTAT 1 /* use pstat(PSTAT_SETCMD, ...) */ +#define SPT_REUSEARGV 2 /* cover argv with title information */ + +#ifndef SPT_TYPE +# define SPT_TYPE SPT_NONE +#endif + +#ifndef SPT_PADCHAR +# define SPT_PADCHAR '\0' +#endif + +#if SPT_TYPE == SPT_REUSEARGV +static char *argv_start = NULL; +static size_t argv_env_len = 0; +#endif + +#endif /* HAVE_SETPROCTITLE */ + +void +compat_init_setproctitle(int argc, char *argv[]) +{ +#if !defined(HAVE_SETPROCTITLE) && \ + defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV + extern char **environ; + char *lastargv = NULL; + char **envp = environ; + int i; + + /* + * NB: This assumes that argv has already been copied out of the + * way. This is true for sshd, but may not be true for other + * programs. Beware. + */ + + if (argc == 0 || argv[0] == NULL) + return; + + /* Fail if we can't allocate room for the new environment */ + for (i = 0; envp[i] != NULL; i++) + ; + if ((environ = calloc(i + 1, sizeof(*environ))) == NULL) { + environ = envp; /* put it back */ + return; + } + + /* + * Find the last argv string or environment variable within + * our process memory area. + */ + for (i = 0; i < argc; i++) { + if (lastargv == NULL || lastargv + 1 == argv[i]) + lastargv = argv[i] + strlen(argv[i]); + } + for (i = 0; envp[i] != NULL; i++) { + if (lastargv + 1 == envp[i]) + lastargv = envp[i] + strlen(envp[i]); + } + + argv[1] = NULL; + argv_start = argv[0]; + argv_env_len = lastargv - argv[0] - 1; + + /* + * Copy environment + * XXX - will truncate env on strdup fail + */ + for (i = 0; envp[i] != NULL; i++) + environ[i] = strdup(envp[i]); + environ[i] = NULL; +#endif /* SPT_REUSEARGV */ +} + +#ifndef HAVE_SETPROCTITLE +void +setproctitle(const char *fmt, ...) +{ +#if SPT_TYPE != SPT_NONE + va_list ap; + char buf[1024], ptitle[1024]; + size_t len = 0; + int r; + extern char *__progname; +#if SPT_TYPE == SPT_PSTAT + union pstun pst; +#endif + +#if SPT_TYPE == SPT_REUSEARGV + if (argv_env_len <= 0) + return; +#endif + + strlcpy(buf, __progname, sizeof(buf)); + + r = -1; + va_start(ap, fmt); + if (fmt != NULL) { + len = strlcat(buf, ": ", sizeof(buf)); + if (len < sizeof(buf)) + r = vsnprintf(buf + len, sizeof(buf) - len , fmt, ap); + } + va_end(ap); + if (r == -1 || (size_t)r >= sizeof(buf) - len) + return; + strnvis(ptitle, buf, sizeof(ptitle), + VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL); + +#if SPT_TYPE == SPT_PSTAT + pst.pst_command = ptitle; + pstat(PSTAT_SETCMD, pst, strlen(ptitle), 0, 0); +#elif SPT_TYPE == SPT_REUSEARGV +/* debug("setproctitle: copy \"%s\" into len %d", + buf, argv_env_len); */ + len = strlcpy(argv_start, ptitle, argv_env_len); + for(; len < argv_env_len; len++) + argv_start[len] = SPT_PADCHAR; +#endif + +#endif /* SPT_NONE */ +} + +#endif /* HAVE_SETPROCTITLE */ diff --git a/aosp/external/openssh/openbsd-compat/sha1.c b/aosp/external/openssh/openbsd-compat/sha1.c new file mode 100644 index 0000000000000000000000000000000000000000..73f8974853d19c09516269635ab1c8d2ebbb235b --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sha1.c @@ -0,0 +1,182 @@ +/* $OpenBSD: sha1.c,v 1.27 2019/06/07 22:56:36 dtucker Exp $ */ + +/* + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ + +#include "includes.h" + +#ifndef WITH_OPENSSL + +#include +#include + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +# define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +typedef union { + u_int8_t c[64]; + u_int32_t l[16]; +} CHAR64LONG16; + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +void +SHA1Transform(u_int32_t state[5], const u_int8_t buffer[SHA1_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e; + u_int8_t workspace[SHA1_BLOCK_LENGTH]; + CHAR64LONG16 *block = (CHAR64LONG16 *)workspace; + + (void)memcpy(block, buffer, SHA1_BLOCK_LENGTH); + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} +DEF_WEAK(SHA1Transform); + + +/* + * SHA1Init - Initialize new context + */ +void +SHA1Init(SHA1_CTX *context) +{ + + /* SHA1 initialization constants */ + context->count = 0; + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; +} +DEF_WEAK(SHA1Init); + + +/* + * Run your data through this. + */ +void +SHA1Update(SHA1_CTX *context, const u_int8_t *data, size_t len) +{ + size_t i, j; + + j = (size_t)((context->count >> 3) & 63); + context->count += ((u_int64_t)len << 3); + if ((j + len) > 63) { + (void)memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + SHA1Transform(context->state, (u_int8_t *)&data[i]); + j = 0; + } else { + i = 0; + } + (void)memcpy(&context->buffer[j], &data[i], len - i); +} +DEF_WEAK(SHA1Update); + + +/* + * Add padding and return the message digest. + */ +void +SHA1Pad(SHA1_CTX *context) +{ + u_int8_t finalcount[8]; + u_int i; + + for (i = 0; i < 8; i++) { + finalcount[i] = (u_int8_t)((context->count >> + ((7 - (i & 7)) * 8)) & 255); /* Endian independent */ + } + SHA1Update(context, (u_int8_t *)"\200", 1); + while ((context->count & 504) != 448) + SHA1Update(context, (u_int8_t *)"\0", 1); + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ +} +DEF_WEAK(SHA1Pad); + +void +SHA1Final(u_int8_t digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context) +{ + u_int i; + + SHA1Pad(context); + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + digest[i] = (u_int8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA1Final); +#endif /* !WITH_OPENSSL */ diff --git a/aosp/external/openssh/openbsd-compat/sha1.h b/aosp/external/openssh/openbsd-compat/sha1.h new file mode 100644 index 0000000000000000000000000000000000000000..327d94cd5a879b186bb521cc356b171201a7a90c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sha1.h @@ -0,0 +1,58 @@ +/* $OpenBSD: sha1.h,v 1.24 2012/12/05 23:19:57 deraadt Exp $ */ + +/* + * SHA-1 in C + * By Steve Reid + * 100% Public Domain + */ + +#ifndef _SHA1_H +#define _SHA1_H + +#ifndef WITH_OPENSSL + +#define SHA1_BLOCK_LENGTH 64 +#define SHA1_DIGEST_LENGTH 20 +#define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1) + +typedef struct { + u_int32_t state[5]; + u_int64_t count; + u_int8_t buffer[SHA1_BLOCK_LENGTH]; +} SHA1_CTX; + +void SHA1Init(SHA1_CTX *); +void SHA1Pad(SHA1_CTX *); +void SHA1Transform(u_int32_t [5], const u_int8_t [SHA1_BLOCK_LENGTH]) + __attribute__((__bounded__(__minbytes__,1,5))) + __attribute__((__bounded__(__minbytes__,2,SHA1_BLOCK_LENGTH))); +void SHA1Update(SHA1_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA1Final(u_int8_t [SHA1_DIGEST_LENGTH], SHA1_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA1_DIGEST_LENGTH))); +char *SHA1End(SHA1_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA1_DIGEST_STRING_LENGTH))); +char *SHA1File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA1_DIGEST_STRING_LENGTH))); +char *SHA1FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA1_DIGEST_STRING_LENGTH))); +char *SHA1Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA1_DIGEST_STRING_LENGTH))); + +#define HTONDIGEST(x) do { \ + x[0] = htonl(x[0]); \ + x[1] = htonl(x[1]); \ + x[2] = htonl(x[2]); \ + x[3] = htonl(x[3]); \ + x[4] = htonl(x[4]); } while (0) + +#define NTOHDIGEST(x) do { \ + x[0] = ntohl(x[0]); \ + x[1] = ntohl(x[1]); \ + x[2] = ntohl(x[2]); \ + x[3] = ntohl(x[3]); \ + x[4] = ntohl(x[4]); } while (0) + +#endif /* !WITH_OPENSSL */ +#endif /* _SHA1_H */ diff --git a/aosp/external/openssh/openbsd-compat/sha2.c b/aosp/external/openssh/openbsd-compat/sha2.c new file mode 100644 index 0000000000000000000000000000000000000000..4f2ad8f2352ae3791bd8d90da7321e69319f44e9 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sha2.c @@ -0,0 +1,1010 @@ +/* $OpenBSD: sha2.c,v 1.28 2019/07/23 12:35:22 dtucker Exp $ */ + +/* + * FILE: sha2.c + * AUTHOR: Aaron D. Gifford + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ + */ + +/* OPENBSD ORIGINAL: lib/libc/hash/sha2.c */ + +#include "includes.h" + +#if !defined(HAVE_SHA256UPDATE) || !defined(HAVE_SHA384UPDATE) || \ + !defined(HAVE_SHA512UPDATE) + +/* no-op out, similar to DEF_WEAK but only needed here */ +#define MAKE_CLONE(x, y) void __ssh_compat_make_clone_##x_##y(void) + +#include +#include "openbsd-compat/sha2.h" + +/* + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ +#ifndef SHA2_SMALL +#if defined(__amd64__) || defined(__i386__) +#define SHA2_UNROLL_TRANSFORM +#endif +#endif + +/*** SHA-224/256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivalent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + + +/*** SHA-224/256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA224_SHORT_BLOCK_LENGTH (SHA224_BLOCK_LENGTH - 8) +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + +/*** ENDIAN SPECIFIC COPY MACROS **************************************/ +#define BE_8_TO_32(dst, cp) do { \ + (dst) = (u_int32_t)(cp)[3] | ((u_int32_t)(cp)[2] << 8) | \ + ((u_int32_t)(cp)[1] << 16) | ((u_int32_t)(cp)[0] << 24); \ +} while(0) + +#define BE_8_TO_64(dst, cp) do { \ + (dst) = (u_int64_t)(cp)[7] | ((u_int64_t)(cp)[6] << 8) | \ + ((u_int64_t)(cp)[5] << 16) | ((u_int64_t)(cp)[4] << 24) | \ + ((u_int64_t)(cp)[3] << 32) | ((u_int64_t)(cp)[2] << 40) | \ + ((u_int64_t)(cp)[1] << 48) | ((u_int64_t)(cp)[0] << 56); \ +} while (0) + +#define BE_64_TO_8(cp, src) do { \ + (cp)[0] = (src) >> 56; \ + (cp)[1] = (src) >> 48; \ + (cp)[2] = (src) >> 40; \ + (cp)[3] = (src) >> 32; \ + (cp)[4] = (src) >> 24; \ + (cp)[5] = (src) >> 16; \ + (cp)[6] = (src) >> 8; \ + (cp)[7] = (src); \ +} while (0) + +#define BE_32_TO_8(cp, src) do { \ + (cp)[0] = (src) >> 24; \ + (cp)[1] = (src) >> 16; \ + (cp)[2] = (src) >> 8; \ + (cp)[3] = (src); \ +} while (0) + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) do { \ + (w)[0] += (u_int64_t)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} while (0) + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-224/256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-224, SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-224 and SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-224, SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-224 and SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-224 and SHA-256: */ +static const u_int32_t K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +static const u_int32_t sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +static const u_int64_t K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-512 */ +static const u_int64_t sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; + +#if !defined(SHA2_SMALL) +#if 0 +/* Initial hash value H for SHA-224: */ +static const u_int32_t sha224_initial_hash_value[8] = { + 0xc1059ed8UL, + 0x367cd507UL, + 0x3070dd17UL, + 0xf70e5939UL, + 0xffc00b31UL, + 0x68581511UL, + 0x64f98fa7UL, + 0xbefa4fa4UL +}; +#endif /* 0 */ + +/* Initial hash value H for SHA-384 */ +static const u_int64_t sha384_initial_hash_value[8] = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL +}; + +#if 0 +/* Initial hash value H for SHA-512-256 */ +static const u_int64_t sha512_256_initial_hash_value[8] = { + 0x22312194fc2bf72cULL, + 0x9f555fa3c84c64c2ULL, + 0x2393b86b6f53b151ULL, + 0x963877195940eabdULL, + 0x96283ee2a88effe3ULL, + 0xbe5e1e2553863992ULL, + 0x2b0199fc2c85b8aaULL, + 0x0eb72ddc81c52ca2ULL +}; + +/*** SHA-224: *********************************************************/ +void +SHA224Init(SHA2_CTX *context) +{ + memcpy(context->state.st32, sha224_initial_hash_value, + sizeof(sha224_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = 0; +} +DEF_WEAK(SHA224Init); + +MAKE_CLONE(SHA224Transform, SHA256Transform); +MAKE_CLONE(SHA224Update, SHA256Update); +MAKE_CLONE(SHA224Pad, SHA256Pad); +DEF_WEAK(SHA224Transform); +DEF_WEAK(SHA224Update); +DEF_WEAK(SHA224Pad); + +void +SHA224Final(u_int8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA224Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 7; i++) + BE_32_TO_8(digest + i * 4, context->state.st32[i]); +#else + memcpy(digest, context->state.st32, SHA224_DIGEST_LENGTH); +#endif + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA224Final); +#endif /* !defined(SHA2_SMALL) */ +#endif /* 0 */ + +/*** SHA-256: *********************************************************/ +void +SHA256Init(SHA2_CTX *context) +{ + memcpy(context->state.st32, sha256_initial_hash_value, + sizeof(sha256_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = 0; +} +DEF_WEAK(SHA256Init); + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \ + BE_8_TO_32(W256[j], data); \ + data += 4; \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +#define ROUND256(a,b,c,d,e,f,g,h) do { \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +void +SHA256Transform(u_int32_t state[8], const u_int8_t data[SHA256_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e, f, g, h, s0, s1; + u_int32_t T1, W256[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 63: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void +SHA256Transform(u_int32_t state[8], const u_int8_t data[SHA256_BLOCK_LENGTH]) +{ + u_int32_t a, b, c, d, e, f, g, h, s0, s1; + u_int32_t T1, T2, W256[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + BE_8_TO_32(W256[j], data); + data += 4; + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ +DEF_WEAK(SHA256Transform); + +void +SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + u_int64_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memcpy(&context->buffer[usedspace], data, freespace); + context->bitcount[0] += freespace << 3; + len -= freespace; + data += freespace; + SHA256Transform(context->state.st32, context->buffer); + } else { + /* The buffer is not yet full */ + memcpy(&context->buffer[usedspace], data, len); + context->bitcount[0] += (u_int64_t)len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256Transform(context->state.st32, data); + context->bitcount[0] += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + memcpy(context->buffer, data, len); + context->bitcount[0] += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} +DEF_WEAK(SHA256Update); + +void +SHA256Pad(SHA2_CTX *context) +{ + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, + SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, + SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256Transform(context->state.st32, context->buffer); + + /* Prepare for last transform: */ + memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits) in big endian format: */ + BE_64_TO_8(&context->buffer[SHA256_SHORT_BLOCK_LENGTH], + context->bitcount[0]); + + /* Final transform: */ + SHA256Transform(context->state.st32, context->buffer); + + /* Clean up: */ + usedspace = 0; +} +DEF_WEAK(SHA256Pad); + +void +SHA256Final(u_int8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA256Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 8; i++) + BE_32_TO_8(digest + i * 4, context->state.st32[i]); +#else + memcpy(digest, context->state.st32, SHA256_DIGEST_LENGTH); +#endif + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA256Final); + + +/*** SHA-512: *********************************************************/ +void +SHA512Init(SHA2_CTX *context) +{ + memcpy(context->state.st64, sha512_initial_hash_value, + sizeof(sha512_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = context->bitcount[1] = 0; +} +DEF_WEAK(SHA512Init); + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \ + BE_8_TO_64(W512[j], data); \ + data += 8; \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + + +#define ROUND512(a,b,c,d,e,f,g,h) do { \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ + j++; \ +} while(0) + +void +SHA512Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) +{ + u_int64_t a, b, c, d, e, f, g, h, s0, s1; + u_int64_t T1, W512[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void +SHA512Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) +{ + u_int64_t a, b, c, d, e, f, g, h, s0, s1; + u_int64_t T1, T2, W512[16]; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + j = 0; + do { + BE_8_TO_64(W512[j], data); + data += 8; + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ +DEF_WEAK(SHA512Transform); + +void +SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + size_t freespace, usedspace; + + /* Calling with no data is valid (we do nothing) */ + if (len == 0) + return; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memcpy(&context->buffer[usedspace], data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + SHA512Transform(context->state.st64, context->buffer); + } else { + /* The buffer is not yet full */ + memcpy(&context->buffer[usedspace], data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA512Transform(context->state.st64, data); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + memcpy(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} +DEF_WEAK(SHA512Update); + +void +SHA512Pad(SHA2_CTX *context) +{ + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA512_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA512Transform(context->state.st64, context->buffer); + + /* And set-up for the last transform: */ + memset(context->buffer, 0, SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + memset(context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits) in big endian format: */ + BE_64_TO_8(&context->buffer[SHA512_SHORT_BLOCK_LENGTH], + context->bitcount[1]); + BE_64_TO_8(&context->buffer[SHA512_SHORT_BLOCK_LENGTH + 8], + context->bitcount[0]); + + /* Final transform: */ + SHA512Transform(context->state.st64, context->buffer); + + /* Clean up: */ + usedspace = 0; +} +DEF_WEAK(SHA512Pad); + +void +SHA512Final(u_int8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA512Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 8; i++) + BE_64_TO_8(digest + i * 8, context->state.st64[i]); +#else + memcpy(digest, context->state.st64, SHA512_DIGEST_LENGTH); +#endif + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA512Final); + +#if !defined(SHA2_SMALL) + +/*** SHA-384: *********************************************************/ +void +SHA384Init(SHA2_CTX *context) +{ + memcpy(context->state.st64, sha384_initial_hash_value, + sizeof(sha384_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = context->bitcount[1] = 0; +} +DEF_WEAK(SHA384Init); + +MAKE_CLONE(SHA384Transform, SHA512Transform); +MAKE_CLONE(SHA384Update, SHA512Update); +MAKE_CLONE(SHA384Pad, SHA512Pad); +DEF_WEAK(SHA384Transform); +DEF_WEAK(SHA384Update); +DEF_WEAK(SHA384Pad); + +/* Equivalent of MAKE_CLONE (which is a no-op) for SHA384 funcs */ +void +SHA384Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) +{ + SHA512Transform(state, data); +} + +void +SHA384Update(SHA2_CTX *context, const u_int8_t *data, size_t len) +{ + SHA512Update(context, data, len); +} + +void +SHA384Pad(SHA2_CTX *context) +{ + SHA512Pad(context); +} + +void +SHA384Final(u_int8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA384Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 6; i++) + BE_64_TO_8(digest + i * 8, context->state.st64[i]); +#else + memcpy(digest, context->state.st64, SHA384_DIGEST_LENGTH); +#endif + /* Zero out state data */ + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA384Final); + +#if 0 +/*** SHA-512/256: *********************************************************/ +void +SHA512_256Init(SHA2_CTX *context) +{ + memcpy(context->state.st64, sha512_256_initial_hash_value, + sizeof(sha512_256_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = context->bitcount[1] = 0; +} +DEF_WEAK(SHA512_256Init); + +MAKE_CLONE(SHA512_256Transform, SHA512Transform); +MAKE_CLONE(SHA512_256Update, SHA512Update); +MAKE_CLONE(SHA512_256Pad, SHA512Pad); +DEF_WEAK(SHA512_256Transform); +DEF_WEAK(SHA512_256Update); +DEF_WEAK(SHA512_256Pad); + +void +SHA512_256Final(u_int8_t digest[SHA512_256_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA512_256Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 4; i++) + BE_64_TO_8(digest + i * 8, context->state.st64[i]); +#else + memcpy(digest, context->state.st64, SHA512_256_DIGEST_LENGTH); +#endif + /* Zero out state data */ + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA512_256Final); +#endif /* !defined(SHA2_SMALL) */ +#endif /* 0 */ + +#endif /* HAVE_SHA{256,384,512}UPDATE */ diff --git a/aosp/external/openssh/openbsd-compat/sha2.h b/aosp/external/openssh/openbsd-compat/sha2.h new file mode 100644 index 0000000000000000000000000000000000000000..d051e96e83c91a32a03e123186288fc9df6051e0 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sha2.h @@ -0,0 +1,174 @@ +/* $OpenBSD: sha2.h,v 1.10 2016/09/03 17:00:29 tedu Exp $ */ + +/* + * FILE: sha2.h + * AUTHOR: Aaron D. Gifford + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ + */ + +/* OPENBSD ORIGINAL: include/sha2.h */ + +#ifndef _SSHSHA2_H +#define _SSHSHA2_H + +#include "includes.h" + +#if !defined(HAVE_SHA256UPDATE) || !defined(HAVE_SHA384UPDATE) || \ + !defined(HAVE_SHA512UPDATE) + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +#define SHA224_BLOCK_LENGTH 64 +#define SHA224_DIGEST_LENGTH 28 +#define SHA224_DIGEST_STRING_LENGTH (SHA224_DIGEST_LENGTH * 2 + 1) +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA384_BLOCK_LENGTH 128 +#define SHA384_DIGEST_LENGTH 48 +#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) +#define SHA512_256_BLOCK_LENGTH 128 +#define SHA512_256_DIGEST_LENGTH 32 +#define SHA512_256_DIGEST_STRING_LENGTH (SHA512_256_DIGEST_LENGTH * 2 + 1) + + +/*** SHA-224/256/384/512 Context Structure *******************************/ +typedef struct _SHA2_CTX { + union { + u_int32_t st32[8]; + u_int64_t st64[8]; + } state; + u_int64_t bitcount[2]; + u_int8_t buffer[SHA512_BLOCK_LENGTH]; +} SHA2_CTX; + +#if 0 +__BEGIN_DECLS +void SHA224Init(SHA2_CTX *); +void SHA224Transform(u_int32_t state[8], const u_int8_t [SHA224_BLOCK_LENGTH]); +void SHA224Update(SHA2_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA224Pad(SHA2_CTX *); +void SHA224Final(u_int8_t [SHA224_DIGEST_LENGTH], SHA2_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA224_DIGEST_LENGTH))); +char *SHA224End(SHA2_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA224_DIGEST_STRING_LENGTH))); +char *SHA224File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA224_DIGEST_STRING_LENGTH))); +char *SHA224FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA224_DIGEST_STRING_LENGTH))); +char *SHA224Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA224_DIGEST_STRING_LENGTH))); +#endif /* 0 */ + +#ifndef HAVE_SHA256UPDATE +void SHA256Init(SHA2_CTX *); +void SHA256Transform(u_int32_t state[8], const u_int8_t [SHA256_BLOCK_LENGTH]); +void SHA256Update(SHA2_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA256Pad(SHA2_CTX *); +void SHA256Final(u_int8_t [SHA256_DIGEST_LENGTH], SHA2_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA256_DIGEST_LENGTH))); +char *SHA256End(SHA2_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA256_DIGEST_STRING_LENGTH))); +char *SHA256File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA256_DIGEST_STRING_LENGTH))); +char *SHA256FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA256_DIGEST_STRING_LENGTH))); +char *SHA256Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA256_DIGEST_STRING_LENGTH))); +#endif /* HAVE_SHA256UPDATE */ + +#ifndef HAVE_SHA384UPDATE +void SHA384Init(SHA2_CTX *); +void SHA384Transform(u_int64_t state[8], const u_int8_t [SHA384_BLOCK_LENGTH]); +void SHA384Update(SHA2_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA384Pad(SHA2_CTX *); +void SHA384Final(u_int8_t [SHA384_DIGEST_LENGTH], SHA2_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA384_DIGEST_LENGTH))); +char *SHA384End(SHA2_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA384_DIGEST_STRING_LENGTH))); +char *SHA384File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA384_DIGEST_STRING_LENGTH))); +char *SHA384FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA384_DIGEST_STRING_LENGTH))); +char *SHA384Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA384_DIGEST_STRING_LENGTH))); +#endif /* HAVE_SHA384UPDATE */ + +#ifndef HAVE_SHA512UPDATE +void SHA512Init(SHA2_CTX *); +void SHA512Transform(u_int64_t state[8], const u_int8_t [SHA512_BLOCK_LENGTH]); +void SHA512Update(SHA2_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA512Pad(SHA2_CTX *); +void SHA512Final(u_int8_t [SHA512_DIGEST_LENGTH], SHA2_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA512_DIGEST_LENGTH))); +char *SHA512End(SHA2_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA512_DIGEST_STRING_LENGTH))); +char *SHA512File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA512_DIGEST_STRING_LENGTH))); +char *SHA512FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA512_DIGEST_STRING_LENGTH))); +char *SHA512Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA512_DIGEST_STRING_LENGTH))); +#endif /* HAVE_SHA512UPDATE */ + +#if 0 +void SHA512_256Init(SHA2_CTX *); +void SHA512_256Transform(u_int64_t state[8], const u_int8_t [SHA512_256_BLOCK_LENGTH]); +void SHA512_256Update(SHA2_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA512_256Pad(SHA2_CTX *); +void SHA512_256Final(u_int8_t [SHA512_256_DIGEST_LENGTH], SHA2_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA512_256_DIGEST_LENGTH))); +char *SHA512_256End(SHA2_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH))); +char *SHA512_256File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH))); +char *SHA512_256FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH))); +char *SHA512_256Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA512_256_DIGEST_STRING_LENGTH))); +__END_DECLS +#endif /* 0 */ + +#endif /* HAVE_SHA{256,384,512}UPDATE */ + +#endif /* _SSHSHA2_H */ diff --git a/aosp/external/openssh/openbsd-compat/sigact.c b/aosp/external/openssh/openbsd-compat/sigact.c new file mode 100644 index 0000000000000000000000000000000000000000..d67845cf1a5466803b4357e2d71e89aecc3597a6 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sigact.c @@ -0,0 +1,132 @@ +/* $OpenBSD: sigaction.c,v 1.4 2001/01/22 18:01:48 millert Exp $ */ + +/**************************************************************************** + * Copyright (c) 1998,2000 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim 1992,1995 * + * and: Eric S. Raymond * + ****************************************************************************/ + +/* OPENBSD ORIGINAL: lib/libcurses/base/sigaction.c */ + +#include "includes.h" +#include +#include +#include "sigact.h" + +/* This file provides sigaction() emulation using sigvec() */ +/* Use only if this is non POSIX system */ + +#if !HAVE_SIGACTION && HAVE_SIGVEC + +int +sigaction(int sig, struct sigaction *sigact, struct sigaction *osigact) +{ + return sigvec(sig, sigact ? &sigact->sv : NULL, + osigact ? &osigact->sv : NULL); +} + +int +sigemptyset (sigset_t *mask) +{ + if (!mask) { + errno = EINVAL; + return -1; + } + *mask = 0; + return 0; +} + +int +sigprocmask (int mode, sigset_t *mask, sigset_t *omask) +{ + sigset_t current = sigsetmask(0); + + if (!mask) { + errno = EINVAL; + return -1; + } + + if (omask) + *omask = current; + + if (mode == SIG_BLOCK) + current |= *mask; + else if (mode == SIG_UNBLOCK) + current &= ~*mask; + else if (mode == SIG_SETMASK) + current = *mask; + + sigsetmask(current); + return 0; +} + +int +sigsuspend (sigset_t *mask) +{ + if (!mask) { + errno = EINVAL; + return -1; + } + return sigpause(*mask); +} + +int +sigdelset (sigset_t *mask, int sig) +{ + if (!mask) { + errno = EINVAL; + return -1; + } + *mask &= ~sigmask(sig); + return 0; +} + +int +sigaddset (sigset_t *mask, int sig) +{ + if (!mask) { + errno = EINVAL; + return -1; + } + *mask |= sigmask(sig); + return 0; +} + +int +sigismember (sigset_t *mask, int sig) +{ + if (!mask) { + errno = EINVAL; + return -1; + } + return (*mask & sigmask(sig)) != 0; +} + +#endif diff --git a/aosp/external/openssh/openbsd-compat/sigact.h b/aosp/external/openssh/openbsd-compat/sigact.h new file mode 100644 index 0000000000000000000000000000000000000000..db96d0a5c58b40d466a92c2b577fb39853585291 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sigact.h @@ -0,0 +1,90 @@ +/* $OpenBSD: SigAction.h,v 1.3 2001/01/22 18:01:32 millert Exp $ */ + +/**************************************************************************** + * Copyright (c) 1998,2000 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ + +/**************************************************************************** + * Author: Zeyd M. Ben-Halim 1992,1995 * + * and: Eric S. Raymond * + ****************************************************************************/ + +/* + * $From: SigAction.h,v 1.6 2000/12/10 02:36:10 tom Exp $ + * + * This file exists to handle non-POSIX systems which don't have , + * and usually no sigaction() nor + */ + +/* OPENBSD ORIGINAL: lib/libcurses/SigAction.h */ + +#ifndef _SIGACTION_H +#define _SIGACTION_H + +#if !defined(HAVE_SIGACTION) && defined(HAVE_SIGVEC) + +#undef SIG_BLOCK +#define SIG_BLOCK 00 + +#undef SIG_UNBLOCK +#define SIG_UNBLOCK 01 + +#undef SIG_SETMASK +#define SIG_SETMASK 02 + +/* + * is in the Linux 1.2.8 + gcc 2.7.0 configuration, + * and is useful for testing this header file. + */ +#if HAVE_BSD_SIGNAL_H +# include +#endif + +struct sigaction +{ + struct sigvec sv; +}; + +typedef unsigned long sigset_t; + +#undef sa_mask +#define sa_mask sv.sv_mask +#undef sa_handler +#define sa_handler sv.sv_handler +#undef sa_flags +#define sa_flags sv.sv_flags + +int sigaction(int sig, struct sigaction *sigact, struct sigaction *osigact); +int sigprocmask (int how, sigset_t *mask, sigset_t *omask); +int sigemptyset (sigset_t *mask); +int sigsuspend (sigset_t *mask); +int sigdelset (sigset_t *mask, int sig); +int sigaddset (sigset_t *mask, int sig); + +#endif /* !defined(HAVE_SIGACTION) && defined(HAVE_SIGVEC) */ + +#endif /* !defined(_SIGACTION_H) */ diff --git a/aosp/external/openssh/openbsd-compat/strcasestr.c b/aosp/external/openssh/openbsd-compat/strcasestr.c new file mode 100644 index 0000000000000000000000000000000000000000..4c4d1475a4b0eab7865a17ff1222e8e7b9b859a7 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strcasestr.c @@ -0,0 +1,69 @@ +/* $OpenBSD: strcasestr.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */ +/* $NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strcasestr.c */ + +#include "includes.h" + +#ifndef HAVE_STRCASESTR + +#include +#include + +/* + * Find the first occurrence of find in s, ignore case. + */ +char * +strcasestr(const char *s, const char *find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = (char)tolower((unsigned char)c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char)tolower((unsigned char)sc) != c); + } while (strncasecmp(s, find, len) != 0); + s--; + } + return ((char *)s); +} +DEF_WEAK(strcasestr); + +#endif diff --git a/aosp/external/openssh/openbsd-compat/strlcat.c b/aosp/external/openssh/openbsd-compat/strlcat.c new file mode 100644 index 0000000000000000000000000000000000000000..bcc1b61ad885c12baa76e7191fe017e52b9ea745 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strlcat.c @@ -0,0 +1,62 @@ +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ + +#include "includes.h" +#ifndef HAVE_STRLCAT + +#include +#include + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCAT */ diff --git a/aosp/external/openssh/openbsd-compat/strlcpy.c b/aosp/external/openssh/openbsd-compat/strlcpy.c new file mode 100644 index 0000000000000000000000000000000000000000..b4b1b6015a28e5075dcfb1f20b5b58562e4eff1b --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strlcpy.c @@ -0,0 +1,58 @@ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ + +#include "includes.h" +#ifndef HAVE_STRLCPY + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCPY */ diff --git a/aosp/external/openssh/openbsd-compat/strmode.c b/aosp/external/openssh/openbsd-compat/strmode.c new file mode 100644 index 0000000000000000000000000000000000000000..4a816142264a34d87a977a6d0c0c102973194e86 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strmode.c @@ -0,0 +1,148 @@ +/* $OpenBSD: strmode.c,v 1.7 2005/08/08 08:05:37 espie Exp $ */ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strmode.c */ + +#include "includes.h" +#ifndef HAVE_STRMODE + +#include +#include +#include + +/* XXX mode should be mode_t */ + +void +strmode(int mode, char *p) +{ + /* print type */ + switch (mode & S_IFMT) { + case S_IFDIR: /* directory */ + *p++ = 'd'; + break; + case S_IFCHR: /* character special */ + *p++ = 'c'; + break; + case S_IFBLK: /* block special */ + *p++ = 'b'; + break; + case S_IFREG: /* regular */ + *p++ = '-'; + break; + case S_IFLNK: /* symbolic link */ + *p++ = 'l'; + break; +#ifdef S_IFSOCK + case S_IFSOCK: /* socket */ + *p++ = 's'; + break; +#endif +#ifdef S_IFIFO + case S_IFIFO: /* fifo */ + *p++ = 'p'; + break; +#endif + default: /* unknown */ + *p++ = '?'; + break; + } + /* usr */ + if (mode & S_IRUSR) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWUSR) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXUSR | S_ISUID)) { + case 0: + *p++ = '-'; + break; + case S_IXUSR: + *p++ = 'x'; + break; + case S_ISUID: + *p++ = 'S'; + break; + case S_IXUSR | S_ISUID: + *p++ = 's'; + break; + } + /* group */ + if (mode & S_IRGRP) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWGRP) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXGRP | S_ISGID)) { + case 0: + *p++ = '-'; + break; + case S_IXGRP: + *p++ = 'x'; + break; + case S_ISGID: + *p++ = 'S'; + break; + case S_IXGRP | S_ISGID: + *p++ = 's'; + break; + } + /* other */ + if (mode & S_IROTH) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWOTH) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXOTH | S_ISVTX)) { + case 0: + *p++ = '-'; + break; + case S_IXOTH: + *p++ = 'x'; + break; + case S_ISVTX: + *p++ = 'T'; + break; + case S_IXOTH | S_ISVTX: + *p++ = 't'; + break; + } + *p++ = ' '; /* will be a '+' if ACL's implemented */ + *p = '\0'; +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/strndup.c b/aosp/external/openssh/openbsd-compat/strndup.c new file mode 100644 index 0000000000000000000000000000000000000000..30ac6f04622d36071ff6441d33660c37facbb097 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strndup.c @@ -0,0 +1,43 @@ +/* $OpenBSD: strndup.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */ + +/* + * Copyright (c) 2010 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" +#if !defined(HAVE_STRNDUP) || defined(BROKEN_STRNDUP) +#include + +#include +#include +#include + +char * +strndup(const char *str, size_t maxlen) +{ + char *copy; + size_t len; + + len = strnlen(str, maxlen); + copy = malloc(len + 1); + if (copy != NULL) { + (void)memcpy(copy, str, len); + copy[len] = '\0'; + } + + return copy; +} +DEF_WEAK(strndup); +#endif /* HAVE_STRNDUP */ diff --git a/aosp/external/openssh/openbsd-compat/strnlen.c b/aosp/external/openssh/openbsd-compat/strnlen.c new file mode 100644 index 0000000000000000000000000000000000000000..7ad3573a6277621f609a67e4fc1d35f0bcd38583 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strnlen.c @@ -0,0 +1,37 @@ +/* $OpenBSD: strnlen.c,v 1.3 2010/06/02 12:58:12 millert Exp $ */ + +/* + * Copyright (c) 2010 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strnlen.c */ + +#include "includes.h" +#if !defined(HAVE_STRNLEN) || defined(BROKEN_STRNLEN) +#include + +#include + +size_t +strnlen(const char *str, size_t maxlen) +{ + const char *cp; + + for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) + ; + + return (size_t)(cp - str); +} +#endif diff --git a/aosp/external/openssh/openbsd-compat/strptime.c b/aosp/external/openssh/openbsd-compat/strptime.c new file mode 100644 index 0000000000000000000000000000000000000000..d8d83d9079f7ecd013ea7a6c5b69c372ae6082a8 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strptime.c @@ -0,0 +1,401 @@ +/* $OpenBSD: strptime.c,v 1.12 2008/06/26 05:42:05 ray Exp $ */ +/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/time/strptime.c */ + +#include "includes.h" + +#ifndef HAVE_STRPTIME + +#define TM_YEAR_BASE 1900 /* from tzfile.h */ + +#include +#include +#include +#include + +/* #define _ctloc(x) (_CurrentTimeLocale->x) */ + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define _ALT_E 0x01 +#define _ALT_O 0x02 +#define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } + + +static int _conv_num(const unsigned char **, int *, int, int); +static char *_strptime(const char *, const char *, struct tm *, int); + + +char * +strptime(const char *buf, const char *fmt, struct tm *tm) +{ + return(_strptime(buf, fmt, tm, 1)); +} + +static char * +_strptime(const char *buf, const char *fmt, struct tm *tm, int initialize) +{ + unsigned char c; + const unsigned char *bp; + size_t len; + int alt_format, i; + static int century, relyear; + + if (initialize) { + century = TM_YEAR_BASE; + relyear = -1; + } + + bp = (unsigned char *)buf; + while ((c = *fmt) != '\0') { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + + /* Eat up white-space. */ + if (isspace(c)) { + while (isspace(*bp)) + bp++; + + fmt++; + continue; + } + + if ((c = *fmt++) != '%') + goto literal; + + +again: switch (c = *fmt++) { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return (NULL); + + break; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + _LEGAL_ALT(0); + alt_format |= _ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + _LEGAL_ALT(0); + alt_format |= _ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ +#if 0 + case 'c': /* Date and time, using the locale's format. */ + _LEGAL_ALT(_ALT_E); + if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0))) + return (NULL); + break; +#endif + case 'D': /* The date as "%m/%d/%y". */ + _LEGAL_ALT(0); + if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0))) + return (NULL); + break; + + case 'R': /* The time as "%H:%M". */ + _LEGAL_ALT(0); + if (!(bp = _strptime(bp, "%H:%M", tm, 0))) + return (NULL); + break; + + case 'r': /* The time as "%I:%M:%S %p". */ + _LEGAL_ALT(0); + if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0))) + return (NULL); + break; + + case 'T': /* The time as "%H:%M:%S". */ + _LEGAL_ALT(0); + if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0))) + return (NULL); + break; +#if 0 + case 'X': /* The time, using the locale's format. */ + _LEGAL_ALT(_ALT_E); + if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0))) + return (NULL); + break; + + case 'x': /* The date, using the locale's format. */ + _LEGAL_ALT(_ALT_E); + if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0))) + return (NULL); + break; +#endif + /* + * "Elementary" conversion rules. + */ +#if 0 + case 'A': /* The day of week, using the locale's form. */ + case 'a': + _LEGAL_ALT(0); + for (i = 0; i < 7; i++) { + /* Full name. */ + len = strlen(_ctloc(day[i])); + if (strncasecmp(_ctloc(day[i]), bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(_ctloc(abday[i])); + if (strncasecmp(_ctloc(abday[i]), bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 7) + return (NULL); + + tm->tm_wday = i; + bp += len; + break; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + _LEGAL_ALT(0); + for (i = 0; i < 12; i++) { + /* Full name. */ + len = strlen(_ctloc(mon[i])); + if (strncasecmp(_ctloc(mon[i]), bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(_ctloc(abmon[i])); + if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 12) + return (NULL); + + tm->tm_mon = i; + bp += len; + break; +#endif + + case 'C': /* The century number. */ + _LEGAL_ALT(_ALT_E); + if (!(_conv_num(&bp, &i, 0, 99))) + return (NULL); + + century = i * 100; + break; + + case 'd': /* The day of month. */ + case 'e': + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_mday, 1, 31))) + return (NULL); + break; + + case 'k': /* The hour (24-hour clock representation). */ + _LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_hour, 0, 23))) + return (NULL); + break; + + case 'l': /* The hour (12-hour clock representation). */ + _LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_hour, 1, 12))) + return (NULL); + break; + + case 'j': /* The day of year. */ + _LEGAL_ALT(0); + if (!(_conv_num(&bp, &tm->tm_yday, 1, 366))) + return (NULL); + tm->tm_yday--; + break; + + case 'M': /* The minute. */ + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_min, 0, 59))) + return (NULL); + break; + + case 'm': /* The month. */ + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_mon, 1, 12))) + return (NULL); + tm->tm_mon--; + break; + +#if 0 + case 'p': /* The locale's equivalent of AM/PM. */ + _LEGAL_ALT(0); + /* AM? */ + len = strlen(_ctloc(am_pm[0])); + if (strncasecmp(_ctloc(am_pm[0]), bp, len) == 0) { + if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */ + return (NULL); + else if (tm->tm_hour == 12) + tm->tm_hour = 0; + + bp += len; + break; + } + /* PM? */ + len = strlen(_ctloc(am_pm[1])); + if (strncasecmp(_ctloc(am_pm[1]), bp, len) == 0) { + if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */ + return (NULL); + else if (tm->tm_hour < 12) + tm->tm_hour += 12; + + bp += len; + break; + } + + /* Nothing matched. */ + return (NULL); +#endif + case 'S': /* The seconds. */ + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_sec, 0, 61))) + return (NULL); + break; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + _LEGAL_ALT(_ALT_O); + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!(_conv_num(&bp, &i, 0, 53))) + return (NULL); + break; + + case 'w': /* The day of week, beginning on sunday. */ + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &tm->tm_wday, 0, 6))) + return (NULL); + break; + + case 'Y': /* The year. */ + _LEGAL_ALT(_ALT_E); + if (!(_conv_num(&bp, &i, 0, 9999))) + return (NULL); + + relyear = -1; + tm->tm_year = i - TM_YEAR_BASE; + break; + + case 'y': /* The year within the century (2 digits). */ + _LEGAL_ALT(_ALT_E | _ALT_O); + if (!(_conv_num(&bp, &relyear, 0, 99))) + return (NULL); + break; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + _LEGAL_ALT(0); + while (isspace(*bp)) + bp++; + break; + + + default: /* Unknown/unsupported conversion. */ + return (NULL); + } + + + } + + /* + * We need to evaluate the two digit year spec (%y) + * last as we can get a century spec (%C) at any time. + */ + if (relyear != -1) { + if (century == TM_YEAR_BASE) { + if (relyear <= 68) + tm->tm_year = relyear + 2000 - TM_YEAR_BASE; + else + tm->tm_year = relyear + 1900 - TM_YEAR_BASE; + } else { + tm->tm_year = relyear + century - TM_YEAR_BASE; + } + } + + return ((char *)bp); +} + + +static int +_conv_num(const unsigned char **buf, int *dest, int llim, int ulim) +{ + int result = 0; + int rulim = ulim; + + if (**buf < '0' || **buf > '9') + return (0); + + /* we use rulim to break out of the loop when we run out of digits */ + do { + result *= 10; + result += *(*buf)++ - '0'; + rulim /= 10; + } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); + + if (result < llim || result > ulim) + return (0); + + *dest = result; + return (1); +} + +#endif /* HAVE_STRPTIME */ + diff --git a/aosp/external/openssh/openbsd-compat/strsep.c b/aosp/external/openssh/openbsd-compat/strsep.c new file mode 100644 index 0000000000000000000000000000000000000000..b36eb8fdad704ec8dd4dd41998e66b17f4ceb7d6 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strsep.c @@ -0,0 +1,79 @@ +/* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strsep.c */ + +#include "includes.h" + +#if !defined(HAVE_STRSEP) + +#include +#include + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +#endif /* !defined(HAVE_STRSEP) */ diff --git a/aosp/external/openssh/openbsd-compat/strtoll.c b/aosp/external/openssh/openbsd-compat/strtoll.c new file mode 100644 index 0000000000000000000000000000000000000000..f629303885989bc99a1e51d38c31e7d8eb812a4c --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strtoll.c @@ -0,0 +1,148 @@ +/* $OpenBSD: strtoll.c,v 1.6 2005/11/10 10:00:17 espie Exp $ */ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtoll.c */ + +#include "includes.h" +#ifndef HAVE_STRTOLL + +#include + +#include +#include +#include +#include + +/* + * Convert a string to a long long. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +long long +strtoll(const char *nptr, char **endptr, int base) +{ + const char *s; + long long acc, cutoff; + int c; + int neg, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = nptr; + do { + c = (unsigned char) *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for long longs is + * [-9223372036854775808..9223372036854775807] and the input base + * is 10, cutoff will be set to 922337203685477580 and cutlim to + * either 7 (neg==0) or 8 (neg==1), meaning that if we have + * accumulated a value > 922337203685477580, or equal but the + * next digit is > 7 (or 8), the number is too big, and we will + * return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? LLONG_MIN : LLONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char) *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LLONG_MIN; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc -= c; + } + } else { + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LLONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return (acc); +} +#endif /* HAVE_STRTOLL */ diff --git a/aosp/external/openssh/openbsd-compat/strtonum.c b/aosp/external/openssh/openbsd-compat/strtonum.c new file mode 100644 index 0000000000000000000000000000000000000000..130d89684e9022ee5dc65fcdbefde4737c08befa --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strtonum.c @@ -0,0 +1,72 @@ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ + +#include "includes.h" + +#ifndef HAVE_STRTONUM +#include +#include +#include + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + char *ep; + int error = 0; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) + error = INVALID; + else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} + +#endif /* HAVE_STRTONUM */ diff --git a/aosp/external/openssh/openbsd-compat/strtoul.c b/aosp/external/openssh/openbsd-compat/strtoul.c new file mode 100644 index 0000000000000000000000000000000000000000..8219c8391b31ad54cf868836ae2afc76de974682 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strtoul.c @@ -0,0 +1,108 @@ +/* $OpenBSD: strtoul.c,v 1.7 2005/08/08 08:05:37 espie Exp $ */ +/* + * Copyright (c) 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtoul.c */ + +#include "includes.h" +#ifndef HAVE_STRTOUL + +#include +#include +#include +#include + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long +strtoul(const char *nptr, char **endptr, int base) +{ + const char *s; + unsigned long acc, cutoff; + int c; + int neg, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + s = nptr; + do { + c = (unsigned char) *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + cutoff = ULONG_MAX / (unsigned long)base; + cutlim = ULONG_MAX % (unsigned long)base; + for (acc = 0, any = 0;; c = (unsigned char) *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (acc > cutoff || acc == cutoff && c > cutlim) { + any = -1; + acc = ULONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= (unsigned long)base; + acc += c; + } + } + if (neg && any > 0) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return (acc); +} +#endif /* !HAVE_STRTOUL */ diff --git a/aosp/external/openssh/openbsd-compat/strtoull.c b/aosp/external/openssh/openbsd-compat/strtoull.c new file mode 100644 index 0000000000000000000000000000000000000000..f7c818c527e953cfaf1e1cdf4fa4415fe39052a4 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/strtoull.c @@ -0,0 +1,110 @@ +/* $OpenBSD: strtoull.c,v 1.5 2005/08/08 08:05:37 espie Exp $ */ +/*- + * Copyright (c) 1992 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtoull.c */ + +#include "includes.h" +#ifndef HAVE_STRTOULL + +#include + +#include +#include +#include +#include + +/* + * Convert a string to an unsigned long long. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long long +strtoull(const char *nptr, char **endptr, int base) +{ + const char *s; + unsigned long long acc, cutoff; + int c; + int neg, any, cutlim; + + /* + * See strtoq for comments as to the logic used. + */ + s = nptr; + do { + c = (unsigned char) *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + cutoff = ULLONG_MAX / (unsigned long long)base; + cutlim = ULLONG_MAX % (unsigned long long)base; + for (acc = 0, any = 0;; c = (unsigned char) *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = ULLONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= (unsigned long long)base; + acc += c; + } + } + if (neg && any > 0) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return (acc); +} +#endif /* !HAVE_STRTOULL */ diff --git a/aosp/external/openssh/openbsd-compat/sys-queue.h b/aosp/external/openssh/openbsd-compat/sys-queue.h new file mode 100644 index 0000000000000000000000000000000000000000..816c15cd42628be7e1a193f4d08a1a1fd272311e --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sys-queue.h @@ -0,0 +1,628 @@ +/* $OpenBSD: queue.h,v 1.45 2018/07/12 14:22:54 sashan Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +/* OPENBSD ORIGINAL: sys/sys/queue.h */ + +#ifndef _FAKE_QUEUE_H_ +#define _FAKE_QUEUE_H_ + +/* + * Require for OS/X and other platforms that have old/broken/incomplete + * . + */ +#undef CIRCLEQ_EMPTY +#undef CIRCLEQ_END +#undef CIRCLEQ_ENTRY +#undef CIRCLEQ_FIRST +#undef CIRCLEQ_FOREACH +#undef CIRCLEQ_FOREACH_REVERSE +#undef CIRCLEQ_HEAD +#undef CIRCLEQ_HEAD_INITIALIZER +#undef CIRCLEQ_INIT +#undef CIRCLEQ_INSERT_AFTER +#undef CIRCLEQ_INSERT_BEFORE +#undef CIRCLEQ_INSERT_HEAD +#undef CIRCLEQ_INSERT_TAIL +#undef CIRCLEQ_LAST +#undef CIRCLEQ_NEXT +#undef CIRCLEQ_PREV +#undef CIRCLEQ_REMOVE +#undef CIRCLEQ_REPLACE +#undef LIST_EMPTY +#undef LIST_END +#undef LIST_ENTRY +#undef LIST_FIRST +#undef LIST_FOREACH +#undef LIST_FOREACH_SAFE +#undef LIST_HEAD +#undef LIST_HEAD_INITIALIZER +#undef LIST_INIT +#undef LIST_INSERT_AFTER +#undef LIST_INSERT_BEFORE +#undef LIST_INSERT_HEAD +#undef LIST_NEXT +#undef LIST_REMOVE +#undef LIST_REPLACE +#undef SIMPLEQ_CONCAT +#undef SIMPLEQ_EMPTY +#undef SIMPLEQ_END +#undef SIMPLEQ_ENTRY +#undef SIMPLEQ_FIRST +#undef SIMPLEQ_FOREACH +#undef SIMPLEQ_FOREACH_SAFE +#undef SIMPLEQ_HEAD +#undef SIMPLEQ_HEAD_INITIALIZER +#undef SIMPLEQ_INIT +#undef SIMPLEQ_INSERT_AFTER +#undef SIMPLEQ_INSERT_HEAD +#undef SIMPLEQ_INSERT_TAIL +#undef SIMPLEQ_NEXT +#undef SIMPLEQ_REMOVE_AFTER +#undef SIMPLEQ_REMOVE_HEAD +#undef SLIST_EMPTY +#undef SLIST_END +#undef SLIST_ENTRY +#undef SLIST_FIRST +#undef SLIST_FOREACH +#undef SLIST_FOREACH_PREVPTR +#undef SLIST_FOREACH_SAFE +#undef SLIST_HEAD +#undef SLIST_HEAD_INITIALIZER +#undef SLIST_INIT +#undef SLIST_INSERT_AFTER +#undef SLIST_INSERT_HEAD +#undef SLIST_NEXT +#undef SLIST_REMOVE +#undef SLIST_REMOVE_AFTER +#undef SLIST_REMOVE_HEAD +#undef SLIST_REMOVE_NEXT +#undef TAILQ_CONCAT +#undef TAILQ_EMPTY +#undef TAILQ_END +#undef TAILQ_ENTRY +#undef TAILQ_FIRST +#undef TAILQ_FOREACH +#undef TAILQ_FOREACH_REVERSE +#undef TAILQ_FOREACH_REVERSE_SAFE +#undef TAILQ_FOREACH_SAFE +#undef TAILQ_HEAD +#undef TAILQ_HEAD_INITIALIZER +#undef TAILQ_INIT +#undef TAILQ_INSERT_AFTER +#undef TAILQ_INSERT_BEFORE +#undef TAILQ_INSERT_HEAD +#undef TAILQ_INSERT_TAIL +#undef TAILQ_LAST +#undef TAILQ_NEXT +#undef TAILQ_PREV +#undef TAILQ_REMOVE +#undef TAILQ_REPLACE + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues and XOR simple queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * An XOR simple queue is used in the same way as a regular simple queue. + * The difference is that the head structure also includes a "cookie" that + * is XOR'd with the queue pointer (first, last or next) to generate the + * real pointer value. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALID ((void *)-1) +#define _Q_INVALIDATE(a) (a) = _Q_INVALID +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + _Q_INVALIDATE((elm)->field.sle_next); \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/aosp/external/openssh/openbsd-compat/sys-tree.h b/aosp/external/openssh/openbsd-compat/sys-tree.h new file mode 100644 index 0000000000000000000000000000000000000000..7f7546ecdb0f0085c8f4be8f236d8eb7b9c4a2e5 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/sys-tree.h @@ -0,0 +1,755 @@ +/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: sys/sys/tree.h */ + +#include "config.h" +#ifdef NO_ATTRIBUTE_ON_RETURN_TYPE +# define __attribute__(x) +#endif + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ diff --git a/aosp/external/openssh/openbsd-compat/timingsafe_bcmp.c b/aosp/external/openssh/openbsd-compat/timingsafe_bcmp.c new file mode 100644 index 0000000000000000000000000000000000000000..7e28c0e2a58f043bb9bbe0fe73d961eee0da1610 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/timingsafe_bcmp.c @@ -0,0 +1,34 @@ +/* $OpenBSD: timingsafe_bcmp.c,v 1.1 2010/09/24 13:33:00 matthew Exp $ */ +/* + * Copyright (c) 2010 Damien Miller. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/timingsafe_bcmp.c */ + +#include "includes.h" +#ifndef HAVE_TIMINGSAFE_BCMP + +int +timingsafe_bcmp(const void *b1, const void *b2, size_t n) +{ + const unsigned char *p1 = b1, *p2 = b2; + int ret = 0; + + for (; n > 0; n--) + ret |= *p1++ ^ *p2++; + return (ret != 0); +} + +#endif /* TIMINGSAFE_BCMP */ diff --git a/aosp/external/openssh/openbsd-compat/vis.c b/aosp/external/openssh/openbsd-compat/vis.c new file mode 100644 index 0000000000000000000000000000000000000000..0e04ed025dde2dbca976cc6e5df3e6c0d88bbc53 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/vis.c @@ -0,0 +1,251 @@ +/* $OpenBSD: vis.c,v 1.25 2015/09/13 11:32:51 guenther Exp $ */ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/vis.c */ + +#include "includes.h" +#if !defined(HAVE_STRNVIS) || defined(BROKEN_STRNVIS) + +#include +#include +#include +#include +#include +#include + +#include "vis.h" + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') +#define isvisible(c,flag) \ + (((c) == '\\' || (flag & VIS_ALL) == 0) && \ + (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ + (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ + (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ + ((flag & VIS_SP) == 0 && (c) == ' ') || \ + ((flag & VIS_TAB) == 0 && (c) == '\t') || \ + ((flag & VIS_NL) == 0 && (c) == '\n') || \ + ((flag & VIS_SAFE) && ((c) == '\b' || \ + (c) == '\007' || (c) == '\r' || \ + isgraph((u_char)(c)))))) + +/* + * vis - visually encode characters + */ +char * +vis(char *dst, int c, int flag, int nextc) +{ + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) + *dst++ = '\\'; + *dst++ = c; + *dst = '\0'; + return (dst); + } + + if (flag & VIS_CSTYLE) { + switch(c) { + case '\n': + *dst++ = '\\'; + *dst++ = 'n'; + goto done; + case '\r': + *dst++ = '\\'; + *dst++ = 'r'; + goto done; + case '\b': + *dst++ = '\\'; + *dst++ = 'b'; + goto done; + case '\a': + *dst++ = '\\'; + *dst++ = 'a'; + goto done; + case '\v': + *dst++ = '\\'; + *dst++ = 'v'; + goto done; + case '\t': + *dst++ = '\\'; + *dst++ = 't'; + goto done; + case '\f': + *dst++ = '\\'; + *dst++ = 'f'; + goto done; + case ' ': + *dst++ = '\\'; + *dst++ = 's'; + goto done; + case '\0': + *dst++ = '\\'; + *dst++ = '0'; + if (isoctal(nextc)) { + *dst++ = '0'; + *dst++ = '0'; + } + goto done; + } + } + if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || + ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { + *dst++ = '\\'; + *dst++ = ((u_char)c >> 6 & 07) + '0'; + *dst++ = ((u_char)c >> 3 & 07) + '0'; + *dst++ = ((u_char)c & 07) + '0'; + goto done; + } + if ((flag & VIS_NOSLASH) == 0) + *dst++ = '\\'; + if (c & 0200) { + c &= 0177; + *dst++ = 'M'; + } + if (iscntrl((u_char)c)) { + *dst++ = '^'; + if (c == 0177) + *dst++ = '?'; + else + *dst++ = c + '@'; + } else { + *dst++ = '-'; + *dst++ = c; + } +done: + *dst = '\0'; + return (dst); +} +DEF_WEAK(vis); + +/* + * strvis, strnvis, strvisx - visually encode characters from src into dst + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strnvis will write no more than siz-1 bytes (and will NULL terminate). + * The number of bytes needed to fully encode the string is returned. + * + * Strvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strvis(char *dst, const char *src, int flag) +{ + char c; + char *start; + + for (start = dst; (c = *src);) + dst = vis(dst, c, flag, *++src); + *dst = '\0'; + return (dst - start); +} +DEF_WEAK(strvis); + +int +strnvis(char *dst, const char *src, size_t siz, int flag) +{ + char *start, *end; + char tbuf[5]; + int c, i; + + i = 0; + for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) { + /* need space for the extra '\\' */ + if (dst + 1 >= end) { + i = 2; + break; + } + *dst++ = '\\'; + } + i = 1; + *dst++ = c; + src++; + } else { + i = vis(tbuf, c, flag, *++src) - tbuf; + if (dst + i <= end) { + memcpy(dst, tbuf, i); + dst += i; + } else { + src--; + break; + } + } + } + if (siz > 0) + *dst = '\0'; + if (dst + i > end) { + /* adjust return value for truncation */ + while ((c = *src)) + dst += vis(tbuf, c, flag, *++src) - tbuf; + } + return (dst - start); +} + +int +stravis(char **outp, const char *src, int flag) +{ + char *buf; + int len, serrno; + + buf = reallocarray(NULL, 4, strlen(src) + 1); + if (buf == NULL) + return -1; + len = strvis(buf, src, flag); + serrno = errno; + *outp = realloc(buf, len + 1); + if (*outp == NULL) { + *outp = buf; + errno = serrno; + } + return (len); +} + +int +strvisx(char *dst, const char *src, size_t len, int flag) +{ + char c; + char *start; + + for (start = dst; len > 1; len--) { + c = *src; + dst = vis(dst, c, flag, *++src); + } + if (len) + dst = vis(dst, *src, flag, '\0'); + *dst = '\0'; + return (dst - start); +} + +#endif diff --git a/aosp/external/openssh/openbsd-compat/vis.h b/aosp/external/openssh/openbsd-compat/vis.h new file mode 100644 index 0000000000000000000000000000000000000000..2cdfd364b64adab6f8b7463112c362b6bc3c1f24 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/vis.h @@ -0,0 +1,98 @@ +/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ +/* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vis.h 5.9 (Berkeley) 4/3/91 + */ + +/* OPENBSD ORIGINAL: include/vis.h */ + +#include "includes.h" +#if !defined(HAVE_STRNVIS) || defined(BROKEN_STRNVIS) + +#ifndef _VIS_H_ +#define _VIS_H_ + +#include +#include + +/* + * to select alternate encoding format + */ +#define VIS_OCTAL 0x01 /* use octal \ddd format */ +#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ + +/* + * to alter set of characters encoded (default is to encode all + * non-graphic except space, tab, and newline). + */ +#define VIS_SP 0x04 /* also encode space */ +#define VIS_TAB 0x08 /* also encode tab */ +#define VIS_NL 0x10 /* also encode newline */ +#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) +#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ +#define VIS_DQ 0x200 /* backslash-escape double quotes */ +#define VIS_ALL 0x400 /* encode all characters */ + +/* + * other + */ +#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ +#define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ + +/* + * unvis return codes + */ +#define UNVIS_VALID 1 /* character valid */ +#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ +#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ +#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ +#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ + +/* + * unvis flags + */ +#define UNVIS_END 1 /* no more characters */ + +char *vis(char *, int, int, int); +int strvis(char *, const char *, int); +int stravis(char **, const char *, int); +int strnvis(char *, const char *, size_t, int) + __attribute__ ((__bounded__(__string__,1,3))); +int strvisx(char *, const char *, size_t, int) + __attribute__ ((__bounded__(__string__,1,3))); +int strunvis(char *, const char *); +int unvis(char *, char, int *, int); +ssize_t strnunvis(char *, const char *, size_t) + __attribute__ ((__bounded__(__string__,1,3))); + +#endif /* !_VIS_H_ */ + +#endif /* !HAVE_STRNVIS || BROKEN_STRNVIS */ diff --git a/aosp/external/openssh/openbsd-compat/xcrypt.c b/aosp/external/openssh/openbsd-compat/xcrypt.c new file mode 100644 index 0000000000000000000000000000000000000000..9cded66845cc239a9c763c57adfa7eb45ba29710 --- /dev/null +++ b/aosp/external/openssh/openbsd-compat/xcrypt.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003 Ben Lindstrom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +# if defined(HAVE_CRYPT_H) && !defined(HAVE_SECUREWARE) +# include +# endif + +# ifdef __hpux +# include +# include +# endif + +# ifdef HAVE_SECUREWARE +# include +# include +# include +# endif + +# if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) +# include +# endif + +# if defined(HAVE_GETPWANAM) && !defined(DISABLE_SHADOW) +# include +# include +# include +# endif + +# if defined(WITH_OPENSSL) && !defined(HAVE_CRYPT) && defined(HAVE_DES_CRYPT) +# include +# define crypt DES_crypt +# endif + +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + +/* + * Pick an appropriate password encryption type and salt for the running + * system by searching through accounts until we find one that has a valid + * salt. Usually this will be root unless the root account is locked out. + * If we don't find one we return a traditional DES-based salt. + */ +static const char * +pick_salt(void) +{ + struct passwd *pw; + char *passwd, *p; + size_t typelen; + static char salt[32]; + + if (salt[0] != '\0') + return salt; + strlcpy(salt, "xx", sizeof(salt)); + setpwent(); + while ((pw = getpwent()) != NULL) { + if ((passwd = shadow_pw(pw)) == NULL) + continue; + if (passwd[0] == '$' && (p = strrchr(passwd+1, '$')) != NULL) { + typelen = p - passwd + 1; + strlcpy(salt, passwd, MINIMUM(typelen, sizeof(salt))); + explicit_bzero(passwd, strlen(passwd)); + goto out; + } + } + out: + endpwent(); + return salt; +} + +char * +xcrypt(const char *password, const char *salt) +{ + char *crypted; + + /* + * If we don't have a salt we are encrypting a fake password for + * for timing purposes. Pick an appropriate salt. + */ + if (salt == NULL) + salt = pick_salt(); + +#if defined(__hpux) && !defined(HAVE_SECUREWARE) + if (iscomsec()) + crypted = bigcrypt(password, salt); + else + crypted = crypt(password, salt); +# elif defined(HAVE_SECUREWARE) + crypted = bigcrypt(password, salt); +# else + crypted = crypt(password, salt); +#endif + + return crypted; +} + +/* + * Handle shadowed password systems in a cleaner way for portable + * version. + */ + +char * +shadow_pw(struct passwd *pw) +{ + char *pw_password = pw->pw_passwd; + +# if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) + struct spwd *spw = getspnam(pw->pw_name); + + if (spw != NULL) + pw_password = spw->sp_pwdp; +# endif + +#ifdef USE_LIBIAF + return(get_iaf_password(pw)); +#endif + +# if defined(HAVE_GETPWANAM) && !defined(DISABLE_SHADOW) + struct passwd_adjunct *spw; + if (issecure() && (spw = getpwanam(pw->pw_name)) != NULL) + pw_password = spw->pwa_passwd; +# elif defined(HAVE_SECUREWARE) + struct pr_passwd *spw = getprpwnam(pw->pw_name); + + if (spw != NULL) + pw_password = spw->ufld.fd_encrypt; +# endif + + return pw_password; +} diff --git a/aosp/external/openssh/openssh.xml.in b/aosp/external/openssh/openssh.xml.in new file mode 100644 index 0000000000000000000000000000000000000000..8afe1d366c482a13f88723a015b9bc37344b976b --- /dev/null +++ b/aosp/external/openssh/openssh.xml.in @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aosp/external/openssh/opensshd.init.in b/aosp/external/openssh/opensshd.init.in new file mode 100644 index 0000000000000000000000000000000000000000..251724805f7f32b70321b46c06a272e79ed03fa3 --- /dev/null +++ b/aosp/external/openssh/opensshd.init.in @@ -0,0 +1,68 @@ +#!@STARTUP_SCRIPT_SHELL@ +# Donated code that was put under PD license. +# +# Stripped PRNGd out of it for the time being. + +umask 022 + +CAT=@CAT@ +KILL=@KILL@ + +prefix=@prefix@ +sysconfdir=@sysconfdir@ +piddir=@piddir@ + +SSHD=$prefix/sbin/sshd +PIDFILE=$piddir/sshd.pid +PidFile=`grep "^PidFile" ${sysconfdir}/sshd_config | tr "=" " " | awk '{print $2}'` +[ X$PidFile = X ] || PIDFILE=$PidFile +SSH_KEYGEN=$prefix/bin/ssh-keygen + +stop_service() { + if [ -r $PIDFILE -a ! -z ${PIDFILE} ]; then + PID=`${CAT} ${PIDFILE}` + fi + if [ ${PID:=0} -gt 1 -a ! "X$PID" = "X " ]; then + ${KILL} ${PID} + else + echo "Unable to read PID file" + fi +} + +start_service() { + # XXX We really should check if the service is already going, but + # XXX we will opt out at this time. - Bal + + # Check to see if we have keys that need to be made + ${SSH_KEYGEN} -A + + # Start SSHD + echo "starting $SSHD... \c" ; $SSHD + + sshd_rc=$? + if [ $sshd_rc -ne 0 ]; then + echo "$0: Error ${sshd_rc} starting ${SSHD}... bailing." + exit $sshd_rc + fi + echo done. +} + +case $1 in + +'start') + start_service + ;; + +'stop') + stop_service + ;; + +'restart') + stop_service + start_service + ;; + +*) + echo "$0: usage: $0 {start|stop|restart}" + ;; +esac diff --git a/aosp/external/openssh/packet.c b/aosp/external/openssh/packet.c new file mode 100644 index 0000000000000000000000000000000000000000..bde6c1045e8a6562c280f20f99bf97f3d90800fd --- /dev/null +++ b/aosp/external/openssh/packet.c @@ -0,0 +1,2719 @@ +/* $OpenBSD: packet.c,v 1.307 2022/01/22 00:49:34 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains code implementing the packet protocol and communication + * with the other side. This same code is used both on client and server side. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * SSH2 packet format added by Markus Friedl. + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include "openbsd-compat/sys-queue.h" +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include + +/* + * Explicitly include OpenSSL before zlib as some versions of OpenSSL have + * "free_func" in their headers, which zlib typedefs. + */ +#ifdef WITH_OPENSSL +# include +# include +# ifdef OPENSSL_HAS_ECC +# include +# endif +#endif + +#ifdef WITH_ZLIB +#include +#endif + +#include "xmalloc.h" +#include "compat.h" +#include "ssh2.h" +#include "cipher.h" +#include "sshkey.h" +#include "kex.h" +#include "digest.h" +#include "mac.h" +#include "log.h" +#include "canohost.h" +#include "misc.h" +#include "channels.h" +#include "ssh.h" +#include "packet.h" +#include "ssherr.h" +#include "sshbuf.h" + +#ifdef PACKET_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif + +#define PACKET_MAX_SIZE (256 * 1024) + +struct packet_state { + u_int32_t seqnr; + u_int32_t packets; + u_int64_t blocks; + u_int64_t bytes; +}; + +struct packet { + TAILQ_ENTRY(packet) next; + u_char type; + struct sshbuf *payload; +}; + +struct session_state { + /* + * This variable contains the file descriptors used for + * communicating with the other side. connection_in is used for + * reading; connection_out for writing. These can be the same + * descriptor, in which case it is assumed to be a socket. + */ + int connection_in; + int connection_out; + + /* Protocol flags for the remote side. */ + u_int remote_protocol_flags; + + /* Encryption context for receiving data. Only used for decryption. */ + struct sshcipher_ctx *receive_context; + + /* Encryption context for sending data. Only used for encryption. */ + struct sshcipher_ctx *send_context; + + /* Buffer for raw input data from the socket. */ + struct sshbuf *input; + + /* Buffer for raw output data going to the socket. */ + struct sshbuf *output; + + /* Buffer for the partial outgoing packet being constructed. */ + struct sshbuf *outgoing_packet; + + /* Buffer for the incoming packet currently being processed. */ + struct sshbuf *incoming_packet; + + /* Scratch buffer for packet compression/decompression. */ + struct sshbuf *compression_buffer; + +#ifdef WITH_ZLIB + /* Incoming/outgoing compression dictionaries */ + z_stream compression_in_stream; + z_stream compression_out_stream; +#endif + int compression_in_started; + int compression_out_started; + int compression_in_failures; + int compression_out_failures; + + /* default maximum packet size */ + u_int max_packet_size; + + /* Flag indicating whether this module has been initialized. */ + int initialized; + + /* Set to true if the connection is interactive. */ + int interactive_mode; + + /* Set to true if we are the server side. */ + int server_side; + + /* Set to true if we are authenticated. */ + int after_authentication; + + int keep_alive_timeouts; + + /* The maximum time that we will wait to send or receive a packet */ + int packet_timeout_ms; + + /* Session key information for Encryption and MAC */ + struct newkeys *newkeys[MODE_MAX]; + struct packet_state p_read, p_send; + + /* Volume-based rekeying */ + u_int64_t max_blocks_in, max_blocks_out, rekey_limit; + + /* Time-based rekeying */ + u_int32_t rekey_interval; /* how often in seconds */ + time_t rekey_time; /* time of last rekeying */ + + /* roundup current message to extra_pad bytes */ + u_char extra_pad; + + /* XXX discard incoming data after MAC error */ + u_int packet_discard; + size_t packet_discard_mac_already; + struct sshmac *packet_discard_mac; + + /* Used in packet_read_poll2() */ + u_int packlen; + + /* Used in packet_send2 */ + int rekeying; + + /* Used in ssh_packet_send_mux() */ + int mux; + + /* Used in packet_set_interactive */ + int set_interactive_called; + + /* Used in packet_set_maxsize */ + int set_maxsize_called; + + /* One-off warning about weak ciphers */ + int cipher_warning_done; + + /* Hook for fuzzing inbound packets */ + ssh_packet_hook_fn *hook_in; + void *hook_in_ctx; + + TAILQ_HEAD(, packet) outgoing; +}; + +struct ssh * +ssh_alloc_session_state(void) +{ + struct ssh *ssh = NULL; + struct session_state *state = NULL; + + if ((ssh = calloc(1, sizeof(*ssh))) == NULL || + (state = calloc(1, sizeof(*state))) == NULL || + (ssh->kex = kex_new()) == NULL || + (state->input = sshbuf_new()) == NULL || + (state->output = sshbuf_new()) == NULL || + (state->outgoing_packet = sshbuf_new()) == NULL || + (state->incoming_packet = sshbuf_new()) == NULL) + goto fail; + TAILQ_INIT(&state->outgoing); + TAILQ_INIT(&ssh->private_keys); + TAILQ_INIT(&ssh->public_keys); + state->connection_in = -1; + state->connection_out = -1; + state->max_packet_size = 32768; + state->packet_timeout_ms = -1; + state->p_send.packets = state->p_read.packets = 0; + state->initialized = 1; + /* + * ssh_packet_send2() needs to queue packets until + * we've done the initial key exchange. + */ + state->rekeying = 1; + ssh->state = state; + return ssh; + fail: + if (ssh) { + kex_free(ssh->kex); + free(ssh); + } + if (state) { + sshbuf_free(state->input); + sshbuf_free(state->output); + sshbuf_free(state->incoming_packet); + sshbuf_free(state->outgoing_packet); + free(state); + } + return NULL; +} + +void +ssh_packet_set_input_hook(struct ssh *ssh, ssh_packet_hook_fn *hook, void *ctx) +{ + ssh->state->hook_in = hook; + ssh->state->hook_in_ctx = ctx; +} + +/* Returns nonzero if rekeying is in progress */ +int +ssh_packet_is_rekeying(struct ssh *ssh) +{ + return ssh->state->rekeying || + (ssh->kex != NULL && ssh->kex->done == 0); +} + +/* + * Sets the descriptors used for communication. + */ +struct ssh * +ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out) +{ + struct session_state *state; + const struct sshcipher *none = cipher_by_name("none"); + int r; + + if (none == NULL) { + error_f("cannot load cipher 'none'"); + return NULL; + } + if (ssh == NULL) + ssh = ssh_alloc_session_state(); + if (ssh == NULL) { + error_f("could not allocate state"); + return NULL; + } + state = ssh->state; + state->connection_in = fd_in; + state->connection_out = fd_out; + if ((r = cipher_init(&state->send_context, none, + (const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || + (r = cipher_init(&state->receive_context, none, + (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) { + error_fr(r, "cipher_init failed"); + free(ssh); /* XXX need ssh_free_session_state? */ + return NULL; + } + state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; + /* + * Cache the IP address of the remote connection for use in error + * messages that might be generated after the connection has closed. + */ + (void)ssh_remote_ipaddr(ssh); + return ssh; +} + +void +ssh_packet_set_timeout(struct ssh *ssh, int timeout, int count) +{ + struct session_state *state = ssh->state; + + if (timeout <= 0 || count <= 0) { + state->packet_timeout_ms = -1; + return; + } + if ((INT_MAX / 1000) / count < timeout) + state->packet_timeout_ms = INT_MAX; + else + state->packet_timeout_ms = timeout * count * 1000; +} + +void +ssh_packet_set_mux(struct ssh *ssh) +{ + ssh->state->mux = 1; + ssh->state->rekeying = 0; + kex_free(ssh->kex); + ssh->kex = NULL; +} + +int +ssh_packet_get_mux(struct ssh *ssh) +{ + return ssh->state->mux; +} + +int +ssh_packet_set_log_preamble(struct ssh *ssh, const char *fmt, ...) +{ + va_list args; + int r; + + free(ssh->log_preamble); + if (fmt == NULL) + ssh->log_preamble = NULL; + else { + va_start(args, fmt); + r = vasprintf(&ssh->log_preamble, fmt, args); + va_end(args); + if (r < 0 || ssh->log_preamble == NULL) + return SSH_ERR_ALLOC_FAIL; + } + return 0; +} + +int +ssh_packet_stop_discard(struct ssh *ssh) +{ + struct session_state *state = ssh->state; + int r; + + if (state->packet_discard_mac) { + char buf[1024]; + size_t dlen = PACKET_MAX_SIZE; + + if (dlen > state->packet_discard_mac_already) + dlen -= state->packet_discard_mac_already; + memset(buf, 'a', sizeof(buf)); + while (sshbuf_len(state->incoming_packet) < dlen) + if ((r = sshbuf_put(state->incoming_packet, buf, + sizeof(buf))) != 0) + return r; + (void) mac_compute(state->packet_discard_mac, + state->p_read.seqnr, + sshbuf_ptr(state->incoming_packet), dlen, + NULL, 0); + } + logit("Finished discarding for %.200s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + return SSH_ERR_MAC_INVALID; +} + +static int +ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, + struct sshmac *mac, size_t mac_already, u_int discard) +{ + struct session_state *state = ssh->state; + int r; + + if (enc == NULL || !cipher_is_cbc(enc->cipher) || (mac && mac->etm)) { + if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) + return r; + return SSH_ERR_MAC_INVALID; + } + /* + * Record number of bytes over which the mac has already + * been computed in order to minimize timing attacks. + */ + if (mac && mac->enabled) { + state->packet_discard_mac = mac; + state->packet_discard_mac_already = mac_already; + } + if (sshbuf_len(state->input) >= discard) + return ssh_packet_stop_discard(ssh); + state->packet_discard = discard - sshbuf_len(state->input); + return 0; +} + +/* Returns 1 if remote host is connected via socket, 0 if not. */ + +int +ssh_packet_connection_is_on_socket(struct ssh *ssh) +{ + struct session_state *state; + struct sockaddr_storage from, to; + socklen_t fromlen, tolen; + + if (ssh == NULL || ssh->state == NULL) + return 0; + + state = ssh->state; + if (state->connection_in == -1 || state->connection_out == -1) + return 0; + /* filedescriptors in and out are the same, so it's a socket */ + if (state->connection_in == state->connection_out) + return 1; + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(state->connection_in, (struct sockaddr *)&from, + &fromlen) == -1) + return 0; + tolen = sizeof(to); + memset(&to, 0, sizeof(to)); + if (getpeername(state->connection_out, (struct sockaddr *)&to, + &tolen) == -1) + return 0; + if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0) + return 0; + if (from.ss_family != AF_INET && from.ss_family != AF_INET6) + return 0; + return 1; +} + +void +ssh_packet_get_bytes(struct ssh *ssh, u_int64_t *ibytes, u_int64_t *obytes) +{ + if (ibytes) + *ibytes = ssh->state->p_read.bytes; + if (obytes) + *obytes = ssh->state->p_send.bytes; +} + +int +ssh_packet_connection_af(struct ssh *ssh) +{ + return get_sock_af(ssh->state->connection_out); +} + +/* Sets the connection into non-blocking mode. */ + +void +ssh_packet_set_nonblocking(struct ssh *ssh) +{ + /* Set the socket into non-blocking mode. */ + set_nonblock(ssh->state->connection_in); + + if (ssh->state->connection_out != ssh->state->connection_in) + set_nonblock(ssh->state->connection_out); +} + +/* Returns the socket used for reading. */ + +int +ssh_packet_get_connection_in(struct ssh *ssh) +{ + return ssh->state->connection_in; +} + +/* Returns the descriptor used for writing. */ + +int +ssh_packet_get_connection_out(struct ssh *ssh) +{ + return ssh->state->connection_out; +} + +/* + * Returns the IP-address of the remote host as a string. The returned + * string must not be freed. + */ + +const char * +ssh_remote_ipaddr(struct ssh *ssh) +{ + int sock; + + /* Check whether we have cached the ipaddr. */ + if (ssh->remote_ipaddr == NULL) { + if (ssh_packet_connection_is_on_socket(ssh)) { + sock = ssh->state->connection_in; + ssh->remote_ipaddr = get_peer_ipaddr(sock); + ssh->remote_port = get_peer_port(sock); + ssh->local_ipaddr = get_local_ipaddr(sock); + ssh->local_port = get_local_port(sock); + } else { + ssh->remote_ipaddr = xstrdup("UNKNOWN"); + ssh->remote_port = 65535; + ssh->local_ipaddr = xstrdup("UNKNOWN"); + ssh->local_port = 65535; + } + } + return ssh->remote_ipaddr; +} + +/* Returns the port number of the remote host. */ + +int +ssh_remote_port(struct ssh *ssh) +{ + (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ + return ssh->remote_port; +} + +/* + * Returns the IP-address of the local host as a string. The returned + * string must not be freed. + */ + +const char * +ssh_local_ipaddr(struct ssh *ssh) +{ + (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ + return ssh->local_ipaddr; +} + +/* Returns the port number of the local host. */ + +int +ssh_local_port(struct ssh *ssh) +{ + (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ + return ssh->local_port; +} + +/* Returns the routing domain of the input socket, or NULL if unavailable */ +const char * +ssh_packet_rdomain_in(struct ssh *ssh) +{ + if (ssh->rdomain_in != NULL) + return ssh->rdomain_in; + if (!ssh_packet_connection_is_on_socket(ssh)) + return NULL; + ssh->rdomain_in = get_rdomain(ssh->state->connection_in); + return ssh->rdomain_in; +} + +/* Closes the connection and clears and frees internal data structures. */ + +static void +ssh_packet_close_internal(struct ssh *ssh, int do_close) +{ + struct session_state *state = ssh->state; + u_int mode; + + if (!state->initialized) + return; + state->initialized = 0; + if (do_close) { + if (state->connection_in == state->connection_out) { + close(state->connection_out); + } else { + close(state->connection_in); + close(state->connection_out); + } + } + sshbuf_free(state->input); + sshbuf_free(state->output); + sshbuf_free(state->outgoing_packet); + sshbuf_free(state->incoming_packet); + for (mode = 0; mode < MODE_MAX; mode++) { + kex_free_newkeys(state->newkeys[mode]); /* current keys */ + state->newkeys[mode] = NULL; + ssh_clear_newkeys(ssh, mode); /* next keys */ + } +#ifdef WITH_ZLIB + /* compression state is in shared mem, so we can only release it once */ + if (do_close && state->compression_buffer) { + sshbuf_free(state->compression_buffer); + if (state->compression_out_started) { + z_streamp stream = &state->compression_out_stream; + debug("compress outgoing: " + "raw data %llu, compressed %llu, factor %.2f", + (unsigned long long)stream->total_in, + (unsigned long long)stream->total_out, + stream->total_in == 0 ? 0.0 : + (double) stream->total_out / stream->total_in); + if (state->compression_out_failures == 0) + deflateEnd(stream); + } + if (state->compression_in_started) { + z_streamp stream = &state->compression_in_stream; + debug("compress incoming: " + "raw data %llu, compressed %llu, factor %.2f", + (unsigned long long)stream->total_out, + (unsigned long long)stream->total_in, + stream->total_out == 0 ? 0.0 : + (double) stream->total_in / stream->total_out); + if (state->compression_in_failures == 0) + inflateEnd(stream); + } + } +#endif /* WITH_ZLIB */ + cipher_free(state->send_context); + cipher_free(state->receive_context); + state->send_context = state->receive_context = NULL; + if (do_close) { + free(ssh->local_ipaddr); + ssh->local_ipaddr = NULL; + free(ssh->remote_ipaddr); + ssh->remote_ipaddr = NULL; + free(ssh->state); + ssh->state = NULL; + kex_free(ssh->kex); + ssh->kex = NULL; + } +} + +void +ssh_packet_close(struct ssh *ssh) +{ + ssh_packet_close_internal(ssh, 1); +} + +void +ssh_packet_clear_keys(struct ssh *ssh) +{ + ssh_packet_close_internal(ssh, 0); +} + +/* Sets remote side protocol flags. */ + +void +ssh_packet_set_protocol_flags(struct ssh *ssh, u_int protocol_flags) +{ + ssh->state->remote_protocol_flags = protocol_flags; +} + +/* Returns the remote protocol flags set earlier by the above function. */ + +u_int +ssh_packet_get_protocol_flags(struct ssh *ssh) +{ + return ssh->state->remote_protocol_flags; +} + +/* + * Starts packet compression from the next packet on in both directions. + * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. + */ + +static int +ssh_packet_init_compression(struct ssh *ssh) +{ + if (!ssh->state->compression_buffer && + ((ssh->state->compression_buffer = sshbuf_new()) == NULL)) + return SSH_ERR_ALLOC_FAIL; + return 0; +} + +#ifdef WITH_ZLIB +static int +start_compression_out(struct ssh *ssh, int level) +{ + if (level < 1 || level > 9) + return SSH_ERR_INVALID_ARGUMENT; + debug("Enabling compression at level %d.", level); + if (ssh->state->compression_out_started == 1) + deflateEnd(&ssh->state->compression_out_stream); + switch (deflateInit(&ssh->state->compression_out_stream, level)) { + case Z_OK: + ssh->state->compression_out_started = 1; + break; + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + default: + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +static int +start_compression_in(struct ssh *ssh) +{ + if (ssh->state->compression_in_started == 1) + inflateEnd(&ssh->state->compression_in_stream); + switch (inflateInit(&ssh->state->compression_in_stream)) { + case Z_OK: + ssh->state->compression_in_started = 1; + break; + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + default: + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +/* XXX remove need for separate compression buffer */ +static int +compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) +{ + u_char buf[4096]; + int r, status; + + if (ssh->state->compression_out_started != 1) + return SSH_ERR_INTERNAL_ERROR; + + /* This case is not handled below. */ + if (sshbuf_len(in) == 0) + return 0; + + /* Input is the contents of the input buffer. */ + if ((ssh->state->compression_out_stream.next_in = + sshbuf_mutable_ptr(in)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + ssh->state->compression_out_stream.avail_in = sshbuf_len(in); + + /* Loop compressing until deflate() returns with avail_out != 0. */ + do { + /* Set up fixed-size output buffer. */ + ssh->state->compression_out_stream.next_out = buf; + ssh->state->compression_out_stream.avail_out = sizeof(buf); + + /* Compress as much data into the buffer as possible. */ + status = deflate(&ssh->state->compression_out_stream, + Z_PARTIAL_FLUSH); + switch (status) { + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + case Z_OK: + /* Append compressed data to output_buffer. */ + if ((r = sshbuf_put(out, buf, sizeof(buf) - + ssh->state->compression_out_stream.avail_out)) != 0) + return r; + break; + case Z_STREAM_ERROR: + default: + ssh->state->compression_out_failures++; + return SSH_ERR_INVALID_FORMAT; + } + } while (ssh->state->compression_out_stream.avail_out == 0); + return 0; +} + +static int +uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) +{ + u_char buf[4096]; + int r, status; + + if (ssh->state->compression_in_started != 1) + return SSH_ERR_INTERNAL_ERROR; + + if ((ssh->state->compression_in_stream.next_in = + sshbuf_mutable_ptr(in)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + ssh->state->compression_in_stream.avail_in = sshbuf_len(in); + + for (;;) { + /* Set up fixed-size output buffer. */ + ssh->state->compression_in_stream.next_out = buf; + ssh->state->compression_in_stream.avail_out = sizeof(buf); + + status = inflate(&ssh->state->compression_in_stream, + Z_SYNC_FLUSH); + switch (status) { + case Z_OK: + if ((r = sshbuf_put(out, buf, sizeof(buf) - + ssh->state->compression_in_stream.avail_out)) != 0) + return r; + break; + case Z_BUF_ERROR: + /* + * Comments in zlib.h say that we should keep calling + * inflate() until we get an error. This appears to + * be the error that we get. + */ + return 0; + case Z_DATA_ERROR: + return SSH_ERR_INVALID_FORMAT; + case Z_MEM_ERROR: + return SSH_ERR_ALLOC_FAIL; + case Z_STREAM_ERROR: + default: + ssh->state->compression_in_failures++; + return SSH_ERR_INTERNAL_ERROR; + } + } + /* NOTREACHED */ +} + +#else /* WITH_ZLIB */ + +static int +start_compression_out(struct ssh *ssh, int level) +{ + return SSH_ERR_INTERNAL_ERROR; +} + +static int +start_compression_in(struct ssh *ssh) +{ + return SSH_ERR_INTERNAL_ERROR; +} + +static int +compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) +{ + return SSH_ERR_INTERNAL_ERROR; +} + +static int +uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) +{ + return SSH_ERR_INTERNAL_ERROR; +} +#endif /* WITH_ZLIB */ + +void +ssh_clear_newkeys(struct ssh *ssh, int mode) +{ + if (ssh->kex && ssh->kex->newkeys[mode]) { + kex_free_newkeys(ssh->kex->newkeys[mode]); + ssh->kex->newkeys[mode] = NULL; + } +} + +int +ssh_set_newkeys(struct ssh *ssh, int mode) +{ + struct session_state *state = ssh->state; + struct sshenc *enc; + struct sshmac *mac; + struct sshcomp *comp; + struct sshcipher_ctx **ccp; + struct packet_state *ps; + u_int64_t *max_blocks; + const char *wmsg; + int r, crypt_type; + const char *dir = mode == MODE_OUT ? "out" : "in"; + + debug2_f("mode %d", mode); + + if (mode == MODE_OUT) { + ccp = &state->send_context; + crypt_type = CIPHER_ENCRYPT; + ps = &state->p_send; + max_blocks = &state->max_blocks_out; + } else { + ccp = &state->receive_context; + crypt_type = CIPHER_DECRYPT; + ps = &state->p_read; + max_blocks = &state->max_blocks_in; + } + if (state->newkeys[mode] != NULL) { + debug_f("rekeying %s, input %llu bytes %llu blocks, " + "output %llu bytes %llu blocks", dir, + (unsigned long long)state->p_read.bytes, + (unsigned long long)state->p_read.blocks, + (unsigned long long)state->p_send.bytes, + (unsigned long long)state->p_send.blocks); + kex_free_newkeys(state->newkeys[mode]); + state->newkeys[mode] = NULL; + } + /* note that both bytes and the seqnr are not reset */ + ps->packets = ps->blocks = 0; + /* move newkeys from kex to state */ + if ((state->newkeys[mode] = ssh->kex->newkeys[mode]) == NULL) + return SSH_ERR_INTERNAL_ERROR; + ssh->kex->newkeys[mode] = NULL; + enc = &state->newkeys[mode]->enc; + mac = &state->newkeys[mode]->mac; + comp = &state->newkeys[mode]->comp; + if (cipher_authlen(enc->cipher) == 0) { + if ((r = mac_init(mac)) != 0) + return r; + } + mac->enabled = 1; + DBG(debug_f("cipher_init: %s", dir)); + cipher_free(*ccp); + *ccp = NULL; + if ((r = cipher_init(ccp, enc->cipher, enc->key, enc->key_len, + enc->iv, enc->iv_len, crypt_type)) != 0) + return r; + if (!state->cipher_warning_done && + (wmsg = cipher_warning_message(*ccp)) != NULL) { + error("Warning: %s", wmsg); + state->cipher_warning_done = 1; + } + /* Deleting the keys does not gain extra security */ + /* explicit_bzero(enc->iv, enc->block_size); + explicit_bzero(enc->key, enc->key_len); + explicit_bzero(mac->key, mac->key_len); */ + if ((comp->type == COMP_ZLIB || + (comp->type == COMP_DELAYED && + state->after_authentication)) && comp->enabled == 0) { + if ((r = ssh_packet_init_compression(ssh)) < 0) + return r; + if (mode == MODE_OUT) { + if ((r = start_compression_out(ssh, 6)) != 0) + return r; + } else { + if ((r = start_compression_in(ssh)) != 0) + return r; + } + comp->enabled = 1; + } + /* + * The 2^(blocksize*2) limit is too expensive for 3DES, + * so enforce a 1GB limit for small blocksizes. + * See RFC4344 section 3.2. + */ + if (enc->block_size >= 16) + *max_blocks = (u_int64_t)1 << (enc->block_size*2); + else + *max_blocks = ((u_int64_t)1 << 30) / enc->block_size; + if (state->rekey_limit) + *max_blocks = MINIMUM(*max_blocks, + state->rekey_limit / enc->block_size); + debug("rekey %s after %llu blocks", dir, + (unsigned long long)*max_blocks); + return 0; +} + +#define MAX_PACKETS (1U<<31) +static int +ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) +{ + struct session_state *state = ssh->state; + u_int32_t out_blocks; + + /* XXX client can't cope with rekeying pre-auth */ + if (!state->after_authentication) + return 0; + + /* Haven't keyed yet or KEX in progress. */ + if (ssh_packet_is_rekeying(ssh)) + return 0; + + /* Peer can't rekey */ + if (ssh->compat & SSH_BUG_NOREKEY) + return 0; + + /* + * Permit one packet in or out per rekey - this allows us to + * make progress when rekey limits are very small. + */ + if (state->p_send.packets == 0 && state->p_read.packets == 0) + return 0; + + /* Time-based rekeying */ + if (state->rekey_interval != 0 && + (int64_t)state->rekey_time + state->rekey_interval <= monotime()) + return 1; + + /* + * Always rekey when MAX_PACKETS sent in either direction + * As per RFC4344 section 3.1 we do this after 2^31 packets. + */ + if (state->p_send.packets > MAX_PACKETS || + state->p_read.packets > MAX_PACKETS) + return 1; + + /* Rekey after (cipher-specific) maximum blocks */ + out_blocks = ROUNDUP(outbound_packet_len, + state->newkeys[MODE_OUT]->enc.block_size); + return (state->max_blocks_out && + (state->p_send.blocks + out_blocks > state->max_blocks_out)) || + (state->max_blocks_in && + (state->p_read.blocks > state->max_blocks_in)); +} + +int +ssh_packet_check_rekey(struct ssh *ssh) +{ + if (!ssh_packet_need_rekeying(ssh, 0)) + return 0; + debug3_f("rekex triggered"); + return kex_start_rekex(ssh); +} + +/* + * Delayed compression for SSH2 is enabled after authentication: + * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent, + * and on the client side after a SSH2_MSG_USERAUTH_SUCCESS is received. + */ +static int +ssh_packet_enable_delayed_compress(struct ssh *ssh) +{ + struct session_state *state = ssh->state; + struct sshcomp *comp = NULL; + int r, mode; + + /* + * Remember that we are past the authentication step, so rekeying + * with COMP_DELAYED will turn on compression immediately. + */ + state->after_authentication = 1; + for (mode = 0; mode < MODE_MAX; mode++) { + /* protocol error: USERAUTH_SUCCESS received before NEWKEYS */ + if (state->newkeys[mode] == NULL) + continue; + comp = &state->newkeys[mode]->comp; + if (comp && !comp->enabled && comp->type == COMP_DELAYED) { + if ((r = ssh_packet_init_compression(ssh)) != 0) + return r; + if (mode == MODE_OUT) { + if ((r = start_compression_out(ssh, 6)) != 0) + return r; + } else { + if ((r = start_compression_in(ssh)) != 0) + return r; + } + comp->enabled = 1; + } + } + return 0; +} + +/* Used to mute debug logging for noisy packet types */ +int +ssh_packet_log_type(u_char type) +{ + switch (type) { + case SSH2_MSG_CHANNEL_DATA: + case SSH2_MSG_CHANNEL_EXTENDED_DATA: + case SSH2_MSG_CHANNEL_WINDOW_ADJUST: + return 0; + default: + return 1; + } +} + +/* + * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) + */ +int +ssh_packet_send2_wrapped(struct ssh *ssh) +{ + struct session_state *state = ssh->state; + u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; + u_char tmp, padlen, pad = 0; + u_int authlen = 0, aadlen = 0; + u_int len; + struct sshenc *enc = NULL; + struct sshmac *mac = NULL; + struct sshcomp *comp = NULL; + int r, block_size; + + if (state->newkeys[MODE_OUT] != NULL) { + enc = &state->newkeys[MODE_OUT]->enc; + mac = &state->newkeys[MODE_OUT]->mac; + comp = &state->newkeys[MODE_OUT]->comp; + /* disable mac for authenticated encryption */ + if ((authlen = cipher_authlen(enc->cipher)) != 0) + mac = NULL; + } + block_size = enc ? enc->block_size : 8; + aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; + + type = (sshbuf_ptr(state->outgoing_packet))[5]; + if (ssh_packet_log_type(type)) + debug3("send packet: type %u", type); +#ifdef PACKET_DEBUG + fprintf(stderr, "plain: "); + sshbuf_dump(state->outgoing_packet, stderr); +#endif + + if (comp && comp->enabled) { + len = sshbuf_len(state->outgoing_packet); + /* skip header, compress only payload */ + if ((r = sshbuf_consume(state->outgoing_packet, 5)) != 0) + goto out; + sshbuf_reset(state->compression_buffer); + if ((r = compress_buffer(ssh, state->outgoing_packet, + state->compression_buffer)) != 0) + goto out; + sshbuf_reset(state->outgoing_packet); + if ((r = sshbuf_put(state->outgoing_packet, + "\0\0\0\0\0", 5)) != 0 || + (r = sshbuf_putb(state->outgoing_packet, + state->compression_buffer)) != 0) + goto out; + DBG(debug("compression: raw %d compressed %zd", len, + sshbuf_len(state->outgoing_packet))); + } + + /* sizeof (packet_len + pad_len + payload) */ + len = sshbuf_len(state->outgoing_packet); + + /* + * calc size of padding, alloc space, get random data, + * minimum padding is 4 bytes + */ + len -= aadlen; /* packet length is not encrypted for EtM modes */ + padlen = block_size - (len % block_size); + if (padlen < 4) + padlen += block_size; + if (state->extra_pad) { + tmp = state->extra_pad; + state->extra_pad = + ROUNDUP(state->extra_pad, block_size); + /* check if roundup overflowed */ + if (state->extra_pad < tmp) + return SSH_ERR_INVALID_ARGUMENT; + tmp = (len + padlen) % state->extra_pad; + /* Check whether pad calculation below will underflow */ + if (tmp > state->extra_pad) + return SSH_ERR_INVALID_ARGUMENT; + pad = state->extra_pad - tmp; + DBG(debug3_f("adding %d (len %d padlen %d extra_pad %d)", + pad, len, padlen, state->extra_pad)); + tmp = padlen; + padlen += pad; + /* Check whether padlen calculation overflowed */ + if (padlen < tmp) + return SSH_ERR_INVALID_ARGUMENT; /* overflow */ + state->extra_pad = 0; + } + if ((r = sshbuf_reserve(state->outgoing_packet, padlen, &cp)) != 0) + goto out; + if (enc && !cipher_ctx_is_plaintext(state->send_context)) { + /* random padding */ + arc4random_buf(cp, padlen); + } else { + /* clear padding */ + explicit_bzero(cp, padlen); + } + /* sizeof (packet_len + pad_len + payload + padding) */ + len = sshbuf_len(state->outgoing_packet); + cp = sshbuf_mutable_ptr(state->outgoing_packet); + if (cp == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + /* packet_length includes payload, padding and padding length field */ + POKE_U32(cp, len - 4); + cp[4] = padlen; + DBG(debug("send: len %d (includes padlen %d, aadlen %d)", + len, padlen, aadlen)); + + /* compute MAC over seqnr and packet(length fields, payload, padding) */ + if (mac && mac->enabled && !mac->etm) { + if ((r = mac_compute(mac, state->p_send.seqnr, + sshbuf_ptr(state->outgoing_packet), len, + macbuf, sizeof(macbuf))) != 0) + goto out; + DBG(debug("done calc MAC out #%d", state->p_send.seqnr)); + } + /* encrypt packet and append to output buffer. */ + if ((r = sshbuf_reserve(state->output, + sshbuf_len(state->outgoing_packet) + authlen, &cp)) != 0) + goto out; + if ((r = cipher_crypt(state->send_context, state->p_send.seqnr, cp, + sshbuf_ptr(state->outgoing_packet), + len - aadlen, aadlen, authlen)) != 0) + goto out; + /* append unencrypted MAC */ + if (mac && mac->enabled) { + if (mac->etm) { + /* EtM: compute mac over aadlen + cipher text */ + if ((r = mac_compute(mac, state->p_send.seqnr, + cp, len, macbuf, sizeof(macbuf))) != 0) + goto out; + DBG(debug("done calc MAC(EtM) out #%d", + state->p_send.seqnr)); + } + if ((r = sshbuf_put(state->output, macbuf, mac->mac_len)) != 0) + goto out; + } +#ifdef PACKET_DEBUG + fprintf(stderr, "encrypted: "); + sshbuf_dump(state->output, stderr); +#endif + /* increment sequence number for outgoing packets */ + if (++state->p_send.seqnr == 0) + logit("outgoing seqnr wraps around"); + if (++state->p_send.packets == 0) + if (!(ssh->compat & SSH_BUG_NOREKEY)) + return SSH_ERR_NEED_REKEY; + state->p_send.blocks += len / block_size; + state->p_send.bytes += len; + sshbuf_reset(state->outgoing_packet); + + if (type == SSH2_MSG_NEWKEYS) + r = ssh_set_newkeys(ssh, MODE_OUT); + else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) + r = ssh_packet_enable_delayed_compress(ssh); + else + r = 0; + out: + return r; +} + +/* returns non-zero if the specified packet type is usec by KEX */ +static int +ssh_packet_type_is_kex(u_char type) +{ + return + type >= SSH2_MSG_TRANSPORT_MIN && + type <= SSH2_MSG_TRANSPORT_MAX && + type != SSH2_MSG_SERVICE_REQUEST && + type != SSH2_MSG_SERVICE_ACCEPT && + type != SSH2_MSG_EXT_INFO; +} + +int +ssh_packet_send2(struct ssh *ssh) +{ + struct session_state *state = ssh->state; + struct packet *p; + u_char type; + int r, need_rekey; + + if (sshbuf_len(state->outgoing_packet) < 6) + return SSH_ERR_INTERNAL_ERROR; + type = sshbuf_ptr(state->outgoing_packet)[5]; + need_rekey = !ssh_packet_type_is_kex(type) && + ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet)); + + /* + * During rekeying we can only send key exchange messages. + * Queue everything else. + */ + if ((need_rekey || state->rekeying) && !ssh_packet_type_is_kex(type)) { + if (need_rekey) + debug3_f("rekex triggered"); + debug("enqueue packet: %u", type); + p = calloc(1, sizeof(*p)); + if (p == NULL) + return SSH_ERR_ALLOC_FAIL; + p->type = type; + p->payload = state->outgoing_packet; + TAILQ_INSERT_TAIL(&state->outgoing, p, next); + state->outgoing_packet = sshbuf_new(); + if (state->outgoing_packet == NULL) + return SSH_ERR_ALLOC_FAIL; + if (need_rekey) { + /* + * This packet triggered a rekey, so send the + * KEXINIT now. + * NB. reenters this function via kex_start_rekex(). + */ + return kex_start_rekex(ssh); + } + return 0; + } + + /* rekeying starts with sending KEXINIT */ + if (type == SSH2_MSG_KEXINIT) + state->rekeying = 1; + + if ((r = ssh_packet_send2_wrapped(ssh)) != 0) + return r; + + /* after a NEWKEYS message we can send the complete queue */ + if (type == SSH2_MSG_NEWKEYS) { + state->rekeying = 0; + state->rekey_time = monotime(); + while ((p = TAILQ_FIRST(&state->outgoing))) { + type = p->type; + /* + * If this packet triggers a rekex, then skip the + * remaining packets in the queue for now. + * NB. re-enters this function via kex_start_rekex. + */ + if (ssh_packet_need_rekeying(ssh, + sshbuf_len(p->payload))) { + debug3_f("queued packet triggered rekex"); + return kex_start_rekex(ssh); + } + debug("dequeue packet: %u", type); + sshbuf_free(state->outgoing_packet); + state->outgoing_packet = p->payload; + TAILQ_REMOVE(&state->outgoing, p, next); + memset(p, 0, sizeof(*p)); + free(p); + if ((r = ssh_packet_send2_wrapped(ssh)) != 0) + return r; + } + } + return 0; +} + +/* + * Waits until a packet has been received, and returns its type. Note that + * no other data is processed until this returns, so this function should not + * be used during the interactive session. + */ + +int +ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) +{ + struct session_state *state = ssh->state; + int len, r, ms_remain; + struct pollfd pfd; + char buf[8192]; + struct timeval start; + struct timespec timespec, *timespecp = NULL; + + DBG(debug("packet_read()")); + + /* + * Since we are blocking, ensure that all written packets have + * been sent. + */ + if ((r = ssh_packet_write_wait(ssh)) != 0) + goto out; + + /* Stay in the loop until we have received a complete packet. */ + for (;;) { + /* Try to read a packet from the buffer. */ + r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); + if (r != 0) + break; + /* If we got a packet, return it. */ + if (*typep != SSH_MSG_NONE) + break; + /* + * Otherwise, wait for some data to arrive, add it to the + * buffer, and try again. + */ + pfd.fd = state->connection_in; + pfd.events = POLLIN; + + if (state->packet_timeout_ms > 0) { + ms_remain = state->packet_timeout_ms; + timespecp = ×pec; + } + /* Wait for some data to arrive. */ + for (;;) { + if (state->packet_timeout_ms > 0) { + ms_to_timespec(×pec, ms_remain); + monotime_tv(&start); + } + if ((r = ppoll(&pfd, 1, timespecp, NULL)) >= 0) + break; + if (errno != EAGAIN && errno != EINTR && + errno != EWOULDBLOCK) { + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + if (state->packet_timeout_ms <= 0) + continue; + ms_subtract_diff(&start, &ms_remain); + if (ms_remain <= 0) { + r = 0; + break; + } + } + if (r == 0) { + r = SSH_ERR_CONN_TIMEOUT; + goto out; + } + /* Read data from the socket. */ + len = read(state->connection_in, buf, sizeof(buf)); + if (len == 0) { + r = SSH_ERR_CONN_CLOSED; + goto out; + } + if (len == -1) { + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + + /* Append it to the buffer. */ + if ((r = ssh_packet_process_incoming(ssh, buf, len)) != 0) + goto out; + } + out: + return r; +} + +int +ssh_packet_read(struct ssh *ssh) +{ + u_char type; + int r; + + if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) + fatal_fr(r, "read"); + return type; +} + +/* + * Waits until a packet has been received, verifies that its type matches + * that given, and gives a fatal error and exits if there is a mismatch. + */ + +int +ssh_packet_read_expect(struct ssh *ssh, u_int expected_type) +{ + int r; + u_char type; + + if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) + return r; + if (type != expected_type) { + if ((r = sshpkt_disconnect(ssh, + "Protocol error: expected packet type %d, got %d", + expected_type, type)) != 0) + return r; + return SSH_ERR_PROTOCOL_ERROR; + } + return 0; +} + +static int +ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) +{ + struct session_state *state = ssh->state; + const u_char *cp; + size_t need; + int r; + + if (ssh->kex) + return SSH_ERR_INTERNAL_ERROR; + *typep = SSH_MSG_NONE; + cp = sshbuf_ptr(state->input); + if (state->packlen == 0) { + if (sshbuf_len(state->input) < 4 + 1) + return 0; /* packet is incomplete */ + state->packlen = PEEK_U32(cp); + if (state->packlen < 4 + 1 || + state->packlen > PACKET_MAX_SIZE) + return SSH_ERR_MESSAGE_INCOMPLETE; + } + need = state->packlen + 4; + if (sshbuf_len(state->input) < need) + return 0; /* packet is incomplete */ + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_put(state->incoming_packet, cp + 4, + state->packlen)) != 0 || + (r = sshbuf_consume(state->input, need)) != 0 || + (r = sshbuf_get_u8(state->incoming_packet, NULL)) != 0 || + (r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) + return r; + if (ssh_packet_log_type(*typep)) + debug3_f("type %u", *typep); + /* sshbuf_dump(state->incoming_packet, stderr); */ + /* reset for next packet */ + state->packlen = 0; + return r; +} + +int +ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) +{ + struct session_state *state = ssh->state; + u_int padlen, need; + u_char *cp; + u_int maclen, aadlen = 0, authlen = 0, block_size; + struct sshenc *enc = NULL; + struct sshmac *mac = NULL; + struct sshcomp *comp = NULL; + int r; + + if (state->mux) + return ssh_packet_read_poll2_mux(ssh, typep, seqnr_p); + + *typep = SSH_MSG_NONE; + + if (state->packet_discard) + return 0; + + if (state->newkeys[MODE_IN] != NULL) { + enc = &state->newkeys[MODE_IN]->enc; + mac = &state->newkeys[MODE_IN]->mac; + comp = &state->newkeys[MODE_IN]->comp; + /* disable mac for authenticated encryption */ + if ((authlen = cipher_authlen(enc->cipher)) != 0) + mac = NULL; + } + maclen = mac && mac->enabled ? mac->mac_len : 0; + block_size = enc ? enc->block_size : 8; + aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; + + if (aadlen && state->packlen == 0) { + if (cipher_get_length(state->receive_context, + &state->packlen, state->p_read.seqnr, + sshbuf_ptr(state->input), sshbuf_len(state->input)) != 0) + return 0; + if (state->packlen < 1 + 4 || + state->packlen > PACKET_MAX_SIZE) { +#ifdef PACKET_DEBUG + sshbuf_dump(state->input, stderr); +#endif + logit("Bad packet length %u.", state->packlen); + if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) + return r; + return SSH_ERR_CONN_CORRUPT; + } + sshbuf_reset(state->incoming_packet); + } else if (state->packlen == 0) { + /* + * check if input size is less than the cipher block size, + * decrypt first block and extract length of incoming packet + */ + if (sshbuf_len(state->input) < block_size) + return 0; + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_reserve(state->incoming_packet, block_size, + &cp)) != 0) + goto out; + if ((r = cipher_crypt(state->receive_context, + state->p_send.seqnr, cp, sshbuf_ptr(state->input), + block_size, 0, 0)) != 0) + goto out; + state->packlen = PEEK_U32(sshbuf_ptr(state->incoming_packet)); + if (state->packlen < 1 + 4 || + state->packlen > PACKET_MAX_SIZE) { +#ifdef PACKET_DEBUG + fprintf(stderr, "input: \n"); + sshbuf_dump(state->input, stderr); + fprintf(stderr, "incoming_packet: \n"); + sshbuf_dump(state->incoming_packet, stderr); +#endif + logit("Bad packet length %u.", state->packlen); + return ssh_packet_start_discard(ssh, enc, mac, 0, + PACKET_MAX_SIZE); + } + if ((r = sshbuf_consume(state->input, block_size)) != 0) + goto out; + } + DBG(debug("input: packet len %u", state->packlen+4)); + + if (aadlen) { + /* only the payload is encrypted */ + need = state->packlen; + } else { + /* + * the payload size and the payload are encrypted, but we + * have a partial packet of block_size bytes + */ + need = 4 + state->packlen - block_size; + } + DBG(debug("partial packet: block %d, need %d, maclen %d, authlen %d," + " aadlen %d", block_size, need, maclen, authlen, aadlen)); + if (need % block_size != 0) { + logit("padding error: need %d block %d mod %d", + need, block_size, need % block_size); + return ssh_packet_start_discard(ssh, enc, mac, 0, + PACKET_MAX_SIZE - block_size); + } + /* + * check if the entire packet has been received and + * decrypt into incoming_packet: + * 'aadlen' bytes are unencrypted, but authenticated. + * 'need' bytes are encrypted, followed by either + * 'authlen' bytes of authentication tag or + * 'maclen' bytes of message authentication code. + */ + if (sshbuf_len(state->input) < aadlen + need + authlen + maclen) + return 0; /* packet is incomplete */ +#ifdef PACKET_DEBUG + fprintf(stderr, "read_poll enc/full: "); + sshbuf_dump(state->input, stderr); +#endif + /* EtM: check mac over encrypted input */ + if (mac && mac->enabled && mac->etm) { + if ((r = mac_check(mac, state->p_read.seqnr, + sshbuf_ptr(state->input), aadlen + need, + sshbuf_ptr(state->input) + aadlen + need + authlen, + maclen)) != 0) { + if (r == SSH_ERR_MAC_INVALID) + logit("Corrupted MAC on input."); + goto out; + } + } + if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need, + &cp)) != 0) + goto out; + if ((r = cipher_crypt(state->receive_context, state->p_read.seqnr, cp, + sshbuf_ptr(state->input), need, aadlen, authlen)) != 0) + goto out; + if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0) + goto out; + if (mac && mac->enabled) { + /* Not EtM: check MAC over cleartext */ + if (!mac->etm && (r = mac_check(mac, state->p_read.seqnr, + sshbuf_ptr(state->incoming_packet), + sshbuf_len(state->incoming_packet), + sshbuf_ptr(state->input), maclen)) != 0) { + if (r != SSH_ERR_MAC_INVALID) + goto out; + logit("Corrupted MAC on input."); + if (need + block_size > PACKET_MAX_SIZE) + return SSH_ERR_INTERNAL_ERROR; + return ssh_packet_start_discard(ssh, enc, mac, + sshbuf_len(state->incoming_packet), + PACKET_MAX_SIZE - need - block_size); + } + /* Remove MAC from input buffer */ + DBG(debug("MAC #%d ok", state->p_read.seqnr)); + if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) + goto out; + } + if (seqnr_p != NULL) + *seqnr_p = state->p_read.seqnr; + if (++state->p_read.seqnr == 0) + logit("incoming seqnr wraps around"); + if (++state->p_read.packets == 0) + if (!(ssh->compat & SSH_BUG_NOREKEY)) + return SSH_ERR_NEED_REKEY; + state->p_read.blocks += (state->packlen + 4) / block_size; + state->p_read.bytes += state->packlen + 4; + + /* get padlen */ + padlen = sshbuf_ptr(state->incoming_packet)[4]; + DBG(debug("input: padlen %d", padlen)); + if (padlen < 4) { + if ((r = sshpkt_disconnect(ssh, + "Corrupted padlen %d on input.", padlen)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + return r; + return SSH_ERR_CONN_CORRUPT; + } + + /* skip packet size + padlen, discard padding */ + if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 || + ((r = sshbuf_consume_end(state->incoming_packet, padlen)) != 0)) + goto out; + + DBG(debug("input: len before de-compress %zd", + sshbuf_len(state->incoming_packet))); + if (comp && comp->enabled) { + sshbuf_reset(state->compression_buffer); + if ((r = uncompress_buffer(ssh, state->incoming_packet, + state->compression_buffer)) != 0) + goto out; + sshbuf_reset(state->incoming_packet); + if ((r = sshbuf_putb(state->incoming_packet, + state->compression_buffer)) != 0) + goto out; + DBG(debug("input: len after de-compress %zd", + sshbuf_len(state->incoming_packet))); + } + /* + * get packet type, implies consume. + * return length of payload (without type field) + */ + if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) + goto out; + if (ssh_packet_log_type(*typep)) + debug3("receive packet: type %u", *typep); + if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) { + if ((r = sshpkt_disconnect(ssh, + "Invalid ssh2 packet type: %d", *typep)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + return r; + return SSH_ERR_PROTOCOL_ERROR; + } + if (state->hook_in != NULL && + (r = state->hook_in(ssh, state->incoming_packet, typep, + state->hook_in_ctx)) != 0) + return r; + if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side) + r = ssh_packet_enable_delayed_compress(ssh); + else + r = 0; +#ifdef PACKET_DEBUG + fprintf(stderr, "read/plain[%d]:\r\n", *typep); + sshbuf_dump(state->incoming_packet, stderr); +#endif + /* reset for next packet */ + state->packlen = 0; + + if ((r = ssh_packet_check_rekey(ssh)) != 0) + return r; + out: + return r; +} + +int +ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) +{ + struct session_state *state = ssh->state; + u_int reason, seqnr; + int r; + u_char *msg; + + for (;;) { + msg = NULL; + r = ssh_packet_read_poll2(ssh, typep, seqnr_p); + if (r != 0) + return r; + if (*typep) { + state->keep_alive_timeouts = 0; + DBG(debug("received packet type %d", *typep)); + } + switch (*typep) { + case SSH2_MSG_IGNORE: + debug3("Received SSH2_MSG_IGNORE"); + break; + case SSH2_MSG_DEBUG: + if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || + (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || + (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { + free(msg); + return r; + } + debug("Remote: %.900s", msg); + free(msg); + break; + case SSH2_MSG_DISCONNECT: + if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || + (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) + return r; + /* Ignore normal client exit notifications */ + do_log2(ssh->state->server_side && + reason == SSH2_DISCONNECT_BY_APPLICATION ? + SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, + "Received disconnect from %s port %d:" + "%u: %.400s", ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), reason, msg); + free(msg); + return SSH_ERR_DISCONNECTED; + case SSH2_MSG_UNIMPLEMENTED: + if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) + return r; + debug("Received SSH2_MSG_UNIMPLEMENTED for %u", + seqnr); + break; + default: + return 0; + } + } +} + +/* + * Buffers the supplied input data. This is intended to be used together + * with packet_read_poll(). + */ +int +ssh_packet_process_incoming(struct ssh *ssh, const char *buf, u_int len) +{ + struct session_state *state = ssh->state; + int r; + + if (state->packet_discard) { + state->keep_alive_timeouts = 0; /* ?? */ + if (len >= state->packet_discard) { + if ((r = ssh_packet_stop_discard(ssh)) != 0) + return r; + } + state->packet_discard -= len; + return 0; + } + if ((r = sshbuf_put(state->input, buf, len)) != 0) + return r; + + return 0; +} + +/* Reads and buffers data from the specified fd */ +int +ssh_packet_process_read(struct ssh *ssh, int fd) +{ + struct session_state *state = ssh->state; + int r; + size_t rlen; + + if ((r = sshbuf_read(fd, state->input, PACKET_MAX_SIZE, &rlen)) != 0) + return r; + + if (state->packet_discard) { + if ((r = sshbuf_consume_end(state->input, rlen)) != 0) + return r; + state->keep_alive_timeouts = 0; /* ?? */ + if (rlen >= state->packet_discard) { + if ((r = ssh_packet_stop_discard(ssh)) != 0) + return r; + } + state->packet_discard -= rlen; + return 0; + } + return 0; +} + +int +ssh_packet_remaining(struct ssh *ssh) +{ + return sshbuf_len(ssh->state->incoming_packet); +} + +/* + * Sends a diagnostic message from the server to the client. This message + * can be sent at any time (but not while constructing another message). The + * message is printed immediately, but only if the client is being executed + * in verbose mode. These messages are primarily intended to ease debugging + * authentication problems. The length of the formatted message must not + * exceed 1024 bytes. This will automatically call ssh_packet_write_wait. + */ +void +ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...) +{ + char buf[1024]; + va_list args; + int r; + + if ((ssh->compat & SSH_BUG_DEBUG)) + return; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + debug3("sending debug message: %s", buf); + + if ((r = sshpkt_start(ssh, SSH2_MSG_DEBUG)) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || /* always display */ + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send DEBUG"); +} + +void +sshpkt_fmt_connection_id(struct ssh *ssh, char *s, size_t l) +{ + snprintf(s, l, "%.200s%s%s port %d", + ssh->log_preamble ? ssh->log_preamble : "", + ssh->log_preamble ? " " : "", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); +} + +/* + * Pretty-print connection-terminating errors and exit. + */ +static void +sshpkt_vfatal(struct ssh *ssh, int r, const char *fmt, va_list ap) +{ + char *tag = NULL, remote_id[512]; + int oerrno = errno; + + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + + switch (r) { + case SSH_ERR_CONN_CLOSED: + ssh_packet_clear_keys(ssh); + logdie("Connection closed by %s", remote_id); + case SSH_ERR_CONN_TIMEOUT: + ssh_packet_clear_keys(ssh); + logdie("Connection %s %s timed out", + ssh->state->server_side ? "from" : "to", remote_id); + case SSH_ERR_DISCONNECTED: + ssh_packet_clear_keys(ssh); + logdie("Disconnected from %s", remote_id); + case SSH_ERR_SYSTEM_ERROR: + if (errno == ECONNRESET) { + ssh_packet_clear_keys(ssh); + logdie("Connection reset by %s", remote_id); + } + /* FALLTHROUGH */ + case SSH_ERR_NO_CIPHER_ALG_MATCH: + case SSH_ERR_NO_MAC_ALG_MATCH: + case SSH_ERR_NO_COMPRESS_ALG_MATCH: + case SSH_ERR_NO_KEX_ALG_MATCH: + case SSH_ERR_NO_HOSTKEY_ALG_MATCH: + if (ssh && ssh->kex && ssh->kex->failed_choice) { + ssh_packet_clear_keys(ssh); + errno = oerrno; + logdie("Unable to negotiate with %s: %s. " + "Their offer: %s", remote_id, ssh_err(r), + ssh->kex->failed_choice); + } + /* FALLTHROUGH */ + default: + if (vasprintf(&tag, fmt, ap) == -1) { + ssh_packet_clear_keys(ssh); + logdie_f("could not allocate failure message"); + } + ssh_packet_clear_keys(ssh); + errno = oerrno; + logdie_r(r, "%s%sConnection %s %s", + tag != NULL ? tag : "", tag != NULL ? ": " : "", + ssh->state->server_side ? "from" : "to", remote_id); + } +} + +void +sshpkt_fatal(struct ssh *ssh, int r, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + sshpkt_vfatal(ssh, r, fmt, ap); + /* NOTREACHED */ + va_end(ap); + logdie_f("should have exited"); +} + +/* + * Logs the error plus constructs and sends a disconnect packet, closes the + * connection, and exits. This function never returns. The error message + * should not contain a newline. The length of the formatted message must + * not exceed 1024 bytes. + */ +void +ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...) +{ + char buf[1024], remote_id[512]; + va_list args; + static int disconnecting = 0; + int r; + + if (disconnecting) /* Guard against recursive invocations. */ + fatal("packet_disconnect called recursively."); + disconnecting = 1; + + /* + * Format the message. Note that the caller must make sure the + * message is of limited size. + */ + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + /* Display the error locally */ + logit("Disconnecting %s: %.100s", remote_id, buf); + + /* + * Send the disconnect message to the other side, and wait + * for it to get sent. + */ + if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0) + sshpkt_fatal(ssh, r, "%s", __func__); + + if ((r = ssh_packet_write_wait(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s", __func__); + + /* Close the connection. */ + ssh_packet_close(ssh); + cleanup_exit(255); +} + +/* + * Checks if there is any buffered output, and tries to write some of + * the output. + */ +int +ssh_packet_write_poll(struct ssh *ssh) +{ + struct session_state *state = ssh->state; + int len = sshbuf_len(state->output); + int r; + + if (len > 0) { + len = write(state->connection_out, + sshbuf_ptr(state->output), len); + if (len == -1) { + if (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK) + return 0; + return SSH_ERR_SYSTEM_ERROR; + } + if (len == 0) + return SSH_ERR_CONN_CLOSED; + if ((r = sshbuf_consume(state->output, len)) != 0) + return r; + } + return 0; +} + +/* + * Calls packet_write_poll repeatedly until all pending output data has been + * written. + */ +int +ssh_packet_write_wait(struct ssh *ssh) +{ + int ret, r, ms_remain = 0; + struct timeval start; + struct timespec timespec, *timespecp = NULL; + struct session_state *state = ssh->state; + struct pollfd pfd; + + if ((r = ssh_packet_write_poll(ssh)) != 0) + return r; + while (ssh_packet_have_data_to_write(ssh)) { + pfd.fd = state->connection_out; + pfd.events = POLLOUT; + + if (state->packet_timeout_ms > 0) { + ms_remain = state->packet_timeout_ms; + timespecp = ×pec; + } + for (;;) { + if (state->packet_timeout_ms > 0) { + ms_to_timespec(×pec, ms_remain); + monotime_tv(&start); + } + if ((ret = ppoll(&pfd, 1, timespecp, NULL)) >= 0) + break; + if (errno != EAGAIN && errno != EINTR && + errno != EWOULDBLOCK) + break; + if (state->packet_timeout_ms <= 0) + continue; + ms_subtract_diff(&start, &ms_remain); + if (ms_remain <= 0) { + ret = 0; + break; + } + } + if (ret == 0) + return SSH_ERR_CONN_TIMEOUT; + if ((r = ssh_packet_write_poll(ssh)) != 0) + return r; + } + return 0; +} + +/* Returns true if there is buffered data to write to the connection. */ + +int +ssh_packet_have_data_to_write(struct ssh *ssh) +{ + return sshbuf_len(ssh->state->output) != 0; +} + +/* Returns true if there is not too much data to write to the connection. */ + +int +ssh_packet_not_very_much_data_to_write(struct ssh *ssh) +{ + if (ssh->state->interactive_mode) + return sshbuf_len(ssh->state->output) < 16384; + else + return sshbuf_len(ssh->state->output) < 128 * 1024; +} + +void +ssh_packet_set_tos(struct ssh *ssh, int tos) +{ + if (!ssh_packet_connection_is_on_socket(ssh) || tos == INT_MAX) + return; + set_sock_tos(ssh->state->connection_in, tos); +} + +/* Informs that the current session is interactive. Sets IP flags for that. */ + +void +ssh_packet_set_interactive(struct ssh *ssh, int interactive, int qos_interactive, int qos_bulk) +{ + struct session_state *state = ssh->state; + + if (state->set_interactive_called) + return; + state->set_interactive_called = 1; + + /* Record that we are in interactive mode. */ + state->interactive_mode = interactive; + + /* Only set socket options if using a socket. */ + if (!ssh_packet_connection_is_on_socket(ssh)) + return; + set_nodelay(state->connection_in); + ssh_packet_set_tos(ssh, interactive ? qos_interactive : qos_bulk); +} + +/* Returns true if the current connection is interactive. */ + +int +ssh_packet_is_interactive(struct ssh *ssh) +{ + return ssh->state->interactive_mode; +} + +int +ssh_packet_set_maxsize(struct ssh *ssh, u_int s) +{ + struct session_state *state = ssh->state; + + if (state->set_maxsize_called) { + logit_f("called twice: old %d new %d", + state->max_packet_size, s); + return -1; + } + if (s < 4 * 1024 || s > 1024 * 1024) { + logit_f("bad size %d", s); + return -1; + } + state->set_maxsize_called = 1; + debug_f("setting to %d", s); + state->max_packet_size = s; + return s; +} + +int +ssh_packet_inc_alive_timeouts(struct ssh *ssh) +{ + return ++ssh->state->keep_alive_timeouts; +} + +void +ssh_packet_set_alive_timeouts(struct ssh *ssh, int ka) +{ + ssh->state->keep_alive_timeouts = ka; +} + +u_int +ssh_packet_get_maxsize(struct ssh *ssh) +{ + return ssh->state->max_packet_size; +} + +void +ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, u_int32_t seconds) +{ + debug3("rekey after %llu bytes, %u seconds", (unsigned long long)bytes, + (unsigned int)seconds); + ssh->state->rekey_limit = bytes; + ssh->state->rekey_interval = seconds; +} + +time_t +ssh_packet_get_rekey_timeout(struct ssh *ssh) +{ + time_t seconds; + + seconds = ssh->state->rekey_time + ssh->state->rekey_interval - + monotime(); + return (seconds <= 0 ? 1 : seconds); +} + +void +ssh_packet_set_server(struct ssh *ssh) +{ + ssh->state->server_side = 1; + ssh->kex->server = 1; /* XXX unify? */ +} + +void +ssh_packet_set_authenticated(struct ssh *ssh) +{ + ssh->state->after_authentication = 1; +} + +void * +ssh_packet_get_input(struct ssh *ssh) +{ + return (void *)ssh->state->input; +} + +void * +ssh_packet_get_output(struct ssh *ssh) +{ + return (void *)ssh->state->output; +} + +/* Reset after_authentication and reset compression in post-auth privsep */ +static int +ssh_packet_set_postauth(struct ssh *ssh) +{ + int r; + + debug_f("called"); + /* This was set in net child, but is not visible in user child */ + ssh->state->after_authentication = 1; + ssh->state->rekeying = 0; + if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0) + return r; + return 0; +} + +/* Packet state (de-)serialization for privsep */ + +/* turn kex into a blob for packet state serialization */ +static int +kex_to_blob(struct sshbuf *m, struct kex *kex) +{ + int r; + + if ((r = sshbuf_put_u32(m, kex->we_need)) != 0 || + (r = sshbuf_put_cstring(m, kex->hostkey_alg)) != 0 || + (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 || + (r = sshbuf_put_u32(m, kex->hostkey_nid)) != 0 || + (r = sshbuf_put_u32(m, kex->kex_type)) != 0 || + (r = sshbuf_put_stringb(m, kex->my)) != 0 || + (r = sshbuf_put_stringb(m, kex->peer)) != 0 || + (r = sshbuf_put_stringb(m, kex->client_version)) != 0 || + (r = sshbuf_put_stringb(m, kex->server_version)) != 0 || + (r = sshbuf_put_stringb(m, kex->session_id)) != 0 || + (r = sshbuf_put_u32(m, kex->flags)) != 0) + return r; + return 0; +} + +/* turn key exchange results into a blob for packet state serialization */ +static int +newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode) +{ + struct sshbuf *b; + struct sshcipher_ctx *cc; + struct sshcomp *comp; + struct sshenc *enc; + struct sshmac *mac; + struct newkeys *newkey; + int r; + + if ((newkey = ssh->state->newkeys[mode]) == NULL) + return SSH_ERR_INTERNAL_ERROR; + enc = &newkey->enc; + mac = &newkey->mac; + comp = &newkey->comp; + cc = (mode == MODE_OUT) ? ssh->state->send_context : + ssh->state->receive_context; + if ((r = cipher_get_keyiv(cc, enc->iv, enc->iv_len)) != 0) + return r; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, enc->name)) != 0 || + (r = sshbuf_put_u32(b, enc->enabled)) != 0 || + (r = sshbuf_put_u32(b, enc->block_size)) != 0 || + (r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 || + (r = sshbuf_put_string(b, enc->iv, enc->iv_len)) != 0) + goto out; + if (cipher_authlen(enc->cipher) == 0) { + if ((r = sshbuf_put_cstring(b, mac->name)) != 0 || + (r = sshbuf_put_u32(b, mac->enabled)) != 0 || + (r = sshbuf_put_string(b, mac->key, mac->key_len)) != 0) + goto out; + } + if ((r = sshbuf_put_u32(b, comp->type)) != 0 || + (r = sshbuf_put_cstring(b, comp->name)) != 0) + goto out; + r = sshbuf_put_stringb(m, b); + out: + sshbuf_free(b); + return r; +} + +/* serialize packet state into a blob */ +int +ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) +{ + struct session_state *state = ssh->state; + int r; + + if ((r = kex_to_blob(m, ssh->kex)) != 0 || + (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 || + (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 || + (r = sshbuf_put_u64(m, state->rekey_limit)) != 0 || + (r = sshbuf_put_u32(m, state->rekey_interval)) != 0 || + (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 || + (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 || + (r = sshbuf_put_u32(m, state->p_send.packets)) != 0 || + (r = sshbuf_put_u64(m, state->p_send.bytes)) != 0 || + (r = sshbuf_put_u32(m, state->p_read.seqnr)) != 0 || + (r = sshbuf_put_u64(m, state->p_read.blocks)) != 0 || + (r = sshbuf_put_u32(m, state->p_read.packets)) != 0 || + (r = sshbuf_put_u64(m, state->p_read.bytes)) != 0 || + (r = sshbuf_put_stringb(m, state->input)) != 0 || + (r = sshbuf_put_stringb(m, state->output)) != 0) + return r; + + return 0; +} + +/* restore key exchange results from blob for packet state de-serialization */ +static int +newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) +{ + struct sshbuf *b = NULL; + struct sshcomp *comp; + struct sshenc *enc; + struct sshmac *mac; + struct newkeys *newkey = NULL; + size_t keylen, ivlen, maclen; + int r; + + if ((newkey = calloc(1, sizeof(*newkey))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_froms(m, &b)) != 0) + goto out; +#ifdef DEBUG_PK + sshbuf_dump(b, stderr); +#endif + enc = &newkey->enc; + mac = &newkey->mac; + comp = &newkey->comp; + + if ((r = sshbuf_get_cstring(b, &enc->name, NULL)) != 0 || + (r = sshbuf_get_u32(b, (u_int *)&enc->enabled)) != 0 || + (r = sshbuf_get_u32(b, &enc->block_size)) != 0 || + (r = sshbuf_get_string(b, &enc->key, &keylen)) != 0 || + (r = sshbuf_get_string(b, &enc->iv, &ivlen)) != 0) + goto out; + if ((enc->cipher = cipher_by_name(enc->name)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (cipher_authlen(enc->cipher) == 0) { + if ((r = sshbuf_get_cstring(b, &mac->name, NULL)) != 0) + goto out; + if ((r = mac_setup(mac, mac->name)) != 0) + goto out; + if ((r = sshbuf_get_u32(b, (u_int *)&mac->enabled)) != 0 || + (r = sshbuf_get_string(b, &mac->key, &maclen)) != 0) + goto out; + if (maclen > mac->key_len) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + mac->key_len = maclen; + } + if ((r = sshbuf_get_u32(b, &comp->type)) != 0 || + (r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0) + goto out; + if (sshbuf_len(b) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + enc->key_len = keylen; + enc->iv_len = ivlen; + ssh->kex->newkeys[mode] = newkey; + newkey = NULL; + r = 0; + out: + free(newkey); + sshbuf_free(b); + return r; +} + +/* restore kex from blob for packet state de-serialization */ +static int +kex_from_blob(struct sshbuf *m, struct kex **kexp) +{ + struct kex *kex; + int r; + + if ((kex = kex_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_u32(m, &kex->we_need)) != 0 || + (r = sshbuf_get_cstring(m, &kex->hostkey_alg, NULL)) != 0 || + (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 || + (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 0 || + (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 || + (r = sshbuf_get_stringb(m, kex->my)) != 0 || + (r = sshbuf_get_stringb(m, kex->peer)) != 0 || + (r = sshbuf_get_stringb(m, kex->client_version)) != 0 || + (r = sshbuf_get_stringb(m, kex->server_version)) != 0 || + (r = sshbuf_get_stringb(m, kex->session_id)) != 0 || + (r = sshbuf_get_u32(m, &kex->flags)) != 0) + goto out; + kex->server = 1; + kex->done = 1; + r = 0; + out: + if (r != 0 || kexp == NULL) { + kex_free(kex); + if (kexp != NULL) + *kexp = NULL; + } else { + kex_free(*kexp); + *kexp = kex; + } + return r; +} + +/* + * Restore packet state from content of blob 'm' (de-serialization). + * Note that 'm' will be partially consumed on parsing or any other errors. + */ +int +ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) +{ + struct session_state *state = ssh->state; + const u_char *input, *output; + size_t ilen, olen; + int r; + + if ((r = kex_from_blob(m, &ssh->kex)) != 0 || + (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 || + (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 || + (r = sshbuf_get_u64(m, &state->rekey_limit)) != 0 || + (r = sshbuf_get_u32(m, &state->rekey_interval)) != 0 || + (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 || + (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 || + (r = sshbuf_get_u32(m, &state->p_send.packets)) != 0 || + (r = sshbuf_get_u64(m, &state->p_send.bytes)) != 0 || + (r = sshbuf_get_u32(m, &state->p_read.seqnr)) != 0 || + (r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 || + (r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 || + (r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0) + return r; + /* + * We set the time here so that in post-auth privsep child we + * count from the completion of the authentication. + */ + state->rekey_time = monotime(); + /* XXX ssh_set_newkeys overrides p_read.packets? XXX */ + if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 || + (r = ssh_set_newkeys(ssh, MODE_OUT)) != 0) + return r; + + if ((r = ssh_packet_set_postauth(ssh)) != 0) + return r; + + sshbuf_reset(state->input); + sshbuf_reset(state->output); + if ((r = sshbuf_get_string_direct(m, &input, &ilen)) != 0 || + (r = sshbuf_get_string_direct(m, &output, &olen)) != 0 || + (r = sshbuf_put(state->input, input, ilen)) != 0 || + (r = sshbuf_put(state->output, output, olen)) != 0) + return r; + + if (sshbuf_len(m)) + return SSH_ERR_INVALID_FORMAT; + debug3_f("done"); + return 0; +} + +/* NEW API */ + +/* put data to the outgoing packet */ + +int +sshpkt_put(struct ssh *ssh, const void *v, size_t len) +{ + return sshbuf_put(ssh->state->outgoing_packet, v, len); +} + +int +sshpkt_putb(struct ssh *ssh, const struct sshbuf *b) +{ + return sshbuf_putb(ssh->state->outgoing_packet, b); +} + +int +sshpkt_put_u8(struct ssh *ssh, u_char val) +{ + return sshbuf_put_u8(ssh->state->outgoing_packet, val); +} + +int +sshpkt_put_u32(struct ssh *ssh, u_int32_t val) +{ + return sshbuf_put_u32(ssh->state->outgoing_packet, val); +} + +int +sshpkt_put_u64(struct ssh *ssh, u_int64_t val) +{ + return sshbuf_put_u64(ssh->state->outgoing_packet, val); +} + +int +sshpkt_put_string(struct ssh *ssh, const void *v, size_t len) +{ + return sshbuf_put_string(ssh->state->outgoing_packet, v, len); +} + +int +sshpkt_put_cstring(struct ssh *ssh, const void *v) +{ + return sshbuf_put_cstring(ssh->state->outgoing_packet, v); +} + +int +sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v) +{ + return sshbuf_put_stringb(ssh->state->outgoing_packet, v); +} + +int +sshpkt_getb_froms(struct ssh *ssh, struct sshbuf **valp) +{ + return sshbuf_froms(ssh->state->incoming_packet, valp); +} + +#ifdef WITH_OPENSSL +#ifdef OPENSSL_HAS_ECC +int +sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g) +{ + return sshbuf_put_ec(ssh->state->outgoing_packet, v, g); +} +#endif /* OPENSSL_HAS_ECC */ + + +int +sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v) +{ + return sshbuf_put_bignum2(ssh->state->outgoing_packet, v); +} +#endif /* WITH_OPENSSL */ + +/* fetch data from the incoming packet */ + +int +sshpkt_get(struct ssh *ssh, void *valp, size_t len) +{ + return sshbuf_get(ssh->state->incoming_packet, valp, len); +} + +int +sshpkt_get_u8(struct ssh *ssh, u_char *valp) +{ + return sshbuf_get_u8(ssh->state->incoming_packet, valp); +} + +int +sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp) +{ + return sshbuf_get_u32(ssh->state->incoming_packet, valp); +} + +int +sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp) +{ + return sshbuf_get_u64(ssh->state->incoming_packet, valp); +} + +int +sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp) +{ + return sshbuf_get_string(ssh->state->incoming_packet, valp, lenp); +} + +int +sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) +{ + return sshbuf_get_string_direct(ssh->state->incoming_packet, valp, lenp); +} + +int +sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) +{ + return sshbuf_peek_string_direct(ssh->state->incoming_packet, valp, lenp); +} + +int +sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp) +{ + return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp); +} + +#ifdef WITH_OPENSSL +#ifdef OPENSSL_HAS_ECC +int +sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g) +{ + return sshbuf_get_ec(ssh->state->incoming_packet, v, g); +} +#endif /* OPENSSL_HAS_ECC */ + +int +sshpkt_get_bignum2(struct ssh *ssh, BIGNUM **valp) +{ + return sshbuf_get_bignum2(ssh->state->incoming_packet, valp); +} +#endif /* WITH_OPENSSL */ + +int +sshpkt_get_end(struct ssh *ssh) +{ + if (sshbuf_len(ssh->state->incoming_packet) > 0) + return SSH_ERR_UNEXPECTED_TRAILING_DATA; + return 0; +} + +const u_char * +sshpkt_ptr(struct ssh *ssh, size_t *lenp) +{ + if (lenp != NULL) + *lenp = sshbuf_len(ssh->state->incoming_packet); + return sshbuf_ptr(ssh->state->incoming_packet); +} + +/* start a new packet */ + +int +sshpkt_start(struct ssh *ssh, u_char type) +{ + u_char buf[6]; /* u32 packet length, u8 pad len, u8 type */ + + DBG(debug("packet_start[%d]", type)); + memset(buf, 0, sizeof(buf)); + buf[sizeof(buf) - 1] = type; + sshbuf_reset(ssh->state->outgoing_packet); + return sshbuf_put(ssh->state->outgoing_packet, buf, sizeof(buf)); +} + +static int +ssh_packet_send_mux(struct ssh *ssh) +{ + struct session_state *state = ssh->state; + u_char type, *cp; + size_t len; + int r; + + if (ssh->kex) + return SSH_ERR_INTERNAL_ERROR; + len = sshbuf_len(state->outgoing_packet); + if (len < 6) + return SSH_ERR_INTERNAL_ERROR; + cp = sshbuf_mutable_ptr(state->outgoing_packet); + type = cp[5]; + if (ssh_packet_log_type(type)) + debug3_f("type %u", type); + /* drop everything, but the connection protocol */ + if (type >= SSH2_MSG_CONNECTION_MIN && + type <= SSH2_MSG_CONNECTION_MAX) { + POKE_U32(cp, len - 4); + if ((r = sshbuf_putb(state->output, + state->outgoing_packet)) != 0) + return r; + /* sshbuf_dump(state->output, stderr); */ + } + sshbuf_reset(state->outgoing_packet); + return 0; +} + +/* + * 9.2. Ignored Data Message + * + * byte SSH_MSG_IGNORE + * string data + * + * All implementations MUST understand (and ignore) this message at any + * time (after receiving the protocol version). No implementation is + * required to send them. This message can be used as an additional + * protection measure against advanced traffic analysis techniques. + */ +int +sshpkt_msg_ignore(struct ssh *ssh, u_int nbytes) +{ + u_int32_t rnd = 0; + int r; + u_int i; + + if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || + (r = sshpkt_put_u32(ssh, nbytes)) != 0) + return r; + for (i = 0; i < nbytes; i++) { + if (i % 4 == 0) + rnd = arc4random(); + if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0) + return r; + rnd >>= 8; + } + return 0; +} + +/* send it */ + +int +sshpkt_send(struct ssh *ssh) +{ + if (ssh->state && ssh->state->mux) + return ssh_packet_send_mux(ssh); + return ssh_packet_send2(ssh); +} + +int +sshpkt_disconnect(struct ssh *ssh, const char *fmt,...) +{ + char buf[1024]; + va_list args; + int r; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || + (r = sshpkt_put_cstring(ssh, buf)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + return 0; +} + +/* roundup current message to pad bytes */ +int +sshpkt_add_padding(struct ssh *ssh, u_char pad) +{ + ssh->state->extra_pad = pad; + return 0; +} diff --git a/aosp/external/openssh/packet.h b/aosp/external/openssh/packet.h new file mode 100644 index 0000000000000000000000000000000000000000..176488b1e5d2f235c366dab1e970ac0617779c81 --- /dev/null +++ b/aosp/external/openssh/packet.h @@ -0,0 +1,223 @@ +/* $OpenBSD: packet.h,v 1.94 2022/01/22 00:49:34 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Interface for the packet protocol functions. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef PACKET_H +#define PACKET_H + +#include + +#ifdef WITH_OPENSSL +# include +# ifdef OPENSSL_HAS_ECC +# include +# else /* OPENSSL_HAS_ECC */ +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +# endif /* OPENSSL_HAS_ECC */ +#else /* WITH_OPENSSL */ +# define BIGNUM void +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +#endif /* WITH_OPENSSL */ + +#include +#include "openbsd-compat/sys-queue.h" + +struct kex; +struct sshkey; +struct sshbuf; +struct session_state; /* private session data */ + +#include "dispatch.h" /* typedef, DISPATCH_MAX */ + +struct key_entry { + TAILQ_ENTRY(key_entry) next; + struct sshkey *key; +}; + +struct ssh { + /* Session state */ + struct session_state *state; + + /* Key exchange */ + struct kex *kex; + + /* cached local and remote ip addresses and ports */ + char *remote_ipaddr; + int remote_port; + char *local_ipaddr; + int local_port; + char *rdomain_in; + + /* Optional preamble for log messages (e.g. username) */ + char *log_preamble; + + /* Dispatcher table */ + dispatch_fn *dispatch[DISPATCH_MAX]; + /* number of packets to ignore in the dispatcher */ + int dispatch_skip_packets; + + /* datafellows */ + int compat; + + /* Lists for private and public keys */ + TAILQ_HEAD(, key_entry) private_keys; + TAILQ_HEAD(, key_entry) public_keys; + + /* Client/Server authentication context */ + void *authctxt; + + /* Channels context */ + struct ssh_channels *chanctxt; + + /* APP data */ + void *app_data; +}; + +typedef int (ssh_packet_hook_fn)(struct ssh *, struct sshbuf *, + u_char *, void *); + +struct ssh *ssh_alloc_session_state(void); +struct ssh *ssh_packet_set_connection(struct ssh *, int, int); +void ssh_packet_set_timeout(struct ssh *, int, int); +int ssh_packet_stop_discard(struct ssh *); +int ssh_packet_connection_af(struct ssh *); +void ssh_packet_set_nonblocking(struct ssh *); +int ssh_packet_get_connection_in(struct ssh *); +int ssh_packet_get_connection_out(struct ssh *); +void ssh_packet_close(struct ssh *); +void ssh_packet_set_input_hook(struct ssh *, ssh_packet_hook_fn *, void *); +void ssh_packet_clear_keys(struct ssh *); +void ssh_clear_newkeys(struct ssh *, int); + +int ssh_packet_is_rekeying(struct ssh *); +int ssh_packet_check_rekey(struct ssh *); +void ssh_packet_set_protocol_flags(struct ssh *, u_int); +u_int ssh_packet_get_protocol_flags(struct ssh *); +void ssh_packet_set_tos(struct ssh *, int); +void ssh_packet_set_interactive(struct ssh *, int, int, int); +int ssh_packet_is_interactive(struct ssh *); +void ssh_packet_set_server(struct ssh *); +void ssh_packet_set_authenticated(struct ssh *); +void ssh_packet_set_mux(struct ssh *); +int ssh_packet_get_mux(struct ssh *); +int ssh_packet_set_log_preamble(struct ssh *, const char *, ...) + __attribute__((format(printf, 2, 3))); + +int ssh_packet_log_type(u_char); + +int ssh_packet_send2_wrapped(struct ssh *); +int ssh_packet_send2(struct ssh *); + +int ssh_packet_read(struct ssh *); +int ssh_packet_read_expect(struct ssh *, u_int type); +int ssh_packet_read_poll(struct ssh *); +int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); +int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len); +int ssh_packet_process_read(struct ssh *, int); +int ssh_packet_read_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); +int ssh_packet_read_poll_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); + +const void *ssh_packet_get_string_ptr(struct ssh *, u_int *length_ptr); +void ssh_packet_disconnect(struct ssh *, const char *fmt, ...) + __attribute__((format(printf, 2, 3))) + __attribute__((noreturn)); +void ssh_packet_send_debug(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); + +int ssh_set_newkeys(struct ssh *, int mode); +void ssh_packet_get_bytes(struct ssh *, u_int64_t *, u_int64_t *); + +int ssh_packet_write_poll(struct ssh *); +int ssh_packet_write_wait(struct ssh *); +int ssh_packet_have_data_to_write(struct ssh *); +int ssh_packet_not_very_much_data_to_write(struct ssh *); + +int ssh_packet_connection_is_on_socket(struct ssh *); +int ssh_packet_remaining(struct ssh *); + +void ssh_tty_make_modes(struct ssh *, int, struct termios *); +void ssh_tty_parse_modes(struct ssh *, int); + +void ssh_packet_set_alive_timeouts(struct ssh *, int); +int ssh_packet_inc_alive_timeouts(struct ssh *); +int ssh_packet_set_maxsize(struct ssh *, u_int); +u_int ssh_packet_get_maxsize(struct ssh *); + +int ssh_packet_get_state(struct ssh *, struct sshbuf *); +int ssh_packet_set_state(struct ssh *, struct sshbuf *); + +const char *ssh_remote_ipaddr(struct ssh *); +int ssh_remote_port(struct ssh *); +const char *ssh_local_ipaddr(struct ssh *); +int ssh_local_port(struct ssh *); +const char *ssh_packet_rdomain_in(struct ssh *); + +void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, u_int32_t); +time_t ssh_packet_get_rekey_timeout(struct ssh *); + +void *ssh_packet_get_input(struct ssh *); +void *ssh_packet_get_output(struct ssh *); + +/* new API */ +int sshpkt_start(struct ssh *ssh, u_char type); +int sshpkt_send(struct ssh *ssh); +int sshpkt_disconnect(struct ssh *, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +int sshpkt_add_padding(struct ssh *, u_char); +void sshpkt_fatal(struct ssh *ssh, int r, const char *fmt, ...) + __attribute__((format(printf, 3, 4))) + __attribute__((noreturn)); +int sshpkt_msg_ignore(struct ssh *, u_int); + +int sshpkt_put(struct ssh *ssh, const void *v, size_t len); +int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b); +int sshpkt_put_u8(struct ssh *ssh, u_char val); +int sshpkt_put_u32(struct ssh *ssh, u_int32_t val); +int sshpkt_put_u64(struct ssh *ssh, u_int64_t val); +int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len); +int sshpkt_put_cstring(struct ssh *ssh, const void *v); +int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v); +int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g); +int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v); + +int sshpkt_get(struct ssh *ssh, void *valp, size_t len); +int sshpkt_get_u8(struct ssh *ssh, u_char *valp); +int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp); +int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp); +int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp); +int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); +int sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); +int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp); +int sshpkt_getb_froms(struct ssh *ssh, struct sshbuf **valp); +int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g); +int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM **valp); +int sshpkt_get_end(struct ssh *ssh); +void sshpkt_fmt_connection_id(struct ssh *ssh, char *s, size_t l); +const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); + +#if !defined(WITH_OPENSSL) +# undef BIGNUM +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#elif !defined(OPENSSL_HAS_ECC) +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#endif + +#endif /* PACKET_H */ diff --git a/aosp/external/openssh/pathnames.h b/aosp/external/openssh/pathnames.h new file mode 100644 index 0000000000000000000000000000000000000000..f7ca5a75a0d2ec763c5863c33dae13445816dfad --- /dev/null +++ b/aosp/external/openssh/pathnames.h @@ -0,0 +1,179 @@ +/* $OpenBSD: pathnames.h,v 1.31 2019/11/12 19:33:08 markus Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#define ETCDIR "/etc" + +#ifndef SSHDIR +#define SSHDIR ETCDIR "/ssh" +#endif + +#ifndef _PATH_SSH_PIDDIR +#define _PATH_SSH_PIDDIR "/var/run" +#endif + +/* + * System-wide file containing host keys of known hosts. This file should be + * world-readable. + */ +#define _PATH_SSH_SYSTEM_HOSTFILE SSHDIR "/ssh_known_hosts" +/* backward compat for protocol 2 */ +#define _PATH_SSH_SYSTEM_HOSTFILE2 SSHDIR "/ssh_known_hosts2" + +/* + * Of these, ssh_host_key must be readable only by root, whereas ssh_config + * should be world-readable. + */ +#define _PATH_SERVER_CONFIG_FILE SSHDIR "/sshd_config" +#define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" +#define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" +#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key" +#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key" +#define _PATH_HOST_XMSS_KEY_FILE SSHDIR "/ssh_host_xmss_key" +#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" +#define _PATH_DH_MODULI SSHDIR "/moduli" + +#ifndef _PATH_SSH_PROGRAM +#define _PATH_SSH_PROGRAM "/usr/bin/ssh" +#endif + +/* + * The process id of the daemon listening for connections is saved here to + * make it easier to kill the correct daemon when necessary. + */ +#define _PATH_SSH_DAEMON_PID_FILE _PATH_SSH_PIDDIR "/sshd.pid" + +/* + * The directory in user's home directory in which the files reside. The + * directory should be world-readable (though not all files are). + */ +#define _PATH_SSH_USER_DIR ".ssh" + +/* + * Per-user file containing host keys of known hosts. This file need not be + * readable by anyone except the user him/herself, though this does not + * contain anything particularly secret. + */ +#define _PATH_SSH_USER_HOSTFILE "~/" _PATH_SSH_USER_DIR "/known_hosts" +/* backward compat for protocol 2 */ +#define _PATH_SSH_USER_HOSTFILE2 "~/" _PATH_SSH_USER_DIR "/known_hosts2" + +/* + * Name of the default file containing client-side authentication key. This + * file should only be readable by the user him/herself. + */ +#define _PATH_SSH_CLIENT_ID_DSA _PATH_SSH_USER_DIR "/id_dsa" +#define _PATH_SSH_CLIENT_ID_ECDSA _PATH_SSH_USER_DIR "/id_ecdsa" +#define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa" +#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519" +#define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss" +#define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk" +#define _PATH_SSH_CLIENT_ID_ED25519_SK _PATH_SSH_USER_DIR "/id_ed25519_sk" + +/* + * Configuration file in user's home directory. This file need not be + * readable by anyone but the user him/herself, but does not contain anything + * particularly secret. If the user's home directory resides on an NFS + * volume where root is mapped to nobody, this may need to be world-readable. + */ +#define _PATH_SSH_USER_CONFFILE _PATH_SSH_USER_DIR "/config" + +/* + * File containing a list of those rsa keys that permit logging in as this + * user. This file need not be readable by anyone but the user him/herself, + * but does not contain anything particularly secret. If the user's home + * directory resides on an NFS volume where root is mapped to nobody, this + * may need to be world-readable. (This file is read by the daemon which is + * running as root.) + */ +#define _PATH_SSH_USER_PERMITTED_KEYS _PATH_SSH_USER_DIR "/authorized_keys" + +/* backward compat for protocol v2 */ +#define _PATH_SSH_USER_PERMITTED_KEYS2 _PATH_SSH_USER_DIR "/authorized_keys2" + +/* + * Per-user and system-wide ssh "rc" files. These files are executed with + * /bin/sh before starting the shell or command if they exist. They will be + * passed "proto cookie" as arguments if X11 forwarding with spoofing is in + * use. xauth will be run if neither of these exists. + */ +#define _PATH_SSH_USER_RC _PATH_SSH_USER_DIR "/rc" +#define _PATH_SSH_SYSTEM_RC SSHDIR "/sshrc" + +/* + * Ssh-only version of /etc/hosts.equiv. Additionally, the daemon may use + * ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. + */ +#define _PATH_SSH_HOSTS_EQUIV SSHDIR "/shosts.equiv" +#define _PATH_RHOSTS_EQUIV "/etc/hosts.equiv" + +/* + * Default location of askpass + */ +#ifndef _PATH_SSH_ASKPASS_DEFAULT +#define _PATH_SSH_ASKPASS_DEFAULT "/usr/X11R6/bin/ssh-askpass" +#endif + +/* Location of ssh-keysign for hostbased authentication */ +#ifndef _PATH_SSH_KEY_SIGN +#define _PATH_SSH_KEY_SIGN "/usr/libexec/ssh-keysign" +#endif + +/* Location of ssh-pkcs11-helper to support keys in tokens */ +#ifndef _PATH_SSH_PKCS11_HELPER +#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" +#endif + +/* Location of ssh-sk-helper to support keys in security keys */ +#ifndef _PATH_SSH_SK_HELPER +#define _PATH_SSH_SK_HELPER "/usr/libexec/ssh-sk-helper" +#endif + +/* xauth for X11 forwarding */ +#ifndef _PATH_XAUTH +#define _PATH_XAUTH "/usr/X11R6/bin/xauth" +#endif + +/* UNIX domain socket for X11 server; displaynum will replace %u */ +#ifndef _PATH_UNIX_X +#define _PATH_UNIX_X "/tmp/.X11-unix/X%u" +#endif + +/* for scp */ +#ifndef _PATH_CP +#define _PATH_CP "cp" +#endif + +/* for sftp */ +#ifndef _PATH_SFTP_SERVER +#define _PATH_SFTP_SERVER "/usr/libexec/sftp-server" +#endif + +/* chroot directory for unprivileged user when UsePrivilegeSeparation=yes */ +#ifndef _PATH_PRIVSEP_CHROOT_DIR +#define _PATH_PRIVSEP_CHROOT_DIR "/var/empty" +#endif + +/* for passwd change */ +#ifndef _PATH_PASSWD_PROG +#define _PATH_PASSWD_PROG "/usr/bin/passwd" +#endif + +#ifndef _PATH_LS +#define _PATH_LS "ls" +#endif + +/* Askpass program define */ +#ifndef ASKPASS_PROGRAM +#define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" +#endif /* ASKPASS_PROGRAM */ diff --git a/aosp/external/openssh/pkcs11.h b/aosp/external/openssh/pkcs11.h new file mode 100644 index 0000000000000000000000000000000000000000..b01d58f9483af5e238814faf0ff25ad9d2daf101 --- /dev/null +++ b/aosp/external/openssh/pkcs11.h @@ -0,0 +1,1357 @@ +/* $OpenBSD: pkcs11.h,v 1.3 2013/11/26 19:15:09 deraadt Exp $ */ +/* pkcs11.h + Copyright 2006, 2007 g10 Code GmbH + Copyright 2006 Andreas Jellinghaus + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. */ + +/* Please submit changes back to the Scute project at + http://www.scute.org/ (or send them to marcus@g10code.com), so that + they can be picked up by other projects from there as well. */ + +/* This file is a modified implementation of the PKCS #11 standard by + RSA Security Inc. It is mostly a drop-in replacement, with the + following change: + + This header file does not require any macro definitions by the user + (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros + for you (if useful, some are missing, let me know if you need + more). + + There is an additional API available that does comply better to the + GNU coding standard. It can be switched on by defining + CRYPTOKI_GNU before including this header file. For this, the + following changes are made to the specification: + + All structure types are changed to a "struct ck_foo" where CK_FOO + is the type name in PKCS #11. + + All non-structure types are changed to ck_foo_t where CK_FOO is the + lowercase version of the type name in PKCS #11. The basic types + (CK_ULONG et al.) are removed without substitute. + + All members of structures are modified in the following way: Type + indication prefixes are removed, and underscore characters are + inserted before words. Then the result is lowercased. + + Note that function names are still in the original case, as they + need for ABI compatibility. + + CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use + . + + If CRYPTOKI_COMPAT is defined before including this header file, + then none of the API changes above take place, and the API is the + one defined by the PKCS #11 standard. */ + +#ifndef PKCS11_H +#define PKCS11_H 1 + +#if defined(__cplusplus) +extern "C" { +#endif + + +/* The version of cryptoki we implement. The revision is changed with + each modification of this file. If you do not use the "official" + version of this file, please consider deleting the revision macro + (you may use a macro with a different name to keep track of your + versions). */ +#define CRYPTOKI_VERSION_MAJOR 2 +#define CRYPTOKI_VERSION_MINOR 20 +#define CRYPTOKI_VERSION_REVISION 6 + + +/* Compatibility interface is default, unless CRYPTOKI_GNU is + given. */ +#ifndef CRYPTOKI_GNU +#ifndef CRYPTOKI_COMPAT +#define CRYPTOKI_COMPAT 1 +#endif +#endif + +/* System dependencies. */ + +#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) + +/* There is a matching pop below. */ +#pragma pack(push, cryptoki, 1) + +#ifdef CRYPTOKI_EXPORTS +#define CK_SPEC __declspec(dllexport) +#else +#define CK_SPEC __declspec(dllimport) +#endif + +#else + +#define CK_SPEC + +#endif + + +#ifdef CRYPTOKI_COMPAT + /* If we are in compatibility mode, switch all exposed names to the + PKCS #11 variant. There are corresponding #undefs below. */ + +#define ck_flags_t CK_FLAGS +#define ck_version _CK_VERSION + +#define ck_info _CK_INFO +#define cryptoki_version cryptokiVersion +#define manufacturer_id manufacturerID +#define library_description libraryDescription +#define library_version libraryVersion + +#define ck_notification_t CK_NOTIFICATION +#define ck_slot_id_t CK_SLOT_ID + +#define ck_slot_info _CK_SLOT_INFO +#define slot_description slotDescription +#define hardware_version hardwareVersion +#define firmware_version firmwareVersion + +#define ck_token_info _CK_TOKEN_INFO +#define serial_number serialNumber +#define max_session_count ulMaxSessionCount +#define session_count ulSessionCount +#define max_rw_session_count ulMaxRwSessionCount +#define rw_session_count ulRwSessionCount +#define max_pin_len ulMaxPinLen +#define min_pin_len ulMinPinLen +#define total_public_memory ulTotalPublicMemory +#define free_public_memory ulFreePublicMemory +#define total_private_memory ulTotalPrivateMemory +#define free_private_memory ulFreePrivateMemory +#define utc_time utcTime + +#define ck_session_handle_t CK_SESSION_HANDLE +#define ck_user_type_t CK_USER_TYPE +#define ck_state_t CK_STATE + +#define ck_session_info _CK_SESSION_INFO +#define slot_id slotID +#define device_error ulDeviceError + +#define ck_object_handle_t CK_OBJECT_HANDLE +#define ck_object_class_t CK_OBJECT_CLASS +#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE +#define ck_key_type_t CK_KEY_TYPE +#define ck_certificate_type_t CK_CERTIFICATE_TYPE +#define ck_attribute_type_t CK_ATTRIBUTE_TYPE + +#define ck_attribute _CK_ATTRIBUTE +#define value pValue +#define value_len ulValueLen + +#define ck_date _CK_DATE + +#define ck_mechanism_type_t CK_MECHANISM_TYPE + +#define ck_mechanism _CK_MECHANISM +#define parameter pParameter +#define parameter_len ulParameterLen + +#define ck_mechanism_info _CK_MECHANISM_INFO +#define min_key_size ulMinKeySize +#define max_key_size ulMaxKeySize + +#define ck_rv_t CK_RV +#define ck_notify_t CK_NOTIFY + +#define ck_function_list _CK_FUNCTION_LIST + +#define ck_createmutex_t CK_CREATEMUTEX +#define ck_destroymutex_t CK_DESTROYMUTEX +#define ck_lockmutex_t CK_LOCKMUTEX +#define ck_unlockmutex_t CK_UNLOCKMUTEX + +#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS +#define create_mutex CreateMutex +#define destroy_mutex DestroyMutex +#define lock_mutex LockMutex +#define unlock_mutex UnlockMutex +#define reserved pReserved + +#endif /* CRYPTOKI_COMPAT */ + + + +typedef unsigned long ck_flags_t; + +struct ck_version +{ + unsigned char major; + unsigned char minor; +}; + + +struct ck_info +{ + struct ck_version cryptoki_version; + unsigned char manufacturer_id[32]; + ck_flags_t flags; + unsigned char library_description[32]; + struct ck_version library_version; +}; + + +typedef unsigned long ck_notification_t; + +#define CKN_SURRENDER (0) + + +typedef unsigned long ck_slot_id_t; + + +struct ck_slot_info +{ + unsigned char slot_description[64]; + unsigned char manufacturer_id[32]; + ck_flags_t flags; + struct ck_version hardware_version; + struct ck_version firmware_version; +}; + + +#define CKF_TOKEN_PRESENT (1 << 0) +#define CKF_REMOVABLE_DEVICE (1 << 1) +#define CKF_HW_SLOT (1 << 2) +#define CKF_ARRAY_ATTRIBUTE (1 << 30) + + +struct ck_token_info +{ + unsigned char label[32]; + unsigned char manufacturer_id[32]; + unsigned char model[16]; + unsigned char serial_number[16]; + ck_flags_t flags; + unsigned long max_session_count; + unsigned long session_count; + unsigned long max_rw_session_count; + unsigned long rw_session_count; + unsigned long max_pin_len; + unsigned long min_pin_len; + unsigned long total_public_memory; + unsigned long free_public_memory; + unsigned long total_private_memory; + unsigned long free_private_memory; + struct ck_version hardware_version; + struct ck_version firmware_version; + unsigned char utc_time[16]; +}; + + +#define CKF_RNG (1 << 0) +#define CKF_WRITE_PROTECTED (1 << 1) +#define CKF_LOGIN_REQUIRED (1 << 2) +#define CKF_USER_PIN_INITIALIZED (1 << 3) +#define CKF_RESTORE_KEY_NOT_NEEDED (1 << 5) +#define CKF_CLOCK_ON_TOKEN (1 << 6) +#define CKF_PROTECTED_AUTHENTICATION_PATH (1 << 8) +#define CKF_DUAL_CRYPTO_OPERATIONS (1 << 9) +#define CKF_TOKEN_INITIALIZED (1 << 10) +#define CKF_SECONDARY_AUTHENTICATION (1 << 11) +#define CKF_USER_PIN_COUNT_LOW (1 << 16) +#define CKF_USER_PIN_FINAL_TRY (1 << 17) +#define CKF_USER_PIN_LOCKED (1 << 18) +#define CKF_USER_PIN_TO_BE_CHANGED (1 << 19) +#define CKF_SO_PIN_COUNT_LOW (1 << 20) +#define CKF_SO_PIN_FINAL_TRY (1 << 21) +#define CKF_SO_PIN_LOCKED (1 << 22) +#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23) + +#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1) +#define CK_EFFECTIVELY_INFINITE (0) + + +typedef unsigned long ck_session_handle_t; + +#define CK_INVALID_HANDLE (0) + + +typedef unsigned long ck_user_type_t; + +#define CKU_SO (0) +#define CKU_USER (1) +#define CKU_CONTEXT_SPECIFIC (2) + + +typedef unsigned long ck_state_t; + +#define CKS_RO_PUBLIC_SESSION (0) +#define CKS_RO_USER_FUNCTIONS (1) +#define CKS_RW_PUBLIC_SESSION (2) +#define CKS_RW_USER_FUNCTIONS (3) +#define CKS_RW_SO_FUNCTIONS (4) + + +struct ck_session_info +{ + ck_slot_id_t slot_id; + ck_state_t state; + ck_flags_t flags; + unsigned long device_error; +}; + +#define CKF_RW_SESSION (1 << 1) +#define CKF_SERIAL_SESSION (1 << 2) + + +typedef unsigned long ck_object_handle_t; + + +typedef unsigned long ck_object_class_t; + +#define CKO_DATA (0) +#define CKO_CERTIFICATE (1) +#define CKO_PUBLIC_KEY (2) +#define CKO_PRIVATE_KEY (3) +#define CKO_SECRET_KEY (4) +#define CKO_HW_FEATURE (5) +#define CKO_DOMAIN_PARAMETERS (6) +#define CKO_MECHANISM (7) +#define CKO_VENDOR_DEFINED (1U << 31) + + +typedef unsigned long ck_hw_feature_type_t; + +#define CKH_MONOTONIC_COUNTER (1) +#define CKH_CLOCK (2) +#define CKH_USER_INTERFACE (3) +#define CKH_VENDOR_DEFINED (1U << 31) + + +typedef unsigned long ck_key_type_t; + +#define CKK_RSA (0) +#define CKK_DSA (1) +#define CKK_DH (2) +#define CKK_ECDSA (3) +#define CKK_EC (3) +#define CKK_X9_42_DH (4) +#define CKK_KEA (5) +#define CKK_GENERIC_SECRET (0x10) +#define CKK_RC2 (0x11) +#define CKK_RC4 (0x12) +#define CKK_DES (0x13) +#define CKK_DES2 (0x14) +#define CKK_DES3 (0x15) +#define CKK_CAST (0x16) +#define CKK_CAST3 (0x17) +#define CKK_CAST128 (0x18) +#define CKK_RC5 (0x19) +#define CKK_IDEA (0x1a) +#define CKK_SKIPJACK (0x1b) +#define CKK_BATON (0x1c) +#define CKK_JUNIPER (0x1d) +#define CKK_CDMF (0x1e) +#define CKK_AES (0x1f) +#define CKK_BLOWFISH (0x20) +#define CKK_TWOFISH (0x21) +#define CKK_VENDOR_DEFINED (1U << 31) + +typedef unsigned long ck_certificate_type_t; + +#define CKC_X_509 (0) +#define CKC_X_509_ATTR_CERT (1) +#define CKC_WTLS (2) +#define CKC_VENDOR_DEFINED (1U << 31) + + +typedef unsigned long ck_attribute_type_t; + +#define CKA_CLASS (0) +#define CKA_TOKEN (1) +#define CKA_PRIVATE (2) +#define CKA_LABEL (3) +#define CKA_APPLICATION (0x10) +#define CKA_VALUE (0x11) +#define CKA_OBJECT_ID (0x12) +#define CKA_CERTIFICATE_TYPE (0x80) +#define CKA_ISSUER (0x81) +#define CKA_SERIAL_NUMBER (0x82) +#define CKA_AC_ISSUER (0x83) +#define CKA_OWNER (0x84) +#define CKA_ATTR_TYPES (0x85) +#define CKA_TRUSTED (0x86) +#define CKA_CERTIFICATE_CATEGORY (0x87) +#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88) +#define CKA_URL (0x89) +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a) +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b) +#define CKA_CHECK_VALUE (0x90) +#define CKA_KEY_TYPE (0x100) +#define CKA_SUBJECT (0x101) +#define CKA_ID (0x102) +#define CKA_SENSITIVE (0x103) +#define CKA_ENCRYPT (0x104) +#define CKA_DECRYPT (0x105) +#define CKA_WRAP (0x106) +#define CKA_UNWRAP (0x107) +#define CKA_SIGN (0x108) +#define CKA_SIGN_RECOVER (0x109) +#define CKA_VERIFY (0x10a) +#define CKA_VERIFY_RECOVER (0x10b) +#define CKA_DERIVE (0x10c) +#define CKA_START_DATE (0x110) +#define CKA_END_DATE (0x111) +#define CKA_MODULUS (0x120) +#define CKA_MODULUS_BITS (0x121) +#define CKA_PUBLIC_EXPONENT (0x122) +#define CKA_PRIVATE_EXPONENT (0x123) +#define CKA_PRIME_1 (0x124) +#define CKA_PRIME_2 (0x125) +#define CKA_EXPONENT_1 (0x126) +#define CKA_EXPONENT_2 (0x127) +#define CKA_COEFFICIENT (0x128) +#define CKA_PRIME (0x130) +#define CKA_SUBPRIME (0x131) +#define CKA_BASE (0x132) +#define CKA_PRIME_BITS (0x133) +#define CKA_SUB_PRIME_BITS (0x134) +#define CKA_VALUE_BITS (0x160) +#define CKA_VALUE_LEN (0x161) +#define CKA_EXTRACTABLE (0x162) +#define CKA_LOCAL (0x163) +#define CKA_NEVER_EXTRACTABLE (0x164) +#define CKA_ALWAYS_SENSITIVE (0x165) +#define CKA_KEY_GEN_MECHANISM (0x166) +#define CKA_MODIFIABLE (0x170) +#define CKA_ECDSA_PARAMS (0x180) +#define CKA_EC_PARAMS (0x180) +#define CKA_EC_POINT (0x181) +#define CKA_SECONDARY_AUTH (0x200) +#define CKA_AUTH_PIN_FLAGS (0x201) +#define CKA_ALWAYS_AUTHENTICATE (0x202) +#define CKA_WRAP_WITH_TRUSTED (0x210) +#define CKA_HW_FEATURE_TYPE (0x300) +#define CKA_RESET_ON_INIT (0x301) +#define CKA_HAS_RESET (0x302) +#define CKA_PIXEL_X (0x400) +#define CKA_PIXEL_Y (0x401) +#define CKA_RESOLUTION (0x402) +#define CKA_CHAR_ROWS (0x403) +#define CKA_CHAR_COLUMNS (0x404) +#define CKA_COLOR (0x405) +#define CKA_BITS_PER_PIXEL (0x406) +#define CKA_CHAR_SETS (0x480) +#define CKA_ENCODING_METHODS (0x481) +#define CKA_MIME_TYPES (0x482) +#define CKA_MECHANISM_TYPE (0x500) +#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501) +#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502) +#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503) +#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211) +#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212) +#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600) +#define CKA_VENDOR_DEFINED (1U << 31) + + +struct ck_attribute +{ + ck_attribute_type_t type; + void *value; + unsigned long value_len; +}; + + +struct ck_date +{ + unsigned char year[4]; + unsigned char month[2]; + unsigned char day[2]; +}; + + +typedef unsigned long ck_mechanism_type_t; + +#define CKM_RSA_PKCS_KEY_PAIR_GEN (0) +#define CKM_RSA_PKCS (1) +#define CKM_RSA_9796 (2) +#define CKM_RSA_X_509 (3) +#define CKM_MD2_RSA_PKCS (4) +#define CKM_MD5_RSA_PKCS (5) +#define CKM_SHA1_RSA_PKCS (6) +#define CKM_RIPEMD128_RSA_PKCS (7) +#define CKM_RIPEMD160_RSA_PKCS (8) +#define CKM_RSA_PKCS_OAEP (9) +#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xa) +#define CKM_RSA_X9_31 (0xb) +#define CKM_SHA1_RSA_X9_31 (0xc) +#define CKM_RSA_PKCS_PSS (0xd) +#define CKM_SHA1_RSA_PKCS_PSS (0xe) +#define CKM_DSA_KEY_PAIR_GEN (0x10) +#define CKM_DSA (0x11) +#define CKM_DSA_SHA1 (0x12) +#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20) +#define CKM_DH_PKCS_DERIVE (0x21) +#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30) +#define CKM_X9_42_DH_DERIVE (0x31) +#define CKM_X9_42_DH_HYBRID_DERIVE (0x32) +#define CKM_X9_42_MQV_DERIVE (0x33) +#define CKM_SHA256_RSA_PKCS (0x40) +#define CKM_SHA384_RSA_PKCS (0x41) +#define CKM_SHA512_RSA_PKCS (0x42) +#define CKM_SHA256_RSA_PKCS_PSS (0x43) +#define CKM_SHA384_RSA_PKCS_PSS (0x44) +#define CKM_SHA512_RSA_PKCS_PSS (0x45) +#define CKM_RC2_KEY_GEN (0x100) +#define CKM_RC2_ECB (0x101) +#define CKM_RC2_CBC (0x102) +#define CKM_RC2_MAC (0x103) +#define CKM_RC2_MAC_GENERAL (0x104) +#define CKM_RC2_CBC_PAD (0x105) +#define CKM_RC4_KEY_GEN (0x110) +#define CKM_RC4 (0x111) +#define CKM_DES_KEY_GEN (0x120) +#define CKM_DES_ECB (0x121) +#define CKM_DES_CBC (0x122) +#define CKM_DES_MAC (0x123) +#define CKM_DES_MAC_GENERAL (0x124) +#define CKM_DES_CBC_PAD (0x125) +#define CKM_DES2_KEY_GEN (0x130) +#define CKM_DES3_KEY_GEN (0x131) +#define CKM_DES3_ECB (0x132) +#define CKM_DES3_CBC (0x133) +#define CKM_DES3_MAC (0x134) +#define CKM_DES3_MAC_GENERAL (0x135) +#define CKM_DES3_CBC_PAD (0x136) +#define CKM_CDMF_KEY_GEN (0x140) +#define CKM_CDMF_ECB (0x141) +#define CKM_CDMF_CBC (0x142) +#define CKM_CDMF_MAC (0x143) +#define CKM_CDMF_MAC_GENERAL (0x144) +#define CKM_CDMF_CBC_PAD (0x145) +#define CKM_MD2 (0x200) +#define CKM_MD2_HMAC (0x201) +#define CKM_MD2_HMAC_GENERAL (0x202) +#define CKM_MD5 (0x210) +#define CKM_MD5_HMAC (0x211) +#define CKM_MD5_HMAC_GENERAL (0x212) +#define CKM_SHA_1 (0x220) +#define CKM_SHA_1_HMAC (0x221) +#define CKM_SHA_1_HMAC_GENERAL (0x222) +#define CKM_RIPEMD128 (0x230) +#define CKM_RIPEMD128_HMAC (0x231) +#define CKM_RIPEMD128_HMAC_GENERAL (0x232) +#define CKM_RIPEMD160 (0x240) +#define CKM_RIPEMD160_HMAC (0x241) +#define CKM_RIPEMD160_HMAC_GENERAL (0x242) +#define CKM_SHA256 (0x250) +#define CKM_SHA256_HMAC (0x251) +#define CKM_SHA256_HMAC_GENERAL (0x252) +#define CKM_SHA384 (0x260) +#define CKM_SHA384_HMAC (0x261) +#define CKM_SHA384_HMAC_GENERAL (0x262) +#define CKM_SHA512 (0x270) +#define CKM_SHA512_HMAC (0x271) +#define CKM_SHA512_HMAC_GENERAL (0x272) +#define CKM_CAST_KEY_GEN (0x300) +#define CKM_CAST_ECB (0x301) +#define CKM_CAST_CBC (0x302) +#define CKM_CAST_MAC (0x303) +#define CKM_CAST_MAC_GENERAL (0x304) +#define CKM_CAST_CBC_PAD (0x305) +#define CKM_CAST3_KEY_GEN (0x310) +#define CKM_CAST3_ECB (0x311) +#define CKM_CAST3_CBC (0x312) +#define CKM_CAST3_MAC (0x313) +#define CKM_CAST3_MAC_GENERAL (0x314) +#define CKM_CAST3_CBC_PAD (0x315) +#define CKM_CAST5_KEY_GEN (0x320) +#define CKM_CAST128_KEY_GEN (0x320) +#define CKM_CAST5_ECB (0x321) +#define CKM_CAST128_ECB (0x321) +#define CKM_CAST5_CBC (0x322) +#define CKM_CAST128_CBC (0x322) +#define CKM_CAST5_MAC (0x323) +#define CKM_CAST128_MAC (0x323) +#define CKM_CAST5_MAC_GENERAL (0x324) +#define CKM_CAST128_MAC_GENERAL (0x324) +#define CKM_CAST5_CBC_PAD (0x325) +#define CKM_CAST128_CBC_PAD (0x325) +#define CKM_RC5_KEY_GEN (0x330) +#define CKM_RC5_ECB (0x331) +#define CKM_RC5_CBC (0x332) +#define CKM_RC5_MAC (0x333) +#define CKM_RC5_MAC_GENERAL (0x334) +#define CKM_RC5_CBC_PAD (0x335) +#define CKM_IDEA_KEY_GEN (0x340) +#define CKM_IDEA_ECB (0x341) +#define CKM_IDEA_CBC (0x342) +#define CKM_IDEA_MAC (0x343) +#define CKM_IDEA_MAC_GENERAL (0x344) +#define CKM_IDEA_CBC_PAD (0x345) +#define CKM_GENERIC_SECRET_KEY_GEN (0x350) +#define CKM_CONCATENATE_BASE_AND_KEY (0x360) +#define CKM_CONCATENATE_BASE_AND_DATA (0x362) +#define CKM_CONCATENATE_DATA_AND_BASE (0x363) +#define CKM_XOR_BASE_AND_DATA (0x364) +#define CKM_EXTRACT_KEY_FROM_KEY (0x365) +#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370) +#define CKM_SSL3_MASTER_KEY_DERIVE (0x371) +#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372) +#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373) +#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374) +#define CKM_TLS_MASTER_KEY_DERIVE (0x375) +#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376) +#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377) +#define CKM_SSL3_MD5_MAC (0x380) +#define CKM_SSL3_SHA1_MAC (0x381) +#define CKM_MD5_KEY_DERIVATION (0x390) +#define CKM_MD2_KEY_DERIVATION (0x391) +#define CKM_SHA1_KEY_DERIVATION (0x392) +#define CKM_PBE_MD2_DES_CBC (0x3a0) +#define CKM_PBE_MD5_DES_CBC (0x3a1) +#define CKM_PBE_MD5_CAST_CBC (0x3a2) +#define CKM_PBE_MD5_CAST3_CBC (0x3a3) +#define CKM_PBE_MD5_CAST5_CBC (0x3a4) +#define CKM_PBE_MD5_CAST128_CBC (0x3a4) +#define CKM_PBE_SHA1_CAST5_CBC (0x3a5) +#define CKM_PBE_SHA1_CAST128_CBC (0x3a5) +#define CKM_PBE_SHA1_RC4_128 (0x3a6) +#define CKM_PBE_SHA1_RC4_40 (0x3a7) +#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8) +#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9) +#define CKM_PBE_SHA1_RC2_128_CBC (0x3aa) +#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab) +#define CKM_PKCS5_PBKD2 (0x3b0) +#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0) +#define CKM_KEY_WRAP_LYNKS (0x400) +#define CKM_KEY_WRAP_SET_OAEP (0x401) +#define CKM_SKIPJACK_KEY_GEN (0x1000) +#define CKM_SKIPJACK_ECB64 (0x1001) +#define CKM_SKIPJACK_CBC64 (0x1002) +#define CKM_SKIPJACK_OFB64 (0x1003) +#define CKM_SKIPJACK_CFB64 (0x1004) +#define CKM_SKIPJACK_CFB32 (0x1005) +#define CKM_SKIPJACK_CFB16 (0x1006) +#define CKM_SKIPJACK_CFB8 (0x1007) +#define CKM_SKIPJACK_WRAP (0x1008) +#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009) +#define CKM_SKIPJACK_RELAYX (0x100a) +#define CKM_KEA_KEY_PAIR_GEN (0x1010) +#define CKM_KEA_KEY_DERIVE (0x1011) +#define CKM_FORTEZZA_TIMESTAMP (0x1020) +#define CKM_BATON_KEY_GEN (0x1030) +#define CKM_BATON_ECB128 (0x1031) +#define CKM_BATON_ECB96 (0x1032) +#define CKM_BATON_CBC128 (0x1033) +#define CKM_BATON_COUNTER (0x1034) +#define CKM_BATON_SHUFFLE (0x1035) +#define CKM_BATON_WRAP (0x1036) +#define CKM_ECDSA_KEY_PAIR_GEN (0x1040) +#define CKM_EC_KEY_PAIR_GEN (0x1040) +#define CKM_ECDSA (0x1041) +#define CKM_ECDSA_SHA1 (0x1042) +#define CKM_ECDH1_DERIVE (0x1050) +#define CKM_ECDH1_COFACTOR_DERIVE (0x1051) +#define CKM_ECMQV_DERIVE (0x1052) +#define CKM_JUNIPER_KEY_GEN (0x1060) +#define CKM_JUNIPER_ECB128 (0x1061) +#define CKM_JUNIPER_CBC128 (0x1062) +#define CKM_JUNIPER_COUNTER (0x1063) +#define CKM_JUNIPER_SHUFFLE (0x1064) +#define CKM_JUNIPER_WRAP (0x1065) +#define CKM_FASTHASH (0x1070) +#define CKM_AES_KEY_GEN (0x1080) +#define CKM_AES_ECB (0x1081) +#define CKM_AES_CBC (0x1082) +#define CKM_AES_MAC (0x1083) +#define CKM_AES_MAC_GENERAL (0x1084) +#define CKM_AES_CBC_PAD (0x1085) +#define CKM_DSA_PARAMETER_GEN (0x2000) +#define CKM_DH_PKCS_PARAMETER_GEN (0x2001) +#define CKM_X9_42_DH_PARAMETER_GEN (0x2002) +#define CKM_VENDOR_DEFINED (1U << 31) + + +struct ck_mechanism +{ + ck_mechanism_type_t mechanism; + void *parameter; + unsigned long parameter_len; +}; + + +struct ck_mechanism_info +{ + unsigned long min_key_size; + unsigned long max_key_size; + ck_flags_t flags; +}; + +#define CKF_HW (1 << 0) +#define CKF_ENCRYPT (1 << 8) +#define CKF_DECRYPT (1 << 9) +#define CKF_DIGEST (1 << 10) +#define CKF_SIGN (1 << 11) +#define CKF_SIGN_RECOVER (1 << 12) +#define CKF_VERIFY (1 << 13) +#define CKF_VERIFY_RECOVER (1 << 14) +#define CKF_GENERATE (1 << 15) +#define CKF_GENERATE_KEY_PAIR (1 << 16) +#define CKF_WRAP (1 << 17) +#define CKF_UNWRAP (1 << 18) +#define CKF_DERIVE (1 << 19) +#define CKF_EXTENSION (1U << 31) + + +/* Flags for C_WaitForSlotEvent. */ +#define CKF_DONT_BLOCK (1) + + +typedef unsigned long ck_rv_t; + + +typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, + ck_notification_t event, void *application); + +/* Forward reference. */ +struct ck_function_list; + +#define _CK_DECLARE_FUNCTION(name, args) \ +typedef ck_rv_t (*CK_ ## name) args; \ +ck_rv_t CK_SPEC name args + +_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args)); +_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved)); +_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info)); +_CK_DECLARE_FUNCTION (C_GetFunctionList, + (struct ck_function_list **function_list)); + +_CK_DECLARE_FUNCTION (C_GetSlotList, + (unsigned char token_present, ck_slot_id_t *slot_list, + unsigned long *count)); +_CK_DECLARE_FUNCTION (C_GetSlotInfo, + (ck_slot_id_t slot_id, struct ck_slot_info *info)); +_CK_DECLARE_FUNCTION (C_GetTokenInfo, + (ck_slot_id_t slot_id, struct ck_token_info *info)); +_CK_DECLARE_FUNCTION (C_WaitForSlotEvent, + (ck_flags_t flags, ck_slot_id_t *slot, void *reserved)); +_CK_DECLARE_FUNCTION (C_GetMechanismList, + (ck_slot_id_t slot_id, + ck_mechanism_type_t *mechanism_list, + unsigned long *count)); +_CK_DECLARE_FUNCTION (C_GetMechanismInfo, + (ck_slot_id_t slot_id, ck_mechanism_type_t type, + struct ck_mechanism_info *info)); +_CK_DECLARE_FUNCTION (C_InitToken, + (ck_slot_id_t slot_id, unsigned char *pin, + unsigned long pin_len, unsigned char *label)); +_CK_DECLARE_FUNCTION (C_InitPIN, + (ck_session_handle_t session, unsigned char *pin, + unsigned long pin_len)); +_CK_DECLARE_FUNCTION (C_SetPIN, + (ck_session_handle_t session, unsigned char *old_pin, + unsigned long old_len, unsigned char *new_pin, + unsigned long new_len)); + +_CK_DECLARE_FUNCTION (C_OpenSession, + (ck_slot_id_t slot_id, ck_flags_t flags, + void *application, ck_notify_t notify, + ck_session_handle_t *session)); +_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session)); +_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id)); +_CK_DECLARE_FUNCTION (C_GetSessionInfo, + (ck_session_handle_t session, + struct ck_session_info *info)); +_CK_DECLARE_FUNCTION (C_GetOperationState, + (ck_session_handle_t session, + unsigned char *operation_state, + unsigned long *operation_state_len)); +_CK_DECLARE_FUNCTION (C_SetOperationState, + (ck_session_handle_t session, + unsigned char *operation_state, + unsigned long operation_state_len, + ck_object_handle_t encryption_key, + ck_object_handle_t authentiation_key)); +_CK_DECLARE_FUNCTION (C_Login, + (ck_session_handle_t session, ck_user_type_t user_type, + unsigned char *pin, unsigned long pin_len)); +_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session)); + +_CK_DECLARE_FUNCTION (C_CreateObject, + (ck_session_handle_t session, + struct ck_attribute *templ, + unsigned long count, ck_object_handle_t *object)); +_CK_DECLARE_FUNCTION (C_CopyObject, + (ck_session_handle_t session, ck_object_handle_t object, + struct ck_attribute *templ, unsigned long count, + ck_object_handle_t *new_object)); +_CK_DECLARE_FUNCTION (C_DestroyObject, + (ck_session_handle_t session, + ck_object_handle_t object)); +_CK_DECLARE_FUNCTION (C_GetObjectSize, + (ck_session_handle_t session, + ck_object_handle_t object, + unsigned long *size)); +_CK_DECLARE_FUNCTION (C_GetAttributeValue, + (ck_session_handle_t session, + ck_object_handle_t object, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_SetAttributeValue, + (ck_session_handle_t session, + ck_object_handle_t object, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_FindObjectsInit, + (ck_session_handle_t session, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_FindObjects, + (ck_session_handle_t session, + ck_object_handle_t *object, + unsigned long max_object_count, + unsigned long *object_count)); +_CK_DECLARE_FUNCTION (C_FindObjectsFinal, + (ck_session_handle_t session)); + +_CK_DECLARE_FUNCTION (C_EncryptInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Encrypt, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *encrypted_data, + unsigned long *encrypted_data_len)); +_CK_DECLARE_FUNCTION (C_EncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_EncryptFinal, + (ck_session_handle_t session, + unsigned char *last_encrypted_part, + unsigned long *last_encrypted_part_len)); + +_CK_DECLARE_FUNCTION (C_DecryptInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Decrypt, + (ck_session_handle_t session, + unsigned char *encrypted_data, + unsigned long encrypted_data_len, + unsigned char *data, unsigned long *data_len)); +_CK_DECLARE_FUNCTION (C_DecryptUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, unsigned long *part_len)); +_CK_DECLARE_FUNCTION (C_DecryptFinal, + (ck_session_handle_t session, + unsigned char *last_part, + unsigned long *last_part_len)); + +_CK_DECLARE_FUNCTION (C_DigestInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism)); +_CK_DECLARE_FUNCTION (C_Digest, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *digest, + unsigned long *digest_len)); +_CK_DECLARE_FUNCTION (C_DigestUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_DigestKey, + (ck_session_handle_t session, ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_DigestFinal, + (ck_session_handle_t session, + unsigned char *digest, + unsigned long *digest_len)); + +_CK_DECLARE_FUNCTION (C_SignInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Sign, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long *signature_len)); +_CK_DECLARE_FUNCTION (C_SignUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_SignFinal, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long *signature_len)); +_CK_DECLARE_FUNCTION (C_SignRecoverInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_SignRecover, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long *signature_len)); + +_CK_DECLARE_FUNCTION (C_VerifyInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Verify, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long signature_len)); +_CK_DECLARE_FUNCTION (C_VerifyUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_VerifyFinal, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long signature_len)); +_CK_DECLARE_FUNCTION (C_VerifyRecoverInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_VerifyRecover, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long signature_len, + unsigned char *data, + unsigned long *data_len)); + +_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, + unsigned long *part_len)); +_CK_DECLARE_FUNCTION (C_SignEncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, + unsigned long *part_len)); + +_CK_DECLARE_FUNCTION (C_GenerateKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + struct ck_attribute *templ, + unsigned long count, + ck_object_handle_t *key)); +_CK_DECLARE_FUNCTION (C_GenerateKeyPair, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + struct ck_attribute *public_key_template, + unsigned long public_key_attribute_count, + struct ck_attribute *private_key_template, + unsigned long private_key_attribute_count, + ck_object_handle_t *public_key, + ck_object_handle_t *private_key)); +_CK_DECLARE_FUNCTION (C_WrapKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t wrapping_key, + ck_object_handle_t key, + unsigned char *wrapped_key, + unsigned long *wrapped_key_len)); +_CK_DECLARE_FUNCTION (C_UnwrapKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t unwrapping_key, + unsigned char *wrapped_key, + unsigned long wrapped_key_len, + struct ck_attribute *templ, + unsigned long attribute_count, + ck_object_handle_t *key)); +_CK_DECLARE_FUNCTION (C_DeriveKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t base_key, + struct ck_attribute *templ, + unsigned long attribute_count, + ck_object_handle_t *key)); + +_CK_DECLARE_FUNCTION (C_SeedRandom, + (ck_session_handle_t session, unsigned char *seed, + unsigned long seed_len)); +_CK_DECLARE_FUNCTION (C_GenerateRandom, + (ck_session_handle_t session, + unsigned char *random_data, + unsigned long random_len)); + +_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session)); +_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session)); + + +struct ck_function_list +{ + struct ck_version version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; +}; + + +typedef ck_rv_t (*ck_createmutex_t) (void **mutex); +typedef ck_rv_t (*ck_destroymutex_t) (void *mutex); +typedef ck_rv_t (*ck_lockmutex_t) (void *mutex); +typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex); + + +struct ck_c_initialize_args +{ + ck_createmutex_t create_mutex; + ck_destroymutex_t destroy_mutex; + ck_lockmutex_t lock_mutex; + ck_unlockmutex_t unlock_mutex; + ck_flags_t flags; + void *reserved; +}; + + +#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0) +#define CKF_OS_LOCKING_OK (1 << 1) + +#define CKR_OK (0) +#define CKR_CANCEL (1) +#define CKR_HOST_MEMORY (2) +#define CKR_SLOT_ID_INVALID (3) +#define CKR_GENERAL_ERROR (5) +#define CKR_FUNCTION_FAILED (6) +#define CKR_ARGUMENTS_BAD (7) +#define CKR_NO_EVENT (8) +#define CKR_NEED_TO_CREATE_THREADS (9) +#define CKR_CANT_LOCK (0xa) +#define CKR_ATTRIBUTE_READ_ONLY (0x10) +#define CKR_ATTRIBUTE_SENSITIVE (0x11) +#define CKR_ATTRIBUTE_TYPE_INVALID (0x12) +#define CKR_ATTRIBUTE_VALUE_INVALID (0x13) +#define CKR_DATA_INVALID (0x20) +#define CKR_DATA_LEN_RANGE (0x21) +#define CKR_DEVICE_ERROR (0x30) +#define CKR_DEVICE_MEMORY (0x31) +#define CKR_DEVICE_REMOVED (0x32) +#define CKR_ENCRYPTED_DATA_INVALID (0x40) +#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41) +#define CKR_FUNCTION_CANCELED (0x50) +#define CKR_FUNCTION_NOT_PARALLEL (0x51) +#define CKR_FUNCTION_NOT_SUPPORTED (0x54) +#define CKR_KEY_HANDLE_INVALID (0x60) +#define CKR_KEY_SIZE_RANGE (0x62) +#define CKR_KEY_TYPE_INCONSISTENT (0x63) +#define CKR_KEY_NOT_NEEDED (0x64) +#define CKR_KEY_CHANGED (0x65) +#define CKR_KEY_NEEDED (0x66) +#define CKR_KEY_INDIGESTIBLE (0x67) +#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68) +#define CKR_KEY_NOT_WRAPPABLE (0x69) +#define CKR_KEY_UNEXTRACTABLE (0x6a) +#define CKR_MECHANISM_INVALID (0x70) +#define CKR_MECHANISM_PARAM_INVALID (0x71) +#define CKR_OBJECT_HANDLE_INVALID (0x82) +#define CKR_OPERATION_ACTIVE (0x90) +#define CKR_OPERATION_NOT_INITIALIZED (0x91) +#define CKR_PIN_INCORRECT (0xa0) +#define CKR_PIN_INVALID (0xa1) +#define CKR_PIN_LEN_RANGE (0xa2) +#define CKR_PIN_EXPIRED (0xa3) +#define CKR_PIN_LOCKED (0xa4) +#define CKR_SESSION_CLOSED (0xb0) +#define CKR_SESSION_COUNT (0xb1) +#define CKR_SESSION_HANDLE_INVALID (0xb3) +#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4) +#define CKR_SESSION_READ_ONLY (0xb5) +#define CKR_SESSION_EXISTS (0xb6) +#define CKR_SESSION_READ_ONLY_EXISTS (0xb7) +#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8) +#define CKR_SIGNATURE_INVALID (0xc0) +#define CKR_SIGNATURE_LEN_RANGE (0xc1) +#define CKR_TEMPLATE_INCOMPLETE (0xd0) +#define CKR_TEMPLATE_INCONSISTENT (0xd1) +#define CKR_TOKEN_NOT_PRESENT (0xe0) +#define CKR_TOKEN_NOT_RECOGNIZED (0xe1) +#define CKR_TOKEN_WRITE_PROTECTED (0xe2) +#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0) +#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1) +#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2) +#define CKR_USER_ALREADY_LOGGED_IN (0x100) +#define CKR_USER_NOT_LOGGED_IN (0x101) +#define CKR_USER_PIN_NOT_INITIALIZED (0x102) +#define CKR_USER_TYPE_INVALID (0x103) +#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104) +#define CKR_USER_TOO_MANY_TYPES (0x105) +#define CKR_WRAPPED_KEY_INVALID (0x110) +#define CKR_WRAPPED_KEY_LEN_RANGE (0x112) +#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113) +#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114) +#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115) +#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120) +#define CKR_RANDOM_NO_RNG (0x121) +#define CKR_DOMAIN_PARAMS_INVALID (0x130) +#define CKR_BUFFER_TOO_SMALL (0x150) +#define CKR_SAVED_STATE_INVALID (0x160) +#define CKR_INFORMATION_SENSITIVE (0x170) +#define CKR_STATE_UNSAVEABLE (0x180) +#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190) +#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191) +#define CKR_MUTEX_BAD (0x1a0) +#define CKR_MUTEX_NOT_LOCKED (0x1a1) +#define CKR_FUNCTION_REJECTED (0x200) +#define CKR_VENDOR_DEFINED (1U << 31) + + + +/* Compatibility layer. */ + +#ifdef CRYPTOKI_COMPAT + +#undef CK_DEFINE_FUNCTION +#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name + +/* For NULL. */ +#include + +typedef unsigned char CK_BYTE; +typedef unsigned char CK_CHAR; +typedef unsigned char CK_UTF8CHAR; +typedef unsigned char CK_BBOOL; +typedef unsigned long int CK_ULONG; +typedef long int CK_LONG; +typedef CK_BYTE *CK_BYTE_PTR; +typedef CK_CHAR *CK_CHAR_PTR; +typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR; +typedef CK_ULONG *CK_ULONG_PTR; +typedef void *CK_VOID_PTR; +typedef void **CK_VOID_PTR_PTR; +#define CK_FALSE 0 +#define CK_TRUE 1 +#ifndef CK_DISABLE_TRUE_FALSE +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#endif + +typedef struct ck_version CK_VERSION; +typedef struct ck_version *CK_VERSION_PTR; + +typedef struct ck_info CK_INFO; +typedef struct ck_info *CK_INFO_PTR; + +typedef ck_slot_id_t *CK_SLOT_ID_PTR; + +typedef struct ck_slot_info CK_SLOT_INFO; +typedef struct ck_slot_info *CK_SLOT_INFO_PTR; + +typedef struct ck_token_info CK_TOKEN_INFO; +typedef struct ck_token_info *CK_TOKEN_INFO_PTR; + +typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR; + +typedef struct ck_session_info CK_SESSION_INFO; +typedef struct ck_session_info *CK_SESSION_INFO_PTR; + +typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR; + +typedef ck_object_class_t *CK_OBJECT_CLASS_PTR; + +typedef struct ck_attribute CK_ATTRIBUTE; +typedef struct ck_attribute *CK_ATTRIBUTE_PTR; + +typedef struct ck_date CK_DATE; +typedef struct ck_date *CK_DATE_PTR; + +typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR; + +typedef struct ck_mechanism CK_MECHANISM; +typedef struct ck_mechanism *CK_MECHANISM_PTR; + +typedef struct ck_mechanism_info CK_MECHANISM_INFO; +typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR; + +typedef struct ck_function_list CK_FUNCTION_LIST; +typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; +typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; + +typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS; +typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR; + +#define NULL_PTR NULL + +/* Delete the helper macros defined at the top of the file. */ +#undef ck_flags_t +#undef ck_version + +#undef ck_info +#undef cryptoki_version +#undef manufacturer_id +#undef library_description +#undef library_version + +#undef ck_notification_t +#undef ck_slot_id_t + +#undef ck_slot_info +#undef slot_description +#undef hardware_version +#undef firmware_version + +#undef ck_token_info +#undef serial_number +#undef max_session_count +#undef session_count +#undef max_rw_session_count +#undef rw_session_count +#undef max_pin_len +#undef min_pin_len +#undef total_public_memory +#undef free_public_memory +#undef total_private_memory +#undef free_private_memory +#undef utc_time + +#undef ck_session_handle_t +#undef ck_user_type_t +#undef ck_state_t + +#undef ck_session_info +#undef slot_id +#undef device_error + +#undef ck_object_handle_t +#undef ck_object_class_t +#undef ck_hw_feature_type_t +#undef ck_key_type_t +#undef ck_certificate_type_t +#undef ck_attribute_type_t + +#undef ck_attribute +#undef value +#undef value_len + +#undef ck_date + +#undef ck_mechanism_type_t + +#undef ck_mechanism +#undef parameter +#undef parameter_len + +#undef ck_mechanism_info +#undef min_key_size +#undef max_key_size + +#undef ck_rv_t +#undef ck_notify_t + +#undef ck_function_list + +#undef ck_createmutex_t +#undef ck_destroymutex_t +#undef ck_lockmutex_t +#undef ck_unlockmutex_t + +#undef ck_c_initialize_args +#undef create_mutex +#undef destroy_mutex +#undef lock_mutex +#undef unlock_mutex +#undef reserved + +#endif /* CRYPTOKI_COMPAT */ + + +/* System dependencies. */ +#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) +#pragma pack(pop, cryptoki) +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* PKCS11_H */ diff --git a/aosp/external/openssh/platform-misc.c b/aosp/external/openssh/platform-misc.c new file mode 100644 index 0000000000000000000000000000000000000000..3f396704953ddf6fec952d31b104207673d74424 --- /dev/null +++ b/aosp/external/openssh/platform-misc.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 Darren Tucker. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include "openbsd-compat/openbsd-compat.h" + +/* + * return 1 if the specified uid is a uid that may own a system directory + * otherwise 0. + */ +int +platform_sys_dir_uid(uid_t uid) +{ + if (uid == 0) + return 1; +#ifdef PLATFORM_SYS_DIR_UID + if (uid == PLATFORM_SYS_DIR_UID) + return 1; +#endif + return 0; +} diff --git a/aosp/external/openssh/platform-pledge.c b/aosp/external/openssh/platform-pledge.c new file mode 100644 index 0000000000000000000000000000000000000000..4a6ec15e1b2d68fd042f9563a91280b66e9cb7c3 --- /dev/null +++ b/aosp/external/openssh/platform-pledge.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015 Joyent, Inc + * Author: Alex Wilson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include +#include + +#include "platform.h" + +#include "openbsd-compat/openbsd-compat.h" + +/* + * Drop any fine-grained privileges that are not needed for post-startup + * operation of ssh-agent + * + * Should be as close as possible to pledge("stdio cpath unix id proc exec", ...) + */ +void +platform_pledge_agent(void) +{ +#ifdef USE_SOLARIS_PRIVS + /* + * Note: Solaris priv dropping is closer to tame() than pledge(), but + * we will use what we have. + */ + solaris_drop_privs_root_pinfo_net(); +#endif +} + +/* + * Drop any fine-grained privileges that are not needed for post-startup + * operation of sftp-server + */ +void +platform_pledge_sftp_server(void) +{ +#ifdef USE_SOLARIS_PRIVS + solaris_drop_privs_pinfo_net_fork_exec(); +#endif +} + +/* + * Drop any fine-grained privileges that are not needed for the post-startup + * operation of the SSH client mux + * + * Should be as close as possible to pledge("stdio proc tty", ...) + */ +void +platform_pledge_mux(void) +{ +#ifdef USE_SOLARIS_PRIVS + solaris_drop_privs_root_pinfo_net_exec(); +#endif +} diff --git a/aosp/external/openssh/platform-tracing.c b/aosp/external/openssh/platform-tracing.c new file mode 100644 index 0000000000000000000000000000000000000000..c2810f2d0b36bb1d62f6e51d740e2c149244a3d1 --- /dev/null +++ b/aosp/external/openssh/platform-tracing.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Darren Tucker. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_PROCCTL_H +#include +#endif +#if defined(HAVE_SYS_PRCTL_H) +#include /* For prctl() and PR_SET_DUMPABLE */ +#endif +#ifdef HAVE_SYS_PTRACE_H +#include +#endif +#ifdef HAVE_PRIV_H +#include /* For setpflags() and __PROC_PROTECT */ +#endif +#include +#include +#include + +#include "log.h" + +void +platform_disable_tracing(int strict) +{ +#if defined(HAVE_PROCCTL) && defined(PROC_TRACE_CTL) + /* On FreeBSD, we should make this process untraceable */ + int disable_trace = PROC_TRACE_CTL_DISABLE; + + if (procctl(P_PID, 0, PROC_TRACE_CTL, &disable_trace) && strict) + fatal("unable to make the process untraceable: %s", + strerror(errno)); +#endif +#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) + /* Disable ptrace on Linux without sgid bit */ + if (prctl(PR_SET_DUMPABLE, 0) != 0 && strict) + fatal("unable to make the process undumpable: %s", + strerror(errno)); +#endif +#if defined(HAVE_SETPFLAGS) && defined(__PROC_PROTECT) + /* On Solaris, we should make this process untraceable */ + if (setpflags(__PROC_PROTECT, 1) != 0 && strict) + fatal("unable to make the process untraceable: %s", + strerror(errno)); +#endif +#ifdef PT_DENY_ATTACH + /* Mac OS X */ + if (ptrace(PT_DENY_ATTACH, 0, 0, 0) == -1 && strict) + fatal("unable to set PT_DENY_ATTACH: %s", strerror(errno)); +#endif +} diff --git a/aosp/external/openssh/platform.c b/aosp/external/openssh/platform.c new file mode 100644 index 0000000000000000000000000000000000000000..4fe8744ee887c4297c9e7721fb83ecf47e14c54d --- /dev/null +++ b/aosp/external/openssh/platform.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2006 Darren Tucker. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "sshkey.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-pam.h" +#include "platform.h" + +#include "openbsd-compat/openbsd-compat.h" + +extern int use_privsep; +extern ServerOptions options; + +void +platform_pre_listen(void) +{ +#ifdef LINUX_OOM_ADJUST + /* Adjust out-of-memory killer so listening process is not killed */ + oom_adjust_setup(); +#endif +} + +void +platform_pre_fork(void) +{ +#ifdef USE_SOLARIS_PROCESS_CONTRACTS + solaris_contract_pre_fork(); +#endif +} + +void +platform_pre_restart(void) +{ +#ifdef LINUX_OOM_ADJUST + oom_adjust_restore(); +#endif +} + +void +platform_post_fork_parent(pid_t child_pid) +{ +#ifdef USE_SOLARIS_PROCESS_CONTRACTS + solaris_contract_post_fork_parent(child_pid); +#endif +} + +void +platform_post_fork_child(void) +{ +#ifdef USE_SOLARIS_PROCESS_CONTRACTS + solaris_contract_post_fork_child(); +#endif +#ifdef LINUX_OOM_ADJUST + oom_adjust_restore(); +#endif +} + +/* return 1 if we are running with privilege to swap UIDs, 0 otherwise */ +int +platform_privileged_uidswap(void) +{ +#ifdef HAVE_CYGWIN + /* uid 0 is not special on Cygwin so always try */ + return 1; +#else + return (getuid() == 0 || geteuid() == 0); +#endif +} + +/* + * This gets called before switching UIDs, and is called even when sshd is + * not running as root. + */ +void +platform_setusercontext(struct passwd *pw) +{ +#ifdef WITH_SELINUX + /* Cache selinux status for later use */ + (void)ssh_selinux_enabled(); +#endif + +#ifdef USE_SOLARIS_PROJECTS + /* + * If solaris projects were detected, set the default now, unless + * we are using PAM in which case it is the responsibility of the + * PAM stack. + */ + if (!options.use_pam && (getuid() == 0 || geteuid() == 0)) + solaris_set_default_project(pw); +#endif + +#if defined(HAVE_LOGIN_CAP) && defined (__bsdi__) + if (getuid() == 0 || geteuid() == 0) + setpgid(0, 0); +# endif + +#if defined(HAVE_LOGIN_CAP) && defined(USE_PAM) + /* + * If we have both LOGIN_CAP and PAM, we want to establish creds + * before calling setusercontext (in session.c:do_setusercontext). + */ + if (getuid() == 0 || geteuid() == 0) { + if (options.use_pam) { + do_pam_setcred(use_privsep); + } + } +# endif /* USE_PAM */ + +#if !defined(HAVE_LOGIN_CAP) && defined(HAVE_GETLUID) && defined(HAVE_SETLUID) + if (getuid() == 0 || geteuid() == 0) { + /* Sets login uid for accounting */ + if (getluid() == -1 && setluid(pw->pw_uid) == -1) + error("setluid: %s", strerror(errno)); + } +#endif +} + +/* + * This gets called after we've established the user's groups, and is only + * called if sshd is running as root. + */ +void +platform_setusercontext_post_groups(struct passwd *pw) +{ +#if !defined(HAVE_LOGIN_CAP) && defined(USE_PAM) + /* + * PAM credentials may take the form of supplementary groups. + * These will have been wiped by the above initgroups() call. + * Reestablish them here. + */ + if (options.use_pam) { + do_pam_setcred(use_privsep); + } +#endif /* USE_PAM */ + +#if !defined(HAVE_LOGIN_CAP) && (defined(WITH_IRIX_PROJECT) || \ + defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY)) + irix_setusercontext(pw); +#endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ + +#ifdef _AIX + aix_usrinfo(pw); +#endif /* _AIX */ + +#ifdef HAVE_SETPCRED + /* + * If we have a chroot directory, we set all creds except real + * uid which we will need for chroot. If we don't have a + * chroot directory, we don't override anything. + */ + { + char **creds = NULL, *chroot_creds[] = + { "REAL_USER=root", NULL }; + + if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) + creds = chroot_creds; + + if (setpcred(pw->pw_name, creds) == -1) + fatal("Failed to set process credentials"); + } +#endif /* HAVE_SETPCRED */ +#ifdef WITH_SELINUX + ssh_selinux_setup_exec_context(pw->pw_name); +#endif +} + +char * +platform_krb5_get_principal_name(const char *pw_name) +{ +#ifdef USE_AIX_KRB_NAME + return aix_krb5_get_principal_name(pw_name); +#else + return NULL; +#endif +} + +/* returns 1 if account is locked */ +int +platform_locked_account(struct passwd *pw) +{ + int locked = 0; + char *passwd = pw->pw_passwd; +#ifdef USE_SHADOW + struct spwd *spw = NULL; +#ifdef USE_LIBIAF + char *iaf_passwd = NULL; +#endif + + spw = getspnam(pw->pw_name); +#ifdef HAS_SHADOW_EXPIRE + if (spw != NULL && auth_shadow_acctexpired(spw)) + return 1; +#endif /* HAS_SHADOW_EXPIRE */ + + if (spw != NULL) +#ifdef USE_LIBIAF + iaf_passwd = passwd = get_iaf_password(pw); +#else + passwd = spw->sp_pwdp; +#endif /* USE_LIBIAF */ +#endif + + /* check for locked account */ + if (passwd && *passwd) { +#ifdef LOCKED_PASSWD_STRING + if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0) + locked = 1; +#endif +#ifdef LOCKED_PASSWD_PREFIX + if (strncmp(passwd, LOCKED_PASSWD_PREFIX, + strlen(LOCKED_PASSWD_PREFIX)) == 0) + locked = 1; +#endif +#ifdef LOCKED_PASSWD_SUBSTR + if (strstr(passwd, LOCKED_PASSWD_SUBSTR)) + locked = 1; +#endif + } +#ifdef USE_LIBIAF + if (iaf_passwd != NULL) + freezero(iaf_passwd, strlen(iaf_passwd)); +#endif /* USE_LIBIAF */ + + return locked; +} diff --git a/aosp/external/openssh/platform.h b/aosp/external/openssh/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..7fef8c983e5ed22d59d391bc7a9fe9e2ad96d75e --- /dev/null +++ b/aosp/external/openssh/platform.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Darren Tucker. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +void platform_pre_listen(void); +void platform_pre_fork(void); +void platform_pre_restart(void); +void platform_post_fork_parent(pid_t child_pid); +void platform_post_fork_child(void); +int platform_privileged_uidswap(void); +void platform_setusercontext(struct passwd *); +void platform_setusercontext_post_groups(struct passwd *); +char *platform_get_krb5_client(const char *); +char *platform_krb5_get_principal_name(const char *); +int platform_locked_account(struct passwd *); +int platform_sys_dir_uid(uid_t); +void platform_disable_tracing(int); + +/* in platform-pledge.c */ +void platform_pledge_agent(void); +void platform_pledge_sftp_server(void); +void platform_pledge_mux(void); diff --git a/aosp/external/openssh/poly1305.c b/aosp/external/openssh/poly1305.c new file mode 100644 index 0000000000000000000000000000000000000000..6fd1fc8cd13cf75e8a7c9e3f5e81d5cb7db572cd --- /dev/null +++ b/aosp/external/openssh/poly1305.c @@ -0,0 +1,160 @@ +/* + * Public Domain poly1305 from Andrew Moon + * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna + */ + +/* $OpenBSD: poly1305.c,v 1.3 2013/12/19 22:57:13 djm Exp $ */ + +#include "includes.h" + +#include +#ifdef HAVE_STDINT_H +# include +#endif + +#include "poly1305.h" + +#define mul32x32_64(a,b) ((uint64_t)(a) * (b)) + +#define U8TO32_LE(p) \ + (((uint32_t)((p)[0])) | \ + ((uint32_t)((p)[1]) << 8) | \ + ((uint32_t)((p)[2]) << 16) | \ + ((uint32_t)((p)[3]) << 24)) + +#define U32TO8_LE(p, v) \ + do { \ + (p)[0] = (uint8_t)((v)); \ + (p)[1] = (uint8_t)((v) >> 8); \ + (p)[2] = (uint8_t)((v) >> 16); \ + (p)[3] = (uint8_t)((v) >> 24); \ + } while (0) + +void +poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) { + uint32_t t0,t1,t2,t3; + uint32_t h0,h1,h2,h3,h4; + uint32_t r0,r1,r2,r3,r4; + uint32_t s1,s2,s3,s4; + uint32_t b, nb; + size_t j; + uint64_t t[5]; + uint64_t f0,f1,f2,f3; + uint32_t g0,g1,g2,g3,g4; + uint64_t c; + unsigned char mp[16]; + + /* clamp key */ + t0 = U8TO32_LE(key+0); + t1 = U8TO32_LE(key+4); + t2 = U8TO32_LE(key+8); + t3 = U8TO32_LE(key+12); + + /* precompute multipliers */ + r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; + r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; + r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; + r3 = t2 & 0x3f03fff; t3 >>= 8; + r4 = t3 & 0x00fffff; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + /* init state */ + h0 = 0; + h1 = 0; + h2 = 0; + h3 = 0; + h4 = 0; + + /* full blocks */ + if (inlen < 16) goto poly1305_donna_atmost15bytes; +poly1305_donna_16bytes: + m += 16; + inlen -= 16; + + t0 = U8TO32_LE(m-16); + t1 = U8TO32_LE(m-12); + t2 = U8TO32_LE(m-8); + t3 = U8TO32_LE(m-4); + + h0 += t0 & 0x3ffffff; + h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff; + h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff; + h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff; + h4 += (t3 >> 8) | (1 << 24); + + +poly1305_donna_mul: + t[0] = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1); + t[1] = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2); + t[2] = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3); + t[3] = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4); + t[4] = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0); + + h0 = (uint32_t)t[0] & 0x3ffffff; c = (t[0] >> 26); + t[1] += c; h1 = (uint32_t)t[1] & 0x3ffffff; b = (uint32_t)(t[1] >> 26); + t[2] += b; h2 = (uint32_t)t[2] & 0x3ffffff; b = (uint32_t)(t[2] >> 26); + t[3] += b; h3 = (uint32_t)t[3] & 0x3ffffff; b = (uint32_t)(t[3] >> 26); + t[4] += b; h4 = (uint32_t)t[4] & 0x3ffffff; b = (uint32_t)(t[4] >> 26); + h0 += b * 5; + + if (inlen >= 16) goto poly1305_donna_16bytes; + + /* final bytes */ +poly1305_donna_atmost15bytes: + if (!inlen) goto poly1305_donna_finish; + + for (j = 0; j < inlen; j++) mp[j] = m[j]; + mp[j++] = 1; + for (; j < 16; j++) mp[j] = 0; + inlen = 0; + + t0 = U8TO32_LE(mp+0); + t1 = U8TO32_LE(mp+4); + t2 = U8TO32_LE(mp+8); + t3 = U8TO32_LE(mp+12); + + h0 += t0 & 0x3ffffff; + h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff; + h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff; + h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff; + h4 += (t3 >> 8); + + goto poly1305_donna_mul; + +poly1305_donna_finish: + b = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += b; + + g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + b - (1 << 26); + + b = (g4 >> 31) - 1; + nb = ~b; + h0 = (h0 & nb) | (g0 & b); + h1 = (h1 & nb) | (g1 & b); + h2 = (h2 & nb) | (g2 & b); + h3 = (h3 & nb) | (g3 & b); + h4 = (h4 & nb) | (g4 & b); + + f0 = ((h0 ) | (h1 << 26)) + (uint64_t)U8TO32_LE(&key[16]); + f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)U8TO32_LE(&key[20]); + f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)U8TO32_LE(&key[24]); + f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)U8TO32_LE(&key[28]); + + U32TO8_LE(&out[ 0], f0); f1 += (f0 >> 32); + U32TO8_LE(&out[ 4], f1); f2 += (f1 >> 32); + U32TO8_LE(&out[ 8], f2); f3 += (f2 >> 32); + U32TO8_LE(&out[12], f3); +} diff --git a/aosp/external/openssh/poly1305.h b/aosp/external/openssh/poly1305.h new file mode 100644 index 0000000000000000000000000000000000000000..f7db5f8d7c98195832e69fd373bc0c2237af37ec --- /dev/null +++ b/aosp/external/openssh/poly1305.h @@ -0,0 +1,22 @@ +/* $OpenBSD: poly1305.h,v 1.4 2014/05/02 03:27:54 djm Exp $ */ + +/* + * Public Domain poly1305 from Andrew Moon + * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna + */ + +#ifndef POLY1305_H +#define POLY1305_H + +#include + +#define POLY1305_KEYLEN 32 +#define POLY1305_TAGLEN 16 + +void poly1305_auth(u_char out[POLY1305_TAGLEN], const u_char *m, size_t inlen, + const u_char key[POLY1305_KEYLEN]) + __attribute__((__bounded__(__minbytes__, 1, POLY1305_TAGLEN))) + __attribute__((__bounded__(__buffer__, 2, 3))) + __attribute__((__bounded__(__minbytes__, 4, POLY1305_KEYLEN))); + +#endif /* POLY1305_H */ diff --git a/aosp/external/openssh/prebuilt-intermediates/config.h b/aosp/external/openssh/prebuilt-intermediates/config.h new file mode 100755 index 0000000000000000000000000000000000000000..e676346482e46983d5e8627d4c02c0779be3ba3f --- /dev/null +++ b/aosp/external/openssh/prebuilt-intermediates/config.h @@ -0,0 +1,2141 @@ +/* config.h. Generated from config.h.in by configure and then hand modified for android */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define if you have a getaddrinfo that fails for the all-zeros IPv6 address + */ +/* #undef AIX_GETNAMEINFO_HACK */ + +/* Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2) */ +/* #undef AIX_LOGINFAILED_4ARG */ + +/* System only supports IPv4 audit records */ +/* #undef AU_IPv4 */ + +/* Define if your resolver libs need this for getrrsetbyname */ +/* #undef BIND_8_COMPAT */ + +/* The system has incomplete BSM API */ +/* #undef BROKEN_BSM_API */ + +/* broken in chroots on older kernels */ +/* #undef BROKEN_CLOSEFROM */ + +/* Define if cmsg_type is not passed correctly */ +/* #undef BROKEN_CMSG_TYPE */ + +/* getaddrinfo is broken (if present) */ +/* #undef BROKEN_GETADDRINFO */ + +/* getgroups(0,NULL) will return -1 */ +/* #undef BROKEN_GETGROUPS */ + +/* getline is not what we expect */ +/* #undef BROKEN_GETLINE */ + +/* FreeBSD glob does not do what we need */ +/* #undef BROKEN_GLOB */ + +/* Define if you system's inet_ntoa is busted (e.g. Irix gcc issue) */ +/* #undef BROKEN_INET_NTOA */ + +/* Define if your struct dirent expects you to allocate extra space for d_name + */ +/* #undef BROKEN_ONE_BYTE_DIRENT_D_NAME */ + +/* System poll(2) implementation is broken */ +/* #undef BROKEN_POLL */ + +/* Can't do comparisons on readv */ +/* #undef BROKEN_READV_COMPARISON */ + +/* NetBSD read function is sometimes redirected, breaking atomicio comparisons + against it */ +/* #undef BROKEN_READ_COMPARISON */ + +/* Needed for NeXT */ +/* #undef BROKEN_SAVED_UIDS */ + +/* Define if your setregid() is broken */ +/* #undef BROKEN_SETREGID */ + +/* Define if your setresgid() is broken */ +/* #undef BROKEN_SETRESGID */ + +/* Define if your setresuid() is broken */ +/* #undef BROKEN_SETRESUID */ + +/* Define if your setreuid() is broken */ +/* #undef BROKEN_SETREUID */ + +/* LynxOS has broken setvbuf() implementation */ +/* #undef BROKEN_SETVBUF */ + +/* QNX shadow support is broken */ +/* #undef BROKEN_SHADOW_EXPIRE */ + +/* Define if your snprintf is busted */ +/* #undef BROKEN_SNPRINTF */ + +/* strndup broken, see APAR IY61211 */ +/* #undef BROKEN_STRNDUP */ + +/* strnlen broken, see APAR IY62551 */ +/* #undef BROKEN_STRNLEN */ + +/* strnvis detected broken */ +#define BROKEN_STRNVIS 1 + +/* tcgetattr with ICANON may hang */ +/* #undef BROKEN_TCGETATTR_ICANON */ + +/* updwtmpx is broken (if present) */ +/* #undef BROKEN_UPDWTMPX */ + +/* Define if you have BSD auth support */ +/* #undef BSD_AUTH */ + +/* Define if you want to specify the path to your lastlog file */ +/* #undef CONF_LASTLOG_FILE */ + +/* Define if you want to specify the path to your utmp file */ +#define CONF_UTMP_FILE "/var/run/utmp" + +/* Define if you want to specify the path to your wtmpx file */ +/* #undef CONF_WTMPX_FILE */ + +/* Define if you want to specify the path to your wtmp file */ +#define CONF_WTMP_FILE "/var/log/wtmp" + +/* Need to call setpgrp as root */ +/* #undef DISABLE_FD_PASSING */ + +/* Define if you don't want to use lastlog */ +/* #undef DISABLE_LASTLOG */ + +/* Define if you don't want to use your system's login() call */ +/* #undef DISABLE_LOGIN */ + +/* Define if you don't want to use pututline() etc. to write [uw]tmp */ +/* #undef DISABLE_PUTUTLINE */ + +/* Define if you don't want to use pututxline() etc. to write [uw]tmpx */ +/* #undef DISABLE_PUTUTXLINE */ + +/* Define if you want to disable shadow passwords */ +#define DISABLE_SHADOW 1 + +/* Define if you don't want to use utmp */ +#define DISABLE_UTMP 1 + +/* Define if you don't want to use utmpx */ +#define DISABLE_UTMPX 1 + +/* Define if you don't want to use wtmp */ +#define DISABLE_WTMP 1 + +/* Define if you don't want to use wtmpx */ +#define DISABLE_WTMPX 1 + +/* Enable for PKCS#11 support */ +/* #undef ENABLE_PKCS11 */ + +/* Enable for U2F/FIDO support */ +#define ENABLE_SK /**/ + +/* Enable for built-in U2F/FIDO support */ +/* #undef ENABLE_SK_INTERNAL */ + +/* define if fflush(NULL) does not work */ +/* #undef FFLUSH_NULL_BUG */ + +/* File names may not contain backslash characters */ +/* #undef FILESYSTEM_NO_BACKSLASH */ + +/* fsid_t has member val */ +/* #undef FSID_HAS_VAL */ + +/* fsid_t has member __val */ +/* #undef FSID_HAS___VAL */ + +/* getpgrp takes one arg */ +#define GETPGRP_VOID 1 + +/* Conflicting defs for getspnam */ +/* #undef GETSPNAM_CONFLICTING_DEFS */ + +/* Define if your system glob() function has the GLOB_ALTDIRFUNC extension */ +#define GLOB_HAS_ALTDIRFUNC 1 + +/* Define if your system glob() function has gl_matchc options in glob_t */ +#define GLOB_HAS_GL_MATCHC 1 + +/* Define if your system glob() function has gl_statv options in glob_t */ +/* #undef GLOB_HAS_GL_STATV */ + +/* Define this if you want GSSAPI support in the version 2 protocol */ +/* #undef GSSAPI */ + +/* Define if you want to use shadow password expire field */ +/* #undef HAS_SHADOW_EXPIRE */ + +/* Define if your system uses access rights style file descriptor passing */ +/* #undef HAVE_ACCRIGHTS_IN_MSGHDR */ + +/* Define if you have ut_addr in utmp.h */ +#define HAVE_ADDR_IN_UTMP 1 + +/* Define if you have ut_addr in utmpx.h */ +/* #undef HAVE_ADDR_IN_UTMPX */ + +/* Define if you have ut_addr_v6 in utmp.h */ +#define HAVE_ADDR_V6_IN_UTMP 1 + +/* Define if you have ut_addr_v6 in utmpx.h */ +/* #undef HAVE_ADDR_V6_IN_UTMPX */ + +/* Define to 1 if you have the `arc4random' function. */ +#define HAVE_ARC4RANDOM 1 + +/* Define to 1 if you have the `arc4random_buf' function. */ +#define HAVE_ARC4RANDOM_BUF 1 + +/* Define to 1 if you have the `arc4random_stir' function. */ +#ifdef _ILP32 +# define HAVE_ARC4RANDOM_STIR 1 +#endif + +/* Define to 1 if you have the `arc4random_uniform' function. */ +#define HAVE_ARC4RANDOM_UNIFORM 1 + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* OpenBSD's gcc has bounded */ +/* #undef HAVE_ATTRIBUTE__BOUNDED__ */ + +/* Have attribute nonnull */ +#define HAVE_ATTRIBUTE__NONNULL__ 1 + +/* OpenBSD's gcc has sentinel */ +#define HAVE_ATTRIBUTE__SENTINEL__ 1 + +/* Define to 1 if you have the `aug_get_machine' function. */ +/* #undef HAVE_AUG_GET_MACHINE */ + +/* Define to 1 if you have the `b64_ntop' function. */ +/* #undef HAVE_B64_NTOP */ + +/* Define to 1 if you have the `b64_pton' function. */ +/* #undef HAVE_B64_PTON */ + +/* Define if you have the basename function. */ +#define HAVE_BASENAME 1 + +/* Define to 1 if you have the `bcopy' function. */ +#ifdef _ILP32 +# define HAVE_BCOPY 1 +#endif + +/* Define to 1 if you have the `bcrypt_pbkdf' function. */ +/* #undef HAVE_BCRYPT_PBKDF */ + +/* Define to 1 if you have the `bindresvport_sa' function. */ +/* #undef HAVE_BINDRESVPORT_SA */ + +/* Define to 1 if you have the `blf_enc' function. */ +/* #undef HAVE_BLF_ENC */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BLF_H */ + +/* Define to 1 if you have the `Blowfish_expand0state' function. */ +/* #undef HAVE_BLOWFISH_EXPAND0STATE */ + +/* Define to 1 if you have the `Blowfish_expandstate' function. */ +/* #undef HAVE_BLOWFISH_EXPANDSTATE */ + +/* Define to 1 if you have the `Blowfish_initstate' function. */ +/* #undef HAVE_BLOWFISH_INITSTATE */ + +/* Define to 1 if you have the `Blowfish_stream2word' function. */ +/* #undef HAVE_BLOWFISH_STREAM2WORD */ + +/* Define to 1 if you have the `BN_is_prime_ex' function. */ +#define HAVE_BN_IS_PRIME_EX 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BSD_LIBUTIL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BSM_AUDIT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BSTRING_H */ + +/* Define to 1 if you have the `bzero' function. */ +/* For Android, bzero is a builtin, which isn't detected by configure correctly */ +#define HAVE_BZERO 1 + +/* calloc(0, x) returns NULL */ +#define HAVE_CALLOC 0 + +/* Define to 1 if you have the `cap_rights_limit' function. */ +/* #undef HAVE_CAP_RIGHTS_LIMIT */ + +/* Define to 1 if you have the `clock' function. */ +#define HAVE_CLOCK 1 + +/* Have clock_gettime */ +#define HAVE_CLOCK_GETTIME 1 + +/* define if you have clock_t data type */ +#define HAVE_CLOCK_T 1 + +/* Define to 1 if you have the `closefrom' function. */ +/* #undef HAVE_CLOSEFROM */ + +/* Define to 1 if you have the `close_range' function. */ +/* #undef HAVE_CLOSE_RANGE */ + +/* Define if gai_strerror() returns const char * */ +#define HAVE_CONST_GAI_STRERROR_PROTO 1 + +/* Define if your system uses ancillary data style file descriptor passing */ +#define HAVE_CONTROL_IN_MSGHDR 1 + +/* Define to 1 if you have the `crypt' function. */ +/* #undef HAVE_CRYPT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CRYPTO_SHA2_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CRYPT_H */ + +/* Define if you are on Cygwin */ +/* #undef HAVE_CYGWIN */ + +/* Define if your libraries define daemon() */ +#define HAVE_DAEMON 1 + +/* Define to 1 if you have the declaration of `AI_NUMERICSERV', and to 0 if + you don't. */ +#define HAVE_DECL_AI_NUMERICSERV 1 + +/* Define to 1 if you have the declaration of `authenticate', and to 0 if you + don't. */ +/* #undef HAVE_DECL_AUTHENTICATE */ + +/* Define to 1 if you have the declaration of `bzero', and to 0 if you don't. + */ +#define HAVE_DECL_BZERO 1 + +/* Define to 1 if you have the declaration of `ftruncate', and to 0 if you + don't. */ +#define HAVE_DECL_FTRUNCATE 1 + +/* Define to 1 if you have the declaration of `getpeereid', and to 0 if you + don't. */ +#define HAVE_DECL_GETPEEREID 0 + +/* Define to 1 if you have the declaration of `GLOB_NOMATCH', and to 0 if you + don't. */ +#define HAVE_DECL_GLOB_NOMATCH 1 + +/* Define to 1 if you have the declaration of `GSS_C_NT_HOSTBASED_SERVICE', + and to 0 if you don't. */ +/* #undef HAVE_DECL_GSS_C_NT_HOSTBASED_SERVICE */ + +/* Define to 1 if you have the declaration of `howmany', and to 0 if you + don't. */ +#define HAVE_DECL_HOWMANY 1 + +/* Define to 1 if you have the declaration of `h_errno', and to 0 if you + don't. */ +#define HAVE_DECL_H_ERRNO 1 + +/* Define to 1 if you have the declaration of `loginfailed', and to 0 if you + don't. */ +/* #undef HAVE_DECL_LOGINFAILED */ + +/* Define to 1 if you have the declaration of `loginrestrictions', and to 0 if + you don't. */ +/* #undef HAVE_DECL_LOGINRESTRICTIONS */ + +/* Define to 1 if you have the declaration of `loginsuccess', and to 0 if you + don't. */ +/* #undef HAVE_DECL_LOGINSUCCESS */ + +/* Define to 1 if you have the declaration of `MAXSYMLINKS', and to 0 if you + don't. */ +#define HAVE_DECL_MAXSYMLINKS 1 + +/* Define to 1 if you have the declaration of `memmem', and to 0 if you don't. + */ +#define HAVE_DECL_MEMMEM 1 + +/* Define to 1 if you have the declaration of `NFDBITS', and to 0 if you + don't. */ +#define HAVE_DECL_NFDBITS 1 + +/* Define to 1 if you have the declaration of `offsetof', and to 0 if you + don't. */ +#define HAVE_DECL_OFFSETOF 1 + +/* Define to 1 if you have the declaration of `O_NONBLOCK', and to 0 if you + don't. */ +#define HAVE_DECL_O_NONBLOCK 1 + +/* Define to 1 if you have the declaration of `passwdexpired', and to 0 if you + don't. */ +/* #undef HAVE_DECL_PASSWDEXPIRED */ + +/* Define to 1 if you have the declaration of `readv', and to 0 if you don't. + */ +#define HAVE_DECL_READV 1 + +/* Define to 1 if you have the declaration of `setauthdb', and to 0 if you + don't. */ +/* #undef HAVE_DECL_SETAUTHDB */ + +/* Define to 1 if you have the declaration of `SHUT_RD', and to 0 if you + don't. */ +#define HAVE_DECL_SHUT_RD 1 + +/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you + don't. */ +#define HAVE_DECL_UINT32_MAX 1 + +/* Define to 1 if you have the declaration of `writev', and to 0 if you don't. + */ +#define HAVE_DECL_WRITEV 1 + +/* Define to 1 if you have the declaration of `_getlong', and to 0 if you + don't. */ +#define HAVE_DECL__GETLONG 0 + +/* Define to 1 if you have the declaration of `_getshort', and to 0 if you + don't. */ +#define HAVE_DECL__GETSHORT 0 + +/* Define to 1 if you have the `DES_crypt' function. */ +/* #undef HAVE_DES_CRYPT */ + +/* Define if you have /dev/ptmx */ +#define HAVE_DEV_PTMX 1 + +/* Define if you have /dev/ptc */ +/* #undef HAVE_DEV_PTS_AND_PTC */ + +/* Define to 1 if you have the `DH_get0_key' function. */ +#define HAVE_DH_GET0_KEY 1 + +/* Define to 1 if you have the `DH_get0_pqg' function. */ +#define HAVE_DH_GET0_PQG 1 + +/* Define to 1 if you have the `DH_set0_key' function. */ +#define HAVE_DH_SET0_KEY 1 + +/* Define to 1 if you have the `DH_set0_pqg' function. */ +#define HAVE_DH_SET0_PQG 1 + +/* Define to 1 if you have the `DH_set_length' function. */ +/* #undef HAVE_DH_SET_LENGTH */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the `dirfd' function. */ +#define HAVE_DIRFD 1 + +/* Define to 1 if you have the `dirname' function. */ +#define HAVE_DIRNAME 1 + +/* Define to 1 if you have the `dlopen' function. */ +#define HAVE_DLOPEN 1 + +/* Define to 1 if you have the `DSA_generate_parameters_ex' function. */ +#define HAVE_DSA_GENERATE_PARAMETERS_EX 1 + +/* Define to 1 if you have the `DSA_get0_key' function. */ +#define HAVE_DSA_GET0_KEY 1 + +/* Define to 1 if you have the `DSA_get0_pqg' function. */ +#define HAVE_DSA_GET0_PQG 1 + +/* Define to 1 if you have the `DSA_set0_key' function. */ +#define HAVE_DSA_SET0_KEY 1 + +/* Define to 1 if you have the `DSA_set0_pqg' function. */ +#define HAVE_DSA_SET0_PQG 1 + +/* Define to 1 if you have the `DSA_SIG_get0' function. */ +/* #undef HAVE_DSA_SIG_GET0 */ + +/* Define to 1 if you have the `DSA_SIG_set0' function. */ +/* #undef HAVE_DSA_SIG_SET0 */ + +/* Define to 1 if you have the `ECDSA_SIG_get0' function. */ +#define HAVE_ECDSA_SIG_GET0 1 + +/* Define to 1 if you have the `ECDSA_SIG_set0' function. */ +#define HAVE_ECDSA_SIG_SET0 1 + +/* Define to 1 if you have the `EC_KEY_METHOD_new' function. */ +/* #undef HAVE_EC_KEY_METHOD_NEW */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ELF_H 1 + +/* Define to 1 if you have the `endgrent' function. */ +#define HAVE_ENDGRENT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the `endutent' function. */ +#define HAVE_ENDUTENT 1 + +/* Define to 1 if you have the `endutxent' function. */ +/* #undef HAVE_ENDUTXENT */ + +/* Define to 1 if you have the `err' function. */ +#define HAVE_ERR 1 + +/* Define to 1 if you have the `errx' function. */ +#define HAVE_ERRX 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERR_H 1 + +/* Define if your system has /etc/default/login */ +/* #undef HAVE_ETC_DEFAULT_LOGIN */ + +/* Define to 1 if you have the `EVP_chacha20' function. */ +/* #undef HAVE_EVP_CHACHA20 */ + +/* Define to 1 if you have the `EVP_CIPHER_CTX_ctrl' function. */ +#define HAVE_EVP_CIPHER_CTX_CTRL 1 + +/* Define to 1 if you have the `EVP_CIPHER_CTX_get_iv' function. */ +/* #undef HAVE_EVP_CIPHER_CTX_GET_IV */ + +/* Define to 1 if you have the `EVP_CIPHER_CTX_get_updated_iv' function. */ +/* #undef HAVE_EVP_CIPHER_CTX_GET_UPDATED_IV */ + +/* Define to 1 if you have the `EVP_CIPHER_CTX_iv' function. */ +/* #undef HAVE_EVP_CIPHER_CTX_IV */ + +/* Define to 1 if you have the `EVP_CIPHER_CTX_iv_noconst' function. */ +/* #undef HAVE_EVP_CIPHER_CTX_IV_NOCONST */ + +/* Define to 1 if you have the `EVP_CIPHER_CTX_set_iv' function. */ +/* #undef HAVE_EVP_CIPHER_CTX_SET_IV */ + +/* Define to 1 if you have the `EVP_DigestFinal_ex' function. */ +#define HAVE_EVP_DIGESTFINAL_EX 1 + +/* Define to 1 if you have the `EVP_DigestInit_ex' function. */ +#define HAVE_EVP_DIGESTINIT_EX 1 + +/* Define to 1 if you have the `EVP_MD_CTX_cleanup' function. */ +#define HAVE_EVP_MD_CTX_CLEANUP 1 + +/* Define to 1 if you have the `EVP_MD_CTX_copy_ex' function. */ +#define HAVE_EVP_MD_CTX_COPY_EX 1 + +/* Define to 1 if you have the `EVP_MD_CTX_free' function. */ +#define HAVE_EVP_MD_CTX_FREE 1 + +/* Define to 1 if you have the `EVP_MD_CTX_init' function. */ +#define HAVE_EVP_MD_CTX_INIT 1 + +/* Define to 1 if you have the `EVP_MD_CTX_new' function. */ +#define HAVE_EVP_MD_CTX_NEW 1 + +/* Define to 1 if you have the `EVP_PKEY_get0_RSA' function. */ +#define HAVE_EVP_PKEY_GET0_RSA 1 + +/* Define to 1 if you have the `EVP_sha256' function. */ +#define HAVE_EVP_SHA256 1 + +/* Define to 1 if you have the `EVP_sha384' function. */ +#define HAVE_EVP_SHA384 1 + +/* Define to 1 if you have the `EVP_sha512' function. */ +#define HAVE_EVP_SHA512 1 + +/* Define if you have ut_exit in utmp.h */ +#define HAVE_EXIT_IN_UTMP 1 + +/* Define to 1 if you have the `explicit_bzero' function. */ +/* #undef HAVE_EXPLICIT_BZERO */ + +/* Define to 1 if you have the `explicit_memset' function. */ +/* #undef HAVE_EXPLICIT_MEMSET */ + +/* Define to 1 if you have the `fchmod' function. */ +#define HAVE_FCHMOD 1 + +/* Define to 1 if you have the `fchmodat' function. */ +#define HAVE_FCHMODAT 1 + +/* Define to 1 if you have the `fchown' function. */ +#define HAVE_FCHOWN 1 + +/* Define to 1 if you have the `fchownat' function. */ +#define HAVE_FCHOWNAT 1 + +/* Use F_CLOSEM fcntl for closefrom */ +/* #undef HAVE_FCNTL_CLOSEM */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if the system has the type `fd_mask'. */ +#define HAVE_FD_MASK 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FEATURES_H 1 + +/* Define to 1 if you have the `fido_assert_set_clientdata' function. */ +/* #undef HAVE_FIDO_ASSERT_SET_CLIENTDATA */ + +/* Define to 1 if you have the `fido_cred_prot' function. */ +/* #undef HAVE_FIDO_CRED_PROT */ + +/* Define to 1 if you have the `fido_cred_set_clientdata' function. */ +/* #undef HAVE_FIDO_CRED_SET_CLIENTDATA */ + +/* Define to 1 if you have the `fido_cred_set_prot' function. */ +/* #undef HAVE_FIDO_CRED_SET_PROT */ + +/* Define to 1 if you have the `fido_dev_get_touch_begin' function. */ +/* #undef HAVE_FIDO_DEV_GET_TOUCH_BEGIN */ + +/* Define to 1 if you have the `fido_dev_get_touch_status' function. */ +/* #undef HAVE_FIDO_DEV_GET_TOUCH_STATUS */ + +/* Define to 1 if you have the `fido_dev_supports_cred_prot' function. */ +/* #undef HAVE_FIDO_DEV_SUPPORTS_CRED_PROT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FLOATINGPOINT_H */ + +/* Define to 1 if you have the `flock' function. */ +#define HAVE_FLOCK 1 + +/* Define to 1 if you have the `fmt_scaled' function. */ +/* #undef HAVE_FMT_SCALED */ + +/* Define to 1 if you have the `fnmatch' function. */ +#define HAVE_FNMATCH 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define to 1 if you have the `freeaddrinfo' function. */ +#define HAVE_FREEADDRINFO 1 + +/* Define to 1 if you have the `freezero' function. */ +/* #undef HAVE_FREEZERO */ + +/* Define to 1 if the system has the type `fsblkcnt_t'. */ +#define HAVE_FSBLKCNT_T 1 + +/* Define to 1 if the system has the type `fsfilcnt_t'. */ +#define HAVE_FSFILCNT_T 1 + +/* Define to 1 if you have the `fstatfs' function. */ +#define HAVE_FSTATFS 1 + +/* Define to 1 if you have the `fstatvfs' function. */ +#define HAVE_FSTATVFS 1 + +/* Define to 1 if you have the `futimes' function. */ +#define HAVE_FUTIMES 1 + +/* Define to 1 if you have the `gai_strerror' function. */ +#define HAVE_GAI_STRERROR 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getaudit' function. */ +/* #undef HAVE_GETAUDIT */ + +/* Define to 1 if you have the `getaudit_addr' function. */ +/* #undef HAVE_GETAUDIT_ADDR */ + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getgrouplist' function. */ +#define HAVE_GETGROUPLIST 1 + +/* Define to 1 if you have the `getgrset' function. */ +/* #undef HAVE_GETGRSET */ + +/* Define to 1 if you have the `getlastlogxbyname' function. */ +/* #undef HAVE_GETLASTLOGXBYNAME */ + +/* Define to 1 if you have the `getline' function. */ +#define HAVE_GETLINE 1 + +/* Define to 1 if you have the `getluid' function. */ +/* #undef HAVE_GETLUID */ + +/* Define to 1 if you have the `getnameinfo' function. */ +#define HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getopt' function. */ +#define HAVE_GETOPT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define if your getopt(3) defines and uses optreset */ +#define HAVE_GETOPT_OPTRESET 1 + +/* Define if your libraries define getpagesize() */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `getpeereid' function. */ +/* #undef HAVE_GETPEEREID */ + +/* Define to 1 if you have the `getpeerucred' function. */ +/* #undef HAVE_GETPEERUCRED */ + +/* Define to 1 if you have the `getpgid' function. */ +#define HAVE_GETPGID 1 + +/* Define to 1 if you have the `getpgrp' function. */ +#define HAVE_GETPGRP 1 + +/* Define to 1 if you have the `getpwanam' function. */ +/* #undef HAVE_GETPWANAM */ + +/* Define to 1 if you have the `getrandom' function. */ +#define HAVE_GETRANDOM 1 + +/* Define to 1 if you have the `getrlimit' function. */ +#define HAVE_GETRLIMIT 1 + +/* Define if getrrsetbyname() exists */ +/* #undef HAVE_GETRRSETBYNAME */ + +/* Define to 1 if you have the `getseuserbyname' function. */ +/* #undef HAVE_GETSEUSERBYNAME */ + +/* Define to 1 if you have the `getsid' function. */ +#define HAVE_GETSID 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `getttyent' function. */ +/* #undef HAVE_GETTTYENT */ + +/* Define to 1 if you have the `getutent' function. */ +#define HAVE_GETUTENT 1 + +/* Define to 1 if you have the `getutid' function. */ +/* #undef HAVE_GETUTID */ + +/* Define to 1 if you have the `getutline' function. */ +/* #undef HAVE_GETUTLINE */ + +/* Define to 1 if you have the `getutxent' function. */ +/* #undef HAVE_GETUTXENT */ + +/* Define to 1 if you have the `getutxid' function. */ +/* #undef HAVE_GETUTXID */ + +/* Define to 1 if you have the `getutxline' function. */ +/* #undef HAVE_GETUTXLINE */ + +/* Define to 1 if you have the `getutxuser' function. */ +/* #undef HAVE_GETUTXUSER */ + +/* Define to 1 if you have the `get_default_context_with_level' function. */ +/* #undef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL */ + +/* Define to 1 if you have the `glob' function. */ +#define HAVE_GLOB 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GLOB_H 1 + +/* Define to 1 if you have the `group_from_gid' function. */ +/* #undef HAVE_GROUP_FROM_GID */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_GENERIC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_GENERIC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_KRB5_H */ + +/* Define if HEADER.ad exists in arpa/nameser.h */ +#define HAVE_HEADER_AD 1 + +/* Define to 1 if you have the `HMAC_CTX_init' function. */ +#define HAVE_HMAC_CTX_INIT 1 + +/* Define if you have ut_host in utmp.h */ +#define HAVE_HOST_IN_UTMP 1 + +/* Define if you have ut_host in utmpx.h */ +/* #undef HAVE_HOST_IN_UTMPX */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IAF_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IA_H */ + +/* Define if you have ut_id in utmp.h */ +#define HAVE_ID_IN_UTMP 1 + +/* Define if you have ut_id in utmpx.h */ +/* #undef HAVE_ID_IN_UTMPX */ + +/* Define to 1 if you have the header file. */ +#define HAVE_IFADDRS_H 1 + +/* Define to 1 if you have the `inet_aton' function. */ +#define HAVE_INET_ATON 1 + +/* Define to 1 if you have the `inet_ntoa' function. */ +#define HAVE_INET_NTOA 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `innetgr' function. */ +/* #undef HAVE_INNETGR */ + +/* define if you have int64_t data type */ +#define HAVE_INT64_T 1 + +/* Define to 1 if the system has the type `intmax_t'. */ +#define HAVE_INTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* define if you have intxx_t data type */ +#define HAVE_INTXX_T 1 + +/* Define to 1 if the system has the type `in_addr_t'. */ +#define HAVE_IN_ADDR_T 1 + +/* Define to 1 if the system has the type `in_port_t'. */ +#define HAVE_IN_PORT_T 1 + +/* Define if you have isblank(3C). */ +#define HAVE_ISBLANK 1 + +/* Define to 1 if you have the `killpg' function. */ +#define HAVE_KILLPG 1 + +/* Define to 1 if you have the `krb5_cc_new_unique' function. */ +/* #undef HAVE_KRB5_CC_NEW_UNIQUE */ + +/* Define to 1 if you have the `krb5_free_error_message' function. */ +/* #undef HAVE_KRB5_FREE_ERROR_MESSAGE */ + +/* Define to 1 if you have the `krb5_get_error_message' function. */ +/* #undef HAVE_KRB5_GET_ERROR_MESSAGE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LASTLOG_H 1 + +/* Define if you want ldns support */ +/* #undef HAVE_LDNS */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LIBAUDIT_H */ + +/* Define to 1 if you have the `bsm' library (-lbsm). */ +/* #undef HAVE_LIBBSM */ + +/* Define to 1 if you have the `crypt' library (-lcrypt). */ +/* #undef HAVE_LIBCRYPT */ + +/* Define to 1 if you have the `dl' library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBGEN_H 1 + +/* Define if system has libiaf that supports set_id */ +/* #undef HAVE_LIBIAF */ + +/* Define to 1 if you have the `network' library (-lnetwork). */ +/* #undef HAVE_LIBNETWORK */ + +/* Define to 1 if you have the `pam' library (-lpam). */ +/* #undef HAVE_LIBPAM */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LIBPROC_H */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LIBUTIL_H */ + +/* Define to 1 if you have the `xnet' library (-lxnet). */ +/* #undef HAVE_LIBXNET */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LINUX_AUDIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LINUX_FILTER_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LINUX_IF_TUN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LINUX_SECCOMP_H 1 + +/* Define to 1 if you have the `llabs' function. */ +#define HAVE_LLABS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `localtime_r' function. */ +#define HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the `login' function. */ +/* #undef HAVE_LOGIN */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LOGIN_CAP_H */ + +/* Define to 1 if you have the `login_getcapbool' function. */ +/* #undef HAVE_LOGIN_GETCAPBOOL */ + +/* Define to 1 if you have the `login_getpwclass' function. */ +/* #undef HAVE_LOGIN_GETPWCLASS */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LOGIN_H */ + +/* Define to 1 if you have the `logout' function. */ +/* #undef HAVE_LOGOUT */ + +/* Define to 1 if you have the `logwtmp' function. */ +/* #undef HAVE_LOGWTMP */ + +/* Define to 1 if the system has the type `long double'. */ +#define HAVE_LONG_DOUBLE 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MAILLOCK_H */ + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 0 + +/* Define to 1 if you have the `mblen' function. */ +#define HAVE_MBLEN 1 + +/* Define to 1 if you have the `mbtowc' function. */ +#define HAVE_MBTOWC 1 + +/* Define to 1 if you have the `memmem' function. */ +#define HAVE_MEMMEM 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset_s' function. */ +/* #undef HAVE_MEMSET_S */ + +/* Define to 1 if you have the `mkdtemp' function. */ +#define HAVE_MKDTEMP 1 + +/* define if you have mode_t data type */ +#define HAVE_MODE_T 1 + +/* Some systems put nanosleep outside of libc */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETGROUP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_TUN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_ROUTE_H 1 + +/* Define if you are on NeXT */ +/* #undef HAVE_NEXT */ + +/* Define to 1 if the system has the type `nfds_t'. */ +#define HAVE_NFDS_T 1 + +/* Define to 1 if you have the `ngetaddrinfo' function. */ +/* #undef HAVE_NGETADDRINFO */ + +/* Define to 1 if you have the `nl_langinfo' function. */ +#define HAVE_NL_LANGINFO 1 + +/* Define to 1 if you have the `nsleep' function. */ +/* #undef HAVE_NSLEEP */ + +/* Define to 1 if you have the `ogetaddrinfo' function. */ +/* #undef HAVE_OGETADDRINFO */ + +/* Define if you have an old version of PAM which takes only one argument to + pam_strerror */ +/* #undef HAVE_OLD_PAM */ + +/* Define to 1 if you have the `openlog_r' function. */ +/* #undef HAVE_OPENLOG_R */ + +/* Define to 1 if you have the `openpty' function. */ +#define HAVE_OPENPTY 1 + +/* as a macro */ +#define HAVE_OPENSSL_ADD_ALL_ALGORITHMS 1 + +/* Define to 1 if you have the `OPENSSL_init_crypto' function. */ +#define HAVE_OPENSSL_INIT_CRYPTO 1 + +/* Define to 1 if you have the `OpenSSL_version' function. */ +#define HAVE_OPENSSL_VERSION 1 + +/* Define to 1 if you have the `OpenSSL_version_num' function. */ +#define HAVE_OPENSSL_VERSION_NUM 1 + +/* Define if you have Digital Unix Security Integration Architecture */ +/* #undef HAVE_OSF_SIA */ + +/* Define to 1 if you have the `pam_getenvlist' function. */ +/* #undef HAVE_PAM_GETENVLIST */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PAM_PAM_APPL_H */ + +/* Define to 1 if you have the `pam_putenv' function. */ +/* #undef HAVE_PAM_PUTENV */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PATHS_H 1 + +/* Define if you have ut_pid in utmp.h */ +#define HAVE_PID_IN_UTMP 1 + +/* define if you have pid_t data type */ +#define HAVE_PID_T 1 + +/* Define to 1 if you have the `pledge' function. */ +/* #undef HAVE_PLEDGE */ + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `ppoll' function. */ +#define HAVE_PPOLL 1 + +/* Define to 1 if you have the `prctl' function. */ +#define HAVE_PRCTL 1 + +/* Define to 1 if you have the `priv_basicset' function. */ +/* #undef HAVE_PRIV_BASICSET */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PRIV_H */ + +/* Define to 1 if you have the `procctl' function. */ +/* #undef HAVE_PROCCTL */ + +/* Define if you have /proc/$pid/fd */ +#define HAVE_PROC_PID 1 + +/* Define to 1 if you have the `proc_pidinfo' function. */ +/* #undef HAVE_PROC_PIDINFO */ + +/* Define to 1 if you have the `pselect' function. */ +#define HAVE_PSELECT 1 + +/* Define to 1 if you have the `pstat' function. */ +/* #undef HAVE_PSTAT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PTY_H 1 + +/* Define to 1 if you have the `pututline' function. */ +#define HAVE_PUTUTLINE 1 + +/* Define to 1 if you have the `pututxline' function. */ +/* #undef HAVE_PUTUTXLINE */ + +/* Define to 1 if you have the `raise' function. */ +#define HAVE_RAISE 1 + +/* Define to 1 if you have the `readpassphrase' function. */ +/* #undef HAVE_READPASSPHRASE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READPASSPHRASE_H */ + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 0 + +/* Define to 1 if you have the `reallocarray' function. */ +#define HAVE_REALLOCARRAY 1 + +/* Define to 1 if you have the `realpath' function. */ +#define HAVE_REALPATH 1 + +/* Define to 1 if you have the `recallocarray' function. */ +/* #undef HAVE_RECALLOCARRAY */ + +/* Define to 1 if you have the `recvmsg' function. */ +#define HAVE_RECVMSG 1 + +/* sys/resource.h has RLIMIT_NPROC */ +#define HAVE_RLIMIT_NPROC /**/ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_RPC_TYPES_H */ + +/* Define to 1 if you have the `rresvport_af' function. */ +/* #undef HAVE_RRESVPORT_AF */ + +/* Define to 1 if you have the `RSA_generate_key_ex' function. */ +#define HAVE_RSA_GENERATE_KEY_EX 1 + +/* Define to 1 if you have the `RSA_get0_crt_params' function. */ +#define HAVE_RSA_GET0_CRT_PARAMS 1 + +/* Define to 1 if you have the `RSA_get0_factors' function. */ +#define HAVE_RSA_GET0_FACTORS 1 + +/* Define to 1 if you have the `RSA_get0_key' function. */ +#define HAVE_RSA_GET0_KEY 1 + +/* Define to 1 if you have the `RSA_get_default_method' function. */ +/* #undef HAVE_RSA_GET_DEFAULT_METHOD */ + +/* Define to 1 if you have the `RSA_meth_dup' function. */ +/* #undef HAVE_RSA_METH_DUP */ + +/* Define to 1 if you have the `RSA_meth_free' function. */ +/* #undef HAVE_RSA_METH_FREE */ + +/* Define to 1 if you have the `RSA_meth_get_finish' function. */ +/* #undef HAVE_RSA_METH_GET_FINISH */ + +/* Define to 1 if you have the `RSA_meth_set1_name' function. */ +/* #undef HAVE_RSA_METH_SET1_NAME */ + +/* Define to 1 if you have the `RSA_meth_set_finish' function. */ +/* #undef HAVE_RSA_METH_SET_FINISH */ + +/* Define to 1 if you have the `RSA_meth_set_priv_dec' function. */ +/* #undef HAVE_RSA_METH_SET_PRIV_DEC */ + +/* Define to 1 if you have the `RSA_meth_set_priv_enc' function. */ +/* #undef HAVE_RSA_METH_SET_PRIV_ENC */ + +/* Define to 1 if you have the `RSA_set0_crt_params' function. */ +#define HAVE_RSA_SET0_CRT_PARAMS 1 + +/* Define to 1 if you have the `RSA_set0_factors' function. */ +#define HAVE_RSA_SET0_FACTORS 1 + +/* Define to 1 if you have the `RSA_set0_key' function. */ +#define HAVE_RSA_SET0_KEY 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SANDBOX_H */ + +/* Define to 1 if you have the `sandbox_init' function. */ +/* #undef HAVE_SANDBOX_INIT */ + +/* define if you have sa_family_t data type */ +#define HAVE_SA_FAMILY_T 1 + +/* Define to 1 if you have the `scan_scaled' function. */ +/* #undef HAVE_SCAN_SCALED */ + +/* Define if you have SecureWare-based protected password database */ +/* #undef HAVE_SECUREWARE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SECURITY_PAM_APPL_H */ + +/* Define to 1 if you have the `sendmsg' function. */ +#define HAVE_SENDMSG 1 + +/* Define to 1 if you have the `setauthdb' function. */ +/* #undef HAVE_SETAUTHDB */ + +/* Define to 1 if you have the `setdtablesize' function. */ +/* #undef HAVE_SETDTABLESIZE */ + +/* Define to 1 if you have the `setegid' function. */ +#define HAVE_SETEGID 1 + +/* Define to 1 if you have the `setenv' function. */ +#define HAVE_SETENV 1 + +/* Define to 1 if you have the `seteuid' function. */ +#define HAVE_SETEUID 1 + +/* Define to 1 if you have the `setgroupent' function. */ +/* #undef HAVE_SETGROUPENT */ + +/* Define to 1 if you have the `setgroups' function. */ +#define HAVE_SETGROUPS 1 + +/* Define to 1 if you have the `setlinebuf' function. */ +#define HAVE_SETLINEBUF 1 + +/* Define to 1 if you have the `setlogin' function. */ +/* #undef HAVE_SETLOGIN */ + +/* Define to 1 if you have the `setluid' function. */ +/* #undef HAVE_SETLUID */ + +/* Define to 1 if you have the `setpassent' function. */ +/* #undef HAVE_SETPASSENT */ + +/* Define to 1 if you have the `setpcred' function. */ +/* #undef HAVE_SETPCRED */ + +/* Define to 1 if you have the `setpflags' function. */ +/* #undef HAVE_SETPFLAGS */ + +/* Define to 1 if you have the `setppriv' function. */ +/* #undef HAVE_SETPPRIV */ + +/* Define to 1 if you have the `setproctitle' function. */ +/* #undef HAVE_SETPROCTITLE */ + +/* Define to 1 if you have the `setregid' function. */ +#define HAVE_SETREGID 1 + +/* Define to 1 if you have the `setresgid' function. */ +#define HAVE_SETRESGID 1 + +/* Define to 1 if you have the `setresuid' function. */ +#define HAVE_SETRESUID 1 + +/* Define to 1 if you have the `setreuid' function. */ +#define HAVE_SETREUID 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#define HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `setsid' function. */ +#define HAVE_SETSID 1 + +/* Define to 1 if you have the `setutent' function. */ +#define HAVE_SETUTENT 1 + +/* Define to 1 if you have the `setutxdb' function. */ +/* #undef HAVE_SETUTXDB */ + +/* Define to 1 if you have the `setutxent' function. */ +/* #undef HAVE_SETUTXENT */ + +/* Define to 1 if you have the `setvbuf' function. */ +#define HAVE_SETVBUF 1 + +/* Define to 1 if you have the `set_id' function. */ +/* #undef HAVE_SET_ID */ + +/* Define to 1 if you have the `SHA256Update' function. */ +/* #undef HAVE_SHA256UPDATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SHA2_H */ + +/* Define to 1 if you have the `SHA384Update' function. */ +/* #undef HAVE_SHA384UPDATE */ + +/* Define to 1 if you have the `SHA512Update' function. */ +/* #undef HAVE_SHA512UPDATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SHADOW_H */ + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if the system has the type `sighandler_t'. */ +#define HAVE_SIGHANDLER_T 1 + +/* Define to 1 if you have the `sigvec' function. */ +/* #undef HAVE_SIGVEC */ + +/* Define to 1 if the system has the type `sig_atomic_t'. */ +#define HAVE_SIG_ATOMIC_T 1 + +/* define if you have size_t data type */ +#define HAVE_SIZE_T 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `socketpair' function. */ +#define HAVE_SOCKETPAIR 1 + +/* Have PEERCRED socket option */ +#define HAVE_SO_PEERCRED 1 + +/* define if you have ssize_t data type */ +#define HAVE_SSIZE_T 1 + +/* Fields in struct sockaddr_storage */ +#define HAVE_SS_FAMILY_IN_SS 1 + +/* Define if you have ut_ss in utmpx.h */ +/* #undef HAVE_SS_IN_UTMPX */ + +/* Define to 1 if you have the `statfs' function. */ +#define HAVE_STATFS 1 + +/* Define to 1 if you have the `statvfs' function. */ +#define HAVE_STATVFS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasestr' function. */ +#define HAVE_STRCASESTR 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcat' function. */ +#define HAVE_STRLCAT 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strmode' function. */ +/* #undef HAVE_STRMODE */ + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if you have the `strnlen' function. */ +#define HAVE_STRNLEN 1 + +/* Define to 1 if you have the `strnvis' function. */ +/* #undef HAVE_STRNVIS */ + +/* Define to 1 if you have the `strptime' function. */ +#define HAVE_STRPTIME 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* Define to 1 if you have the `strsignal' function. */ +#define HAVE_STRSIGNAL 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtonum' function. */ +/* #undef HAVE_STRTONUM */ + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#define HAVE_STRTOULL 1 + +/* define if you have struct addrinfo data type */ +#define HAVE_STRUCT_ADDRINFO 1 + +/* define if you have struct in6_addr data type */ +#define HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if `pw_change' is a member of `struct passwd'. */ +/* #undef HAVE_STRUCT_PASSWD_PW_CHANGE */ + +/* Define to 1 if `pw_class' is a member of `struct passwd'. */ +/* #undef HAVE_STRUCT_PASSWD_PW_CLASS */ + +/* Define to 1 if `pw_expire' is a member of `struct passwd'. */ +/* #undef HAVE_STRUCT_PASSWD_PW_EXPIRE */ + +/* Define to 1 if `pw_gecos' is a member of `struct passwd'. */ +#define HAVE_STRUCT_PASSWD_PW_GECOS 1 + +/* Define to 1 if `fd' is a member of `struct pollfd'. */ +#define HAVE_STRUCT_POLLFD_FD 1 + +/* define if you have struct sockaddr_in6 data type */ +#define HAVE_STRUCT_SOCKADDR_IN6 1 + +/* Define to 1 if `sin6_scope_id' is a member of `struct sockaddr_in6'. */ +#define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* define if you have struct sockaddr_storage data type */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if `f_files' is a member of `struct statfs'. */ +#define HAVE_STRUCT_STATFS_F_FILES 1 + +/* Define to 1 if `f_flags' is a member of `struct statfs'. */ +#define HAVE_STRUCT_STATFS_F_FLAGS 1 + +/* Define to 1 if `st_blksize' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_BLKSIZE 1 + +/* Define to 1 if `st_mtim' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_MTIM 1 + +/* Define to 1 if `st_mtime' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_MTIME 1 + +/* define if you have struct timespec */ +#define HAVE_STRUCT_TIMESPEC 1 + +/* define if you have struct timeval */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define to 1 if you have the `swap32' function. */ +/* #undef HAVE_SWAP32 */ + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define if you have syslen in utmpx.h */ +/* #undef HAVE_SYSLEN_IN_UTMPX */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_AUDIT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BITYPES_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BSDTTY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_BYTEORDER_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_CAPSICUM_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_CDEFS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define if your system defines sys_errlist[] */ +/* #undef HAVE_SYS_ERRLIST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_LABEL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MOUNT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if your system defines sys_nerr */ +/* #undef HAVE_SYS_NERR */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PRCTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PROCCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PTMS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PTRACE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RANDOM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STATVFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_STREAM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_STROPTS_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_STRTIO_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCTL_H */ + +/* Force use of sys/syslog.h on Ultrix */ +/* #undef HAVE_SYS_SYSLOG_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSMACROS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TIMERS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_VFS_H 1 + +/* Define to 1 if you have the `tcgetpgrp' function. */ +#define HAVE_TCGETPGRP 1 + +/* Define to 1 if you have the `tcsendbreak' function. */ +#define HAVE_TCSENDBREAK 1 + +/* Define to 1 if you have the `time' function. */ +#define HAVE_TIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if you have ut_time in utmp.h */ +/* #undef HAVE_TIME_IN_UTMP */ + +/* Define if you have ut_time in utmpx.h */ +/* #undef HAVE_TIME_IN_UTMPX */ + +/* Define to 1 if you have the `timingsafe_bcmp' function. */ +/* #undef HAVE_TIMINGSAFE_BCMP */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TMPDIR_H */ + +/* Define to 1 if you have the `truncate' function. */ +#define HAVE_TRUNCATE 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TTYENT_H */ + +/* Define if you have ut_tv in utmp.h */ +#define HAVE_TV_IN_UTMP 1 + +/* Define if you have ut_tv in utmpx.h */ +/* #undef HAVE_TV_IN_UTMPX */ + +/* Define if you have ut_type in utmp.h */ +#define HAVE_TYPE_IN_UTMP 1 + +/* Define if you have ut_type in utmpx.h */ +/* #undef HAVE_TYPE_IN_UTMPX */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UCRED_H */ + +/* Define to 1 if the system has the type `uintmax_t'. */ +#define HAVE_UINTMAX_T 1 + +/* define if you have uintxx_t data type */ +#define HAVE_UINTXX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `unsetenv' function. */ +#define HAVE_UNSETENV 1 + +/* Define to 1 if the system has the type `unsigned long long'. */ +#define HAVE_UNSIGNED_LONG_LONG 1 + +/* Define to 1 if you have the `updwtmp' function. */ +/* #undef HAVE_UPDWTMP */ + +/* Define to 1 if you have the `updwtmpx' function. */ +/* #undef HAVE_UPDWTMPX */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_USERSEC_H */ + +/* Define to 1 if you have the `user_from_uid' function. */ +/* #undef HAVE_USER_FROM_UID */ + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UTIL_H */ + +/* Define to 1 if you have the `utimensat' function. */ +#define HAVE_UTIMENSAT 1 + +/* Define to 1 if you have the `utimes' function. */ +#define HAVE_UTIMES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if you have the `utmpname' function. */ +#define HAVE_UTMPNAME 1 + +/* Define to 1 if you have the `utmpxname' function. */ +/* #undef HAVE_UTMPXNAME */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UTMPX_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UTMP_H 1 + +/* define if you have u_char data type */ +#define HAVE_U_CHAR 1 + +/* define if you have u_int data type */ +#define HAVE_U_INT 1 + +/* define if you have u_int64_t data type */ +#define HAVE_U_INT64_T 1 + +/* define if you have u_intxx_t data type */ +#define HAVE_U_INTXX_T 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define if va_copy exists */ +#define HAVE_VA_COPY 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VIS_H */ + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `waitpid' function. */ +#define HAVE_WAITPID 1 + +/* Define to 1 if you have the `warn' function. */ +#define HAVE_WARN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the `wcwidth' function. */ +#define HAVE_WCWIDTH 1 + +/* Define to 1 if you have the `_getlong' function. */ +#define HAVE__GETLONG 1 + +/* Define to 1 if you have the `_getpty' function. */ +/* #undef HAVE__GETPTY */ + +/* Define to 1 if you have the `_getshort' function. */ +#define HAVE__GETSHORT 1 + +/* Define if you have struct __res_state _res as an extern */ +/* #undef HAVE__RES_EXTERN */ + +/* Define to 1 if you have the `__b64_ntop' function. */ +#define HAVE___B64_NTOP 1 + +/* Define to 1 if you have the `__b64_pton' function. */ +#define HAVE___B64_PTON 1 + +/* Define if compiler implements __FUNCTION__ */ +#define HAVE___FUNCTION__ 1 + +/* Define if libc defines __progname */ +#define HAVE___PROGNAME 1 + +/* Fields in struct sockaddr_storage */ +/* #undef HAVE___SS_FAMILY_IN_SS */ + +/* Define if __va_copy exists */ +#define HAVE___VA_COPY 1 + +/* Define if compiler implements __func__ */ +#define HAVE___func__ 1 + +/* Define this if you are using the Heimdal version of Kerberos V5 */ +/* #undef HEIMDAL */ + +/* Define if you need to use IP address instead of hostname in $DISPLAY */ +/* #undef IPADDR_IN_DISPLAY */ + +/* Detect IPv4 in IPv6 mapped addresses and treat as IPv4 */ +#define IPV4_IN_IPV6 1 + +/* Define if your system choked on IP TOS setting */ +/* #undef IP_TOS_IS_BROKEN */ + +/* Define if you want Kerberos 5 support */ +/* #undef KRB5 */ + +/* Define if pututxline updates lastlog too */ +/* #undef LASTLOG_WRITE_PUTUTXLINE */ + +/* Define to whatever link() returns for "not supported" if it doesn't return + EOPNOTSUPP. */ +#define LINK_OPNOTSUPP_ERRNO EPERM + +/* Adjust Linux out-of-memory killer */ +#define LINUX_OOM_ADJUST 1 + +/* max value of long long calculated by configure */ +/* #undef LLONG_MAX */ + +/* min value of long long calculated by configure */ +/* #undef LLONG_MIN */ + +/* Account locked with pw(1) */ +#define LOCKED_PASSWD_PREFIX "!" + +/* String used in /etc/passwd to denote locked account */ +/* #undef LOCKED_PASSWD_STRING */ + +/* String used in /etc/passwd to denote locked account */ +/* #undef LOCKED_PASSWD_SUBSTR */ + +/* Some systems need a utmpx entry for /bin/login to work */ +/* #undef LOGIN_NEEDS_UTMPX */ + +/* Set this to your mail directory if you do not have _PATH_MAILDIR */ +/* #undef MAIL_DIRECTORY */ + +/* Need setpgrp to for controlling tty */ +/* #undef NEED_SETPGRP */ + +/* compiler does not accept __attribute__ on prototype args */ +/* #undef NO_ATTRIBUTE_ON_PROTOTYPE_ARGS */ + +/* compiler does not accept __attribute__ on return types */ +/* #undef NO_ATTRIBUTE_ON_RETURN_TYPE */ + +/* SA_RESTARTed signals do no interrupt select */ +/* #undef NO_SA_RESTART */ + +/* Define to disable UID restoration test */ +/* #undef NO_UID_RESTORATION_TEST */ + +/* Define if X11 doesn't support AF_UNIX sockets on that system */ +/* #undef NO_X11_UNIX_SOCKETS */ + +/* Define if EVP_DigestUpdate returns void */ +/* #undef OPENSSL_EVP_DIGESTUPDATE_VOID */ + +/* OpenSSL has ECC */ +#define OPENSSL_HAS_ECC 1 + +/* libcrypto has NID_X9_62_prime256v1 */ +#define OPENSSL_HAS_NISTP256 1 + +/* libcrypto has NID_secp384r1 */ +#define OPENSSL_HAS_NISTP384 1 + +/* libcrypto has NID_secp521r1 */ +#define OPENSSL_HAS_NISTP521 1 + +/* libcrypto has EVP AES CTR */ +#define OPENSSL_HAVE_EVPCTR 1 + +/* libcrypto has EVP AES GCM */ +#define OPENSSL_HAVE_EVPGCM 1 + +/* libcrypto is missing AES 192 and 256 bit functions */ +/* #undef OPENSSL_LOBOTOMISED_AES */ + +/* Define if you want the OpenSSL internally seeded PRNG only */ +#define OPENSSL_PRNG_ONLY 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "openssh-unix-dev@mindrot.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "OpenSSH" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "OpenSSH Portable" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "openssh" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "Portable" + +/* Define if you are using Solaris-derived PAM which passes pam_messages to + the conversation function with an extra level of indirection */ +/* #undef PAM_SUN_CODEBASE */ + +/* Work around problematic Linux PAM modules handling of PAM_TTY */ +#define PAM_TTY_KLUDGE 1 + +/* must supply username to passwd */ +/* #undef PASSWD_NEEDS_USERNAME */ + +/* System dirs owned by bin (uid 2) */ +/* #undef PLATFORM_SYS_DIR_UID */ + +/* Port number of PRNGD/EGD random number socket */ +/* #undef PRNGD_PORT */ + +/* Location of PRNGD/EGD random number socket */ +/* #undef PRNGD_SOCKET */ + +/* read(1) can return 0 for a non-closed fd */ +/* #undef PTY_ZEROREAD */ + +/* Sandbox using capsicum */ +/* #undef SANDBOX_CAPSICUM */ + +/* Sandbox using Darwin sandbox_init(3) */ +/* #undef SANDBOX_DARWIN */ + +/* no privsep sandboxing */ +/* #undef SANDBOX_NULL */ + +/* Sandbox using pledge(2) */ +/* #undef SANDBOX_PLEDGE */ + +/* Sandbox using setrlimit(2) */ +#define SANDBOX_RLIMIT 1 + +/* Sandbox using seccomp filter */ +/* #undef SANDBOX_SECCOMP_FILTER */ + +/* setrlimit RLIMIT_FSIZE works */ +/* #undef SANDBOX_SKIP_RLIMIT_FSIZE */ + +/* define if setrlimit RLIMIT_NOFILE breaks things */ +/* On Android, this breaks using ppoll */ +#define SANDBOX_SKIP_RLIMIT_NOFILE 1 + +/* Sandbox using Solaris/Illumos privileges */ +/* #undef SANDBOX_SOLARIS */ + +/* Sandbox using systrace(4) */ +/* #undef SANDBOX_SYSTRACE */ + +/* Specify the system call convention in use */ +#ifdef _ILP32 +# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM +#else +# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64 +#endif + +/* Define if your platform breaks doing a seteuid before a setuid */ +/* #undef SETEUID_BREAKS_SETUID */ + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT __SIZEOF_INT__ + +/* The size of `long int', as computed by sizeof. */ +#define SIZEOF_LONG_INT __SIZEOF_LONG__ + +/* The size of `long long int', as computed by sizeof. */ +#define SIZEOF_LONG_LONG_INT __SIZEOF_LONG_LONG__ + +/* The size of `short int', as computed by sizeof. */ +#define SIZEOF_SHORT_INT __SIZEOF_SHORT__ + +/* The size of `time_t', as computed by sizeof. */ +#define SIZEOF_TIME_T __SIZEOF_LONG__ + +/* Define as const if snprintf() can declare const char *fmt */ +#define SNPRINTF_CONST const + +/* Define to a Set Process Title type if your system is supported by + bsd-setproctitle.c */ +#define SPT_TYPE SPT_REUSEARGV + +/* Define if sshd somehow reacquires a controlling TTY after setsid() */ +/* #undef SSHD_ACQUIRES_CTTY */ + +/* sshd PAM service name */ +/* #undef SSHD_PAM_SERVICE */ + +/* Define if pam_chauthtok wants real uid set to the unpriv'ed user */ +/* #undef SSHPAM_CHAUTHTOK_NEEDS_RUID */ + +/* Use audit debugging module */ +/* #undef SSH_AUDIT_EVENTS */ + +/* Windows is sensitive to read buffer size */ +/* #undef SSH_IOBUFSZ */ + +/* non-privileged user for privilege separation */ +#define SSH_PRIVSEP_USER "shell" + +/* Use tunnel device compatibility to OpenBSD */ +#define SSH_TUN_COMPAT_AF 1 + +/* Open tunnel devices the FreeBSD way */ +/* #undef SSH_TUN_FREEBSD */ + +/* Open tunnel devices the Linux tun/tap way */ +#define SSH_TUN_LINUX 1 + +/* No layer 2 tunnel support */ +/* #undef SSH_TUN_NO_L2 */ + +/* Open tunnel devices the OpenBSD way */ +/* #undef SSH_TUN_OPENBSD */ + +/* Prepend the address family to IP tunnel traffic */ +#define SSH_TUN_PREPEND_AF 1 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you want a different $PATH for the superuser */ +/* #undef SUPERUSER_PATH */ + +/* syslog_r function is safe to use in in a signal handler */ +/* #undef SYSLOG_R_SAFE_IN_SIGHAND */ + +/* Support routing domains using Linux VRF */ +#define SYS_RDOMAIN_LINUX 1 + +/* Support passwords > 8 chars */ +/* #undef UNIXWARE_LONG_PASSWORDS */ + +/* Specify default $PATH */ +#define USER_PATH "/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin" + +/* Define this if you want to use libkafs' AFS support */ +/* #undef USE_AFS */ + +/* Use BSM audit module */ +/* #undef USE_BSM_AUDIT */ + +/* Use btmp to log bad logins */ +/* #undef USE_BTMP */ + +/* Use libedit for sftp */ +/* #undef USE_LIBEDIT */ + +/* Use Linux audit module */ +/* #undef USE_LINUX_AUDIT */ + +/* Enable OpenSSL engine support */ +/* #undef USE_OPENSSL_ENGINE */ + +/* Define if you want to enable PAM support */ +/* #undef USE_PAM */ + +/* Use PIPES instead of a socketpair() */ +/* #undef USE_PIPES */ + +/* Define if you have Solaris privileges */ +/* #undef USE_SOLARIS_PRIVS */ + +/* Define if you have Solaris process contracts */ +/* #undef USE_SOLARIS_PROCESS_CONTRACTS */ + +/* Define if you have Solaris projects */ +/* #undef USE_SOLARIS_PROJECTS */ + +/* compiler variable declarations after code */ +#define VARIABLE_DECLARATION_AFTER_CODE 1 + +/* compiler supports variable length arrays */ +#define VARIABLE_LENGTH_ARRAYS 1 + +/* Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp */ +/* #undef WITH_ABBREV_NO_TTY */ + +/* Define if you want to enable AIX4's authenticate function */ +/* #undef WITH_AIXAUTHENTICATE */ + +/* Define if you have/want arrays (cluster-wide session management, not C + arrays) */ +/* #undef WITH_IRIX_ARRAY */ + +/* Define if you want IRIX audit trails */ +/* #undef WITH_IRIX_AUDIT */ + +/* Define if you want IRIX kernel jobs */ +/* #undef WITH_IRIX_JOBS */ + +/* Define if you want IRIX project management */ +/* #undef WITH_IRIX_PROJECT */ + +/* use libcrypto for cryptography */ +#define WITH_OPENSSL 1 + +/* Define if you want SELinux support. */ +/* #undef WITH_SELINUX */ + +/* Enable zlib */ +#define WITH_ZLIB 1 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define if xauth is found in your path */ +#define XAUTH_PATH "/usr/bin/xauth" + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#ifdef _ILP32 +# define _FILE_OFFSET_BITS 64 +#endif + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* log for bad login attempts */ +#define _PATH_BTMP "/data/ssh" + +/* Full path of your "passwd" program */ +#define _PATH_PASSWD_PROG "/usr/bin/passwd" + +/* Specify location of ssh.pid */ +#define _PATH_SSH_PIDDIR "/data/ssh" + +/* Define if we don't have struct __res_state in resolv.h */ +#define __res_state state + +/* Define to rpl_calloc if the replacement function should be used. */ +#define calloc rpl_calloc + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +#define malloc rpl_malloc + +/* Define to rpl_realloc if the replacement function should be used. */ +#define realloc rpl_realloc + +/* type to use in place of socklen_t if not defined */ +/* #undef socklen_t */ + +#ifndef SSHDIR +#define SSHDIR "/data/ssh" +#endif + +#define _PATH_PRIVSEP_CHROOT_DIR SSHDIR "/empty" + +#define _PATH_SSH_PROGRAM "/system/bin/ssh" + +/* Android doesn't actually have these, but we don't need the substitutes either (and they fail to build) */ +#define HAVE_RSA_METH_DUP 1 +#define HAVE_RSA_METH_FREE 1 +#define HAVE_RSA_METH_GET_FINISH 1 +#define HAVE_RSA_METH_SET1_NAME 1 +#define HAVE_RSA_METH_SET_FINISH 1 +#define HAVE_RSA_METH_SET_PRIV_DEC 1 +#define HAVE_RSA_METH_SET_PRIV_ENC 1 diff --git a/aosp/external/openssh/progressmeter.c b/aosp/external/openssh/progressmeter.c new file mode 100644 index 0000000000000000000000000000000000000000..8baf798f1813c79c20af6cf99a458d46c87098c6 --- /dev/null +++ b/aosp/external/openssh/progressmeter.c @@ -0,0 +1,296 @@ +/* $OpenBSD: progressmeter.c,v 1.50 2020/01/23 07:10:22 dtucker Exp $ */ +/* + * Copyright (c) 2003 Nils Nordman. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "progressmeter.h" +#include "atomicio.h" +#include "misc.h" +#include "utf8.h" + +#define DEFAULT_WINSIZE 80 +#define MAX_WINSIZE 512 +#define PADDING 1 /* padding between the progress indicators */ +#define UPDATE_INTERVAL 1 /* update the progress meter every second */ +#define STALL_TIME 5 /* we're stalled after this many seconds */ + +/* determines whether we can output to the terminal */ +static int can_output(void); + +/* formats and inserts the specified size into the given buffer */ +static void format_size(char *, int, off_t); +static void format_rate(char *, int, off_t); + +/* window resizing */ +static void sig_winch(int); +static void setscreensize(void); + +/* signal handler for updating the progress meter */ +static void sig_alarm(int); + +static double start; /* start progress */ +static double last_update; /* last progress update */ +static const char *file; /* name of the file being transferred */ +static off_t start_pos; /* initial position of transfer */ +static off_t end_pos; /* ending position of transfer */ +static off_t cur_pos; /* transfer position as of last refresh */ +static volatile off_t *counter; /* progress counter */ +static long stalled; /* how long we have been stalled */ +static int bytes_per_second; /* current speed in bytes per second */ +static int win_size; /* terminal window size */ +static volatile sig_atomic_t win_resized; /* for window resizing */ +static volatile sig_atomic_t alarm_fired; + +/* units for format_size */ +static const char unit[] = " KMGT"; + +static int +can_output(void) +{ + return (getpgrp() == tcgetpgrp(STDOUT_FILENO)); +} + +static void +format_rate(char *buf, int size, off_t bytes) +{ + int i; + + bytes *= 100; + for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + if (i == 0) { + i++; + bytes = (bytes + 512) / 1024; + } + snprintf(buf, size, "%3lld.%1lld%c%s", + (long long) (bytes + 5) / 100, + (long long) (bytes + 5) / 10 % 10, + unit[i], + i ? "B" : " "); +} + +static void +format_size(char *buf, int size, off_t bytes) +{ + int i; + + for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + snprintf(buf, size, "%4lld%c%s", + (long long) bytes, + unit[i], + i ? "B" : " "); +} + +void +refresh_progress_meter(int force_update) +{ + char buf[MAX_WINSIZE + 1]; + off_t transferred; + double elapsed, now; + int percent; + off_t bytes_left; + int cur_speed; + int hours, minutes, seconds; + int file_len; + + if ((!force_update && !alarm_fired && !win_resized) || !can_output()) + return; + alarm_fired = 0; + + if (win_resized) { + setscreensize(); + win_resized = 0; + } + + transferred = *counter - (cur_pos ? cur_pos : start_pos); + cur_pos = *counter; + now = monotime_double(); + bytes_left = end_pos - cur_pos; + + if (bytes_left > 0) + elapsed = now - last_update; + else { + elapsed = now - start; + /* Calculate true total speed when done */ + transferred = end_pos - start_pos; + bytes_per_second = 0; + } + + /* calculate speed */ + if (elapsed != 0) + cur_speed = (transferred / elapsed); + else + cur_speed = transferred; + +#define AGE_FACTOR 0.9 + if (bytes_per_second != 0) { + bytes_per_second = (bytes_per_second * AGE_FACTOR) + + (cur_speed * (1.0 - AGE_FACTOR)); + } else + bytes_per_second = cur_speed; + + /* filename */ + buf[0] = '\0'; + file_len = win_size - 36; + if (file_len > 0) { + buf[0] = '\r'; + snmprintf(buf+1, sizeof(buf)-1, &file_len, "%-*s", + file_len, file); + } + + /* percent of transfer done */ + if (end_pos == 0 || cur_pos == end_pos) + percent = 100; + else + percent = ((float)cur_pos / end_pos) * 100; + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %3d%% ", percent); + + /* amount transferred */ + format_size(buf + strlen(buf), win_size - strlen(buf), + cur_pos); + strlcat(buf, " ", win_size); + + /* bandwidth usage */ + format_rate(buf + strlen(buf), win_size - strlen(buf), + (off_t)bytes_per_second); + strlcat(buf, "/s ", win_size); + + /* ETA */ + if (!transferred) + stalled += elapsed; + else + stalled = 0; + + if (stalled >= STALL_TIME) + strlcat(buf, "- stalled -", win_size); + else if (bytes_per_second == 0 && bytes_left) + strlcat(buf, " --:-- ETA", win_size); + else { + if (bytes_left > 0) + seconds = bytes_left / bytes_per_second; + else + seconds = elapsed; + + hours = seconds / 3600; + seconds -= hours * 3600; + minutes = seconds / 60; + seconds -= minutes * 60; + + if (hours != 0) + snprintf(buf + strlen(buf), win_size - strlen(buf), + "%d:%02d:%02d", hours, minutes, seconds); + else + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %02d:%02d", minutes, seconds); + + if (bytes_left > 0) + strlcat(buf, " ETA", win_size); + else + strlcat(buf, " ", win_size); + } + + atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1); + last_update = now; +} + +/*ARGSUSED*/ +static void +sig_alarm(int ignore) +{ + alarm_fired = 1; + alarm(UPDATE_INTERVAL); +} + +void +start_progress_meter(const char *f, off_t filesize, off_t *ctr) +{ + start = last_update = monotime_double(); + file = f; + start_pos = *ctr; + end_pos = filesize; + cur_pos = 0; + counter = ctr; + stalled = 0; + bytes_per_second = 0; + + setscreensize(); + refresh_progress_meter(1); + + ssh_signal(SIGALRM, sig_alarm); + ssh_signal(SIGWINCH, sig_winch); + alarm(UPDATE_INTERVAL); +} + +void +stop_progress_meter(void) +{ + alarm(0); + + if (!can_output()) + return; + + /* Ensure we complete the progress */ + if (cur_pos != end_pos) + refresh_progress_meter(1); + + atomicio(vwrite, STDOUT_FILENO, "\n", 1); +} + +/*ARGSUSED*/ +static void +sig_winch(int sig) +{ + win_resized = 1; +} + +static void +setscreensize(void) +{ + struct winsize winsize; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 && + winsize.ws_col != 0) { + if (winsize.ws_col > MAX_WINSIZE) + win_size = MAX_WINSIZE; + else + win_size = winsize.ws_col; + } else + win_size = DEFAULT_WINSIZE; + win_size += 1; /* trailing \0 */ +} diff --git a/aosp/external/openssh/progressmeter.h b/aosp/external/openssh/progressmeter.h new file mode 100644 index 0000000000000000000000000000000000000000..1703ea75babc0a15873f056e2fa201420929cdcd --- /dev/null +++ b/aosp/external/openssh/progressmeter.h @@ -0,0 +1,28 @@ +/* $OpenBSD: progressmeter.h,v 1.5 2019/01/24 16:52:17 dtucker Exp $ */ +/* + * Copyright (c) 2002 Nils Nordman. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void start_progress_meter(const char *, off_t, off_t *); +void refresh_progress_meter(int); +void stop_progress_meter(void); diff --git a/aosp/external/openssh/readconf.c b/aosp/external/openssh/readconf.c new file mode 100644 index 0000000000000000000000000000000000000000..f26fabaa6af465f927c021eeb651815afad0d37d --- /dev/null +++ b/aosp/external/openssh/readconf.c @@ -0,0 +1,3476 @@ +/* $OpenBSD: readconf.c,v 1.366 2022/02/08 08:59:12 dtucker Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for reading the configuration files. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include +#include +#ifdef USE_SYSTEM_GLOB +# include +#else +# include "openbsd-compat/glob.h" +#endif +#ifdef HAVE_UTIL_H +#include +#endif +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +# include +#endif + +#include "xmalloc.h" +#include "ssh.h" +#include "ssherr.h" +#include "compat.h" +#include "cipher.h" +#include "pathnames.h" +#include "log.h" +#include "sshkey.h" +#include "misc.h" +#include "readconf.h" +#include "match.h" +#include "kex.h" +#include "mac.h" +#include "uidswap.h" +#include "myproposal.h" +#include "digest.h" + +/* Format of the configuration file: + + # Configuration data is parsed as follows: + # 1. command line options + # 2. user-specific file + # 3. system-wide file + # Any configuration value is only changed the first time it is set. + # Thus, host-specific definitions should be at the beginning of the + # configuration file, and defaults at the end. + + # Host-specific declarations. These may override anything above. A single + # host may match multiple declarations; these are processed in the order + # that they are given in. + + Host *.ngs.fi ngs.fi + User foo + + Host fake.com + Hostname another.host.name.real.org + User blaah + Port 34289 + ForwardX11 no + ForwardAgent no + + Host books.com + RemoteForward 9999 shadows.cs.hut.fi:9999 + Ciphers 3des-cbc + + Host fascist.blob.com + Port 23123 + User tylonen + PasswordAuthentication no + + Host puukko.hut.fi + User t35124p + ProxyCommand ssh-proxy %h %p + + Host *.fr + PublicKeyAuthentication no + + Host *.su + Ciphers aes128-ctr + PasswordAuthentication no + + Host vpn.fake.com + Tunnel yes + TunnelDevice 3 + + # Defaults for various options + Host * + ForwardAgent no + ForwardX11 no + PasswordAuthentication yes + StrictHostKeyChecking yes + TcpKeepAlive no + IdentityFile ~/.ssh/identity + Port 22 + EscapeChar ~ + +*/ + +static int read_config_file_depth(const char *filename, struct passwd *pw, + const char *host, const char *original_host, Options *options, + int flags, int *activep, int *want_final_pass, int depth); +static int process_config_line_depth(Options *options, struct passwd *pw, + const char *host, const char *original_host, char *line, + const char *filename, int linenum, int *activep, int flags, + int *want_final_pass, int depth); + +/* Keyword tokens. */ + +typedef enum { + oBadOption, + oHost, oMatch, oInclude, + oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, + oGatewayPorts, oExitOnForwardFailure, + oPasswordAuthentication, + oXAuthLocation, + oIdentityFile, oHostname, oPort, oRemoteForward, oLocalForward, + oPermitRemoteOpen, + oCertificateFile, oAddKeysToAgent, oIdentityAgent, + oUser, oEscapeChar, oProxyCommand, + oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, + oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, + oTCPKeepAlive, oNumberOfPasswordPrompts, + oLogFacility, oLogLevel, oLogVerbose, oCiphers, oMacs, + oPubkeyAuthentication, + oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, + oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, + oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider, + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, + oTunnel, oTunnelDevice, + oLocalCommand, oPermitLocalCommand, oRemoteCommand, + oVisualHostKey, + oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull, + oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass, + oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, + oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, + oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, + oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms, + oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump, + oSecurityKeyProvider, oKnownHostsCommand, + oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported +} OpCodes; + +/* Textual representations of the tokens. */ + +static struct { + const char *name; + OpCodes opcode; +} keywords[] = { + /* Deprecated options */ + { "protocol", oIgnore }, /* NB. silently ignored */ + { "cipher", oDeprecated }, + { "fallbacktorsh", oDeprecated }, + { "globalknownhostsfile2", oDeprecated }, + { "rhostsauthentication", oDeprecated }, + { "userknownhostsfile2", oDeprecated }, + { "useroaming", oDeprecated }, + { "usersh", oDeprecated }, + { "useprivilegedport", oDeprecated }, + + /* Unsupported options */ + { "afstokenpassing", oUnsupported }, + { "kerberosauthentication", oUnsupported }, + { "kerberostgtpassing", oUnsupported }, + { "rsaauthentication", oUnsupported }, + { "rhostsrsaauthentication", oUnsupported }, + { "compressionlevel", oUnsupported }, + + /* Sometimes-unsupported options */ +#if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, + { "gssapidelegatecredentials", oGssDelegateCreds }, +# else + { "gssapiauthentication", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, +#endif +#ifdef ENABLE_PKCS11 + { "pkcs11provider", oPKCS11Provider }, + { "smartcarddevice", oPKCS11Provider }, +# else + { "smartcarddevice", oUnsupported }, + { "pkcs11provider", oUnsupported }, +#endif + + { "forwardagent", oForwardAgent }, + { "forwardx11", oForwardX11 }, + { "forwardx11trusted", oForwardX11Trusted }, + { "forwardx11timeout", oForwardX11Timeout }, + { "exitonforwardfailure", oExitOnForwardFailure }, + { "xauthlocation", oXAuthLocation }, + { "gatewayports", oGatewayPorts }, + { "passwordauthentication", oPasswordAuthentication }, + { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, + { "kbdinteractivedevices", oKbdInteractiveDevices }, + { "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */ + { "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */ + { "tisauthentication", oKbdInteractiveAuthentication }, /* alias */ + { "pubkeyauthentication", oPubkeyAuthentication }, + { "dsaauthentication", oPubkeyAuthentication }, /* alias */ + { "hostbasedauthentication", oHostbasedAuthentication }, + { "identityfile", oIdentityFile }, + { "identityfile2", oIdentityFile }, /* obsolete */ + { "identitiesonly", oIdentitiesOnly }, + { "certificatefile", oCertificateFile }, + { "addkeystoagent", oAddKeysToAgent }, + { "identityagent", oIdentityAgent }, + { "hostname", oHostname }, + { "hostkeyalias", oHostKeyAlias }, + { "proxycommand", oProxyCommand }, + { "port", oPort }, + { "ciphers", oCiphers }, + { "macs", oMacs }, + { "remoteforward", oRemoteForward }, + { "localforward", oLocalForward }, + { "permitremoteopen", oPermitRemoteOpen }, + { "user", oUser }, + { "host", oHost }, + { "match", oMatch }, + { "escapechar", oEscapeChar }, + { "globalknownhostsfile", oGlobalKnownHostsFile }, + { "userknownhostsfile", oUserKnownHostsFile }, + { "connectionattempts", oConnectionAttempts }, + { "batchmode", oBatchMode }, + { "checkhostip", oCheckHostIP }, + { "stricthostkeychecking", oStrictHostKeyChecking }, + { "compression", oCompression }, + { "tcpkeepalive", oTCPKeepAlive }, + { "keepalive", oTCPKeepAlive }, /* obsolete */ + { "numberofpasswordprompts", oNumberOfPasswordPrompts }, + { "syslogfacility", oLogFacility }, + { "loglevel", oLogLevel }, + { "logverbose", oLogVerbose }, + { "dynamicforward", oDynamicForward }, + { "preferredauthentications", oPreferredAuthentications }, + { "hostkeyalgorithms", oHostKeyAlgorithms }, + { "casignaturealgorithms", oCASignatureAlgorithms }, + { "bindaddress", oBindAddress }, + { "bindinterface", oBindInterface }, + { "clearallforwardings", oClearAllForwardings }, + { "enablesshkeysign", oEnableSSHKeysign }, + { "verifyhostkeydns", oVerifyHostKeyDNS }, + { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, + { "rekeylimit", oRekeyLimit }, + { "connecttimeout", oConnectTimeout }, + { "addressfamily", oAddressFamily }, + { "serveraliveinterval", oServerAliveInterval }, + { "serveralivecountmax", oServerAliveCountMax }, + { "sendenv", oSendEnv }, + { "setenv", oSetEnv }, + { "controlpath", oControlPath }, + { "controlmaster", oControlMaster }, + { "controlpersist", oControlPersist }, + { "hashknownhosts", oHashKnownHosts }, + { "include", oInclude }, + { "tunnel", oTunnel }, + { "tunneldevice", oTunnelDevice }, + { "localcommand", oLocalCommand }, + { "permitlocalcommand", oPermitLocalCommand }, + { "remotecommand", oRemoteCommand }, + { "visualhostkey", oVisualHostKey }, + { "kexalgorithms", oKexAlgorithms }, + { "ipqos", oIPQoS }, + { "requesttty", oRequestTTY }, + { "sessiontype", oSessionType }, + { "stdinnull", oStdinNull }, + { "forkafterauthentication", oForkAfterAuthentication }, + { "proxyusefdpass", oProxyUseFdpass }, + { "canonicaldomains", oCanonicalDomains }, + { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, + { "canonicalizehostname", oCanonicalizeHostname }, + { "canonicalizemaxdots", oCanonicalizeMaxDots }, + { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, + { "streamlocalbindmask", oStreamLocalBindMask }, + { "streamlocalbindunlink", oStreamLocalBindUnlink }, + { "revokedhostkeys", oRevokedHostKeys }, + { "fingerprinthash", oFingerprintHash }, + { "updatehostkeys", oUpdateHostkeys }, + { "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms }, + { "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */ + { "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms }, + { "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */ + { "ignoreunknown", oIgnoreUnknown }, + { "proxyjump", oProxyJump }, + { "securitykeyprovider", oSecurityKeyProvider }, + { "knownhostscommand", oKnownHostsCommand }, + + { NULL, oBadOption } +}; + +static const char *lookup_opcode_name(OpCodes code); + +const char * +kex_default_pk_alg(void) +{ + static char *pkalgs; + + if (pkalgs == NULL) { + char *all_key; + + all_key = sshkey_alg_list(0, 0, 1, ','); + pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); + free(all_key); + } + return pkalgs; +} + +char * +ssh_connection_hash(const char *thishost, const char *host, const char *portstr, + const char *user) +{ + struct ssh_digest_ctx *md; + u_char conn_hash[SSH_DIGEST_MAX_LENGTH]; + + if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL || + ssh_digest_update(md, thishost, strlen(thishost)) < 0 || + ssh_digest_update(md, host, strlen(host)) < 0 || + ssh_digest_update(md, portstr, strlen(portstr)) < 0 || + ssh_digest_update(md, user, strlen(user)) < 0 || + ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0) + fatal_f("mux digest failed"); + ssh_digest_free(md); + return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1)); +} + +/* + * Adds a local TCP/IP port forward to options. Never returns if there is an + * error. + */ + +void +add_local_forward(Options *options, const struct Forward *newfwd) +{ + struct Forward *fwd; + int i; + + /* Don't add duplicates */ + for (i = 0; i < options->num_local_forwards; i++) { + if (forward_equals(newfwd, options->local_forwards + i)) + return; + } + options->local_forwards = xreallocarray(options->local_forwards, + options->num_local_forwards + 1, + sizeof(*options->local_forwards)); + fwd = &options->local_forwards[options->num_local_forwards++]; + + fwd->listen_host = newfwd->listen_host; + fwd->listen_port = newfwd->listen_port; + fwd->listen_path = newfwd->listen_path; + fwd->connect_host = newfwd->connect_host; + fwd->connect_port = newfwd->connect_port; + fwd->connect_path = newfwd->connect_path; +} + +/* + * Adds a remote TCP/IP port forward to options. Never returns if there is + * an error. + */ + +void +add_remote_forward(Options *options, const struct Forward *newfwd) +{ + struct Forward *fwd; + int i; + + /* Don't add duplicates */ + for (i = 0; i < options->num_remote_forwards; i++) { + if (forward_equals(newfwd, options->remote_forwards + i)) + return; + } + options->remote_forwards = xreallocarray(options->remote_forwards, + options->num_remote_forwards + 1, + sizeof(*options->remote_forwards)); + fwd = &options->remote_forwards[options->num_remote_forwards++]; + + fwd->listen_host = newfwd->listen_host; + fwd->listen_port = newfwd->listen_port; + fwd->listen_path = newfwd->listen_path; + fwd->connect_host = newfwd->connect_host; + fwd->connect_port = newfwd->connect_port; + fwd->connect_path = newfwd->connect_path; + fwd->handle = newfwd->handle; + fwd->allocated_port = 0; +} + +static void +clear_forwardings(Options *options) +{ + int i; + + for (i = 0; i < options->num_local_forwards; i++) { + free(options->local_forwards[i].listen_host); + free(options->local_forwards[i].listen_path); + free(options->local_forwards[i].connect_host); + free(options->local_forwards[i].connect_path); + } + if (options->num_local_forwards > 0) { + free(options->local_forwards); + options->local_forwards = NULL; + } + options->num_local_forwards = 0; + for (i = 0; i < options->num_remote_forwards; i++) { + free(options->remote_forwards[i].listen_host); + free(options->remote_forwards[i].listen_path); + free(options->remote_forwards[i].connect_host); + free(options->remote_forwards[i].connect_path); + } + if (options->num_remote_forwards > 0) { + free(options->remote_forwards); + options->remote_forwards = NULL; + } + options->num_remote_forwards = 0; + options->tun_open = SSH_TUNMODE_NO; +} + +void +add_certificate_file(Options *options, const char *path, int userprovided) +{ + int i; + + if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES) + fatal("Too many certificate files specified (max %d)", + SSH_MAX_CERTIFICATE_FILES); + + /* Avoid registering duplicates */ + for (i = 0; i < options->num_certificate_files; i++) { + if (options->certificate_file_userprovided[i] == userprovided && + strcmp(options->certificate_files[i], path) == 0) { + debug2_f("ignoring duplicate key %s", path); + return; + } + } + + options->certificate_file_userprovided[options->num_certificate_files] = + userprovided; + options->certificate_files[options->num_certificate_files++] = + xstrdup(path); +} + +void +add_identity_file(Options *options, const char *dir, const char *filename, + int userprovided) +{ + char *path; + int i; + + if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) + fatal("Too many identity files specified (max %d)", + SSH_MAX_IDENTITY_FILES); + + if (dir == NULL) /* no dir, filename is absolute */ + path = xstrdup(filename); + else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX) + fatal("Identity file path %s too long", path); + + /* Avoid registering duplicates */ + for (i = 0; i < options->num_identity_files; i++) { + if (options->identity_file_userprovided[i] == userprovided && + strcmp(options->identity_files[i], path) == 0) { + debug2_f("ignoring duplicate key %s", path); + free(path); + return; + } + } + + options->identity_file_userprovided[options->num_identity_files] = + userprovided; + options->identity_files[options->num_identity_files++] = path; +} + +int +default_ssh_port(void) +{ + static int port; + struct servent *sp; + + if (port == 0) { + sp = getservbyname(SSH_SERVICE_NAME, "tcp"); + port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; + } + return port; +} + +/* + * Execute a command in a shell. + * Return its exit status or -1 on abnormal exit. + */ +static int +execute_in_shell(const char *cmd) +{ + char *shell; + pid_t pid; + int status; + + if ((shell = getenv("SHELL")) == NULL) + shell = _PATH_BSHELL; + + if (access(shell, X_OK) == -1) { + fatal("Shell \"%s\" is not executable: %s", + shell, strerror(errno)); + } + + debug("Executing command: '%.500s'", cmd); + + /* Fork and execute the command. */ + if ((pid = fork()) == 0) { + char *argv[4]; + + if (stdfd_devnull(1, 1, 0) == -1) + fatal_f("stdfd_devnull failed"); + closefrom(STDERR_FILENO + 1); + + argv[0] = shell; + argv[1] = "-c"; + argv[2] = xstrdup(cmd); + argv[3] = NULL; + + execv(argv[0], argv); + error("Unable to execute '%.100s': %s", cmd, strerror(errno)); + /* Die with signal to make this error apparent to parent. */ + ssh_signal(SIGTERM, SIG_DFL); + kill(getpid(), SIGTERM); + _exit(1); + } + /* Parent. */ + if (pid == -1) + fatal_f("fork: %.100s", strerror(errno)); + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR && errno != EAGAIN) + fatal_f("waitpid: %s", strerror(errno)); + } + if (!WIFEXITED(status)) { + error("command '%.100s' exited abnormally", cmd); + return -1; + } + debug3("command returned status %d", WEXITSTATUS(status)); + return WEXITSTATUS(status); +} + +/* + * Parse and execute a Match directive. + */ +static int +match_cfg_line(Options *options, char **condition, struct passwd *pw, + const char *host_arg, const char *original_host, int final_pass, + int *want_final_pass, const char *filename, int linenum) +{ + char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; + const char *ruser; + int r, port, this_result, result = 1, attributes = 0, negate; + char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; + char uidstr[32]; + + /* + * Configuration is likely to be incomplete at this point so we + * must be prepared to use default values. + */ + port = options->port <= 0 ? default_ssh_port() : options->port; + ruser = options->user == NULL ? pw->pw_name : options->user; + if (final_pass) { + host = xstrdup(options->hostname); + } else if (options->hostname != NULL) { + /* NB. Please keep in sync with ssh.c:main() */ + host = percent_expand(options->hostname, + "h", host_arg, (char *)NULL); + } else { + host = xstrdup(host_arg); + } + + debug2("checking match for '%s' host %s originally %s", + cp, host, original_host); + while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') { + /* Terminate on comment */ + if (*attrib == '#') { + cp = NULL; /* mark all arguments consumed */ + break; + } + arg = criteria = NULL; + this_result = 1; + if ((negate = attrib[0] == '!')) + attrib++; + /* Criterion "all" has no argument and must appear alone */ + if (strcasecmp(attrib, "all") == 0) { + if (attributes > 1 || ((arg = strdelim(&cp)) != NULL && + *arg != '\0' && *arg != '#')) { + error("%.200s line %d: '%s' cannot be combined " + "with other Match attributes", + filename, linenum, oattrib); + result = -1; + goto out; + } + if (arg != NULL && *arg == '#') + cp = NULL; /* mark all arguments consumed */ + if (result) + result = negate ? 0 : 1; + goto out; + } + attributes++; + /* criteria "final" and "canonical" have no argument */ + if (strcasecmp(attrib, "canonical") == 0 || + strcasecmp(attrib, "final") == 0) { + /* + * If the config requests "Match final" then remember + * this so we can perform a second pass later. + */ + if (strcasecmp(attrib, "final") == 0 && + want_final_pass != NULL) + *want_final_pass = 1; + r = !!final_pass; /* force bitmask member to boolean */ + if (r == (negate ? 1 : 0)) + this_result = result = 0; + debug3("%.200s line %d: %smatched '%s'", + filename, linenum, + this_result ? "" : "not ", oattrib); + continue; + } + /* All other criteria require an argument */ + if ((arg = strdelim(&cp)) == NULL || + *arg == '\0' || *arg == '#') { + error("Missing Match criteria for %s", attrib); + result = -1; + goto out; + } + if (strcasecmp(attrib, "host") == 0) { + criteria = xstrdup(host); + r = match_hostname(host, arg) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; + } else if (strcasecmp(attrib, "originalhost") == 0) { + criteria = xstrdup(original_host); + r = match_hostname(original_host, arg) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; + } else if (strcasecmp(attrib, "user") == 0) { + criteria = xstrdup(ruser); + r = match_pattern_list(ruser, arg, 0) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; + } else if (strcasecmp(attrib, "localuser") == 0) { + criteria = xstrdup(pw->pw_name); + r = match_pattern_list(pw->pw_name, arg, 0) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; + } else if (strcasecmp(attrib, "exec") == 0) { + char *conn_hash_hex, *keyalias; + + if (gethostname(thishost, sizeof(thishost)) == -1) + fatal("gethostname: %s", strerror(errno)); + strlcpy(shorthost, thishost, sizeof(shorthost)); + shorthost[strcspn(thishost, ".")] = '\0'; + snprintf(portstr, sizeof(portstr), "%d", port); + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)pw->pw_uid); + conn_hash_hex = ssh_connection_hash(thishost, host, + portstr, ruser); + keyalias = options->host_key_alias ? + options->host_key_alias : host; + + cmd = percent_expand(arg, + "C", conn_hash_hex, + "L", shorthost, + "d", pw->pw_dir, + "h", host, + "k", keyalias, + "l", thishost, + "n", original_host, + "p", portstr, + "r", ruser, + "u", pw->pw_name, + "i", uidstr, + (char *)NULL); + free(conn_hash_hex); + if (result != 1) { + /* skip execution if prior predicate failed */ + debug3("%.200s line %d: skipped exec " + "\"%.100s\"", filename, linenum, cmd); + free(cmd); + continue; + } + r = execute_in_shell(cmd); + if (r == -1) { + fatal("%.200s line %d: match exec " + "'%.100s' error", filename, + linenum, cmd); + } + criteria = xstrdup(cmd); + free(cmd); + /* Force exit status to boolean */ + r = r == 0; + if (r == (negate ? 1 : 0)) + this_result = result = 0; + } else { + error("Unsupported Match attribute %s", attrib); + result = -1; + goto out; + } + debug3("%.200s line %d: %smatched '%s \"%.100s\"' ", + filename, linenum, this_result ? "": "not ", + oattrib, criteria); + free(criteria); + } + if (attributes == 0) { + error("One or more attributes required for Match"); + result = -1; + goto out; + } + out: + if (result != -1) + debug2("match %sfound", result ? "" : "not "); + *condition = cp; + free(host); + return result; +} + +/* Remove environment variable by pattern */ +static void +rm_env(Options *options, const char *arg, const char *filename, int linenum) +{ + int i, j, onum_send_env = options->num_send_env; + char *cp; + + /* Remove an environment variable */ + for (i = 0; i < options->num_send_env; ) { + cp = xstrdup(options->send_env[i]); + if (!match_pattern(cp, arg + 1)) { + free(cp); + i++; + continue; + } + debug3("%s line %d: removing environment %s", + filename, linenum, cp); + free(cp); + free(options->send_env[i]); + options->send_env[i] = NULL; + for (j = i; j < options->num_send_env - 1; j++) { + options->send_env[j] = options->send_env[j + 1]; + options->send_env[j + 1] = NULL; + } + options->num_send_env--; + /* NB. don't increment i */ + } + if (onum_send_env != options->num_send_env) { + options->send_env = xrecallocarray(options->send_env, + onum_send_env, options->num_send_env, + sizeof(*options->send_env)); + } +} + +/* + * Returns the number of the token pointed to by cp or oBadOption. + */ +static OpCodes +parse_token(const char *cp, const char *filename, int linenum, + const char *ignored_unknown) +{ + int i; + + for (i = 0; keywords[i].name; i++) + if (strcmp(cp, keywords[i].name) == 0) + return keywords[i].opcode; + if (ignored_unknown != NULL && + match_pattern_list(cp, ignored_unknown, 1) == 1) + return oIgnoredUnknownOption; + error("%s: line %d: Bad configuration option: %s", + filename, linenum, cp); + return oBadOption; +} + +/* Multistate option parsing */ +struct multistate { + char *key; + int value; +}; +static const struct multistate multistate_flag[] = { + { "true", 1 }, + { "false", 0 }, + { "yes", 1 }, + { "no", 0 }, + { NULL, -1 } +}; +static const struct multistate multistate_yesnoask[] = { + { "true", 1 }, + { "false", 0 }, + { "yes", 1 }, + { "no", 0 }, + { "ask", 2 }, + { NULL, -1 } +}; +static const struct multistate multistate_strict_hostkey[] = { + { "true", SSH_STRICT_HOSTKEY_YES }, + { "false", SSH_STRICT_HOSTKEY_OFF }, + { "yes", SSH_STRICT_HOSTKEY_YES }, + { "no", SSH_STRICT_HOSTKEY_OFF }, + { "ask", SSH_STRICT_HOSTKEY_ASK }, + { "off", SSH_STRICT_HOSTKEY_OFF }, + { "accept-new", SSH_STRICT_HOSTKEY_NEW }, + { NULL, -1 } +}; +static const struct multistate multistate_yesnoaskconfirm[] = { + { "true", 1 }, + { "false", 0 }, + { "yes", 1 }, + { "no", 0 }, + { "ask", 2 }, + { "confirm", 3 }, + { NULL, -1 } +}; +static const struct multistate multistate_addressfamily[] = { + { "inet", AF_INET }, + { "inet6", AF_INET6 }, + { "any", AF_UNSPEC }, + { NULL, -1 } +}; +static const struct multistate multistate_controlmaster[] = { + { "true", SSHCTL_MASTER_YES }, + { "yes", SSHCTL_MASTER_YES }, + { "false", SSHCTL_MASTER_NO }, + { "no", SSHCTL_MASTER_NO }, + { "auto", SSHCTL_MASTER_AUTO }, + { "ask", SSHCTL_MASTER_ASK }, + { "autoask", SSHCTL_MASTER_AUTO_ASK }, + { NULL, -1 } +}; +static const struct multistate multistate_tunnel[] = { + { "ethernet", SSH_TUNMODE_ETHERNET }, + { "point-to-point", SSH_TUNMODE_POINTOPOINT }, + { "true", SSH_TUNMODE_DEFAULT }, + { "yes", SSH_TUNMODE_DEFAULT }, + { "false", SSH_TUNMODE_NO }, + { "no", SSH_TUNMODE_NO }, + { NULL, -1 } +}; +static const struct multistate multistate_requesttty[] = { + { "true", REQUEST_TTY_YES }, + { "yes", REQUEST_TTY_YES }, + { "false", REQUEST_TTY_NO }, + { "no", REQUEST_TTY_NO }, + { "force", REQUEST_TTY_FORCE }, + { "auto", REQUEST_TTY_AUTO }, + { NULL, -1 } +}; +static const struct multistate multistate_sessiontype[] = { + { "none", SESSION_TYPE_NONE }, + { "subsystem", SESSION_TYPE_SUBSYSTEM }, + { "default", SESSION_TYPE_DEFAULT }, + { NULL, -1 } +}; +static const struct multistate multistate_canonicalizehostname[] = { + { "true", SSH_CANONICALISE_YES }, + { "false", SSH_CANONICALISE_NO }, + { "yes", SSH_CANONICALISE_YES }, + { "no", SSH_CANONICALISE_NO }, + { "always", SSH_CANONICALISE_ALWAYS }, + { NULL, -1 } +}; +static const struct multistate multistate_pubkey_auth[] = { + { "true", SSH_PUBKEY_AUTH_ALL }, + { "false", SSH_PUBKEY_AUTH_NO }, + { "yes", SSH_PUBKEY_AUTH_ALL }, + { "no", SSH_PUBKEY_AUTH_NO }, + { "unbound", SSH_PUBKEY_AUTH_UNBOUND }, + { "host-bound", SSH_PUBKEY_AUTH_HBOUND }, + { NULL, -1 } +}; +static const struct multistate multistate_compression[] = { +#ifdef WITH_ZLIB + { "yes", COMP_ZLIB }, +#endif + { "no", COMP_NONE }, + { NULL, -1 } +}; + +static int +parse_multistate_value(const char *arg, const char *filename, int linenum, + const struct multistate *multistate_ptr) +{ + int i; + + if (!arg || *arg == '\0') { + error("%s line %d: missing argument.", filename, linenum); + return -1; + } + for (i = 0; multistate_ptr[i].key != NULL; i++) { + if (strcasecmp(arg, multistate_ptr[i].key) == 0) + return multistate_ptr[i].value; + } + return -1; +} + +/* + * Processes a single option line as used in the configuration files. This + * only sets those values that have not already been set. + */ +int +process_config_line(Options *options, struct passwd *pw, const char *host, + const char *original_host, char *line, const char *filename, + int linenum, int *activep, int flags) +{ + return process_config_line_depth(options, pw, host, original_host, + line, filename, linenum, activep, flags, NULL, 0); +} + +#define WHITESPACE " \t\r\n" +static int +process_config_line_depth(Options *options, struct passwd *pw, const char *host, + const char *original_host, char *line, const char *filename, + int linenum, int *activep, int flags, int *want_final_pass, int depth) +{ + char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p; + char **cpptr, ***cppptr, fwdarg[256]; + u_int i, *uintptr, uvalue, max_entries = 0; + int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0; + int remotefwd, dynamicfwd; + LogLevel *log_level_ptr; + SyslogFacility *log_facility_ptr; + long long val64; + size_t len; + struct Forward fwd; + const struct multistate *multistate_ptr; + struct allowed_cname *cname; + glob_t gl; + const char *errstr; + char **oav = NULL, **av; + int oac = 0, ac; + int ret = -1; + + if (activep == NULL) { /* We are processing a command line directive */ + cmdline = 1; + activep = &cmdline; + } + + /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ + if ((len = strlen(line)) == 0) + return 0; + for (len--; len > 0; len--) { + if (strchr(WHITESPACE "\f", line[len]) == NULL) + break; + line[len] = '\0'; + } + + str = line; + /* Get the keyword. (Each line is supposed to begin with a keyword). */ + if ((keyword = strdelim(&str)) == NULL) + return 0; + /* Ignore leading whitespace. */ + if (*keyword == '\0') + keyword = strdelim(&str); + if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') + return 0; + /* Match lowercase keyword */ + lowercase(keyword); + + /* Prepare to parse remainder of line */ + if (str != NULL) + str += strspn(str, WHITESPACE); + if (str == NULL || *str == '\0') { + error("%s line %d: no argument after keyword \"%s\"", + filename, linenum, keyword); + return -1; + } + opcode = parse_token(keyword, filename, linenum, + options->ignored_unknown); + if (argv_split(str, &oac, &oav, 1) != 0) { + error("%s line %d: invalid quotes", filename, linenum); + return -1; + } + ac = oac; + av = oav; + + switch (opcode) { + case oBadOption: + /* don't panic, but count bad options */ + goto out; + case oIgnore: + argv_consume(&ac); + break; + case oIgnoredUnknownOption: + debug("%s line %d: Ignored unknown option \"%s\"", + filename, linenum, keyword); + argv_consume(&ac); + break; + case oConnectTimeout: + intptr = &options->connection_timeout; +parse_time: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%s line %d: missing time value.", + filename, linenum); + goto out; + } + if (strcmp(arg, "none") == 0) + value = -1; + else if ((value = convtime(arg)) == -1) { + error("%s line %d: invalid time value.", + filename, linenum); + goto out; + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oForwardAgent: + intptr = &options->forward_agent; + + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%s line %d: missing argument.", + filename, linenum); + goto out; + } + + value = -1; + multistate_ptr = multistate_flag; + for (i = 0; multistate_ptr[i].key != NULL; i++) { + if (strcasecmp(arg, multistate_ptr[i].key) == 0) { + value = multistate_ptr[i].value; + break; + } + } + if (value != -1) { + if (*activep && *intptr == -1) + *intptr = value; + break; + } + /* ForwardAgent wasn't 'yes' or 'no', assume a path */ + if (*activep && *intptr == -1) + *intptr = 1; + + charptr = &options->forward_agent_sock_path; + goto parse_agent_path; + + case oForwardX11: + intptr = &options->forward_x11; + parse_flag: + multistate_ptr = multistate_flag; + parse_multistate: + arg = argv_next(&ac, &av); + if ((value = parse_multistate_value(arg, filename, linenum, + multistate_ptr)) == -1) { + error("%s line %d: unsupported option \"%s\".", + filename, linenum, arg); + goto out; + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oForwardX11Trusted: + intptr = &options->forward_x11_trusted; + goto parse_flag; + + case oForwardX11Timeout: + intptr = &options->forward_x11_timeout; + goto parse_time; + + case oGatewayPorts: + intptr = &options->fwd_opts.gateway_ports; + goto parse_flag; + + case oExitOnForwardFailure: + intptr = &options->exit_on_forward_failure; + goto parse_flag; + + case oPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; + + case oKbdInteractiveAuthentication: + intptr = &options->kbd_interactive_authentication; + goto parse_flag; + + case oKbdInteractiveDevices: + charptr = &options->kbd_interactive_devices; + goto parse_string; + + case oPubkeyAuthentication: + multistate_ptr = multistate_pubkey_auth; + intptr = &options->pubkey_authentication; + goto parse_multistate; + + case oHostbasedAuthentication: + intptr = &options->hostbased_authentication; + goto parse_flag; + + case oGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; + + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; + + case oCheckHostIP: + intptr = &options->check_host_ip; + goto parse_flag; + + case oVerifyHostKeyDNS: + intptr = &options->verify_host_key_dns; + multistate_ptr = multistate_yesnoask; + goto parse_multistate; + + case oStrictHostKeyChecking: + intptr = &options->strict_host_key_checking; + multistate_ptr = multistate_strict_hostkey; + goto parse_multistate; + + case oCompression: + intptr = &options->compression; + multistate_ptr = multistate_compression; + goto parse_multistate; + + case oTCPKeepAlive: + intptr = &options->tcp_keep_alive; + goto parse_flag; + + case oNoHostAuthenticationForLocalhost: + intptr = &options->no_host_authentication_for_localhost; + goto parse_flag; + + case oNumberOfPasswordPrompts: + intptr = &options->number_of_password_prompts; + goto parse_int; + + case oRekeyLimit: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, + linenum); + goto out; + } + if (strcmp(arg, "default") == 0) { + val64 = 0; + } else { + if (scan_scaled(arg, &val64) == -1) { + error("%.200s line %d: Bad number '%s': %s", + filename, linenum, arg, strerror(errno)); + goto out; + } + if (val64 != 0 && val64 < 16) { + error("%.200s line %d: RekeyLimit too small", + filename, linenum); + goto out; + } + } + if (*activep && options->rekey_limit == -1) + options->rekey_limit = val64; + if (ac != 0) { /* optional rekey interval present */ + if (strcmp(av[0], "none") == 0) { + (void)argv_next(&ac, &av); /* discard */ + break; + } + intptr = &options->rekey_interval; + goto parse_time; + } + break; + + case oIdentityFile: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*activep) { + intptr = &options->num_identity_files; + if (*intptr >= SSH_MAX_IDENTITY_FILES) { + error("%.200s line %d: Too many identity files " + "specified (max %d).", filename, linenum, + SSH_MAX_IDENTITY_FILES); + goto out; + } + add_identity_file(options, NULL, + arg, flags & SSHCONF_USERCONF); + } + break; + + case oCertificateFile: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*activep) { + intptr = &options->num_certificate_files; + if (*intptr >= SSH_MAX_CERTIFICATE_FILES) { + error("%.200s line %d: Too many certificate " + "files specified (max %d).", + filename, linenum, + SSH_MAX_CERTIFICATE_FILES); + goto out; + } + add_certificate_file(options, arg, + flags & SSHCONF_USERCONF); + } + break; + + case oXAuthLocation: + charptr=&options->xauth_location; + goto parse_string; + + case oUser: + charptr = &options->user; +parse_string: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case oGlobalKnownHostsFile: + cpptr = (char **)&options->system_hostfiles; + uintptr = &options->num_system_hostfiles; + max_entries = SSH_MAX_HOSTS_FILES; +parse_char_array: + i = 0; + value = *uintptr == 0; /* was array empty when we started? */ + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + /* Allow "none" only in first position */ + if (strcasecmp(arg, "none") == 0) { + if (i > 0 || ac > 0) { + error("%s line %d: keyword %s \"none\" " + "argument must appear alone.", + filename, linenum, keyword); + goto out; + } + } + i++; + if (*activep && value) { + if ((*uintptr) >= max_entries) { + error("%s line %d: too many %s " + "entries.", filename, linenum, + keyword); + goto out; + } + cpptr[(*uintptr)++] = xstrdup(arg); + } + } + break; + + case oUserKnownHostsFile: + cpptr = (char **)&options->user_hostfiles; + uintptr = &options->num_user_hostfiles; + max_entries = SSH_MAX_HOSTS_FILES; + goto parse_char_array; + + case oHostname: + charptr = &options->hostname; + goto parse_string; + + case oHostKeyAlias: + charptr = &options->host_key_alias; + goto parse_string; + + case oPreferredAuthentications: + charptr = &options->preferred_authentications; + goto parse_string; + + case oBindAddress: + charptr = &options->bind_address; + goto parse_string; + + case oBindInterface: + charptr = &options->bind_interface; + goto parse_string; + + case oPKCS11Provider: + charptr = &options->pkcs11_provider; + goto parse_string; + + case oSecurityKeyProvider: + charptr = &options->sk_provider; + goto parse_string; + + case oKnownHostsCommand: + charptr = &options->known_hosts_command; + goto parse_command; + + case oProxyCommand: + charptr = &options->proxy_command; + /* Ignore ProxyCommand if ProxyJump already specified */ + if (options->jump_host != NULL) + charptr = &options->jump_host; /* Skip below */ +parse_command: + if (str == NULL) { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + len = strspn(str, WHITESPACE "="); + if (*activep && *charptr == NULL) + *charptr = xstrdup(str + len); + argv_consume(&ac); + break; + + case oProxyJump: + if (str == NULL) { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + len = strspn(str, WHITESPACE "="); + /* XXX use argv? */ + if (parse_jump(str + len, options, *activep) == -1) { + error("%.200s line %d: Invalid ProxyJump \"%s\"", + filename, linenum, str + len); + goto out; + } + argv_consume(&ac); + break; + + case oPort: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + value = a2port(arg); + if (value <= 0) { + error("%.200s line %d: Bad port '%s'.", + filename, linenum, arg); + goto out; + } + if (*activep && options->port == -1) + options->port = value; + break; + + case oConnectionAttempts: + intptr = &options->connection_attempts; +parse_int: + arg = argv_next(&ac, &av); + if ((errstr = atoi_err(arg, &value)) != NULL) { + error("%s line %d: integer value %s.", + filename, linenum, errstr); + goto out; + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oCiphers: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*arg != '-' && + !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){ + error("%.200s line %d: Bad SSH2 cipher spec '%s'.", + filename, linenum, arg ? arg : ""); + goto out; + } + if (*activep && options->ciphers == NULL) + options->ciphers = xstrdup(arg); + break; + + case oMacs: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*arg != '-' && + !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) { + error("%.200s line %d: Bad SSH2 MAC spec '%s'.", + filename, linenum, arg ? arg : ""); + goto out; + } + if (*activep && options->macs == NULL) + options->macs = xstrdup(arg); + break; + + case oKexAlgorithms: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*arg != '-' && + !kex_names_valid(*arg == '+' || *arg == '^' ? + arg + 1 : arg)) { + error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", + filename, linenum, arg ? arg : ""); + goto out; + } + if (*activep && options->kex_algorithms == NULL) + options->kex_algorithms = xstrdup(arg); + break; + + case oHostKeyAlgorithms: + charptr = &options->hostkeyalgorithms; +parse_pubkey_algos: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (*arg != '-' && + !sshkey_names_valid2(*arg == '+' || *arg == '^' ? + arg + 1 : arg, 1)) { + error("%s line %d: Bad key types '%s'.", + filename, linenum, arg ? arg : ""); + goto out; + } + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case oCASignatureAlgorithms: + charptr = &options->ca_sign_algorithms; + goto parse_pubkey_algos; + + case oLogLevel: + log_level_ptr = &options->log_level; + arg = argv_next(&ac, &av); + value = log_level_number(arg); + if (value == SYSLOG_LEVEL_NOT_SET) { + error("%.200s line %d: unsupported log level '%s'", + filename, linenum, arg ? arg : ""); + goto out; + } + if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET) + *log_level_ptr = (LogLevel) value; + break; + + case oLogFacility: + log_facility_ptr = &options->log_facility; + arg = argv_next(&ac, &av); + value = log_facility_number(arg); + if (value == SYSLOG_FACILITY_NOT_SET) { + error("%.200s line %d: unsupported log facility '%s'", + filename, linenum, arg ? arg : ""); + goto out; + } + if (*log_facility_ptr == -1) + *log_facility_ptr = (SyslogFacility) value; + break; + + case oLogVerbose: + cppptr = &options->log_verbose; + uintptr = &options->num_log_verbose; + i = 0; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + /* Allow "none" only in first position */ + if (strcasecmp(arg, "none") == 0) { + if (i > 0 || ac > 0) { + error("%s line %d: keyword %s \"none\" " + "argument must appear alone.", + filename, linenum, keyword); + goto out; + } + } + i++; + if (*activep && *uintptr == 0) { + *cppptr = xrecallocarray(*cppptr, *uintptr, + *uintptr + 1, sizeof(**cppptr)); + (*cppptr)[(*uintptr)++] = xstrdup(arg); + } + } + break; + + case oLocalForward: + case oRemoteForward: + case oDynamicForward: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + + remotefwd = (opcode == oRemoteForward); + dynamicfwd = (opcode == oDynamicForward); + + if (!dynamicfwd) { + arg2 = argv_next(&ac, &av); + if (arg2 == NULL || *arg2 == '\0') { + if (remotefwd) + dynamicfwd = 1; + else { + error("%.200s line %d: Missing target " + "argument.", filename, linenum); + goto out; + } + } else { + /* construct a string for parse_forward */ + snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, + arg2); + } + } + if (dynamicfwd) + strlcpy(fwdarg, arg, sizeof(fwdarg)); + + if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) { + error("%.200s line %d: Bad forwarding specification.", + filename, linenum); + goto out; + } + + if (*activep) { + if (remotefwd) { + add_remote_forward(options, &fwd); + } else { + add_local_forward(options, &fwd); + } + } + break; + + case oPermitRemoteOpen: + uintptr = &options->num_permitted_remote_opens; + cppptr = &options->permitted_remote_opens; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing %s specification", + filename, linenum, lookup_opcode_name(opcode)); + uvalue = *uintptr; /* modified later */ + if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { + if (*activep && uvalue == 0) { + *uintptr = 1; + *cppptr = xcalloc(1, sizeof(**cppptr)); + (*cppptr)[0] = xstrdup(arg); + } + break; + } + while ((arg = argv_next(&ac, &av)) != NULL) { + arg2 = xstrdup(arg); + p = hpdelim(&arg); + if (p == NULL) { + fatal("%s line %d: missing host in %s", + filename, linenum, + lookup_opcode_name(opcode)); + } + p = cleanhostname(p); + /* + * don't want to use permitopen_port to avoid + * dependency on channels.[ch] here. + */ + if (arg == NULL || + (strcmp(arg, "*") != 0 && a2port(arg) <= 0)) { + fatal("%s line %d: bad port number in %s", + filename, linenum, + lookup_opcode_name(opcode)); + } + if (*activep && uvalue == 0) { + opt_array_append(filename, linenum, + lookup_opcode_name(opcode), + cppptr, uintptr, arg2); + } + free(arg2); + } + break; + + case oClearAllForwardings: + intptr = &options->clear_forwardings; + goto parse_flag; + + case oHost: + if (cmdline) { + error("Host directive not supported as a command-line " + "option"); + goto out; + } + *activep = 0; + arg2 = NULL; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + if ((flags & SSHCONF_NEVERMATCH) != 0) { + argv_consume(&ac); + break; + } + negated = *arg == '!'; + if (negated) + arg++; + if (match_pattern(host, arg)) { + if (negated) { + debug("%.200s line %d: Skipping Host " + "block because of negated match " + "for %.100s", filename, linenum, + arg); + *activep = 0; + argv_consume(&ac); + break; + } + if (!*activep) + arg2 = arg; /* logged below */ + *activep = 1; + } + } + if (*activep) + debug("%.200s line %d: Applying options for %.100s", + filename, linenum, arg2); + break; + + case oMatch: + if (cmdline) { + error("Host directive not supported as a command-line " + "option"); + goto out; + } + value = match_cfg_line(options, &str, pw, host, original_host, + flags & SSHCONF_FINAL, want_final_pass, + filename, linenum); + if (value < 0) { + error("%.200s line %d: Bad Match condition", filename, + linenum); + goto out; + } + *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value; + /* + * If match_cfg_line() didn't consume all its arguments then + * arrange for the extra arguments check below to fail. + */ + + if (str == NULL || *str == '\0') + argv_consume(&ac); + break; + + case oEscapeChar: + intptr = &options->escape_char; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if (strcmp(arg, "none") == 0) + value = SSH_ESCAPECHAR_NONE; + else if (arg[1] == '\0') + value = (u_char) arg[0]; + else if (arg[0] == '^' && arg[2] == 0 && + (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) + value = (u_char) arg[1] & 31; + else { + error("%.200s line %d: Bad escape character.", + filename, linenum); + goto out; + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oAddressFamily: + intptr = &options->address_family; + multistate_ptr = multistate_addressfamily; + goto parse_multistate; + + case oEnableSSHKeysign: + intptr = &options->enable_ssh_keysign; + goto parse_flag; + + case oIdentitiesOnly: + intptr = &options->identities_only; + goto parse_flag; + + case oServerAliveInterval: + intptr = &options->server_alive_interval; + goto parse_time; + + case oServerAliveCountMax: + intptr = &options->server_alive_count_max; + goto parse_int; + + case oSendEnv: + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0' || strchr(arg, '=') != NULL) { + error("%s line %d: Invalid environment name.", + filename, linenum); + goto out; + } + if (!*activep) + continue; + if (*arg == '-') { + /* Removing an env var */ + rm_env(options, arg, filename, linenum); + continue; + } else { + /* Adding an env var */ + if (options->num_send_env >= INT_MAX) { + error("%s line %d: too many send env.", + filename, linenum); + goto out; + } + options->send_env = xrecallocarray( + options->send_env, options->num_send_env, + options->num_send_env + 1, + sizeof(*options->send_env)); + options->send_env[options->num_send_env++] = + xstrdup(arg); + } + } + break; + + case oSetEnv: + value = options->num_setenv; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (strchr(arg, '=') == NULL) { + error("%s line %d: Invalid SetEnv.", + filename, linenum); + goto out; + } + if (!*activep || value != 0) + continue; + /* Adding a setenv var */ + if (options->num_setenv >= INT_MAX) { + error("%s line %d: too many SetEnv.", + filename, linenum); + goto out; + } + options->setenv = xrecallocarray( + options->setenv, options->num_setenv, + options->num_setenv + 1, sizeof(*options->setenv)); + options->setenv[options->num_setenv++] = xstrdup(arg); + } + break; + + case oControlPath: + charptr = &options->control_path; + goto parse_string; + + case oControlMaster: + intptr = &options->control_master; + multistate_ptr = multistate_controlmaster; + goto parse_multistate; + + case oControlPersist: + /* no/false/yes/true, or a time spec */ + intptr = &options->control_persist; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing ControlPersist" + " argument.", filename, linenum); + goto out; + } + value = 0; + value2 = 0; /* timeout */ + if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) + value = 0; + else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) + value = 1; + else if ((value2 = convtime(arg)) >= 0) + value = 1; + else { + error("%.200s line %d: Bad ControlPersist argument.", + filename, linenum); + goto out; + } + if (*activep && *intptr == -1) { + *intptr = value; + options->control_persist_timeout = value2; + } + break; + + case oHashKnownHosts: + intptr = &options->hash_known_hosts; + goto parse_flag; + + case oTunnel: + intptr = &options->tun_open; + multistate_ptr = multistate_tunnel; + goto parse_multistate; + + case oTunnelDevice: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + value = a2tun(arg, &value2); + if (value == SSH_TUNID_ERR) { + error("%.200s line %d: Bad tun device.", + filename, linenum); + goto out; + } + if (*activep && options->tun_local == -1) { + options->tun_local = value; + options->tun_remote = value2; + } + break; + + case oLocalCommand: + charptr = &options->local_command; + goto parse_command; + + case oPermitLocalCommand: + intptr = &options->permit_local_command; + goto parse_flag; + + case oRemoteCommand: + charptr = &options->remote_command; + goto parse_command; + + case oVisualHostKey: + intptr = &options->visual_host_key; + goto parse_flag; + + case oInclude: + if (cmdline) { + error("Include directive not supported as a " + "command-line option"); + goto out; + } + value = 0; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + /* + * Ensure all paths are anchored. User configuration + * files may begin with '~/' but system configurations + * must not. If the path is relative, then treat it + * as living in ~/.ssh for user configurations or + * /etc/ssh for system ones. + */ + if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) { + error("%.200s line %d: bad include path %s.", + filename, linenum, arg); + goto out; + } + if (!path_absolute(arg) && *arg != '~') { + xasprintf(&arg2, "%s/%s", + (flags & SSHCONF_USERCONF) ? + "~/" _PATH_SSH_USER_DIR : SSHDIR, arg); + } else + arg2 = xstrdup(arg); + memset(&gl, 0, sizeof(gl)); + r = glob(arg2, GLOB_TILDE, NULL, &gl); + if (r == GLOB_NOMATCH) { + debug("%.200s line %d: include %s matched no " + "files",filename, linenum, arg2); + free(arg2); + continue; + } else if (r != 0) { + error("%.200s line %d: glob failed for %s.", + filename, linenum, arg2); + goto out; + } + free(arg2); + oactive = *activep; + for (i = 0; i < gl.gl_pathc; i++) { + debug3("%.200s line %d: Including file %s " + "depth %d%s", filename, linenum, + gl.gl_pathv[i], depth, + oactive ? "" : " (parse only)"); + r = read_config_file_depth(gl.gl_pathv[i], + pw, host, original_host, options, + flags | SSHCONF_CHECKPERM | + (oactive ? 0 : SSHCONF_NEVERMATCH), + activep, want_final_pass, depth + 1); + if (r != 1 && errno != ENOENT) { + error("Can't open user config file " + "%.100s: %.100s", gl.gl_pathv[i], + strerror(errno)); + globfree(&gl); + goto out; + } + /* + * don't let Match in includes clobber the + * containing file's Match state. + */ + *activep = oactive; + if (r != 1) + value = -1; + } + globfree(&gl); + } + if (value != 0) + ret = value; + break; + + case oIPQoS: + arg = argv_next(&ac, &av); + if ((value = parse_ipqos(arg)) == -1) { + error("%s line %d: Bad IPQoS value: %s", + filename, linenum, arg); + goto out; + } + arg = argv_next(&ac, &av); + if (arg == NULL) + value2 = value; + else if ((value2 = parse_ipqos(arg)) == -1) { + error("%s line %d: Bad IPQoS value: %s", + filename, linenum, arg); + goto out; + } + if (*activep && options->ip_qos_interactive == -1) { + options->ip_qos_interactive = value; + options->ip_qos_bulk = value2; + } + break; + + case oRequestTTY: + intptr = &options->request_tty; + multistate_ptr = multistate_requesttty; + goto parse_multistate; + + case oSessionType: + intptr = &options->session_type; + multistate_ptr = multistate_sessiontype; + goto parse_multistate; + + case oStdinNull: + intptr = &options->stdin_null; + goto parse_flag; + + case oForkAfterAuthentication: + intptr = &options->fork_after_authentication; + goto parse_flag; + + case oIgnoreUnknown: + charptr = &options->ignored_unknown; + goto parse_string; + + case oProxyUseFdpass: + intptr = &options->proxy_use_fdpass; + goto parse_flag; + + case oCanonicalDomains: + value = options->num_canonical_domains != 0; + i = 0; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + /* Allow "none" only in first position */ + if (strcasecmp(arg, "none") == 0) { + if (i > 0 || ac > 0) { + error("%s line %d: keyword %s \"none\" " + "argument must appear alone.", + filename, linenum, keyword); + goto out; + } + } + i++; + if (!valid_domain(arg, 1, &errstr)) { + error("%s line %d: %s", filename, linenum, + errstr); + goto out; + } + if (!*activep || value) + continue; + if (options->num_canonical_domains >= + MAX_CANON_DOMAINS) { + error("%s line %d: too many hostname suffixes.", + filename, linenum); + goto out; + } + options->canonical_domains[ + options->num_canonical_domains++] = xstrdup(arg); + } + break; + + case oCanonicalizePermittedCNAMEs: + value = options->num_permitted_cnames != 0; + i = 0; + while ((arg = argv_next(&ac, &av)) != NULL) { + /* + * Either 'none' (only in first position), '*' for + * everything or 'list:list' + */ + if (strcasecmp(arg, "none") == 0) { + if (i > 0 || ac > 0) { + error("%s line %d: keyword %s \"none\" " + "argument must appear alone.", + filename, linenum, keyword); + goto out; + } + arg2 = ""; + } else if (strcmp(arg, "*") == 0) { + arg2 = arg; + } else { + lowercase(arg); + if ((arg2 = strchr(arg, ':')) == NULL || + arg2[1] == '\0') { + error("%s line %d: " + "Invalid permitted CNAME \"%s\"", + filename, linenum, arg); + goto out; + } + *arg2 = '\0'; + arg2++; + } + i++; + if (!*activep || value) + continue; + if (options->num_permitted_cnames >= + MAX_CANON_DOMAINS) { + error("%s line %d: too many permitted CNAMEs.", + filename, linenum); + goto out; + } + cname = options->permitted_cnames + + options->num_permitted_cnames++; + cname->source_list = xstrdup(arg); + cname->target_list = xstrdup(arg2); + } + break; + + case oCanonicalizeHostname: + intptr = &options->canonicalize_hostname; + multistate_ptr = multistate_canonicalizehostname; + goto parse_multistate; + + case oCanonicalizeMaxDots: + intptr = &options->canonicalize_max_dots; + goto parse_int; + + case oCanonicalizeFallbackLocal: + intptr = &options->canonicalize_fallback_local; + goto parse_flag; + + case oStreamLocalBindMask: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing StreamLocalBindMask " + "argument.", filename, linenum); + goto out; + } + /* Parse mode in octal format */ + value = strtol(arg, &endofnumber, 8); + if (arg == endofnumber || value < 0 || value > 0777) { + error("%.200s line %d: Bad mask.", filename, linenum); + goto out; + } + options->fwd_opts.streamlocal_bind_mask = (mode_t)value; + break; + + case oStreamLocalBindUnlink: + intptr = &options->fwd_opts.streamlocal_bind_unlink; + goto parse_flag; + + case oRevokedHostKeys: + charptr = &options->revoked_host_keys; + goto parse_string; + + case oFingerprintHash: + intptr = &options->fingerprint_hash; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + if ((value = ssh_digest_alg_by_name(arg)) == -1) { + error("%.200s line %d: Invalid hash algorithm \"%s\".", + filename, linenum, arg); + goto out; + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oUpdateHostkeys: + intptr = &options->update_hostkeys; + multistate_ptr = multistate_yesnoask; + goto parse_multistate; + + case oHostbasedAcceptedAlgorithms: + charptr = &options->hostbased_accepted_algos; + goto parse_pubkey_algos; + + case oPubkeyAcceptedAlgorithms: + charptr = &options->pubkey_accepted_algos; + goto parse_pubkey_algos; + + case oAddKeysToAgent: + arg = argv_next(&ac, &av); + arg2 = argv_next(&ac, &av); + value = parse_multistate_value(arg, filename, linenum, + multistate_yesnoaskconfirm); + value2 = 0; /* unlimited lifespan by default */ + if (value == 3 && arg2 != NULL) { + /* allow "AddKeysToAgent confirm 5m" */ + if ((value2 = convtime(arg2)) == -1 || + value2 > INT_MAX) { + error("%s line %d: invalid time value.", + filename, linenum); + goto out; + } + } else if (value == -1 && arg2 == NULL) { + if ((value2 = convtime(arg)) == -1 || + value2 > INT_MAX) { + error("%s line %d: unsupported option", + filename, linenum); + goto out; + } + value = 1; /* yes */ + } else if (value == -1 || arg2 != NULL) { + error("%s line %d: unsupported option", + filename, linenum); + goto out; + } + if (*activep && options->add_keys_to_agent == -1) { + options->add_keys_to_agent = value; + options->add_keys_to_agent_lifespan = value2; + } + break; + + case oIdentityAgent: + charptr = &options->identity_agent; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + goto out; + } + parse_agent_path: + /* Extra validation if the string represents an env var. */ + if ((arg2 = dollar_expand(&r, arg)) == NULL || r) { + error("%.200s line %d: Invalid environment expansion " + "%s.", filename, linenum, arg); + goto out; + } + free(arg2); + /* check for legacy environment format */ + if (arg[0] == '$' && arg[1] != '{' && + !valid_env_name(arg + 1)) { + error("%.200s line %d: Invalid environment name %s.", + filename, linenum, arg); + goto out; + } + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case oDeprecated: + debug("%s line %d: Deprecated option \"%s\"", + filename, linenum, keyword); + argv_consume(&ac); + break; + + case oUnsupported: + error("%s line %d: Unsupported option \"%s\"", + filename, linenum, keyword); + argv_consume(&ac); + break; + + default: + error("%s line %d: Unimplemented opcode %d", + filename, linenum, opcode); + goto out; + } + + /* Check that there is no garbage at end of line. */ + if (ac > 0) { + error("%.200s line %d: keyword %s extra arguments " + "at end of line", filename, linenum, keyword); + goto out; + } + + /* success */ + ret = 0; + out: + argv_free(oav, oac); + return ret; +} + +/* + * Reads the config file and modifies the options accordingly. Options + * should already be initialized before this call. This never returns if + * there is an error. If the file does not exist, this returns 0. + */ +int +read_config_file(const char *filename, struct passwd *pw, const char *host, + const char *original_host, Options *options, int flags, + int *want_final_pass) +{ + int active = 1; + + return read_config_file_depth(filename, pw, host, original_host, + options, flags, &active, want_final_pass, 0); +} + +#define READCONF_MAX_DEPTH 16 +static int +read_config_file_depth(const char *filename, struct passwd *pw, + const char *host, const char *original_host, Options *options, + int flags, int *activep, int *want_final_pass, int depth) +{ + FILE *f; + char *line = NULL; + size_t linesize = 0; + int linenum; + int bad_options = 0; + + if (depth < 0 || depth > READCONF_MAX_DEPTH) + fatal("Too many recursive configuration includes"); + + if ((f = fopen(filename, "r")) == NULL) + return 0; + + if (flags & SSHCONF_CHECKPERM) { + struct stat sb; + + if (fstat(fileno(f), &sb) == -1) + fatal("fstat %s: %s", filename, strerror(errno)); + if (((sb.st_uid != 0 && sb.st_uid != getuid()) || + (sb.st_mode & 022) != 0)) + fatal("Bad owner or permissions on %s", filename); + } + + debug("Reading configuration data %.200s", filename); + + /* + * Mark that we are now processing the options. This flag is turned + * on/off by Host specifications. + */ + linenum = 0; + while (getline(&line, &linesize, f) != -1) { + /* Update line number counter. */ + linenum++; + /* + * Trim out comments and strip whitespace. + * NB - preserve newlines, they are needed to reproduce + * line numbers later for error messages. + */ + if (process_config_line_depth(options, pw, host, original_host, + line, filename, linenum, activep, flags, want_final_pass, + depth) != 0) + bad_options++; + } + free(line); + fclose(f); + if (bad_options > 0) + fatal("%s: terminating, %d bad configuration options", + filename, bad_options); + return 1; +} + +/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ +int +option_clear_or_none(const char *o) +{ + return o == NULL || strcasecmp(o, "none") == 0; +} + +/* + * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise. + * Allowed to be called on non-final configuration. + */ +int +config_has_permitted_cnames(Options *options) +{ + if (options->num_permitted_cnames == 1 && + strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 && + strcmp(options->permitted_cnames[0].target_list, "") == 0) + return 0; + return options->num_permitted_cnames > 0; +} + +/* + * Initializes options to special values that indicate that they have not yet + * been set. Read_config_file will only set options with this value. Options + * are processed in the following order: command line, user config file, + * system config file. Last, fill_default_options is called. + */ + +void +initialize_options(Options * options) +{ + memset(options, 'X', sizeof(*options)); + options->forward_agent = -1; + options->forward_agent_sock_path = NULL; + options->forward_x11 = -1; + options->forward_x11_trusted = -1; + options->forward_x11_timeout = -1; + options->stdio_forward_host = NULL; + options->stdio_forward_port = 0; + options->clear_forwardings = -1; + options->exit_on_forward_failure = -1; + options->xauth_location = NULL; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; + options->pubkey_authentication = -1; + options->gss_authentication = -1; + options->gss_deleg_creds = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; + options->hostbased_authentication = -1; + options->batch_mode = -1; + options->check_host_ip = -1; + options->strict_host_key_checking = -1; + options->compression = -1; + options->tcp_keep_alive = -1; + options->port = -1; + options->address_family = -1; + options->connection_attempts = -1; + options->connection_timeout = -1; + options->number_of_password_prompts = -1; + options->ciphers = NULL; + options->macs = NULL; + options->kex_algorithms = NULL; + options->hostkeyalgorithms = NULL; + options->ca_sign_algorithms = NULL; + options->num_identity_files = 0; + memset(options->identity_keys, 0, sizeof(options->identity_keys)); + options->num_certificate_files = 0; + memset(options->certificates, 0, sizeof(options->certificates)); + options->hostname = NULL; + options->host_key_alias = NULL; + options->proxy_command = NULL; + options->jump_user = NULL; + options->jump_host = NULL; + options->jump_port = -1; + options->jump_extra = NULL; + options->user = NULL; + options->escape_char = -1; + options->num_system_hostfiles = 0; + options->num_user_hostfiles = 0; + options->local_forwards = NULL; + options->num_local_forwards = 0; + options->remote_forwards = NULL; + options->num_remote_forwards = 0; + options->permitted_remote_opens = NULL; + options->num_permitted_remote_opens = 0; + options->log_facility = SYSLOG_FACILITY_NOT_SET; + options->log_level = SYSLOG_LEVEL_NOT_SET; + options->num_log_verbose = 0; + options->log_verbose = NULL; + options->preferred_authentications = NULL; + options->bind_address = NULL; + options->bind_interface = NULL; + options->pkcs11_provider = NULL; + options->sk_provider = NULL; + options->enable_ssh_keysign = - 1; + options->no_host_authentication_for_localhost = - 1; + options->identities_only = - 1; + options->rekey_limit = - 1; + options->rekey_interval = -1; + options->verify_host_key_dns = -1; + options->server_alive_interval = -1; + options->server_alive_count_max = -1; + options->send_env = NULL; + options->num_send_env = 0; + options->setenv = NULL; + options->num_setenv = 0; + options->control_path = NULL; + options->control_master = -1; + options->control_persist = -1; + options->control_persist_timeout = 0; + options->hash_known_hosts = -1; + options->tun_open = -1; + options->tun_local = -1; + options->tun_remote = -1; + options->local_command = NULL; + options->permit_local_command = -1; + options->remote_command = NULL; + options->add_keys_to_agent = -1; + options->add_keys_to_agent_lifespan = -1; + options->identity_agent = NULL; + options->visual_host_key = -1; + options->ip_qos_interactive = -1; + options->ip_qos_bulk = -1; + options->request_tty = -1; + options->session_type = -1; + options->stdin_null = -1; + options->fork_after_authentication = -1; + options->proxy_use_fdpass = -1; + options->ignored_unknown = NULL; + options->num_canonical_domains = 0; + options->num_permitted_cnames = 0; + options->canonicalize_max_dots = -1; + options->canonicalize_fallback_local = -1; + options->canonicalize_hostname = -1; + options->revoked_host_keys = NULL; + options->fingerprint_hash = -1; + options->update_hostkeys = -1; + options->hostbased_accepted_algos = NULL; + options->pubkey_accepted_algos = NULL; + options->known_hosts_command = NULL; +} + +/* + * A petite version of fill_default_options() that just fills the options + * needed for hostname canonicalization to proceed. + */ +void +fill_default_options_for_canonicalization(Options *options) +{ + if (options->canonicalize_max_dots == -1) + options->canonicalize_max_dots = 1; + if (options->canonicalize_fallback_local == -1) + options->canonicalize_fallback_local = 1; + if (options->canonicalize_hostname == -1) + options->canonicalize_hostname = SSH_CANONICALISE_NO; +} + +/* + * Called after processing other sources of option data, this fills those + * options for which no value has been specified with their default values. + */ +int +fill_default_options(Options * options) +{ + char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig; + char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig; + int ret = 0, r; + + if (options->forward_agent == -1) + options->forward_agent = 0; + if (options->forward_x11 == -1) + options->forward_x11 = 0; + if (options->forward_x11_trusted == -1) + options->forward_x11_trusted = 0; + if (options->forward_x11_timeout == -1) + options->forward_x11_timeout = 1200; + /* + * stdio forwarding (-W) changes the default for these but we defer + * setting the values so they can be overridden. + */ + if (options->exit_on_forward_failure == -1) + options->exit_on_forward_failure = + options->stdio_forward_host != NULL ? 1 : 0; + if (options->clear_forwardings == -1) + options->clear_forwardings = + options->stdio_forward_host != NULL ? 1 : 0; + if (options->clear_forwardings == 1) + clear_forwardings(options); + + if (options->xauth_location == NULL) + options->xauth_location = xstrdup(_PATH_XAUTH); + if (options->fwd_opts.gateway_ports == -1) + options->fwd_opts.gateway_ports = 0; + if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) + options->fwd_opts.streamlocal_bind_mask = 0177; + if (options->fwd_opts.streamlocal_bind_unlink == -1) + options->fwd_opts.streamlocal_bind_unlink = 0; + if (options->pubkey_authentication == -1) + options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL; + if (options->gss_authentication == -1) + options->gss_authentication = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) + options->kbd_interactive_authentication = 1; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; + if (options->batch_mode == -1) + options->batch_mode = 0; + if (options->check_host_ip == -1) + options->check_host_ip = 0; + if (options->strict_host_key_checking == -1) + options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK; + if (options->compression == -1) + options->compression = 0; + if (options->tcp_keep_alive == -1) + options->tcp_keep_alive = 1; + if (options->port == -1) + options->port = 0; /* Filled in ssh_connect. */ + if (options->address_family == -1) + options->address_family = AF_UNSPEC; + if (options->connection_attempts == -1) + options->connection_attempts = 1; + if (options->number_of_password_prompts == -1) + options->number_of_password_prompts = 3; + /* options->hostkeyalgorithms, default set in myproposals.h */ + if (options->add_keys_to_agent == -1) { + options->add_keys_to_agent = 0; + options->add_keys_to_agent_lifespan = 0; + } + if (options->num_identity_files == 0) { + add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0); +#ifdef OPENSSL_HAS_ECC + add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0); + add_identity_file(options, "~/", + _PATH_SSH_CLIENT_ID_ECDSA_SK, 0); +#endif + add_identity_file(options, "~/", + _PATH_SSH_CLIENT_ID_ED25519, 0); + add_identity_file(options, "~/", + _PATH_SSH_CLIENT_ID_ED25519_SK, 0); + add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0); + add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0); + } + if (options->escape_char == -1) + options->escape_char = '~'; + if (options->num_system_hostfiles == 0) { + options->system_hostfiles[options->num_system_hostfiles++] = + xstrdup(_PATH_SSH_SYSTEM_HOSTFILE); + options->system_hostfiles[options->num_system_hostfiles++] = + xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2); + } + if (options->update_hostkeys == -1) { + if (options->verify_host_key_dns <= 0 && + (options->num_user_hostfiles == 0 || + (options->num_user_hostfiles == 1 && strcmp(options-> + user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0))) + options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES; + else + options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO; + } + if (options->num_user_hostfiles == 0) { + options->user_hostfiles[options->num_user_hostfiles++] = + xstrdup(_PATH_SSH_USER_HOSTFILE); + options->user_hostfiles[options->num_user_hostfiles++] = + xstrdup(_PATH_SSH_USER_HOSTFILE2); + } + if (options->log_level == SYSLOG_LEVEL_NOT_SET) + options->log_level = SYSLOG_LEVEL_INFO; + if (options->log_facility == SYSLOG_FACILITY_NOT_SET) + options->log_facility = SYSLOG_FACILITY_USER; + if (options->no_host_authentication_for_localhost == - 1) + options->no_host_authentication_for_localhost = 0; + if (options->identities_only == -1) + options->identities_only = 0; + if (options->enable_ssh_keysign == -1) + options->enable_ssh_keysign = 0; + if (options->rekey_limit == -1) + options->rekey_limit = 0; + if (options->rekey_interval == -1) + options->rekey_interval = 0; + if (options->verify_host_key_dns == -1) + options->verify_host_key_dns = 0; + if (options->server_alive_interval == -1) + options->server_alive_interval = 0; + if (options->server_alive_count_max == -1) + options->server_alive_count_max = 3; + if (options->control_master == -1) + options->control_master = 0; + if (options->control_persist == -1) { + options->control_persist = 0; + options->control_persist_timeout = 0; + } + if (options->hash_known_hosts == -1) + options->hash_known_hosts = 0; + if (options->tun_open == -1) + options->tun_open = SSH_TUNMODE_NO; + if (options->tun_local == -1) + options->tun_local = SSH_TUNID_ANY; + if (options->tun_remote == -1) + options->tun_remote = SSH_TUNID_ANY; + if (options->permit_local_command == -1) + options->permit_local_command = 0; + if (options->visual_host_key == -1) + options->visual_host_key = 0; + if (options->ip_qos_interactive == -1) + options->ip_qos_interactive = IPTOS_DSCP_AF21; + if (options->ip_qos_bulk == -1) + options->ip_qos_bulk = IPTOS_DSCP_CS1; + if (options->request_tty == -1) + options->request_tty = REQUEST_TTY_AUTO; + if (options->session_type == -1) + options->session_type = SESSION_TYPE_DEFAULT; + if (options->stdin_null == -1) + options->stdin_null = 0; + if (options->fork_after_authentication == -1) + options->fork_after_authentication = 0; + if (options->proxy_use_fdpass == -1) + options->proxy_use_fdpass = 0; + if (options->canonicalize_max_dots == -1) + options->canonicalize_max_dots = 1; + if (options->canonicalize_fallback_local == -1) + options->canonicalize_fallback_local = 1; + if (options->canonicalize_hostname == -1) + options->canonicalize_hostname = SSH_CANONICALISE_NO; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; +#ifdef ENABLE_SK_INTERNAL + if (options->sk_provider == NULL) + options->sk_provider = xstrdup("internal"); +#else + if (options->sk_provider == NULL) + options->sk_provider = xstrdup("$SSH_SK_PROVIDER"); +#endif + + /* Expand KEX name lists */ + all_cipher = cipher_alg_list(',', 0); + all_mac = mac_alg_list(','); + all_kex = kex_alg_list(','); + all_key = sshkey_alg_list(0, 0, 1, ','); + all_sig = sshkey_alg_list(0, 1, 1, ','); + /* remove unsupported algos from default lists */ + def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher); + def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac); + def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex); + def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); + def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig); +#define ASSEMBLE(what, defaults, all) \ + do { \ + if ((r = kex_assemble_names(&options->what, \ + defaults, all)) != 0) { \ + error_fr(r, "%s", #what); \ + goto fail; \ + } \ + } while (0) + ASSEMBLE(ciphers, def_cipher, all_cipher); + ASSEMBLE(macs, def_mac, all_mac); + ASSEMBLE(kex_algorithms, def_kex, all_kex); + ASSEMBLE(hostbased_accepted_algos, def_key, all_key); + ASSEMBLE(pubkey_accepted_algos, def_key, all_key); + ASSEMBLE(ca_sign_algorithms, def_sig, all_sig); +#undef ASSEMBLE + +#define CLEAR_ON_NONE(v) \ + do { \ + if (option_clear_or_none(v)) { \ + free(v); \ + v = NULL; \ + } \ + } while(0) + CLEAR_ON_NONE(options->local_command); + CLEAR_ON_NONE(options->remote_command); + CLEAR_ON_NONE(options->proxy_command); + CLEAR_ON_NONE(options->control_path); + CLEAR_ON_NONE(options->revoked_host_keys); + CLEAR_ON_NONE(options->pkcs11_provider); + CLEAR_ON_NONE(options->sk_provider); + CLEAR_ON_NONE(options->known_hosts_command); + if (options->jump_host != NULL && + strcmp(options->jump_host, "none") == 0 && + options->jump_port == 0 && options->jump_user == NULL) { + free(options->jump_host); + options->jump_host = NULL; + } + if (options->num_permitted_cnames == 1 && + !config_has_permitted_cnames(options)) { + /* clean up CanonicalizePermittedCNAMEs=none */ + free(options->permitted_cnames[0].source_list); + free(options->permitted_cnames[0].target_list); + memset(options->permitted_cnames, '\0', + sizeof(*options->permitted_cnames)); + options->num_permitted_cnames = 0; + } + /* options->identity_agent distinguishes NULL from 'none' */ + /* options->user will be set in the main program if appropriate */ + /* options->hostname will be set in the main program if appropriate */ + /* options->host_key_alias should not be set by default */ + /* options->preferred_authentications will be set in ssh */ + + /* success */ + ret = 0; + fail: + free(all_cipher); + free(all_mac); + free(all_kex); + free(all_key); + free(all_sig); + free(def_cipher); + free(def_mac); + free(def_kex); + free(def_key); + free(def_sig); + return ret; +} + +void +free_options(Options *o) +{ + int i; + + if (o == NULL) + return; + +#define FREE_ARRAY(type, n, a) \ + do { \ + type _i; \ + for (_i = 0; _i < (n); _i++) \ + free((a)[_i]); \ + } while (0) + + free(o->forward_agent_sock_path); + free(o->xauth_location); + FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose); + free(o->log_verbose); + free(o->ciphers); + free(o->macs); + free(o->hostkeyalgorithms); + free(o->kex_algorithms); + free(o->ca_sign_algorithms); + free(o->hostname); + free(o->host_key_alias); + free(o->proxy_command); + free(o->user); + FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles); + FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles); + free(o->preferred_authentications); + free(o->bind_address); + free(o->bind_interface); + free(o->pkcs11_provider); + free(o->sk_provider); + for (i = 0; i < o->num_identity_files; i++) { + free(o->identity_files[i]); + sshkey_free(o->identity_keys[i]); + } + for (i = 0; i < o->num_certificate_files; i++) { + free(o->certificate_files[i]); + sshkey_free(o->certificates[i]); + } + free(o->identity_agent); + for (i = 0; i < o->num_local_forwards; i++) { + free(o->local_forwards[i].listen_host); + free(o->local_forwards[i].listen_path); + free(o->local_forwards[i].connect_host); + free(o->local_forwards[i].connect_path); + } + free(o->local_forwards); + for (i = 0; i < o->num_remote_forwards; i++) { + free(o->remote_forwards[i].listen_host); + free(o->remote_forwards[i].listen_path); + free(o->remote_forwards[i].connect_host); + free(o->remote_forwards[i].connect_path); + } + free(o->remote_forwards); + free(o->stdio_forward_host); + FREE_ARRAY(int, o->num_send_env, o->send_env); + free(o->send_env); + FREE_ARRAY(int, o->num_setenv, o->setenv); + free(o->setenv); + free(o->control_path); + free(o->local_command); + free(o->remote_command); + FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains); + for (i = 0; i < o->num_permitted_cnames; i++) { + free(o->permitted_cnames[i].source_list); + free(o->permitted_cnames[i].target_list); + } + free(o->revoked_host_keys); + free(o->hostbased_accepted_algos); + free(o->pubkey_accepted_algos); + free(o->jump_user); + free(o->jump_host); + free(o->jump_extra); + free(o->ignored_unknown); + explicit_bzero(o, sizeof(*o)); +#undef FREE_ARRAY +} + +struct fwdarg { + char *arg; + int ispath; +}; + +/* + * parse_fwd_field + * parses the next field in a port forwarding specification. + * sets fwd to the parsed field and advances p past the colon + * or sets it to NULL at end of string. + * returns 0 on success, else non-zero. + */ +static int +parse_fwd_field(char **p, struct fwdarg *fwd) +{ + char *ep, *cp = *p; + int ispath = 0; + + if (*cp == '\0') { + *p = NULL; + return -1; /* end of string */ + } + + /* + * A field escaped with square brackets is used literally. + * XXX - allow ']' to be escaped via backslash? + */ + if (*cp == '[') { + /* find matching ']' */ + for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) { + if (*ep == '/') + ispath = 1; + } + /* no matching ']' or not at end of field. */ + if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0')) + return -1; + /* NUL terminate the field and advance p past the colon */ + *ep++ = '\0'; + if (*ep != '\0') + *ep++ = '\0'; + fwd->arg = cp + 1; + fwd->ispath = ispath; + *p = ep; + return 0; + } + + for (cp = *p; *cp != '\0'; cp++) { + switch (*cp) { + case '\\': + memmove(cp, cp + 1, strlen(cp + 1) + 1); + if (*cp == '\0') + return -1; + break; + case '/': + ispath = 1; + break; + case ':': + *cp++ = '\0'; + goto done; + } + } +done: + fwd->arg = *p; + fwd->ispath = ispath; + *p = cp; + return 0; +} + +/* + * parse_forward + * parses a string containing a port forwarding specification of the form: + * dynamicfwd == 0 + * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath + * listenpath:connectpath + * dynamicfwd == 1 + * [listenhost:]listenport + * returns number of arguments parsed or zero on error + */ +int +parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) +{ + struct fwdarg fwdargs[4]; + char *p, *cp; + int i, err; + + memset(fwd, 0, sizeof(*fwd)); + memset(fwdargs, 0, sizeof(fwdargs)); + + /* + * We expand environment variables before checking if we think they're + * paths so that if ${VAR} expands to a fully qualified path it is + * treated as a path. + */ + cp = p = dollar_expand(&err, fwdspec); + if (p == NULL || err) + return 0; + + /* skip leading spaces */ + while (isspace((u_char)*cp)) + cp++; + + for (i = 0; i < 4; ++i) { + if (parse_fwd_field(&cp, &fwdargs[i]) != 0) + break; + } + + /* Check for trailing garbage */ + if (cp != NULL && *cp != '\0') { + i = 0; /* failure */ + } + + switch (i) { + case 1: + if (fwdargs[0].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + } + fwd->connect_host = xstrdup("socks"); + break; + + case 2: + if (fwdargs[0].ispath && fwdargs[1].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + fwd->connect_path = xstrdup(fwdargs[1].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else if (fwdargs[1].ispath) { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + fwd->connect_path = xstrdup(fwdargs[1].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_host = xstrdup("socks"); + } + break; + + case 3: + if (fwdargs[0].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + fwd->connect_host = xstrdup(fwdargs[1].arg); + fwd->connect_port = a2port(fwdargs[2].arg); + } else if (fwdargs[2].ispath) { + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_path = xstrdup(fwdargs[2].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + fwd->connect_host = xstrdup(fwdargs[1].arg); + fwd->connect_port = a2port(fwdargs[2].arg); + } + break; + + case 4: + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_host = xstrdup(fwdargs[2].arg); + fwd->connect_port = a2port(fwdargs[3].arg); + break; + default: + i = 0; /* failure */ + } + + free(p); + + if (dynamicfwd) { + if (!(i == 1 || i == 2)) + goto fail_free; + } else { + if (!(i == 3 || i == 4)) { + if (fwd->connect_path == NULL && + fwd->listen_path == NULL) + goto fail_free; + } + if (fwd->connect_port <= 0 && fwd->connect_path == NULL) + goto fail_free; + } + + if ((fwd->listen_port < 0 && fwd->listen_path == NULL) || + (!remotefwd && fwd->listen_port == 0)) + goto fail_free; + if (fwd->connect_host != NULL && + strlen(fwd->connect_host) >= NI_MAXHOST) + goto fail_free; + /* + * XXX - if connecting to a remote socket, max sun len may not + * match this host + */ + if (fwd->connect_path != NULL && + strlen(fwd->connect_path) >= PATH_MAX_SUN) + goto fail_free; + if (fwd->listen_host != NULL && + strlen(fwd->listen_host) >= NI_MAXHOST) + goto fail_free; + if (fwd->listen_path != NULL && + strlen(fwd->listen_path) >= PATH_MAX_SUN) + goto fail_free; + + return (i); + + fail_free: + free(fwd->connect_host); + fwd->connect_host = NULL; + free(fwd->connect_path); + fwd->connect_path = NULL; + free(fwd->listen_host); + fwd->listen_host = NULL; + free(fwd->listen_path); + fwd->listen_path = NULL; + return (0); +} + +int +parse_jump(const char *s, Options *o, int active) +{ + char *orig, *sdup, *cp; + char *host = NULL, *user = NULL; + int r, ret = -1, port = -1, first; + + active &= o->proxy_command == NULL && o->jump_host == NULL; + + orig = sdup = xstrdup(s); + + /* Remove comment and trailing whitespace */ + if ((cp = strchr(orig, '#')) != NULL) + *cp = '\0'; + rtrim(orig); + + first = active; + do { + if (strcasecmp(s, "none") == 0) + break; + if ((cp = strrchr(sdup, ',')) == NULL) + cp = sdup; /* last */ + else + *cp++ = '\0'; + + if (first) { + /* First argument and configuration is active */ + r = parse_ssh_uri(cp, &user, &host, &port); + if (r == -1 || (r == 1 && + parse_user_host_port(cp, &user, &host, &port) != 0)) + goto out; + } else { + /* Subsequent argument or inactive configuration */ + r = parse_ssh_uri(cp, NULL, NULL, NULL); + if (r == -1 || (r == 1 && + parse_user_host_port(cp, NULL, NULL, NULL) != 0)) + goto out; + } + first = 0; /* only check syntax for subsequent hosts */ + } while (cp != sdup); + /* success */ + if (active) { + if (strcasecmp(s, "none") == 0) { + o->jump_host = xstrdup("none"); + o->jump_port = 0; + } else { + o->jump_user = user; + o->jump_host = host; + o->jump_port = port; + o->proxy_command = xstrdup("none"); + user = host = NULL; + if ((cp = strrchr(s, ',')) != NULL && cp != s) { + o->jump_extra = xstrdup(s); + o->jump_extra[cp - s] = '\0'; + } + } + } + ret = 0; + out: + free(orig); + free(user); + free(host); + return ret; +} + +int +parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp) +{ + char *user = NULL, *host = NULL, *path = NULL; + int r, port; + + r = parse_uri("ssh", uri, &user, &host, &port, &path); + if (r == 0 && path != NULL) + r = -1; /* path not allowed */ + if (r == 0) { + if (userp != NULL) { + *userp = user; + user = NULL; + } + if (hostp != NULL) { + *hostp = host; + host = NULL; + } + if (portp != NULL) + *portp = port; + } + free(user); + free(host); + free(path); + return r; +} + +/* XXX the following is a near-vebatim copy from servconf.c; refactor */ +static const char * +fmt_multistate_int(int val, const struct multistate *m) +{ + u_int i; + + for (i = 0; m[i].key != NULL; i++) { + if (m[i].value == val) + return m[i].key; + } + return "UNKNOWN"; +} + +static const char * +fmt_intarg(OpCodes code, int val) +{ + if (val == -1) + return "unset"; + switch (code) { + case oAddressFamily: + return fmt_multistate_int(val, multistate_addressfamily); + case oVerifyHostKeyDNS: + case oUpdateHostkeys: + return fmt_multistate_int(val, multistate_yesnoask); + case oStrictHostKeyChecking: + return fmt_multistate_int(val, multistate_strict_hostkey); + case oControlMaster: + return fmt_multistate_int(val, multistate_controlmaster); + case oTunnel: + return fmt_multistate_int(val, multistate_tunnel); + case oRequestTTY: + return fmt_multistate_int(val, multistate_requesttty); + case oSessionType: + return fmt_multistate_int(val, multistate_sessiontype); + case oCanonicalizeHostname: + return fmt_multistate_int(val, multistate_canonicalizehostname); + case oAddKeysToAgent: + return fmt_multistate_int(val, multistate_yesnoaskconfirm); + case oPubkeyAuthentication: + return fmt_multistate_int(val, multistate_pubkey_auth); + case oFingerprintHash: + return ssh_digest_alg_name(val); + default: + switch (val) { + case 0: + return "no"; + case 1: + return "yes"; + default: + return "UNKNOWN"; + } + } +} + +static const char * +lookup_opcode_name(OpCodes code) +{ + u_int i; + + for (i = 0; keywords[i].name != NULL; i++) + if (keywords[i].opcode == code) + return(keywords[i].name); + return "UNKNOWN"; +} + +static void +dump_cfg_int(OpCodes code, int val) +{ + printf("%s %d\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_fmtint(OpCodes code, int val) +{ + printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); +} + +static void +dump_cfg_string(OpCodes code, const char *val) +{ + if (val == NULL) + return; + printf("%s %s\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_strarray(OpCodes code, u_int count, char **vals) +{ + u_int i; + + for (i = 0; i < count; i++) + printf("%s %s\n", lookup_opcode_name(code), vals[i]); +} + +static void +dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals) +{ + u_int i; + + printf("%s", lookup_opcode_name(code)); + if (count == 0) + printf(" none"); + for (i = 0; i < count; i++) + printf(" %s", vals[i]); + printf("\n"); +} + +static void +dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds) +{ + const struct Forward *fwd; + u_int i; + + /* oDynamicForward */ + for (i = 0; i < count; i++) { + fwd = &fwds[i]; + if (code == oDynamicForward && fwd->connect_host != NULL && + strcmp(fwd->connect_host, "socks") != 0) + continue; + if (code == oLocalForward && fwd->connect_host != NULL && + strcmp(fwd->connect_host, "socks") == 0) + continue; + printf("%s", lookup_opcode_name(code)); + if (fwd->listen_port == PORT_STREAMLOCAL) + printf(" %s", fwd->listen_path); + else if (fwd->listen_host == NULL) + printf(" %d", fwd->listen_port); + else { + printf(" [%s]:%d", + fwd->listen_host, fwd->listen_port); + } + if (code != oDynamicForward) { + if (fwd->connect_port == PORT_STREAMLOCAL) + printf(" %s", fwd->connect_path); + else if (fwd->connect_host == NULL) + printf(" %d", fwd->connect_port); + else { + printf(" [%s]:%d", + fwd->connect_host, fwd->connect_port); + } + } + printf("\n"); + } +} + +void +dump_client_config(Options *o, const char *host) +{ + int i, r; + char buf[8], *all_key; + + /* + * Expand HostKeyAlgorithms name lists. This isn't handled in + * fill_default_options() like the other algorithm lists because + * the host key algorithms are by default dynamically chosen based + * on the host's keys found in known_hosts. + */ + all_key = sshkey_alg_list(0, 0, 1, ','); + if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(), + all_key)) != 0) + fatal_fr(r, "expand HostKeyAlgorithms"); + free(all_key); + + /* Most interesting options first: user, host, port */ + dump_cfg_string(oUser, o->user); + dump_cfg_string(oHostname, host); + dump_cfg_int(oPort, o->port); + + /* Flag options */ + dump_cfg_fmtint(oAddressFamily, o->address_family); + dump_cfg_fmtint(oBatchMode, o->batch_mode); + dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local); + dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname); + dump_cfg_fmtint(oCheckHostIP, o->check_host_ip); + dump_cfg_fmtint(oCompression, o->compression); + dump_cfg_fmtint(oControlMaster, o->control_master); + dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign); + dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings); + dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure); + dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash); + dump_cfg_fmtint(oForwardX11, o->forward_x11); + dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted); + dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); +#ifdef GSSAPI + dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); +#endif /* GSSAPI */ + dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); + dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); + dump_cfg_fmtint(oIdentitiesOnly, o->identities_only); + dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication); + dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost); + dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command); + dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass); + dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication); + dump_cfg_fmtint(oRequestTTY, o->request_tty); + dump_cfg_fmtint(oSessionType, o->session_type); + dump_cfg_fmtint(oStdinNull, o->stdin_null); + dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication); + dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); + dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking); + dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive); + dump_cfg_fmtint(oTunnel, o->tun_open); + dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns); + dump_cfg_fmtint(oVisualHostKey, o->visual_host_key); + dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys); + + /* Integer options */ + dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots); + dump_cfg_int(oConnectionAttempts, o->connection_attempts); + dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout); + dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts); + dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max); + dump_cfg_int(oServerAliveInterval, o->server_alive_interval); + + /* String options */ + dump_cfg_string(oBindAddress, o->bind_address); + dump_cfg_string(oBindInterface, o->bind_interface); + dump_cfg_string(oCiphers, o->ciphers); + dump_cfg_string(oControlPath, o->control_path); + dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms); + dump_cfg_string(oHostKeyAlias, o->host_key_alias); + dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos); + dump_cfg_string(oIdentityAgent, o->identity_agent); + dump_cfg_string(oIgnoreUnknown, o->ignored_unknown); + dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices); + dump_cfg_string(oKexAlgorithms, o->kex_algorithms); + dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms); + dump_cfg_string(oLocalCommand, o->local_command); + dump_cfg_string(oRemoteCommand, o->remote_command); + dump_cfg_string(oLogLevel, log_level_name(o->log_level)); + dump_cfg_string(oMacs, o->macs); +#ifdef ENABLE_PKCS11 + dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); +#endif + dump_cfg_string(oSecurityKeyProvider, o->sk_provider); + dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); + dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos); + dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); + dump_cfg_string(oXAuthLocation, o->xauth_location); + dump_cfg_string(oKnownHostsCommand, o->known_hosts_command); + + /* Forwards */ + dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards); + dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards); + dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards); + + /* String array options */ + dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files); + dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains); + dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files); + dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles); + dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles); + dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env); + dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv); + dump_cfg_strarray_oneline(oLogVerbose, + o->num_log_verbose, o->log_verbose); + + /* Special cases */ + + /* PermitRemoteOpen */ + if (o->num_permitted_remote_opens == 0) + printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen)); + else + dump_cfg_strarray_oneline(oPermitRemoteOpen, + o->num_permitted_remote_opens, o->permitted_remote_opens); + + /* AddKeysToAgent */ + if (o->add_keys_to_agent_lifespan <= 0) + dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent); + else { + printf("addkeystoagent%s %d\n", + o->add_keys_to_agent == 3 ? " confirm" : "", + o->add_keys_to_agent_lifespan); + } + + /* oForwardAgent */ + if (o->forward_agent_sock_path == NULL) + dump_cfg_fmtint(oForwardAgent, o->forward_agent); + else + dump_cfg_string(oForwardAgent, o->forward_agent_sock_path); + + /* oConnectTimeout */ + if (o->connection_timeout == -1) + printf("connecttimeout none\n"); + else + dump_cfg_int(oConnectTimeout, o->connection_timeout); + + /* oTunnelDevice */ + printf("tunneldevice"); + if (o->tun_local == SSH_TUNID_ANY) + printf(" any"); + else + printf(" %d", o->tun_local); + if (o->tun_remote == SSH_TUNID_ANY) + printf(":any"); + else + printf(":%d", o->tun_remote); + printf("\n"); + + /* oCanonicalizePermittedCNAMEs */ + printf("canonicalizePermittedcnames"); + if (o->num_permitted_cnames == 0) + printf(" none"); + for (i = 0; i < o->num_permitted_cnames; i++) { + printf(" %s:%s", o->permitted_cnames[i].source_list, + o->permitted_cnames[i].target_list); + } + printf("\n"); + + /* oControlPersist */ + if (o->control_persist == 0 || o->control_persist_timeout == 0) + dump_cfg_fmtint(oControlPersist, o->control_persist); + else + dump_cfg_int(oControlPersist, o->control_persist_timeout); + + /* oEscapeChar */ + if (o->escape_char == SSH_ESCAPECHAR_NONE) + printf("escapechar none\n"); + else { + vis(buf, o->escape_char, VIS_WHITE, 0); + printf("escapechar %s\n", buf); + } + + /* oIPQoS */ + printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); + printf("%s\n", iptos2str(o->ip_qos_bulk)); + + /* oRekeyLimit */ + printf("rekeylimit %llu %d\n", + (unsigned long long)o->rekey_limit, o->rekey_interval); + + /* oStreamLocalBindMask */ + printf("streamlocalbindmask 0%o\n", + o->fwd_opts.streamlocal_bind_mask); + + /* oLogFacility */ + printf("syslogfacility %s\n", log_facility_name(o->log_facility)); + + /* oProxyCommand / oProxyJump */ + if (o->jump_host == NULL) + dump_cfg_string(oProxyCommand, o->proxy_command); + else { + /* Check for numeric addresses */ + i = strchr(o->jump_host, ':') != NULL || + strspn(o->jump_host, "1234567890.") == strlen(o->jump_host); + snprintf(buf, sizeof(buf), "%d", o->jump_port); + printf("proxyjump %s%s%s%s%s%s%s%s%s\n", + /* optional additional jump spec */ + o->jump_extra == NULL ? "" : o->jump_extra, + o->jump_extra == NULL ? "" : ",", + /* optional user */ + o->jump_user == NULL ? "" : o->jump_user, + o->jump_user == NULL ? "" : "@", + /* opening [ if hostname is numeric */ + i ? "[" : "", + /* mandatory hostname */ + o->jump_host, + /* closing ] if hostname is numeric */ + i ? "]" : "", + /* optional port number */ + o->jump_port <= 0 ? "" : ":", + o->jump_port <= 0 ? "" : buf); + } +} diff --git a/aosp/external/openssh/readconf.h b/aosp/external/openssh/readconf.h new file mode 100644 index 0000000000000000000000000000000000000000..ded13c943d3f993da682636aab0459d288b7028a --- /dev/null +++ b/aosp/external/openssh/readconf.h @@ -0,0 +1,244 @@ +/* $OpenBSD: readconf.h,v 1.146 2021/12/19 22:14:47 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for reading the configuration file. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef READCONF_H +#define READCONF_H + +/* Data structure for representing option data. */ + +#define SSH_MAX_HOSTS_FILES 32 +#define MAX_CANON_DOMAINS 32 +#define PATH_MAX_SUN (sizeof((struct sockaddr_un *)0)->sun_path) + +struct allowed_cname { + char *source_list; + char *target_list; +}; + +typedef struct { + int forward_agent; /* Forward authentication agent. */ + char *forward_agent_sock_path; /* Optional path of the agent. */ + int forward_x11; /* Forward X11 display. */ + int forward_x11_timeout; /* Expiration for Cookies */ + int forward_x11_trusted; /* Trust Forward X11 display. */ + int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ + char *xauth_location; /* Location for xauth program */ + struct ForwardOptions fwd_opts; /* forwarding options */ + int pubkey_authentication; /* Try ssh2 pubkey authentication. */ + int hostbased_authentication; /* ssh2's rhosts_rsa */ + int gss_authentication; /* Try GSS authentication */ + int gss_deleg_creds; /* Delegate GSS credentials */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ + char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ + int batch_mode; /* Batch mode: do not ask for passwords. */ + int check_host_ip; /* Also keep track of keys for IP address */ + int strict_host_key_checking; /* Strict host key checking. */ + int compression; /* Compress packets in both directions. */ + int tcp_keep_alive; /* Set SO_KEEPALIVE. */ + int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ + int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for logging. */ + u_int num_log_verbose; /* Verbose log overrides */ + char **log_verbose; + int port; /* Port to connect. */ + int address_family; + int connection_attempts; /* Max attempts (seconds) before + * giving up */ + int connection_timeout; /* Max time (seconds) before + * aborting connection attempt */ + int number_of_password_prompts; /* Max number of password + * prompts. */ + char *ciphers; /* SSH2 ciphers in order of preference. */ + char *macs; /* SSH2 macs in order of preference. */ + char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ + char *ca_sign_algorithms; /* Allowed CA signature algorithms */ + char *hostname; /* Real host to connect. */ + char *host_key_alias; /* hostname alias for .ssh/known_hosts */ + char *proxy_command; /* Proxy command for connecting the host. */ + char *user; /* User to log in as. */ + int escape_char; /* Escape character; -2 = none */ + + u_int num_system_hostfiles; /* Paths for /etc/ssh/ssh_known_hosts */ + char *system_hostfiles[SSH_MAX_HOSTS_FILES]; + u_int num_user_hostfiles; /* Path for $HOME/.ssh/known_hosts */ + char *user_hostfiles[SSH_MAX_HOSTS_FILES]; + char *preferred_authentications; + char *bind_address; /* local socket address for connection to sshd */ + char *bind_interface; /* local interface for bind address */ + char *pkcs11_provider; /* PKCS#11 provider */ + char *sk_provider; /* Security key provider */ + int verify_host_key_dns; /* Verify host key using DNS */ + + int num_identity_files; /* Number of files for RSA/DSA identities. */ + char *identity_files[SSH_MAX_IDENTITY_FILES]; + int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; + struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; + + int num_certificate_files; /* Number of extra certificates for ssh. */ + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; + + int add_keys_to_agent; + int add_keys_to_agent_lifespan; + char *identity_agent; /* Optional path to ssh-agent socket */ + + /* Local TCP/IP forward requests. */ + int num_local_forwards; + struct Forward *local_forwards; + + /* Remote TCP/IP forward requests. */ + int num_remote_forwards; + struct Forward *remote_forwards; + int clear_forwardings; + + /* Restrict remote dynamic forwarding */ + char **permitted_remote_opens; + u_int num_permitted_remote_opens; + + /* stdio forwarding (-W) host and port */ + char *stdio_forward_host; + int stdio_forward_port; + + int enable_ssh_keysign; + int64_t rekey_limit; + int rekey_interval; + int no_host_authentication_for_localhost; + int identities_only; + int server_alive_interval; + int server_alive_count_max; + + int num_send_env; + char **send_env; + int num_setenv; + char **setenv; + + char *control_path; + int control_master; + int control_persist; /* ControlPersist flag */ + int control_persist_timeout; /* ControlPersist timeout (seconds) */ + + int hash_known_hosts; + + int tun_open; /* tun(4) */ + int tun_local; /* force tun device (optional) */ + int tun_remote; /* force tun device (optional) */ + + char *local_command; + int permit_local_command; + char *remote_command; + int visual_host_key; + + int request_tty; + int session_type; + int stdin_null; + int fork_after_authentication; + + int proxy_use_fdpass; + + int num_canonical_domains; + char *canonical_domains[MAX_CANON_DOMAINS]; + int canonicalize_hostname; + int canonicalize_max_dots; + int canonicalize_fallback_local; + int num_permitted_cnames; + struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS]; + + char *revoked_host_keys; + + int fingerprint_hash; + + int update_hostkeys; /* one of SSH_UPDATE_HOSTKEYS_* */ + + char *hostbased_accepted_algos; + char *pubkey_accepted_algos; + + char *jump_user; + char *jump_host; + int jump_port; + char *jump_extra; + + char *known_hosts_command; + + char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ +} Options; + +#define SSH_PUBKEY_AUTH_NO 0x00 +#define SSH_PUBKEY_AUTH_UNBOUND 0x01 +#define SSH_PUBKEY_AUTH_HBOUND 0x02 +#define SSH_PUBKEY_AUTH_ALL 0x03 + +#define SSH_CANONICALISE_NO 0 +#define SSH_CANONICALISE_YES 1 +#define SSH_CANONICALISE_ALWAYS 2 + +#define SSHCTL_MASTER_NO 0 +#define SSHCTL_MASTER_YES 1 +#define SSHCTL_MASTER_AUTO 2 +#define SSHCTL_MASTER_ASK 3 +#define SSHCTL_MASTER_AUTO_ASK 4 + +#define REQUEST_TTY_AUTO 0 +#define REQUEST_TTY_NO 1 +#define REQUEST_TTY_YES 2 +#define REQUEST_TTY_FORCE 3 + +#define SESSION_TYPE_NONE 0 +#define SESSION_TYPE_SUBSYSTEM 1 +#define SESSION_TYPE_DEFAULT 2 + +#define SSHCONF_CHECKPERM 1 /* check permissions on config file */ +#define SSHCONF_USERCONF 2 /* user provided config file not system */ +#define SSHCONF_FINAL 4 /* Final pass over config, after canon. */ +#define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */ + +#define SSH_UPDATE_HOSTKEYS_NO 0 +#define SSH_UPDATE_HOSTKEYS_YES 1 +#define SSH_UPDATE_HOSTKEYS_ASK 2 + +#define SSH_STRICT_HOSTKEY_OFF 0 +#define SSH_STRICT_HOSTKEY_NEW 1 +#define SSH_STRICT_HOSTKEY_YES 2 +#define SSH_STRICT_HOSTKEY_ASK 3 + +const char *kex_default_pk_alg(void); +char *ssh_connection_hash(const char *thishost, const char *host, + const char *portstr, const char *user); +void initialize_options(Options *); +int fill_default_options(Options *); +void fill_default_options_for_canonicalization(Options *); +void free_options(Options *o); +int process_config_line(Options *, struct passwd *, const char *, + const char *, char *, const char *, int, int *, int); +int read_config_file(const char *, struct passwd *, const char *, + const char *, Options *, int, int *); +int parse_forward(struct Forward *, const char *, int, int); +int parse_jump(const char *, Options *, int); +int parse_ssh_uri(const char *, char **, char **, int *); +int default_ssh_port(void); +int option_clear_or_none(const char *); +int config_has_permitted_cnames(Options *); +void dump_client_config(Options *o, const char *host); + +void add_local_forward(Options *, const struct Forward *); +void add_remote_forward(Options *, const struct Forward *); +void add_identity_file(Options *, const char *, const char *, int); +void add_certificate_file(Options *, const char *, int); + +#endif /* READCONF_H */ diff --git a/aosp/external/openssh/readpass.c b/aosp/external/openssh/readpass.c new file mode 100644 index 0000000000000000000000000000000000000000..39af25c887298a2da3553c3b0f5914bace77757a --- /dev/null +++ b/aosp/external/openssh/readpass.c @@ -0,0 +1,331 @@ +/* $OpenBSD: readpass.c,v 1.69 2021/07/23 05:56:47 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "misc.h" +#include "pathnames.h" +#include "log.h" +#include "ssh.h" +#include "uidswap.h" + +static char * +ssh_askpass(char *askpass, const char *msg, const char *env_hint) +{ + pid_t pid, ret; + size_t len; + char *pass; + int p[2], status; + char buf[1024]; + void (*osigchld)(int); + + if (fflush(stdout) != 0) + error_f("fflush: %s", strerror(errno)); + if (askpass == NULL) + fatal("internal error: askpass undefined"); + if (pipe(p) == -1) { + error_f("pipe: %s", strerror(errno)); + return NULL; + } + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + if ((pid = fork()) == -1) { + error_f("fork: %s", strerror(errno)); + ssh_signal(SIGCHLD, osigchld); + return NULL; + } + if (pid == 0) { + close(p[0]); + if (dup2(p[1], STDOUT_FILENO) == -1) + fatal_f("dup2: %s", strerror(errno)); + if (env_hint != NULL) + setenv("SSH_ASKPASS_PROMPT", env_hint, 1); + execlp(askpass, askpass, msg, (char *)NULL); + fatal_f("exec(%s): %s", askpass, strerror(errno)); + } + close(p[1]); + + len = 0; + do { + ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len); + + if (r == -1 && errno == EINTR) + continue; + if (r <= 0) + break; + len += r; + } while (sizeof(buf) - 1 - len > 0); + buf[len] = '\0'; + + close(p[0]); + while ((ret = waitpid(pid, &status, 0)) == -1) + if (errno != EINTR) + break; + ssh_signal(SIGCHLD, osigchld); + if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + explicit_bzero(buf, sizeof(buf)); + return NULL; + } + + buf[strcspn(buf, "\r\n")] = '\0'; + pass = xstrdup(buf); + explicit_bzero(buf, sizeof(buf)); + return pass; +} + +/* private/internal read_passphrase flags */ +#define RP_ASK_PERMISSION 0x8000 /* pass hint to askpass for confirm UI */ + +/* + * Reads a passphrase from /dev/tty with echo turned off/on. Returns the + * passphrase (allocated with xmalloc). Exits if EOF is encountered. If + * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no + * tty is or askpass program is available + */ +char * +read_passphrase(const char *prompt, int flags) +{ + char cr = '\r', *askpass = NULL, *ret, buf[1024]; + int rppflags, ttyfd, use_askpass = 0, allow_askpass = 0; + const char *askpass_hint = NULL; + const char *s; + + if ((s = getenv("DISPLAY")) != NULL) + allow_askpass = *s != '\0'; + if ((s = getenv(SSH_ASKPASS_REQUIRE_ENV)) != NULL) { + if (strcasecmp(s, "force") == 0) { + use_askpass = 1; + allow_askpass = 1; + } else if (strcasecmp(s, "prefer") == 0) + use_askpass = allow_askpass; + else if (strcasecmp(s, "never") == 0) + allow_askpass = 0; + } + + rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF; + if (use_askpass) + debug_f("requested to askpass"); + else if (flags & RP_USE_ASKPASS) + use_askpass = 1; + else if (flags & RP_ALLOW_STDIN) { + if (!isatty(STDIN_FILENO)) { + debug_f("stdin is not a tty"); + use_askpass = 1; + } + } else { + rppflags |= RPP_REQUIRE_TTY; + ttyfd = open(_PATH_TTY, O_RDWR); + if (ttyfd >= 0) { + /* + * If we're on a tty, ensure that show the prompt at + * the beginning of the line. This will hopefully + * clobber any password characters the user has + * optimistically typed before echo is disabled. + */ + (void)write(ttyfd, &cr, 1); + close(ttyfd); + } else { + debug_f("can't open %s: %s", _PATH_TTY, + strerror(errno)); + use_askpass = 1; + } + } + + if ((flags & RP_USE_ASKPASS) && !allow_askpass) + return (flags & RP_ALLOW_EOF) ? NULL : xstrdup(""); + + if (use_askpass && allow_askpass) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = _PATH_SSH_ASKPASS_DEFAULT; + if ((flags & RP_ASK_PERMISSION) != 0) + askpass_hint = "confirm"; + if ((ret = ssh_askpass(askpass, prompt, askpass_hint)) == NULL) + if (!(flags & RP_ALLOW_EOF)) + return xstrdup(""); + return ret; + } + + if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) { + if (flags & RP_ALLOW_EOF) + return NULL; + return xstrdup(""); + } + + ret = xstrdup(buf); + explicit_bzero(buf, sizeof(buf)); + return ret; +} + +int +ask_permission(const char *fmt, ...) +{ + va_list args; + char *p, prompt[1024]; + int allowed = 0; + + va_start(args, fmt); + vsnprintf(prompt, sizeof(prompt), fmt, args); + va_end(args); + + p = read_passphrase(prompt, + RP_USE_ASKPASS|RP_ALLOW_EOF|RP_ASK_PERMISSION); + if (p != NULL) { + /* + * Accept empty responses and responses consisting + * of the word "yes" as affirmative. + */ + if (*p == '\0' || *p == '\n' || + strcasecmp(p, "yes") == 0) + allowed = 1; + free(p); + } + + return (allowed); +} + +static void +writemsg(const char *msg) +{ + (void)write(STDERR_FILENO, "\r", 1); + (void)write(STDERR_FILENO, msg, strlen(msg)); + (void)write(STDERR_FILENO, "\r\n", 2); +} + +struct notifier_ctx { + pid_t pid; + void (*osigchld)(int); +}; + +struct notifier_ctx * +notify_start(int force_askpass, const char *fmt, ...) +{ + va_list args; + char *prompt = NULL; + pid_t pid = -1; + void (*osigchld)(int) = NULL; + const char *askpass, *s; + struct notifier_ctx *ret = NULL; + + va_start(args, fmt); + xvasprintf(&prompt, fmt, args); + va_end(args); + + if (fflush(NULL) != 0) + error_f("fflush: %s", strerror(errno)); + if (!force_askpass && isatty(STDERR_FILENO)) { + writemsg(prompt); + goto out_ctx; + } + if ((askpass = getenv("SSH_ASKPASS")) == NULL) + askpass = _PATH_SSH_ASKPASS_DEFAULT; + if (*askpass == '\0') { + debug3_f("cannot notify: no askpass"); + goto out; + } + if (getenv("DISPLAY") == NULL && + ((s = getenv(SSH_ASKPASS_REQUIRE_ENV)) == NULL || + strcmp(s, "force") != 0)) { + debug3_f("cannot notify: no display"); + goto out; + } + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + if ((pid = fork()) == -1) { + error_f("fork: %s", strerror(errno)); + ssh_signal(SIGCHLD, osigchld); + free(prompt); + return NULL; + } + if (pid == 0) { + if (stdfd_devnull(1, 1, 0) == -1) + fatal_f("stdfd_devnull failed"); + closefrom(STDERR_FILENO + 1); + setenv("SSH_ASKPASS_PROMPT", "none", 1); /* hint to UI */ + execlp(askpass, askpass, prompt, (char *)NULL); + error_f("exec(%s): %s", askpass, strerror(errno)); + _exit(1); + /* NOTREACHED */ + } + out_ctx: + if ((ret = calloc(1, sizeof(*ret))) == NULL) { + kill(pid, SIGTERM); + fatal_f("calloc failed"); + } + ret->pid = pid; + ret->osigchld = osigchld; + out: + free(prompt); + return ret; +} + +void +notify_complete(struct notifier_ctx *ctx, const char *fmt, ...) +{ + int ret; + char *msg = NULL; + va_list args; + + if (ctx != NULL && fmt != NULL && ctx->pid == -1) { + /* + * notify_start wrote to stderr, so send conclusion message + * there too + */ + va_start(args, fmt); + xvasprintf(&msg, fmt, args); + va_end(args); + writemsg(msg); + free(msg); + } + + if (ctx == NULL || ctx->pid <= 0) { + free(ctx); + return; + } + kill(ctx->pid, SIGTERM); + while ((ret = waitpid(ctx->pid, NULL, 0)) == -1) { + if (errno != EINTR) + break; + } + if (ret == -1) + fatal_f("waitpid: %s", strerror(errno)); + ssh_signal(SIGCHLD, ctx->osigchld); + free(ctx); +} diff --git a/aosp/external/openssh/regress/Makefile b/aosp/external/openssh/regress/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0554c1ac9bc7f233b88b4edd47e719d8fa31ebbb --- /dev/null +++ b/aosp/external/openssh/regress/Makefile @@ -0,0 +1,275 @@ +# $OpenBSD: Makefile,v 1.120 2022/01/06 21:46:56 dtucker Exp $ + +tests: prep file-tests t-exec unit + +REGRESS_TARGETS= t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 + +# File based tests +file-tests: $(REGRESS_TARGETS) + +# Interop tests are not run by default +interop interop-tests: t-exec-interop + +prep: + test "x${USE_VALGRIND}" = "x" || mkdir -p $(OBJ)/valgrind-out + +clean: + for F in $(CLEANFILES); do rm -f $(OBJ)$$F; done + rm -rf $(OBJ).putty + +distclean: clean + +LTESTS= connect \ + proxy-connect \ + sshfp-connect \ + connect-privsep \ + connect-uri \ + proto-version \ + proto-mismatch \ + exit-status \ + exit-status-signal \ + envpass \ + transfer \ + banner \ + rekey \ + dhgex \ + stderr-data \ + stderr-after-eof \ + broken-pipe \ + try-ciphers \ + yes-head \ + login-timeout \ + agent \ + agent-getpeereid \ + agent-timeout \ + agent-ptrace \ + agent-subprocess \ + keyscan \ + keygen-change \ + keygen-comment \ + keygen-convert \ + keygen-knownhosts \ + keygen-moduli \ + keygen-sshfp \ + key-options \ + scp \ + scp3 \ + scp-uri \ + sftp \ + sftp-chroot \ + sftp-cmds \ + sftp-badcmds \ + sftp-batch \ + sftp-glob \ + sftp-perm \ + sftp-uri \ + reconfigure \ + dynamic-forward \ + forwarding \ + multiplex \ + reexec \ + brokenkeys \ + sshcfgparse \ + cfgparse \ + cfgmatch \ + cfgmatchlisten \ + percent \ + addrmatch \ + localcommand \ + forcecommand \ + portnum \ + keytype \ + kextype \ + cert-hostkey \ + cert-userkey \ + host-expand \ + keys-command \ + forward-control \ + integrity \ + krl \ + multipubkey \ + limit-keytype \ + hostkey-agent \ + hostkey-rotate \ + principals-command \ + cert-file \ + cfginclude \ + servcfginclude \ + allow-deny-users \ + authinfo \ + sshsig \ + knownhosts \ + knownhosts-command \ + agent-restrict \ + hostbased + +INTEROP_TESTS= putty-transfer putty-ciphers putty-kex conch-ciphers +#INTEROP_TESTS+=ssh-com ssh-com-client ssh-com-keygen ssh-com-sftp + +EXTRA_TESTS= agent-pkcs11 +#EXTRA_TESTS+= cipher-speed + +USERNAME= ${LOGNAME} +CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ + authorized_keys_${USERNAME}.* \ + authorized_principals_${USERNAME} \ + banner.in banner.out cert_host_key* cert_user_key* \ + copy.1 copy.2 data ed25519-agent ed25519-agent* \ + ed25519-agent.pub ed25519 ed25519.pub empty.in \ + expect failed-regress.log failed-ssh.log failed-sshd.log \ + hkr.* host.ecdsa-sha2-nistp256 host.ecdsa-sha2-nistp384 \ + host.ecdsa-sha2-nistp521 host.ssh-dss host.ssh-ed25519 \ + host.ssh-rsa host_ca_key* host_krl_* host_revoked_* key.* \ + key.dsa-* key.ecdsa-* key.ed25519-512 \ + key.ed25519-512.pub key.rsa-* keys-command-args kh.* askpass \ + known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \ + modpipe netcat no_identity_config \ + pidfile putty.rsa2 ready regress.log remote_pid \ + revoked-* rsa rsa-agent rsa-agent.pub rsa.pub rsa_ssh2_cr.prv \ + rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ + scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ + sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ + ssh-rsa_oldfmt knownhosts_command \ + ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ + ssh_proxy_* sshd.log sshd_config sshd_config.* \ + sshd_config.* sshd_proxy sshd_proxy.* sshd_proxy_bak \ + sshd_proxy_orig t10.out t10.out.pub t12.out t12.out.pub \ + t2.out t3.out t6.out1 t6.out2 t7.out t7.out.pub \ + t8.out t8.out.pub t9.out t9.out.pub testdata \ + user_*key* user_ca* user_key* + +# Enable all malloc(3) randomisations and checks +TEST_ENV= "MALLOC_OPTIONS=CFGJRSUX" + +TEST_SSH_SSHKEYGEN?=ssh-keygen + +CPPFLAGS=-I.. + +t1: + ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/rsa_ssh2.prv | diff - ${.CURDIR}/rsa_openssh.prv + tr '\n' '\r' <${.CURDIR}/rsa_ssh2.prv > ${.OBJDIR}/rsa_ssh2_cr.prv + ${TEST_SSH_SSHKEYGEN} -if ${.OBJDIR}/rsa_ssh2_cr.prv | diff - ${.CURDIR}/rsa_openssh.prv + awk '{print $$0 "\r"}' ${.CURDIR}/rsa_ssh2.prv > ${.OBJDIR}/rsa_ssh2_crnl.prv + ${TEST_SSH_SSHKEYGEN} -if ${.OBJDIR}/rsa_ssh2_crnl.prv | diff - ${.CURDIR}/rsa_openssh.prv + +t2: + cat ${.CURDIR}/rsa_openssh.prv > $(OBJ)/t2.out + chmod 600 $(OBJ)/t2.out + ${TEST_SSH_SSHKEYGEN} -yf $(OBJ)/t2.out | diff - ${.CURDIR}/rsa_openssh.pub + +t3: + ${TEST_SSH_SSHKEYGEN} -ef ${.CURDIR}/rsa_openssh.pub >$(OBJ)/t3.out + ${TEST_SSH_SSHKEYGEN} -if $(OBJ)/t3.out | diff - ${.CURDIR}/rsa_openssh.pub + +t4: + ${TEST_SSH_SSHKEYGEN} -E md5 -lf ${.CURDIR}/rsa_openssh.pub |\ + awk '{print $$2}' | diff - ${.CURDIR}/t4.ok + +t5: + ${TEST_SSH_SSHKEYGEN} -Bf ${.CURDIR}/rsa_openssh.pub |\ + awk '{print $$2}' | diff - ${.CURDIR}/t5.ok + +t6: + ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/dsa_ssh2.prv > $(OBJ)/t6.out1 + ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/dsa_ssh2.pub > $(OBJ)/t6.out2 + chmod 600 $(OBJ)/t6.out1 + ${TEST_SSH_SSHKEYGEN} -yf $(OBJ)/t6.out1 | diff - $(OBJ)/t6.out2 + +$(OBJ)/t7.out: + ${TEST_SSH_SSHKEYGEN} -q -t rsa -N '' -f $@ + +t7: $(OBJ)/t7.out + ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t7.out > /dev/null + ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t7.out > /dev/null + +$(OBJ)/t8.out: + ${TEST_SSH_SSHKEYGEN} -q -t dsa -N '' -f $@ + +t8: $(OBJ)/t8.out + ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t8.out > /dev/null + ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t8.out > /dev/null + +$(OBJ)/t9.out: + ! ${TEST_SSH_SSH} -Q key-plain | grep ecdsa >/dev/null || \ + ${TEST_SSH_SSHKEYGEN} -q -t ecdsa -N '' -f $@ + +t9: $(OBJ)/t9.out + ! ${TEST_SSH_SSH} -Q key-plain | grep ecdsa >/dev/null || \ + ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t9.out > /dev/null + ! ${TEST_SSH_SSH} -Q key-plain | grep ecdsa >/dev/null || \ + ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t9.out > /dev/null + + +$(OBJ)/t10.out: + ${TEST_SSH_SSHKEYGEN} -q -t ed25519 -N '' -f $@ + +t10: $(OBJ)/t10.out + ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t10.out > /dev/null + ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t10.out > /dev/null + +t11: + ${TEST_SSH_SSHKEYGEN} -E sha256 -lf ${.CURDIR}/rsa_openssh.pub |\ + awk '{print $$2}' | diff - ${.CURDIR}/t11.ok + +$(OBJ)/t12.out: + ${TEST_SSH_SSHKEYGEN} -q -t ed25519 -N '' -C 'test-comment-1234' -f $@ + +t12: $(OBJ)/t12.out + ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t12.out.pub | grep test-comment-1234 >/dev/null + +t-exec: ${LTESTS:=.sh} + @if [ "x$?" = "x" ]; then exit 0; fi; \ + for TEST in ""$?; do \ + skip=no; \ + for t in ""$${SKIP_LTESTS}; do \ + if [ "x$${t}.sh" = "x$${TEST}" ]; then skip=yes; fi; \ + done; \ + if [ "x$${skip}" = "xno" ]; then \ + echo "run test $${TEST}" ... 1>&2; \ + (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ + else \ + echo skip test $${TEST} 1>&2; \ + fi; \ + done + +t-exec-interop: ${INTEROP_TESTS:=.sh} + @if [ "x$?" = "x" ]; then exit 0; fi; \ + for TEST in ""$?; do \ + echo "run test $${TEST}" ... 1>&2; \ + (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ + done + +t-extra: ${EXTRA_TESTS:=.sh} + @if [ "x$?" = "x" ]; then exit 0; fi; \ + for TEST in ""$?; do \ + echo "run test $${TEST}" ... 1>&2; \ + (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ + done + +# Not run by default +interop: ${INTEROP_TARGETS} + +# Unit tests, built by top-level Makefile +unit: + set -e ; if test -z "${SKIP_UNIT}" ; then \ + V="" ; \ + test "x${USE_VALGRIND}" = "x" || \ + V=${.CURDIR}/valgrind-unit.sh ; \ + $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ + $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ + -d ${.CURDIR}/unittests/sshkey/testdata ; \ + $$V ${.OBJDIR}/unittests/sshsig/test_sshsig \ + -d ${.CURDIR}/unittests/sshsig/testdata ; \ + $$V ${.OBJDIR}/unittests/authopt/test_authopt \ + -d ${.CURDIR}/unittests/authopt/testdata ; \ + $$V ${.OBJDIR}/unittests/bitmap/test_bitmap ; \ + $$V ${.OBJDIR}/unittests/conversion/test_conversion ; \ + $$V ${.OBJDIR}/unittests/kex/test_kex ; \ + $$V ${.OBJDIR}/unittests/hostkeys/test_hostkeys \ + -d ${.CURDIR}/unittests/hostkeys/testdata ; \ + $$V ${.OBJDIR}/unittests/match/test_match ; \ + $$V ${.OBJDIR}/unittests/misc/test_misc ; \ + if test "x${TEST_SSH_UTF8}" = "xyes" ; then \ + $$V ${.OBJDIR}/unittests/utf8/test_utf8 ; \ + fi \ + fi diff --git a/aosp/external/openssh/regress/README.regress b/aosp/external/openssh/regress/README.regress new file mode 100644 index 0000000000000000000000000000000000000000..ac2e8487e78e085a18f1755118087c358939f1dd --- /dev/null +++ b/aosp/external/openssh/regress/README.regress @@ -0,0 +1,161 @@ +Overview. + +$ ./configure && make tests + +You'll see some progress info. A failure will cause either the make to +abort or the driver script to report a "FATAL" failure. + +The test consists of 2 parts. The first is the file-based tests which is +driven by the Makefile, and the second is a set of network or proxycommand +based tests, which are driven by a driver script (test-exec.sh) which is +called multiple times by the Makefile. + +Failures in the first part will cause the Makefile to return an error. +Failures in the second part will print a "FATAL" message for the failed +test and continue. + +OpenBSD has a system-wide regression test suite. OpenSSH Portable's test +suite is based on OpenBSD's with modifications. + + +Environment variables. + +SKIP_UNIT: Skip unit tests. +SUDO: path to sudo/doas command, if desired. Note that some systems + (notably systems using PAM) require sudo to execute some tests. +LTESTS: Whitespace separated list of tests (filenames without the .sh + extension) to run. +SKIP_LTESTS: Whitespace separated list of tests to skip. +OBJ: used by test scripts to access build dir. +TEST_SHELL: shell used for running the test scripts. +TEST_SSH_FAIL_FATAL: set to "yes" to make any failure abort the test + currently in progress. +TEST_SSH_PORT: TCP port to be used for the listening tests. +TEST_SSH_QUIET: set to "yes" to suppress non-fatal output. +TEST_SSH_SSHD_CONFOPTS: Configuration directives to be added to sshd_config + before running each test. +TEST_SSH_SSH_CONFOPTS: Configuration directives to be added to + ssh_config before running each test. +TEST_SSH_TRACE: set to "yes" for verbose output from tests +TEST_SSH_x: path to "ssh" command under test, where x is one of + SSH, SSHD, SSHAGENT, SSHADD, SSHKEYGEN, SSHKEYSCAN, SFTP or + SFTPSERVER +USE_VALGRIND: Run the tests under valgrind memory checker. + + +Individual tests. + +You can run an individual test from the top-level Makefile, eg: +$ make tests LTESTS=agent-timeout + +If you need to manipulate the environment more you can invoke test-exec.sh +directly if you set up the path to find the binaries under test and the +test scripts themselves, for example: + +$ cd regress +$ PATH=`pwd`/..:$PATH:. TEST_SHELL=/bin/sh sh test-exec.sh `pwd` \ + agent-timeout.sh +ok agent timeout test + + +Files. + +test-exec.sh: the main test driver. Sets environment, creates config files +and keys and runs the specified test. + +At the time of writing, the individual tests are: +connect.sh: simple connect +proxy-connect.sh: proxy connect +connect-privsep.sh: proxy connect with privsep +connect-uri.sh: uri connect +proto-version.sh: sshd version with different protocol combinations +proto-mismatch.sh: protocol version mismatch +exit-status.sh: remote exit status +envpass.sh: environment passing +transfer.sh: transfer data +banner.sh: banner +rekey.sh: rekey +stderr-data.sh: stderr data transfer +stderr-after-eof.sh: stderr data after eof +broken-pipe.sh: broken pipe test +try-ciphers.sh: try ciphers +yes-head.sh: yes pipe head +login-timeout.sh: connect after login grace timeout +agent.sh: simple connect via agent +agent-getpeereid.sh: disallow agent attach from other uid +agent-timeout.sh: agent timeout test +agent-ptrace.sh: disallow agent ptrace attach +keyscan.sh: keyscan +keygen-change.sh: change passphrase for key +keygen-convert.sh: convert keys +keygen-moduli.sh: keygen moduli +key-options.sh: key options +scp.sh: scp +scp-uri.sh: scp-uri +sftp.sh: basic sftp put/get +sftp-chroot.sh: sftp in chroot +sftp-cmds.sh: sftp command +sftp-badcmds.sh: sftp invalid commands +sftp-batch.sh: sftp batchfile +sftp-glob.sh: sftp glob +sftp-perm.sh: sftp permissions +sftp-uri.sh: sftp-uri +ssh-com-client.sh: connect with ssh.com client +ssh-com-keygen.sh: ssh.com key import +ssh-com-sftp.sh: basic sftp put/get with ssh.com server +ssh-com.sh: connect to ssh.com server +reconfigure.sh: simple connect after reconfigure +dynamic-forward.sh: dynamic forwarding +forwarding.sh: local and remote forwarding +multiplex.sh: connection multiplexing +reexec.sh: reexec tests +brokenkeys.sh: broken keys +sshcfgparse.sh: ssh config parse +cfgparse.sh: sshd config parse +cfgmatch.sh: sshd_config match +cfgmatchlisten.sh: sshd_config matchlisten +addrmatch.sh: address match +localcommand.sh: localcommand +forcecommand.sh: forced command +portnum.sh: port number parsing +keytype.sh: login with different key types +kextype.sh: login with different key exchange algorithms +cert-hostkey.sh certified host keys +cert-userkey.sh: certified user keys +host-expand.sh: expand %h and %n +keys-command.sh: authorized keys from command +forward-control.sh: sshd control of local and remote forwarding +integrity.sh: integrity +krl.sh: key revocation lists +multipubkey.sh: multiple pubkey +limit-keytype.sh: restrict pubkey type +hostkey-agent.sh: hostkey agent +keygen-knownhosts.sh: ssh-keygen known_hosts +hostkey-rotate.sh: hostkey rotate +principals-command.sh: authorized principals command +cert-file.sh: ssh with certificates +cfginclude.sh: config include +allow-deny-users.sh: AllowUsers/DenyUsers +authinfo.sh: authinfo + + +Problems? + +Run the failing test with shell tracing (-x) turned on: +$ PATH=`pwd`/..:$PATH:. sh -x test-exec.sh `pwd` agent-timeout.sh + +Failed tests can be difficult to diagnose. Suggestions: +- run the individual test via ./test-exec.sh `pwd` [testname] +- set LogLevel to VERBOSE in test-exec.sh and enable syslogging of + auth.debug (eg to /var/log/authlog). + + +Known Issues. + +- Similarly, if you do not have "scp" in your system's $PATH then the + multiplex scp tests will fail (since the system's shell startup scripts + will determine where the shell started by sshd will look for scp). + +- Recent GNU coreutils deprecate "head -[n]": this will cause the yes-head + test to fail. The old behaviour can be restored by setting (and + exporting) _POSIX2_VERSION=199209 before running the tests. diff --git a/aosp/external/openssh/regress/addrmatch.sh b/aosp/external/openssh/regress/addrmatch.sh new file mode 100644 index 0000000000000000000000000000000000000000..26e0c9910c47ab93d60efde373ccad9390a94613 --- /dev/null +++ b/aosp/external/openssh/regress/addrmatch.sh @@ -0,0 +1,68 @@ +# $OpenBSD: addrmatch.sh,v 1.6 2020/08/28 03:17:13 dtucker Exp $ +# Placed in the Public Domain. + +tid="address match" + +mv $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +run_trial() +{ + user="$1"; addr="$2"; host="$3"; laddr="$4"; lport="$5" + expected="$6"; descr="$7" + + verbose "test $descr for $user $addr $host" + result=`${SSHD} -f $OBJ/sshd_proxy -T \ + -C user=${user},addr=${addr},host=${host},laddr=${laddr},lport=${lport} | \ + awk '/^forcecommand/ {print $2}'` + if [ "$result" != "$expected" ]; then + fail "failed '$descr' expected $expected got $result" + fi +} + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >>$OBJ/sshd_proxy < $OBJ/sshd_proxy + ${SUDO} ${SSHD} -f $OBJ/sshd_proxy -t >/dev/null 2>&1 && \ + fail "accepted invalid match $a $i" + done +done + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +rm $OBJ/sshd_proxy_bak diff --git a/aosp/external/openssh/regress/agent-getpeereid.sh b/aosp/external/openssh/regress/agent-getpeereid.sh new file mode 100644 index 0000000000000000000000000000000000000000..b84471a78558caeb1acce8967ceaa7450ff81a43 --- /dev/null +++ b/aosp/external/openssh/regress/agent-getpeereid.sh @@ -0,0 +1,56 @@ +# $OpenBSD: agent-getpeereid.sh,v 1.13 2021/09/01 00:50:27 dtucker Exp $ +# Placed in the Public Domain. + +tid="disallow agent attach from other uid" + +UNPRIV=nobody +ASOCK=${OBJ}/agent +SSH_AUTH_SOCK=/nonexistent + +if config_defined HAVE_GETPEEREID HAVE_GETPEERUCRED HAVE_SO_PEERCRED ; then + : +else + skip "skipped (not supported on this platform)" +fi +if test "x$USER" = "xroot"; then + skip "skipped (running as root)" +fi +case "x$SUDO" in + xsudo) sudo=1;; + xdoas|xdoas\ *) ;; + x) + skip "need SUDO to switch to uid $UNPRIV" ;; + *) + skip "unsupported $SUDO - "doas" and "sudo" are allowed" ;; +esac + +trace "start agent" +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s -a ${ASOCK}` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fail "could not start ssh-agent: exit code $r" +else + chmod 644 ${SSH_AUTH_SOCK} + + ${SSHADD} -l > /dev/null 2>&1 + r=$? + if [ $r -ne 1 ]; then + fail "ssh-add failed with $r != 1" + fi + if test -z "$sudo" ; then + # doas + ${SUDO} -n -u ${UNPRIV} ${SSHADD} -l 2>/dev/null + else + # sudo + < /dev/null ${SUDO} -S -u ${UNPRIV} ${SSHADD} -l 2>/dev/null + fi + r=$? + if [ $r -lt 2 ]; then + fail "ssh-add did not fail for ${UNPRIV}: $r < 2" + fi + + trace "kill agent" + ${SSHAGENT} -k > /dev/null +fi + +rm -f ${OBJ}/agent diff --git a/aosp/external/openssh/regress/agent-pkcs11.sh b/aosp/external/openssh/regress/agent-pkcs11.sh new file mode 100644 index 0000000000000000000000000000000000000000..268a70de8885bd79c223fe73e86cf9d97ed51244 --- /dev/null +++ b/aosp/external/openssh/regress/agent-pkcs11.sh @@ -0,0 +1,124 @@ +# $OpenBSD: agent-pkcs11.sh,v 1.9 2021/07/25 12:13:03 dtucker Exp $ +# Placed in the Public Domain. + +tid="pkcs11 agent test" + +try_token_libs() { + for _lib in "$@" ; do + if test -f "$_lib" ; then + verbose "Using token library $_lib" + TEST_SSH_PKCS11="$_lib" + return + fi + done + echo "skipped: Unable to find PKCS#11 token library" + exit 0 +} + +try_token_libs \ + /usr/local/lib/softhsm/libsofthsm2.so \ + /usr/lib64/pkcs11/libsofthsm2.so \ + /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so + +TEST_SSH_PIN=1234 +TEST_SSH_SOPIN=12345678 +if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then + SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}" + export SSH_PKCS11_HELPER +fi + +test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" + +# setup environment for softhsm2 token +DIR=$OBJ/SOFTHSM +rm -rf $DIR +TOKEN=$DIR/tokendir +mkdir -p $TOKEN +SOFTHSM2_CONF=$DIR/softhsm2.conf +export SOFTHSM2_CONF +cat > $SOFTHSM2_CONF << EOF +# SoftHSM v2 configuration file +directories.tokendir = ${TOKEN} +objectstore.backend = file +# ERROR, WARNING, INFO, DEBUG +log.level = DEBUG +# If CKF_REMOVABLE_DEVICE flag should be set +slots.removable = false +EOF +out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN") +slot=$(echo -- $out | sed 's/.* //') + +# prevent ssh-agent from calling ssh-askpass +SSH_ASKPASS=/usr/bin/true +export SSH_ASKPASS +unset DISPLAY + +# start command w/o tty, so ssh-add accepts pin from stdin +notty() { + perl -e 'use POSIX; POSIX::setsid(); + if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" +} + +trace "generating keys" +RSA=${DIR}/RSA +EC=${DIR}/EC +$OPENSSL_BIN genpkey -algorithm rsa > $RSA +$OPENSSL_BIN pkcs8 -nocrypt -in $RSA |\ + softhsm2-util --slot "$slot" --label 01 --id 01 --pin "$TEST_SSH_PIN" --import /dev/stdin +$OPENSSL_BIN genpkey \ + -genparam \ + -algorithm ec \ + -pkeyopt ec_paramgen_curve:prime256v1 |\ + $OPENSSL_BIN genpkey \ + -paramfile /dev/stdin > $EC +$OPENSSL_BIN pkcs8 -nocrypt -in $EC |\ + softhsm2-util --slot "$slot" --label 02 --id 02 --pin "$TEST_SSH_PIN" --import /dev/stdin + +trace "start agent" +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fail "could not start ssh-agent: exit code $r" +else + trace "add pkcs11 key to agent" + echo ${TEST_SSH_PIN} | notty ${SSHADD} -s ${TEST_SSH_PKCS11} > /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "ssh-add -s failed: exit code $r" + fi + + trace "pkcs11 list via agent" + ${SSHADD} -l > /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "ssh-add -l failed: exit code $r" + fi + + for k in $RSA $EC; do + trace "testing $k" + chmod 600 $k + ssh-keygen -y -f $k > $k.pub + pub=$(cat $k.pub) + ${SSHADD} -L | grep -q "$pub" || fail "key $k missing in ssh-add -L" + ${SSHADD} -T $k.pub || fail "ssh-add -T with $k failed" + + # add to authorized keys + cat $k.pub > $OBJ/authorized_keys_$USER + trace "pkcs11 connect via agent ($k)" + ${SSH} -F $OBJ/ssh_proxy somehost exit 5 + r=$? + if [ $r -ne 5 ]; then + fail "ssh connect failed (exit code $r)" + fi + done + + trace "remove pkcs11 keys" + echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "ssh-add -e failed: exit code $r" + fi + + trace "kill agent" + ${SSHAGENT} -k > /dev/null +fi diff --git a/aosp/external/openssh/regress/agent-ptrace.sh b/aosp/external/openssh/regress/agent-ptrace.sh new file mode 100644 index 0000000000000000000000000000000000000000..9cd68d7ec84eea5a7abb713bd11a44364d0a17c6 --- /dev/null +++ b/aosp/external/openssh/regress/agent-ptrace.sh @@ -0,0 +1,66 @@ +# $OpenBSD: agent-ptrace.sh,v 1.3 2015/09/11 04:55:01 djm Exp $ +# Placed in the Public Domain. + +tid="disallow agent ptrace attach" + +if have_prog uname ; then + case `uname` in + AIX|CYGWIN*|OSF1) + echo "skipped (not supported on this platform)" + exit 0 + ;; + esac +fi + +if [ "x$USER" = "xroot" ]; then + echo "Skipped: running as root" + exit 0 +fi + +if have_prog gdb ; then + : ok +else + echo "skipped (gdb not found)" + exit 0 +fi + +if $OBJ/setuid-allowed ${SSHAGENT} ; then + : ok +else + echo "skipped (${SSHAGENT} is mounted on a no-setuid filesystem)" + exit 0 +fi + +if test -z "$SUDO" ; then + echo "skipped (SUDO not set)" + exit 0 +else + $SUDO chown 0 ${SSHAGENT} + $SUDO chgrp 0 ${SSHAGENT} + $SUDO chmod 2755 ${SSHAGENT} +fi + +trace "start agent" +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fail "could not start ssh-agent: exit code $r" +else + # ls -l ${SSH_AUTH_SOCK} + gdb ${SSHAGENT} ${SSH_AGENT_PID} > ${OBJ}/gdb.out 2>&1 << EOF + quit +EOF + r=$? + if [ $r -ne 0 ]; then + fail "gdb failed: exit code $r" + fi + egrep 'ptrace: Operation not permitted.|procfs:.*Permission denied.|ttrace.*Permission denied.|procfs:.*: Invalid argument.|Unable to access task ' >/dev/null ${OBJ}/gdb.out + r=$? + rm -f ${OBJ}/gdb.out + if [ $r -ne 0 ]; then + fail "ptrace succeeded?: exit code $r" + fi + + trace "kill agent" + ${SSHAGENT} -k > /dev/null +fi diff --git a/aosp/external/openssh/regress/agent-restrict.sh b/aosp/external/openssh/regress/agent-restrict.sh new file mode 100644 index 0000000000000000000000000000000000000000..a30aed7bf3d5ec43b89d06f98e8f4bab531c5c5b --- /dev/null +++ b/aosp/external/openssh/regress/agent-restrict.sh @@ -0,0 +1,495 @@ +# $OpenBSD: agent-restrict.sh,v 1.5 2022/01/13 04:53:16 dtucker Exp $ +# Placed in the Public Domain. + +tid="agent restrictions" + +SSH_AUTH_SOCK="$OBJ/agent.sock" +export SSH_AUTH_SOCK +rm -f $SSH_AUTH_SOCK $OBJ/agent.log $OBJ/host_[abcdex]* $OBJ/user_[abcdex]* +rm -f $OBJ/sshd_proxy_host* $OBJ/ssh_output* $OBJ/expect_* +rm -f $OBJ/ssh_proxy[._]* $OBJ/command + +verbose "generate keys" +for h in a b c d e x ca ; do + $SSHKEYGEN -q -t ed25519 -C host_$h -N '' -f $OBJ/host_$h || \ + fatal "ssh-keygen hostkey failed" + $SSHKEYGEN -q -t ed25519 -C user_$h -N '' -f $OBJ/user_$h || \ + fatal "ssh-keygen userkey failed" +done + +# Make some hostcerts +for h in d e ; do + id="host_$h" + $SSHKEYGEN -q -s $OBJ/host_ca -I $id -n $id -h $OBJ/host_${h}.pub || \ + fatal "ssh-keygen certify failed" +done + +verbose "prepare client config" +egrep -vi '(identityfile|hostname|hostkeyalias|proxycommand)' \ + $OBJ/ssh_proxy > $OBJ/ssh_proxy.bak +cat << _EOF > $OBJ/ssh_proxy +IdentitiesOnly yes +ForwardAgent yes +ExitOnForwardFailure yes +_EOF +cp $OBJ/ssh_proxy $OBJ/ssh_proxy_noid +for h in a b c d e ; do + cat << _EOF >> $OBJ/ssh_proxy +Host host_$h + Hostname host_$h + HostkeyAlias host_$h + IdentityFile $OBJ/user_$h + ProxyCommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy_host_$h +_EOF + # Variant with no specified keys. + cat << _EOF >> $OBJ/ssh_proxy_noid +Host host_$h + Hostname host_$h + HostkeyAlias host_$h + ProxyCommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy_host_$h +_EOF +done +cat $OBJ/ssh_proxy.bak >> $OBJ/ssh_proxy +cat $OBJ/ssh_proxy.bak >> $OBJ/ssh_proxy_noid + +LC_ALL=C +export LC_ALL +echo "SetEnv LC_ALL=${LC_ALL}" >> sshd_proxy + +verbose "prepare known_hosts" +rm -f $OBJ/known_hosts +for h in a b c x ; do + (printf "host_$h " ; cat $OBJ/host_${h}.pub) >> $OBJ/known_hosts +done +(printf "@cert-authority host_* " ; cat $OBJ/host_ca.pub) >> $OBJ/known_hosts + +verbose "prepare server configs" +egrep -vi '(hostkey|pidfile)' $OBJ/sshd_proxy \ + > $OBJ/sshd_proxy.bak +for h in a b c d e; do + cp $OBJ/sshd_proxy.bak $OBJ/sshd_proxy_host_$h + cat << _EOF >> $OBJ/sshd_proxy_host_$h +ExposeAuthInfo yes +PidFile none +Hostkey $OBJ/host_$h +_EOF +done +for h in d e ; do + echo "HostCertificate $OBJ/host_${h}-cert.pub" \ + >> $OBJ/sshd_proxy_host_$h +done +# Create authorized_keys with canned command. +reset_keys() { + _whichcmd="$1" + _command="" + case "$_whichcmd" in + authinfo) _command="cat \$SSH_USER_AUTH" ;; + keylist) _command="$SSHADD -L | cut -d' ' -f-2 | sort" ;; + *) fatal "unsupported command $_whichcmd" ;; + esac + trace "reset keys" + >$OBJ/authorized_keys_$USER + for h in e d c b a; do + (printf "%s" "restrict,agent-forwarding,command=\"$_command\" "; + cat $OBJ/user_$h.pub) >> $OBJ/authorized_keys_$USER + done +} +# Prepare a key for comparison with ExposeAuthInfo/$SSH_USER_AUTH. +expect_key() { + _key="$OBJ/${1}.pub" + _file="$OBJ/$2" + (printf "publickey " ; cut -d' ' -f-2 $_key) > $_file +} +# Prepare expect_* files to compare against authinfo forced command to ensure +# keys used for authentication match. +reset_expect_keys() { + for u in a b c d e; do + expect_key user_$u expect_$u + done +} +# ssh to host, expecting success and that output matched expectation for +# that host (expect_$h file). +expect_succeed() { + _id="$1" + _case="$2" + shift; shift; _extra="$@" + _host="host_$_id" + trace "connect $_host expect success" + rm -f $OBJ/ssh_output + ${SSH} $_extra -F $OBJ/ssh_proxy $_host true > $OBJ/ssh_output + _s=$? + test $_s -eq 0 || fail "host $_host $_case fail, exit status $_s" + diff $OBJ/ssh_output $OBJ/expect_${_id} || + fail "unexpected ssh output" +} +# ssh to host using explicit key, expecting success and that the key was +# actually used for authentication. +expect_succeed_key() { + _id="$1" + _key="$2" + _case="$3" + shift; shift; shift; _extra="$@" + _host="host_$_id" + trace "connect $_host expect success, with key $_key" + _keyfile="$OBJ/$_key" + rm -f $OBJ/ssh_output + ${SSH} $_extra -F $OBJ/ssh_proxy_noid \ + -oIdentityFile=$_keyfile $_host true > $OBJ/ssh_output + _s=$? + test $_s -eq 0 || fail "host $_host $_key $_case fail, exit status $_s" + expect_key $_key expect_key + diff $OBJ/ssh_output $OBJ/expect_key || + fail "incorrect key used for authentication" +} +# ssh to a host, expecting it to fail. +expect_fail() { + _host="$1" + _case="$2" + shift; shift; _extra="$@" + trace "connect $_host expect failure" + ${SSH} $_extra -F $OBJ/ssh_proxy $_host true >/dev/null && \ + fail "host $_host $_case succeeded unexpectedly" +} +# ssh to a host using an explicit key, expecting it to fail. +expect_fail_key() { + _id="$1" + _key="$2" + _case="$3" + shift; shift; shift; _extra="$@" + _host="host_$_id" + trace "connect $_host expect failure, with key $_key" + _keyfile="$OBJ/$_key" + ${SSH} $_extra -F $OBJ/ssh_proxy_noid -oIdentityFile=$_keyfile \ + $_host true > $OBJ/ssh_output && \ + fail "host $_host $_key $_case succeeded unexpectedly" +} +# Move the private key files out of the way to force use of agent-hosted keys. +hide_privatekeys() { + trace "hide private keys" + for u in a b c d e x; do + mv $OBJ/user_$u $OBJ/user_x$u || fatal "hide privkey $u" + done +} +# Put the private key files back. +restore_privatekeys() { + trace "restore private keys" + for u in a b c d e x; do + mv $OBJ/user_x$u $OBJ/user_$u || fatal "restore privkey $u" + done +} +clear_agent() { + ${SSHADD} -D > /dev/null 2>&1 || fatal "clear agent failed" +} + +reset_keys authinfo +reset_expect_keys + +verbose "authentication w/o agent" +for h in a b c d e ; do + expect_succeed $h "w/o agent" + wrongkey=user_e + test "$h" = "e" && wrongkey=user_a + expect_succeed_key $h $wrongkey "\"wrong\" key w/o agent" +done +hide_privatekeys +for h in a b c d e ; do + expect_fail $h "w/o agent" +done +restore_privatekeys + +verbose "start agent" +${SSHAGENT} ${EXTRA_AGENT_ARGS} -d -a $SSH_AUTH_SOCK > $OBJ/agent.log 2>&1 & +AGENT_PID=$! +trap "kill $AGENT_PID" EXIT +sleep 4 # Give it a chance to start +# Check that it's running. +${SSHADD} -l > /dev/null 2>&1 +if [ $? -ne 1 ]; then + fail "ssh-add -l did not fail with exit code 1" +fi + +verbose "authentication with agent (no restrict)" +for u in a b c d e x; do + $SSHADD -q $OBJ/user_$u || fatal "add key $u unrestricted" +done +hide_privatekeys +for h in a b c d e ; do + expect_succeed $h "with agent" + wrongkey=user_e + test "$h" = "e" && wrongkey=user_a + expect_succeed_key $h $wrongkey "\"wrong\" key with agent" +done + +verbose "unrestricted keylist" +reset_keys keylist +rm -f $OBJ/expect_list.pre +# List of keys from agent should contain everything. +for u in a b c d e x; do + cut -d " " -f-2 $OBJ/user_${u}.pub >> $OBJ/expect_list.pre +done +sort $OBJ/expect_list.pre > $OBJ/expect_list +for h in a b c d e; do + cp $OBJ/expect_list $OBJ/expect_$h + expect_succeed $h "unrestricted keylist" +done +restore_privatekeys + +verbose "authentication with agent (basic restrict)" +reset_keys authinfo +reset_expect_keys +for h in a b c d e; do + $SSHADD -h host_$h -H $OBJ/known_hosts -q $OBJ/user_$h \ + || fatal "add key $u basic restrict" +done +# One more, unrestricted +$SSHADD -q $OBJ/user_x || fatal "add unrestricted key" +hide_privatekeys +# Authentication to host with expected key should work. +for h in a b c d e ; do + expect_succeed $h "with agent" +done +# Authentication to host with incorrect key should fail. +verbose "authentication with agent incorrect key (basic restrict)" +for h in a b c d e ; do + wrongkey=user_e + test "$h" = "e" && wrongkey=user_a + expect_fail_key $h $wrongkey "wrong key with agent (basic restrict)" +done + +verbose "keylist (basic restrict)" +reset_keys keylist +# List from forwarded agent should contain only user_x - the unrestricted key. +cut -d " " -f-2 $OBJ/user_x.pub > $OBJ/expect_list +for h in a b c d e; do + cp $OBJ/expect_list $OBJ/expect_$h + expect_succeed $h "keylist (basic restrict)" +done +restore_privatekeys + +verbose "username" +reset_keys authinfo +reset_expect_keys +for h in a b c d e; do + $SSHADD -h "${USER}@host_$h" -H $OBJ/known_hosts -q $OBJ/user_$h \ + || fatal "add key $u basic restrict" +done +hide_privatekeys +for h in a b c d e ; do + expect_succeed $h "wildcard user" +done +restore_privatekeys + +verbose "username wildcard" +reset_keys authinfo +reset_expect_keys +for h in a b c d e; do + $SSHADD -h "*@host_$h" -H $OBJ/known_hosts -q $OBJ/user_$h \ + || fatal "add key $u basic restrict" +done +hide_privatekeys +for h in a b c d e ; do + expect_succeed $h "wildcard user" +done +restore_privatekeys + +verbose "username incorrect" +reset_keys authinfo +reset_expect_keys +for h in a b c d e; do + $SSHADD -h "--BADUSER@host_$h" -H $OBJ/known_hosts -q $OBJ/user_$h \ + || fatal "add key $u basic restrict" +done +hide_privatekeys +for h in a b c d e ; do + expect_fail $h "incorrect user" +done +restore_privatekeys + + +verbose "agent restriction honours certificate principal" +reset_keys authinfo +reset_expect_keys +clear_agent +$SSHADD -h host_e -H $OBJ/known_hosts -q $OBJ/user_d || fatal "add key" +hide_privatekeys +expect_fail d "restricted agent w/ incorrect cert principal" +restore_privatekeys + +# Prepares the script used to drive chained ssh connections for the +# multihop tests. Believe me, this is easier than getting the escaping +# right for 5 hops on the command-line... +prepare_multihop_script() { + MULTIHOP_RUN=$OBJ/command + cat << _EOF > $MULTIHOP_RUN +#!/bin/sh +#set -x +me="\$1" ; shift +next="\$1" +if test ! -z "\$me" ; then + rm -f $OBJ/done + echo "HOSTNAME host_\$me" + echo "AUTHINFO" + cat \$SSH_USER_AUTH +fi +echo AGENT +$SSHADD -L | egrep "^ssh" | cut -d" " -f-2 | sort +if test -z "\$next" ; then + touch $OBJ/done + echo "FINISH" + e=0 +else + echo NEXT + ${SSH} -F $OBJ/ssh_proxy_noid -oIdentityFile=$OBJ/user_a \ + host_\$next $MULTIHOP_RUN "\$@" + e=\$? +fi +echo "COMPLETE \"\$me\"" +if test ! -z "\$me" ; then + if test ! -f $OBJ/done ; then + echo "DONE MARKER MISSING" + test \$e -eq 0 && e=63 + fi +fi +exit \$e +_EOF + chmod u+x $MULTIHOP_RUN +} + +# Prepare expected output for multihop tests at expect_a +prepare_multihop_expected() { + _keys="$1" + _hops="a b c d e" + test -z "$2" || _hops="$2" + _revhops=$(echo "$_hops" | rev) + _lasthop=$(echo "$_hops" | sed 's/.* //') + + rm -f $OBJ/expect_keys + for h in a b c d e; do + cut -d" " -f-2 $OBJ/user_${h}.pub >> $OBJ/expect_keys + done + rm -f $OBJ/expect_a + echo "AGENT" >> $OBJ/expect_a + test "x$_keys" = "xnone" || sort $OBJ/expect_keys >> $OBJ/expect_a + echo "NEXT" >> $OBJ/expect_a + for h in $_hops ; do + echo "HOSTNAME host_$h" >> $OBJ/expect_a + echo "AUTHINFO" >> $OBJ/expect_a + (printf "publickey " ; cut -d" " -f-2 $OBJ/user_a.pub) >> $OBJ/expect_a + echo "AGENT" >> $OBJ/expect_a + if test "x$_keys" = "xall" ; then + sort $OBJ/expect_keys >> $OBJ/expect_a + fi + if test "x$h" != "x$_lasthop" ; then + if test "x$_keys" = "xfiltered" ; then + cut -d" " -f-2 $OBJ/user_a.pub >> $OBJ/expect_a + fi + echo "NEXT" >> $OBJ/expect_a + fi + done + echo "FINISH" >> $OBJ/expect_a + for h in $_revhops "" ; do + echo "COMPLETE \"$h\"" >> $OBJ/expect_a + done +} + +prepare_multihop_script +cp $OBJ/user_a.pub $OBJ/authorized_keys_$USER # only one key used. + +verbose "multihop without agent" +clear_agent +prepare_multihop_expected none +$MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop no agent ssh failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" + +verbose "multihop agent unrestricted" +clear_agent +$SSHADD -q $OBJ/user_[abcde] +prepare_multihop_expected all +$MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop no agent ssh failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" + +verbose "multihop restricted" +clear_agent +prepare_multihop_expected filtered +# Add user_a, with permission to connect through the whole chain. +$SSHADD -h host_a -h "host_a>host_b" -h "host_b>host_c" \ + -h "host_c>host_d" -h "host_d>host_e" \ + -H $OBJ/known_hosts -q $OBJ/user_a \ + || fatal "add key user_a multihop" +# Add the other keys, bound to a unused host. +$SSHADD -q -h host_x -H $OBJ/known_hosts $OBJ/user_[bcde] || fail "add keys" +hide_privatekeys +$MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop ssh failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" +restore_privatekeys + +verbose "multihop username" +$SSHADD -h host_a -h "host_a>${USER}@host_b" -h "host_b>${USER}@host_c" \ + -h "host_c>${USER}@host_d" -h "host_d>${USER}@host_e" \ + -H $OBJ/known_hosts -q $OBJ/user_a || fatal "add key user_a multihop" +hide_privatekeys +$MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop w/ user ssh failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" +restore_privatekeys + +verbose "multihop wildcard username" +$SSHADD -h host_a -h "host_a>*@host_b" -h "host_b>*@host_c" \ + -h "host_c>*@host_d" -h "host_d>*@host_e" \ + -H $OBJ/known_hosts -q $OBJ/user_a || fatal "add key user_a multihop" +hide_privatekeys +$MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop w/ user ssh failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" +restore_privatekeys + +verbose "multihop wrong username" +$SSHADD -h host_a -h "host_a>*@host_b" -h "host_b>*@host_c" \ + -h "host_c>--BADUSER@host_d" -h "host_d>*@host_e" \ + -H $OBJ/known_hosts -q $OBJ/user_a || fatal "add key user_a multihop" +hide_privatekeys +$MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output && \ + fail "multihop with wrong user succeeded unexpectedly" +restore_privatekeys + +verbose "multihop cycle no agent" +clear_agent +prepare_multihop_expected none "a b a a c d e" +$MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output || \ + fail "multihop cycle no-agent fail" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" + +verbose "multihop cycle agent unrestricted" +clear_agent +$SSHADD -q $OBJ/user_[abcde] || fail "add keys" +prepare_multihop_expected all "a b a a c d e" +$MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output || \ + fail "multihop cycle agent ssh failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" + +verbose "multihop cycle restricted deny" +clear_agent +$SSHADD -q -h host_x -H $OBJ/known_hosts $OBJ/user_[bcde] || fail "add keys" +$SSHADD -h host_a -h "host_a>host_b" -h "host_b>host_c" \ + -h "host_c>host_d" -h "host_d>host_e" \ + -H $OBJ/known_hosts -q $OBJ/user_a \ + || fatal "add key user_a multihop" +prepare_multihop_expected filtered "a b a a c d e" +hide_privatekeys +$MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output && \ + fail "multihop cycle restricted deny succeded unexpectedly" +restore_privatekeys + +verbose "multihop cycle restricted allow" +clear_agent +$SSHADD -q -h host_x -H $OBJ/known_hosts $OBJ/user_[bcde] || fail "add keys" +$SSHADD -h host_a -h "host_a>host_b" -h "host_b>host_c" \ + -h "host_c>host_d" -h "host_d>host_e" \ + -h "host_b>host_a" -h "host_a>host_a" -h "host_a>host_c" \ + -H $OBJ/known_hosts -q $OBJ/user_a \ + || fatal "add key user_a multihop" +prepare_multihop_expected filtered "a b a a c d e" +hide_privatekeys +$MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output || \ + fail "multihop cycle restricted allow failed" +diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output" +restore_privatekeys + diff --git a/aosp/external/openssh/regress/agent-subprocess.sh b/aosp/external/openssh/regress/agent-subprocess.sh new file mode 100644 index 0000000000000000000000000000000000000000..2f36d70cccae0897850c3c46b71df7a4f2fa69d0 --- /dev/null +++ b/aosp/external/openssh/regress/agent-subprocess.sh @@ -0,0 +1,22 @@ +# $OpenBSD: agent-subprocess.sh,v 1.1 2020/06/19 05:07:09 dtucker Exp $ +# Placed in the Public Domain. + +tid="agent subprocess" + +trace "ensure agent exits when run as subprocess" +${SSHAGENT} sh -c "echo \$SSH_AGENT_PID >$OBJ/pidfile; sleep 1" + +pid=`cat $OBJ/pidfile` + +# Currently ssh-agent polls every 10s so we need to wait at least that long. +n=12 +while kill -0 $pid >/dev/null 2>&1 && test "$n" -gt "0"; do + n=$(($n - 1)) + sleep 1 +done + +if test "$n" -eq "0"; then + fail "agent still running" +fi + +rm -f $OBJ/pidfile diff --git a/aosp/external/openssh/regress/agent-timeout.sh b/aosp/external/openssh/regress/agent-timeout.sh new file mode 100644 index 0000000000000000000000000000000000000000..6dec09285908de2c503e5505eca94db6767d51b5 --- /dev/null +++ b/aosp/external/openssh/regress/agent-timeout.sh @@ -0,0 +1,38 @@ +# $OpenBSD: agent-timeout.sh,v 1.6 2019/11/26 23:43:10 djm Exp $ +# Placed in the Public Domain. + +tid="agent timeout test" + +SSHAGENT_TIMEOUT=10 + +trace "start agent" +eval `${SSHAGENT} -s ${EXTRA_AGENT_ARGS}` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fail "could not start ssh-agent: exit code $r" +else + trace "add keys with timeout" + keys=0 + for t in ${SSH_KEYTYPES}; do + ${SSHADD} -kt ${SSHAGENT_TIMEOUT} $OBJ/$t > /dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh-add did succeed exit code 0" + fi + keys=$((${keys} + 1)) + done + n=`${SSHADD} -l 2> /dev/null | wc -l` + trace "agent has $n keys" + if [ $n -ne $keys ]; then + fail "ssh-add -l did not return $keys keys: $n" + fi + trace "sleeping 2*${SSHAGENT_TIMEOUT} seconds" + sleep ${SSHAGENT_TIMEOUT} + sleep ${SSHAGENT_TIMEOUT} + ${SSHADD} -l 2> /dev/null | grep 'The agent has no identities.' >/dev/null + if [ $? -ne 0 ]; then + fail "ssh-add -l still returns keys after timeout" + fi + + trace "kill agent" + ${SSHAGENT} -k > /dev/null +fi diff --git a/aosp/external/openssh/regress/agent.sh b/aosp/external/openssh/regress/agent.sh new file mode 100644 index 0000000000000000000000000000000000000000..f187b675720142a9f235536478074b093e56e8d1 --- /dev/null +++ b/aosp/external/openssh/regress/agent.sh @@ -0,0 +1,227 @@ +# $OpenBSD: agent.sh,v 1.20 2021/02/25 03:27:34 djm Exp $ +# Placed in the Public Domain. + +tid="simple agent test" + +SSH_AUTH_SOCK=/nonexistent ${SSHADD} -l > /dev/null 2>&1 +if [ $? -ne 2 ]; then + fail "ssh-add -l did not fail with exit code 2" +fi + +trace "start agent, args ${EXTRA_AGENT_ARGS} -s" +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fatal "could not start ssh-agent: exit code $r" +fi + +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s | sed 's/SSH_/FW_SSH_/g'` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fatal "could not start second ssh-agent: exit code $r" +fi + +${SSHADD} -l > /dev/null 2>&1 +if [ $? -ne 1 ]; then + fail "ssh-add -l did not fail with exit code 1" +fi + +rm -f $OBJ/user_ca_key $OBJ/user_ca_key.pub +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key \ + || fatal "ssh-keygen failed" + +trace "overwrite authorized keys" +printf '' > $OBJ/authorized_keys_$USER + +for t in ${SSH_KEYTYPES}; do + # generate user key for agent + rm -f $OBJ/$t-agent $OBJ/$t-agent.pub* + ${SSHKEYGEN} -q -N '' -t $t -f $OBJ/$t-agent ||\ + fatal "ssh-keygen for $t-agent failed" + # Make a certificate for each too. + ${SSHKEYGEN} -qs $OBJ/user_ca_key -I "$t cert" \ + -n estragon $OBJ/$t-agent.pub || fatal "ca sign failed" + + # add to authorized keys + cat $OBJ/$t-agent.pub >> $OBJ/authorized_keys_$USER + # add private key to agent + ${SSHADD} $OBJ/$t-agent > /dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh-add failed exit code $?" + fi + # add private key to second agent + SSH_AUTH_SOCK=$FW_SSH_AUTH_SOCK ${SSHADD} $OBJ/$t-agent > /dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh-add failed exit code $?" + fi + # Move private key to ensure that we aren't accidentally using it. + # Keep the corresponding public keys/certs around for later use. + mv -f $OBJ/$t-agent $OBJ/$t-agent-private + cp -f $OBJ/$t-agent.pub $OBJ/$t-agent-private.pub + cp -f $OBJ/$t-agent-cert.pub $OBJ/$t-agent-private-cert.pub +done + +# Remove explicit identity directives from ssh_proxy +mv $OBJ/ssh_proxy $OBJ/ssh_proxy_bak +grep -vi identityfile $OBJ/ssh_proxy_bak > $OBJ/ssh_proxy + +${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -l failed: exit code $r" +fi +# the same for full pubkey output +${SSHADD} -L > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -L failed: exit code $r" +fi + +trace "simple connect via agent" +${SSH} -F $OBJ/ssh_proxy somehost exit 52 +r=$? +if [ $r -ne 52 ]; then + fail "ssh connect with failed (exit code $r)" +fi + +for t in ${SSH_KEYTYPES}; do + trace "connect via agent using $t key" + if [ "$t" = "ssh-dss" ]; then + echo "PubkeyAcceptedAlgorithms +ssh-dss" >> $OBJ/ssh_proxy + echo "PubkeyAcceptedAlgorithms +ssh-dss" >> $OBJ/sshd_proxy + fi + ${SSH} -F $OBJ/ssh_proxy -i $OBJ/$t-agent.pub -oIdentitiesOnly=yes \ + somehost exit 52 + r=$? + if [ $r -ne 52 ]; then + fail "ssh connect with failed (exit code $r)" + fi +done + +trace "agent forwarding" +${SSH} -A -F $OBJ/ssh_proxy somehost ${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -l via agent fwd failed (exit code $r)" +fi +${SSH} "-oForwardAgent=$SSH_AUTH_SOCK" -F $OBJ/ssh_proxy somehost ${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -l via agent path fwd failed (exit code $r)" +fi +${SSH} -A -F $OBJ/ssh_proxy somehost \ + "${SSH} -F $OBJ/ssh_proxy somehost exit 52" +r=$? +if [ $r -ne 52 ]; then + fail "agent fwd failed (exit code $r)" +fi + +trace "agent forwarding different agent" +${SSH} "-oForwardAgent=$FW_SSH_AUTH_SOCK" -F $OBJ/ssh_proxy somehost ${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -l via agent path fwd of different agent failed (exit code $r)" +fi +${SSH} '-oForwardAgent=$FW_SSH_AUTH_SOCK' -F $OBJ/ssh_proxy somehost ${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -l via agent path env fwd of different agent failed (exit code $r)" +fi + +# Remove keys from forwarded agent, ssh-add on remote machine should now fail. +SSH_AUTH_SOCK=$FW_SSH_AUTH_SOCK ${SSHADD} -D > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -D failed: exit code $r" +fi +${SSH} '-oForwardAgent=$FW_SSH_AUTH_SOCK' -F $OBJ/ssh_proxy somehost ${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 1 ]; then + fail "ssh-add -l with different agent did not fail with exit code 1 (exit code $r)" +fi + +(printf 'cert-authority,principals="estragon" '; cat $OBJ/user_ca_key.pub) \ + > $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + if [ "$t" != "ssh-dss" ]; then + trace "connect via agent using $t key" + ${SSH} -F $OBJ/ssh_proxy -i $OBJ/$t-agent.pub \ + -oCertificateFile=$OBJ/$t-agent-cert.pub \ + -oIdentitiesOnly=yes somehost exit 52 + r=$? + if [ $r -ne 52 ]; then + fail "ssh connect with failed (exit code $r)" + fi + fi +done + +## Deletion tests. + +trace "delete all agent keys" +${SSHADD} -D > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -D failed: exit code $r" +fi +# make sure they're gone +${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 1 ]; then + fail "ssh-add -l returned unexpected exit code: $r" +fi +trace "readd keys" +# re-add keys/certs to agent +for t in ${SSH_KEYTYPES}; do + ${SSHADD} $OBJ/$t-agent-private >/dev/null 2>&1 || \ + fail "ssh-add failed exit code $?" +done +# make sure they are there +${SSHADD} -l > /dev/null 2>&1 +r=$? +if [ $r -ne 0 ]; then + fail "ssh-add -l failed: exit code $r" +fi + +check_key_absent() { + ${SSHADD} -L | grep "^$1 " >/dev/null + if [ $? -eq 0 ]; then + fail "$1 key unexpectedly present" + fi +} +check_key_present() { + ${SSHADD} -L | grep "^$1 " >/dev/null + if [ $? -ne 0 ]; then + fail "$1 key missing from agent" + fi +} + +# delete the ed25519 key +trace "delete single key by file" +${SSHADD} -qdk $OBJ/ssh-ed25519-agent || fail "ssh-add -d ed25519 failed" +check_key_absent ssh-ed25519 +check_key_present ssh-ed25519-cert-v01@openssh.com +# Put key/cert back. +${SSHADD} $OBJ/ssh-ed25519-agent-private >/dev/null 2>&1 || \ + fail "ssh-add failed exit code $?" +check_key_present ssh-ed25519 +# Delete both key and certificate. +trace "delete key/cert by file" +${SSHADD} -qd $OBJ/ssh-ed25519-agent || fail "ssh-add -d ed25519 failed" +check_key_absent ssh-ed25519 +check_key_absent ssh-ed25519-cert-v01@openssh.com +# Put key/cert back. +${SSHADD} $OBJ/ssh-ed25519-agent-private >/dev/null 2>&1 || \ + fail "ssh-add failed exit code $?" +check_key_present ssh-ed25519 +# Delete certificate via stdin +${SSHADD} -qd - < $OBJ/ssh-ed25519-agent-cert.pub || fail "ssh-add -d - failed" +check_key_present ssh-ed25519 +check_key_absent ssh-ed25519-cert-v01@openssh.com +# Delete key via stdin +${SSHADD} -qd - < $OBJ/ssh-ed25519-agent.pub || fail "ssh-add -d - failed" +check_key_absent ssh-ed25519 +check_key_absent ssh-ed25519-cert-v01@openssh.com + +trace "kill agent" +${SSHAGENT} -k > /dev/null +SSH_AGENT_PID=$FW_SSH_AGENT_PID ${SSHAGENT} -k > /dev/null diff --git a/aosp/external/openssh/regress/allow-deny-users.sh b/aosp/external/openssh/regress/allow-deny-users.sh new file mode 100644 index 0000000000000000000000000000000000000000..6c053eef08824b61ccf14acfc7e3c24604c1de4d --- /dev/null +++ b/aosp/external/openssh/regress/allow-deny-users.sh @@ -0,0 +1,43 @@ +# Public Domain +# Zev Weiss, 2016 +# $OpenBSD: allow-deny-users.sh,v 1.6 2021/06/07 00:00:50 djm Exp $ + +tid="AllowUsers/DenyUsers" + +me="$LOGNAME" +if [ "x$me" = "x" ]; then + me=`whoami` +fi +other="nobody" + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy.orig + +test_auth() +{ + deny="$1" + allow="$2" + should_succeed="$3" + failmsg="$4" + + cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy + test -z "$deny" || echo DenyUsers="$deny" >> $OBJ/sshd_proxy + test -z "$allow" || echo AllowUsers="$allow" >> $OBJ/sshd_proxy + + ${SSH} -F $OBJ/ssh_proxy "$me@somehost" true + status=$? + + if (test $status -eq 0 && ! $should_succeed) \ + || (test $status -ne 0 && $should_succeed); then + fail "$failmsg" + fi +} + +# DenyUsers AllowUsers should_succeed failure_message +test_auth "" "" true "user in neither DenyUsers nor AllowUsers denied" +test_auth "$other $me" "" false "user in DenyUsers allowed" +test_auth "$me $other" "" false "user in DenyUsers allowed" +test_auth "" "$other" false "user not in AllowUsers allowed" +test_auth "" "$other $me" true "user in AllowUsers denied" +test_auth "" "$me $other" true "user in AllowUsers denied" +test_auth "$me $other" "$me $other" false "user in both DenyUsers and AllowUsers allowed" +test_auth "$other $me" "$other $me" false "user in both DenyUsers and AllowUsers allowed" diff --git a/aosp/external/openssh/regress/authinfo.sh b/aosp/external/openssh/regress/authinfo.sh new file mode 100644 index 0000000000000000000000000000000000000000..693424afafa7ec9688294159eb487ed00d76293e --- /dev/null +++ b/aosp/external/openssh/regress/authinfo.sh @@ -0,0 +1,17 @@ +# $OpenBSD: authinfo.sh,v 1.3 2018/04/10 00:13:27 djm Exp $ +# Placed in the Public Domain. + +tid="authinfo" + +# Ensure the environment variable doesn't leak when ExposeAuthInfo=no. +verbose "ExposeAuthInfo=no" +env SSH_USER_AUTH=blah ${SSH} -F $OBJ/ssh_proxy x \ + 'env | grep SSH_USER_AUTH >/dev/null' && fail "SSH_USER_AUTH present" + +verbose "ExposeAuthInfo=yes" +echo ExposeAuthInfo=yes >> $OBJ/sshd_proxy +${SSH} -F $OBJ/ssh_proxy x \ + 'grep ^publickey "$SSH_USER_AUTH" /dev/null >/dev/null' || + fail "ssh with ExposeAuthInfo failed" + +# XXX test multiple auth and key contents diff --git a/aosp/external/openssh/regress/banner.sh b/aosp/external/openssh/regress/banner.sh new file mode 100644 index 0000000000000000000000000000000000000000..a84feb5ad7c13c2da7dd8b9584ff229c1ecb4611 --- /dev/null +++ b/aosp/external/openssh/regress/banner.sh @@ -0,0 +1,46 @@ +# $OpenBSD: banner.sh,v 1.4 2021/08/08 06:38:33 dtucker Exp $ +# Placed in the Public Domain. + +tid="banner" +echo "Banner $OBJ/banner.in" >> $OBJ/sshd_proxy + +rm -f $OBJ/banner.out $OBJ/banner.in $OBJ/empty.in +touch $OBJ/empty.in + +trace "test missing banner file" +verbose "test $tid: missing banner file" +( ${SSH} -F $OBJ/ssh_proxy otherhost true 2>$OBJ/banner.out && \ + cmp $OBJ/empty.in $OBJ/banner.out ) || \ + fail "missing banner file" + +for s in 0 10 100 1000 10000 100000 ; do + if [ "$s" = "0" ]; then + # create empty banner + touch $OBJ/banner.in + elif [ "$s" = "10" ]; then + # create 10-byte banner file + echo "abcdefghi" >$OBJ/banner.in + else + # increase size 10x + cp $OBJ/banner.in $OBJ/banner.out + for i in 0 1 2 3 4 5 6 7 8 ; do + cat $OBJ/banner.out >> $OBJ/banner.in + done + fi + + trace "test banner size $s" + verbose "test $tid: size $s" + ( ${SSH} -F $OBJ/ssh_proxy otherhost true 2>$OBJ/banner.out && \ + cmp $OBJ/banner.in $OBJ/banner.out ) || \ + fail "banner size $s mismatch" +done + +trace "test suppress banner (-q)" +verbose "test $tid: suppress banner (-q)" +# ssh-log-wrapper drops "-q" to preserve debug output so use ssh directly +# for just this test. +( ${REAL_SSH} -q -F $OBJ/ssh_proxy otherhost true 2>$OBJ/banner.out && \ + cmp $OBJ/empty.in $OBJ/banner.out ) || \ + fail "suppress banner (-q)" + +rm -f $OBJ/banner.out $OBJ/banner.in $OBJ/empty.in diff --git a/aosp/external/openssh/regress/broken-pipe.sh b/aosp/external/openssh/regress/broken-pipe.sh new file mode 100644 index 0000000000000000000000000000000000000000..c69276e27057d6d208adf92aacbd138cba593e8b --- /dev/null +++ b/aosp/external/openssh/regress/broken-pipe.sh @@ -0,0 +1,12 @@ +# $OpenBSD: broken-pipe.sh,v 1.6 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="broken pipe test" + +for i in 1 2 3 4; do + ${SSH} -F $OBJ/ssh_config_config nexthost echo $i 2> /dev/null | true + r=$? + if [ $r -ne 0 ]; then + fail "broken pipe returns $r" + fi +done diff --git a/aosp/external/openssh/regress/brokenkeys.sh b/aosp/external/openssh/regress/brokenkeys.sh new file mode 100644 index 0000000000000000000000000000000000000000..9d5a54fa9db5269173c12c8061da494c479e367c --- /dev/null +++ b/aosp/external/openssh/regress/brokenkeys.sh @@ -0,0 +1,23 @@ +# $OpenBSD: brokenkeys.sh,v 1.2 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="broken keys" + +KEYS="$OBJ/authorized_keys_${USER}" + +start_sshd + +mv ${KEYS} ${KEYS}.bak + +# Truncated key +echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEABTM= bad key" > $KEYS +cat ${KEYS}.bak >> ${KEYS} +cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER + +${SSH} -F $OBJ/ssh_config somehost true +if [ $? -ne 0 ]; then + fail "ssh connect with failed" +fi + +mv ${KEYS}.bak ${KEYS} + diff --git a/aosp/external/openssh/regress/cert-file.sh b/aosp/external/openssh/regress/cert-file.sh new file mode 100644 index 0000000000000000000000000000000000000000..94e672a99b6c4557ffdad8db8ae8b15384164469 --- /dev/null +++ b/aosp/external/openssh/regress/cert-file.sh @@ -0,0 +1,166 @@ +# $OpenBSD: cert-file.sh,v 1.8 2019/11/26 23:43:10 djm Exp $ +# Placed in the Public Domain. + +tid="ssh with certificates" + +rm -f $OBJ/user_ca_key* $OBJ/user_key* +rm -f $OBJ/cert_user_key* + +# Create a CA key +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key1 ||\ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key2 ||\ + fatal "ssh-keygen failed" + +# Make some keys and certificates. +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key1 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key2 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key3 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key4 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key5 || \ + fatal "ssh-keygen failed" + +# Move the certificate to a different address to better control +# when it is offered. +${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \ + -z $$ -n ${USER} $OBJ/user_key1 || + fatal "couldn't sign user_key1 with user_ca_key1" +mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1_1.pub +${SSHKEYGEN} -q -s $OBJ/user_ca_key2 -I "regress user key for $USER" \ + -z $$ -n ${USER} $OBJ/user_key1 || + fatal "couldn't sign user_key1 with user_ca_key2" +mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1_2.pub +${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \ + -z $$ -n ${USER} $OBJ/user_key3 || + fatal "couldn't sign user_key3 with user_ca_key1" +rm $OBJ/user_key3.pub # to test use of private key w/o public half. +${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \ + -z $$ -n ${USER} $OBJ/user_key4 || + fatal "couldn't sign user_key4 with user_ca_key1" +rm $OBJ/user_key4 $OBJ/user_key4.pub # to test no matching pub/private key case. + +trace 'try with identity files' +opts="-F $OBJ/ssh_proxy -oIdentitiesOnly=yes" +opts2="$opts -i $OBJ/user_key1 -i $OBJ/user_key2" +echo "cert-authority $(cat $OBJ/user_ca_key1.pub)" > $OBJ/authorized_keys_$USER + +# Make a clean config that doesn't have any pre-added identities. +cat $OBJ/ssh_proxy | grep -v IdentityFile > $OBJ/no_identity_config + +# XXX: verify that certificate used was what we expect. Needs exposure of +# keys via environment variable or similar. + + # Key with no .pub should work - finding the equivalent *-cert.pub. +verbose "identity cert with no plain public file" +${SSH} -F $OBJ/no_identity_config -oIdentitiesOnly=yes \ + -i $OBJ/user_key3 somehost exit 52 +[ $? -ne 52 ] && fail "ssh failed" + +# CertificateFile matching private key with no .pub file should work. +verbose "CertificateFile with no plain public file" +${SSH} -F $OBJ/no_identity_config -oIdentitiesOnly=yes \ + -oCertificateFile=$OBJ/user_key3-cert.pub \ + -i $OBJ/user_key3 somehost exit 52 +[ $? -ne 52 ] && fail "ssh failed" + +# Just keys should fail +verbose "plain keys" +${SSH} $opts2 somehost exit 52 +r=$? +if [ $r -eq 52 ]; then + fail "ssh succeeded with no certs" +fi + +# Keys with untrusted cert should fail. +verbose "untrusted cert" +opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_2.pub" +${SSH} $opts3 somehost exit 52 +r=$? +if [ $r -eq 52 ]; then + fail "ssh succeeded with bad cert" +fi + +# Good cert with bad key should fail. +verbose "good cert, bad key" +opts3="$opts -i $OBJ/user_key2" +opts3="$opts3 -oCertificateFile=$OBJ/cert_user_key1_1.pub" +${SSH} $opts3 somehost exit 52 +r=$? +if [ $r -eq 52 ]; then + fail "ssh succeeded with no matching key" +fi + +# Keys with one trusted cert, should succeed. +verbose "single trusted" +opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_1.pub" +${SSH} $opts3 somehost exit 52 +r=$? +if [ $r -ne 52 ]; then + fail "ssh failed with trusted cert and key" +fi + +# Multiple certs and keys, with one trusted cert, should succeed. +verbose "multiple trusted" +opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_2.pub" +opts3="$opts3 -oCertificateFile=$OBJ/cert_user_key1_1.pub" +${SSH} $opts3 somehost exit 52 +r=$? +if [ $r -ne 52 ]; then + fail "ssh failed with multiple certs" +fi + +#next, using an agent in combination with the keys +SSH_AUTH_SOCK=/nonexistent ${SSHADD} -l > /dev/null 2>&1 +if [ $? -ne 2 ]; then + fatal "ssh-add -l did not fail with exit code 2" +fi + +trace "start agent" +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fatal "could not start ssh-agent: exit code $r" +fi + +# add private keys to agent +${SSHADD} -k $OBJ/user_key2 > /dev/null 2>&1 +if [ $? -ne 0 ]; then + fatal "ssh-add did not succeed with exit code 0" +fi +${SSHADD} -k $OBJ/user_key1 > /dev/null 2>&1 +if [ $? -ne 0 ]; then + fatal "ssh-add did not succeed with exit code 0" +fi + +# try ssh with the agent and certificates +opts="-F $OBJ/ssh_proxy" +# with no certificates, should fail +${SSH} $opts somehost exit 52 +if [ $? -eq 52 ]; then + fail "ssh connect with agent in succeeded with no cert" +fi + +#with an untrusted certificate, should fail +opts="$opts -oCertificateFile=$OBJ/cert_user_key1_2.pub" +${SSH} $opts somehost exit 52 +if [ $? -eq 52 ]; then + fail "ssh connect with agent in succeeded with bad cert" +fi + +#with an additional trusted certificate, should succeed +opts="$opts -oCertificateFile=$OBJ/cert_user_key1_1.pub" +${SSH} $opts somehost exit 52 +if [ $? -ne 52 ]; then + fail "ssh connect with agent in failed with good cert" +fi + +trace "kill agent" +${SSHAGENT} -k > /dev/null + +#cleanup +rm -f $OBJ/user_ca_key* $OBJ/user_key* +rm -f $OBJ/cert_user_key* diff --git a/aosp/external/openssh/regress/cert-hostkey.sh b/aosp/external/openssh/regress/cert-hostkey.sh new file mode 100644 index 0000000000000000000000000000000000000000..a3414e1a5c50b378121b801279a9c0bba0b8118f --- /dev/null +++ b/aosp/external/openssh/regress/cert-hostkey.sh @@ -0,0 +1,325 @@ +# $OpenBSD: cert-hostkey.sh,v 1.27 2021/09/30 05:26:26 dtucker Exp $ +# Placed in the Public Domain. + +tid="certified host keys" + +rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/host_revoked_* +rm -f $OBJ/cert_host_key* $OBJ/host_krl_* + +# Allow all hostkey/pubkey types, prefer certs for the client +rsa=0 +types="" +for i in `$SSH -Q key | maybe_filter_sk`; do + if [ -z "$types" ]; then + types="$i" + continue + fi + case "$i" in + # Special treatment for RSA keys. + *rsa*cert*) + types="rsa-sha2-256-cert-v01@openssh.com,$i,$types" + types="rsa-sha2-512-cert-v01@openssh.com,$types";; + *rsa*) + rsa=1 + types="$types,rsa-sha2-512,rsa-sha2-256,$i";; + # Prefer certificate to plain keys. + *cert*) types="$i,$types";; + *) types="$types,$i";; + esac +done +( + echo "HostKeyAlgorithms ${types}" + echo "PubkeyAcceptedAlgorithms *" +) >> $OBJ/ssh_proxy +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak +( + echo "HostKeyAlgorithms *" + echo "PubkeyAcceptedAlgorithms *" +) >> $OBJ/sshd_proxy_bak + +HOSTS='localhost-with-alias,127.0.0.1,::1' + +kh_ca() { + for k in "$@" ; do + printf "@cert-authority $HOSTS " + cat $OBJ/$k || fatal "couldn't cat $k" + done +} +kh_revoke() { + for k in "$@" ; do + printf "@revoked * " + cat $OBJ/$k || fatal "couldn't cat $k" + done +} + +# Create a CA key and add it to known hosts. Ed25519 chosen for speed. +# RSA for testing RSA/SHA2 signatures if supported. +ktype2=ed25519 +[ "x$rsa" = "x1" ] && ktype2=rsa +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/host_ca_key ||\ + fail "ssh-keygen of host_ca_key failed" +${SSHKEYGEN} -q -N '' -t $ktype2 -f $OBJ/host_ca_key2 ||\ + fail "ssh-keygen of host_ca_key failed" + +kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig +cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + +# Plain text revocation files +touch $OBJ/host_revoked_empty +touch $OBJ/host_revoked_plain +touch $OBJ/host_revoked_cert +cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca + +PLAIN_TYPES=`echo "$SSH_KEYTYPES" | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` + +if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then + PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" +fi + +# Prepare certificate, plain key and CA KRLs +${SSHKEYGEN} -kf $OBJ/host_krl_empty || fatal "KRL init failed" +${SSHKEYGEN} -kf $OBJ/host_krl_plain || fatal "KRL init failed" +${SSHKEYGEN} -kf $OBJ/host_krl_cert || fatal "KRL init failed" +${SSHKEYGEN} -kf $OBJ/host_krl_ca $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub \ + || fatal "KRL init failed" + +# Generate and sign host keys +serial=1 +for ktype in $PLAIN_TYPES ; do + verbose "$tid: sign host ${ktype} cert" + # Generate and sign a host key + ${SSHKEYGEN} -q -N '' -t ${ktype} \ + -f $OBJ/cert_host_key_${ktype} || \ + fatal "ssh-keygen of cert_host_key_${ktype} failed" + ${SSHKEYGEN} -ukf $OBJ/host_krl_plain \ + $OBJ/cert_host_key_${ktype}.pub || fatal "KRL update failed" + cat $OBJ/cert_host_key_${ktype}.pub >> $OBJ/host_revoked_plain + case $ktype in + rsa-sha2-*) tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;; + *) tflag=""; ca="$OBJ/host_ca_key" ;; + esac + ${SSHKEYGEN} -h -q -s $ca -z $serial $tflag \ + -I "regress host key for $USER" \ + -n $HOSTS $OBJ/cert_host_key_${ktype} || + fatal "couldn't sign cert_host_key_${ktype}" + ${SSHKEYGEN} -ukf $OBJ/host_krl_cert \ + $OBJ/cert_host_key_${ktype}-cert.pub || \ + fatal "KRL update failed" + cat $OBJ/cert_host_key_${ktype}-cert.pub >> $OBJ/host_revoked_cert + serial=`expr $serial + 1` +done + +attempt_connect() { + _ident="$1" + _expect_success="$2" + shift; shift + verbose "$tid: $_ident expect success $_expect_success" + cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ + -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ + "$@" -F $OBJ/ssh_proxy somehost true + _r=$? + if [ "x$_expect_success" = "xyes" ] ; then + if [ $_r -ne 0 ]; then + fail "ssh cert connect $_ident failed" + fi + else + if [ $_r -eq 0 ]; then + fail "ssh cert connect $_ident succeeded unexpectedly" + fi + fi +} + +# Basic connect and revocation tests. +for ktype in $PLAIN_TYPES ; do + verbose "$tid: host ${ktype} cert connect" + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${ktype} + echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub + ) > $OBJ/sshd_proxy + + # test name expect success + attempt_connect "$ktype basic connect" "yes" + attempt_connect "$ktype empty KRL" "yes" \ + -oRevokedHostKeys=$OBJ/host_krl_empty + attempt_connect "$ktype KRL w/ plain key revoked" "no" \ + -oRevokedHostKeys=$OBJ/host_krl_plain + attempt_connect "$ktype KRL w/ cert revoked" "no" \ + -oRevokedHostKeys=$OBJ/host_krl_cert + attempt_connect "$ktype KRL w/ CA revoked" "no" \ + -oRevokedHostKeys=$OBJ/host_krl_ca + attempt_connect "$ktype empty plaintext revocation" "yes" \ + -oRevokedHostKeys=$OBJ/host_revoked_empty + attempt_connect "$ktype plain key plaintext revocation" "no" \ + -oRevokedHostKeys=$OBJ/host_revoked_plain + attempt_connect "$ktype cert plaintext revocation" "no" \ + -oRevokedHostKeys=$OBJ/host_revoked_cert + attempt_connect "$ktype CA plaintext revocation" "no" \ + -oRevokedHostKeys=$OBJ/host_revoked_ca +done + +# Revoked certificates with key present +kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig +for ktype in $PLAIN_TYPES ; do + test -f "$OBJ/cert_host_key_${ktype}.pub" || fatal "no pubkey" + kh_revoke cert_host_key_${ktype}.pub >> $OBJ/known_hosts-cert.orig +done +cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert +for ktype in $PLAIN_TYPES ; do + verbose "$tid: host ${ktype} revoked cert" + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${ktype} + echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub + ) > $OBJ/sshd_proxy + + cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ + -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi +done + +# Revoked CA +kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig +kh_revoke host_ca_key.pub host_ca_key2.pub >> $OBJ/known_hosts-cert.orig +cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert +for ktype in $PLAIN_TYPES ; do + verbose "$tid: host ${ktype} revoked cert" + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${ktype} + echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub + ) > $OBJ/sshd_proxy + cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ + -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi +done + +# Create a CA key and add it to known hosts +kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig +cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + +test_one() { + ident=$1 + result=$2 + sign_opts=$3 + + for kt in $PLAIN_TYPES; do + case $ktype in + rsa-sha2-*) tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;; + *) tflag=""; ca="$OBJ/host_ca_key" ;; + esac + ${SSHKEYGEN} -q -s $ca $tflag -I "regress host key for $USER" \ + $sign_opts $OBJ/cert_host_key_${kt} || + fatal "couldn't sign cert_host_key_${kt}" + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${kt} + echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub + ) > $OBJ/sshd_proxy + + cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ + -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + rc=$? + if [ "x$result" = "xsuccess" ] ; then + if [ $rc -ne 0 ]; then + fail "ssh cert connect $ident failed unexpectedly" + fi + else + if [ $rc -eq 0 ]; then + fail "ssh cert connect $ident succeeded unexpectedly" + fi + fi + done +} + +test_one "user-certificate" failure "-n $HOSTS" +test_one "empty principals" success "-h" +test_one "wrong principals" failure "-h -n foo" +test_one "cert not yet valid" failure "-h -V20300101:20320101" +test_one "cert expired" failure "-h -V19800101:19900101" +test_one "cert valid interval" success "-h -V-1w:+2w" +test_one "cert has constraints" failure "-h -Oforce-command=false" + +# Check downgrade of cert to raw key when no CA found +for ktype in $PLAIN_TYPES ; do + rm -f $OBJ/known_hosts-cert $OBJ/cert_host_key* + verbose "$tid: host ${ktype} ${v} cert downgrade to raw key" + # Generate and sign a host key + ${SSHKEYGEN} -q -N '' -t ${ktype} -f $OBJ/cert_host_key_${ktype} || \ + fail "ssh-keygen of cert_host_key_${ktype} failed" + case $ktype in + rsa-sha2-*) tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;; + *) tflag=""; ca="$OBJ/host_ca_key" ;; + esac + ${SSHKEYGEN} -h -q $tflag -s $ca $tflag \ + -I "regress host key for $USER" \ + -n $HOSTS $OBJ/cert_host_key_${ktype} || + fatal "couldn't sign cert_host_key_${ktype}" + ( + printf "$HOSTS " + cat $OBJ/cert_host_key_${ktype}.pub + ) > $OBJ/known_hosts-cert + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${ktype} + echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub + ) > $OBJ/sshd_proxy + + ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ + -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "ssh cert connect failed" + fi + # Also check that it works when the known_hosts file is not in the + # first array position. + ${SSH} -oUserKnownHostsFile="/dev/null $OBJ/known_hosts-cert" \ + -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "ssh cert connect failed known_hosts 2nd" + fi +done + +# Wrong certificate +kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig +cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert +for kt in $PLAIN_TYPES ; do + verbose "$tid: host ${kt} connect wrong cert" + rm -f $OBJ/cert_host_key* + # Self-sign key + ${SSHKEYGEN} -q -N '' -t ${kt} -f $OBJ/cert_host_key_${kt} || \ + fail "ssh-keygen of cert_host_key_${kt} failed" + case $kt in + rsa-sha2-*) tflag="-t $kt" ;; + *) tflag="" ;; + esac + ${SSHKEYGEN} $tflag -h -q -s $OBJ/cert_host_key_${kt} \ + -I "regress host key for $USER" \ + -n $HOSTS $OBJ/cert_host_key_${kt} || + fatal "couldn't sign cert_host_key_${kt}" + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${kt} + echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub + ) > $OBJ/sshd_proxy + + cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ + -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ + -F $OBJ/ssh_proxy -q somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect $ident succeeded unexpectedly" + fi +done + +rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/cert_host_key* diff --git a/aosp/external/openssh/regress/cert-userkey.sh b/aosp/external/openssh/regress/cert-userkey.sh new file mode 100644 index 0000000000000000000000000000000000000000..4ea29b7cdbb4d5e060637663031237b42a706943 --- /dev/null +++ b/aosp/external/openssh/regress/cert-userkey.sh @@ -0,0 +1,396 @@ +# $OpenBSD: cert-userkey.sh,v 1.28 2021/09/30 05:26:26 dtucker Exp $ +# Placed in the Public Domain. + +tid="certified user keys" + +rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key* +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak +cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak + +PLAIN_TYPES=`$SSH -Q key-plain | maybe_filter_sk | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` +EXTRA_TYPES="" +rsa="" + +if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then + rsa=rsa + PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" +fi + +kname() { + case $1 in + rsa-sha2-*) n="$1" ;; + sk-ecdsa-*) n="sk-ecdsa" ;; + sk-ssh-ed25519*) n="sk-ssh-ed25519" ;; + # subshell because some seds will add a newline + *) n=$(echo $1 | sed 's/^dsa/ssh-dss/;s/^rsa/ssh-rsa/;s/^ed/ssh-ed/') ;; + esac + if [ -z "$rsa" ]; then + echo "$n*,ssh-ed25519*" + else + echo "$n*,ssh-rsa*,ssh-ed25519*" + fi +} + +# Create a CA key +if [ ! -z "$rsa" ]; then + catype=rsa +else + catype=ed25519 +fi +${SSHKEYGEN} -q -N '' -t $catype -f $OBJ/user_ca_key ||\ + fail "ssh-keygen of user_ca_key failed" + +# Generate and sign user keys +for ktype in $PLAIN_TYPES $EXTRA_TYPES ; do + verbose "$tid: sign user ${ktype} cert" + ${SSHKEYGEN} -q -N '' -t ${ktype} \ + -f $OBJ/cert_user_key_${ktype} || \ + fatal "ssh-keygen of cert_user_key_${ktype} failed" + # Generate RSA/SHA2 certs for rsa-sha2* keys. + case $ktype in + rsa-sha2-*) tflag="-t $ktype" ;; + *) tflag="" ;; + esac + ${SSHKEYGEN} -q -s $OBJ/user_ca_key -z $$ \ + -I "regress user key for $USER" \ + -n ${USER},mekmitasdigoat $tflag $OBJ/cert_user_key_${ktype} || \ + fatal "couldn't sign cert_user_key_${ktype}" +done + +# Test explicitly-specified principals +for ktype in $EXTRA_TYPES $PLAIN_TYPES ; do + t=$(kname $ktype) + _prefix="${ktype}" + + # Setup for AuthorizedPrincipalsFile + rm -f $OBJ/authorized_keys_$USER + ( + cat $OBJ/sshd_proxy_bak + echo "AuthorizedPrincipalsFile " \ + "$OBJ/authorized_principals_%u" + echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" + echo "PubkeyAcceptedAlgorithms ${t}" + ) > $OBJ/sshd_proxy + ( + cat $OBJ/ssh_proxy_bak + echo "PubkeyAcceptedAlgorithms ${t}" + ) > $OBJ/ssh_proxy + + # Missing authorized_principals + verbose "$tid: ${_prefix} missing authorized_principals" + rm -f $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi + + # Empty authorized_principals + verbose "$tid: ${_prefix} empty authorized_principals" + echo > $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi + + # Wrong authorized_principals + verbose "$tid: ${_prefix} wrong authorized_principals" + echo gregorsamsa > $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi + + # Correct authorized_principals + verbose "$tid: ${_prefix} correct authorized_principals" + echo mekmitasdigoat > $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh cert connect failed" + fi + + # authorized_principals with bad key option + verbose "$tid: ${_prefix} authorized_principals bad key opt" + echo 'blah mekmitasdigoat' > $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi + + # authorized_principals with command=false + verbose "$tid: ${_prefix} authorized_principals command=false" + echo 'command="false" mekmitasdigoat' > \ + $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi + + + # authorized_principals with command=true + verbose "$tid: ${_prefix} authorized_principals command=true" + echo 'command="true" mekmitasdigoat' > \ + $OBJ/authorized_principals_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost false >/dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh cert connect failed" + fi + + # Setup for principals= key option + rm -f $OBJ/authorized_principals_$USER + ( + cat $OBJ/sshd_proxy_bak + echo "PubkeyAcceptedAlgorithms ${t}" + ) > $OBJ/sshd_proxy + ( + cat $OBJ/ssh_proxy_bak + echo "PubkeyAcceptedAlgorithms ${t}" + ) > $OBJ/ssh_proxy + + # Wrong principals list + verbose "$tid: ${_prefix} wrong principals key option" + ( + printf 'cert-authority,principals="gregorsamsa" ' + cat $OBJ/user_ca_key.pub + ) > $OBJ/authorized_keys_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" + fi + + # Correct principals list + verbose "$tid: ${_prefix} correct principals key option" + ( + printf 'cert-authority,principals="mekmitasdigoat" ' + cat $OBJ/user_ca_key.pub + ) > $OBJ/authorized_keys_$USER + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh cert connect failed" + fi +done + +basic_tests() { + auth=$1 + if test "x$auth" = "xauthorized_keys" ; then + # Add CA to authorized_keys + ( + printf 'cert-authority ' + cat $OBJ/user_ca_key.pub + ) > $OBJ/authorized_keys_$USER + else + echo > $OBJ/authorized_keys_$USER + extra_sshd="TrustedUserCAKeys $OBJ/user_ca_key.pub" + fi + + for ktype in $PLAIN_TYPES ; do + t=$(kname $ktype) + _prefix="${ktype} $auth" + # Simple connect + verbose "$tid: ${_prefix} connect" + ( + cat $OBJ/sshd_proxy_bak + echo "PubkeyAcceptedAlgorithms ${t}" + echo "$extra_sshd" + ) > $OBJ/sshd_proxy + ( + cat $OBJ/ssh_proxy_bak + echo "PubkeyAcceptedAlgorithms ${t}" + ) > $OBJ/ssh_proxy + + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "ssh cert connect failed" + fi + + # Revoked keys + verbose "$tid: ${_prefix} revoked key" + ( + cat $OBJ/sshd_proxy_bak + echo "RevokedKeys $OBJ/cert_user_key_revoked" + echo "PubkeyAcceptedAlgorithms ${t}" + echo "$extra_sshd" + ) > $OBJ/sshd_proxy + cp $OBJ/cert_user_key_${ktype}.pub \ + $OBJ/cert_user_key_revoked + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpecedly" + fi + verbose "$tid: ${_prefix} revoked via KRL" + rm $OBJ/cert_user_key_revoked + ${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked \ + $OBJ/cert_user_key_${ktype}.pub + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpecedly" + fi + verbose "$tid: ${_prefix} empty KRL" + ${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "ssh cert connect failed" + fi + done + + # Revoked CA + verbose "$tid: ${ktype} $auth revoked CA key" + ( + cat $OBJ/sshd_proxy_bak + echo "RevokedKeys $OBJ/user_ca_key.pub" + echo "PubkeyAcceptedAlgorithms ${t}" + echo "$extra_sshd" + ) > $OBJ/sshd_proxy + ${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \ + somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpecedly" + fi + + verbose "$tid: $auth CA does not authenticate" + ( + cat $OBJ/sshd_proxy_bak + echo "PubkeyAcceptedAlgorithms ${t}" + echo "$extra_sshd" + ) > $OBJ/sshd_proxy + verbose "$tid: ensure CA key does not authenticate user" + ${SSH} -i $OBJ/user_ca_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect with CA key succeeded unexpectedly" + fi +} + +basic_tests authorized_keys +basic_tests TrustedUserCAKeys + +test_one() { + ident=$1 + result=$2 + sign_opts=$3 + auth_choice=$4 + auth_opt=$5 + + if test "x$auth_choice" = "x" ; then + auth_choice="authorized_keys TrustedUserCAKeys" + fi + + for auth in $auth_choice ; do + for ktype in $rsa ed25519 ; do + cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy + if test "x$auth" = "xauthorized_keys" ; then + # Add CA to authorized_keys + ( + printf "cert-authority${auth_opt} " + cat $OBJ/user_ca_key.pub + ) > $OBJ/authorized_keys_$USER + else + echo > $OBJ/authorized_keys_$USER + echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" \ + >> $OBJ/sshd_proxy + echo "PubkeyAcceptedAlgorithms ${t}*" \ + >> $OBJ/sshd_proxy + if test "x$auth_opt" != "x" ; then + echo $auth_opt >> $OBJ/sshd_proxy + fi + fi + + verbose "$tid: $ident auth $auth expect $result $ktype" + ${SSHKEYGEN} -q -s $OBJ/user_ca_key \ + -I "regress user key for $USER" \ + $sign_opts $OBJ/cert_user_key_${ktype} || + fail "couldn't sign cert_user_key_${ktype}" + + ${SSH} -i $OBJ/cert_user_key_${ktype} \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 + rc=$? + if [ "x$result" = "xsuccess" ] ; then + if [ $rc -ne 0 ]; then + fail "$ident failed unexpectedly" + fi + else + if [ $rc -eq 0 ]; then + fail "$ident succeeded unexpectedly" + fi + fi + done + done +} + +test_one "correct principal" success "-n ${USER}" +test_one "host-certificate" failure "-n ${USER} -h" +test_one "wrong principals" failure "-n foo" +test_one "cert not yet valid" failure "-n ${USER} -V20300101:20320101" +test_one "cert expired" failure "-n ${USER} -V19800101:19900101" +test_one "cert valid interval" success "-n ${USER} -V-1w:+2w" +test_one "wrong source-address" failure "-n ${USER} -Osource-address=10.0.0.0/8" +test_one "force-command" failure "-n ${USER} -Oforce-command=false" + +# Behaviour is different here: TrustedUserCAKeys doesn't allow empty principals +test_one "empty principals" success "" authorized_keys +test_one "empty principals" failure "" TrustedUserCAKeys + +# Check explicitly-specified principals: an empty principals list in the cert +# should always be refused. + +# AuthorizedPrincipalsFile +rm -f $OBJ/authorized_keys_$USER +echo mekmitasdigoat > $OBJ/authorized_principals_$USER +test_one "AuthorizedPrincipalsFile principals" success "-n mekmitasdigoat" \ + TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u" +test_one "AuthorizedPrincipalsFile no principals" failure "" \ + TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u" + +# principals= key option +rm -f $OBJ/authorized_principals_$USER +test_one "principals key option principals" success "-n mekmitasdigoat" \ + authorized_keys ',principals="mekmitasdigoat"' +test_one "principals key option no principals" failure "" \ + authorized_keys ',principals="mekmitasdigoat"' + +# command= options vs. force-command in key +test_one "force-command match true" success \ + "-n ${USER} -Oforce-command=true" \ + authorized_keys ',command="true"' +test_one "force-command match true" failure \ + "-n ${USER} -Oforce-command=false" \ + authorized_keys ',command="false"' +test_one "force-command mismatch 1" failure \ + "-n ${USER} -Oforce-command=false" \ + authorized_keys ',command="true"' +test_one "force-command mismatch 2" failure \ + "-n ${USER} -Oforce-command=true" \ + authorized_keys ',command="false"' + +# Wrong certificate +cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy +for ktype in $PLAIN_TYPES ; do + t=$(kname $ktype) + # Self-sign + ${SSHKEYGEN} -q -s $OBJ/cert_user_key_${ktype} -I \ + "regress user key for $USER" \ + -n $USER $OBJ/cert_user_key_${ktype} || + fatal "couldn't sign cert_user_key_${ktype}" + verbose "$tid: user ${ktype} connect wrong cert" + ${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \ + somehost true >/dev/null 2>&1 + if [ $? -eq 0 ]; then + fail "ssh cert connect $ident succeeded unexpectedly" + fi +done + +rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key* +rm -f $OBJ/authorized_principals_$USER + diff --git a/aosp/external/openssh/regress/cfginclude.sh b/aosp/external/openssh/regress/cfginclude.sh new file mode 100644 index 0000000000000000000000000000000000000000..f5b492f1786767a1fc19eb362171518b6556269e --- /dev/null +++ b/aosp/external/openssh/regress/cfginclude.sh @@ -0,0 +1,293 @@ +# $OpenBSD: cfginclude.sh,v 1.3 2021/06/08 06:52:43 djm Exp $ +# Placed in the Public Domain. + +tid="config include" + +# to appease StrictModes +umask 022 + +cat > $OBJ/ssh_config.i << _EOF +Match host a + Hostname aa + +Match host b # comment + Hostname bb + Include $OBJ/ssh_config.i.* + +Match host c + Include $OBJ/ssh_config.i.* + Hostname cc + +Match host m + Include $OBJ/ssh_config.i.* # comment + +Host d + Hostname dd # comment + +Host e + Hostname ee + Include $OBJ/ssh_config.i.* + +Host f + Include $OBJ/ssh_config.i.* + Hostname ff + +Host n + Include $OBJ/ssh_config.i.* +_EOF + +cat > $OBJ/ssh_config.i.0 << _EOF +Match host xxxxxx +_EOF + +cat > $OBJ/ssh_config.i.1 << _EOF +Match host a + Hostname aaa + +Match host b + Hostname bbb + +Match host c # comment + Hostname ccc + +Host d # comment + Hostname ddd + +Host e + Hostname eee + +Host f + Hostname fff # comment +_EOF + +cat > $OBJ/ssh_config.i.2 << _EOF +Match host a + Hostname aaaa + +Match host b + Hostname bbbb + +Match host c + Hostname cccc + +Host d + Hostname dddd + +Host e + Hostname eeee + +Host f + Hostname ffff + +Match all + Hostname xxxx +_EOF + +trial() { + _host="$1" + _exp="$2" + ${REAL_SSH} -F $OBJ/ssh_config.i -G "$_host" > $OBJ/ssh_config.out || + fatal "ssh config parse failed" + _got=`grep -i '^hostname ' $OBJ/ssh_config.out | awk '{print $2}'` + if test "x$_exp" != "x$_got" ; then + fail "host $_host include fail: expected $_exp got $_got" + fi +} + +trial a aa +trial b bb +trial c ccc +trial d dd +trial e ee +trial f fff +trial m xxxx +trial n xxxx +trial x x + +# Prepare an included config with an error. + +cat > $OBJ/ssh_config.i.3 << _EOF +Hostname xxxx + Junk +_EOF + +${REAL_SSH} -F $OBJ/ssh_config.i -G a 2>/dev/null && \ + fail "ssh include allowed invalid config" + +${REAL_SSH} -F $OBJ/ssh_config.i -G x 2>/dev/null && \ + fail "ssh include allowed invalid config" + +rm -f $OBJ/ssh_config.i.* + +# Ensure that a missing include is not fatal. +cat > $OBJ/ssh_config.i << _EOF +Include $OBJ/ssh_config.i.* +Hostname aa +_EOF + +trial a aa + +# Ensure that Match/Host in an included config does not affect parent. +cat > $OBJ/ssh_config.i.x << _EOF +Match host x +_EOF + +trial a aa + +cat > $OBJ/ssh_config.i.x << _EOF +Host x +_EOF + +trial a aa + +# cleanup +rm -f $OBJ/ssh_config.i $OBJ/ssh_config.i.* $OBJ/ssh_config.out +# $OpenBSD: cfginclude.sh,v 1.3 2021/06/08 06:52:43 djm Exp $ +# Placed in the Public Domain. + +tid="config include" + +cat > $OBJ/ssh_config.i << _EOF +Match host a + Hostname aa + +Match host b + Hostname bb + Include $OBJ/ssh_config.i.* + +Match host c + Include $OBJ/ssh_config.i.* + Hostname cc + +Match host m + Include $OBJ/ssh_config.i.* + +Host d + Hostname dd + +Host e + Hostname ee + Include $OBJ/ssh_config.i.* + +Host f + Include $OBJ/ssh_config.i.* + Hostname ff + +Host n + Include $OBJ/ssh_config.i.* +_EOF + +cat > $OBJ/ssh_config.i.0 << _EOF +Match host xxxxxx +_EOF + +cat > $OBJ/ssh_config.i.1 << _EOF +Match host a + Hostname aaa + +Match host b # comment + Hostname bbb + +Match host c + Hostname ccc # comment + +Host d + Hostname ddd + +Host e + Hostname eee + +Host f + Hostname fff +_EOF + +cat > $OBJ/ssh_config.i.2 << _EOF +Match host a + Hostname aaaa + +Match host b + Hostname bbbb + +Match host c + Hostname cccc + +Host d + Hostname dddd + +Host e + Hostname eeee + +Host f + Hostname ffff + +Match all # comment + Hostname xxxx # comment +_EOF + +trial() { + _host="$1" + _exp="$2" + ${REAL_SSH} -F $OBJ/ssh_config.i -G "$_host" > $OBJ/ssh_config.out || + fatal "ssh config parse failed" + _got=`grep -i '^hostname ' $OBJ/ssh_config.out | awk '{print $2}'` + if test "x$_exp" != "x$_got" ; then + fail "host $_host include fail: expected $_exp got $_got" + fi +} + +trial a aa +trial b bb +trial c ccc +trial d dd +trial e ee +trial f fff +trial m xxxx +trial n xxxx +trial x x + +# Prepare an included config with an error. + +cat > $OBJ/ssh_config.i.3 << _EOF +Hostname xxxx + Junk +_EOF + +${REAL_SSH} -F $OBJ/ssh_config.i -G a 2>/dev/null && \ + fail "ssh include allowed invalid config" + +${REAL_SSH} -F $OBJ/ssh_config.i -G x 2>/dev/null && \ + fail "ssh include allowed invalid config" + +rm -f $OBJ/ssh_config.i.* + +# Ensure that a missing include is not fatal. +cat > $OBJ/ssh_config.i << _EOF +Include $OBJ/ssh_config.i.* +Hostname aa +_EOF + +trial a aa + +# Ensure that Match/Host in an included config does not affect parent. +cat > $OBJ/ssh_config.i.x << _EOF +Match host x +_EOF + +trial a aa + +cat > $OBJ/ssh_config.i.x << _EOF +Host x +_EOF + +trial a aa + +# Ensure that recursive includes are bounded. +cat > $OBJ/ssh_config.i << _EOF +Include $OBJ/ssh_config.i +_EOF + +${REAL_SSH} -F $OBJ/ssh_config.i -G a 2>/dev/null && \ + fail "ssh include allowed infinite recursion?" # or hang... + +# cleanup +rm -f $OBJ/ssh_config.i $OBJ/ssh_config.i.* $OBJ/ssh_config.out diff --git a/aosp/external/openssh/regress/cfgmatch.sh b/aosp/external/openssh/regress/cfgmatch.sh new file mode 100644 index 0000000000000000000000000000000000000000..05a6668551a9e632c230b5a8ed6dd42ef5cb2cc0 --- /dev/null +++ b/aosp/external/openssh/regress/cfgmatch.sh @@ -0,0 +1,158 @@ +# $OpenBSD: cfgmatch.sh,v 1.13 2021/06/08 06:52:43 djm Exp $ +# Placed in the Public Domain. + +tid="sshd_config match" + +pidfile=$OBJ/remote_pid +fwdport=3301 +fwd="-L $fwdport:127.0.0.1:$PORT" + +echo "ExitOnForwardFailure=yes" >> $OBJ/ssh_config +echo "ExitOnForwardFailure=yes" >> $OBJ/ssh_proxy + +start_client() +{ + rm -f $pidfile + ${SSH} -q $fwd "$@" somehost \ + exec sh -c \'"echo \$\$ > $pidfile; exec sleep 100"\' \ + >>$TEST_REGRESS_LOGFILE 2>&1 & + client_pid=$! + # Wait for remote end + n=0 + while test ! -f $pidfile ; do + sleep 1 + n=`expr $n + 1` + if test $n -gt 60; then + kill $client_pid + fatal "timeout waiting for background ssh" + fi + done +} + +stop_client() +{ + pid=`cat $pidfile` + if [ ! -z "$pid" ]; then + kill $pid + fi + wait +} + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak +echo "PermitOpen 127.0.0.1:1 # comment" >>$OBJ/sshd_config +echo "Match Address 127.0.0.1" >>$OBJ/sshd_config +echo "PermitOpen 127.0.0.1:2 127.0.0.1:3 127.0.0.1:$PORT" >>$OBJ/sshd_config + +grep -v AuthorizedKeysFile $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy +echo "AuthorizedKeysFile /dev/null # comment" >>$OBJ/sshd_proxy +echo "PermitOpen 127.0.0.1:1" >>$OBJ/sshd_proxy +echo "Match user $USER" >>$OBJ/sshd_proxy +echo "AuthorizedKeysFile /dev/null $OBJ/authorized_keys_%u" >>$OBJ/sshd_proxy +echo "Match Address 127.0.0.1 # comment" >>$OBJ/sshd_proxy +echo "PermitOpen 127.0.0.1:2 127.0.0.1:3 127.0.0.1:$PORT" >>$OBJ/sshd_proxy + +${SUDO} ${SSHD} -f $OBJ/sshd_config -T >/dev/null || \ + fail "config w/match fails config test" + +start_sshd + +# Test Match + PermitOpen in sshd_config. This should be permitted +trace "match permitopen localhost" +start_client -F $OBJ/ssh_config +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match permitopen permit" +stop_client + +# Same but from different source. This should not be permitted +trace "match permitopen proxy" +start_client -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true && \ + fail "match permitopen deny" +stop_client + +# Retry previous with key option, should also be denied. +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'permitopen="127.0.0.1:'$PORT'" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done +trace "match permitopen proxy w/key opts" +start_client -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true && \ + fail "match permitopen deny w/key opt" +stop_client + +# Test both sshd_config and key options permitting the same dst/port pair. +# Should be permitted. +trace "match permitopen localhost" +start_client -F $OBJ/ssh_config +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match permitopen permit" +stop_client + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitOpen 127.0.0.1:1 127.0.0.1:$PORT 127.0.0.2:2" >>$OBJ/sshd_proxy +echo "Match User $USER" >>$OBJ/sshd_proxy +echo "PermitOpen 127.0.0.1:1 127.0.0.1:2" >>$OBJ/sshd_proxy + +# Test that a Match overrides a PermitOpen in the global section +trace "match permitopen proxy w/key opts" +start_client -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true && \ + fail "match override permitopen" +stop_client + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitOpen 127.0.0.1:1 127.0.0.1:$PORT 127.0.0.2:2" >>$OBJ/sshd_proxy +echo "Match User NoSuchUser" >>$OBJ/sshd_proxy +echo "PermitOpen 127.0.0.1:1 127.0.0.1:2" >>$OBJ/sshd_proxy + +# Test that a rule that doesn't match doesn't override, plus test a +# PermitOpen entry that's not at the start of the list +trace "nomatch permitopen proxy w/key opts" +start_client -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "nomatch override permitopen" +stop_client + +# Test parsing of available Match criteria (with the exception of Group which +# requires knowledge of actual group memberships user running the test). +params="user:user:u1 host:host:h1 address:addr:1.2.3.4 \ + localaddress:laddr:5.6.7.8 rdomain:rdomain:rdom1" +cp $OBJ/sshd_proxy_bak $OBJ/sshd_config +echo 'Banner /nomatch' >>$OBJ/sshd_config +for i in $params; do + config=`echo $i | cut -f1 -d:` + criteria=`echo $i | cut -f2 -d:` + value=`echo $i | cut -f3 -d:` + cat >>$OBJ/sshd_config </dev/null || \ + fail "validate config for w/out spec" + +# Test matching each criteria. +for i in $params; do + testcriteria=`echo $i | cut -f2 -d:` + expected=/`echo $i | cut -f3 -d:` + spec="" + for j in $params; do + config=`echo $j | cut -f1 -d:` + criteria=`echo $j | cut -f2 -d:` + value=`echo $j | cut -f3 -d:` + if [ "$criteria" = "$testcriteria" ]; then + spec="$criteria=$value,$spec" + else + spec="$criteria=1$value,$spec" + fi + done + trace "test spec $spec" + result=`${SUDO} ${SSHD} -f $OBJ/sshd_config -T -C "$spec" | \ + awk '$1=="banner"{print $2}'` + if [ "$result" != "$expected" ]; then + fail "match $config expected $expected got $result" + fi +done diff --git a/aosp/external/openssh/regress/cfgmatchlisten.sh b/aosp/external/openssh/regress/cfgmatchlisten.sh new file mode 100644 index 0000000000000000000000000000000000000000..a4fd66b3224c64d186ffb1b3c9cac36bc00163f3 --- /dev/null +++ b/aosp/external/openssh/regress/cfgmatchlisten.sh @@ -0,0 +1,202 @@ +# $OpenBSD: cfgmatchlisten.sh,v 1.3 2018/07/02 14:13:30 dtucker Exp $ +# Placed in the Public Domain. + +tid="sshd_config matchlisten" + +pidfile=$OBJ/remote_pid +fwdport=3301 +fwdspec="localhost:${fwdport}" +fwd="-R $fwdport:127.0.0.1:$PORT" + +echo "ExitOnForwardFailure=yes" >> $OBJ/ssh_config +echo "ExitOnForwardFailure=yes" >> $OBJ/ssh_proxy + +start_client() +{ + rm -f $pidfile + ${SSH} -vvv $fwd "$@" somehost true >>$TEST_REGRESS_LOGFILE 2>&1 + r=$? + if [ $r -ne 0 ]; then + return $r + fi + ${SSH} -vvv $fwd "$@" somehost \ + exec sh -c \'"echo \$\$ > $pidfile; exec sleep 100"\' \ + >>$TEST_REGRESS_LOGFILE 2>&1 & + client_pid=$! + # Wait for remote end + n=0 + while test ! -f $pidfile ; do + sleep 1 + n=`expr $n + 1` + if test $n -gt 60; then + kill $client_pid + fatal "timeout waiting for background ssh" + fi + done + return $r +} + +expect_client_ok() +{ + start_client "$@" || + fail "client did not start" +} + +expect_client_fail() +{ + local failmsg="$1" + shift + start_client "$@" && + fail $failmsg +} + +stop_client() +{ + pid=`cat $pidfile` + if [ ! -z "$pid" ]; then + kill $pid + fi + wait +} + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak +echo "PermitListen 127.0.0.1:1" >>$OBJ/sshd_config +echo "Match Address 127.0.0.1" >>$OBJ/sshd_config +echo "PermitListen 127.0.0.1:2 127.0.0.1:3 $fwdspec" >>$OBJ/sshd_config + +grep -v AuthorizedKeysFile $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy +echo "AuthorizedKeysFile /dev/null" >>$OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1" >>$OBJ/sshd_proxy +echo "Match user $USER" >>$OBJ/sshd_proxy +echo "AuthorizedKeysFile /dev/null $OBJ/authorized_keys_%u" >>$OBJ/sshd_proxy +echo "Match Address 127.0.0.1" >>$OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:2 127.0.0.1:3 $fwdspec" >>$OBJ/sshd_proxy + +start_sshd + +#set -x + +# Test Match + PermitListen in sshd_config. This should be permitted +trace "match permitlisten localhost" +expect_client_ok -F $OBJ/ssh_config +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match permitlisten permit" +stop_client + +# Same but from different source. This should not be permitted +trace "match permitlisten proxy" +expect_client_fail "match permitlisten deny" \ + -F $OBJ/ssh_proxy + +# Retry previous with key option, should also be denied. +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'permitlisten="'$fwdspec'" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done +trace "match permitlisten proxy w/key opts" +expect_client_fail "match permitlisten deny w/key opt"\ + -F $OBJ/ssh_proxy + +# Test both sshd_config and key options permitting the same dst/port pair. +# Should be permitted. +trace "match permitlisten localhost" +expect_client_ok -F $OBJ/ssh_config +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match permitlisten permit" +stop_client + +# Test that a bare port number is accepted in PermitListen +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 $fwdport 127.0.0.2:2" >>$OBJ/sshd_proxy +trace "match permitlisten bare" +expect_client_ok -F $OBJ/ssh_config +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match permitlisten bare" +stop_client + +# Test that an incorrect bare port number is denied as expected +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitListen 1 2 99" >>$OBJ/sshd_proxy +trace "match permitlisten bare" +expect_client_fail -F $OBJ/ssh_config + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 $fwdspec 127.0.0.2:2" >>$OBJ/sshd_proxy +echo "Match User $USER" >>$OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 127.0.0.1:2" >>$OBJ/sshd_proxy + +# Test that a Match overrides a PermitListen in the global section +trace "match permitlisten proxy w/key opts" +expect_client_fail "match override permitlisten" \ + -F $OBJ/ssh_proxy + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 $fwdspec 127.0.0.2:2" >>$OBJ/sshd_proxy +echo "Match User NoSuchUser" >>$OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 127.0.0.1:2" >>$OBJ/sshd_proxy + +# Test that a rule that doesn't match doesn't override, plus test a +# PermitListen entry that's not at the start of the list +trace "nomatch permitlisten proxy w/key opts" +expect_client_ok -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "nomatch override permitlisten" +stop_client + +# bind to 127.0.0.1 instead of default localhost +fwdspec2="127.0.0.1:${fwdport}" +fwd="-R ${fwdspec2}:127.0.0.1:$PORT" + +# first try w/ old fwdspec both in server config and key opts +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 $fwdspec 127.0.0.2:2" >>$OBJ/sshd_proxy +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'permitlisten="'$fwdspec'" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done +trace "nomatch permitlisten 127.0.0.1 server config and userkey" +expect_client_fail "nomatch 127.0.0.1 server config and userkey" \ + -F $OBJ/ssh_config + +# correct server config, denied by key opts +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "PermitListen 127.0.0.1:1 ${fwdspec2} 127.0.0.2:2" >>$OBJ/sshd_proxy +trace "nomatch permitlisten 127.0.0.1 w/key opts" +expect_client_fail "nomatch 127.0.0.1 w/key opts" \ + -F $OBJ/ssh_config + +# fix key opts +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'permitlisten="'$fwdspec2'" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done +trace "match permitlisten 127.0.0.1 server config w/key opts" +expect_client_ok -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match 127.0.0.1 server config w/key opts" +stop_client + +# key opts with bare port number +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'permitlisten="'$fwdport'" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done +trace "match permitlisten 127.0.0.1 server config w/key opts (bare)" +expect_client_ok -F $OBJ/ssh_proxy +${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \ + fail "match 127.0.0.1 server config w/key opts (bare)" +stop_client + +# key opts with incorrect bare port number +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'permitlisten="99" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done +trace "match permitlisten 127.0.0.1 server config w/key opts (wrong bare)" +expect_client_fail "nomatch 127.0.0.1 w/key opts (wrong bare)" \ + -F $OBJ/ssh_config diff --git a/aosp/external/openssh/regress/cfgparse.sh b/aosp/external/openssh/regress/cfgparse.sh new file mode 100644 index 0000000000000000000000000000000000000000..a9e5c6b09ee2d66400dfa6ecd57030a7d7d52610 --- /dev/null +++ b/aosp/external/openssh/regress/cfgparse.sh @@ -0,0 +1,75 @@ +# $OpenBSD: cfgparse.sh,v 1.7 2018/05/11 03:51:06 dtucker Exp $ +# Placed in the Public Domain. + +tid="sshd config parse" + +# This is a reasonable proxy for IPv6 support. +if ! config_defined HAVE_STRUCT_IN6_ADDR ; then + SKIP_IPV6=yes +fi + +# We need to use the keys generated for the regression test because sshd -T +# will fail if we're not running with SUDO (no permissions for real keys) or +# if we are running tests on a system that has never had sshd installed +# because the keys won't exist. + +grep "HostKey " $OBJ/sshd_config > $OBJ/sshd_config_minimal +SSHD_KEYS="`cat $OBJ/sshd_config_minimal`" + +verbose "reparse minimal config" +($SUDO ${SSHD} -T -f $OBJ/sshd_config_minimal >$OBJ/sshd_config.1 && + $SUDO ${SSHD} -T -f $OBJ/sshd_config.1 >$OBJ/sshd_config.2 && + diff $OBJ/sshd_config.1 $OBJ/sshd_config.2) || fail "reparse minimal config" + +verbose "reparse regress config" +($SUDO ${SSHD} -T -f $OBJ/sshd_config >$OBJ/sshd_config.1 && + $SUDO ${SSHD} -T -f $OBJ/sshd_config.1 >$OBJ/sshd_config.2 && + diff $OBJ/sshd_config.1 $OBJ/sshd_config.2) || fail "reparse regress config" + +verbose "listenaddress order" +# expected output +cat > $OBJ/sshd_config.0 <> $OBJ/sshd_config.0 < $OBJ/sshd_config.1 <> $OBJ/sshd_config.1 <$OBJ/sshd_config.2 && + diff $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ + fail "listenaddress order 1" +# test 2: listenaddress first +cat > $OBJ/sshd_config.1 <> $OBJ/sshd_config.1 <$OBJ/sshd_config.2 && + diff $OBJ/sshd_config.0 $OBJ/sshd_config.2) || \ + fail "listenaddress order 2" + +# cleanup +rm -f $OBJ/sshd_config.[012] diff --git a/aosp/external/openssh/regress/check-perm.c b/aosp/external/openssh/regress/check-perm.c new file mode 100644 index 0000000000000000000000000000000000000000..dac307d244648fed81f7e59bf4df75d708de3979 --- /dev/null +++ b/aosp/external/openssh/regress/check-perm.c @@ -0,0 +1,205 @@ +/* + * Placed in the public domain + */ + +/* $OpenBSD: modpipe.c,v 1.6 2013/11/21 03:16:47 djm Exp $ */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBGEN_H +#include +#endif + +static void +fatal(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); + exit(1); +} +/* Based on session.c. NB. keep tests in sync */ +static void +safely_chroot(const char *path, uid_t uid) +{ + const char *cp; + char component[PATH_MAX]; + struct stat st; + + if (*path != '/') + fatal("chroot path does not begin at root"); + if (strlen(path) >= sizeof(component)) + fatal("chroot path too long"); + + /* + * Descend the path, checking that each component is a + * root-owned directory with strict permissions. + */ + for (cp = path; cp != NULL;) { + if ((cp = strchr(cp, '/')) == NULL) + strlcpy(component, path, sizeof(component)); + else { + cp++; + memcpy(component, path, cp - path); + component[cp - path] = '\0'; + } + + /* debug3("%s: checking '%s'", __func__, component); */ + + if (stat(component, &st) != 0) + fatal("%s: stat(\"%s\"): %s", __func__, + component, strerror(errno)); + if (st.st_uid != 0 || (st.st_mode & 022) != 0) + fatal("bad ownership or modes for chroot " + "directory %s\"%s\"", + cp == NULL ? "" : "component ", component); + if (!S_ISDIR(st.st_mode)) + fatal("chroot path %s\"%s\" is not a directory", + cp == NULL ? "" : "component ", component); + + } + + if (chdir(path) == -1) + fatal("Unable to chdir to chroot path \"%s\": " + "%s", path, strerror(errno)); +} + +/* from platform.c */ +int +platform_sys_dir_uid(uid_t uid) +{ + if (uid == 0) + return 1; +#ifdef PLATFORM_SYS_DIR_UID + if (uid == PLATFORM_SYS_DIR_UID) + return 1; +#endif + return 0; +} + +/* from auth.c */ +int +auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, + uid_t uid, char *err, size_t errlen) +{ + char buf[PATH_MAX], homedir[PATH_MAX]; + char *cp; + int comparehome = 0; + struct stat st; + + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, + strerror(errno)); + return -1; + } + if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) + comparehome = 1; + + if (!S_ISREG(stp->st_mode)) { + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } + if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || + (stp->st_mode & 022) != 0) { + snprintf(err, errlen, "bad ownership or modes for file %s", + buf); + return -1; + } + + /* for each component of the canonical path, walking upwards */ + for (;;) { + if ((cp = dirname(buf)) == NULL) { + snprintf(err, errlen, "dirname() failed"); + return -1; + } + strlcpy(buf, cp, sizeof(buf)); + + if (stat(buf, &st) < 0 || + (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || + (st.st_mode & 022) != 0) { + snprintf(err, errlen, + "bad ownership or modes for directory %s", buf); + return -1; + } + + /* If are past the homedir then we can stop */ + if (comparehome && strcmp(homedir, buf) == 0) + break; + + /* + * dirname should always complete with a "/" path, + * but we can be paranoid and check for "." too + */ + if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) + break; + } + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "check-perm -m [chroot | keys-command] [path]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *path = "."; + char errmsg[256]; + int ch, mode = -1; + extern char *optarg; + extern int optind; + struct stat st; + + while ((ch = getopt(argc, argv, "hm:")) != -1) { + switch (ch) { + case 'm': + if (strcasecmp(optarg, "chroot") == 0) + mode = 1; + else if (strcasecmp(optarg, "keys-command") == 0) + mode = 2; + else { + fprintf(stderr, "Invalid -m option\n"), + usage(); + } + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + else if (argc == 1) + path = argv[0]; + + if (mode == 1) + safely_chroot(path, getuid()); + else if (mode == 2) { + if (stat(path, &st) < 0) + fatal("Could not stat %s: %s", path, strerror(errno)); + if (auth_secure_path(path, &st, NULL, 0, + errmsg, sizeof(errmsg)) != 0) + fatal("Unsafe %s: %s", path, errmsg); + } else { + fprintf(stderr, "Invalid mode\n"); + usage(); + } + return 0; +} diff --git a/aosp/external/openssh/regress/cipher-speed.sh b/aosp/external/openssh/regress/cipher-speed.sh new file mode 100644 index 0000000000000000000000000000000000000000..1340bd159873fd0c60d741ccc387c8a0ea2ba357 --- /dev/null +++ b/aosp/external/openssh/regress/cipher-speed.sh @@ -0,0 +1,42 @@ +# $OpenBSD: cipher-speed.sh,v 1.14 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="cipher speed" + +# Enable all supported ciphers and macs. +ciphers=`${SSH} -Q Ciphers | tr '\n' , | sed 's/,$//'` +macs=`${SSH} -Q MACs | tr '\n' , | sed 's/,$//'` +cat >>$OBJ/sshd_proxy <&1 | getbytes + + if [ $? -ne 0 ]; then + fail "ssh failed with mac $m cipher $c" + fi + done + # No point trying all MACs for AEAD ciphers since they are ignored. + if ${SSH} -Q cipher-auth | grep "^${c}\$" >/dev/null 2>&1 ; then + break + fi + n=`expr $n + 1` +done; done diff --git a/aosp/external/openssh/regress/conch-ciphers.sh b/aosp/external/openssh/regress/conch-ciphers.sh new file mode 100644 index 0000000000000000000000000000000000000000..6678813a2bdb9745d9ce4f6294a7d57bbf05731a --- /dev/null +++ b/aosp/external/openssh/regress/conch-ciphers.sh @@ -0,0 +1,28 @@ +# $OpenBSD: conch-ciphers.sh,v 1.4 2019/07/05 04:12:46 dtucker Exp $ +# Placed in the Public Domain. + +tid="conch ciphers" + +if test "x$REGRESS_INTEROP_CONCH" != "xyes" ; then + echo "conch interop tests not enabled" + exit 0 +fi + +start_sshd + +for c in aes256-ctr aes256-cbc aes192-ctr aes192-cbc aes128-ctr aes128-cbc \ + cast128-cbc blowfish 3des-cbc ; do + verbose "$tid: cipher $c" + rm -f ${COPY} + # XXX the 2nd "cat" seems to be needed because of buggy FD handling + # in conch + ${CONCH} --identity $OBJ/ssh-rsa --port $PORT --user $USER -e none \ + --known-hosts $OBJ/known_hosts --notty --noagent --nox11 -n \ + 127.0.0.1 "cat ${DATA}" 2>/dev/null | cat > ${COPY} + if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy" +done +rm -f ${COPY} + diff --git a/aosp/external/openssh/regress/connect-privsep.sh b/aosp/external/openssh/regress/connect-privsep.sh new file mode 100644 index 0000000000000000000000000000000000000000..8970340a29c477f261d67f88da01c918de92e4f4 --- /dev/null +++ b/aosp/external/openssh/regress/connect-privsep.sh @@ -0,0 +1,34 @@ +# $OpenBSD: connect-privsep.sh,v 1.9 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="proxy connect with privsep" + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy.orig +echo 'UsePrivilegeSeparation yes' >> $OBJ/sshd_proxy + +${SSH} -F $OBJ/ssh_proxy 999.999.999.999 true +if [ $? -ne 0 ]; then + fail "ssh privsep+proxyconnect failed" +fi + +cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy +echo 'UsePrivilegeSeparation sandbox' >> $OBJ/sshd_proxy + +${SSH} -F $OBJ/ssh_proxy 999.999.999.999 true +if [ $? -ne 0 ]; then + fail "ssh privsep/sandbox+proxyconnect failed" +fi + +# Because sandbox is sensitive to changes in libc, especially malloc, retest +# with every malloc.conf option (and none). +if [ -z "$TEST_MALLOC_OPTIONS" ]; then + mopts="C F G J R S U X < >" +else + mopts=`echo $TEST_MALLOC_OPTIONS | sed 's/./& /g'` +fi +for m in '' $mopts ; do + env MALLOC_OPTIONS="$m" ${SSH} -F $OBJ/ssh_proxy 999.999.999.999 true + if [ $? -ne 0 ]; then + fail "ssh privsep/sandbox+proxyconnect mopt '$m' failed" + fi +done diff --git a/aosp/external/openssh/regress/connect-uri.sh b/aosp/external/openssh/regress/connect-uri.sh new file mode 100644 index 0000000000000000000000000000000000000000..f13f15e660df1f2a10cfbe3eafcb27494f48906a --- /dev/null +++ b/aosp/external/openssh/regress/connect-uri.sh @@ -0,0 +1,29 @@ +# $OpenBSD: connect-uri.sh,v 1.1 2017/10/24 19:33:32 millert Exp $ +# Placed in the Public Domain. + +tid="uri connect" + +# Remove Port and User from ssh_config, we want to rely on the URI +cp $OBJ/ssh_config $OBJ/ssh_config.orig +egrep -v '^ +(Port|User) +.*$' $OBJ/ssh_config.orig > $OBJ/ssh_config + +start_sshd + +verbose "$tid: no trailing slash" +${SSH} -F $OBJ/ssh_config "ssh://${USER}@somehost:${PORT}" true +if [ $? -ne 0 ]; then + fail "ssh connection failed" +fi + +verbose "$tid: trailing slash" +${SSH} -F $OBJ/ssh_config "ssh://${USER}@somehost:${PORT}/" true +if [ $? -ne 0 ]; then + fail "ssh connection failed" +fi + +verbose "$tid: with path name" +${SSH} -F $OBJ/ssh_config "ssh://${USER}@somehost:${PORT}/${DATA}" true \ + > /dev/null 2>&1 +if [ $? -eq 0 ]; then + fail "ssh connection succeeded, expected failure" +fi diff --git a/aosp/external/openssh/regress/connect.sh b/aosp/external/openssh/regress/connect.sh new file mode 100644 index 0000000000000000000000000000000000000000..46f12b7b3c9266d4bcdb4500a9343125fd3ebb01 --- /dev/null +++ b/aosp/external/openssh/regress/connect.sh @@ -0,0 +1,18 @@ +# $OpenBSD: connect.sh,v 1.8 2020/01/25 02:57:53 dtucker Exp $ +# Placed in the Public Domain. + +tid="simple connect" + +start_sshd + +trace "direct connect" +${SSH} -F $OBJ/ssh_config somehost true +if [ $? -ne 0 ]; then + fail "ssh direct connect failed" +fi + +trace "proxy connect" +${SSH} -F $OBJ/ssh_config -o "proxycommand $NC %h %p" somehost true +if [ $? -ne 0 ]; then + fail "ssh proxycommand connect failed" +fi diff --git a/aosp/external/openssh/regress/dhgex.sh b/aosp/external/openssh/regress/dhgex.sh new file mode 100644 index 0000000000000000000000000000000000000000..6dd4cfe3f94a696634de68f043f23edee6ed8d18 --- /dev/null +++ b/aosp/external/openssh/regress/dhgex.sh @@ -0,0 +1,61 @@ +# $OpenBSD: dhgex.sh,v 1.7 2020/12/21 22:48:41 dtucker Exp $ +# Placed in the Public Domain. + +tid="dhgex" + +LOG=${TEST_SSH_LOGFILE} +rm -f ${LOG} +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +kexs=`${SSH} -Q kex | grep diffie-hellman-group-exchange` + +ssh_test_dhgex() +{ + bits="$1"; shift + cipher="$1"; shift + kex="$1"; shift + + cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy + echo "KexAlgorithms=$kex" >> $OBJ/sshd_proxy + echo "Ciphers=$cipher" >> $OBJ/sshd_proxy + rm -f ${LOG} + opts="-oKexAlgorithms=$kex -oCiphers=$cipher" + min=2048 + max=8192 + groupsz="$min<$bits<$max" + verbose "$tid bits $bits $kex $cipher" + ${SSH} ${opts} $@ -vvv -F ${OBJ}/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "ssh failed ($@)" + fi + # check what we request + grep "SSH2_MSG_KEX_DH_GEX_REQUEST($groupsz) sent" ${LOG} >/dev/null + if [ $? != 0 ]; then + got=`egrep "SSH2_MSG_KEX_DH_GEX_REQUEST(.*) sent" ${LOG}` + fail "$tid unexpected GEX sizes, expected $groupsz, got $got" + fi + # check what we got. + gotbits="`awk 'BEGIN{FS="/"}/bits set:/{print $2}' ${LOG} | + head -1 | tr -d '\r\n'`" + trace "expected '$bits' got '$gotbits'" + if [ -z "$gotbits" ] || [ "$gotbits" -lt "$bits" ]; then + fatal "$tid expected $bits bit group, got $gotbits" + fi +} + +check() +{ + bits="$1"; shift + + for c in $@; do + for k in $kexs; do + ssh_test_dhgex $bits $c $k + done + done +} + +check 3072 3des-cbc # 112 bits. +check 3072 `${SSH} -Q cipher | grep 128` +check 7680 `${SSH} -Q cipher | grep 192` +check 8192 `${SSH} -Q cipher | grep 256` +check 8192 chacha20-poly1305@openssh.com diff --git a/aosp/external/openssh/regress/dsa_ssh2.prv b/aosp/external/openssh/regress/dsa_ssh2.prv new file mode 100644 index 0000000000000000000000000000000000000000..c93b4037194ca40477dd33126e71b27958786076 --- /dev/null +++ b/aosp/external/openssh/regress/dsa_ssh2.prv @@ -0,0 +1,14 @@ +---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- +Subject: ssh-keygen test +Comment: "1024-bit dsa, Tue Jan 08 2002 22:00:23 +0100" +P2/56wAAAgIAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3BsYWlufX0AAA +AEbm9uZQAAAcQAAAHAAAAAAAAABACwUfm3AxZTut3icBmwCcD48nY64HzuELlQ+vEqjIcR +Lo49es/DQTeLNQ+kdKRCfouosGNv0WqxRtF0tUsWdXxS37oHGa4QPugBdHRd7YlZGZv8kg +x7FsoepY7v7E683/97dv2zxL3AGagTEzWr7fl0yPexAaZoDvtQrrjX44BLmwAABACWQkvv +MxnD8eFkS1konFfMJ1CkuRfTN34CBZ6dY7VTSGemy4QwtFdMKmoufD0eKgy3p5WOeWCYKt +F4FhjHKZk/aaxFjjIbtkrnlvXg64QI11dSZyBN6/ViQkHPSkUDF+A6AAEhrNbQbAFSvao1 +kTvNtPCtL0AkUIduEMzGQfLCTAAAAKDeC043YVo9Zo0zAEeIA4uZh4LBCQAAA/9aj7Y5ik +ehygJ4qTDSlVypsPuV+n59tMS0e2pfrSG87yf5r94AKBmJeho5OO6wYaXCxsVB7AFbSUD6 +75AK8mHF4v1/+7SWKk5f8xlMCMSPZ9K0+j/W1d/q2qkhnnDZolOHDomLA+U00i5ya/jnTV +zyDPWLFpWK8u3xGBPAYX324gAAAKDHFvooRnaXdZbeWGTTqmgHB1GU9A== +---- END SSH2 ENCRYPTED PRIVATE KEY ---- diff --git a/aosp/external/openssh/regress/dsa_ssh2.pub b/aosp/external/openssh/regress/dsa_ssh2.pub new file mode 100644 index 0000000000000000000000000000000000000000..215d73baef31fa0a364660b92fda5d6e8eda305c --- /dev/null +++ b/aosp/external/openssh/regress/dsa_ssh2.pub @@ -0,0 +1,13 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Subject: ssh-keygen test +Comment: "1024-bit dsa, Tue Jan 08 2002 22:00:23 +0100" +AAAAB3NzaC1kc3MAAACBALBR+bcDFlO63eJwGbAJwPjydjrgfO4QuVD68SqMhxEujj16z8 +NBN4s1D6R0pEJ+i6iwY2/RarFG0XS1SxZ1fFLfugcZrhA+6AF0dF3tiVkZm/ySDHsWyh6l +ju/sTrzf/3t2/bPEvcAZqBMTNavt+XTI97EBpmgO+1CuuNfjgEubAAAAFQDeC043YVo9Zo +0zAEeIA4uZh4LBCQAAAIEAlkJL7zMZw/HhZEtZKJxXzCdQpLkX0zd+AgWenWO1U0hnpsuE +MLRXTCpqLnw9HioMt6eVjnlgmCrReBYYxymZP2msRY4yG7ZK55b14OuECNdXUmcgTev1Yk +JBz0pFAxfgOgABIazW0GwBUr2qNZE7zbTwrS9AJFCHbhDMxkHywkwAAACAWo+2OYpHocoC +eKkw0pVcqbD7lfp+fbTEtHtqX60hvO8n+a/eACgZiXoaOTjusGGlwsbFQewBW0lA+u+QCv +JhxeL9f/u0lipOX/MZTAjEj2fStPo/1tXf6tqpIZ5w2aJThw6JiwPlNNIucmv4501c8gz1 +ixaVivLt8RgTwGF99uI= +---- END SSH2 PUBLIC KEY ---- diff --git a/aosp/external/openssh/regress/dynamic-forward.sh b/aosp/external/openssh/regress/dynamic-forward.sh new file mode 100644 index 0000000000000000000000000000000000000000..84f8ee19280ad2ea641b4d8ff9566c5ec651259c --- /dev/null +++ b/aosp/external/openssh/regress/dynamic-forward.sh @@ -0,0 +1,61 @@ +# $OpenBSD: dynamic-forward.sh,v 1.13 2017/09/21 19:18:12 markus Exp $ +# Placed in the Public Domain. + +tid="dynamic forwarding" + +FWDPORT=`expr $PORT + 1` + +if have_prog nc && nc -h 2>&1 | grep "proxy address" >/dev/null; then + proxycmd="nc -x 127.0.0.1:$FWDPORT -X" +elif have_prog connect; then + proxycmd="connect -S 127.0.0.1:$FWDPORT -" +else + echo "skipped (no suitable ProxyCommand found)" + exit 0 +fi +trace "will use ProxyCommand $proxycmd" + +start_sshd + +for d in D R; do + n=0 + error="1" + trace "start dynamic forwarding, fork to background" + + while [ "$error" -ne 0 -a "$n" -lt 3 ]; do + n=`expr $n + 1` + ${SSH} -F $OBJ/ssh_config -f -$d $FWDPORT -q \ + -oExitOnForwardFailure=yes somehost exec sh -c \ + \'"echo \$\$ > $OBJ/remote_pid; exec sleep 444"\' + error=$? + if [ "$error" -ne 0 ]; then + trace "forward failed attempt $n err $error" + sleep $n + fi + done + if [ "$error" -ne 0 ]; then + fatal "failed to start dynamic forwarding" + fi + + for s in 4 5; do + for h in 127.0.0.1 localhost; do + trace "testing ssh socks version $s host $h (-$d)" + ${SSH} -F $OBJ/ssh_config \ + -o "ProxyCommand ${proxycmd}${s} $h $PORT" \ + somehost cat ${DATA} > ${COPY} + test -f ${COPY} || fail "failed copy ${DATA}" + cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" + done + done + + if [ -f $OBJ/remote_pid ]; then + remote=`cat $OBJ/remote_pid` + trace "terminate remote shell, pid $remote" + if [ $remote -gt 1 ]; then + kill -HUP $remote + fi + else + fail "no pid file: $OBJ/remote_pid" + fi + +done diff --git a/aosp/external/openssh/regress/ed25519_openssh.prv b/aosp/external/openssh/regress/ed25519_openssh.prv new file mode 100644 index 0000000000000000000000000000000000000000..9f191b778962ffe7a08499c2f5435944a66e305b --- /dev/null +++ b/aosp/external/openssh/regress/ed25519_openssh.prv @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACDE8/0FM7Yw6xc53QpiZUQAh/LK2mEAwNDNYdSR6GIGIwAAAKC+Cfdzvgn3 +cwAAAAtzc2gtZWQyNTUxOQAAACDE8/0FM7Yw6xc53QpiZUQAh/LK2mEAwNDNYdSR6GIGIw +AAAEBm+60DgH0WMW7Z5oyvu1dxo7MaXe5RRMWTMJCfLkHexMTz/QUztjDrFzndCmJlRACH +8sraYQDA0M1h1JHoYgYjAAAAGWR0dWNrZXJAcXVvbGwuZHR1Y2tlci5uZXQBAgME +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/ed25519_openssh.pub b/aosp/external/openssh/regress/ed25519_openssh.pub new file mode 100644 index 0000000000000000000000000000000000000000..910363138658908e4e9b0bcf324b14fddcfff8a8 --- /dev/null +++ b/aosp/external/openssh/regress/ed25519_openssh.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMTz/QUztjDrFzndCmJlRACH8sraYQDA0M1h1JHoYgYj diff --git a/aosp/external/openssh/regress/envpass.sh b/aosp/external/openssh/regress/envpass.sh new file mode 100644 index 0000000000000000000000000000000000000000..af7eafe3d16ee4e52d7c2afb36cc1079a6d856ce --- /dev/null +++ b/aosp/external/openssh/regress/envpass.sh @@ -0,0 +1,60 @@ +# $OpenBSD: envpass.sh,v 1.4 2005/03/04 08:48:46 djm Exp $ +# Placed in the Public Domain. + +tid="environment passing" + +# NB accepted env vars are in test-exec.sh (_XXX_TEST_* and _XXX_TEST) + +# Prepare a custom config to test for a configuration parsing bug fixed in 4.0 +cat << EOF > $OBJ/ssh_proxy_envpass +Host test-sendenv-confparse-bug + SendEnv * +EOF +cat $OBJ/ssh_proxy >> $OBJ/ssh_proxy_envpass + +trace "pass env, don't accept" +verbose "test $tid: pass env, don't accept" +_TEST_ENV=blah ${SSH} -oSendEnv="*" -F $OBJ/ssh_proxy_envpass otherhost \ + sh << 'EOF' + test -z "$_TEST_ENV" +EOF +r=$? +if [ $r -ne 0 ]; then + fail "environment found" +fi + +trace "don't pass env, accept" +verbose "test $tid: don't pass env, accept" +_XXX_TEST_A=1 _XXX_TEST_B=2 ${SSH} -F $OBJ/ssh_proxy_envpass otherhost \ + sh << 'EOF' + test -z "$_XXX_TEST_A" && test -z "$_XXX_TEST_B" +EOF +r=$? +if [ $r -ne 0 ]; then + fail "environment found" +fi + +trace "pass single env, accept single env" +verbose "test $tid: pass single env, accept single env" +_XXX_TEST=blah ${SSH} -oSendEnv="_XXX_TEST" -F $OBJ/ssh_proxy_envpass \ + otherhost sh << 'EOF' + test X"$_XXX_TEST" = X"blah" +EOF +r=$? +if [ $r -ne 0 ]; then + fail "environment not found" +fi + +trace "pass multiple env, accept multiple env" +verbose "test $tid: pass multiple env, accept multiple env" +_XXX_TEST_A=1 _XXX_TEST_B=2 ${SSH} -oSendEnv="_XXX_TEST_*" \ + -F $OBJ/ssh_proxy_envpass otherhost \ + sh << 'EOF' + test X"$_XXX_TEST_A" = X"1" -a X"$_XXX_TEST_B" = X"2" +EOF +r=$? +if [ $r -ne 0 ]; then + fail "environment not found" +fi + +rm -f $OBJ/ssh_proxy_envpass diff --git a/aosp/external/openssh/regress/exit-status-signal.sh b/aosp/external/openssh/regress/exit-status-signal.sh new file mode 100644 index 0000000000000000000000000000000000000000..1b3af0d84ddde3e3f6d557cf12149aaf7234ca91 --- /dev/null +++ b/aosp/external/openssh/regress/exit-status-signal.sh @@ -0,0 +1,24 @@ +# This test performs validation that ssh client is not successive on being terminated + +tid="exit status on signal" + +# spawn client in background +rm -f $OBJ/remote_pid +${SSH} -F $OBJ/ssh_proxy somehost 'echo $$ >'$OBJ'/remote_pid; sleep 444' & +ssh_pid=$! + +# wait for it to start +n=20 +while [ ! -f $OBJ/remote_pid ] && [ $n -gt 0 ]; do + n=$(($n - 1)) + sleep 1 +done + +kill $ssh_pid +wait $ssh_pid +exit_code=$? + +if [ $exit_code -eq 0 ]; then + fail "ssh client should fail on signal" +fi + diff --git a/aosp/external/openssh/regress/exit-status.sh b/aosp/external/openssh/regress/exit-status.sh new file mode 100644 index 0000000000000000000000000000000000000000..aadf99fb3c8b2ea689798f0d09ff670363802cf1 --- /dev/null +++ b/aosp/external/openssh/regress/exit-status.sh @@ -0,0 +1,22 @@ +# $OpenBSD: exit-status.sh,v 1.8 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="remote exit status" + +for s in 0 1 4 5 44; do + trace "status $s" + verbose "test $tid: status $s" + ${SSH} -F $OBJ/ssh_proxy otherhost exit $s + r=$? + if [ $r -ne $s ]; then + fail "exit code mismatch for: $r != $s" + fi + + # same with early close of stdout/err + ${SSH} -F $OBJ/ssh_proxy -n otherhost exec \ + sh -c \'"sleep 2; exec > /dev/null 2>&1; sleep 3; exit $s"\' + r=$? + if [ $r -ne $s ]; then + fail "exit code (with sleep) mismatch for: $r != $s" + fi +done diff --git a/aosp/external/openssh/regress/forcecommand.sh b/aosp/external/openssh/regress/forcecommand.sh new file mode 100644 index 0000000000000000000000000000000000000000..e059f1fdbc615b8a6757474585c41586204e719a --- /dev/null +++ b/aosp/external/openssh/regress/forcecommand.sh @@ -0,0 +1,35 @@ +# $OpenBSD: forcecommand.sh,v 1.4 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="forced command" + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'command="true" ' >>$OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done + +trace "forced command in key option" +${SSH} -F $OBJ/ssh_proxy somehost false || fail "forced command in key" + +cp /dev/null $OBJ/authorized_keys_$USER +for t in ${SSH_KEYTYPES}; do + printf 'command="false" ' >> $OBJ/authorized_keys_$USER + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER +done + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "ForceCommand true" >> $OBJ/sshd_proxy + +trace "forced command in sshd_config overrides key option" +${SSH} -F $OBJ/ssh_proxy somehost false || fail "forced command in key" + +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +echo "ForceCommand false" >> $OBJ/sshd_proxy +echo "Match User $USER" >> $OBJ/sshd_proxy +echo " ForceCommand true" >> $OBJ/sshd_proxy + +trace "forced command with match" +${SSH} -F $OBJ/ssh_proxy somehost false || fail "forced command in key" diff --git a/aosp/external/openssh/regress/forward-control.sh b/aosp/external/openssh/regress/forward-control.sh new file mode 100644 index 0000000000000000000000000000000000000000..02f7667a665b527adb4bf1805cdf42511826e5ff --- /dev/null +++ b/aosp/external/openssh/regress/forward-control.sh @@ -0,0 +1,235 @@ +# $OpenBSD: forward-control.sh,v 1.8 2021/05/07 09:23:40 dtucker Exp $ +# Placed in the Public Domain. + +tid="sshd control of local and remote forwarding" + +LFWD_PORT=3320 +RFWD_PORT=3321 +CTL=$OBJ/ctl-sock +READY=$OBJ/ready + +wait_for_file_to_appear() { + _path=$1 + _n=0 + while test ! -f $_path ; do + test $_n -eq 1 && trace "waiting for $_path to appear" + _n=`expr $_n + 1` + test $_n -ge 20 && return 1 + sleep 1 + done + return 0 +} + +wait_for_process_to_exit() { + _pid=$1 + _n=0 + while kill -0 $_pid 2>/dev/null ; do + test $_n -eq 1 && trace "waiting for $_pid to exit" + _n=`expr $_n + 1` + test $_n -ge 20 && return 1 + sleep 1 + done + return 0 +} + +# usage: check_lfwd Y|N message +check_lfwd() { + _expected=$1 + _message=$2 + rm -f $READY + ${SSH} -F $OBJ/ssh_proxy \ + -L$LFWD_PORT:127.0.0.1:$PORT \ + -o ExitOnForwardFailure=yes \ + -n host exec sh -c \'"sleep 60 & echo \$! > $READY ; wait "\' \ + >/dev/null 2>&1 & + _sshpid=$! + wait_for_file_to_appear $READY || \ + fatal "check_lfwd ssh fail: $_message" + ${SSH} -F $OBJ/ssh_config -p $LFWD_PORT \ + -oConnectionAttempts=10 host true >/dev/null 2>&1 + _result=$? + kill $_sshpid `cat $READY` 2>/dev/null + wait_for_process_to_exit $_sshpid + if test "x$_expected" = "xY" -a $_result -ne 0 ; then + fail "check_lfwd failed (expecting success): $_message" + elif test "x$_expected" = "xN" -a $_result -eq 0 ; then + fail "check_lfwd succeeded (expecting failure): $_message" + elif test "x$_expected" != "xY" -a "x$_expected" != "xN" ; then + fatal "check_lfwd invalid argument \"$_expected\"" + else + verbose "check_lfwd done (expecting $_expected): $_message" + fi +} + +# usage: check_rfwd Y|N message +check_rfwd() { + _expected=$1 + _message=$2 + rm -f $READY + ${SSH} -F $OBJ/ssh_proxy \ + -R127.0.0.1:$RFWD_PORT:127.0.0.1:$PORT \ + -o ExitOnForwardFailure=yes \ + -n host exec sh -c \'"sleep 60 & echo \$! > $READY ; wait "\' \ + >/dev/null 2>&1 & + _sshpid=$! + wait_for_file_to_appear $READY + _result=$? + if test $_result -eq 0 ; then + ${SSH} -F $OBJ/ssh_config -p $RFWD_PORT \ + -oConnectionAttempts=10 host true >/dev/null 2>&1 + _result=$? + kill $_sshpid `cat $READY` 2>/dev/null + wait_for_process_to_exit $_sshpid + fi + if test "x$_expected" = "xY" -a $_result -ne 0 ; then + fail "check_rfwd failed (expecting success): $_message" + elif test "x$_expected" = "xN" -a $_result -eq 0 ; then + fail "check_rfwd succeeded (expecting failure): $_message" + elif test "x$_expected" != "xY" -a "x$_expected" != "xN" ; then + fatal "check_rfwd invalid argument \"$_expected\"" + else + verbose "check_rfwd done (expecting $_expected): $_message" + fi +} + +start_sshd +cp ${OBJ}/sshd_proxy ${OBJ}/sshd_proxy.bak +cp ${OBJ}/authorized_keys_${USER} ${OBJ}/authorized_keys_${USER}.bak + +# Sanity check: ensure the default config allows forwarding +check_lfwd Y "default configuration" +check_rfwd Y "default configuration" + +# Usage: lperm_tests yes|local|remote|no Y|N Y|N Y|N Y|N Y|N Y|N +lperm_tests() { + _tcpfwd=$1 + _plain_lfwd=$2 + _plain_rfwd=$3 + _nopermit_lfwd=$4 + _nopermit_rfwd=$5 + _permit_lfwd=$6 + _permit_rfwd=$7 + _badfwd1=127.0.0.1:22 + _badfwd2=127.0.0.2:22 + _goodfwd=127.0.0.1:${PORT} + cp ${OBJ}/authorized_keys_${USER}.bak ${OBJ}/authorized_keys_${USER} + _prefix="AllowTcpForwarding=$_tcpfwd" + + # No PermitOpen + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_plain_lfwd "$_prefix" + check_rfwd $_plain_rfwd "$_prefix" + + # PermitOpen via sshd_config that doesn't match + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ; + echo "PermitOpen $_badfwd1 $_badfwd2" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_nopermit_lfwd "$_prefix, !PermitOpen" + check_rfwd $_nopermit_rfwd "$_prefix, !PermitOpen" + # PermitOpen via sshd_config that does match + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ; + echo "PermitOpen $_badfwd1 $_goodfwd $_badfwd2" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_plain_lfwd "$_prefix, PermitOpen" + check_rfwd $_plain_rfwd "$_prefix, PermitOpen" + + # permitopen keys option. + # NB. permitopen via authorized_keys should have same + # success/fail as via sshd_config + # permitopen via authorized_keys that doesn't match + sed "s/^/permitopen=\"$_badfwd1\",permitopen=\"$_badfwd2\" /" \ + < ${OBJ}/authorized_keys_${USER}.bak \ + > ${OBJ}/authorized_keys_${USER} || fatal "sed 1 fail" + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_nopermit_lfwd "$_prefix, !permitopen" + check_rfwd $_nopermit_rfwd "$_prefix, !permitopen" + # permitopen via authorized_keys that does match + sed "s/^/permitopen=\"$_badfwd1\",permitopen=\"$_goodfwd\" /" \ + < ${OBJ}/authorized_keys_${USER}.bak \ + > ${OBJ}/authorized_keys_${USER} || fatal "sed 2 fail" + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_permit_lfwd "$_prefix, permitopen" + check_rfwd $_permit_rfwd "$_prefix, permitopen" + + # Check port-forwarding flags in authorized_keys. + # These two should refuse all. + sed "s/^/no-port-forwarding /" \ + < ${OBJ}/authorized_keys_${USER}.bak \ + > ${OBJ}/authorized_keys_${USER} || fatal "sed 3 fail" + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ) \ + > ${OBJ}/sshd_proxy + check_lfwd N "$_prefix, no-port-forwarding" + check_rfwd N "$_prefix, no-port-forwarding" + sed "s/^/restrict /" \ + < ${OBJ}/authorized_keys_${USER}.bak \ + > ${OBJ}/authorized_keys_${USER} || fatal "sed 4 fail" + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ) \ + > ${OBJ}/sshd_proxy + check_lfwd N "$_prefix, restrict" + check_rfwd N "$_prefix, restrict" + # This should pass the same cases as _nopermit* + sed "s/^/restrict,port-forwarding /" \ + < ${OBJ}/authorized_keys_${USER}.bak \ + > ${OBJ}/authorized_keys_${USER} || fatal "sed 5 fail" + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_plain_lfwd "$_prefix, restrict,port-forwarding" + check_rfwd $_plain_rfwd "$_prefix, restrict,port-forwarding" +} + +# permit-open none mismatch match +# AllowTcpForwarding local remote local remote local remote +lperm_tests yes Y Y N Y Y Y +lperm_tests local Y N N N Y N +lperm_tests remote N Y N Y N Y +lperm_tests no N N N N N N + +# Usage: rperm_tests yes|local|remote|no Y|N Y|N Y|N Y|N Y|N Y|N +rperm_tests() { + _tcpfwd=$1 + _plain_lfwd=$2 + _plain_rfwd=$3 + _nopermit_lfwd=$4 + _nopermit_rfwd=$5 + _permit_lfwd=$6 + _permit_rfwd=$7 + _badfwd1=127.0.0.1:22 + _badfwd2=127.0.0.2:${RFWD_PORT} + _goodfwd=127.0.0.1:${RFWD_PORT} + cp ${OBJ}/authorized_keys_${USER}.bak ${OBJ}/authorized_keys_${USER} + _prefix="AllowTcpForwarding=$_tcpfwd" + + # PermitListen via sshd_config that doesn't match + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ; + echo "PermitListen $_badfwd1 $_badfwd2" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_nopermit_lfwd "$_prefix, !PermitListen" + check_rfwd $_nopermit_rfwd "$_prefix, !PermitListen" + # PermitListen via sshd_config that does match + ( cat ${OBJ}/sshd_proxy.bak ; + echo "AllowTcpForwarding $_tcpfwd" ; + echo "PermitListen $_badfwd1 $_goodfwd $_badfwd2" ) \ + > ${OBJ}/sshd_proxy + check_lfwd $_plain_lfwd "$_prefix, PermitListen" + check_rfwd $_plain_rfwd "$_prefix, PermitListen" +} + +# permit-remote-open none mismatch match +# AllowTcpForwarding local remote local remote local remote +rperm_tests yes Y Y Y N Y Y +rperm_tests local Y N Y N Y N +rperm_tests remote N Y N N N Y +rperm_tests no N N N N N N + diff --git a/aosp/external/openssh/regress/forwarding.sh b/aosp/external/openssh/regress/forwarding.sh new file mode 100644 index 0000000000000000000000000000000000000000..a72bd3a05cfa63617783dfc5d18ccd98d5ad8674 --- /dev/null +++ b/aosp/external/openssh/regress/forwarding.sh @@ -0,0 +1,136 @@ +# $OpenBSD: forwarding.sh,v 1.24 2021/05/07 09:23:40 dtucker Exp $ +# Placed in the Public Domain. + +tid="local and remote forwarding" + +DATA=/bin/ls${EXEEXT} + +start_sshd + +base=33 +last=$PORT +fwd="" +make_tmpdir +CTL=${SSH_REGRESS_TMP}/ctl-sock + +for j in 0 1 2; do + for i in 0 1 2; do + a=$base$j$i + b=`expr $a + 50` + c=$last + # fwd chain: $a -> $b -> $c + fwd="$fwd -L$a:127.0.0.1:$b -R$b:127.0.0.1:$c" + last=$a + done +done + +trace "start forwarding, fork to background" +rm -f $CTL +${SSH} -S $CTL -N -M -F $OBJ/ssh_config -f $fwd somehost + +trace "transfer over forwarded channels and check result" +${SSH} -F $OBJ/ssh_config -p$last -o 'ConnectionAttempts=10' \ + somehost cat ${DATA} > ${COPY} +test -s ${COPY} || fail "failed copy of ${DATA}" +cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" + +${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost 2>/dev/null + +for d in L R; do + trace "exit on -$d forward failure" + + # this one should succeed + ${SSH} -F $OBJ/ssh_config \ + -$d ${base}01:127.0.0.1:$PORT \ + -$d ${base}02:127.0.0.1:$PORT \ + -$d ${base}03:127.0.0.1:$PORT \ + -$d ${base}04:127.0.0.1:$PORT \ + -oExitOnForwardFailure=yes somehost true + if [ $? != 0 ]; then + fatal "connection failed, should not" + else + # this one should fail + ${SSH} -q -F $OBJ/ssh_config \ + -$d ${base}01:127.0.0.1:$PORT \ + -$d ${base}02:127.0.0.1:$PORT \ + -$d ${base}03:127.0.0.1:$PORT \ + -$d ${base}01:localhost:$PORT \ + -$d ${base}04:127.0.0.1:$PORT \ + -oExitOnForwardFailure=yes somehost true + r=$? + if [ $r != 255 ]; then + fail "connection not termintated, but should ($r)" + fi + fi +done + +trace "simple clear forwarding" +${SSH} -F $OBJ/ssh_config -oClearAllForwardings=yes somehost true + +trace "clear local forward" +rm -f $CTL +${SSH} -S $CTL -N -M -f -F $OBJ/ssh_config -L ${base}01:127.0.0.1:$PORT \ + -oClearAllForwardings=yes somehost +if [ $? != 0 ]; then + fail "connection failed with cleared local forwarding" +else + # this one should fail + ${SSH} -F $OBJ/ssh_config -p ${base}01 somehost true \ + >>$TEST_REGRESS_LOGFILE 2>&1 && \ + fail "local forwarding not cleared" +fi +${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost 2>/dev/null + +trace "clear remote forward" +rm -f $CTL +${SSH} -S $CTL -N -M -f -F $OBJ/ssh_config -R ${base}01:127.0.0.1:$PORT \ + -oClearAllForwardings=yes somehost +if [ $? != 0 ]; then + fail "connection failed with cleared remote forwarding" +else + # this one should fail + ${SSH} -F $OBJ/ssh_config -p ${base}01 somehost true \ + >>$TEST_REGRESS_LOGFILE 2>&1 && \ + fail "remote forwarding not cleared" +fi +${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost 2>/dev/null + +trace "stdio forwarding" +cmd="${SSH} -F $OBJ/ssh_config" +$cmd -o "ProxyCommand $cmd -q -W localhost:$PORT somehost" somehost true +if [ $? != 0 ]; then + fail "stdio forwarding" +fi + +echo "LocalForward ${base}01 127.0.0.1:$PORT" >> $OBJ/ssh_config +echo "RemoteForward ${base}02 127.0.0.1:${base}01" >> $OBJ/ssh_config + +trace "config file: start forwarding, fork to background" +rm -f $CTL +${SSH} -S $CTL -N -M -F $OBJ/ssh_config -f somehost + +trace "config file: transfer over forwarded channels and check result" +${SSH} -F $OBJ/ssh_config -p${base}02 -o 'ConnectionAttempts=10' \ + somehost cat ${DATA} > ${COPY} +test -s ${COPY} || fail "failed copy of ${DATA}" +cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" + +${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost 2>/dev/null + +trace "transfer over chained unix domain socket forwards and check result" +rm -f $OBJ/unix-[123].fwd +rm -f $CTL $CTL.[123] +${SSH} -S $CTL -N -M -f -F $OBJ/ssh_config -R${base}01:[$OBJ/unix-1.fwd] somehost +${SSH} -S $CTL.1 -N -M -f -F $OBJ/ssh_config -L[$OBJ/unix-1.fwd]:[$OBJ/unix-2.fwd] somehost +${SSH} -S $CTL.2 -N -M -f -F $OBJ/ssh_config -R[$OBJ/unix-2.fwd]:[$OBJ/unix-3.fwd] somehost +${SSH} -S $CTL.3 -N -M -f -F $OBJ/ssh_config -L[$OBJ/unix-3.fwd]:127.0.0.1:$PORT somehost +${SSH} -F $OBJ/ssh_config -p${base}01 -o 'ConnectionAttempts=10' \ + somehost cat ${DATA} > ${COPY} +test -s ${COPY} || fail "failed copy ${DATA}" +cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" + +${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost 2>/dev/null +${SSH} -F $OBJ/ssh_config -S $CTL.1 -O exit somehost 2>/dev/null +${SSH} -F $OBJ/ssh_config -S $CTL.2 -O exit somehost 2>/dev/null +${SSH} -F $OBJ/ssh_config -S $CTL.3 -O exit somehost 2>/dev/null + diff --git a/aosp/external/openssh/regress/host-expand.sh b/aosp/external/openssh/regress/host-expand.sh new file mode 100644 index 0000000000000000000000000000000000000000..9444f7fb619ebc4141a798c7d69cbc88fe02f8ab --- /dev/null +++ b/aosp/external/openssh/regress/host-expand.sh @@ -0,0 +1,16 @@ +# $OpenBSD: host-expand.sh,v 1.5 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="expand %h and %n" + +echo 'PermitLocalCommand yes' >> $OBJ/ssh_proxy +printf 'LocalCommand printf "%%%%s\\n" "%%n" "%%h"\n' >> $OBJ/ssh_proxy + +cat >$OBJ/expect <$OBJ/actual +diff $OBJ/expect $OBJ/actual || fail "$tid" + diff --git a/aosp/external/openssh/regress/hostbased.sh b/aosp/external/openssh/regress/hostbased.sh new file mode 100644 index 0000000000000000000000000000000000000000..04a1c1a2da458aaae86fbd75312db68096435082 --- /dev/null +++ b/aosp/external/openssh/regress/hostbased.sh @@ -0,0 +1,66 @@ +# $OpenBSD: hostbased.sh,v 1.3 2022/01/08 07:55:26 dtucker Exp $ +# Placed in the Public Domain. + +# This test requires external setup and thus is skipped unless +# TEST_SSH_HOSTBASED_AUTH and SUDO are set to "yes". +# Since ssh-keysign has key paths hard coded, unlike the other tests it +# needs to use the real host keys. It requires: +# - ssh-keysign must be installed and setuid. +# - "EnableSSHKeysign yes" must be in the system ssh_config. +# - the system's own real FQDN the system-wide shosts.equiv. +# - the system's real public key fingerprints must me in global ssh_known_hosts. +# +tid="hostbased" + +if [ -z "${TEST_SSH_HOSTBASED_AUTH}" ]; then + skip "TEST_SSH_HOSTBASED_AUTH not set." +elif [ -z "${SUDO}" ]; then + skip "SUDO not set" +fi + +# Enable all supported hostkey algos (but no others) +hostkeyalgos=`${SSH} -Q HostKeyAlgorithms | tr '\n' , | sed 's/,$//'` + +cat >>$OBJ/sshd_proxy <>$OBJ/ssh_proxy < /dev/null +r=$? +[ $r -ne 0 ] && fatal "could not start ssh-agent: exit code $r" + +grep -vi 'hostkey' $OBJ/sshd_proxy > $OBJ/sshd_proxy.orig +echo "HostKeyAgent $SSH_AUTH_SOCK" >> $OBJ/sshd_proxy.orig + +trace "make CA key" + +${SSHKEYGEN} -qt ed25519 -f $OBJ/agent-ca -N '' || fatal "ssh-keygen CA" + +trace "load hostkeys" +for k in $SSH_KEYTYPES ; do + ${SSHKEYGEN} -qt $k -f $OBJ/agent-key.$k -N '' || fatal "ssh-keygen $k" + ${SSHKEYGEN} -s $OBJ/agent-ca -qh -n localhost-with-alias \ + -I localhost-with-alias $OBJ/agent-key.$k.pub || \ + fatal "sign $k" + ${SSHADD} -k $OBJ/agent-key.$k >/dev/null 2>&1 || \ + fatal "couldn't load key $OBJ/agent-key.$k" + # Remove private key so the server can't use it. + rm $OBJ/agent-key.$k || fatal "couldn't rm $OBJ/agent-key.$k" +done +rm $OBJ/agent-ca # Don't need CA private any more either + +unset SSH_AUTH_SOCK + +for k in $SSH_KEYTYPES ; do + verbose "key type $k" + cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy + echo "HostKeyAlgorithms $k" >> $OBJ/sshd_proxy + echo "Hostkey $OBJ/agent-key.${k}" >> $OBJ/sshd_proxy + opts="-oHostKeyAlgorithms=$k -F $OBJ/ssh_proxy" + ( printf 'localhost-with-alias,127.0.0.1,::1 ' ; + cat $OBJ/agent-key.$k.pub) > $OBJ/known_hosts + SSH_CONNECTION=`${SSH} $opts host 'echo $SSH_CONNECTION'` + if [ $? -ne 0 ]; then + fail "keytype $k failed" + fi + if [ "$SSH_CONNECTION" != "UNKNOWN 65535 UNKNOWN 65535" ]; then + fail "bad SSH_CONNECTION key type $k" + fi +done + +SSH_CERTTYPES=`ssh -Q key-sig | grep 'cert-v01@openssh.com'` + +# Prepare sshd_proxy for certificates. +cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy +HOSTKEYALGS="" +for k in $SSH_CERTTYPES ; do + test -z "$HOSTKEYALGS" || HOSTKEYALGS="${HOSTKEYALGS}," + HOSTKEYALGS="${HOSTKEYALGS}${k}" +done +for k in $SSH_KEYTYPES ; do + echo "Hostkey $OBJ/agent-key.${k}.pub" >> $OBJ/sshd_proxy + echo "HostCertificate $OBJ/agent-key.${k}-cert.pub" >> $OBJ/sshd_proxy + test -f $OBJ/agent-key.${k}.pub || fatal "no $k key" + test -f $OBJ/agent-key.${k}-cert.pub || fatal "no $k cert" +done +echo "HostKeyAlgorithms $HOSTKEYALGS" >> $OBJ/sshd_proxy + +# Add only CA trust anchor to known_hosts. +( printf '@cert-authority localhost-with-alias ' ; + cat $OBJ/agent-ca.pub) > $OBJ/known_hosts + +for k in $SSH_CERTTYPES ; do + verbose "cert type $k" + opts="-oHostKeyAlgorithms=$k -F $OBJ/ssh_proxy" + SSH_CONNECTION=`${SSH} $opts host 'echo $SSH_CONNECTION'` + if [ $? -ne 0 ]; then + fail "cert type $k failed" + fi + if [ "$SSH_CONNECTION" != "UNKNOWN 65535 UNKNOWN 65535" ]; then + fail "bad SSH_CONNECTION key type $k" + fi +done + +trace "kill agent" +${SSHAGENT} -k > /dev/null + diff --git a/aosp/external/openssh/regress/hostkey-rotate.sh b/aosp/external/openssh/regress/hostkey-rotate.sh new file mode 100644 index 0000000000000000000000000000000000000000..5898cbd8ae4bcb6ac9ff6d623544bb631c845649 --- /dev/null +++ b/aosp/external/openssh/regress/hostkey-rotate.sh @@ -0,0 +1,152 @@ +# $OpenBSD: hostkey-rotate.sh,v 1.10 2022/01/05 08:25:05 djm Exp $ +# Placed in the Public Domain. + +tid="hostkey rotate" + +# +# GNU (f)grep <=2.18, as shipped by FreeBSD<=12 and NetBSD<=9 will occasionally +# fail to find ssh host keys in the hostkey-rotate test. If we have those +# versions, use awk instead. +# See # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=258616 +# +case `grep --version 2>&1 | awk '/GNU grep/{print $4}'` in +2.19) fgrep=good ;; +1.*|2.?|2.?.?|2.1?) fgrep=bad ;; # stock GNU grep +2.5.1*) fgrep=bad ;; # FreeBSD and NetBSD +*) fgrep=good ;; +esac +if test "x$fgrep" = "xbad"; then + fgrep() +{ + awk 'BEGIN{e=1} {if (index($0,"'$1'")>0){e=0;print}} END{exit e}' $2 +} +fi + +rm -f $OBJ/hkr.* $OBJ/ssh_proxy.orig $OBJ/ssh_proxy.orig + +grep -vi 'hostkey' $OBJ/sshd_proxy > $OBJ/sshd_proxy.orig +mv $OBJ/ssh_proxy $OBJ/ssh_proxy.orig +grep -vi 'globalknownhostsfile' $OBJ/ssh_proxy.orig > $OBJ/ssh_proxy +echo "UpdateHostkeys=yes" >> $OBJ/ssh_proxy +echo "GlobalKnownHostsFile=none" >> $OBJ/ssh_proxy +rm $OBJ/known_hosts + +# The "primary" key type is ed25519 since it's supported even when built +# without OpenSSL. The secondary is RSA if it's supported. +primary="ssh-ed25519" +secondary="$primary" + +trace "prepare hostkeys" +nkeys=0 +all_algs="" +for k in $SSH_HOSTKEY_TYPES; do + ${SSHKEYGEN} -qt $k -f $OBJ/hkr.$k -N '' || fatal "ssh-keygen $k" + echo "Hostkey $OBJ/hkr.${k}" >> $OBJ/sshd_proxy.orig + nkeys=`expr $nkeys + 1` + test "x$all_algs" = "x" || all_algs="${all_algs}," + case "$k" in + ssh-rsa) + secondary="ssh-rsa" + all_algs="${all_algs}rsa-sha2-256,rsa-sha2-512,$k" + ;; + *) + all_algs="${all_algs}$k" + ;; + esac +done + +dossh() { + # All ssh should succeed in this test + ${SSH} -F $OBJ/ssh_proxy "$@" x true || fail "ssh $@ failed" +} + +expect_nkeys() { + _expected=$1 + _message=$2 + _n=`wc -l $OBJ/known_hosts | awk '{ print $1 }'` || fatal "wc failed" + [ "x$_n" = "x$_expected" ] || fail "$_message (got $_n wanted $_expected)" +} + +check_key_present() { + _type=$1 + _kfile=$2 + test "x$_kfile" = "x" && _kfile="$OBJ/hkr.${_type}.pub" + _kpub=`awk "/$_type /"' { print $2 }' < $_kfile` || \ + fatal "awk failed" + fgrep "$_kpub" $OBJ/known_hosts > /dev/null +} + +cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy + +# Connect to sshd with StrictHostkeyChecking=no +verbose "learn hostkey with StrictHostKeyChecking=no" +>$OBJ/known_hosts +dossh -oHostKeyAlgorithms=$primary -oStrictHostKeyChecking=no +# Verify no additional keys learned +expect_nkeys 1 "unstrict connect keys" +check_key_present $primary || fail "unstrict didn't learn key" + +# Connect to sshd as usual +verbose "learn additional hostkeys" +dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=$all_algs +# Check that other keys learned +expect_nkeys $nkeys "learn hostkeys" +for k in $SSH_HOSTKEY_TYPES; do + check_key_present $k || fail "didn't learn keytype $k" +done + +# Check each key type +for k in $SSH_HOSTKEY_TYPES; do + case "$k" in + ssh-rsa) alg="rsa-sha2-256,rsa-sha2-512,ssh-rsa" ;; + *) alg="$k" ;; + esac + verbose "learn additional hostkeys, type=$k" + dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=$alg,$all_algs + expect_nkeys $nkeys "learn hostkeys $k" + check_key_present $k || fail "didn't learn $k correctly" +done + +# Change one hostkey (non primary) and relearn +if [ "$primary" != "$secondary" ]; then + verbose "learn changed non-primary hostkey type=${secondary}" + mv $OBJ/hkr.${secondary}.pub $OBJ/hkr.${secondary}.pub.old + rm -f $OBJ/hkr.${secondary} + ${SSHKEYGEN} -qt ${secondary} -f $OBJ/hkr.${secondary} -N '' || \ + fatal "ssh-keygen $secondary" + dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=$all_algs + # Check that the key was replaced + expect_nkeys $nkeys "learn hostkeys" + check_key_present ${secondary} $OBJ/hkr.${secondary}.pub.old && \ + fail "old key present" + check_key_present ${secondary} || fail "didn't learn changed key" +fi + +# Add new hostkey (primary type) to sshd and connect +verbose "learn new primary hostkey" +${SSHKEYGEN} -qt ${primary} -f $OBJ/hkr.${primary}-new -N '' || fatal "ssh-keygen ed25519" +( cat $OBJ/sshd_proxy.orig ; echo HostKey $OBJ/hkr.${primary}-new ) \ + > $OBJ/sshd_proxy +# Check new hostkey added +dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=${primary},$all_algs +expect_nkeys `expr $nkeys + 1` "learn hostkeys" +check_key_present ${primary} || fail "current key missing" +check_key_present ${primary} $OBJ/hkr.${primary}-new.pub || fail "new key missing" + +# Remove old hostkey (primary type) from sshd +verbose "rotate primary hostkey" +cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy +mv $OBJ/hkr.${primary}.pub $OBJ/hkr.${primary}.pub.old +mv $OBJ/hkr.${primary}-new.pub $OBJ/hkr.${primary}.pub +mv $OBJ/hkr.${primary}-new $OBJ/hkr.${primary} +# Check old hostkey removed +dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=${primary},$all_algs +expect_nkeys $nkeys "learn hostkeys" +check_key_present ${primary} $OBJ/hkr.${primary}.pub.old && fail "old key present" +check_key_present ${primary} || fail "didn't learn changed key" + +# Connect again, forcing rotated key +verbose "check rotate primary hostkey" +dossh -oStrictHostKeyChecking=yes -oHostKeyAlgorithms=${primary} +expect_nkeys 1 "learn hostkeys" +check_key_present ${primary} || fail "didn't learn changed key" diff --git a/aosp/external/openssh/regress/integrity.sh b/aosp/external/openssh/regress/integrity.sh new file mode 100644 index 0000000000000000000000000000000000000000..bc030cb74f3566121a446224512c98d31c92c92b --- /dev/null +++ b/aosp/external/openssh/regress/integrity.sh @@ -0,0 +1,76 @@ +# $OpenBSD: integrity.sh,v 1.24 2020/01/21 08:06:27 djm Exp $ +# Placed in the Public Domain. + +tid="integrity" +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +# start at byte 2900 (i.e. after kex) and corrupt at different offsets +tries=10 +startoffset=2900 +macs=`${SSH} -Q mac` +# The following are not MACs, but ciphers with integrated integrity. They are +# handled specially below. +macs="$macs `${SSH} -Q cipher-auth`" + +# avoid DH group exchange as the extra traffic makes it harder to get the +# offset into the stream right. +#echo "KexAlgorithms -diffie-hellman-group*" \ +# >> $OBJ/ssh_proxy + +# sshd-command for proxy (see test-exec.sh) +cmd="$SUDO env SSH_SK_HELPER="$SSH_SK_HELPER" sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy" + +for m in $macs; do + trace "test $tid: mac $m" + elen=0 + epad=0 + emac=0 + etmo=0 + ecnt=0 + skip=0 + for off in `jot $tries $startoffset`; do + skip=`expr $skip - 1` + if [ $skip -gt 0 ]; then + # avoid modifying the high bytes of the length + continue + fi + cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy + # modify output from sshd at offset $off + pxy="proxycommand=$cmd | $OBJ/modpipe -wm xor:$off:1" + if ${SSH} -Q cipher-auth | grep "^${m}\$" >/dev/null 2>&1 ; then + echo "Ciphers=$m" >> $OBJ/sshd_proxy + macopt="-c $m" + else + echo "Ciphers=aes128-ctr" >> $OBJ/sshd_proxy + echo "MACs=$m" >> $OBJ/sshd_proxy + macopt="-m $m -c aes128-ctr" + fi + verbose "test $tid: $m @$off" + ${SSH} $macopt -F $OBJ/ssh_proxy -o "$pxy" \ + -oServerAliveInterval=1 -oServerAliveCountMax=30 \ + 999.999.999.999 'printf "%4096s" " "' >/dev/null + if [ $? -eq 0 ]; then + fail "ssh -m $m succeeds with bit-flip at $off" + fi + ecnt=`expr $ecnt + 1` + out=$(egrep -v "^debug" $TEST_SSH_LOGFILE | tail -2 | \ + tr -s '\r\n' '.') + case "$out" in + Bad?packet*) elen=`expr $elen + 1`; skip=3;; + Corrupted?MAC* | *message?authentication?code?incorrect*) + emac=`expr $emac + 1`; skip=0;; + padding*) epad=`expr $epad + 1`; skip=0;; + *Timeout,?server*) + etmo=`expr $etmo + 1`; skip=0;; + *) fail "unexpected error mac $m at $off: $out";; + esac + done + verbose "test $tid: $ecnt errors: mac $emac padding $epad length $elen timeout $etmo" + if [ $emac -eq 0 ]; then + fail "$m: no mac errors" + fi + expect=`expr $ecnt - $epad - $elen - $etmo` + if [ $emac -ne $expect ]; then + fail "$m: expected $expect mac errors, got $emac" + fi +done diff --git a/aosp/external/openssh/regress/kextype.sh b/aosp/external/openssh/regress/kextype.sh new file mode 100644 index 0000000000000000000000000000000000000000..e27189904bbb0eb4f57de5e97f5c1036b564f481 --- /dev/null +++ b/aosp/external/openssh/regress/kextype.sh @@ -0,0 +1,25 @@ +# $OpenBSD: kextype.sh,v 1.6 2015/03/24 20:19:15 markus Exp $ +# Placed in the Public Domain. + +tid="login with different key exchange algorithms" + +TIME=/usr/bin/time +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak +cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak + +# Make server accept all key exchanges. +ALLKEX=`${SSH} -Q kex` +KEXOPT=`echo $ALLKEX | tr ' ' ,` +echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy + +tries="1 2 3 4" +for k in `${SSH} -Q kex`; do + verbose "kex $k" + for i in $tries; do + ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true + if [ $? -ne 0 ]; then + fail "ssh kex $k" + fi + done +done + diff --git a/aosp/external/openssh/regress/key-options.sh b/aosp/external/openssh/regress/key-options.sh new file mode 100644 index 0000000000000000000000000000000000000000..2f3d66e2e9eacd3f5e5e6e88d509502faf9fd852 --- /dev/null +++ b/aosp/external/openssh/regress/key-options.sh @@ -0,0 +1,124 @@ +# $OpenBSD: key-options.sh,v 1.9 2018/07/03 13:53:26 djm Exp $ +# Placed in the Public Domain. + +tid="key options" + +origkeys="$OBJ/authkeys_orig" +authkeys="$OBJ/authorized_keys_${USER}" +cp $authkeys $origkeys + +# Allocating ptys can require privileges on some platforms. +skip_pty="" +if ! config_defined HAVE_OPENPTY && [ "x$SUDO" = "x" ]; then + skip_pty="no openpty(3) and SUDO not set" +fi + +# Test command= forced command +for c in 'command="echo bar"' 'no-pty,command="echo bar"'; do + sed "s/.*/$c &/" $origkeys >$authkeys + verbose "key option $c" + r=`${SSH} -q -F $OBJ/ssh_proxy somehost echo foo` + if [ "$r" = "foo" ]; then + fail "key option forced command not restricted" + fi + if [ "$r" != "bar" ]; then + fail "key option forced command not executed" + fi +done + +# Test no-pty +expect_pty_succeed() { + which=$1 + opts=$2 + rm -f $OBJ/data + sed "s/.*/$opts &/" $origkeys >$authkeys + verbose "key option pty $which" + [ "x$skip_pty" != "x" ] && verbose "skipped because $skip_pty" && return + ${SSH} -ttq -F $OBJ/ssh_proxy somehost "tty > $OBJ/data; exit 0" + if [ $? -ne 0 ] ; then + fail "key option failed $which" + else + r=`cat $OBJ/data` + case "$r" in + /dev/*) ;; + *) fail "key option failed $which (pty $r)" ;; + esac + fi +} +expect_pty_fail() { + which=$1 + opts=$2 + rm -f $OBJ/data + sed "s/.*/$opts &/" $origkeys >$authkeys + verbose "key option pty $which" + [ "x$skip_pty" != "x" ] && verbose "skipped because $skip_pty" && return + ${SSH} -ttq -F $OBJ/ssh_proxy somehost "tty > $OBJ/data; exit 0" + if [ $? -eq 0 ]; then + r=`cat $OBJ/data` + if [ -e "$r" ]; then + fail "key option failed $which (pty $r)" + fi + case "$r" in + /dev/*) fail "key option failed $which (pty $r)" ;; + *) ;; + esac + fi +} +# First ensure that we can allocate a pty by default. +expect_pty_succeed "default" "" +expect_pty_fail "no-pty" "no-pty" +expect_pty_fail "restrict" "restrict" +expect_pty_succeed "restrict,pty" "restrict,pty" + +# Test environment= +# XXX this can fail if ~/.ssh/environment exists for the user running the test +echo 'PermitUserEnvironment yes' >> $OBJ/sshd_proxy +sed 's/.*/environment="FOO=bar" &/' $origkeys >$authkeys +verbose "key option environment" +r=`${SSH} -q -F $OBJ/ssh_proxy somehost 'echo $FOO'` +if [ "$r" != "bar" ]; then + fail "key option environment not set" +fi + +# Test from= restriction +start_sshd +for f in 127.0.0.1 '127.0.0.0\/8'; do + cat $origkeys >$authkeys + ${SSH} -q -F $OBJ/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "key option failed without restriction" + fi + + sed 's/.*/from="'"$f"'" &/' $origkeys >$authkeys + from=`head -1 $authkeys | cut -f1 -d ' '` + verbose "key option $from" + r=`${SSH} -q -F $OBJ/ssh_proxy somehost 'echo true'` + if [ "$r" = "true" ]; then + fail "key option $from not restricted" + fi + + r=`${SSH} -q -F $OBJ/ssh_config somehost 'echo true'` + if [ "$r" != "true" ]; then + fail "key option $from not allowed but should be" + fi +done + +check_valid_before() { + which=$1 + opts=$2 + expect=$3 + sed "s/.*/$opts &/" $origkeys >$authkeys + verbose "key option expiry-time $which" + ${SSH} -q -F $OBJ/ssh_proxy somehost true + r=$? + case "$expect" in + fail) test $r -eq 0 && fail "key option succeeded $which" ;; + pass) test $r -ne 0 && fail "key option failed $which" ;; + *) fatal "unknown expectation $expect" ;; + esac +} +check_valid_before "default" "" "pass" +check_valid_before "invalid" 'expiry-time="INVALID"' "fail" +check_valid_before "expired" 'expiry-time="19990101"' "fail" +check_valid_before "valid" 'expiry-time="20380101"' "pass" + diff --git a/aosp/external/openssh/regress/keygen-change.sh b/aosp/external/openssh/regress/keygen-change.sh new file mode 100644 index 0000000000000000000000000000000000000000..3863e33b528722fb1826dc91bee025d184f0ab30 --- /dev/null +++ b/aosp/external/openssh/regress/keygen-change.sh @@ -0,0 +1,22 @@ +# $OpenBSD: keygen-change.sh,v 1.9 2019/12/16 02:39:05 djm Exp $ +# Placed in the Public Domain. + +tid="change passphrase for key" + +S1="secret1" +S2="2secret" + +for t in $SSH_KEYTYPES; do + trace "generating $t key" + rm -f $OBJ/$t-key + ${SSHKEYGEN} -q -N ${S1} -t $t -f $OBJ/$t-key + if [ $? -eq 0 ]; then + ${SSHKEYGEN} -p -P ${S1} -N ${S2} -f $OBJ/$t-key > /dev/null + if [ $? -ne 0 ]; then + fail "ssh-keygen -p failed for $t-key" + fi + else + fail "ssh-keygen for $t-key failed" + fi + rm -f $OBJ/$t-key $OBJ/$t-key.pub +done diff --git a/aosp/external/openssh/regress/keygen-comment.sh b/aosp/external/openssh/regress/keygen-comment.sh new file mode 100644 index 0000000000000000000000000000000000000000..af571d39035fb1ea2a60e4f54329b9f87f95c108 --- /dev/null +++ b/aosp/external/openssh/regress/keygen-comment.sh @@ -0,0 +1,52 @@ +#    Placed in the Public Domain. + +tid="Comment extraction from private key" + +S1="secret1" + +check_fingerprint () { + file="$1" + comment="$2" + trace "fingerprinting $file" + if ! ${SSHKEYGEN} -l -E sha256 -f $file > $OBJ/$t-fgp ; then + fail "ssh-keygen -l failed for $t-key" + fi + if ! egrep "^([0-9]+) SHA256:(.){43} ${comment} \(.*\)\$" \ + $OBJ/$t-fgp >/dev/null 2>&1 ; then + fail "comment is not correctly recovered for $t-key" + fi + rm -f $OBJ/$t-fgp +} + +for fmt in '' RFC4716 PKCS8 PEM; do + for t in $SSH_KEYTYPES; do + trace "generating $t key in '$fmt' format" + rm -f $OBJ/$t-key* + oldfmt="" + case "$fmt" in + PKCS8|PEM) oldfmt=1 ;; + esac + # Some key types like ssh-ed25519 and *@openssh.com are never + # stored in old formats. + case "$t" in + ssh-ed25519|*openssh.com) test -z "$oldfmt" || continue ;; + esac + comment="foo bar" + fmtarg="" + test -z "$fmt" || fmtarg="-m $fmt" + ${SSHKEYGEN} $fmtarg -N '' -C "${comment}" \ + -t $t -f $OBJ/$t-key >/dev/null 2>&1 || \ + fatal "keygen of $t in format $fmt failed" + check_fingerprint $OBJ/$t-key "${comment}" + check_fingerprint $OBJ/$t-key.pub "${comment}" + # Output fingerprint using only private file + trace "fingerprinting $t key using private key file" + rm -f $OBJ/$t-key.pub + if [ ! -z "$oldfmt" ] ; then + # Comment cannot be recovered from old format keys. + comment="no comment" + fi + check_fingerprint $OBJ/$t-key "${comment}" + rm -f $OBJ/$t-key* + done +done diff --git a/aosp/external/openssh/regress/keygen-convert.sh b/aosp/external/openssh/regress/keygen-convert.sh new file mode 100644 index 0000000000000000000000000000000000000000..95656581c5b1facabe26cae00862facf18141d9b --- /dev/null +++ b/aosp/external/openssh/regress/keygen-convert.sh @@ -0,0 +1,55 @@ +# $OpenBSD: keygen-convert.sh,v 1.6 2021/07/24 02:57:28 dtucker Exp $ +# Placed in the Public Domain. + +tid="convert keys" + +cat > $OBJ/askpass <&1 | grep "ssh-keygen -e" >/dev/null; then + test_import_export=1 +fi + +for t in ${SSH_KEYTYPES}; do + # generate user key for agent + trace "generating $t key" + rm -f $OBJ/$t-key + ${SSHKEYGEN} -q -N "" -t $t -f $OBJ/$t-key + + if test "x$test_import_export" = "x1"; then + trace "export $t private to rfc4716 public" + ${SSHKEYGEN} -q -e -f $OBJ/$t-key >$OBJ/$t-key-rfc || \ + fail "export $t private to rfc4716 public" + + trace "export $t public to rfc4716 public" + ${SSHKEYGEN} -q -e -f $OBJ/$t-key.pub >$OBJ/$t-key-rfc.pub || \ + fail "$t public to rfc4716 public" + + cmp $OBJ/$t-key-rfc $OBJ/$t-key-rfc.pub || \ + fail "$t rfc4716 exports differ between public and private" + + trace "import $t rfc4716 public" + ${SSHKEYGEN} -q -i -f $OBJ/$t-key-rfc >$OBJ/$t-rfc-imported || \ + fail "$t import rfc4716 public" + + cut -f1,2 -d " " $OBJ/$t-key.pub >$OBJ/$t-key-nocomment.pub + cmp $OBJ/$t-key-nocomment.pub $OBJ/$t-rfc-imported || \ + fail "$t imported differs from original" + fi + + trace "set passphrase $t" + ${SSHKEYGEN} -q -p -P '' -N 'hunter2' -f $OBJ/$t-key >/dev/null || \ + fail "$t set passphrase failed" + + trace "export $t to public with passphrase" + SSH_ASKPASS=$OBJ/askpass SSH_ASKPASS_REQUIRE=force \ + ${SSHKEYGEN} -y -f $OBJ/$t-key >$OBJ/$t-key-nocomment.pub + cmp $OBJ/$t-key.pub $OBJ/$t-key-nocomment.pub || \ + fail "$t exported pubkey differs from generated" + + rm -f $OBJ/$t-key $OBJ/$t-key.pub $OBJ/$t-key-rfc $OBJ/$t-key-rfc.pub \ + $OBJ/$t-rfc-imported $OBJ/$t-key-nocomment.pub +done diff --git a/aosp/external/openssh/regress/keygen-knownhosts.sh b/aosp/external/openssh/regress/keygen-knownhosts.sh new file mode 100644 index 0000000000000000000000000000000000000000..37af34769ecb36b79aaacf9d9d750b2e2742eae7 --- /dev/null +++ b/aosp/external/openssh/regress/keygen-knownhosts.sh @@ -0,0 +1,220 @@ +# $OpenBSD: keygen-knownhosts.sh,v 1.4 2018/06/01 03:52:37 djm Exp $ +# Placed in the Public Domain. + +tid="ssh-keygen known_hosts" + +rm -f $OBJ/kh.* + +# Generate some keys for testing (just ed25519 for speed) and make a hosts file. +for x in host-a host-b host-c host-d host-e host-f host-a2 host-b2; do + ${SSHKEYGEN} -qt ed25519 -f $OBJ/kh.$x -C "$x" -N "" || \ + fatal "ssh-keygen failed" + # Add a comment that we expect should be preserved. + echo "# $x" >> $OBJ/kh.hosts + ( + case "$x" in + host-a|host-b) printf "$x " ;; + host-c) printf "@cert-authority $x " ;; + host-d) printf "@revoked $x " ;; + host-e) printf "host-e* " ;; + host-f) printf "host-f,host-g,host-h " ;; + host-a2) printf "host-a " ;; + host-b2) printf "host-b " ;; + esac + cat $OBJ/kh.${x}.pub + # Blank line should be preserved. + echo "" >> $OBJ/kh.hosts + ) >> $OBJ/kh.hosts +done + +# Generate a variant with an invalid line. We'll use this for most tests, +# because keygen should be able to cope and it should be preserved in any +# output file. +cat $OBJ/kh.hosts >> $OBJ/kh.invalid +echo "host-i " >> $OBJ/kh.invalid + +cp $OBJ/kh.invalid $OBJ/kh.invalid.orig +cp $OBJ/kh.hosts $OBJ/kh.hosts.orig + +expect_key() { + _host=$1 + _hosts=$2 + _key=$3 + _line=$4 + _mark=$5 + _marker="" + test "x$_mark" = "xCA" && _marker="@cert-authority " + test "x$_mark" = "xREVOKED" && _marker="@revoked " + test "x$_line" != "x" && + echo "# Host $_host found: line $_line $_mark" >> $OBJ/kh.expect + printf "${_marker}$_hosts " >> $OBJ/kh.expect + cat $OBJ/kh.${_key}.pub >> $OBJ/kh.expect || + fatal "${_key}.pub missing" +} + +check_find() { + _host=$1 + _name=$2 + shift; shift + ${SSHKEYGEN} "$@" -f $OBJ/kh.invalid -F $_host > $OBJ/kh.result + if ! diff -w $OBJ/kh.expect $OBJ/kh.result ; then + fail "didn't find $_name" + fi +} + +check_find_exit_code() { + _host=$1 + _name=$2 + _keygenopt=$3 + _exp_exit_code=$4 + ${SSHKEYGEN} $_keygenopt -f $OBJ/kh.invalid -F $_host > /dev/null + if [ "$?" != "$_exp_exit_code" ] ; then + fail "Unexpected exit code $_name" + fi +} + +# Find key +rm -f $OBJ/kh.expect +expect_key host-a host-a host-a 2 +expect_key host-a host-a host-a2 20 +check_find host-a "simple find" + +# find CA key +rm -f $OBJ/kh.expect +expect_key host-c host-c host-c 8 CA +check_find host-c "find CA key" + +# find revoked key +rm -f $OBJ/kh.expect +expect_key host-d host-d host-d 11 REVOKED +check_find host-d "find revoked key" + +# find key with wildcard +rm -f $OBJ/kh.expect +expect_key host-e.somedomain "host-e*" host-e 14 +check_find host-e.somedomain "find wildcard key" + +# find key among multiple hosts +rm -f $OBJ/kh.expect +expect_key host-h "host-f,host-g,host-h " host-f 17 +check_find host-h "find multiple hosts" + +# Check exit code, known host +check_find_exit_code host-a "known host" "-q" "0" + +# Check exit code, unknown host +check_find_exit_code host-aa "unknown host" "-q" "1" + +# Check exit code, the hash mode, known host +check_find_exit_code host-a "known host" "-q -H" "0" + +# Check exit code, the hash mode, unknown host +check_find_exit_code host-aa "unknown host" "-q -H" "1" + +check_hashed_find() { + _host=$1 + _name=$2 + _file=$3 + test "x$_file" = "x" && _file=$OBJ/kh.invalid + ${SSHKEYGEN} -f $_file -HF $_host | grep '|1|' | \ + sed "s/^[^ ]*/$_host/" > $OBJ/kh.result + if ! diff -w $OBJ/kh.expect $OBJ/kh.result ; then + fail "didn't find $_name" + fi +} + +# Find key and hash +rm -f $OBJ/kh.expect +expect_key host-a host-a host-a +expect_key host-a host-a host-a2 +check_hashed_find host-a "find simple and hash" + +# Find CA key and hash +rm -f $OBJ/kh.expect +expect_key host-c host-c host-c "" CA +# CA key output is not hashed. +check_find host-c "find simple and hash" -Hq + +# Find revoked key and hash +rm -f $OBJ/kh.expect +expect_key host-d host-d host-d "" REVOKED +# Revoked key output is not hashed. +check_find host-d "find simple and hash" -Hq + +# find key with wildcard and hash +rm -f $OBJ/kh.expect +expect_key host-e "host-e*" host-e "" +# Key with wildcard hostname should not be hashed. +check_find host-e "find wildcard key" -Hq + +# find key among multiple hosts +rm -f $OBJ/kh.expect +# Comma-separated hostnames should be expanded and hashed. +expect_key host-f "host-h " host-f +expect_key host-g "host-h " host-f +expect_key host-h "host-h " host-f +check_hashed_find host-h "find multiple hosts" + +# Attempt remove key on invalid file. +cp $OBJ/kh.invalid.orig $OBJ/kh.invalid +${SSHKEYGEN} -qf $OBJ/kh.invalid -R host-a 2>/dev/null +diff $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "remove on invalid succeeded" + +# Remove key +cp $OBJ/kh.hosts.orig $OBJ/kh.hosts +${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-a 2>/dev/null +grep -v "^host-a " $OBJ/kh.hosts.orig > $OBJ/kh.expect +diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove simple" + +# Remove CA key +cp $OBJ/kh.hosts.orig $OBJ/kh.hosts +${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-c 2>/dev/null +# CA key should not be removed. +diff $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove CA" + +# Remove revoked key +cp $OBJ/kh.hosts.orig $OBJ/kh.hosts +${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-d 2>/dev/null +# revoked key should not be removed. +diff $OBJ/kh.hosts $OBJ/kh.hosts.orig || fail "remove revoked" + +# Remove wildcard +cp $OBJ/kh.hosts.orig $OBJ/kh.hosts +${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-e.blahblah 2>/dev/null +grep -v "^host-e[*] " $OBJ/kh.hosts.orig > $OBJ/kh.expect +diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" + +# Remove multiple +cp $OBJ/kh.hosts.orig $OBJ/kh.hosts +${SSHKEYGEN} -qf $OBJ/kh.hosts -R host-h 2>/dev/null +grep -v "^host-f," $OBJ/kh.hosts.orig > $OBJ/kh.expect +diff $OBJ/kh.hosts $OBJ/kh.expect || fail "remove wildcard" + +# Attempt hash on invalid file +cp $OBJ/kh.invalid.orig $OBJ/kh.invalid +${SSHKEYGEN} -qf $OBJ/kh.invalid -H 2>/dev/null && fail "hash invalid succeeded" +diff $OBJ/kh.invalid $OBJ/kh.invalid.orig || fail "invalid file modified" + +# Hash valid file +cp $OBJ/kh.hosts.orig $OBJ/kh.hosts +${SSHKEYGEN} -qf $OBJ/kh.hosts -H 2>/dev/null || fail "hash failed" +diff $OBJ/kh.hosts.old $OBJ/kh.hosts.orig || fail "backup differs" +grep "^host-[abfgh]" $OBJ/kh.hosts && fail "original hostnames persist" + +cp $OBJ/kh.hosts $OBJ/kh.hashed.orig + +# Test lookup +rm -f $OBJ/kh.expect +expect_key host-a host-a host-a +expect_key host-a host-a host-a2 +check_hashed_find host-a "find simple in hashed" $OBJ/kh.hosts + +# Test multiple expanded +rm -f $OBJ/kh.expect +expect_key host-h host-h host-f +check_hashed_find host-h "find simple in hashed" $OBJ/kh.hosts + +# Test remove +cp $OBJ/kh.hashed.orig $OBJ/kh.hashed +${SSHKEYGEN} -qf $OBJ/kh.hashed -R host-a 2>/dev/null +${SSHKEYGEN} -qf $OBJ/kh.hashed -F host-a && fail "found key after hashed remove" diff --git a/aosp/external/openssh/regress/keygen-moduli.sh b/aosp/external/openssh/regress/keygen-moduli.sh new file mode 100644 index 0000000000000000000000000000000000000000..8be53f92f852f40c12613a96de934bd7af215247 --- /dev/null +++ b/aosp/external/openssh/regress/keygen-moduli.sh @@ -0,0 +1,27 @@ +# $OpenBSD: keygen-moduli.sh,v 1.4 2020/01/02 13:25:38 dtucker Exp $ +# Placed in the Public Domain. + +tid="keygen moduli" + +dhgex=0 +for kex in `${SSH} -Q kex`; do + case $kex in + diffie-hellman-group*) dhgex=1 ;; + esac +done + +# Try "start at the beginning and stop after 1", "skip 1 then stop after 1" +# and "skip 2 and run to the end with checkpointing". Since our test data +# file has 3 lines, these should always result in 1 line of output. +if [ "x$dhgex" = "x1" ]; then + for i in "-O lines=1" "-O start-line=1 -O lines=1" "-O start-line=2 -O checkpoint=$OBJ/moduli.ckpt"; do + trace "keygen $i" + rm -f $OBJ/moduli.out $OBJ/moduli.ckpt + ${SSHKEYGEN} -M screen -f ${SRC}/moduli.in $i $OBJ/moduli.out 2>/dev/null || \ + fail "keygen screen failed $i" + lines=`wc -l <$OBJ/moduli.out` + test "$lines" -eq "1" || fail "expected 1 line, got $lines" + done +fi + +rm -f $OBJ/moduli.out $OBJ/moduli.ckpt diff --git a/aosp/external/openssh/regress/keygen-sshfp.sh b/aosp/external/openssh/regress/keygen-sshfp.sh new file mode 100644 index 0000000000000000000000000000000000000000..2abf9adecac71b2cf4639459e1736a3d065dabcf --- /dev/null +++ b/aosp/external/openssh/regress/keygen-sshfp.sh @@ -0,0 +1,29 @@ +# $OpenBSD: keygen-sshfp.sh,v 1.2 2021/07/19 02:29:28 dtucker Exp $ +# Placed in the Public Domain. + +tid="keygen-sshfp" + +trace "keygen fingerprints" +fp=`${SSHKEYGEN} -r test -f ${SRC}/ed25519_openssh.pub | \ + awk '$5=="1"{print $6}'` +if [ "$fp" != "8a8647a7567e202ce317e62606c799c53d4c121f" ]; then + fail "keygen fingerprint sha1" +fi +fp=`${SSHKEYGEN} -r test -f ${SRC}/ed25519_openssh.pub | \ + awk '$5=="2"{print $6}'` +if [ "$fp" != \ + "54a506fb849aafb9f229cf78a94436c281efcb4ae67c8a430e8c06afcb5ee18f" ]; then + fail "keygen fingerprint sha256" +fi + +if ${SSH} -Q key-plain | grep ssh-rsa >/dev/null; then + fp=`${SSHKEYGEN} -r test -f ${SRC}/rsa_openssh.pub | awk '$5=="1"{print $6}'` + if [ "$fp" != "99c79cc09f5f81069cc017cdf9552cfc94b3b929" ]; then + fail "keygen fingerprint sha1" + fi + fp=`${SSHKEYGEN} -r test -f ${SRC}/rsa_openssh.pub | awk '$5=="2"{print $6}'` + if [ "$fp" != \ + "e30d6b9eb7a4de495324e4d5870b8220577993ea6af417e8e4a4f1c5bf01a9b6" ]; then + fail "keygen fingerprint sha256" + fi +fi diff --git a/aosp/external/openssh/regress/keys-command.sh b/aosp/external/openssh/regress/keys-command.sh new file mode 100644 index 0000000000000000000000000000000000000000..5feec172b45df783498b92d81881fd7fa84e43fd --- /dev/null +++ b/aosp/external/openssh/regress/keys-command.sh @@ -0,0 +1,79 @@ +# $OpenBSD: keys-command.sh,v 1.8 2021/09/30 04:22:50 dtucker Exp $ +# Placed in the Public Domain. + +tid="authorized keys from command" + +if [ -z "$SUDO" -a ! -w /var/run ]; then + skip "need SUDO to create file in /var/run, test won't work without" +fi + +rm -f $OBJ/keys-command-args + +touch $OBJ/keys-command-args +chmod a+rw $OBJ/keys-command-args + +expected_key_text=`awk '{ print $2 }' < $OBJ/ssh-ed25519.pub` +expected_key_fp=`$SSHKEYGEN -lf $OBJ/ssh-ed25519.pub | awk '{ print $2 }'` + +# Establish a AuthorizedKeysCommand in /var/run where it will have +# acceptable directory permissions. +KEY_COMMAND="/var/run/keycommand_${LOGNAME}.$$" +trap "${SUDO} rm -f ${KEY_COMMAND}" 0 +cat << _EOF | $SUDO sh -c "rm -f '$KEY_COMMAND' ; cat > '$KEY_COMMAND'" +#!/bin/sh +echo args: "\$@" >> $OBJ/keys-command-args +echo "$PATH" | grep -q mekmitasdigoat && exit 7 +test "x\$1" != "x${LOGNAME}" && exit 1 +if test $# -eq 6 ; then + test "x\$2" != "xblah" && exit 2 + test "x\$3" != "x${expected_key_text}" && exit 3 + test "x\$4" != "xssh-rsa" && exit 4 + test "x\$5" != "x${expected_key_fp}" && exit 5 + test "x\$6" != "xblah" && exit 6 +fi +exec cat "$OBJ/authorized_keys_${LOGNAME}" +_EOF +$SUDO chmod 0755 "$KEY_COMMAND" + +if ! $OBJ/check-perm -m keys-command $KEY_COMMAND ; then + echo "skipping: $KEY_COMMAND is unsuitable as AuthorizedKeysCommand" + $SUDO rm -f $KEY_COMMAND + exit 0 +fi + +if [ -x $KEY_COMMAND ]; then + cp $OBJ/sshd_proxy $OBJ/sshd_proxy.bak + + verbose "AuthorizedKeysCommand with arguments" + ( + grep -vi AuthorizedKeysFile $OBJ/sshd_proxy.bak + echo AuthorizedKeysFile none + echo AuthorizedKeysCommand $KEY_COMMAND %u blah %k %t %f blah + echo AuthorizedKeysCommandUser ${LOGNAME} + ) > $OBJ/sshd_proxy + + # Ensure that $PATH is sanitised in sshd + env PATH=$PATH:/sbin/mekmitasdigoat \ + ${SSH} -F $OBJ/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "connect failed" + fi + + verbose "AuthorizedKeysCommand without arguments" + # Check legacy behavior of no-args resulting in username being passed. + ( + grep -vi AuthorizedKeysFile $OBJ/sshd_proxy.bak + echo AuthorizedKeysFile none + echo AuthorizedKeysCommand $KEY_COMMAND + echo AuthorizedKeysCommandUser ${LOGNAME} + ) > $OBJ/sshd_proxy + + # Ensure that $PATH is sanitised in sshd + env PATH=$PATH:/sbin/mekmitasdigoat \ + ${SSH} -F $OBJ/ssh_proxy somehost true + if [ $? -ne 0 ]; then + fail "connect failed" + fi +else + skip "$KEY_COMMAND not executable (/var/run mounted noexec?)" +fi diff --git a/aosp/external/openssh/regress/keyscan.sh b/aosp/external/openssh/regress/keyscan.sh new file mode 100644 index 0000000000000000000000000000000000000000..75a14ee0eecb2dca90a2fd7f72544b0a7416270d --- /dev/null +++ b/aosp/external/openssh/regress/keyscan.sh @@ -0,0 +1,25 @@ +# $OpenBSD: keyscan.sh,v 1.13 2020/01/22 07:31:27 dtucker Exp $ +# Placed in the Public Domain. + +tid="keyscan" + +for i in $SSH_KEYTYPES; do + if [ -z "$algs" ]; then + algs="$i" + else + algs="$algs,$i" + fi +done +echo "HostKeyAlgorithms $algs" >> $OBJ/sshd_config + +start_sshd + +for t in $SSH_KEYTYPES; do + trace "keyscan type $t" + ${SSHKEYSCAN} -t $t -T 15 -p $PORT 127.0.0.1 127.0.0.1 127.0.0.1 \ + > /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "ssh-keyscan -t $t failed with: $r" + fi +done diff --git a/aosp/external/openssh/regress/keytype.sh b/aosp/external/openssh/regress/keytype.sh new file mode 100644 index 0000000000000000000000000000000000000000..f1c045183bd38523c907a77c9267f2af60f041b6 --- /dev/null +++ b/aosp/external/openssh/regress/keytype.sh @@ -0,0 +1,83 @@ +# $OpenBSD: keytype.sh,v 1.11 2021/02/25 03:27:34 djm Exp $ +# Placed in the Public Domain. + +tid="login with different key types" + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak +cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak + +# Construct list of key types based on what the built binaries support. +ktypes="" +for i in ${SSH_KEYTYPES}; do + case "$i" in + ssh-dss) ktypes="$ktypes dsa-1024" ;; + ssh-rsa) ktypes="$ktypes rsa-2048 rsa-3072" ;; + ssh-ed25519) ktypes="$ktypes ed25519-512" ;; + ecdsa-sha2-nistp256) ktypes="$ktypes ecdsa-256" ;; + ecdsa-sha2-nistp384) ktypes="$ktypes ecdsa-384" ;; + ecdsa-sha2-nistp521) ktypes="$ktypes ecdsa-521" ;; + sk-ssh-ed25519*) ktypes="$ktypes ed25519-sk" ;; + sk-ecdsa-sha2-nistp256*) ktypes="$ktypes ecdsa-sk" ;; + esac +done + +for kt in $ktypes; do + rm -f $OBJ/key.$kt + xbits=`echo ${kt} | awk -F- '{print $2}'` + xtype=`echo ${kt} | awk -F- '{print $1}'` + case "$kt" in + *sk) type="$kt"; bits="n/a"; bits_arg="";; + *) type=$xtype; bits=$xbits; bits_arg="-b $bits";; + esac + verbose "keygen $type, $bits bits" + ${SSHKEYGEN} $bits_arg -q -N '' -t $type -f $OBJ/key.$kt || \ + fail "ssh-keygen for type $type, $bits bits failed" +done + +kname_to_ktype() { + case $1 in + dsa-1024) echo ssh-dss;; + ecdsa-256) echo ecdsa-sha2-nistp256;; + ecdsa-384) echo ecdsa-sha2-nistp384;; + ecdsa-521) echo ecdsa-sha2-nistp521;; + ed25519-512) echo ssh-ed25519;; + rsa-*) echo rsa-sha2-512,rsa-sha2-256,ssh-rsa;; + ed25519-sk) echo sk-ssh-ed25519@openssh.com;; + ecdsa-sk) echo sk-ecdsa-sha2-nistp256@openssh.com;; + esac +} + +tries="1 2 3" +for ut in $ktypes; do + user_type=`kname_to_ktype "$ut"` + htypes="$ut" + #htypes=$ktypes + for ht in $htypes; do + host_type=`kname_to_ktype "$ht"` + trace "ssh connect, userkey $ut, hostkey $ht" + ( + grep -v HostKey $OBJ/sshd_proxy_bak + echo HostKey $OBJ/key.$ht + echo PubkeyAcceptedAlgorithms $user_type + echo HostKeyAlgorithms $host_type + ) > $OBJ/sshd_proxy + ( + grep -v IdentityFile $OBJ/ssh_proxy_bak + echo IdentityFile $OBJ/key.$ut + echo PubkeyAcceptedAlgorithms $user_type + echo HostKeyAlgorithms $host_type + ) > $OBJ/ssh_proxy + ( + printf 'localhost-with-alias,127.0.0.1,::1 ' + cat $OBJ/key.$ht.pub + ) > $OBJ/known_hosts + cat $OBJ/key.$ut.pub > $OBJ/authorized_keys_$USER + for i in $tries; do + verbose "userkey $ut, hostkey ${ht}" + ${SSH} -F $OBJ/ssh_proxy 999.999.999.999 true + if [ $? -ne 0 ]; then + fail "ssh userkey $ut, hostkey $ht failed" + fi + done + done +done diff --git a/aosp/external/openssh/regress/knownhosts-command.sh b/aosp/external/openssh/regress/knownhosts-command.sh new file mode 100644 index 0000000000000000000000000000000000000000..8472ec8121c5259cf5b1dcb991076834171532a8 --- /dev/null +++ b/aosp/external/openssh/regress/knownhosts-command.sh @@ -0,0 +1,55 @@ +# $OpenBSD: knownhosts-command.sh,v 1.3 2021/08/30 01:15:45 djm Exp $ +# Placed in the Public Domain. + +tid="known hosts command " + +rm -f $OBJ/knownhosts_command $OBJ/ssh_proxy_khc +cp $OBJ/ssh_proxy $OBJ/ssh_proxy_orig + +( grep -vi GlobalKnownHostsFile $OBJ/ssh_proxy_orig | \ + grep -vi UserKnownHostsFile; + echo "GlobalKnownHostsFile none" ; + echo "UserKnownHostsFile none" ; + echo "KnownHostsCommand $OBJ/knownhosts_command '%t' '%K' '%u'" ; +) > $OBJ/ssh_proxy + +verbose "simple connection" +cat > $OBJ/knownhosts_command << _EOF +#!/bin/sh +cat $OBJ/known_hosts +_EOF +chmod a+x $OBJ/knownhosts_command +${SSH} -F $OBJ/ssh_proxy x true || fail "ssh connect failed" + +verbose "no keys" +cat > $OBJ/knownhosts_command << _EOF +#!/bin/sh +exit 0 +_EOF +chmod a+x $OBJ/knownhosts_command +${SSH} -F $OBJ/ssh_proxy x true && fail "ssh connect succeeded with no keys" + +verbose "bad exit status" +cat > $OBJ/knownhosts_command << _EOF +#!/bin/sh +cat $OBJ/known_hosts +exit 1 +_EOF +chmod a+x $OBJ/knownhosts_command +${SSH} -F $OBJ/ssh_proxy x true && fail "ssh connect succeeded with bad exit" + +for keytype in ${SSH_HOSTKEY_TYPES} ; do + algs=$keytype + test "x$keytype" = "xssh-dss" && continue + test "x$keytype" = "xssh-rsa" && algs=ssh-rsa,rsa-sha2-256,rsa-sha2-512 + verbose "keytype $keytype" + cat > $OBJ/knownhosts_command << _EOF +#!/bin/sh +die() { echo "\$@" 1>&2 ; exit 1; } +test "x\$1" = "x$keytype" || die "wrong keytype \$1 (expected $keytype)" +test "x\$3" = "x$LOGNAME" || die "wrong username \$3 (expected $LOGNAME)" +grep -- "\$1.*\$2" $OBJ/known_hosts +_EOF + ${SSH} -F $OBJ/ssh_proxy -oHostKeyAlgorithms=$algs x true || + fail "ssh connect failed for keytype $x" +done diff --git a/aosp/external/openssh/regress/knownhosts.sh b/aosp/external/openssh/regress/knownhosts.sh new file mode 100644 index 0000000000000000000000000000000000000000..dfc768ac9742335b699032d75490f5dc952fd1e5 --- /dev/null +++ b/aosp/external/openssh/regress/knownhosts.sh @@ -0,0 +1,17 @@ +# $OpenBSD: knownhosts.sh,v 1.1 2021/10/01 05:20:20 dtucker Exp $ +# Placed in the Public Domain. + +tid="known hosts" + +opts="-F $OBJ/ssh_proxy" + +trace "test initial connection" +${SSH} $opts somehost true || fail "initial connection" + +trace "learn hashed known host" +>$OBJ/known_hosts +${SSH} -ohashknownhosts=yes -o stricthostkeychecking=no $opts somehost true \ + || fail "learn hashed known_hosts" + +trace "test hashed known hosts" +${SSH} $opts somehost true || fail "reconnect with hashed known hosts" diff --git a/aosp/external/openssh/regress/krl.sh b/aosp/external/openssh/regress/krl.sh new file mode 100644 index 0000000000000000000000000000000000000000..c381225ed7cd6b30b0d9aaaa1fb81d36fcf0bf33 --- /dev/null +++ b/aosp/external/openssh/regress/krl.sh @@ -0,0 +1,217 @@ +# $OpenBSD: krl.sh,v 1.11 2019/12/16 02:39:05 djm Exp $ +# Placed in the Public Domain. + +tid="key revocation lists" + +# Use ed25519 by default since it's fast and it's supported when building +# w/out OpenSSL. Populate ktype[2-4] with the other types if supported. +ktype1=ed25519; ktype2=ed25519; ktype3=ed25519; +ktype4=ed25519; ktype5=ed25519; ktype6=ed25519; +for t in $SSH_KEYTYPES; do + case "$t" in + ecdsa*) ktype2=ecdsa ;; + ssh-rsa) ktype3=rsa ;; + ssh-dss) ktype4=dsa ;; + sk-ssh-ed25519@openssh.com) ktype5=ed25519-sk ;; + sk-ecdsa-sha2-nistp256@openssh.com) ktype6=ecdsa-sk ;; + esac +done + +# Do most testing with ssh-keygen; it uses the same verification code as sshd. + +# Old keys will interfere with ssh-keygen. +rm -f $OBJ/revoked-* $OBJ/krl-* + +# Generate a CA key +$SSHKEYGEN -t $ktype1 -f $OBJ/revoked-ca -C "" -N "" > /dev/null || + fatal "$SSHKEYGEN CA failed" +$SSHKEYGEN -t $ktype2 -f $OBJ/revoked-ca2 -C "" -N "" > /dev/null || + fatal "$SSHKEYGEN CA2 failed" + +# A specification that revokes some certificates by serial numbers +# The serial pattern is chosen to ensure the KRL includes list, range and +# bitmap sections. +cat << EOF >> $OBJ/revoked-serials +serial: 1-4 +serial: 10 +serial: 15 +serial: 30 +serial: 50 +serial: 90 +serial: 999 +# The following sum to 500-799 +serial: 500 +serial: 501 +serial: 502 +serial: 503-600 +serial: 700-797 +serial: 798 +serial: 799 +serial: 599-701 +# Some multiple consecutive serial number ranges +serial: 10000-20000 +serial: 30000-40000 +EOF + +# A specification that revokes some certificated by key ID. +touch $OBJ/revoked-keyid +for n in 1 2 3 4 10 15 30 50 90 `jot 500 300` 999 1000 1001 1002; do + test "x$n" = "x499" && continue + # Fill in by-ID revocation spec. + echo "id: revoked $n" >> $OBJ/revoked-keyid +done + +keygen() { + N=$1 + f=$OBJ/revoked-`printf "%04d" $N` + # Vary the keytype. We use mostly ed25519 since this is fast and well + # supported. + keytype=$ktype1 + case $N in + 2 | 10 | 510 | 1001) keytype=$ktype2 ;; + 4 | 30 | 520 | 1002) keytype=$ktype3 ;; + 8 | 50 | 530 | 1003) keytype=$ktype4 ;; + 16 | 70 | 540 | 1004) keytype=$ktype5 ;; + 32 | 90 | 550 | 1005) keytype=$ktype6 ;; + esac + $SSHKEYGEN -t $keytype -f $f -C "" -N "" > /dev/null \ + || fatal "$SSHKEYGEN failed" + # Sign cert + $SSHKEYGEN -s $OBJ/revoked-ca -z $n -I "revoked $N" $f >/dev/null 2>&1 \ + || fatal "$SSHKEYGEN sign failed" + echo $f +} + +# Generate some keys. +verbose "$tid: generating test keys" +REVOKED_SERIALS="1 4 10 50 90 500 510 520 550 799 999" +for n in $REVOKED_SERIALS ; do + f=`keygen $n` + RKEYS="$RKEYS ${f}.pub" + RCERTS="$RCERTS ${f}-cert.pub" +done +UNREVOKED_SERIALS="5 9 14 16 29 49 51 499 800 1010 1011" +UNREVOKED="" +for n in $UNREVOKED_SERIALS ; do + f=`keygen $n` + UKEYS="$UKEYS ${f}.pub" + UCERTS="$UCERTS ${f}-cert.pub" +done + +# Specifications that revoke keys by hash. +touch $OBJ/revoked-sha1 $OBJ/revoked-sha256 $OBJ/revoked-hash +for rkey in $RKEYS; do + (printf "sha1: "; cat $rkey) >> $OBJ/revoked-sha1 + (printf "sha256: "; cat $rkey) >> $OBJ/revoked-sha256 + (printf "hash: "; $SSHKEYGEN -lf $rkey | \ + awk '{ print $2 }') >> $OBJ/revoked-hash +done + +genkrls() { + OPTS=$1 +$SSHKEYGEN $OPTS -kf $OBJ/krl-empty - /dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-keys $RKEYS \ + >/dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-cert $RCERTS \ + >/dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-all $RKEYS $RCERTS \ + >/dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-ca $OBJ/revoked-ca.pub \ + >/dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-sha1 $OBJ/revoked-sha1 \ + >/dev/null 2>&1 || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-sha256 $OBJ/revoked-sha256 \ + >/dev/null 2>&1 || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-hash $OBJ/revoked-hash \ + >/dev/null 2>&1 || fatal "$SSHKEYGEN KRL failed" +# This should fail as KRLs from serial/key-id spec need the CA specified. +$SSHKEYGEN $OPTS -kf $OBJ/krl-serial $OBJ/revoked-serials \ + >/dev/null 2>&1 && fatal "$SSHKEYGEN KRL succeeded unexpectedly" +$SSHKEYGEN $OPTS -kf $OBJ/krl-keyid $OBJ/revoked-keyid \ + >/dev/null 2>&1 && fatal "$SSHKEYGEN KRL succeeded unexpectedly" +# These should succeed; they specify an explicit CA key. +$SSHKEYGEN $OPTS -kf $OBJ/krl-serial -s $OBJ/revoked-ca \ + $OBJ/revoked-serials >/dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-keyid -s $OBJ/revoked-ca.pub \ + $OBJ/revoked-keyid >/dev/null || fatal "$SSHKEYGEN KRL failed" +# These should succeed; they specify an wildcard CA key. +$SSHKEYGEN $OPTS -kf $OBJ/krl-serial-wild -s NONE $OBJ/revoked-serials \ + >/dev/null || fatal "$SSHKEYGEN KRL failed" +$SSHKEYGEN $OPTS -kf $OBJ/krl-keyid-wild -s NONE $OBJ/revoked-keyid \ + >/dev/null || fatal "$SSHKEYGEN KRL failed" +# Revoke the same serials with the second CA key to ensure a multi-CA +# KRL is generated. +$SSHKEYGEN $OPTS -kf $OBJ/krl-serial -u -s $OBJ/revoked-ca2 \ + $OBJ/revoked-serials >/dev/null || fatal "$SSHKEYGEN KRL failed" +} + +## XXX dump with trace and grep for set cert serials +## XXX test ranges near (u64)-1, etc. + +verbose "$tid: generating KRLs" +genkrls + +check_krl() { + KEY=$1 + KRL=$2 + EXPECT_REVOKED=$3 + TAG=$4 + $SSHKEYGEN -Qf $KRL $KEY >/dev/null + result=$? + if test "x$EXPECT_REVOKED" = "xy" -a $result -eq 0 ; then + fatal "key $KEY not revoked by KRL $KRL: $TAG" + elif test "x$EXPECT_REVOKED" = "xn" -a $result -ne 0 ; then + fatal "key $KEY unexpectedly revoked by KRL $KRL: $TAG" + fi +} +test_rev() { + FILES=$1 + TAG=$2 + KEYS_RESULT=$3 + ALL_RESULT=$4 + HASH_RESULT=$5 + SERIAL_RESULT=$6 + KEYID_RESULT=$7 + CERTS_RESULT=$8 + CA_RESULT=$9 + SERIAL_WRESULT=$10 + KEYID_WRESULT=$11 + verbose "$tid: checking revocations for $TAG" + for f in $FILES ; do + check_krl $f $OBJ/krl-empty no "$TAG" + check_krl $f $OBJ/krl-keys $KEYS_RESULT "$TAG" + check_krl $f $OBJ/krl-all $ALL_RESULT "$TAG" + check_krl $f $OBJ/krl-sha1 $HASH_RESULT "$TAG" + check_krl $f $OBJ/krl-sha256 $HASH_RESULT "$TAG" + check_krl $f $OBJ/krl-hash $HASH_RESULT "$TAG" + check_krl $f $OBJ/krl-serial $SERIAL_RESULT "$TAG" + check_krl $f $OBJ/krl-keyid $KEYID_RESULT "$TAG" + check_krl $f $OBJ/krl-cert $CERTS_RESULT "$TAG" + check_krl $f $OBJ/krl-ca $CA_RESULT "$TAG" + check_krl $f $OBJ/krl-serial-wild $SERIAL_WRESULT "$TAG" + check_krl $f $OBJ/krl-keyid-wild $KEYID_WRESULT "$TAG" + done +} + +test_all() { + # wildcard + # keys all hash sr# ID cert CA srl ID + test_rev "$RKEYS" "revoked keys" y y y n n n n n n + test_rev "$UKEYS" "unrevoked keys" n n n n n n n n n + test_rev "$RCERTS" "revoked certs" y y y y y y y y y + test_rev "$UCERTS" "unrevoked certs" n n n n n n y n n +} + +test_all + +# Check update. Results should be identical. +verbose "$tid: testing KRL update" +for f in $OBJ/krl-keys $OBJ/krl-cert $OBJ/krl-all \ + $OBJ/krl-ca $OBJ/krl-serial $OBJ/krl-keyid \ + $OBJ/krl-serial-wild $OBJ/krl-keyid-wild; do + cp -f $OBJ/krl-empty $f + genkrls -u +done + +test_all diff --git a/aosp/external/openssh/regress/limit-keytype.sh b/aosp/external/openssh/regress/limit-keytype.sh new file mode 100644 index 0000000000000000000000000000000000000000..7127de007cc63cceee25211ea36dd93a9ab37043 --- /dev/null +++ b/aosp/external/openssh/regress/limit-keytype.sh @@ -0,0 +1,133 @@ +# $OpenBSD: limit-keytype.sh,v 1.10 2021/02/25 03:27:34 djm Exp $ +# Placed in the Public Domain. + +tid="restrict pubkey type" + +# XXX sk-* keys aren't actually tested ATM. + +rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/user_key* +rm -f $OBJ/authorized_principals_$USER $OBJ/cert_user_key* + +mv $OBJ/sshd_proxy $OBJ/sshd_proxy.orig +mv $OBJ/ssh_proxy $OBJ/ssh_proxy.orig + +ktype1=ed25519; ktype2=ed25519; ktype3=ed25519; +ktype4=ed25519; ktype5=ed25519; ktype6=ed25519; +for t in $SSH_KEYTYPES ; do + case "$t" in + ssh-rsa) ktype2=rsa ;; + ecdsa*) ktype3=ecdsa ;; # unused + ssh-dss) ktype4=dsa ;; + sk-ssh-ed25519@openssh.com) ktype5=ed25519-sk ;; + sk-ecdsa-sha2-nistp256@openssh.com) ktype6=ecdsa-sk ;; + esac +done + +# Create a CA key +${SSHKEYGEN} -q -N '' -t $ktype1 -f $OBJ/user_ca_key ||\ + fatal "ssh-keygen failed" + +# Make some keys and a certificate. +${SSHKEYGEN} -q -N '' -t $ktype1 -f $OBJ/user_key1 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t $ktype2 -f $OBJ/user_key2 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t $ktype2 -f $OBJ/user_key3 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t $ktype4 -f $OBJ/user_key4 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t $ktype5 -f $OBJ/user_key5 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t $ktype6 -f $OBJ/user_key6 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -s $OBJ/user_ca_key -I "regress user key for $USER" \ + -z $$ -n ${USER},mekmitasdigoat $OBJ/user_key3 || + fatal "couldn't sign user_key1" +# Copy the private key alongside the cert to allow better control of when +# it is offered. +mv $OBJ/user_key3-cert.pub $OBJ/cert_user_key3.pub + +grep -v IdentityFile $OBJ/ssh_proxy.orig > $OBJ/ssh_proxy + +opts="-oProtocol=2 -F $OBJ/ssh_proxy -oIdentitiesOnly=yes" +certopts="$opts -i $OBJ/user_key3 -oCertificateFile=$OBJ/cert_user_key3.pub" + +echo mekmitasdigoat > $OBJ/authorized_principals_$USER +cat $OBJ/user_key1.pub > $OBJ/authorized_keys_$USER +cat $OBJ/user_key2.pub >> $OBJ/authorized_keys_$USER + +prepare_config() { + ( + grep -v "Protocol" $OBJ/sshd_proxy.orig + echo "Protocol 2" + echo "AuthenticationMethods publickey" + echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" + echo "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u" + for x in "$@" ; do + echo "$x" + done + ) > $OBJ/sshd_proxy +} + +# Return the required parameter for PubkeyAcceptedAlgorithms corresponding to +# the supplied key type. +keytype() { + case "$1" in + ecdsa) printf "ecdsa-sha2-*" ;; + ed25519) printf "ssh-ed25519" ;; + dsa) printf "ssh-dss" ;; + rsa) printf "rsa-sha2-256,rsa-sha2-512,ssh-rsa" ;; + sk-ecdsa) printf "sk-ecdsa-*" ;; + sk-ssh-ed25519) printf "sk-ssh-ed25519-*" ;; + esac +} + +prepare_config + +# Check we can log in with all key types. +${SSH} $certopts proxy true || fatal "cert failed" +${SSH} $opts -i $OBJ/user_key1 proxy true || fatal "key1 failed" +${SSH} $opts -i $OBJ/user_key2 proxy true || fatal "key2 failed" + +# Allow plain Ed25519 and RSA. The certificate should fail. +verbose "allow $ktype2,$ktype1" +prepare_config \ + "PubkeyAcceptedAlgorithms `keytype $ktype2`,`keytype $ktype1`" +${SSH} $certopts proxy true && fatal "cert succeeded" +${SSH} $opts -i $OBJ/user_key1 proxy true || fatal "key1 failed" +${SSH} $opts -i $OBJ/user_key2 proxy true || fatal "key2 failed" + +# Allow Ed25519 only. +verbose "allow $ktype1" +prepare_config "PubkeyAcceptedAlgorithms `keytype $ktype1`" +${SSH} $certopts proxy true && fatal "cert succeeded" +${SSH} $opts -i $OBJ/user_key1 proxy true || fatal "key1 failed" +if [ "$ktype1" != "$ktype2" ]; then + ${SSH} $opts -i $OBJ/user_key2 proxy true && fatal "key2 succeeded" +fi + +# Allow all certs. Plain keys should fail. +verbose "allow cert only" +prepare_config "PubkeyAcceptedAlgorithms *-cert-v01@openssh.com" +${SSH} $certopts proxy true || fatal "cert failed" +${SSH} $opts -i $OBJ/user_key1 proxy true && fatal "key1 succeeded" +${SSH} $opts -i $OBJ/user_key2 proxy true && fatal "key2 succeeded" + +# Allow RSA in main config, Ed25519 for non-existent user. +verbose "match w/ no match" +prepare_config "PubkeyAcceptedAlgorithms `keytype $ktype2`" \ + "Match user x$USER" "PubkeyAcceptedAlgorithms +`keytype $ktype1`" +${SSH} $certopts proxy true && fatal "cert succeeded" +if [ "$ktype1" != "$ktype2" ]; then + ${SSH} $opts -i $OBJ/user_key1 proxy true && fatal "key1 succeeded" +fi +${SSH} $opts -i $OBJ/user_key2 proxy true || fatal "key2 failed" + +# Allow only DSA in main config, Ed25519 for user. +verbose "match w/ matching" +prepare_config "PubkeyAcceptedAlgorithms `keytype $ktype4`" \ + "Match user $USER" "PubkeyAcceptedAlgorithms +`keytype $ktype1`" +${SSH} $certopts proxy true || fatal "cert failed" +${SSH} $opts -i $OBJ/user_key1 proxy true || fatal "key1 failed" +${SSH} $opts -i $OBJ/user_key4 proxy true && fatal "key4 succeeded" + diff --git a/aosp/external/openssh/regress/localcommand.sh b/aosp/external/openssh/regress/localcommand.sh new file mode 100644 index 0000000000000000000000000000000000000000..5224a16b24d7c5bb61d9bad679a5e773a677337d --- /dev/null +++ b/aosp/external/openssh/regress/localcommand.sh @@ -0,0 +1,13 @@ +# $OpenBSD: localcommand.sh,v 1.4 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="localcommand" + +echo 'PermitLocalCommand yes' >> $OBJ/ssh_proxy +echo 'LocalCommand echo foo' >> $OBJ/ssh_proxy + +verbose "test $tid: proto $p localcommand" +a=`${SSH} -F $OBJ/ssh_proxy somehost true` +if [ "$a" != "foo" ] ; then + fail "$tid proto $p" +fi diff --git a/aosp/external/openssh/regress/login-timeout.sh b/aosp/external/openssh/regress/login-timeout.sh new file mode 100644 index 0000000000000000000000000000000000000000..1577da1590f6450807c3e5a4440d1e85428978d1 --- /dev/null +++ b/aosp/external/openssh/regress/login-timeout.sh @@ -0,0 +1,18 @@ +# $OpenBSD: login-timeout.sh,v 1.10 2021/09/30 05:20:08 dtucker Exp $ +# Placed in the Public Domain. + +tid="connect after login grace timeout" + +trace "test login grace time" +cp $OBJ/sshd_config $OBJ/sshd_config.orig +grep -vi LoginGraceTime $OBJ/sshd_config.orig > $OBJ/sshd_config +echo "LoginGraceTime 10s" >> $OBJ/sshd_config +echo "MaxStartups 1" >> $OBJ/sshd_config +start_sshd + +(echo SSH-2.0-fake; sleep 60) | telnet 127.0.0.1 ${PORT} >/dev/null 2>&1 & +sleep 15 +${SSH} -F $OBJ/ssh_config somehost true +if [ $? -ne 0 ]; then + fail "ssh connect after login grace timeout failed" +fi diff --git a/aosp/external/openssh/regress/misc/Makefile b/aosp/external/openssh/regress/misc/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b9149f2879eb2133a393962dea0ed2b3fc8c6606 --- /dev/null +++ b/aosp/external/openssh/regress/misc/Makefile @@ -0,0 +1,3 @@ +SUBDIR= sk-dummy + +.include diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/Makefile b/aosp/external/openssh/regress/misc/fuzz-harness/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..3938ac853d3137a68677cb87628692fed9ca930e --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/Makefile @@ -0,0 +1,52 @@ +# NB. libssh and libopenbsd-compat should be built with the same sanitizer opts. +CC=clang-11 +CXX=clang++-11 +FUZZ_FLAGS=-fsanitize=address,fuzzer -fno-omit-frame-pointer +FUZZ_LIBS=-lFuzzer + +CXXFLAGS=-O2 -g -Wall -Wextra -Wno-unused-parameter -I ../../.. $(FUZZ_FLAGS) +CFLAGS=$(CXXFLAGS) +LDFLAGS=-L ../../.. -L ../../../openbsd-compat -g $(FUZZ_FLAGS) +LIBS=-lssh -lopenbsd-compat -lmd -lcrypto -lfido2 -lcbor $(FUZZ_LIBS) +SK_NULL_OBJS=ssh-sk-null.o +COMMON_DEPS=../../../libssh.a + +TARGETS=pubkey_fuzz sig_fuzz authopt_fuzz sshsig_fuzz \ + sshsigopt_fuzz privkey_fuzz kex_fuzz agent_fuzz + +all: $(TARGETS) + +.cc.o: + $(CXX) $(CXXFLAGS) -c $< -o $@ + +pubkey_fuzz: pubkey_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ pubkey_fuzz.o $(SK_NULL_OBJS) $(LDFLAGS) $(LIBS) + +sig_fuzz: sig_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ sig_fuzz.o $(SK_NULL_OBJS) $(LDFLAGS) $(LIBS) + +authopt_fuzz: authopt_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ authopt_fuzz.o $(SK_NULL_OBJS) ../../../auth-options.o $(LDFLAGS) $(LIBS) + +sshsig_fuzz: sshsig_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ sshsig_fuzz.o $(SK_NULL_OBJS) ../../../sshsig.o $(LDFLAGS) $(LIBS) + +sshsigopt_fuzz: sshsigopt_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ sshsigopt_fuzz.o $(SK_NULL_OBJS) ../../../sshsig.o $(LDFLAGS) $(LIBS) + +privkey_fuzz: privkey_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ privkey_fuzz.o $(SK_NULL_OBJS) $(LDFLAGS) $(LIBS) + +kex_fuzz: kex_fuzz.o $(SK_NULL_OBJS) $(COMMON_DEPS) + $(CXX) -o $@ kex_fuzz.o $(SK_NULL_OBJS) $(LDFLAGS) $(LIBS) -lz + +agent_fuzz: agent_fuzz.o agent_fuzz_helper.o sk-dummy.o ../../../ssh-sk.o $(COMMON_DEPS) + $(CXX) -o $@ agent_fuzz.o agent_fuzz_helper.o sk-dummy.o ../../../ssh-sk.o $(LDFLAGS) $(LIBS) -lz + +agent_fuzz_helper.o: agent_fuzz_helper.c ../../../ssh-agent.c + +sk-dummy.o: ../sk-dummy/sk-dummy.c + $(CC) $(CFLAGS) -c -o $@ ../sk-dummy/sk-dummy.c -DSK_DUMMY_INTEGRATE=1 $(LDFLAGS) + +clean: + -rm -f *.o $(TARGETS) diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/README b/aosp/external/openssh/regress/misc/fuzz-harness/README new file mode 100644 index 0000000000000000000000000000000000000000..ae6fbe75d6b6af0c46bdf249076d198b07173b29 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/README @@ -0,0 +1 @@ +This directory contains fuzzing harnesses for use with clang's libfuzzer. diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/agent_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/agent_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..ad85b2f9a29750c1c5cc8c9b48081e5571619bcd --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/agent_fuzz.cc @@ -0,0 +1,15 @@ +// cc_fuzz_target test for ssh-agent. +extern "C" { + +#include +#include + +extern void test_one(const uint8_t* s, size_t slen); + +int LLVMFuzzerTestOneInput(const uint8_t* s, size_t slen) +{ + test_one(s, slen); + return 0; +} + +} // extern diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/agent_fuzz_helper.c b/aosp/external/openssh/regress/misc/fuzz-harness/agent_fuzz_helper.c new file mode 100644 index 0000000000000000000000000000000000000000..1d419820cc5d3b0789506bdef3c3f2850c89aab9 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/agent_fuzz_helper.c @@ -0,0 +1,177 @@ +#include "fixed-keys.h" +#include + +#define main(ac, av) xxxmain(ac, av) +#include "../../../ssh-agent.c" + +void test_one(const uint8_t* s, size_t slen); + +static int +devnull_or_die(void) +{ + int fd; + + if ((fd = open("/dev/null", O_RDWR)) == -1) { + error_f("open /dev/null: %s", strerror(errno)); + abort(); + } + return fd; +} + +static struct sshkey * +pubkey_or_die(const char *s) +{ + char *tmp, *cp; + struct sshkey *pubkey; + int r; + + tmp = cp = xstrdup(s); + if ((pubkey = sshkey_new(KEY_UNSPEC)) == NULL) + abort(); + if ((r = sshkey_read(pubkey, &cp)) != 0) { + error_fr(r, "parse"); + abort(); + } + free(tmp); + return pubkey; +} + +static struct sshkey * +privkey_or_die(const char *s) +{ + int r; + struct sshbuf *b; + struct sshkey *privkey; + + if ((b = sshbuf_from(s, strlen(s))) == NULL) { + error_f("sshbuf_from failed"); + abort(); + } + if ((r = sshkey_parse_private_fileblob(b, "", &privkey, NULL)) != 0) { + error_fr(r, "parse"); + abort(); + } + sshbuf_free(b); + return privkey; +} + +static void +add_key(const char *privkey, const char *certpath) +{ + Identity *id; + int r; + struct sshkey *cert; + + id = xcalloc(1, sizeof(Identity)); + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); + idtab->nentries++; + id->key = privkey_or_die(privkey); + id->comment = xstrdup("rhododaktulos Eos"); + if (sshkey_is_sk(id->key)) + id->sk_provider = xstrdup("internal"); + + /* Now the cert too */ + id = xcalloc(1, sizeof(Identity)); + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); + idtab->nentries++; + id->key = privkey_or_die(privkey); + cert = pubkey_or_die(certpath); + if ((r = sshkey_to_certified(id->key)) != 0) { + error_fr(r, "sshkey_to_certified"); + abort(); + } + if ((r = sshkey_cert_copy(cert, id->key)) != 0) { + error_fr(r, "sshkey_cert_copy"); + abort(); + } + sshkey_free(cert); + id->comment = xstrdup("outis"); + if (sshkey_is_sk(id->key)) + id->sk_provider = xstrdup("internal"); +} + +static void +cleanup_idtab(void) +{ + Identity *id; + + if (idtab == NULL) return; + for (id = TAILQ_FIRST(&idtab->idlist); id; + id = TAILQ_FIRST(&idtab->idlist)) { + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + } + free(idtab); + idtab = NULL; +} + +static void +reset_idtab(void) +{ + cleanup_idtab(); + idtab_init(); + // Load keys. + add_key(PRIV_RSA, CERT_RSA); + add_key(PRIV_DSA, CERT_DSA); + add_key(PRIV_ECDSA, CERT_ECDSA); + add_key(PRIV_ED25519, CERT_ED25519); + add_key(PRIV_ECDSA_SK, CERT_ECDSA_SK); + add_key(PRIV_ED25519_SK, CERT_ED25519_SK); +} + +static void +cleanup_sockettab(void) +{ + u_int i; + for (i = 0; i < sockets_alloc; i++) { + if (sockets[i].type != AUTH_UNUSED) + close_socket(sockets + i); + } + free(sockets); + sockets = NULL; + sockets_alloc = 0; +} + +static void +reset_sockettab(int devnull) +{ + int fd; + + cleanup_sockettab(); + if ((fd = dup(devnull)) == -1) { + error_f("dup: %s", strerror(errno)); + abort(); + } + new_socket(AUTH_CONNECTION, fd); + assert(sockets[0].type == AUTH_CONNECTION); + assert(sockets[0].fd == fd); +} + +#define MAX_MESSAGES 256 +void +test_one(const uint8_t* s, size_t slen) +{ + static int devnull = -1; + size_t i, olen, nlen; + + if (devnull == -1) { + log_init(__progname, SYSLOG_LEVEL_DEBUG3, + SYSLOG_FACILITY_AUTH, 1); + devnull = devnull_or_die(); + allowed_providers = xstrdup(""); + setenv("DISPLAY", "", 1); /* ban askpass */ + } + + reset_idtab(); + reset_sockettab(devnull); + (void)sshbuf_put(sockets[0].input, s, slen); + for (i = 0; i < MAX_MESSAGES; i++) { + olen = sshbuf_len(sockets[0].input); + process_message(0); + nlen = sshbuf_len(sockets[0].input); + if (nlen == 0 || nlen == olen) + break; + } + cleanup_idtab(); + cleanup_sockettab(); +} diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/authopt_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/authopt_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..a76d5a3f17fa02dbc4f82c896ce549007c0126f8 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/authopt_fuzz.cc @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +extern "C" { + +#include "auth-options.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + char *cp = (char *)malloc(size + 1); + struct sshauthopt *opts = NULL, *merge = NULL, *add = sshauthopt_new(); + + if (cp == NULL || add == NULL) + goto out; + memcpy(cp, data, size); + cp[size] = '\0'; + if ((opts = sshauthopt_parse(cp, NULL)) == NULL) + goto out; + if ((merge = sshauthopt_merge(opts, add, NULL)) == NULL) + goto out; + + out: + free(cp); + sshauthopt_free(add); + sshauthopt_free(opts); + sshauthopt_free(merge); + return 0; +} + +} // extern "C" diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/fixed-keys.h b/aosp/external/openssh/regress/misc/fuzz-harness/fixed-keys.h new file mode 100644 index 0000000000000000000000000000000000000000..c6e7c6cc1828d6bff809cfc138ffdfec6c08a7f3 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/fixed-keys.h @@ -0,0 +1,119 @@ +/* + * Some keys used by fuzzers + */ + +#define PRIV_RSA \ +"-----BEGIN OPENSSH PRIVATE KEY-----\n"\ +"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\n"\ +"NhAAAAAwEAAQAAAQEA3+epf+VGKoGPaAZXrf6S0cyumQnddkGBnVFX0A5eh37RtLug0qY5\n"\ +"thxsBUbGGVr9mTd2QXwLujBwYg5l1MP/Fmg+5312Zgx9pHmS+qKULbar0hlNgptNEb+aNU\n"\ +"d3o9qg3aXqXm7+ZnjAV05ef/mxNRN2ZvuEkw7cRppTJcbBI+vF3lXuCXnX2klDI95Gl2AW\n"\ +"3WHRtanqLHZXuBkjjRBDKc7MUq/GP1hmLiAd95dvU7fZjRlIEsP84zGEI1Fb0L/kmPHcOt\n"\ +"iVfHft8CtmC9v6+94JrOiPBBNScV+dyrgAGPsdKdr/1vIpQmCNiI8s3PCiD8J7ZiBaYm0I\n"\ +"8fq5G/qnUwAAA7ggw2dXIMNnVwAAAAdzc2gtcnNhAAABAQDf56l/5UYqgY9oBlet/pLRzK\n"\ +"6ZCd12QYGdUVfQDl6HftG0u6DSpjm2HGwFRsYZWv2ZN3ZBfAu6MHBiDmXUw/8WaD7nfXZm\n"\ +"DH2keZL6opQttqvSGU2Cm00Rv5o1R3ej2qDdpepebv5meMBXTl5/+bE1E3Zm+4STDtxGml\n"\ +"MlxsEj68XeVe4JedfaSUMj3kaXYBbdYdG1qeosdle4GSONEEMpzsxSr8Y/WGYuIB33l29T\n"\ +"t9mNGUgSw/zjMYQjUVvQv+SY8dw62JV8d+3wK2YL2/r73gms6I8EE1JxX53KuAAY+x0p2v\n"\ +"/W8ilCYI2Ijyzc8KIPwntmIFpibQjx+rkb+qdTAAAAAwEAAQAAAQEArWm5B4tFasppjUHM\n"\ +"SsAuajtCxtizI1Hc10EW59cZM4vvUzE2f6+qZvdgWj3UU/L7Et23w0QVuSCnCerox379ZB\n"\ +"ddEOFFAAiQjwBx65hbd4RRUymxtIQfjq18++LcMJW1nbVQ7c69ThQbtALIggmbS+ZE/8Gx\n"\ +"jkwmIrCH0Ww8TlpsPe+mNHuyNk7UEZoXLm22lNLqq5qkIL5JgT6M2iNJpMOJy9/CKi6kO4\n"\ +"JPuVwjdG4C5pBPaMN3KJ1IvAlSlLGNaXnfXcn85gWfsCjsZmH3liey2NJamqp/w83BrKUg\n"\ +"YZvMR2qeWZaKkFTahpzN5KRK1BFeB37O0P84Dzh1biDX8QAAAIEAiWXW8ePYFwLpa2mFIh\n"\ +"VvRTdcrN70rVK5eWVaL3pyS4vGA56Jixq86dHveOnbSY+iNb1jQidtXc8SWUt2wtHqZ32h\n"\ +"Lji9/hMSKqe9SEP3xvDRDmUJqsVw0ySyrFrzm4160QY6RKU3CIQCVFslMZ9fxmrfZ/hxoU\n"\ +"0X3FVsxmC4+kwAAACBAPOc1YERpV6PjANBrGR+1o1RCdACbm5myc42QzSNIaOZmgrYs+Gt\n"\ +"7+EcoqSdbJzHJNCNQfF+A+vjbIkFiuZqq/5wwr59qXx5OAlijLB/ywwKmTWq6lp//Zxny+\n"\ +"ka3sIGNO14eQvmxNDnlLL+RIZleCTEKBXSW6CZhr+uHMZFKKMtAAAAgQDrSkm+LbILB7H9\n"\ +"jxEBZLhv53aAn4u81kFKQOJ7PzzpBGSoD12i7oIJu5siSD5EKDNVEr+SvCf0ISU3BuMpzl\n"\ +"t3YrPrHRheOFhn5e3j0e//zB8rBC0DGB4CtTDdeh7rOXUL4K0pz+8wEpNkV62SWxhC6NRW\n"\ +"I79JhtGkh+GtcnkEfwAAAAAB\n"\ +"-----END OPENSSH PRIVATE KEY-----\n" +#define PUB_RSA \ +"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf56l/5UYqgY9oBlet/pLRzK6ZCd12QYGdUVfQDl6HftG0u6DSpjm2HGwFRsYZWv2ZN3ZBfAu6MHBiDmXUw/8WaD7nfXZmDH2keZL6opQttqvSGU2Cm00Rv5o1R3ej2qDdpepebv5meMBXTl5/+bE1E3Zm+4STDtxGmlMlxsEj68XeVe4JedfaSUMj3kaXYBbdYdG1qeosdle4GSONEEMpzsxSr8Y/WGYuIB33l29Tt9mNGUgSw/zjMYQjUVvQv+SY8dw62JV8d+3wK2YL2/r73gms6I8EE1JxX53KuAAY+x0p2v/W8ilCYI2Ijyzc8KIPwntmIFpibQjx+rkb+qdT" +#define CERT_RSA \ +"ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg89JX6OBMYDSxER8fnU5y8xxeMCHR/hI0uVqdEhNyCpcAAAADAQABAAABAQDf56l/5UYqgY9oBlet/pLRzK6ZCd12QYGdUVfQDl6HftG0u6DSpjm2HGwFRsYZWv2ZN3ZBfAu6MHBiDmXUw/8WaD7nfXZmDH2keZL6opQttqvSGU2Cm00Rv5o1R3ej2qDdpepebv5meMBXTl5/+bE1E3Zm+4STDtxGmlMlxsEj68XeVe4JedfaSUMj3kaXYBbdYdG1qeosdle4GSONEEMpzsxSr8Y/WGYuIB33l29Tt9mNGUgSw/zjMYQjUVvQv+SY8dw62JV8d+3wK2YL2/r73gms6I8EE1JxX53KuAAY+x0p2v/W8ilCYI2Ijyzc8KIPwntmIFpibQjx+rkb+qdTAAAAAAAAA+0AAAABAAAAB3VseXNzZXMAAAAXAAAAB3VseXNzZXMAAAAIb2R5c3NldXMAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgM9BeYRUxUuZ4VHJp8oxVaA8OS/z+5EFPCZwQNq1nMwMAAABTAAAAC3NzaC1lZDI1NTE5AAAAQGCDA6PWw4x9bHQl0w7NqifHepumqD3dmyMx+hZGuPRon+TsyCjfytu7hWmV7l9XUF0fPQNFQ7FGat5e+7YUNgE= id_rsa.pub" +#define PRIV_DSA \ +"-----BEGIN OPENSSH PRIVATE KEY-----\n"\ +"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH\n"\ +"NzAAAAgQCsGTfjpQ465EOkfQXJM9BOvfRQE0fqlykAls+ncz+T7hrbeScRu8xpwzsznJNm\n"\ +"xlW8o6cUDiHmBJ5OHgamUC9N7YJeU/6fnOAZifgN8mqK6k8pKHuje8ANOiYgHLl0yiASQA\n"\ +"3//qMyzZ+W/hemoLSmLAbEqlfWVeyYx+wta1Vm+QAAABUAvWyehvUvdHvQxavYgS5p0t5Q\n"\ +"d7UAAACBAIRA9Yy+f4Kzqpv/qICPO3zk42UuP7WAhSW2nCbQdLlCiSTxcjKgcvXNRckwJP\n"\ +"44JjSHOtJy/AMtJrPIbLYG6KuWTdBlEHFiG6DafvLG+qPMSL2bPjXTOhuOMbCHIZ+5WBkW\n"\ +"THeG/Nv11iI01Of9V6tXkig23K370flkRkXFi9MdAAAAgCt6YUcQkNwG7B/e5M1FZsLP9O\n"\ +"kVB3BwLAOjmWdHpyhu3HpwSJa3XLEvhXN0i6IVI2KgPo/2GtYA6rHt14L+6u1pmhh8sAvQ\n"\ +"ksp3qZB+xh/NP+hBqf0sbHX0yYbzKOvI5SCc/kKK6yagcBZOsubM/KC8TxyVgmD5c6WzYs\n"\ +"h5TEpvAAAB2PHjRbbx40W2AAAAB3NzaC1kc3MAAACBAKwZN+OlDjrkQ6R9Bckz0E699FAT\n"\ +"R+qXKQCWz6dzP5PuGtt5JxG7zGnDOzOck2bGVbyjpxQOIeYEnk4eBqZQL03tgl5T/p+c4B\n"\ +"mJ+A3yaorqTykoe6N7wA06JiAcuXTKIBJADf/+ozLNn5b+F6agtKYsBsSqV9ZV7JjH7C1r\n"\ +"VWb5AAAAFQC9bJ6G9S90e9DFq9iBLmnS3lB3tQAAAIEAhED1jL5/grOqm/+ogI87fOTjZS\n"\ +"4/tYCFJbacJtB0uUKJJPFyMqBy9c1FyTAk/jgmNIc60nL8Ay0ms8hstgboq5ZN0GUQcWIb\n"\ +"oNp+8sb6o8xIvZs+NdM6G44xsIchn7lYGRZMd4b82/XWIjTU5/1Xq1eSKDbcrfvR+WRGRc\n"\ +"WL0x0AAACAK3phRxCQ3AbsH97kzUVmws/06RUHcHAsA6OZZ0enKG7cenBIlrdcsS+Fc3SL\n"\ +"ohUjYqA+j/Ya1gDqse3Xgv7q7WmaGHywC9CSynepkH7GH80/6EGp/SxsdfTJhvMo68jlIJ\n"\ +"z+QorrJqBwFk6y5sz8oLxPHJWCYPlzpbNiyHlMSm8AAAAUUA+OGldMi76ClO/sstpdbBUE\n"\ +"lq8AAAAAAQI=\n"\ +"-----END OPENSSH PRIVATE KEY-----\n" +#define PUB_DSA \ +"ssh-dss AAAAB3NzaC1kc3MAAACBAKwZN+OlDjrkQ6R9Bckz0E699FATR+qXKQCWz6dzP5PuGtt5JxG7zGnDOzOck2bGVbyjpxQOIeYEnk4eBqZQL03tgl5T/p+c4BmJ+A3yaorqTykoe6N7wA06JiAcuXTKIBJADf/+ozLNn5b+F6agtKYsBsSqV9ZV7JjH7C1rVWb5AAAAFQC9bJ6G9S90e9DFq9iBLmnS3lB3tQAAAIEAhED1jL5/grOqm/+ogI87fOTjZS4/tYCFJbacJtB0uUKJJPFyMqBy9c1FyTAk/jgmNIc60nL8Ay0ms8hstgboq5ZN0GUQcWIboNp+8sb6o8xIvZs+NdM6G44xsIchn7lYGRZMd4b82/XWIjTU5/1Xq1eSKDbcrfvR+WRGRcWL0x0AAACAK3phRxCQ3AbsH97kzUVmws/06RUHcHAsA6OZZ0enKG7cenBIlrdcsS+Fc3SLohUjYqA+j/Ya1gDqse3Xgv7q7WmaGHywC9CSynepkH7GH80/6EGp/SxsdfTJhvMo68jlIJz+QorrJqBwFk6y5sz8oLxPHJWCYPlzpbNiyHlMSm8=" +#define CERT_DSA \ +"ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAguF716Yub+vVKNlONKLsfxGYWkRe/PyjfYdGRTsFaDvAAAACBAKwZN+OlDjrkQ6R9Bckz0E699FATR+qXKQCWz6dzP5PuGtt5JxG7zGnDOzOck2bGVbyjpxQOIeYEnk4eBqZQL03tgl5T/p+c4BmJ+A3yaorqTykoe6N7wA06JiAcuXTKIBJADf/+ozLNn5b+F6agtKYsBsSqV9ZV7JjH7C1rVWb5AAAAFQC9bJ6G9S90e9DFq9iBLmnS3lB3tQAAAIEAhED1jL5/grOqm/+ogI87fOTjZS4/tYCFJbacJtB0uUKJJPFyMqBy9c1FyTAk/jgmNIc60nL8Ay0ms8hstgboq5ZN0GUQcWIboNp+8sb6o8xIvZs+NdM6G44xsIchn7lYGRZMd4b82/XWIjTU5/1Xq1eSKDbcrfvR+WRGRcWL0x0AAACAK3phRxCQ3AbsH97kzUVmws/06RUHcHAsA6OZZ0enKG7cenBIlrdcsS+Fc3SLohUjYqA+j/Ya1gDqse3Xgv7q7WmaGHywC9CSynepkH7GH80/6EGp/SxsdfTJhvMo68jlIJz+QorrJqBwFk6y5sz8oLxPHJWCYPlzpbNiyHlMSm8AAAAAAAAD6AAAAAEAAAAHdWx5c3NlcwAAABcAAAAHdWx5c3NlcwAAAAhvZHlzc2V1cwAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAFMAAAALc3NoLWVkMjU1MTkAAABAjMQEZcbdUYJBjIC4GxByFDOb8tv71vDZdx7irHwaqIjx5rzpJUuOV1r8ZO4kY+Yaiun1yrWj2QYkfJrHBvD1DA== id_dsa.pub" +#define PRIV_ECDSA \ +"-----BEGIN OPENSSH PRIVATE KEY-----\n"\ +"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS\n"\ +"1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTDJ0VlMv+0rguNzaJ1DF2KueHaxRSQ\n"\ +"6LpIxGbulrg1a8RPbnMXwag5GcDiDllD2lDUJUuBEWyjXA0rZoZX35ELAAAAoE/Bbr5PwW\n"\ +"6+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMMnRWUy/7SuC43N\n"\ +"onUMXYq54drFFJDoukjEZu6WuDVrxE9ucxfBqDkZwOIOWUPaUNQlS4ERbKNcDStmhlffkQ\n"\ +"sAAAAhAIhE6hCID5oOm1TDktc++KFKyScjLifcZ6Cgv5xSSyLOAAAAAAECAwQFBgc=\n"\ +"-----END OPENSSH PRIVATE KEY-----\n" +#define PUB_ECDSA \ +"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMMnRWUy/7SuC43NonUMXYq54drFFJDoukjEZu6WuDVrxE9ucxfBqDkZwOIOWUPaUNQlS4ERbKNcDStmhlffkQs=" +#define CERT_ECDSA \ +"ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgVJZuM/1AOe6n++qRWMyUuAThYqLvvQxj5CGflLODp60AAAAIbmlzdHAyNTYAAABBBMMnRWUy/7SuC43NonUMXYq54drFFJDoukjEZu6WuDVrxE9ucxfBqDkZwOIOWUPaUNQlS4ERbKNcDStmhlffkQsAAAAAAAAD6QAAAAEAAAAHdWx5c3NlcwAAABcAAAAHdWx5c3NlcwAAAAhvZHlzc2V1cwAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAFMAAAALc3NoLWVkMjU1MTkAAABAtdJpcF6ZmQL+ueices4QZeL7AK8Xuo08jyLgiolhjKy2jj4LSUki4aX/ZeZeJuby1ovGrfaeFAgx3itPLR7IAQ== id_ecdsa.pub" +#define PRIV_ED25519 \ +"-----BEGIN OPENSSH PRIVATE KEY-----\n"\ +"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n"\ +"QyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAIhWlP99VpT/\n"\ +"fQAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAw\n"\ +"AAAEDE1rlcMC0s0X3TKVZAOVavZOywwkXw8tO5dLObxaCMEDPQXmEVMVLmeFRyafKMVWgP\n"\ +"Dkv8/uRBTwmcEDatZzMDAAAAAAECAwQF\n"\ +"-----END OPENSSH PRIVATE KEY-----\n" +#define PUB_ED25519 \ +"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDPQXmEVMVLmeFRyafKMVWgPDkv8/uRBTwmcEDatZzMD" +#define CERT_ED25519 \ +"ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIMDQjYH6XRzH3j3MW1DdjCoAfvrHfgjnVGF+sLK0pBfqAAAAIDPQXmEVMVLmeFRyafKMVWgPDkv8/uRBTwmcEDatZzMDAAAAAAAAA+sAAAABAAAAB3VseXNzZXMAAAAXAAAAB3VseXNzZXMAAAAIb2R5c3NldXMAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgM9BeYRUxUuZ4VHJp8oxVaA8OS/z+5EFPCZwQNq1nMwMAAABTAAAAC3NzaC1lZDI1NTE5AAAAQBj0og+s09/HpwdHZbzN0twooKPDWWrxGfnP1Joy6cDnY2BCSQ7zg9vbq11kLF8H/sKOTZWAQrUZ7LlChOu9Ogw= id_ed25519.pub" +#define PRIV_ECDSA_SK \ +"-----BEGIN OPENSSH PRIVATE KEY-----\n"\ +"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2\n"\ +"RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQTYyU76zop1\n"\ +"VOb4DfKWYnR5b0TOC3zw8DzObAfHWB5o6xls+tOYiEleXvIEi00Da2iCK47habZTOhLyeB\n"\ +"X2Avu5AAAABHNzaDoAAAGYqUAQSKlAEEgAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv\n"\ +"cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEE2MlO+s6KdVTm+A3ylmJ0eW9Ezgt88PA8zm\n"\ +"wHx1geaOsZbPrTmIhJXl7yBItNA2togiuO4Wm2UzoS8ngV9gL7uQAAAARzc2g6AQAAAOMt\n"\ +"LS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUhjQ0FRRUVJSHFsZjNsWTkxZFhwUn\n"\ +"dYZDBrS0lYWmNpeDRRcDBNSU15Ny9JMUxXSTFuWG9Bb0dDQ3FHU000OQpBd0VIb1VRRFFn\n"\ +"QUUyTWxPK3M2S2RWVG0rQTN5bG1KMGVXOUV6Z3Q4OFBBOHptd0h4MWdlYU9zWmJQclRtSW\n"\ +"hKClhsN3lCSXROQTJ0b2dpdU80V20yVXpvUzhuZ1Y5Z0w3dVE9PQotLS0tLUVORCBFQyBQ\n"\ +"UklWQVRFIEtFWS0tLS0tCgAAAAAAAAAbZGptQGRqbS5zeWQuY29ycC5nb29nbGUuY29tAQ\n"\ +"IDBAUG\n"\ +"-----END OPENSSH PRIVATE KEY-----\n" +#define PUB_ECDSA_SK \ +"sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBNjJTvrOinVU5vgN8pZidHlvRM4LfPDwPM5sB8dYHmjrGWz605iISV5e8gSLTQNraIIrjuFptlM6EvJ4FfYC+7kAAAAEc3NoOg==" +#define CERT_ECDSA_SK \ +"sk-ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAK3NrLWVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgKLHtIca++5VoDrUAXU/KqGJZ7jZEnuJSTvt7VrYY9foAAAAIbmlzdHAyNTYAAABBBNjJTvrOinVU5vgN8pZidHlvRM4LfPDwPM5sB8dYHmjrGWz605iISV5e8gSLTQNraIIrjuFptlM6EvJ4FfYC+7kAAAAEc3NoOgAAAAAAAAPqAAAAAQAAAAd1bHlzc2VzAAAAFwAAAAd1bHlzc2VzAAAACG9keXNzZXVzAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIDPQXmEVMVLmeFRyafKMVWgPDkv8/uRBTwmcEDatZzMDAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEB1naZOQDLaDr+fwn6E9x8/8HeiaUubDzPexfNQMz+m/7RD0gd5uJhHYUfDb5+/sIx1I7bUEeRIDkBbmZ2foo0E" +#define PRIV_ED25519_SK \ +"-----BEGIN OPENSSH PRIVATE KEY-----\n"\ +"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2\n"\ +"gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACCTJtH10vWhIDxd62edvMLg9u2cwYKyqa7332je\n"\ +"RArHjAAAAARzc2g6AAAAwN7vvE3e77xNAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2\n"\ +"9tAAAAIJMm0fXS9aEgPF3rZ528wuD27ZzBgrKprvffaN5ECseMAAAABHNzaDoBAAAAQEsS\n"\ +"xLFiVzfpH2mt9xh8i/zmHV646Hud4QruNBAGNl8gkybR9dL1oSA8XetnnbzC4PbtnMGCsq\n"\ +"mu999o3kQKx4wAAAAAAAAAG2RqbUBkam0uc3lkLmNvcnAuZ29vZ2xlLmNvbQECAwQFBg==\n"\ +"-----END OPENSSH PRIVATE KEY-----\n" +#define PUB_ED25519_SK \ +"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJMm0fXS9aEgPF3rZ528wuD27ZzBgrKprvffaN5ECseMAAAABHNzaDo=" +#define CERT_ED25519_SK \ +"sk-ssh-ed25519-cert-v01@openssh.com AAAAI3NrLXNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJiT+C/VLMWholFZ4xhOyJr0nSLZSFRIM3I07wUNTRPaAAAAIJMm0fXS9aEgPF3rZ528wuD27ZzBgrKprvffaN5ECseMAAAABHNzaDoAAAAAAAAD7AAAAAEAAAAHdWx5c3NlcwAAABcAAAAHdWx5c3NlcwAAAAhvZHlzc2V1cwAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAFMAAAALc3NoLWVkMjU1MTkAAABAX0Pu13B94pVR3qq8MJQGkOS1Cd7AAM1k6O2VSwyDPM/LfsWIQ4ywgxDmk3hjXWOY7BqljuMxo5VO4JymEIhQBA==" diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/kex_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/kex_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..d38ca8597072ff84bc4961255fa30be93cb5fc5d --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/kex_fuzz.cc @@ -0,0 +1,460 @@ +// libfuzzer driver for key exchange fuzzing. + + +#include +#include +#include +#include +#include + +extern "C" { + +#include "includes.h" +#include "ssherr.h" +#include "ssh_api.h" +#include "sshbuf.h" +#include "packet.h" +#include "myproposal.h" +#include "xmalloc.h" +#include "authfile.h" +#include "log.h" + +#include "fixed-keys.h" + +// Define if you want to generate traces. +/* #define STANDALONE 1 */ + +static int prepare_key(struct shared_state *st, int keytype, int bits); + +struct shared_state { + size_t nkeys; + struct sshkey **privkeys, **pubkeys; +}; + +struct test_state { + struct sshbuf *smsgs, *cmsgs; /* output, for standalone mode */ + struct sshbuf *sin, *cin; /* input; setup per-test in do_kex_with_key */ + struct sshbuf *s_template, *c_template; /* main copy of input */ +}; + +static int +do_send_and_receive(struct ssh *from, struct ssh *to, + struct sshbuf *store, int clobber, size_t *n) +{ + u_char type; + size_t len; + const u_char *buf; + int r; + + for (*n = 0;; (*n)++) { + if ((r = ssh_packet_next(from, &type)) != 0) { + debug_fr(r, "ssh_packet_next"); + return r; + } + if (type != 0) + return 0; + buf = ssh_output_ptr(from, &len); + debug_f("%zu%s", len, clobber ? " ignore" : ""); + if (len == 0) + return 0; + if ((r = ssh_output_consume(from, len)) != 0) { + debug_fr(r, "ssh_output_consume"); + return r; + } + if (store != NULL && (r = sshbuf_put(store, buf, len)) != 0) { + debug_fr(r, "sshbuf_put"); + return r; + } + if (!clobber && (r = ssh_input_append(to, buf, len)) != 0) { + debug_fr(r, "ssh_input_append"); + return r; + } + } +} + +static int +run_kex(struct test_state *ts, struct ssh *client, struct ssh *server) +{ + int r = 0; + size_t cn, sn; + + /* If fuzzing, replace server/client input */ + if (ts->sin != NULL) { + if ((r = ssh_input_append(server, sshbuf_ptr(ts->sin), + sshbuf_len(ts->sin))) != 0) { + error_fr(r, "ssh_input_append"); + return r; + } + sshbuf_reset(ts->sin); + } + if (ts->cin != NULL) { + if ((r = ssh_input_append(client, sshbuf_ptr(ts->cin), + sshbuf_len(ts->cin))) != 0) { + error_fr(r, "ssh_input_append"); + return r; + } + sshbuf_reset(ts->cin); + } + while (!server->kex->done || !client->kex->done) { + cn = sn = 0; + debug_f("S:"); + if ((r = do_send_and_receive(server, client, + ts->smsgs, ts->cin != NULL, &sn)) != 0) { + debug_fr(r, "S->C"); + break; + } + debug_f("C:"); + if ((r = do_send_and_receive(client, server, + ts->cmsgs, ts->sin != NULL, &cn)) != 0) { + debug_fr(r, "C->S"); + break; + } + if (cn == 0 && sn == 0) { + debug_f("kex stalled"); + r = SSH_ERR_PROTOCOL_ERROR; + break; + } + } + debug_fr(r, "done"); + return r; +} + +static void +store_key(struct shared_state *st, struct sshkey *pubkey, + struct sshkey *privkey) +{ + if (st == NULL || pubkey->type < 0 || pubkey->type > INT_MAX || + privkey->type != pubkey->type || + ((size_t)pubkey->type < st->nkeys && + st->pubkeys[pubkey->type] != NULL)) + abort(); + if ((size_t)pubkey->type >= st->nkeys) { + st->pubkeys = (struct sshkey **)xrecallocarray(st->pubkeys, + st->nkeys, pubkey->type + 1, sizeof(*st->pubkeys)); + st->privkeys = (struct sshkey **)xrecallocarray(st->privkeys, + st->nkeys, privkey->type + 1, sizeof(*st->privkeys)); + st->nkeys = privkey->type + 1; + } + debug_f("store %s at %d", sshkey_ssh_name(pubkey), pubkey->type); + st->pubkeys[pubkey->type] = pubkey; + st->privkeys[privkey->type] = privkey; +} + +static int +prepare_keys(struct shared_state *st) +{ + if (prepare_key(st, KEY_RSA, 2048) != 0 || + prepare_key(st, KEY_DSA, 1024) != 0 || + prepare_key(st, KEY_ECDSA, 256) != 0 || + prepare_key(st, KEY_ED25519, 256) != 0) { + error_f("key prepare failed"); + return -1; + } + return 0; +} + +static struct sshkey * +get_pubkey(struct shared_state *st, int keytype) +{ + if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys || + st->pubkeys == NULL || st->pubkeys[keytype] == NULL) + abort(); + return st->pubkeys[keytype]; +} + +static struct sshkey * +get_privkey(struct shared_state *st, int keytype) +{ + if (st == NULL || keytype < 0 || (size_t)keytype >= st->nkeys || + st->privkeys == NULL || st->privkeys[keytype] == NULL) + abort(); + return st->privkeys[keytype]; +} + +static int +do_kex_with_key(struct shared_state *st, struct test_state *ts, + const char *kex, int keytype) +{ + struct ssh *client = NULL, *server = NULL; + struct sshkey *privkey = NULL, *pubkey = NULL; + struct sshbuf *state = NULL; + struct kex_params kex_params; + const char *ccp, *proposal[PROPOSAL_MAX] = { KEX_CLIENT }; + char *myproposal[PROPOSAL_MAX] = {0}, *keyname = NULL; + int i, r; + + ts->cin = ts->sin = NULL; + if (ts->c_template != NULL && + (ts->cin = sshbuf_fromb(ts->c_template)) == NULL) + abort(); + if (ts->s_template != NULL && + (ts->sin = sshbuf_fromb(ts->s_template)) == NULL) + abort(); + + pubkey = get_pubkey(st, keytype); + privkey = get_privkey(st, keytype); + keyname = xstrdup(sshkey_ssh_name(privkey)); + if (ts->cin != NULL) { + debug_f("%s %s clobber client %zu", kex, keyname, + sshbuf_len(ts->cin)); + } else if (ts->sin != NULL) { + debug_f("%s %s clobber server %zu", kex, keyname, + sshbuf_len(ts->sin)); + } else + debug_f("%s %s noclobber", kex, keyname); + + for (i = 0; i < PROPOSAL_MAX; i++) { + ccp = proposal[i]; +#ifdef CIPHER_NONE_AVAIL + if (i == PROPOSAL_ENC_ALGS_CTOS || i == PROPOSAL_ENC_ALGS_STOC) + ccp = "none"; +#endif + if (i == PROPOSAL_SERVER_HOST_KEY_ALGS) + ccp = keyname; + else if (i == PROPOSAL_KEX_ALGS && kex != NULL) + ccp = kex; + if ((myproposal[i] = strdup(ccp)) == NULL) { + error_f("strdup prop %d", i); + goto fail; + } + } + memcpy(kex_params.proposal, myproposal, sizeof(myproposal)); + if ((r = ssh_init(&client, 0, &kex_params)) != 0) { + error_fr(r, "init client"); + goto fail; + } + if ((r = ssh_init(&server, 1, &kex_params)) != 0) { + error_fr(r, "init server"); + goto fail; + } + if ((r = ssh_add_hostkey(server, privkey)) != 0 || + (r = ssh_add_hostkey(client, pubkey)) != 0) { + error_fr(r, "add hostkeys"); + goto fail; + } + if ((r = run_kex(ts, client, server)) != 0) { + error_fr(r, "kex"); + goto fail; + } + /* XXX rekex, set_state, etc */ + fail: + for (i = 0; i < PROPOSAL_MAX; i++) + free(myproposal[i]); + sshbuf_free(ts->sin); + sshbuf_free(ts->cin); + sshbuf_free(state); + ssh_free(client); + ssh_free(server); + free(keyname); + return r; +} + +static int +prepare_key(struct shared_state *st, int kt, int bits) +{ + const char *pubstr = NULL; + const char *privstr = NULL; + char *tmp, *cp; + struct sshkey *privkey = NULL, *pubkey = NULL; + struct sshbuf *b = NULL; + int r; + + switch (kt) { + case KEY_RSA: + pubstr = PUB_RSA; + privstr = PRIV_RSA; + break; + case KEY_DSA: + pubstr = PUB_DSA; + privstr = PRIV_DSA; + break; + case KEY_ECDSA: + pubstr = PUB_ECDSA; + privstr = PRIV_ECDSA; + break; + case KEY_ED25519: + pubstr = PUB_ED25519; + privstr = PRIV_ED25519; + break; + default: + abort(); + } + if ((b = sshbuf_from(privstr, strlen(privstr))) == NULL) + abort(); + if ((r = sshkey_parse_private_fileblob(b, "", &privkey, NULL)) != 0) { + error_fr(r, "priv %d", kt); + abort(); + } + sshbuf_free(b); + tmp = cp = xstrdup(pubstr); + if ((pubkey = sshkey_new(KEY_UNSPEC)) == NULL) + abort(); + if ((r = sshkey_read(pubkey, &cp)) != 0) { + error_fr(r, "pub %d", kt); + abort(); + } + free(tmp); + + store_key(st, pubkey, privkey); + return 0; +} + +#if defined(STANDALONE) + +#if 0 /* use this if generating new keys to embed above */ +static int +prepare_key(struct shared_state *st, int keytype, int bits) +{ + struct sshkey *privkey = NULL, *pubkey = NULL; + int r; + + if ((r = sshkey_generate(keytype, bits, &privkey)) != 0) { + error_fr(r, "generate"); + abort(); + } + if ((r = sshkey_from_private(privkey, &pubkey)) != 0) { + error_fr(r, "make pubkey"); + abort(); + } + store_key(st, pubkey, privkey); + return 0; +} +#endif + +int main(void) +{ + static struct shared_state *st; + struct test_state *ts; + const int keytypes[] = { KEY_RSA, KEY_DSA, KEY_ECDSA, KEY_ED25519, -1 }; + static const char * const kextypes[] = { + "sntrup761x25519-sha512@openssh.com", + "curve25519-sha256@libssh.org", + "ecdh-sha2-nistp256", + "diffie-hellman-group1-sha1", + "diffie-hellman-group-exchange-sha1", + NULL, + }; + int i, j; + char *path; + FILE *f; + + log_init("kex_fuzz", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 1); + + if (st == NULL) { + st = (struct shared_state *)xcalloc(1, sizeof(*st)); + prepare_keys(st); + } + /* Run each kex method for each key and save client/server packets */ + for (i = 0; keytypes[i] != -1; i++) { + for (j = 0; kextypes[j] != NULL; j++) { + ts = (struct test_state *)xcalloc(1, sizeof(*ts)); + ts->smsgs = sshbuf_new(); + ts->cmsgs = sshbuf_new(); + do_kex_with_key(st, ts, kextypes[j], keytypes[i]); + xasprintf(&path, "S2C-%s-%s", + kextypes[j], sshkey_type(st->pubkeys[keytypes[i]])); + debug_f("%s", path); + if ((f = fopen(path, "wb+")) == NULL) + abort(); + if (fwrite(sshbuf_ptr(ts->smsgs), 1, + sshbuf_len(ts->smsgs), f) != sshbuf_len(ts->smsgs)) + abort(); + fclose(f); + free(path); + //sshbuf_dump(ts->smsgs, stderr); + xasprintf(&path, "C2S-%s-%s", + kextypes[j], sshkey_type(st->pubkeys[keytypes[i]])); + debug_f("%s", path); + if ((f = fopen(path, "wb+")) == NULL) + abort(); + if (fwrite(sshbuf_ptr(ts->cmsgs), 1, + sshbuf_len(ts->cmsgs), f) != sshbuf_len(ts->cmsgs)) + abort(); + fclose(f); + free(path); + //sshbuf_dump(ts->cmsgs, stderr); + sshbuf_free(ts->smsgs); + sshbuf_free(ts->cmsgs); + free(ts); + } + } + for (i = 0; keytypes[i] != -1; i++) { + xasprintf(&path, "%s.priv", + sshkey_type(st->privkeys[keytypes[i]])); + debug_f("%s", path); + if (sshkey_save_private(st->privkeys[keytypes[i]], path, + "", "", SSHKEY_PRIVATE_OPENSSH, NULL, 0) != 0) + abort(); + free(path); + xasprintf(&path, "%s.pub", + sshkey_type(st->pubkeys[keytypes[i]])); + debug_f("%s", path); + if (sshkey_save_public(st->pubkeys[keytypes[i]], path, "") != 0) + abort(); + free(path); + } +} +#else /* !STANDALONE */ +static void +do_kex(struct shared_state *st, struct test_state *ts, const char *kex) +{ + do_kex_with_key(st, ts, kex, KEY_RSA); + do_kex_with_key(st, ts, kex, KEY_DSA); + do_kex_with_key(st, ts, kex, KEY_ECDSA); + do_kex_with_key(st, ts, kex, KEY_ED25519); +} + +static void +kex_tests(struct shared_state *st, struct test_state *ts) +{ + do_kex(st, ts, "sntrup761x25519-sha512@openssh.com"); + do_kex(st, ts, "curve25519-sha256@libssh.org"); + do_kex(st, ts, "ecdh-sha2-nistp256"); + do_kex(st, ts, "diffie-hellman-group1-sha1"); + do_kex(st, ts, "diffie-hellman-group-exchange-sha1"); +} + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + static struct shared_state *st; + struct test_state *ts; + u_char crbuf[SSH_MAX_PRE_BANNER_LINES * 4]; + u_char zbuf[4096] = {0}; + static LogLevel loglevel = SYSLOG_LEVEL_INFO; + + if (st == NULL) { + if (getenv("DEBUG") != NULL || getenv("KEX_FUZZ_DEBUG") != NULL) + loglevel = SYSLOG_LEVEL_DEBUG3; + log_init("kex_fuzz", + loglevel, SYSLOG_FACILITY_AUTH, 1); + st = (struct shared_state *)xcalloc(1, sizeof(*st)); + prepare_keys(st); + } + + /* Ensure that we can complete (fail) banner exchange at least */ + memset(crbuf, '\n', sizeof(crbuf)); + + ts = (struct test_state *)xcalloc(1, sizeof(*ts)); + if ((ts->s_template = sshbuf_new()) == NULL || + sshbuf_put(ts->s_template, data, size) != 0 || + sshbuf_put(ts->s_template, crbuf, sizeof(crbuf)) != 0 || + sshbuf_put(ts->s_template, zbuf, sizeof(zbuf)) != 0) + abort(); + kex_tests(st, ts); + sshbuf_free(ts->s_template); + free(ts); + + ts = (struct test_state *)xcalloc(1, sizeof(*ts)); + if ((ts->c_template = sshbuf_new()) == NULL || + sshbuf_put(ts->c_template, data, size) != 0 || + sshbuf_put(ts->c_template, crbuf, sizeof(crbuf)) != 0 || + sshbuf_put(ts->c_template, zbuf, sizeof(zbuf)) != 0) + abort(); + kex_tests(st, ts); + sshbuf_free(ts->c_template); + free(ts); + + return 0; +} +#endif /* STANDALONE */ +} /* extern "C" */ diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/privkey_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/privkey_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..ff0b0f7769dc80b4c3109dc839996128d9fa63ae --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/privkey_fuzz.cc @@ -0,0 +1,21 @@ +#include +#include +#include + +extern "C" { + +#include "sshkey.h" +#include "sshbuf.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + struct sshkey *k = NULL; + struct sshbuf *b = sshbuf_from(data, size); + int r = sshkey_private_deserialize(b, &k); + if (r == 0) sshkey_free(k); + sshbuf_free(b); + return 0; +} + +} // extern + diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/pubkey_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/pubkey_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..8bbc110931b102fb2de3382081306c66fb407cac --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/pubkey_fuzz.cc @@ -0,0 +1,18 @@ +#include +#include +#include + +extern "C" { + +#include "sshkey.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + struct sshkey *k = NULL; + int r = sshkey_from_blob(data, size, &k); + if (r == 0) sshkey_free(k); + return 0; +} + +} // extern + diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/sig_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/sig_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..b32502ba023f2bdd70ca3e2ec08e7017e421ca8d --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/sig_fuzz.cc @@ -0,0 +1,62 @@ +// cc_fuzz_target test for public key parsing. + +#include +#include +#include +#include +#include + +extern "C" { + +#include "includes.h" +#include "sshkey.h" +#include "ssherr.h" + +static struct sshkey *generate_or_die(int type, unsigned bits) { + int r; + struct sshkey *ret; + if ((r = sshkey_generate(type, bits, &ret)) != 0) { + fprintf(stderr, "generate(%d, %u): %s", type, bits, ssh_err(r)); + abort(); + } + return ret; +} + +int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen) +{ +#ifdef WITH_OPENSSL + static struct sshkey *rsa = generate_or_die(KEY_RSA, 2048); + static struct sshkey *dsa = generate_or_die(KEY_DSA, 1024); + static struct sshkey *ecdsa256 = generate_or_die(KEY_ECDSA, 256); + static struct sshkey *ecdsa384 = generate_or_die(KEY_ECDSA, 384); + static struct sshkey *ecdsa521 = generate_or_die(KEY_ECDSA, 521); +#endif + struct sshkey_sig_details *details = NULL; + static struct sshkey *ed25519 = generate_or_die(KEY_ED25519, 0); + static const char *data = "If everyone started announcing his nose had " + "run away, I don’t know how it would all end"; + static const size_t dlen = strlen(data); + +#ifdef WITH_OPENSSL + sshkey_verify(rsa, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); + sshkey_sig_details_free(details); + details = NULL; + sshkey_verify(dsa, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); + sshkey_sig_details_free(details); + details = NULL; + sshkey_verify(ecdsa256, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); + sshkey_sig_details_free(details); + details = NULL; + sshkey_verify(ecdsa384, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); + sshkey_sig_details_free(details); + details = NULL; + sshkey_verify(ecdsa521, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); + sshkey_sig_details_free(details); + details = NULL; +#endif + sshkey_verify(ed25519, sig, slen, (const u_char *)data, dlen, NULL, 0, &details); + sshkey_sig_details_free(details); + return 0; +} + +} // extern diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/ssh-sk-null.cc b/aosp/external/openssh/regress/misc/fuzz-harness/ssh-sk-null.cc new file mode 100644 index 0000000000000000000000000000000000000000..948c3d933c5a5429474742ffb83659cd8575c030 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/ssh-sk-null.cc @@ -0,0 +1,52 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +extern "C" { + +#include "includes.h" + +#include + +#include "ssherr.h" +#include "ssh-sk.h" + +int +sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest) +{ + return SSH_ERR_FEATURE_UNSUPPORTED; +} + +int +sshsk_sign(const char *provider_path, struct sshkey *key, + u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, + u_int compat, const char *pin) +{ + return SSH_ERR_FEATURE_UNSUPPORTED; +} + +int +sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, u_int flags, struct sshsk_resident_key ***srksp, + size_t *nsrksp) +{ + return SSH_ERR_FEATURE_UNSUPPORTED; +} + +}; diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/sshsig_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/sshsig_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..02211a096b24fd302cbb0468103d246e3b64fded --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/sshsig_fuzz.cc @@ -0,0 +1,37 @@ +// cc_fuzz_target test for sshsig verification. + +#include +#include +#include +#include +#include + +extern "C" { + +#include "includes.h" +#include "sshkey.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "sshsig.h" +#include "log.h" + +int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen) +{ + static const char *data = "If everyone started announcing his nose had " + "run away, I don’t know how it would all end"; + struct sshbuf *signature = sshbuf_from(sig, slen); + struct sshbuf *message = sshbuf_from(data, strlen(data)); + struct sshkey *k = NULL; + struct sshkey_sig_details *details = NULL; + extern char *__progname; + + log_init(__progname, SYSLOG_LEVEL_QUIET, SYSLOG_FACILITY_USER, 1); + sshsig_verifyb(signature, message, "castle", &k, &details); + sshkey_sig_details_free(details); + sshkey_free(k); + sshbuf_free(signature); + sshbuf_free(message); + return 0; +} + +} // extern diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/sshsigopt_fuzz.cc b/aosp/external/openssh/regress/misc/fuzz-harness/sshsigopt_fuzz.cc new file mode 100644 index 0000000000000000000000000000000000000000..7424fcbe358e08588217c4c1bebdae78615f9865 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/sshsigopt_fuzz.cc @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +extern "C" { + +#include "sshsig.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + char *cp = (char *)malloc(size + 1); + struct sshsigopt *opts = NULL; + + if (cp == NULL) + goto out; + memcpy(cp, data, size); + cp[size] = '\0'; + if ((opts = sshsigopt_parse(cp, "libfuzzer", 0, NULL)) == NULL) + goto out; + + out: + free(cp); + sshsigopt_free(opts); + return 0; +} + +} // extern "C" diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/README b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/README new file mode 100644 index 0000000000000000000000000000000000000000..752053001ac4682ebc7937f6d71249d31edf7ff8 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/README @@ -0,0 +1,4 @@ +This is preparatory data for fuzzing testing including scripts and test keys, +corresponding to ../fixed-keys that are used in the fuzz tests and consequent +fuzzing seed corpora. They should not be changed unless the affected seed +corpora are also regenerated. diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/create-agent-corpus.sh b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/create-agent-corpus.sh new file mode 100644 index 0000000000000000000000000000000000000000..1043b9ff47d73cdd0e491727ff0a221d016300e7 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/create-agent-corpus.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# Exercise ssh-agent to generate fuzzing corpus + +# XXX assumes agent hacked up with sk-dummy.o and ssh-sk.o linked directly +# and dumping of e->request for each message. + +set -xe +SSH_AUTH_SOCK=$PWD/sock +rm -f agent-[0-9]* $SSH_AUTH_SOCK +export SSH_AUTH_SOCK +../../../../ssh-agent -D -a $SSH_AUTH_SOCK & +sleep 1 +AGENT_PID=$! +trap "kill $AGENT_PID" EXIT + +PRIV="id_dsa id_ecdsa id_ecdsa_sk id_ed25519 id_ed25519_sk id_rsa" + +# add keys +ssh-add $PRIV + +# sign +ssh-add -T *.pub + +# list +ssh-add -l + +# remove individually +ssh-add -d $PRIV + +# re-add with constraints +ssh-add -c -t 3h $PRIV + +# delete all +ssh-add -D + +# attempt to add a PKCS#11 token +ssh-add -s /fake || : + +# attempt to delete PKCS#11 +ssh-add -e /fake || : + +ssh-add -L + diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa new file mode 100644 index 0000000000000000000000000000000000000000..88bf5566c93b6db54a9dcbbbf6d9c3547da9d0c4 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa @@ -0,0 +1,21 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH +NzAAAAgQCsGTfjpQ465EOkfQXJM9BOvfRQE0fqlykAls+ncz+T7hrbeScRu8xpwzsznJNm +xlW8o6cUDiHmBJ5OHgamUC9N7YJeU/6fnOAZifgN8mqK6k8pKHuje8ANOiYgHLl0yiASQA +3//qMyzZ+W/hemoLSmLAbEqlfWVeyYx+wta1Vm+QAAABUAvWyehvUvdHvQxavYgS5p0t5Q +d7UAAACBAIRA9Yy+f4Kzqpv/qICPO3zk42UuP7WAhSW2nCbQdLlCiSTxcjKgcvXNRckwJP +44JjSHOtJy/AMtJrPIbLYG6KuWTdBlEHFiG6DafvLG+qPMSL2bPjXTOhuOMbCHIZ+5WBkW +THeG/Nv11iI01Of9V6tXkig23K370flkRkXFi9MdAAAAgCt6YUcQkNwG7B/e5M1FZsLP9O +kVB3BwLAOjmWdHpyhu3HpwSJa3XLEvhXN0i6IVI2KgPo/2GtYA6rHt14L+6u1pmhh8sAvQ +ksp3qZB+xh/NP+hBqf0sbHX0yYbzKOvI5SCc/kKK6yagcBZOsubM/KC8TxyVgmD5c6WzYs +h5TEpvAAAB2PHjRbbx40W2AAAAB3NzaC1kc3MAAACBAKwZN+OlDjrkQ6R9Bckz0E699FAT +R+qXKQCWz6dzP5PuGtt5JxG7zGnDOzOck2bGVbyjpxQOIeYEnk4eBqZQL03tgl5T/p+c4B +mJ+A3yaorqTykoe6N7wA06JiAcuXTKIBJADf/+ozLNn5b+F6agtKYsBsSqV9ZV7JjH7C1r +VWb5AAAAFQC9bJ6G9S90e9DFq9iBLmnS3lB3tQAAAIEAhED1jL5/grOqm/+ogI87fOTjZS +4/tYCFJbacJtB0uUKJJPFyMqBy9c1FyTAk/jgmNIc60nL8Ay0ms8hstgboq5ZN0GUQcWIb +oNp+8sb6o8xIvZs+NdM6G44xsIchn7lYGRZMd4b82/XWIjTU5/1Xq1eSKDbcrfvR+WRGRc +WL0x0AAACAK3phRxCQ3AbsH97kzUVmws/06RUHcHAsA6OZZ0enKG7cenBIlrdcsS+Fc3SL +ohUjYqA+j/Ya1gDqse3Xgv7q7WmaGHywC9CSynepkH7GH80/6EGp/SxsdfTJhvMo68jlIJ +z+QorrJqBwFk6y5sz8oLxPHJWCYPlzpbNiyHlMSm8AAAAUUA+OGldMi76ClO/sstpdbBUE +lq8AAAAAAQI= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa-cert.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..3afb87fe62f72424fb118a7038a5fdb5baf8f75f --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa-cert.pub @@ -0,0 +1 @@ +ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAguF716Yub+vVKNlONKLsfxGYWkRe/PyjfYdGRTsFaDvAAAACBAKwZN+OlDjrkQ6R9Bckz0E699FATR+qXKQCWz6dzP5PuGtt5JxG7zGnDOzOck2bGVbyjpxQOIeYEnk4eBqZQL03tgl5T/p+c4BmJ+A3yaorqTykoe6N7wA06JiAcuXTKIBJADf/+ozLNn5b+F6agtKYsBsSqV9ZV7JjH7C1rVWb5AAAAFQC9bJ6G9S90e9DFq9iBLmnS3lB3tQAAAIEAhED1jL5/grOqm/+ogI87fOTjZS4/tYCFJbacJtB0uUKJJPFyMqBy9c1FyTAk/jgmNIc60nL8Ay0ms8hstgboq5ZN0GUQcWIboNp+8sb6o8xIvZs+NdM6G44xsIchn7lYGRZMd4b82/XWIjTU5/1Xq1eSKDbcrfvR+WRGRcWL0x0AAACAK3phRxCQ3AbsH97kzUVmws/06RUHcHAsA6OZZ0enKG7cenBIlrdcsS+Fc3SLohUjYqA+j/Ya1gDqse3Xgv7q7WmaGHywC9CSynepkH7GH80/6EGp/SxsdfTJhvMo68jlIJz+QorrJqBwFk6y5sz8oLxPHJWCYPlzpbNiyHlMSm8AAAAAAAAD6AAAAAEAAAAHdWx5c3NlcwAAABcAAAAHdWx5c3NlcwAAAAhvZHlzc2V1cwAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAFMAAAALc3NoLWVkMjU1MTkAAABAjMQEZcbdUYJBjIC4GxByFDOb8tv71vDZdx7irHwaqIjx5rzpJUuOV1r8ZO4kY+Yaiun1yrWj2QYkfJrHBvD1DA== id_dsa.pub diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa.pub new file mode 100644 index 0000000000000000000000000000000000000000..6f91c4e0733610aa31556d9d8327c248d5de79a0 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAKwZN+OlDjrkQ6R9Bckz0E699FATR+qXKQCWz6dzP5PuGtt5JxG7zGnDOzOck2bGVbyjpxQOIeYEnk4eBqZQL03tgl5T/p+c4BmJ+A3yaorqTykoe6N7wA06JiAcuXTKIBJADf/+ozLNn5b+F6agtKYsBsSqV9ZV7JjH7C1rVWb5AAAAFQC9bJ6G9S90e9DFq9iBLmnS3lB3tQAAAIEAhED1jL5/grOqm/+ogI87fOTjZS4/tYCFJbacJtB0uUKJJPFyMqBy9c1FyTAk/jgmNIc60nL8Ay0ms8hstgboq5ZN0GUQcWIboNp+8sb6o8xIvZs+NdM6G44xsIchn7lYGRZMd4b82/XWIjTU5/1Xq1eSKDbcrfvR+WRGRcWL0x0AAACAK3phRxCQ3AbsH97kzUVmws/06RUHcHAsA6OZZ0enKG7cenBIlrdcsS+Fc3SLohUjYqA+j/Ya1gDqse3Xgv7q7WmaGHywC9CSynepkH7GH80/6EGp/SxsdfTJhvMo68jlIJz+QorrJqBwFk6y5sz8oLxPHJWCYPlzpbNiyHlMSm8= diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa new file mode 100644 index 0000000000000000000000000000000000000000..c1a96c6f9eafa6033f9939c98a4534339efdd89a --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTDJ0VlMv+0rguNzaJ1DF2KueHaxRSQ +6LpIxGbulrg1a8RPbnMXwag5GcDiDllD2lDUJUuBEWyjXA0rZoZX35ELAAAAoE/Bbr5PwW +6+AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMMnRWUy/7SuC43N +onUMXYq54drFFJDoukjEZu6WuDVrxE9ucxfBqDkZwOIOWUPaUNQlS4ERbKNcDStmhlffkQ +sAAAAhAIhE6hCID5oOm1TDktc++KFKyScjLifcZ6Cgv5xSSyLOAAAAAAECAwQFBgc= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa-cert.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..9de599917de653f2b5056d460d91492199e2b759 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa-cert.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgVJZuM/1AOe6n++qRWMyUuAThYqLvvQxj5CGflLODp60AAAAIbmlzdHAyNTYAAABBBMMnRWUy/7SuC43NonUMXYq54drFFJDoukjEZu6WuDVrxE9ucxfBqDkZwOIOWUPaUNQlS4ERbKNcDStmhlffkQsAAAAAAAAD6QAAAAEAAAAHdWx5c3NlcwAAABcAAAAHdWx5c3NlcwAAAAhvZHlzc2V1cwAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAFMAAAALc3NoLWVkMjU1MTkAAABAtdJpcF6ZmQL+ueices4QZeL7AK8Xuo08jyLgiolhjKy2jj4LSUki4aX/ZeZeJuby1ovGrfaeFAgx3itPLR7IAQ== id_ecdsa.pub diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa.pub new file mode 100644 index 0000000000000000000000000000000000000000..30a7cc23b2286ce68869c68494c18a51500bb3b8 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMMnRWUy/7SuC43NonUMXYq54drFFJDoukjEZu6WuDVrxE9ucxfBqDkZwOIOWUPaUNQlS4ERbKNcDStmhlffkQs= diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk new file mode 100644 index 0000000000000000000000000000000000000000..5a364ed39f1ee8376d0e7eae9f78c0ea94571d6f --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk @@ -0,0 +1,14 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2 +RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQTYyU76zop1 +VOb4DfKWYnR5b0TOC3zw8DzObAfHWB5o6xls+tOYiEleXvIEi00Da2iCK47habZTOhLyeB +X2Avu5AAAABHNzaDoAAAGYqUAQSKlAEEgAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv +cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEE2MlO+s6KdVTm+A3ylmJ0eW9Ezgt88PA8zm +wHx1geaOsZbPrTmIhJXl7yBItNA2togiuO4Wm2UzoS8ngV9gL7uQAAAARzc2g6AQAAAOMt +LS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUhjQ0FRRUVJSHFsZjNsWTkxZFhwUn +dYZDBrS0lYWmNpeDRRcDBNSU15Ny9JMUxXSTFuWG9Bb0dDQ3FHU000OQpBd0VIb1VRRFFn +QUUyTWxPK3M2S2RWVG0rQTN5bG1KMGVXOUV6Z3Q4OFBBOHptd0h4MWdlYU9zWmJQclRtSW +hKClhsN3lCSXROQTJ0b2dpdU80V20yVXpvUzhuZ1Y5Z0w3dVE9PQotLS0tLUVORCBFQyBQ +UklWQVRFIEtFWS0tLS0tCgAAAAAAAAAbZGptQGRqbS5zeWQuY29ycC5nb29nbGUuY29tAQ +IDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk-cert.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..14040fad7aa620f7db8826b2ed4a2ece4bc0fdf8 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk-cert.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAK3NrLWVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgKLHtIca++5VoDrUAXU/KqGJZ7jZEnuJSTvt7VrYY9foAAAAIbmlzdHAyNTYAAABBBNjJTvrOinVU5vgN8pZidHlvRM4LfPDwPM5sB8dYHmjrGWz605iISV5e8gSLTQNraIIrjuFptlM6EvJ4FfYC+7kAAAAEc3NoOgAAAAAAAAPqAAAAAQAAAAd1bHlzc2VzAAAAFwAAAAd1bHlzc2VzAAAACG9keXNzZXVzAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIDPQXmEVMVLmeFRyafKMVWgPDkv8/uRBTwmcEDatZzMDAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEB1naZOQDLaDr+fwn6E9x8/8HeiaUubDzPexfNQMz+m/7RD0gd5uJhHYUfDb5+/sIx1I7bUEeRIDkBbmZ2foo0E djm@djm.syd.corp.google.com diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk.pub new file mode 100644 index 0000000000000000000000000000000000000000..1b5e829b74181c80e094cf020258cb489f162199 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ecdsa_sk.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBNjJTvrOinVU5vgN8pZidHlvRM4LfPDwPM5sB8dYHmjrGWz605iISV5e8gSLTQNraIIrjuFptlM6EvJ4FfYC+7kAAAAEc3NoOg== djm@djm.syd.corp.google.com diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519 b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519 new file mode 100644 index 0000000000000000000000000000000000000000..6a7fbac929d1df5f1433a77814948984749fbe76 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAIhWlP99VpT/ +fQAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAw +AAAEDE1rlcMC0s0X3TKVZAOVavZOywwkXw8tO5dLObxaCMEDPQXmEVMVLmeFRyafKMVWgP +Dkv8/uRBTwmcEDatZzMDAAAAAAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519-cert.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..6a95fed2ac8069ecd18cba092d07d42881b71f22 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519-cert.pub @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIMDQjYH6XRzH3j3MW1DdjCoAfvrHfgjnVGF+sLK0pBfqAAAAIDPQXmEVMVLmeFRyafKMVWgPDkv8/uRBTwmcEDatZzMDAAAAAAAAA+sAAAABAAAAB3VseXNzZXMAAAAXAAAAB3VseXNzZXMAAAAIb2R5c3NldXMAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgM9BeYRUxUuZ4VHJp8oxVaA8OS/z+5EFPCZwQNq1nMwMAAABTAAAAC3NzaC1lZDI1NTE5AAAAQBj0og+s09/HpwdHZbzN0twooKPDWWrxGfnP1Joy6cDnY2BCSQ7zg9vbq11kLF8H/sKOTZWAQrUZ7LlChOu9Ogw= id_ed25519.pub diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519.pub new file mode 100644 index 0000000000000000000000000000000000000000..87b61744701f8cca6ccaa079ba56684f6d965763 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519.pub @@ -0,0 +1,2 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDPQXmEVMVLmeFRyafKMVWgPDkv8/uRBTwmcEDatZzMD + diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk new file mode 100644 index 0000000000000000000000000000000000000000..9dcda6c4626f42373c7b5cfcf88f0d8cab0e9745 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2 +gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACCTJtH10vWhIDxd62edvMLg9u2cwYKyqa7332je +RArHjAAAAARzc2g6AAAAwN7vvE3e77xNAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2 +9tAAAAIJMm0fXS9aEgPF3rZ528wuD27ZzBgrKprvffaN5ECseMAAAABHNzaDoBAAAAQEsS +xLFiVzfpH2mt9xh8i/zmHV646Hud4QruNBAGNl8gkybR9dL1oSA8XetnnbzC4PbtnMGCsq +mu999o3kQKx4wAAAAAAAAAG2RqbUBkam0uc3lkLmNvcnAuZ29vZ2xlLmNvbQECAwQFBg== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk-cert.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..9e41eec00df46d15f689f02a68656fca5473af54 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk-cert.pub @@ -0,0 +1 @@ +sk-ssh-ed25519-cert-v01@openssh.com AAAAI3NrLXNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJiT+C/VLMWholFZ4xhOyJr0nSLZSFRIM3I07wUNTRPaAAAAIJMm0fXS9aEgPF3rZ528wuD27ZzBgrKprvffaN5ECseMAAAABHNzaDoAAAAAAAAD7AAAAAEAAAAHdWx5c3NlcwAAABcAAAAHdWx5c3NlcwAAAAhvZHlzc2V1cwAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAz0F5hFTFS5nhUcmnyjFVoDw5L/P7kQU8JnBA2rWczAwAAAFMAAAALc3NoLWVkMjU1MTkAAABAX0Pu13B94pVR3qq8MJQGkOS1Cd7AAM1k6O2VSwyDPM/LfsWIQ4ywgxDmk3hjXWOY7BqljuMxo5VO4JymEIhQBA== djm@djm.syd.corp.google.com diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk.pub new file mode 100644 index 0000000000000000000000000000000000000000..38d198444b79ae31b22614af15e88927fe17375e --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_ed25519_sk.pub @@ -0,0 +1 @@ +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJMm0fXS9aEgPF3rZ528wuD27ZzBgrKprvffaN5ECseMAAAABHNzaDo= djm@djm.syd.corp.google.com diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa new file mode 100644 index 0000000000000000000000000000000000000000..574fecf47008c63b6561e6964e63e94e4b1a8a44 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEA3+epf+VGKoGPaAZXrf6S0cyumQnddkGBnVFX0A5eh37RtLug0qY5 +thxsBUbGGVr9mTd2QXwLujBwYg5l1MP/Fmg+5312Zgx9pHmS+qKULbar0hlNgptNEb+aNU +d3o9qg3aXqXm7+ZnjAV05ef/mxNRN2ZvuEkw7cRppTJcbBI+vF3lXuCXnX2klDI95Gl2AW +3WHRtanqLHZXuBkjjRBDKc7MUq/GP1hmLiAd95dvU7fZjRlIEsP84zGEI1Fb0L/kmPHcOt +iVfHft8CtmC9v6+94JrOiPBBNScV+dyrgAGPsdKdr/1vIpQmCNiI8s3PCiD8J7ZiBaYm0I +8fq5G/qnUwAAA7ggw2dXIMNnVwAAAAdzc2gtcnNhAAABAQDf56l/5UYqgY9oBlet/pLRzK +6ZCd12QYGdUVfQDl6HftG0u6DSpjm2HGwFRsYZWv2ZN3ZBfAu6MHBiDmXUw/8WaD7nfXZm +DH2keZL6opQttqvSGU2Cm00Rv5o1R3ej2qDdpepebv5meMBXTl5/+bE1E3Zm+4STDtxGml +MlxsEj68XeVe4JedfaSUMj3kaXYBbdYdG1qeosdle4GSONEEMpzsxSr8Y/WGYuIB33l29T +t9mNGUgSw/zjMYQjUVvQv+SY8dw62JV8d+3wK2YL2/r73gms6I8EE1JxX53KuAAY+x0p2v +/W8ilCYI2Ijyzc8KIPwntmIFpibQjx+rkb+qdTAAAAAwEAAQAAAQEArWm5B4tFasppjUHM +SsAuajtCxtizI1Hc10EW59cZM4vvUzE2f6+qZvdgWj3UU/L7Et23w0QVuSCnCerox379ZB +ddEOFFAAiQjwBx65hbd4RRUymxtIQfjq18++LcMJW1nbVQ7c69ThQbtALIggmbS+ZE/8Gx +jkwmIrCH0Ww8TlpsPe+mNHuyNk7UEZoXLm22lNLqq5qkIL5JgT6M2iNJpMOJy9/CKi6kO4 +JPuVwjdG4C5pBPaMN3KJ1IvAlSlLGNaXnfXcn85gWfsCjsZmH3liey2NJamqp/w83BrKUg +YZvMR2qeWZaKkFTahpzN5KRK1BFeB37O0P84Dzh1biDX8QAAAIEAiWXW8ePYFwLpa2mFIh +VvRTdcrN70rVK5eWVaL3pyS4vGA56Jixq86dHveOnbSY+iNb1jQidtXc8SWUt2wtHqZ32h +Lji9/hMSKqe9SEP3xvDRDmUJqsVw0ySyrFrzm4160QY6RKU3CIQCVFslMZ9fxmrfZ/hxoU +0X3FVsxmC4+kwAAACBAPOc1YERpV6PjANBrGR+1o1RCdACbm5myc42QzSNIaOZmgrYs+Gt +7+EcoqSdbJzHJNCNQfF+A+vjbIkFiuZqq/5wwr59qXx5OAlijLB/ywwKmTWq6lp//Zxny+ +ka3sIGNO14eQvmxNDnlLL+RIZleCTEKBXSW6CZhr+uHMZFKKMtAAAAgQDrSkm+LbILB7H9 +jxEBZLhv53aAn4u81kFKQOJ7PzzpBGSoD12i7oIJu5siSD5EKDNVEr+SvCf0ISU3BuMpzl +t3YrPrHRheOFhn5e3j0e//zB8rBC0DGB4CtTDdeh7rOXUL4K0pz+8wEpNkV62SWxhC6NRW +I79JhtGkh+GtcnkEfwAAAAAB +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa-cert.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..01761a38fa0f57568ab2b341fc59eb25c39efd8a --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa-cert.pub @@ -0,0 +1 @@ +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg89JX6OBMYDSxER8fnU5y8xxeMCHR/hI0uVqdEhNyCpcAAAADAQABAAABAQDf56l/5UYqgY9oBlet/pLRzK6ZCd12QYGdUVfQDl6HftG0u6DSpjm2HGwFRsYZWv2ZN3ZBfAu6MHBiDmXUw/8WaD7nfXZmDH2keZL6opQttqvSGU2Cm00Rv5o1R3ej2qDdpepebv5meMBXTl5/+bE1E3Zm+4STDtxGmlMlxsEj68XeVe4JedfaSUMj3kaXYBbdYdG1qeosdle4GSONEEMpzsxSr8Y/WGYuIB33l29Tt9mNGUgSw/zjMYQjUVvQv+SY8dw62JV8d+3wK2YL2/r73gms6I8EE1JxX53KuAAY+x0p2v/W8ilCYI2Ijyzc8KIPwntmIFpibQjx+rkb+qdTAAAAAAAAA+0AAAABAAAAB3VseXNzZXMAAAAXAAAAB3VseXNzZXMAAAAIb2R5c3NldXMAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgM9BeYRUxUuZ4VHJp8oxVaA8OS/z+5EFPCZwQNq1nMwMAAABTAAAAC3NzaC1lZDI1NTE5AAAAQGCDA6PWw4x9bHQl0w7NqifHepumqD3dmyMx+hZGuPRon+TsyCjfytu7hWmV7l9XUF0fPQNFQ7FGat5e+7YUNgE= id_rsa.pub diff --git a/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa.pub b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa.pub new file mode 100644 index 0000000000000000000000000000000000000000..05015e12bfaa108c26d8bb5a94b83a80f7379c92 --- /dev/null +++ b/aosp/external/openssh/regress/misc/fuzz-harness/testdata/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf56l/5UYqgY9oBlet/pLRzK6ZCd12QYGdUVfQDl6HftG0u6DSpjm2HGwFRsYZWv2ZN3ZBfAu6MHBiDmXUw/8WaD7nfXZmDH2keZL6opQttqvSGU2Cm00Rv5o1R3ej2qDdpepebv5meMBXTl5/+bE1E3Zm+4STDtxGmlMlxsEj68XeVe4JedfaSUMj3kaXYBbdYdG1qeosdle4GSONEEMpzsxSr8Y/WGYuIB33l29Tt9mNGUgSw/zjMYQjUVvQv+SY8dw62JV8d+3wK2YL2/r73gms6I8EE1JxX53KuAAY+x0p2v/W8ilCYI2Ijyzc8KIPwntmIFpibQjx+rkb+qdT diff --git a/aosp/external/openssh/regress/misc/sk-dummy/Makefile b/aosp/external/openssh/regress/misc/sk-dummy/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..29e313c8296509d307c3787e0d3eee9171f7df9e --- /dev/null +++ b/aosp/external/openssh/regress/misc/sk-dummy/Makefile @@ -0,0 +1,66 @@ +# $OpenBSD: Makefile,v 1.2 2019/11/29 00:13:29 djm Exp $ + +.include +.include + +PROG= sk-dummy.so +NOMAN= + +SSHREL=../../../../../usr.bin/ssh +.PATH: ${.CURDIR}/${SSHREL} + +SRCS=sk-dummy.c +# From usr.bin/ssh +SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c +OPENSSL?= yes + +CFLAGS+= -fPIC + +.if (${OPENSSL:L} == "yes") +CFLAGS+= -DWITH_OPENSSL +.endif + +# enable warnings +WARNINGS=Yes + +DEBUG=-g +CFLAGS+= -fstack-protector-all +CDIAGFLAGS= -Wall +CDIAGFLAGS+= -Wextra +CDIAGFLAGS+= -Werror +CDIAGFLAGS+= -Wchar-subscripts +CDIAGFLAGS+= -Wcomment +CDIAGFLAGS+= -Wformat +CDIAGFLAGS+= -Wformat-security +CDIAGFLAGS+= -Wimplicit +CDIAGFLAGS+= -Winline +CDIAGFLAGS+= -Wmissing-declarations +CDIAGFLAGS+= -Wmissing-prototypes +CDIAGFLAGS+= -Wparentheses +CDIAGFLAGS+= -Wpointer-arith +CDIAGFLAGS+= -Wreturn-type +CDIAGFLAGS+= -Wshadow +CDIAGFLAGS+= -Wsign-compare +CDIAGFLAGS+= -Wstrict-aliasing +CDIAGFLAGS+= -Wstrict-prototypes +CDIAGFLAGS+= -Wswitch +CDIAGFLAGS+= -Wtrigraphs +CDIAGFLAGS+= -Wuninitialized +CDIAGFLAGS+= -Wunused +CDIAGFLAGS+= -Wno-unused-parameter +.if ${COMPILER_VERSION:L} != "gcc3" +CDIAGFLAGS+= -Wold-style-definition +.endif + +CFLAGS+=-I${.CURDIR}/${SSHREL} + +.if (${OPENSSL:L} == "yes") +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} +.endif + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDADD) + +.include + diff --git a/aosp/external/openssh/regress/misc/sk-dummy/fatal.c b/aosp/external/openssh/regress/misc/sk-dummy/fatal.c new file mode 100644 index 0000000000000000000000000000000000000000..c6e4b5d6fa710c19af1f0d3481992a8baca219b6 --- /dev/null +++ b/aosp/external/openssh/regress/misc/sk-dummy/fatal.c @@ -0,0 +1,27 @@ +/* public domain */ + +#include "includes.h" + +#include +#include +#include +#include + +#include "log.h" + +void +sshfatal(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, ...) +{ + va_list ap; + + if (showfunc) + fprintf(stderr, "%s: ", func); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (suffix != NULL) + fprintf(stderr, ": %s", suffix); + fputc('\n', stderr); + _exit(1); +} diff --git a/aosp/external/openssh/regress/misc/sk-dummy/sk-dummy.c b/aosp/external/openssh/regress/misc/sk-dummy/sk-dummy.c new file mode 100644 index 0000000000000000000000000000000000000000..a10c0be281b1cf656a982f1f51ae76b7039f71b1 --- /dev/null +++ b/aosp/external/openssh/regress/misc/sk-dummy/sk-dummy.c @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2019 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_SHA2_H +#include +#endif + +#include "crypto_api.h" +#include "sk-api.h" + +#if defined(WITH_OPENSSL) && !defined(OPENSSL_HAS_ECC) +# undef WITH_OPENSSL +#endif + +#ifdef WITH_OPENSSL +/* We don't use sha2 from OpenSSL and they can conflict with system sha2.h */ +#define OPENSSL_NO_SHA +#define USE_LIBC_SHA2 /* NetBSD 9 */ +#include +#include +#include +#include +#include +#include +#include + +/* Compatibility with OpenSSH 1.0.x */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +#define ECDSA_SIG_get0(sig, pr, ps) \ + do { \ + (*pr) = sig->r; \ + (*ps) = sig->s; \ + } while (0) +#endif +#endif /* WITH_OPENSSL */ + +/* #define SK_DEBUG 1 */ + +#if SSH_SK_VERSION_MAJOR != 0x00090000 +# error SK API has changed, sk-dummy.c needs an update +#endif + +#ifdef SK_DUMMY_INTEGRATE +# define sk_api_version ssh_sk_api_version +# define sk_enroll ssh_sk_enroll +# define sk_sign ssh_sk_sign +# define sk_load_resident_keys ssh_sk_load_resident_keys +#endif /* !SK_STANDALONE */ + +static void skdebug(const char *func, const char *fmt, ...) + __attribute__((__format__ (printf, 2, 3))); + +static void +skdebug(const char *func, const char *fmt, ...) +{ +#if defined(SK_DEBUG) + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "sk-dummy %s: ", func); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); +#else + (void)func; /* XXX */ + (void)fmt; /* XXX */ +#endif +} + +uint32_t +sk_api_version(void) +{ + return SSH_SK_VERSION_MAJOR; +} + +static int +pack_key_ecdsa(struct sk_enroll_response *response) +{ +#ifdef OPENSSL_HAS_ECC + EC_KEY *key = NULL; + const EC_GROUP *g; + const EC_POINT *q; + int ret = -1; + long privlen; + BIO *bio = NULL; + char *privptr; + + response->public_key = NULL; + response->public_key_len = 0; + response->key_handle = NULL; + response->key_handle_len = 0; + + if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) { + skdebug(__func__, "EC_KEY_new_by_curve_name"); + goto out; + } + if (EC_KEY_generate_key(key) != 1) { + skdebug(__func__, "EC_KEY_generate_key"); + goto out; + } + EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); + if ((bio = BIO_new(BIO_s_mem())) == NULL || + (g = EC_KEY_get0_group(key)) == NULL || + (q = EC_KEY_get0_public_key(key)) == NULL) { + skdebug(__func__, "couldn't get key parameters"); + goto out; + } + response->public_key_len = EC_POINT_point2oct(g, q, + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + if (response->public_key_len == 0 || response->public_key_len > 2048) { + skdebug(__func__, "bad pubkey length %zu", + response->public_key_len); + goto out; + } + if ((response->public_key = malloc(response->public_key_len)) == NULL) { + skdebug(__func__, "malloc pubkey failed"); + goto out; + } + if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, + response->public_key, response->public_key_len, NULL) == 0) { + skdebug(__func__, "EC_POINT_point2oct failed"); + goto out; + } + /* Key handle contains PEM encoded private key */ + if (!PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) { + skdebug(__func__, "PEM_write_bio_ECPrivateKey failed"); + goto out; + } + if ((privlen = BIO_get_mem_data(bio, &privptr)) <= 0) { + skdebug(__func__, "BIO_get_mem_data failed"); + goto out; + } + if ((response->key_handle = malloc(privlen)) == NULL) { + skdebug(__func__, "malloc key_handle failed"); + goto out; + } + response->key_handle_len = (size_t)privlen; + memcpy(response->key_handle, privptr, response->key_handle_len); + /* success */ + ret = 0; + out: + if (ret != 0) { + if (response->public_key != NULL) { + memset(response->public_key, 0, + response->public_key_len); + free(response->public_key); + response->public_key = NULL; + } + if (response->key_handle != NULL) { + memset(response->key_handle, 0, + response->key_handle_len); + free(response->key_handle); + response->key_handle = NULL; + } + } + BIO_free(bio); + EC_KEY_free(key); + return ret; +#else + return -1; +#endif +} + +static int +pack_key_ed25519(struct sk_enroll_response *response) +{ + int ret = -1; + u_char pk[crypto_sign_ed25519_PUBLICKEYBYTES]; + u_char sk[crypto_sign_ed25519_SECRETKEYBYTES]; + + response->public_key = NULL; + response->public_key_len = 0; + response->key_handle = NULL; + response->key_handle_len = 0; + + memset(pk, 0, sizeof(pk)); + memset(sk, 0, sizeof(sk)); + crypto_sign_ed25519_keypair(pk, sk); + + response->public_key_len = sizeof(pk); + if ((response->public_key = malloc(response->public_key_len)) == NULL) { + skdebug(__func__, "malloc pubkey failed"); + goto out; + } + memcpy(response->public_key, pk, sizeof(pk)); + /* Key handle contains sk */ + response->key_handle_len = sizeof(sk); + if ((response->key_handle = malloc(response->key_handle_len)) == NULL) { + skdebug(__func__, "malloc key_handle failed"); + goto out; + } + memcpy(response->key_handle, sk, sizeof(sk)); + /* success */ + ret = 0; + out: + if (ret != 0) + free(response->public_key); + return ret; +} + +static int +check_options(struct sk_option **options) +{ + size_t i; + + if (options == NULL) + return 0; + for (i = 0; options[i] != NULL; i++) { + skdebug(__func__, "requested unsupported option %s", + options[i]->name); + if (options[i]->required) { + skdebug(__func__, "unknown required option"); + return -1; + } + } + return 0; +} + +int +sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, const char *pin, + struct sk_option **options, struct sk_enroll_response **enroll_response) +{ + struct sk_enroll_response *response = NULL; + int ret = SSH_SK_ERR_GENERAL; + + (void)flags; /* XXX; unused */ + + if (enroll_response == NULL) { + skdebug(__func__, "enroll_response == NULL"); + goto out; + } + *enroll_response = NULL; + if (check_options(options) != 0) + goto out; /* error already logged */ + if ((response = calloc(1, sizeof(*response))) == NULL) { + skdebug(__func__, "calloc response failed"); + goto out; + } + response->flags = flags; + switch(alg) { + case SSH_SK_ECDSA: + if (pack_key_ecdsa(response) != 0) + goto out; + break; + case SSH_SK_ED25519: + if (pack_key_ed25519(response) != 0) + goto out; + break; + default: + skdebug(__func__, "unsupported key type %d", alg); + return -1; + } + /* Have to return something here */ + if ((response->signature = calloc(1, 1)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + response->signature_len = 0; + + *enroll_response = response; + response = NULL; + ret = 0; + out: + if (response != NULL) { + free(response->public_key); + free(response->key_handle); + free(response->signature); + free(response->attestation_cert); + free(response); + } + return ret; +} + +static void +dump(const char *preamble, const void *sv, size_t l) +{ +#ifdef SK_DEBUG + const u_char *s = (const u_char *)sv; + size_t i; + + fprintf(stderr, "%s (len %zu):\n", preamble, l); + for (i = 0; i < l; i++) { + if (i % 16 == 0) + fprintf(stderr, "%04zu: ", i); + fprintf(stderr, "%02x", s[i]); + if (i % 16 == 15 || i == l - 1) + fprintf(stderr, "\n"); + } +#endif +} + +static int +sig_ecdsa(const uint8_t *message, size_t message_len, + const char *application, uint32_t counter, uint8_t flags, + const uint8_t *key_handle, size_t key_handle_len, + struct sk_sign_response *response) +{ +#ifdef OPENSSL_HAS_ECC + ECDSA_SIG *sig = NULL; + const BIGNUM *sig_r, *sig_s; + int ret = -1; + BIO *bio = NULL; + EVP_PKEY *pk = NULL; + EC_KEY *ec = NULL; + SHA2_CTX ctx; + uint8_t apphash[SHA256_DIGEST_LENGTH]; + uint8_t sighash[SHA256_DIGEST_LENGTH]; + uint8_t countbuf[4]; + + /* Decode EC_KEY from key handle */ + if ((bio = BIO_new(BIO_s_mem())) == NULL || + BIO_write(bio, key_handle, key_handle_len) != (int)key_handle_len) { + skdebug(__func__, "BIO setup failed"); + goto out; + } + if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, "")) == NULL) { + skdebug(__func__, "PEM_read_bio_PrivateKey failed"); + goto out; + } + if (EVP_PKEY_base_id(pk) != EVP_PKEY_EC) { + skdebug(__func__, "Not an EC key: %d", EVP_PKEY_base_id(pk)); + goto out; + } + if ((ec = EVP_PKEY_get1_EC_KEY(pk)) == NULL) { + skdebug(__func__, "EVP_PKEY_get1_EC_KEY failed"); + goto out; + } + /* Expect message to be pre-hashed */ + if (message_len != SHA256_DIGEST_LENGTH) { + skdebug(__func__, "bad message len %zu", message_len); + goto out; + } + /* Prepare data to be signed */ + dump("message", message, message_len); + SHA256Init(&ctx); + SHA256Update(&ctx, (const u_char *)application, strlen(application)); + SHA256Final(apphash, &ctx); + dump("apphash", apphash, sizeof(apphash)); + countbuf[0] = (counter >> 24) & 0xff; + countbuf[1] = (counter >> 16) & 0xff; + countbuf[2] = (counter >> 8) & 0xff; + countbuf[3] = counter & 0xff; + dump("countbuf", countbuf, sizeof(countbuf)); + dump("flags", &flags, sizeof(flags)); + SHA256Init(&ctx); + SHA256Update(&ctx, apphash, sizeof(apphash)); + SHA256Update(&ctx, &flags, sizeof(flags)); + SHA256Update(&ctx, countbuf, sizeof(countbuf)); + SHA256Update(&ctx, message, message_len); + SHA256Final(sighash, &ctx); + dump("sighash", sighash, sizeof(sighash)); + /* create and encode signature */ + if ((sig = ECDSA_do_sign(sighash, sizeof(sighash), ec)) == NULL) { + skdebug(__func__, "ECDSA_do_sign failed"); + goto out; + } + ECDSA_SIG_get0(sig, &sig_r, &sig_s); + response->sig_r_len = BN_num_bytes(sig_r); + response->sig_s_len = BN_num_bytes(sig_s); + if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || + (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + BN_bn2bin(sig_r, response->sig_r); + BN_bn2bin(sig_s, response->sig_s); + ret = 0; + out: + explicit_bzero(&ctx, sizeof(ctx)); + explicit_bzero(&apphash, sizeof(apphash)); + explicit_bzero(&sighash, sizeof(sighash)); + ECDSA_SIG_free(sig); + if (ret != 0) { + free(response->sig_r); + free(response->sig_s); + response->sig_r = NULL; + response->sig_s = NULL; + } + BIO_free(bio); + EC_KEY_free(ec); + EVP_PKEY_free(pk); + return ret; +#else + return -1; +#endif +} + +static int +sig_ed25519(const uint8_t *message, size_t message_len, + const char *application, uint32_t counter, uint8_t flags, + const uint8_t *key_handle, size_t key_handle_len, + struct sk_sign_response *response) +{ + size_t o; + int ret = -1; + SHA2_CTX ctx; + uint8_t apphash[SHA256_DIGEST_LENGTH]; + uint8_t signbuf[sizeof(apphash) + sizeof(flags) + + sizeof(counter) + SHA256_DIGEST_LENGTH]; + uint8_t sig[crypto_sign_ed25519_BYTES + sizeof(signbuf)]; + unsigned long long smlen; + + if (key_handle_len != crypto_sign_ed25519_SECRETKEYBYTES) { + skdebug(__func__, "bad key handle length %zu", key_handle_len); + goto out; + } + /* Expect message to be pre-hashed */ + if (message_len != SHA256_DIGEST_LENGTH) { + skdebug(__func__, "bad message len %zu", message_len); + goto out; + } + /* Prepare data to be signed */ + dump("message", message, message_len); + SHA256Init(&ctx); + SHA256Update(&ctx, (const u_char *)application, strlen(application)); + SHA256Final(apphash, &ctx); + dump("apphash", apphash, sizeof(apphash)); + + memcpy(signbuf, apphash, sizeof(apphash)); + o = sizeof(apphash); + signbuf[o++] = flags; + signbuf[o++] = (counter >> 24) & 0xff; + signbuf[o++] = (counter >> 16) & 0xff; + signbuf[o++] = (counter >> 8) & 0xff; + signbuf[o++] = counter & 0xff; + memcpy(signbuf + o, message, message_len); + o += message_len; + if (o != sizeof(signbuf)) { + skdebug(__func__, "bad sign buf len %zu, expected %zu", + o, sizeof(signbuf)); + goto out; + } + dump("signbuf", signbuf, sizeof(signbuf)); + /* create and encode signature */ + smlen = sizeof(signbuf); + if (crypto_sign_ed25519(sig, &smlen, signbuf, sizeof(signbuf), + key_handle) != 0) { + skdebug(__func__, "crypto_sign_ed25519 failed"); + goto out; + } + if (smlen <= sizeof(signbuf)) { + skdebug(__func__, "bad sign smlen %llu, expected min %zu", + smlen, sizeof(signbuf) + 1); + goto out; + } + response->sig_r_len = (size_t)(smlen - sizeof(signbuf)); + if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + memcpy(response->sig_r, sig, response->sig_r_len); + dump("sig_r", response->sig_r, response->sig_r_len); + ret = 0; + out: + explicit_bzero(&ctx, sizeof(ctx)); + explicit_bzero(&apphash, sizeof(apphash)); + explicit_bzero(&signbuf, sizeof(signbuf)); + explicit_bzero(&sig, sizeof(sig)); + if (ret != 0) { + free(response->sig_r); + response->sig_r = NULL; + } + return ret; +} + +int +sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, + const char *application, const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response) +{ + struct sk_sign_response *response = NULL; + int ret = SSH_SK_ERR_GENERAL; + SHA2_CTX ctx; + uint8_t message[32]; + + if (sign_response == NULL) { + skdebug(__func__, "sign_response == NULL"); + goto out; + } + *sign_response = NULL; + if (check_options(options) != 0) + goto out; /* error already logged */ + if ((response = calloc(1, sizeof(*response))) == NULL) { + skdebug(__func__, "calloc response failed"); + goto out; + } + SHA256Init(&ctx); + SHA256Update(&ctx, data, datalen); + SHA256Final(message, &ctx); + response->flags = flags; + response->counter = 0x12345678; + switch(alg) { + case SSH_SK_ECDSA: + if (sig_ecdsa(message, sizeof(message), application, + response->counter, flags, key_handle, key_handle_len, + response) != 0) + goto out; + break; + case SSH_SK_ED25519: + if (sig_ed25519(message, sizeof(message), application, + response->counter, flags, key_handle, key_handle_len, + response) != 0) + goto out; + break; + default: + skdebug(__func__, "unsupported key type %d", alg); + return -1; + } + *sign_response = response; + response = NULL; + ret = 0; + out: + explicit_bzero(message, sizeof(message)); + if (response != NULL) { + free(response->sig_r); + free(response->sig_s); + free(response); + } + return ret; +} + +int +sk_load_resident_keys(const char *pin, struct sk_option **options, + struct sk_resident_key ***rks, size_t *nrks) +{ + return SSH_SK_ERR_UNSUPPORTED; +} diff --git a/aosp/external/openssh/regress/mkdtemp.c b/aosp/external/openssh/regress/mkdtemp.c new file mode 100644 index 0000000000000000000000000000000000000000..a7be1bdab4b05603991147ab7b5a9175523df98c --- /dev/null +++ b/aosp/external/openssh/regress/mkdtemp.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Colin Watson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Roughly equivalent to "mktemp -d -t TEMPLATE", but portable. */ + +#include "includes.h" + +#include +#include +#include +#include +#include + +#include "log.h" + +static void +usage(void) +{ + fprintf(stderr, "mkdtemp template\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *base; + const char *tmpdir; + char template[PATH_MAX]; + int r; + char *dir; + + if (argc != 2) + usage(); + base = argv[1]; + + if ((tmpdir = getenv("TMPDIR")) == NULL) + tmpdir = "/tmp"; + r = snprintf(template, sizeof(template), "%s/%s", tmpdir, base); + if (r < 0 || (size_t)r >= sizeof(template)) + fatal("template string too long"); + dir = mkdtemp(template); + if (dir == NULL) { + perror("mkdtemp"); + exit(1); + } + puts(dir); + return 0; +} diff --git a/aosp/external/openssh/regress/modpipe.c b/aosp/external/openssh/regress/modpipe.c new file mode 100644 index 0000000000000000000000000000000000000000..5f4824b51d024df7d31da0f51fcf9799dee887e6 --- /dev/null +++ b/aosp/external/openssh/regress/modpipe.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2012 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: modpipe.c,v 1.6 2013/11/21 03:16:47 djm Exp $ */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ERR_H +# include +#endif +#include "openbsd-compat/getopt_long.c" + +static void +usage(void) +{ + fprintf(stderr, "Usage: modpipe -w [-m modspec ...] < in > out\n"); + fprintf(stderr, "modspec is one of:\n"); + fprintf(stderr, " xor:offset:value - XOR \"value\" at \"offset\"\n"); + fprintf(stderr, " andor:offset:val1:val2 - AND \"val1\" then OR \"val2\" at \"offset\"\n"); + exit(1); +} + +#define MAX_MODIFICATIONS 256 +struct modification { + enum { MOD_XOR, MOD_AND_OR } what; + unsigned long long offset; + u_int8_t m1, m2; +}; + +static void +parse_modification(const char *s, struct modification *m) +{ + char what[16+1]; + int n, m1, m2; + + bzero(m, sizeof(*m)); + if ((n = sscanf(s, "%16[^:]%*[:]%llu%*[:]%i%*[:]%i", + what, &m->offset, &m1, &m2)) < 3) + errx(1, "Invalid modification spec \"%s\"", s); + if (strcasecmp(what, "xor") == 0) { + if (n > 3) + errx(1, "Invalid modification spec \"%s\"", s); + if (m1 < 0 || m1 > 0xff) + errx(1, "Invalid XOR modification value"); + m->what = MOD_XOR; + m->m1 = m1; + } else if (strcasecmp(what, "andor") == 0) { + if (n != 4) + errx(1, "Invalid modification spec \"%s\"", s); + if (m1 < 0 || m1 > 0xff) + errx(1, "Invalid AND modification value"); + if (m2 < 0 || m2 > 0xff) + errx(1, "Invalid OR modification value"); + m->what = MOD_AND_OR; + m->m1 = m1; + m->m2 = m2; + } else + errx(1, "Invalid modification type \"%s\"", what); +} + +int +main(int argc, char **argv) +{ + int ch; + u_char buf[8192]; + size_t total; + ssize_t r, s, o; + struct modification mods[MAX_MODIFICATIONS]; + u_int i, wflag = 0, num_mods = 0; + + while ((ch = getopt(argc, argv, "wm:")) != -1) { + switch (ch) { + case 'm': + if (num_mods >= MAX_MODIFICATIONS) + errx(1, "Too many modifications"); + parse_modification(optarg, &(mods[num_mods++])); + break; + case 'w': + wflag = 1; + break; + default: + usage(); + /* NOTREACHED */ + } + } + for (total = 0;;) { + r = s = read(STDIN_FILENO, buf, sizeof(buf)); + if (r == 0) + break; + if (r < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + err(1, "read"); + } + for (i = 0; i < num_mods; i++) { + if (mods[i].offset < total || + mods[i].offset >= total + s) + continue; + switch (mods[i].what) { + case MOD_XOR: + buf[mods[i].offset - total] ^= mods[i].m1; + break; + case MOD_AND_OR: + buf[mods[i].offset - total] &= mods[i].m1; + buf[mods[i].offset - total] |= mods[i].m2; + break; + } + } + for (o = 0; o < s; o += r) { + r = write(STDOUT_FILENO, buf, s - o); + if (r == 0) + break; + if (r < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + err(1, "write"); + } + } + total += s; + } + /* Warn if modifications not reached in input stream */ + r = 0; + for (i = 0; wflag && i < num_mods; i++) { + if (mods[i].offset < total) + continue; + r = 1; + fprintf(stderr, "modpipe: warning - mod %u not reached\n", i); + } + return r; +} diff --git a/aosp/external/openssh/regress/moduli.in b/aosp/external/openssh/regress/moduli.in new file mode 100644 index 0000000000000000000000000000000000000000..e69c902a2227c14a99f2e20b367a1a03ccba5c9f --- /dev/null +++ b/aosp/external/openssh/regress/moduli.in @@ -0,0 +1,3 @@ +20160301052556 2 6 100 2047 5 DA57B18976E9C55CEAC3BFFF70419A1550258EA7359400BD4FAC8F4203B73E0BC54D62C0A2D9AA9B543FACA0290514EA426DE6FEF897CB858243511DCE5170420C799D888DCFDC4502FF49B66F34E75C00E98A55408A791FF5CFEA7C288F8E6664226A6A90BE237D2E40C207B5AD0CAEDFDA4946E63AEA351A09EF462515FED4098694241CD07E2CB7727B39B8B1B9467D72DFB908D8169F5DB3CD5A6BEBE1344C585A882508B760402E86EB9B5548A7B98635ECFCDC02FF62B29C53847142FC598ADC66F622F6E9F73BDF02B3D795C0DF23D00E5A3A7748F3E1D5B06F46D4568CE3F4CC57E67D4C36DF5C12800620698C727CC5F5BCACF3B7E17E37D19F4647 +20160301052601 2 6 100 2047 2 DA57B18976E9C55CEAC3BFFF70419A1550258EA7359400BD4FAC8F4203B73E0BC54D62C0A2D9AA9B543FACA0290514EA426DE6FEF897CB858243511DCE5170420C799D888DCFDC4502FF49B66F34E75C00E98A55408A791FF5CFEA7C288F8E6664226A6A90BE237D2E40C207B5AD0CAEDFDA4946E63AEA351A09EF462515FED4098694241CD07E2CB7727B39B8B1B9467D72DFB908D8169F5DB3CD5A6BEBE1344C585A882508B760402E86EB9B5548A7B98635ECFCDC02FF62B29C53847142FC598ADC66F622F6E9F73BDF02B3D795C0DF23D00E5A3A7748F3E1D5B06F46D4568CE3F4CC57E67D4C36DF5C12800620698C727CC5F5BCACF3B7E17E37D1A5C13B +20160301052612 2 6 100 2047 5 DA57B18976E9C55CEAC3BFFF70419A1550258EA7359400BD4FAC8F4203B73E0BC54D62C0A2D9AA9B543FACA0290514EA426DE6FEF897CB858243511DCE5170420C799D888DCFDC4502FF49B66F34E75C00E98A55408A791FF5CFEA7C288F8E6664226A6A90BE237D2E40C207B5AD0CAEDFDA4946E63AEA351A09EF462515FED4098694241CD07E2CB7727B39B8B1B9467D72DFB908D8169F5DB3CD5A6BEBE1344C585A882508B760402E86EB9B5548A7B98635ECFCDC02FF62B29C53847142FC598ADC66F622F6E9F73BDF02B3D795C0DF23D00E5A3A7748F3E1D5B06F46D4568CE3F4CC57E67D4C36DF5C12800620698C727CC5F5BCACF3B7E17E37D1B7A3EF diff --git a/aosp/external/openssh/regress/multiplex.sh b/aosp/external/openssh/regress/multiplex.sh new file mode 100644 index 0000000000000000000000000000000000000000..4744fa3d97d6158f3dc3e3b0d6e87552463cc66f --- /dev/null +++ b/aosp/external/openssh/regress/multiplex.sh @@ -0,0 +1,199 @@ +# $OpenBSD: multiplex.sh,v 1.33 2020/06/24 15:16:23 markus Exp $ +# Placed in the Public Domain. + +make_tmpdir +CTL=${SSH_REGRESS_TMP}/ctl-sock + +tid="connection multiplexing" + +trace "will use ProxyCommand $proxycmd" +if config_defined DISABLE_FD_PASSING ; then + echo "skipped (not supported on this platform)" + exit 0 +fi + +P=3301 # test port + +wait_for_mux_master_ready() +{ + for i in 1 2 3 4 5 6 7 8 9; do + ${SSH} -F $OBJ/ssh_config -S $CTL -Ocheck otherhost \ + >/dev/null 2>&1 && return 0 + sleep $i + done + fatal "mux master never becomes ready" +} + +start_sshd + +start_mux_master() +{ + trace "start master, fork to background" + ${SSH} -Nn2 -MS$CTL -F $OBJ/ssh_config -oSendEnv="_XXX_TEST" somehost \ + -E $TEST_REGRESS_LOGFILE 2>&1 & + # NB. $SSH_PID will be killed by test-exec.sh:cleanup on fatal errors. + SSH_PID=$! + wait_for_mux_master_ready +} + +start_mux_master + +verbose "test $tid: envpass" +trace "env passing over multiplexed connection" +_XXX_TEST=blah ${SSH} -F $OBJ/ssh_config -oSendEnv="_XXX_TEST" -S$CTL otherhost sh << 'EOF' + test X"$_XXX_TEST" = X"blah" +EOF +if [ $? -ne 0 ]; then + fail "environment not found" +fi + +verbose "test $tid: transfer" +rm -f ${COPY} +trace "ssh transfer over multiplexed connection and check result" +${SSH} -F $OBJ/ssh_config -S$CTL otherhost cat ${DATA} > ${COPY} +test -f ${COPY} || fail "ssh -Sctl: failed copy ${DATA}" +cmp ${DATA} ${COPY} || fail "ssh -Sctl: corrupted copy of ${DATA}" + +rm -f ${COPY} +trace "ssh transfer over multiplexed connection and check result" +${SSH} -F $OBJ/ssh_config -S $CTL otherhost cat ${DATA} > ${COPY} +test -f ${COPY} || fail "ssh -S ctl: failed copy ${DATA}" +cmp ${DATA} ${COPY} || fail "ssh -S ctl: corrupted copy of ${DATA}" + +rm -f ${COPY} +trace "sftp transfer over multiplexed connection and check result" +echo "get ${DATA} ${COPY}" | \ + ${SFTP} -S ${SSH} -F $OBJ/ssh_config -oControlPath=$CTL otherhost >>$TEST_REGRESS_LOGFILE 2>&1 +test -f ${COPY} || fail "sftp: failed copy ${DATA}" +cmp ${DATA} ${COPY} || fail "sftp: corrupted copy of ${DATA}" + +rm -f ${COPY} +trace "scp transfer over multiplexed connection and check result" +${SCP} -S ${SSH} -F $OBJ/ssh_config -oControlPath=$CTL otherhost:${DATA} ${COPY} >>$TEST_REGRESS_LOGFILE 2>&1 +test -f ${COPY} || fail "scp: failed copy ${DATA}" +cmp ${DATA} ${COPY} || fail "scp: corrupted copy of ${DATA}" + +rm -f ${COPY} +verbose "test $tid: forward" +trace "forward over TCP/IP and check result" +$NC -N -l 127.0.0.1 $((${PORT} + 1)) < ${DATA} > /dev/null & +netcat_pid=$! +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -L127.0.0.1:$((${PORT} + 2)):127.0.0.1:$((${PORT} + 1)) otherhost >>$TEST_SSH_LOGFILE 2>&1 +sleep 1 # XXX remove once race fixed +$NC 127.0.0.1 $((${PORT} + 2)) < /dev/null > ${COPY} +cmp ${DATA} ${COPY} || fail "ssh: corrupted copy of ${DATA}" +kill $netcat_pid 2>/dev/null +rm -f ${COPY} $OBJ/unix-[123].fwd + +trace "forward over UNIX and check result" +$NC -N -Ul $OBJ/unix-1.fwd < ${DATA} > /dev/null & +netcat_pid=$! +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -L$OBJ/unix-2.fwd:$OBJ/unix-1.fwd otherhost >>$TEST_SSH_LOGFILE 2>&1 +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -R$OBJ/unix-3.fwd:$OBJ/unix-2.fwd otherhost >>$TEST_SSH_LOGFILE 2>&1 +sleep 1 # XXX remove once race fixed +$NC -U $OBJ/unix-3.fwd < /dev/null > ${COPY} +cmp ${DATA} ${COPY} || fail "ssh: corrupted copy of ${DATA}" +kill $netcat_pid 2>/dev/null +rm -f ${COPY} $OBJ/unix-[123].fwd + +for s in 0 1 4 5 44; do + for mode in "" "-Oproxy"; do + trace "exit status $s over multiplexed connection ($mode)" + verbose "test $tid: status $s ($mode)" + ${SSH} -F $OBJ/ssh_config -S $CTL $mode otherhost exit $s + r=$? + if [ $r -ne $s ]; then + fail "exit code mismatch: $r != $s" + fi + + # same with early close of stdout/err + trace "exit status $s with early close over multiplexed connection ($mode)" + ${SSH} -F $OBJ/ssh_config -S $CTL -n $mode otherhost \ + exec sh -c \'"sleep 2; exec > /dev/null 2>&1; sleep 3; exit $s"\' + r=$? + if [ $r -ne $s ]; then + fail "exit code (with sleep) mismatch: $r != $s" + fi + done +done + +verbose "test $tid: cmd check" +${SSH} -F $OBJ/ssh_config -S $CTL -Ocheck otherhost >>$TEST_REGRESS_LOGFILE 2>&1 \ + || fail "check command failed" + +verbose "test $tid: cmd forward local (TCP)" +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -L $P:localhost:$PORT otherhost \ + || fail "request local forward failed" +sleep 1 # XXX remove once race fixed +${SSH} -F $OBJ/ssh_config -p$P otherhost true \ + || fail "connect to local forward port failed" +${SSH} -F $OBJ/ssh_config -S $CTL -Ocancel -L $P:localhost:$PORT otherhost \ + || fail "cancel local forward failed" +${SSH} -F $OBJ/ssh_config -p$P otherhost true \ + && fail "local forward port still listening" + +verbose "test $tid: cmd forward remote (TCP)" +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -R $P:localhost:$PORT otherhost \ + || fail "request remote forward failed" +sleep 1 # XXX remove once race fixed +${SSH} -F $OBJ/ssh_config -p$P otherhost true \ + || fail "connect to remote forwarded port failed" +${SSH} -F $OBJ/ssh_config -S $CTL -Ocancel -R $P:localhost:$PORT otherhost \ + || fail "cancel remote forward failed" +${SSH} -F $OBJ/ssh_config -p$P otherhost true \ + && fail "remote forward port still listening" + +verbose "test $tid: cmd forward local (UNIX)" +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -L $OBJ/unix-1.fwd:localhost:$PORT otherhost \ + || fail "request local forward failed" +sleep 1 # XXX remove once race fixed +echo "" | $NC -U $OBJ/unix-1.fwd | \ + grep "Invalid SSH identification string" >/dev/null 2>&1 \ + || fail "connect to local forward path failed" +${SSH} -F $OBJ/ssh_config -S $CTL -Ocancel -L $OBJ/unix-1.fwd:localhost:$PORT otherhost \ + || fail "cancel local forward failed" +N=$(echo "xyzzy" | $NC -U $OBJ/unix-1.fwd 2>&1 | grep "xyzzy" | wc -l) +test ${N} -eq 0 || fail "local forward path still listening" +rm -f $OBJ/unix-1.fwd + +verbose "test $tid: cmd forward remote (UNIX)" +${SSH} -F $OBJ/ssh_config -S $CTL -Oforward -R $OBJ/unix-1.fwd:localhost:$PORT otherhost \ + || fail "request remote forward failed" +sleep 1 # XXX remove once race fixed +echo "" | $NC -U $OBJ/unix-1.fwd | \ + grep "Invalid SSH identification string" >/dev/null 2>&1 \ + || fail "connect to remote forwarded path failed" +${SSH} -F $OBJ/ssh_config -S $CTL -Ocancel -R $OBJ/unix-1.fwd:localhost:$PORT otherhost \ + || fail "cancel remote forward failed" +N=$(echo "xyzzy" | $NC -U $OBJ/unix-1.fwd 2>&1 | grep "xyzzy" | wc -l) +test ${N} -eq 0 || fail "remote forward path still listening" +rm -f $OBJ/unix-1.fwd + +verbose "test $tid: cmd exit" +${SSH} -F $OBJ/ssh_config -S $CTL -Oexit otherhost >>$TEST_REGRESS_LOGFILE 2>&1 \ + || fail "send exit command failed" + +# Wait for master to exit +wait $SSH_PID +kill -0 $SSH_PID >/dev/null 2>&1 && fail "exit command failed" + +# Restart master and test -O stop command with master using -N +verbose "test $tid: cmd stop" +trace "restart master, fork to background" +start_mux_master + +# start a long-running command then immediately request a stop +${SSH} -F $OBJ/ssh_config -S $CTL otherhost "sleep 10; exit 0" \ + >>$TEST_REGRESS_LOGFILE 2>&1 & +SLEEP_PID=$! +${SSH} -F $OBJ/ssh_config -S $CTL -Ostop otherhost >>$TEST_REGRESS_LOGFILE 2>&1 \ + || fail "send stop command failed" + +# wait until both long-running command and master have exited. +wait $SLEEP_PID +[ $! != 0 ] || fail "waiting for concurrent command" +wait $SSH_PID +[ $! != 0 ] || fail "waiting for master stop" +kill -0 $SSH_PID >/dev/null 2>&1 && fatal "stop command failed" +SSH_PID="" # Already gone, so don't kill in cleanup + diff --git a/aosp/external/openssh/regress/multipubkey.sh b/aosp/external/openssh/regress/multipubkey.sh new file mode 100644 index 0000000000000000000000000000000000000000..8cdda1a9ae0dece409c8b8f317d02b74b2808d90 --- /dev/null +++ b/aosp/external/openssh/regress/multipubkey.sh @@ -0,0 +1,75 @@ +# $OpenBSD: multipubkey.sh,v 1.4 2021/06/07 01:16:34 djm Exp $ +# Placed in the Public Domain. + +tid="multiple pubkey" + +rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/user_key* +rm -f $OBJ/authorized_principals_$USER $OBJ/cert_user_key* + +mv $OBJ/sshd_proxy $OBJ/sshd_proxy.orig +mv $OBJ/ssh_proxy $OBJ/ssh_proxy.orig + +# Create a CA key +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key ||\ + fatal "ssh-keygen failed" + +# Make some keys and a certificate. +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key1 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key2 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -s $OBJ/user_ca_key -I "regress user key for $USER" \ + -z $$ -n ${USER},mekmitasdigoat $OBJ/user_key1 || + fail "couldn't sign user_key1" +# Copy the private key alongside the cert to allow better control of when +# it is offered. +mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1.pub +cp -p $OBJ/user_key1 $OBJ/cert_user_key1 + +grep -v IdentityFile $OBJ/ssh_proxy.orig > $OBJ/ssh_proxy + +opts="-oProtocol=2 -F $OBJ/ssh_proxy -oIdentitiesOnly=yes" +opts="$opts -i $OBJ/cert_user_key1 -i $OBJ/user_key1 -i $OBJ/user_key2" + +for match in no yes ; do + ( + cat $OBJ/sshd_proxy.orig + echo "Protocol 2" + echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" + echo "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u" + ) > $OBJ/sshd_proxy + if test "$match" = "yes" ; then + echo "AuthenticationMethods none" >> $OBJ/sshd_proxy + echo "PubkeyAuthentication no" >> $OBJ/sshd_proxy + echo "Match all" >> $OBJ/sshd_proxy + echo "PubkeyAuthentication yes" >> $OBJ/sshd_proxy + fi + echo "AuthenticationMethods publickey,publickey" >> $OBJ/sshd_proxy + + # Single key should fail. + trace "match $match single key" + rm -f $OBJ/authorized_principals_$USER + cat $OBJ/user_key1.pub > $OBJ/authorized_keys_$USER + ${SSH} $opts proxy true && fail "ssh succeeded with key" + + # Single key with same-public cert should fail. + trace "match $match pubkey + identical cert" + echo mekmitasdigoat > $OBJ/authorized_principals_$USER + cat $OBJ/user_key1.pub > $OBJ/authorized_keys_$USER + ${SSH} $opts proxy true && fail "ssh succeeded with key+cert" + + # Multiple plain keys should succeed. + trace "match $match multiple public" + rm -f $OBJ/authorized_principals_$USER + cat $OBJ/user_key1.pub $OBJ/user_key2.pub > \ + $OBJ/authorized_keys_$USER + ${SSH} $opts proxy true || fail "ssh failed with multiple keys" + # Cert and different key should succeed + + # Key and different-public cert should succeed. + trace "match $match pubkey + different cert" + echo mekmitasdigoat > $OBJ/authorized_principals_$USER + cat $OBJ/user_key2.pub > $OBJ/authorized_keys_$USER + ${SSH} $opts proxy true || fail "ssh failed with key/cert" +done + diff --git a/aosp/external/openssh/regress/netcat.c b/aosp/external/openssh/regress/netcat.c new file mode 100644 index 0000000000000000000000000000000000000000..20ec3f5954faad76dba6615bdba18bbf8d3dc9f3 --- /dev/null +++ b/aosp/external/openssh/regress/netcat.c @@ -0,0 +1,1686 @@ +/* $OpenBSD: netcat.c,v 1.131 2015/09/03 23:06:28 sobrado Exp $ */ +/* + * Copyright (c) 2001 Eric Jackson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Re-written nc(1) for OpenBSD. Original implementation by + * *Hobbit* . + */ + +#include "includes.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atomicio.h" + +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif +#ifdef HAVE_ERR_H +# include +#endif +#ifdef HAVE_SYS_BYTEORDER_H +# include +#endif + +/* rename to avoid collision in libssh */ +#define timeout_connect netcat_timeout_connect + +/* Telnet options from arpa/telnet.h */ +#define IAC 255 +#define DONT 254 +#define DO 253 +#define WONT 252 +#define WILL 251 + +#ifndef SUN_LEN +#define SUN_LEN(su) \ + (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) +#endif + +#define PORT_MAX 65535 +#define PORT_MAX_LEN 6 +#define UNIX_DG_TMP_SOCKET_SIZE 19 + +#define POLL_STDIN 0 +#define POLL_NETOUT 1 +#define POLL_NETIN 2 +#define POLL_STDOUT 3 +#define BUFSIZE 16384 + +/* Command Line Options */ +int dflag; /* detached, no stdin */ +int Fflag; /* fdpass sock to stdout */ +unsigned int iflag; /* Interval Flag */ +int kflag; /* More than one connect */ +int lflag; /* Bind to local port */ +int Nflag; /* shutdown() network socket */ +int nflag; /* Don't do name look up */ +char *Pflag; /* Proxy username */ +char *pflag; /* Localport flag */ +int rflag; /* Random ports flag */ +char *sflag; /* Source Address */ +int tflag; /* Telnet Emulation */ +int uflag; /* UDP - Default to TCP */ +int vflag; /* Verbosity */ +int xflag; /* Socks proxy */ +int zflag; /* Port Scan Flag */ +int Dflag; /* sodebug */ +int Iflag; /* TCP receive buffer size */ +int Oflag; /* TCP send buffer size */ +int Sflag; /* TCP MD5 signature option */ +int Tflag = -1; /* IP Type of Service */ +int rtableid = -1; + +int timeout = -1; +int family = AF_UNSPEC; +char *portlist[PORT_MAX+1]; +char *unix_dg_tmp_socket; + +void atelnet(int, unsigned char *, unsigned int); +void build_ports(char *); +void help(void); +int local_listen(char *, char *, struct addrinfo); +void readwrite(int); +void fdpass(int nfd) __attribute__((noreturn)); +int remote_connect(const char *, const char *, struct addrinfo); +int timeout_connect(int, const struct sockaddr *, socklen_t); +int socks_connect(const char *, const char *, struct addrinfo, + const char *, const char *, struct addrinfo, int, const char *); +int udptest(int); +int unix_bind(char *); +int unix_connect(char *); +int unix_listen(char *); +void set_common_sockopts(int, int); +int map_tos(char *, int *); +void report_connect(const struct sockaddr *, socklen_t); +void usage(int); +ssize_t drainbuf(int, unsigned char *, size_t *); +ssize_t fillbuf(int, unsigned char *, size_t *); + + +int +main(int argc, char *argv[]) +{ + int ch, s, ret, socksv; + char *host, *uport; + struct addrinfo hints; + struct servent *sv; + socklen_t len; + struct sockaddr_storage cliaddr; + char *proxy = NULL; + const char *errstr, *proxyhost = "", *proxyport = NULL; + struct addrinfo proxyhints; + char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE]; + + ret = 1; + s = 0; + socksv = 5; + host = NULL; + uport = NULL; + sv = NULL; + + signal(SIGPIPE, SIG_IGN); + + while ((ch = getopt(argc, argv, + "46DdFhI:i:klNnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) { + switch (ch) { + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; + case 'U': + family = AF_UNIX; + break; + case 'X': + if (strcasecmp(optarg, "connect") == 0) + socksv = -1; /* HTTP proxy CONNECT */ + else if (strcmp(optarg, "4") == 0) + socksv = 4; /* SOCKS v.4 */ + else if (strcmp(optarg, "5") == 0) + socksv = 5; /* SOCKS v.5 */ + else + errx(1, "unsupported proxy protocol"); + break; + case 'd': + dflag = 1; + break; + case 'F': + Fflag = 1; + break; + case 'h': + help(); + break; + case 'i': + iflag = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr) + errx(1, "interval %s: %s", errstr, optarg); + break; + case 'k': + kflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'N': + Nflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'P': + Pflag = optarg; + break; + case 'p': + pflag = optarg; + break; + case 'r': + rflag = 1; + break; + case 's': + sflag = optarg; + break; + case 't': + tflag = 1; + break; + case 'u': + uflag = 1; + break; +#ifdef SO_RTABLE + case 'V': + rtableid = (int)strtonum(optarg, 0, + RT_TABLEID_MAX, &errstr); + if (errstr) + errx(1, "rtable %s: %s", errstr, optarg); + break; +#endif + case 'v': + vflag = 1; + break; + case 'w': + timeout = strtonum(optarg, 0, INT_MAX / 1000, &errstr); + if (errstr) + errx(1, "timeout %s: %s", errstr, optarg); + timeout *= 1000; + break; + case 'x': + xflag = 1; + if ((proxy = strdup(optarg)) == NULL) + errx(1, "strdup"); + break; + case 'z': + zflag = 1; + break; + case 'D': + Dflag = 1; + break; + case 'I': + Iflag = strtonum(optarg, 1, 65536 << 14, &errstr); + if (errstr != NULL) + errx(1, "TCP receive window %s: %s", + errstr, optarg); + break; + case 'O': + Oflag = strtonum(optarg, 1, 65536 << 14, &errstr); + if (errstr != NULL) + errx(1, "TCP send window %s: %s", + errstr, optarg); + break; + case 'S': + Sflag = 1; + break; + case 'T': + errstr = NULL; + errno = 0; + if (map_tos(optarg, &Tflag)) + break; + if (strlen(optarg) > 1 && optarg[0] == '0' && + optarg[1] == 'x') + Tflag = (int)strtol(optarg, NULL, 16); + else + Tflag = (int)strtonum(optarg, 0, 255, + &errstr); + if (Tflag < 0 || Tflag > 255 || errstr || errno) + errx(1, "illegal tos value %s", optarg); + break; + default: + usage(1); + } + } + argc -= optind; + argv += optind; + + /* Cruft to make sure options are clean, and used properly. */ + if (argv[0] && !argv[1] && family == AF_UNIX) { + host = argv[0]; + uport = NULL; + } else if (argv[0] && !argv[1]) { + if (!lflag) + usage(1); + uport = argv[0]; + host = NULL; + } else if (argv[0] && argv[1]) { + host = argv[0]; + uport = argv[1]; + } else + usage(1); + + if (lflag && sflag) + errx(1, "cannot use -s and -l"); + if (lflag && pflag) + errx(1, "cannot use -p and -l"); + if (lflag && zflag) + errx(1, "cannot use -z and -l"); + if (!lflag && kflag) + errx(1, "must use -l with -k"); + + /* Get name of temporary socket for unix datagram client */ + if ((family == AF_UNIX) && uflag && !lflag) { + if (sflag) { + unix_dg_tmp_socket = sflag; + } else { + strlcpy(unix_dg_tmp_socket_buf, "/tmp/nc.XXXXXXXXXX", + UNIX_DG_TMP_SOCKET_SIZE); + if (mktemp(unix_dg_tmp_socket_buf) == NULL) + err(1, "mktemp"); + unix_dg_tmp_socket = unix_dg_tmp_socket_buf; + } + } + + /* Initialize addrinfo structure. */ + if (family != AF_UNIX) { + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP; + if (nflag) + hints.ai_flags |= AI_NUMERICHOST; + } + + if (xflag) { + if (uflag) + errx(1, "no proxy support for UDP mode"); + + if (lflag) + errx(1, "no proxy support for listen"); + + if (family == AF_UNIX) + errx(1, "no proxy support for unix sockets"); + + /* XXX IPv6 transport to proxy would probably work */ + if (family == AF_INET6) + errx(1, "no proxy support for IPv6"); + + if (sflag) + errx(1, "no proxy support for local source address"); + + proxyhost = strsep(&proxy, ":"); + proxyport = proxy; + + memset(&proxyhints, 0, sizeof(struct addrinfo)); + proxyhints.ai_family = family; + proxyhints.ai_socktype = SOCK_STREAM; + proxyhints.ai_protocol = IPPROTO_TCP; + if (nflag) + proxyhints.ai_flags |= AI_NUMERICHOST; + } + + if (lflag) { + int connfd; + ret = 0; + + if (family == AF_UNIX) { + if (uflag) + s = unix_bind(host); + else + s = unix_listen(host); + } + + /* Allow only one connection at a time, but stay alive. */ + for (;;) { + if (family != AF_UNIX) + s = local_listen(host, uport, hints); + if (s < 0) + err(1, "local_listen"); + /* + * For UDP and -k, don't connect the socket, let it + * receive datagrams from multiple socket pairs. + */ + if (uflag && kflag) + readwrite(s); + /* + * For UDP and not -k, we will use recvfrom() initially + * to wait for a caller, then use the regular functions + * to talk to the caller. + */ + else if (uflag && !kflag) { + int rv, plen; + char buf[16384]; + struct sockaddr_storage z; + + len = sizeof(z); + plen = 2048; + rv = recvfrom(s, buf, plen, MSG_PEEK, + (struct sockaddr *)&z, &len); + if (rv < 0) + err(1, "recvfrom"); + + rv = connect(s, (struct sockaddr *)&z, len); + if (rv < 0) + err(1, "connect"); + + if (vflag) + report_connect((struct sockaddr *)&z, len); + + readwrite(s); + } else { + len = sizeof(cliaddr); + connfd = accept(s, (struct sockaddr *)&cliaddr, + &len); + if (connfd == -1) { + /* For now, all errnos are fatal */ + err(1, "accept"); + } + if (vflag) + report_connect((struct sockaddr *)&cliaddr, len); + + readwrite(connfd); + close(connfd); + } + + if (family != AF_UNIX) + close(s); + else if (uflag) { + if (connect(s, NULL, 0) < 0) + err(1, "connect"); + } + + if (!kflag) + break; + } + } else if (family == AF_UNIX) { + ret = 0; + + if ((s = unix_connect(host)) > 0 && !zflag) { + readwrite(s); + close(s); + } else + ret = 1; + + if (uflag) + unlink(unix_dg_tmp_socket); + exit(ret); + + } else { + int i = 0; + + /* Construct the portlist[] array. */ + build_ports(uport); + + /* Cycle through portlist, connecting to each port. */ + for (i = 0; portlist[i] != NULL; i++) { + if (s) + close(s); + + if (xflag) + s = socks_connect(host, portlist[i], hints, + proxyhost, proxyport, proxyhints, socksv, + Pflag); + else + s = remote_connect(host, portlist[i], hints); + + if (s < 0) + continue; + + ret = 0; + if (vflag || zflag) { + /* For UDP, make sure we are connected. */ + if (uflag) { + if (udptest(s) == -1) { + ret = 1; + continue; + } + } + + /* Don't look up port if -n. */ + if (nflag) + sv = NULL; + else { + sv = getservbyport( + ntohs(atoi(portlist[i])), + uflag ? "udp" : "tcp"); + } + + fprintf(stderr, + "Connection to %s %s port [%s/%s] " + "succeeded!\n", host, portlist[i], + uflag ? "udp" : "tcp", + sv ? sv->s_name : "*"); + } + if (Fflag) + fdpass(s); + else if (!zflag) + readwrite(s); + } + } + + if (s) + close(s); + + exit(ret); +} + +/* + * unix_bind() + * Returns a unix socket bound to the given path + */ +int +unix_bind(char *path) +{ + struct sockaddr_un sun_sa; + int s; + + /* Create unix domain socket. */ + if ((s = socket(AF_UNIX, uflag ? SOCK_DGRAM : SOCK_STREAM, + 0)) < 0) + return (-1); + + memset(&sun_sa, 0, sizeof(struct sockaddr_un)); + sun_sa.sun_family = AF_UNIX; + + if (strlcpy(sun_sa.sun_path, path, sizeof(sun_sa.sun_path)) >= + sizeof(sun_sa.sun_path)) { + close(s); + errno = ENAMETOOLONG; + return (-1); + } + + if (bind(s, (struct sockaddr *)&sun_sa, SUN_LEN(&sun_sa)) < 0) { + close(s); + return (-1); + } + return (s); +} + +/* + * unix_connect() + * Returns a socket connected to a local unix socket. Returns -1 on failure. + */ +int +unix_connect(char *path) +{ + struct sockaddr_un sun_sa; + int s; + + if (uflag) { + if ((s = unix_bind(unix_dg_tmp_socket)) < 0) + return (-1); + } else { + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return (-1); + } + (void)fcntl(s, F_SETFD, FD_CLOEXEC); + + memset(&sun_sa, 0, sizeof(struct sockaddr_un)); + sun_sa.sun_family = AF_UNIX; + + if (strlcpy(sun_sa.sun_path, path, sizeof(sun_sa.sun_path)) >= + sizeof(sun_sa.sun_path)) { + close(s); + errno = ENAMETOOLONG; + return (-1); + } + if (connect(s, (struct sockaddr *)&sun_sa, SUN_LEN(&sun_sa)) < 0) { + close(s); + return (-1); + } + return (s); + +} + +/* + * unix_listen() + * Create a unix domain socket, and listen on it. + */ +int +unix_listen(char *path) +{ + int s; + if ((s = unix_bind(path)) < 0) + return (-1); + + if (listen(s, 5) < 0) { + close(s); + return (-1); + } + return (s); +} + +/* + * remote_connect() + * Returns a socket connected to a remote host. Properly binds to a local + * port or source address if needed. Returns -1 on failure. + */ +int +remote_connect(const char *host, const char *port, struct addrinfo hints) +{ + struct addrinfo *res, *res0; + int s, error; +#if defined(SO_RTABLE) || defined(SO_BINDANY) + int on = 1; +#endif + + if ((error = getaddrinfo(host, port, &hints, &res))) + errx(1, "getaddrinfo: %s", gai_strerror(error)); + + res0 = res; + do { + if ((s = socket(res0->ai_family, res0->ai_socktype, + res0->ai_protocol)) < 0) + continue; + +#ifdef SO_RTABLE + if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_RTABLE, + &rtableid, sizeof(rtableid)) == -1)) + err(1, "setsockopt SO_RTABLE"); +#endif + /* Bind to a local port or source address if specified. */ + if (sflag || pflag) { + struct addrinfo ahints, *ares; + +#ifdef SO_BINDANY + /* try SO_BINDANY, but don't insist */ + setsockopt(s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on)); +#endif + memset(&ahints, 0, sizeof(struct addrinfo)); + ahints.ai_family = res0->ai_family; + ahints.ai_socktype = uflag ? SOCK_DGRAM : SOCK_STREAM; + ahints.ai_protocol = uflag ? IPPROTO_UDP : IPPROTO_TCP; + ahints.ai_flags = AI_PASSIVE; + if ((error = getaddrinfo(sflag, pflag, &ahints, &ares))) + errx(1, "getaddrinfo: %s", gai_strerror(error)); + + if (bind(s, (struct sockaddr *)ares->ai_addr, + ares->ai_addrlen) < 0) + err(1, "bind failed"); + freeaddrinfo(ares); + } + + set_common_sockopts(s, res0->ai_family); + + if (timeout_connect(s, res0->ai_addr, res0->ai_addrlen) == 0) + break; + else if (vflag) + warn("connect to %s port %s (%s) failed", host, port, + uflag ? "udp" : "tcp"); + + close(s); + s = -1; + } while ((res0 = res0->ai_next) != NULL); + + freeaddrinfo(res); + + return (s); +} + +int +timeout_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct pollfd pfd; + socklen_t optlen; + int flags = 0, optval; + int ret; + + if (timeout != -1) { + flags = fcntl(s, F_GETFL, 0); + if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) + err(1, "set non-blocking mode"); + } + + if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) { + pfd.fd = s; + pfd.events = POLLOUT; + if ((ret = poll(&pfd, 1, timeout)) == 1) { + optlen = sizeof(optval); + if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR, + &optval, &optlen)) == 0) { + errno = optval; + ret = optval == 0 ? 0 : -1; + } + } else if (ret == 0) { + errno = ETIMEDOUT; + ret = -1; + } else + err(1, "poll failed"); + } + + if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1) + err(1, "restoring flags"); + + return (ret); +} + +/* + * local_listen() + * Returns a socket listening on a local port, binds to specified source + * address. Returns -1 on failure. + */ +int +local_listen(char *host, char *port, struct addrinfo hints) +{ + struct addrinfo *res, *res0; + int s, ret, x = 1; + int error; + + /* Allow nodename to be null. */ + hints.ai_flags |= AI_PASSIVE; + + /* + * In the case of binding to a wildcard address + * default to binding to an ipv4 address. + */ + if (host == NULL && hints.ai_family == AF_UNSPEC) + hints.ai_family = AF_INET; + + if ((error = getaddrinfo(host, port, &hints, &res))) + errx(1, "getaddrinfo: %s", gai_strerror(error)); + + res0 = res; + do { + if ((s = socket(res0->ai_family, res0->ai_socktype, + res0->ai_protocol)) < 0) + continue; + +#ifdef SO_RTABLE + if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_RTABLE, + &rtableid, sizeof(rtableid)) == -1)) + err(1, "setsockopt SO_RTABLE"); +#endif +#ifdef SO_REUSEPORT + ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x)); + if (ret == -1) + err(1, "setsockopt SO_REUSEPORT"); +#endif +#ifdef SO_REUSEADDR + ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); + if (ret == -1) + err(1, "setsockopt SO_REUSEADDR"); +#endif + set_common_sockopts(s, res0->ai_family); + + if (bind(s, (struct sockaddr *)res0->ai_addr, + res0->ai_addrlen) == 0) + break; + + close(s); + s = -1; + } while ((res0 = res0->ai_next) != NULL); + + if (!uflag && s != -1) { + if (listen(s, 1) < 0) + err(1, "listen"); + } + + freeaddrinfo(res); + + return (s); +} + +/* + * readwrite() + * Loop that polls on the network file descriptor and stdin. + */ +void +readwrite(int net_fd) +{ + struct pollfd pfd[4]; + int stdin_fd = STDIN_FILENO; + int stdout_fd = STDOUT_FILENO; + unsigned char netinbuf[BUFSIZE]; + size_t netinbufpos = 0; + unsigned char stdinbuf[BUFSIZE]; + size_t stdinbufpos = 0; + int n, num_fds; + ssize_t ret; + + /* don't read from stdin if requested */ + if (dflag) + stdin_fd = -1; + + /* stdin */ + pfd[POLL_STDIN].fd = stdin_fd; + pfd[POLL_STDIN].events = POLLIN; + + /* network out */ + pfd[POLL_NETOUT].fd = net_fd; + pfd[POLL_NETOUT].events = 0; + + /* network in */ + pfd[POLL_NETIN].fd = net_fd; + pfd[POLL_NETIN].events = POLLIN; + + /* stdout */ + pfd[POLL_STDOUT].fd = stdout_fd; + pfd[POLL_STDOUT].events = 0; + + while (1) { + /* both inputs are gone, buffers are empty, we are done */ + if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1 + && stdinbufpos == 0 && netinbufpos == 0) { + close(net_fd); + return; + } + /* both outputs are gone, we can't continue */ + if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) { + close(net_fd); + return; + } + /* listen and net in gone, queues empty, done */ + if (lflag && pfd[POLL_NETIN].fd == -1 + && stdinbufpos == 0 && netinbufpos == 0) { + close(net_fd); + return; + } + + /* help says -i is for "wait between lines sent". We read and + * write arbitrary amounts of data, and we don't want to start + * scanning for newlines, so this is as good as it gets */ + if (iflag) + sleep(iflag); + + /* poll */ + num_fds = poll(pfd, 4, timeout); + + /* treat poll errors */ + if (num_fds == -1) { + close(net_fd); + err(1, "polling error"); + } + + /* timeout happened */ + if (num_fds == 0) + return; + + /* treat socket error conditions */ + for (n = 0; n < 4; n++) { + if (pfd[n].revents & (POLLERR|POLLNVAL)) { + pfd[n].fd = -1; + } + } + /* reading is possible after HUP */ + if (pfd[POLL_STDIN].events & POLLIN && + pfd[POLL_STDIN].revents & POLLHUP && + ! (pfd[POLL_STDIN].revents & POLLIN)) + pfd[POLL_STDIN].fd = -1; + + if (pfd[POLL_NETIN].events & POLLIN && + pfd[POLL_NETIN].revents & POLLHUP && + ! (pfd[POLL_NETIN].revents & POLLIN)) + pfd[POLL_NETIN].fd = -1; + + if (pfd[POLL_NETOUT].revents & POLLHUP) { + if (Nflag) + shutdown(pfd[POLL_NETOUT].fd, SHUT_WR); + pfd[POLL_NETOUT].fd = -1; + } + /* if HUP, stop watching stdout */ + if (pfd[POLL_STDOUT].revents & POLLHUP) + pfd[POLL_STDOUT].fd = -1; + /* if no net out, stop watching stdin */ + if (pfd[POLL_NETOUT].fd == -1) + pfd[POLL_STDIN].fd = -1; + /* if no stdout, stop watching net in */ + if (pfd[POLL_STDOUT].fd == -1) { + if (pfd[POLL_NETIN].fd != -1) + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + } + + /* try to read from stdin */ + if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) { + ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf, + &stdinbufpos); + /* error or eof on stdin - remove from pfd */ + if (ret == 0 || ret == -1) + pfd[POLL_STDIN].fd = -1; + /* read something - poll net out */ + if (stdinbufpos > 0) + pfd[POLL_NETOUT].events = POLLOUT; + /* filled buffer - remove self from polling */ + if (stdinbufpos == BUFSIZE) + pfd[POLL_STDIN].events = 0; + } + /* try to write to network */ + if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) { + ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf, + &stdinbufpos); + if (ret == -1) + pfd[POLL_NETOUT].fd = -1; + /* buffer empty - remove self from polling */ + if (stdinbufpos == 0) + pfd[POLL_NETOUT].events = 0; + /* buffer no longer full - poll stdin again */ + if (stdinbufpos < BUFSIZE) + pfd[POLL_STDIN].events = POLLIN; + } + /* try to read from network */ + if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) { + ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf, + &netinbufpos); + if (ret == -1) + pfd[POLL_NETIN].fd = -1; + /* eof on net in - remove from pfd */ + if (ret == 0) { + shutdown(pfd[POLL_NETIN].fd, SHUT_RD); + pfd[POLL_NETIN].fd = -1; + } + /* read something - poll stdout */ + if (netinbufpos > 0) + pfd[POLL_STDOUT].events = POLLOUT; + /* filled buffer - remove self from polling */ + if (netinbufpos == BUFSIZE) + pfd[POLL_NETIN].events = 0; + /* handle telnet */ + if (tflag) + atelnet(pfd[POLL_NETIN].fd, netinbuf, + netinbufpos); + } + /* try to write to stdout */ + if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) { + ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf, + &netinbufpos); + if (ret == -1) + pfd[POLL_STDOUT].fd = -1; + /* buffer empty - remove self from polling */ + if (netinbufpos == 0) + pfd[POLL_STDOUT].events = 0; + /* buffer no longer full - poll net in again */ + if (netinbufpos < BUFSIZE) + pfd[POLL_NETIN].events = POLLIN; + } + + /* stdin gone and queue empty? */ + if (pfd[POLL_STDIN].fd == -1 && stdinbufpos == 0) { + if (pfd[POLL_NETOUT].fd != -1 && Nflag) + shutdown(pfd[POLL_NETOUT].fd, SHUT_WR); + pfd[POLL_NETOUT].fd = -1; + } + /* net in gone and queue empty? */ + if (pfd[POLL_NETIN].fd == -1 && netinbufpos == 0) { + pfd[POLL_STDOUT].fd = -1; + } + } +} + +ssize_t +drainbuf(int fd, unsigned char *buf, size_t *bufpos) +{ + ssize_t n; + ssize_t adjust; + + n = write(fd, buf, *bufpos); + /* don't treat EAGAIN, EINTR as error */ + if (n == -1 && (errno == EAGAIN || errno == EINTR)) + n = -2; + if (n <= 0) + return n; + /* adjust buffer */ + adjust = *bufpos - n; + if (adjust > 0) + memmove(buf, buf + n, adjust); + *bufpos -= n; + return n; +} + + +ssize_t +fillbuf(int fd, unsigned char *buf, size_t *bufpos) +{ + size_t num = BUFSIZE - *bufpos; + ssize_t n; + + n = read(fd, buf + *bufpos, num); + /* don't treat EAGAIN, EINTR as error */ + if (n == -1 && (errno == EAGAIN || errno == EINTR)) + n = -2; + if (n <= 0) + return n; + *bufpos += n; + return n; +} + +/* + * fdpass() + * Pass the connected file descriptor to stdout and exit. + */ +void +fdpass(int nfd) +{ +#if defined(HAVE_SENDMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) + struct msghdr msg; +#ifndef HAVE_ACCRIGHTS_IN_MSGHDR + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; +#endif + struct iovec vec; + char ch = '\0'; + struct pollfd pfd; + ssize_t r; + + memset(&msg, 0, sizeof(msg)); +#ifdef HAVE_ACCRIGHTS_IN_MSGHDR + msg.msg_accrights = (caddr_t)&nfd; + msg.msg_accrightslen = sizeof(nfd); +#else + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = (caddr_t)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = nfd; +#endif + + vec.iov_base = &ch; + vec.iov_len = 1; + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + bzero(&pfd, sizeof(pfd)); + pfd.fd = STDOUT_FILENO; + pfd.events = POLLOUT; + for (;;) { + r = sendmsg(STDOUT_FILENO, &msg, 0); + if (r == -1) { + if (errno == EAGAIN || errno == EINTR) { + if (poll(&pfd, 1, -1) == -1) + err(1, "poll"); + continue; + } + err(1, "sendmsg"); + } else if (r != 1) + errx(1, "sendmsg: unexpected return value %zd", r); + else + break; + } + exit(0); +#else + errx(1, "%s: file descriptor passing not supported", __func__); +#endif +} + +/* Deal with RFC 854 WILL/WONT DO/DONT negotiation. */ +void +atelnet(int nfd, unsigned char *buf, unsigned int size) +{ + unsigned char *p, *end; + unsigned char obuf[4]; + + if (size < 3) + return; + end = buf + size - 2; + + for (p = buf; p < end; p++) { + if (*p != IAC) + continue; + + obuf[0] = IAC; + p++; + if ((*p == WILL) || (*p == WONT)) + obuf[1] = DONT; + else if ((*p == DO) || (*p == DONT)) + obuf[1] = WONT; + else + continue; + + p++; + obuf[2] = *p; + if (atomicio(vwrite, nfd, obuf, 3) != 3) + warn("Write Error!"); + } +} + +/* + * build_ports() + * Build an array of ports in portlist[], listing each port + * that we should try to connect to. + */ +void +build_ports(char *p) +{ + const char *errstr; + char *n; + int hi, lo, cp; + int x = 0; + + if ((n = strchr(p, '-')) != NULL) { + *n = '\0'; + n++; + + /* Make sure the ports are in order: lowest->highest. */ + hi = strtonum(n, 1, PORT_MAX, &errstr); + if (errstr) + errx(1, "port number %s: %s", errstr, n); + lo = strtonum(p, 1, PORT_MAX, &errstr); + if (errstr) + errx(1, "port number %s: %s", errstr, p); + + if (lo > hi) { + cp = hi; + hi = lo; + lo = cp; + } + + /* Load ports sequentially. */ + for (cp = lo; cp <= hi; cp++) { + portlist[x] = calloc(1, PORT_MAX_LEN); + if (portlist[x] == NULL) + errx(1, "calloc"); + snprintf(portlist[x], PORT_MAX_LEN, "%d", cp); + x++; + } + + /* Randomly swap ports. */ + if (rflag) { + int y; + char *c; + + for (x = 0; x <= (hi - lo); x++) { + y = (arc4random() & 0xFFFF) % (hi - lo); + c = portlist[x]; + portlist[x] = portlist[y]; + portlist[y] = c; + } + } + } else { + hi = strtonum(p, 1, PORT_MAX, &errstr); + if (errstr) + errx(1, "port number %s: %s", errstr, p); + portlist[0] = strdup(p); + if (portlist[0] == NULL) + errx(1, "strdup"); + } +} + +/* + * udptest() + * Do a few writes to see if the UDP port is there. + * Fails once PF state table is full. + */ +int +udptest(int s) +{ + int i, ret; + + for (i = 0; i <= 3; i++) { + if (write(s, "X", 1) == 1) + ret = 1; + else + ret = -1; + } + return (ret); +} + +void +set_common_sockopts(int s, int af) +{ + int x = 1; + +#ifdef TCP_MD5SIG + if (Sflag) { + if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, + &x, sizeof(x)) == -1) + err(1, "setsockopt"); + } +#endif + if (Dflag) { + if (setsockopt(s, SOL_SOCKET, SO_DEBUG, + &x, sizeof(x)) == -1) + err(1, "setsockopt"); + } +#if defined(IP_TOS) && defined(IPV6_TCLASS) + if (Tflag != -1) { + int proto, option; + + if (af == AF_INET6) { + proto = IPPROTO_IPV6; + option = IPV6_TCLASS; + } else { + proto = IPPROTO_IP; + option = IP_TOS; + } + + if (setsockopt(s, proto, option, &Tflag, sizeof(Tflag)) == -1) + err(1, "set IP ToS"); + } +#endif + if (Iflag) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, + &Iflag, sizeof(Iflag)) == -1) + err(1, "set TCP receive buffer size"); + } + if (Oflag) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + &Oflag, sizeof(Oflag)) == -1) + err(1, "set TCP send buffer size"); + } +} + +int +map_tos(char *s, int *val) +{ +#ifdef IP_TOS + /* DiffServ Codepoints and other TOS mappings */ + const struct toskeywords { + const char *keyword; + int val; + } *t, toskeywords[] = { + { "af11", IPTOS_DSCP_AF11 }, + { "af12", IPTOS_DSCP_AF12 }, + { "af13", IPTOS_DSCP_AF13 }, + { "af21", IPTOS_DSCP_AF21 }, + { "af22", IPTOS_DSCP_AF22 }, + { "af23", IPTOS_DSCP_AF23 }, + { "af31", IPTOS_DSCP_AF31 }, + { "af32", IPTOS_DSCP_AF32 }, + { "af33", IPTOS_DSCP_AF33 }, + { "af41", IPTOS_DSCP_AF41 }, + { "af42", IPTOS_DSCP_AF42 }, + { "af43", IPTOS_DSCP_AF43 }, + { "critical", IPTOS_PREC_CRITIC_ECP }, + { "cs0", IPTOS_DSCP_CS0 }, + { "cs1", IPTOS_DSCP_CS1 }, + { "cs2", IPTOS_DSCP_CS2 }, + { "cs3", IPTOS_DSCP_CS3 }, + { "cs4", IPTOS_DSCP_CS4 }, + { "cs5", IPTOS_DSCP_CS5 }, + { "cs6", IPTOS_DSCP_CS6 }, + { "cs7", IPTOS_DSCP_CS7 }, + { "ef", IPTOS_DSCP_EF }, + { "inetcontrol", IPTOS_PREC_INTERNETCONTROL }, + { "lowdelay", IPTOS_LOWDELAY }, + { "netcontrol", IPTOS_PREC_NETCONTROL }, + { "reliability", IPTOS_RELIABILITY }, + { "throughput", IPTOS_THROUGHPUT }, + { NULL, -1 }, + }; + + for (t = toskeywords; t->keyword != NULL; t++) { + if (strcmp(s, t->keyword) == 0) { + *val = t->val; + return (1); + } + } +#endif + + return (0); +} + +void +report_connect(const struct sockaddr *sa, socklen_t salen) +{ + char remote_host[NI_MAXHOST]; + char remote_port[NI_MAXSERV]; + int herr; + int flags = NI_NUMERICSERV; + + if (nflag) + flags |= NI_NUMERICHOST; + + if ((herr = getnameinfo(sa, salen, + remote_host, sizeof(remote_host), + remote_port, sizeof(remote_port), + flags)) != 0) { + if (herr == EAI_SYSTEM) + err(1, "getnameinfo"); + else + errx(1, "getnameinfo: %s", gai_strerror(herr)); + } + + fprintf(stderr, + "Connection from %s %s " + "received!\n", remote_host, remote_port); +} + +void +help(void) +{ + usage(0); + fprintf(stderr, "\tCommand Summary:\n\ + \t-4 Use IPv4\n\ + \t-6 Use IPv6\n\ + \t-D Enable the debug socket option\n\ + \t-d Detach from stdin\n\ + \t-F Pass socket fd\n\ + \t-h This help text\n\ + \t-I length TCP receive buffer length\n\ + \t-i secs\t Delay interval for lines sent, ports scanned\n\ + \t-k Keep inbound sockets open for multiple connects\n\ + \t-l Listen mode, for inbound connects\n\ + \t-N Shutdown the network socket after EOF on stdin\n\ + \t-n Suppress name/port resolutions\n\ + \t-O length TCP send buffer length\n\ + \t-P proxyuser\tUsername for proxy authentication\n\ + \t-p port\t Specify local port for remote connects\n\ + \t-r Randomize remote ports\n\ + \t-S Enable the TCP MD5 signature option\n\ + \t-s addr\t Local source address\n\ + \t-T toskeyword\tSet IP Type of Service\n\ + \t-t Answer TELNET negotiation\n\ + \t-U Use UNIX domain socket\n\ + \t-u UDP mode\n\ + \t-V rtable Specify alternate routing table\n\ + \t-v Verbose\n\ + \t-w secs\t Timeout for connects and final net reads\n\ + \t-X proto Proxy protocol: \"4\", \"5\" (SOCKS) or \"connect\"\n\ + \t-x addr[:port]\tSpecify proxy address and port\n\ + \t-z Zero-I/O mode [used for scanning]\n\ + Port numbers can be individual or ranges: lo-hi [inclusive]\n"); + exit(1); +} + +void +usage(int ret) +{ + fprintf(stderr, + "usage: nc [-46DdFhklNnrStUuvz] [-I length] [-i interval] [-O length]\n" + "\t [-P proxy_username] [-p source_port] [-s source] [-T toskeyword]\n" + "\t [-V rtable] [-w timeout] [-X proxy_protocol]\n" + "\t [-x proxy_address[:port]] [destination] [port]\n"); + if (ret) + exit(1); +} + +/* *** src/usr.bin/nc/socks.c *** */ + + +/* $OpenBSD: socks.c,v 1.20 2012/03/08 09:56:28 espie Exp $ */ + +/* + * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. + * Copyright (c) 2004, 2005 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SOCKS_PORT "1080" +#define HTTP_PROXY_PORT "3128" +#define HTTP_MAXHDRS 64 +#define SOCKS_V5 5 +#define SOCKS_V4 4 +#define SOCKS_NOAUTH 0 +#define SOCKS_NOMETHOD 0xff +#define SOCKS_CONNECT 1 +#define SOCKS_IPV4 1 +#define SOCKS_DOMAIN 3 +#define SOCKS_IPV6 4 + +int remote_connect(const char *, const char *, struct addrinfo); +int socks_connect(const char *, const char *, struct addrinfo, + const char *, const char *, struct addrinfo, int, + const char *); + +static int +decode_addrport(const char *h, const char *p, struct sockaddr *addr, + socklen_t addrlen, int v4only, int numeric) +{ + int r; + struct addrinfo hints, *res; + + bzero(&hints, sizeof(hints)); + hints.ai_family = v4only ? PF_INET : PF_UNSPEC; + hints.ai_flags = numeric ? AI_NUMERICHOST : 0; + hints.ai_socktype = SOCK_STREAM; + r = getaddrinfo(h, p, &hints, &res); + /* Don't fatal when attempting to convert a numeric address */ + if (r != 0) { + if (!numeric) { + errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p, + gai_strerror(r)); + } + return (-1); + } + if (addrlen < res->ai_addrlen) { + freeaddrinfo(res); + errx(1, "internal error: addrlen < res->ai_addrlen"); + } + memcpy(addr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return (0); +} + +static int +proxy_read_line(int fd, char *buf, size_t bufsz) +{ + size_t off; + + for(off = 0;;) { + if (off >= bufsz) + errx(1, "proxy read too long"); + if (atomicio(read, fd, buf + off, 1) != 1) + err(1, "proxy read"); + /* Skip CR */ + if (buf[off] == '\r') + continue; + if (buf[off] == '\n') { + buf[off] = '\0'; + break; + } + off++; + } + return (off); +} + +static const char * +getproxypass(const char *proxyuser, const char *proxyhost) +{ + char prompt[512]; + static char pw[256]; + + snprintf(prompt, sizeof(prompt), "Proxy password for %s@%s: ", + proxyuser, proxyhost); + if (readpassphrase(prompt, pw, sizeof(pw), RPP_REQUIRE_TTY) == NULL) + errx(1, "Unable to read proxy passphrase"); + return (pw); +} + +int +socks_connect(const char *host, const char *port, + struct addrinfo hints __attribute__ ((__unused__)), + const char *proxyhost, const char *proxyport, struct addrinfo proxyhints, + int socksv, const char *proxyuser) +{ + int proxyfd, r, authretry = 0; + size_t hlen, wlen = 0; + unsigned char buf[1024]; + size_t cnt; + struct sockaddr_storage addr; + struct sockaddr_in *in4 = (struct sockaddr_in *)&addr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr; + in_port_t serverport; + const char *proxypass = NULL; + + if (proxyport == NULL) + proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT; + + /* Abuse API to lookup port */ + if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr, + sizeof(addr), 1, 1) == -1) + errx(1, "unknown port \"%.64s\"", port); + serverport = in4->sin_port; + + again: + if (authretry++ > 3) + errx(1, "Too many authentication failures"); + + proxyfd = remote_connect(proxyhost, proxyport, proxyhints); + + if (proxyfd < 0) + return (-1); + + if (socksv == 5) { + if (decode_addrport(host, port, (struct sockaddr *)&addr, + sizeof(addr), 0, 1) == -1) + addr.ss_family = 0; /* used in switch below */ + + /* Version 5, one method: no authentication */ + buf[0] = SOCKS_V5; + buf[1] = 1; + buf[2] = SOCKS_NOAUTH; + cnt = atomicio(vwrite, proxyfd, buf, 3); + if (cnt != 3) + err(1, "write failed (%zu/3)", cnt); + + cnt = atomicio(read, proxyfd, buf, 2); + if (cnt != 2) + err(1, "read failed (%zu/3)", cnt); + + if (buf[1] == SOCKS_NOMETHOD) + errx(1, "authentication method negotiation failed"); + + switch (addr.ss_family) { + case 0: + /* Version 5, connect: domain name */ + + /* Max domain name length is 255 bytes */ + hlen = strlen(host); + if (hlen > 255) + errx(1, "host name too long for SOCKS5"); + buf[0] = SOCKS_V5; + buf[1] = SOCKS_CONNECT; + buf[2] = 0; + buf[3] = SOCKS_DOMAIN; + buf[4] = hlen; + memcpy(buf + 5, host, hlen); + memcpy(buf + 5 + hlen, &serverport, sizeof serverport); + wlen = 7 + hlen; + break; + case AF_INET: + /* Version 5, connect: IPv4 address */ + buf[0] = SOCKS_V5; + buf[1] = SOCKS_CONNECT; + buf[2] = 0; + buf[3] = SOCKS_IPV4; + memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); + memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port); + wlen = 10; + break; + case AF_INET6: + /* Version 5, connect: IPv6 address */ + buf[0] = SOCKS_V5; + buf[1] = SOCKS_CONNECT; + buf[2] = 0; + buf[3] = SOCKS_IPV6; + memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr); + memcpy(buf + 20, &in6->sin6_port, + sizeof in6->sin6_port); + wlen = 22; + break; + default: + errx(1, "internal error: silly AF"); + } + + cnt = atomicio(vwrite, proxyfd, buf, wlen); + if (cnt != wlen) + err(1, "write failed (%zu/%zu)", cnt, wlen); + + cnt = atomicio(read, proxyfd, buf, 4); + if (cnt != 4) + err(1, "read failed (%zu/4)", cnt); + if (buf[1] != 0) + errx(1, "connection failed, SOCKS error %d", buf[1]); + switch (buf[3]) { + case SOCKS_IPV4: + cnt = atomicio(read, proxyfd, buf + 4, 6); + if (cnt != 6) + err(1, "read failed (%zu/6)", cnt); + break; + case SOCKS_IPV6: + cnt = atomicio(read, proxyfd, buf + 4, 18); + if (cnt != 18) + err(1, "read failed (%zu/18)", cnt); + break; + default: + errx(1, "connection failed, unsupported address type"); + } + } else if (socksv == 4) { + /* This will exit on lookup failure */ + decode_addrport(host, port, (struct sockaddr *)&addr, + sizeof(addr), 1, 0); + + /* Version 4 */ + buf[0] = SOCKS_V4; + buf[1] = SOCKS_CONNECT; /* connect */ + memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port); + memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr); + buf[8] = 0; /* empty username */ + wlen = 9; + + cnt = atomicio(vwrite, proxyfd, buf, wlen); + if (cnt != wlen) + err(1, "write failed (%zu/%zu)", cnt, wlen); + + cnt = atomicio(read, proxyfd, buf, 8); + if (cnt != 8) + err(1, "read failed (%zu/8)", cnt); + if (buf[1] != 90) + errx(1, "connection failed, SOCKS error %d", buf[1]); + } else if (socksv == -1) { + /* HTTP proxy CONNECT */ + + /* Disallow bad chars in hostname */ + if (strcspn(host, "\r\n\t []:") != strlen(host)) + errx(1, "Invalid hostname"); + + /* Try to be sane about numeric IPv6 addresses */ + if (strchr(host, ':') != NULL) { + r = snprintf(buf, sizeof(buf), + "CONNECT [%s]:%d HTTP/1.0\r\n", + host, ntohs(serverport)); + } else { + r = snprintf(buf, sizeof(buf), + "CONNECT %s:%d HTTP/1.0\r\n", + host, ntohs(serverport)); + } + if (r == -1 || (size_t)r >= sizeof(buf)) + errx(1, "hostname too long"); + r = strlen(buf); + + cnt = atomicio(vwrite, proxyfd, buf, r); + if (cnt != (size_t)r) + err(1, "write failed (%zu/%d)", cnt, r); + + if (authretry > 1) { + char resp[1024]; + + proxypass = getproxypass(proxyuser, proxyhost); + r = snprintf(buf, sizeof(buf), "%s:%s", + proxyuser, proxypass); + if (r == -1 || (size_t)r >= sizeof(buf) || + b64_ntop(buf, strlen(buf), resp, + sizeof(resp)) == -1) + errx(1, "Proxy username/password too long"); + r = snprintf(buf, sizeof(buf), "Proxy-Authorization: " + "Basic %s\r\n", resp); + if (r == -1 || (size_t)r >= sizeof(buf)) + errx(1, "Proxy auth response too long"); + r = strlen(buf); + if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != (size_t)r) + err(1, "write failed (%zu/%d)", cnt, r); + } + + /* Terminate headers */ + if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2) + err(1, "write failed (2/%d)", r); + + /* Read status reply */ + proxy_read_line(proxyfd, buf, sizeof(buf)); + if (proxyuser != NULL && + strncmp(buf, "HTTP/1.0 407 ", 12) == 0) { + if (authretry > 1) { + fprintf(stderr, "Proxy authentication " + "failed\n"); + } + close(proxyfd); + goto again; + } else if (strncmp(buf, "HTTP/1.0 200 ", 12) != 0 && + strncmp(buf, "HTTP/1.1 200 ", 12) != 0) + errx(1, "Proxy error: \"%s\"", buf); + + /* Headers continue until we hit an empty line */ + for (r = 0; r < HTTP_MAXHDRS; r++) { + proxy_read_line(proxyfd, buf, sizeof(buf)); + if (*buf == '\0') + break; + } + if (*buf != '\0') + errx(1, "Too many proxy headers received"); + } else + errx(1, "Unknown proxy protocol %d", socksv); + + return (proxyfd); +} + diff --git a/aosp/external/openssh/regress/percent.sh b/aosp/external/openssh/regress/percent.sh new file mode 100644 index 0000000000000000000000000000000000000000..bb81779a013db27652f23ead7831a7cce17c7a8f --- /dev/null +++ b/aosp/external/openssh/regress/percent.sh @@ -0,0 +1,120 @@ +# $OpenBSD: percent.sh,v 1.14 2022/02/20 03:47:26 dtucker Exp $ +# Placed in the Public Domain. + +tid="percent expansions" + +if [ -x "/usr/xpg4/bin/id" ]; then + PATH=/usr/xpg4/bin:$PATH + export PATH +fi + +USER=`id -u -n` +USERID=`id -u` +HOST=`hostname | cut -f1 -d.` +HOSTNAME=`hostname` + +# Localcommand is evaluated after connection because %T is not available +# until then. Because of this we use a different method of exercising it, +# and we can't override the remote user otherwise authentication will fail. +# We also have to explicitly enable it. +echo "permitlocalcommand yes" >> $OBJ/ssh_proxy + +trial() +{ + opt="$1"; arg="$2" + expect=`echo "$3" | sed 's|^//|/|'` # approximate realpath + + trace "test $opt=$arg $expect" + rm -f $OBJ/actual + got="" + case "$opt" in + localcommand) + ${SSH} -F $OBJ/ssh_proxy -o $opt="echo '$arg' >$OBJ/actual" \ + somehost true + got=`cat $OBJ/actual` + ;; + userknownhostsfile) + # Move the userknownhosts file to what the expansion says, + # make sure ssh works then put it back. + mv "$OBJ/known_hosts" "$OBJ/$expect" + ${SSH} -F $OBJ/ssh_proxy -o $opt="$OBJ/$arg" somehost true && \ + got="$expect" + mv "$OBJ/$expect" "$OBJ/known_hosts" + ;; + matchexec) + (cat $OBJ/ssh_proxy && \ + echo "Match Exec \"echo '$arg' >$OBJ/actual\"") \ + >$OBJ/ssh_proxy_match + ${SSH} -F $OBJ/ssh_proxy_match remuser@somehost true || true + got=`cat $OBJ/actual` + ;; + *forward) + # LocalForward and RemoteForward take two args and only + # operate on Unix domain socket paths + got=`${SSH} -F $OBJ/ssh_proxy -o $opt="/$arg /$arg" -G \ + remuser@somehost | awk '$1=="'$opt'"{print $2" "$3}'` + expect="/$expect /$expect" + ;; + *) + got=`${SSH} -F $OBJ/ssh_proxy -o $opt="$arg" -G \ + remuser@somehost | awk '$1=="'$opt'"{print $2}'` + esac + if [ "$got" != "$expect" ]; then + fail "$opt=$arg expect $expect got $got" + fi +} + +for i in matchexec localcommand remotecommand controlpath identityagent \ + forwardagent localforward remoteforward userknownhostsfile; do + verbose $tid $i percent + case "$i" in + localcommand|userknownhostsfile) + # Any test that's going to actually make a connection needs + # to use the real username. + REMUSER=$USER ;; + *) + REMUSER=remuser ;; + esac + if [ "$i" = "$localcommand" ]; then + trial $i '%T' NONE + fi + # Matches implementation in readconf.c:ssh_connection_hash() + HASH=`printf "${HOSTNAME}127.0.0.1${PORT}$REMUSER" | + $OPENSSL_BIN sha1 | cut -f2 -d' '` + trial $i '%%' '%' + trial $i '%C' $HASH + trial $i '%i' $USERID + trial $i '%h' 127.0.0.1 + trial $i '%L' $HOST + trial $i '%l' $HOSTNAME + trial $i '%n' somehost + trial $i '%k' localhost-with-alias + trial $i '%p' $PORT + trial $i '%r' $REMUSER + trial $i '%u' $USER + # We can't specify a full path outside the regress dir, so skip tests + # containing %d for UserKnownHostsFile + if [ "$i" != "userknownhostsfile" ]; then + trial $i '%d' $HOME + trial $i '%%/%C/%i/%h/%d/%L/%l/%n/%p/%r/%u' \ + "%/$HASH/$USERID/127.0.0.1/$HOME/$HOST/$HOSTNAME/somehost/$PORT/$REMUSER/$USER" + fi +done + +# Subset of above since we don't expand shell-style variables on anything that +# runs a command because the shell will expand those. +for i in controlpath identityagent forwardagent localforward remoteforward \ + userknownhostsfile; do + verbose $tid $i dollar + FOO=bar + export FOO + trial $i '${FOO}' $FOO +done + + +# A subset of options support tilde expansion +for i in controlpath identityagent forwardagent; do + verbose $tid $i tilde + trial $i '~' $HOME/ + trial $i '~/.ssh' $HOME/.ssh +done diff --git a/aosp/external/openssh/regress/portnum.sh b/aosp/external/openssh/regress/portnum.sh new file mode 100644 index 0000000000000000000000000000000000000000..c56b869a31bd08e26b686e2ef02b90c90e1e9e2b --- /dev/null +++ b/aosp/external/openssh/regress/portnum.sh @@ -0,0 +1,34 @@ +# $OpenBSD: portnum.sh,v 1.2 2013/05/17 10:34:30 dtucker Exp $ +# Placed in the Public Domain. + +tid="port number parsing" + +badport() { + port=$1 + verbose "$tid: invalid port $port" + if ${SSH} -F $OBJ/ssh_proxy -p $port somehost true 2>/dev/null ; then + fail "$tid accepted invalid port $port" + fi +} +goodport() { + port=$1 + verbose "$tid: valid port $port" + if ${SSH} -F $OBJ/ssh_proxy -p $port somehost true 2>/dev/null ; then + : + else + fail "$tid rejected valid port $port" + fi +} + +badport 0 +badport 65536 +badport 131073 +badport 2000blah +badport blah2000 + +goodport 1 +goodport 22 +goodport 2222 +goodport 22222 +goodport 65535 + diff --git a/aosp/external/openssh/regress/principals-command.sh b/aosp/external/openssh/regress/principals-command.sh new file mode 100644 index 0000000000000000000000000000000000000000..8278711e86f58bbfc93da44449e3dc7de784345a --- /dev/null +++ b/aosp/external/openssh/regress/principals-command.sh @@ -0,0 +1,168 @@ +# $OpenBSD: principals-command.sh,v 1.14 2021/09/30 05:26:26 dtucker Exp $ +# Placed in the Public Domain. + +tid="authorized principals command" + +rm -f $OBJ/user_ca_key* $OBJ/cert_user_key* +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +if [ -z "$SUDO" -a ! -w /var/run ]; then + skip "need SUDO to create file in /var/run, test won't work without" +fi + +case "$SSH_KEYTYPES" in + *ssh-rsa*) userkeytype=rsa ;; + *) userkeytype=ed25519 ;; +esac + +SERIAL=$$ + +# Create a CA key and a user certificate. +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key || \ + fatal "ssh-keygen of user_ca_key failed" +${SSHKEYGEN} -q -N '' -t ${userkeytype} -f $OBJ/cert_user_key || \ + fatal "ssh-keygen of cert_user_key failed" +${SSHKEYGEN} -q -s $OBJ/user_ca_key -I "Joanne User" \ + -z $$ -n ${USER},mekmitasdigoat $OBJ/cert_user_key || \ + fatal "couldn't sign cert_user_key" + +CERT_BODY=`cat $OBJ/cert_user_key-cert.pub | awk '{ print $2 }'` +CA_BODY=`cat $OBJ/user_ca_key.pub | awk '{ print $2 }'` +CERT_FP=`${SSHKEYGEN} -lf $OBJ/cert_user_key-cert.pub | awk '{ print $2 }'` +CA_FP=`${SSHKEYGEN} -lf $OBJ/user_ca_key.pub | awk '{ print $2 }'` + +# Establish a AuthorizedPrincipalsCommand in /var/run where it will have +# acceptable directory permissions. +PRINCIPALS_COMMAND="/var/run/principals_command_${LOGNAME}.$$" +trap "$SUDO rm -f ${PRINCIPALS_COMMAND}" 0 +cat << _EOF | $SUDO sh -c "cat > '$PRINCIPALS_COMMAND'" +#!/bin/sh +test "x\$1" != "x${LOGNAME}" && exit 1 +test "x\$2" != "xssh-${userkeytype}-cert-v01@openssh.com" && exit 1 +test "x\$3" != "xssh-ed25519" && exit 1 +test "x\$4" != "xJoanne User" && exit 1 +test "x\$5" != "x${SERIAL}" && exit 1 +test "x\$6" != "x${CA_FP}" && exit 1 +test "x\$7" != "x${CERT_FP}" && exit 1 +test "x\$8" != "x${CERT_BODY}" && exit 1 +test "x\$9" != "x${CA_BODY}" && exit 1 +test -f "$OBJ/authorized_principals_${LOGNAME}" && + exec cat "$OBJ/authorized_principals_${LOGNAME}" +_EOF +test $? -eq 0 || fatal "couldn't prepare principals command" +$SUDO chmod 0755 "$PRINCIPALS_COMMAND" + +if ! $OBJ/check-perm -m keys-command $PRINCIPALS_COMMAND ; then + echo "skipping: $PRINCIPALS_COMMAND is unsuitable as " \ + "AuthorizedPrincipalsCommand" + $SUDO rm -f $PRINCIPALS_COMMAND + exit 0 +fi + +if [ ! -x $PRINCIPALS_COMMAND ]; then + skip "$PRINCIPALS_COMMAND not executable " \ + "(/var/run mounted noexec?)" +fi + +# Test explicitly-specified principals +# Setup for AuthorizedPrincipalsCommand +rm -f $OBJ/authorized_keys_$USER +( + cat $OBJ/sshd_proxy_bak + echo "AuthorizedKeysFile none" + echo "AuthorizedPrincipalsCommand $PRINCIPALS_COMMAND" \ + "%u %t %T %i %s %F %f %k %K" + echo "AuthorizedPrincipalsCommandUser ${LOGNAME}" + echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" +) > $OBJ/sshd_proxy + +# XXX test missing command +# XXX test failing command + +# Empty authorized_principals +verbose "$tid: empty authorized_principals" +echo > $OBJ/authorized_principals_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" +fi + +# Wrong authorized_principals +verbose "$tid: wrong authorized_principals" +echo gregorsamsa > $OBJ/authorized_principals_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" +fi + +# Correct authorized_principals +verbose "$tid: correct authorized_principals" +echo mekmitasdigoat > $OBJ/authorized_principals_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -ne 0 ]; then + fail "ssh cert connect failed" +fi + +# authorized_principals with bad key option +verbose "$tid: authorized_principals bad key opt" +echo 'blah mekmitasdigoat' > $OBJ/authorized_principals_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" +fi + +# authorized_principals with command=false +verbose "$tid: authorized_principals command=false" +echo 'command="false" mekmitasdigoat' > \ + $OBJ/authorized_principals_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" +fi + + +# authorized_principals with command=true +verbose "$tid: authorized_principals command=true" +echo 'command="true" mekmitasdigoat' > \ + $OBJ/authorized_principals_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost false >/dev/null 2>&1 +if [ $? -ne 0 ]; then + fail "ssh cert connect failed" +fi + +# Setup for principals= key option +# TODO: remove? +rm -f $OBJ/authorized_principals_$USER +( + cat $OBJ/sshd_proxy_bak +) > $OBJ/sshd_proxy + +# Wrong principals list +verbose "$tid: wrong principals key option" +( + printf 'cert-authority,principals="gregorsamsa" ' + cat $OBJ/user_ca_key.pub +) > $OBJ/authorized_keys_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -eq 0 ]; then + fail "ssh cert connect succeeded unexpectedly" +fi + +# Correct principals list +verbose "$tid: correct principals key option" +( + printf 'cert-authority,principals="mekmitasdigoat" ' + cat $OBJ/user_ca_key.pub +) > $OBJ/authorized_keys_$USER +${SSH} -i $OBJ/cert_user_key \ + -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 +if [ $? -ne 0 ]; then + fail "ssh cert connect failed" +fi diff --git a/aosp/external/openssh/regress/proto-mismatch.sh b/aosp/external/openssh/regress/proto-mismatch.sh new file mode 100644 index 0000000000000000000000000000000000000000..6ab28c9a7ad9411949a13952956db7a0d3b62e52 --- /dev/null +++ b/aosp/external/openssh/regress/proto-mismatch.sh @@ -0,0 +1,17 @@ +# $OpenBSD: proto-mismatch.sh,v 1.5 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="protocol version mismatch" + +mismatch () +{ + client=$2 + banner=`echo ${client} | ${SSHD} -i -f ${OBJ}/sshd_proxy` + r=$? + trace "sshd prints ${banner}" + if [ $r -ne 255 ]; then + fail "sshd prints ${banner} but accepts version ${client}" + fi +} + +mismatch SSH-1.5-HALLO diff --git a/aosp/external/openssh/regress/proto-version.sh b/aosp/external/openssh/regress/proto-version.sh new file mode 100644 index 0000000000000000000000000000000000000000..1f33b1f00b773af7b77b11a8bde64503d4af63e9 --- /dev/null +++ b/aosp/external/openssh/regress/proto-version.sh @@ -0,0 +1,30 @@ +# $OpenBSD: proto-version.sh,v 1.7 2017/06/07 01:48:15 djm Exp $ +# Placed in the Public Domain. + +tid="sshd version with different protocol combinations" + +# we just start sshd in inetd mode and check the banner +check_version () +{ + expect=$1 + banner=`printf '' | ${SSHD} -i -f ${OBJ}/sshd_proxy` + case ${banner} in + SSH-1.99-*) + proto=199 + ;; + SSH-2.0-*) + proto=20 + ;; + SSH-1.5-*) + proto=15 + ;; + *) + proto=0 + ;; + esac + if [ ${expect} -ne ${proto} ]; then + fail "wrong protocol version ${banner}" + fi +} + +check_version 20 diff --git a/aosp/external/openssh/regress/proxy-connect.sh b/aosp/external/openssh/regress/proxy-connect.sh new file mode 100644 index 0000000000000000000000000000000000000000..8847fe0c664b825ec5a1e024aa6f494969cda3bd --- /dev/null +++ b/aosp/external/openssh/regress/proxy-connect.sh @@ -0,0 +1,27 @@ +# $OpenBSD: proxy-connect.sh,v 1.12 2020/01/23 11:19:12 dtucker Exp $ +# Placed in the Public Domain. + +tid="proxy connect" + +if [ "`${SSH} -Q compression`" = "none" ]; then + comp="no" +else + comp="no yes" +fi + +for c in $comp; do + verbose "plain username comp=$c" + opts="-oCompression=$c -F $OBJ/ssh_proxy" + SSH_CONNECTION=`${SSH} $opts 999.999.999.999 'echo $SSH_CONNECTION'` + if [ $? -ne 0 ]; then + fail "ssh proxyconnect comp=$c failed" + fi + if [ "$SSH_CONNECTION" != "UNKNOWN 65535 UNKNOWN 65535" ]; then + fail "bad SSH_CONNECTION comp=$c: " \ + "$SSH_CONNECTION" + fi +done + +verbose "username with style" +${SSH} -F $OBJ/ssh_proxy ${USER}:style@999.999.999.999 true || \ + fail "ssh proxyconnect failed" diff --git a/aosp/external/openssh/regress/putty-ciphers.sh b/aosp/external/openssh/regress/putty-ciphers.sh new file mode 100644 index 0000000000000000000000000000000000000000..5b8e25a27199f47eb2d9a1d0f1dd7e33e483c413 --- /dev/null +++ b/aosp/external/openssh/regress/putty-ciphers.sh @@ -0,0 +1,32 @@ +# $OpenBSD: putty-ciphers.sh,v 1.11 2021/09/01 03:16:06 dtucker Exp $ +# Placed in the Public Domain. + +tid="putty ciphers" + +if test "x$REGRESS_INTEROP_PUTTY" != "xyes" ; then + skip "putty interop tests not enabled" +fi + +# Re-enable ssh-rsa on older PuTTY versions. +oldver="`${PLINK} --version | awk '/plink: Release/{if ($3<0.76)print "yes"}'`" +if [ "x$oldver" = "xyes" ]; then + echo "HostKeyAlgorithms +ssh-rsa" >> ${OBJ}/sshd_proxy + echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> ${OBJ}/sshd_proxy +fi + +for c in aes 3des aes128-ctr aes192-ctr aes256-ctr chacha20 ; do + verbose "$tid: cipher $c" + cp ${OBJ}/.putty/sessions/localhost_proxy \ + ${OBJ}/.putty/sessions/cipher_$c + echo "Cipher=$c" >> ${OBJ}/.putty/sessions/cipher_$c + + rm -f ${COPY} + env HOME=$PWD ${PLINK} -load cipher_$c -batch -i ${OBJ}/putty.rsa2 \ + cat ${DATA} > ${COPY} + if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy" +done +rm -f ${COPY} + diff --git a/aosp/external/openssh/regress/putty-kex.sh b/aosp/external/openssh/regress/putty-kex.sh new file mode 100644 index 0000000000000000000000000000000000000000..c75802a06103207fe169a89f3015b72442f92ab5 --- /dev/null +++ b/aosp/external/openssh/regress/putty-kex.sh @@ -0,0 +1,28 @@ +# $OpenBSD: putty-kex.sh,v 1.9 2021/09/01 03:16:06 dtucker Exp $ +# Placed in the Public Domain. + +tid="putty KEX" + +if test "x$REGRESS_INTEROP_PUTTY" != "xyes" ; then + skip "putty interop tests not enabled" +fi + +# Re-enable ssh-rsa on older PuTTY versions. +oldver="`${PLINK} --version | awk '/plink: Release/{if ($3<0.76)print "yes"}'`" +if [ "x$oldver" = "xyes" ]; then + echo "HostKeyAlgorithms +ssh-rsa" >> ${OBJ}/sshd_proxy + echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> ${OBJ}/sshd_proxy +fi + +for k in dh-gex-sha1 dh-group1-sha1 dh-group14-sha1 ecdh ; do + verbose "$tid: kex $k" + cp ${OBJ}/.putty/sessions/localhost_proxy \ + ${OBJ}/.putty/sessions/kex_$k + echo "KEX=$k" >> ${OBJ}/.putty/sessions/kex_$k + + env HOME=$PWD ${PLINK} -load kex_$k -batch -i ${OBJ}/putty.rsa2 true + if [ $? -ne 0 ]; then + fail "KEX $k failed" + fi +done + diff --git a/aosp/external/openssh/regress/putty-transfer.sh b/aosp/external/openssh/regress/putty-transfer.sh new file mode 100644 index 0000000000000000000000000000000000000000..a6864f9515a73961e5db504ec57b1221026e0677 --- /dev/null +++ b/aosp/external/openssh/regress/putty-transfer.sh @@ -0,0 +1,50 @@ +# $OpenBSD: putty-transfer.sh,v 1.11 2021/09/01 03:16:06 dtucker Exp $ +# Placed in the Public Domain. + +tid="putty transfer data" + +if test "x$REGRESS_INTEROP_PUTTY" != "xyes" ; then + skip "putty interop tests not enabled" +fi + +# Re-enable ssh-rsa on older PuTTY versions. +oldver="`${PLINK} --version | awk '/plink: Release/{if ($3<0.76)print "yes"}'`" +if [ "x$oldver" = "xyes" ]; then + echo "HostKeyAlgorithms +ssh-rsa" >> ${OBJ}/sshd_proxy + echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> ${OBJ}/sshd_proxy +fi + +if [ "`${SSH} -Q compression`" = "none" ]; then + comp="0" +else + comp="0 1" +fi + +for c in $comp; do + verbose "$tid: compression $c" + rm -f ${COPY} + cp ${OBJ}/.putty/sessions/localhost_proxy \ + ${OBJ}/.putty/sessions/compression_$c + echo "Compression=$c" >> ${OBJ}/.putty/sessions/kex_$k + env HOME=$PWD ${PLINK} -load compression_$c -batch \ + -i ${OBJ}/putty.rsa2 cat ${DATA} > ${COPY} + if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy" + + for s in 10 100 1k 32k 64k 128k 256k; do + trace "compression $c dd-size ${s}" + rm -f ${COPY} + dd if=$DATA obs=${s} 2> /dev/null | \ + env HOME=$PWD ${PLINK} -load compression_$c \ + -batch -i ${OBJ}/putty.rsa2 \ + "cat > ${COPY}" + if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" + fi + cmp $DATA ${COPY} || fail "corrupted copy" + done +done +rm -f ${COPY} + diff --git a/aosp/external/openssh/regress/reconfigure.sh b/aosp/external/openssh/regress/reconfigure.sh new file mode 100644 index 0000000000000000000000000000000000000000..d5b4e9808fcc6d606ca09fa52058586ee91ad3c9 --- /dev/null +++ b/aosp/external/openssh/regress/reconfigure.sh @@ -0,0 +1,65 @@ +# $OpenBSD: reconfigure.sh,v 1.9 2021/06/10 09:46:28 dtucker Exp $ +# Placed in the Public Domain. + +tid="simple connect after reconfigure" + +# we need the full path to sshd for -HUP +if test "x$USE_VALGRIND" = "x" ; then + case $SSHD in + /*) + # full path is OK + ;; + *) + # otherwise make fully qualified + SSHD=$OBJ/$SSHD + esac +fi + +start_sshd + +trace "connect before restart" +${SSH} -F $OBJ/ssh_config somehost true +if [ $? -ne 0 ]; then + fail "ssh connect with failed before reconfigure" +fi + +PID=`$SUDO cat $PIDFILE` +rm -f $PIDFILE +$SUDO kill -HUP $PID + +trace "wait for sshd to restart" +i=0; +while [ ! -f $PIDFILE -a $i -lt 10 ]; do + i=`expr $i + 1` + sleep $i +done + +test -f $PIDFILE || fatal "sshd did not restart" + +trace "connect after restart" +${SSH} -F $OBJ/ssh_config somehost true +if [ $? -ne 0 ]; then + fail "ssh connect with failed after reconfigure" +fi + +trace "reconfigure with active clients" +${SSH} -F $OBJ/ssh_config somehost sleep 10 # authenticated client +${NC} -d 127.0.0.1 $PORT >/dev/null & # unauthenticated client +PID=`$SUDO cat $PIDFILE` +rm -f $PIDFILE +$SUDO kill -HUP $PID + +trace "wait for sshd to restart" +i=0; +while [ ! -f $PIDFILE -a $i -lt 10 ]; do + i=`expr $i + 1` + sleep $i +done + +test -f $PIDFILE || fatal "sshd did not restart" + +trace "connect after restart with active clients" +${SSH} -F $OBJ/ssh_config somehost true +if [ $? -ne 0 ]; then + fail "ssh connect with failed after reconfigure" +fi diff --git a/aosp/external/openssh/regress/reexec.sh b/aosp/external/openssh/regress/reexec.sh new file mode 100644 index 0000000000000000000000000000000000000000..8966ba524e6004154835d97c6033f77e1672b568 --- /dev/null +++ b/aosp/external/openssh/regress/reexec.sh @@ -0,0 +1,57 @@ +# $OpenBSD: reexec.sh,v 1.12 2017/08/07 03:52:55 dtucker Exp $ +# Placed in the Public Domain. + +tid="reexec tests" + +SSHD_ORIG=$SSHD +SSHD_COPY=$OBJ/sshd + +# Start a sshd and then delete it +start_sshd_copy () +{ + # NB. prefer ln to cp here. On some OSX 19.4 configurations, + # djm has seen failure after fork() when the executable image + # has been removed from the filesystem. + ln $SSHD_ORIG $SSHD_COPY || cp $SSHD_ORIG $SSHD_COPY + SSHD=$SSHD_COPY + start_sshd + SSHD=$SSHD_ORIG +} + +# Do basic copy tests +copy_tests () +{ + rm -f ${COPY} + ${SSH} -nq -F $OBJ/ssh_config somehost \ + cat ${DATA} > ${COPY} + if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy" + rm -f ${COPY} +} + +verbose "test config passing" + +cp $OBJ/sshd_config $OBJ/sshd_config.orig +start_sshd +echo "InvalidXXX=no" >> $OBJ/sshd_config + +copy_tests + +stop_sshd + +cp $OBJ/sshd_config.orig $OBJ/sshd_config + +# cygwin can't fork a deleted binary +if [ "$os" != "cygwin" ]; then + +verbose "test reexec fallback" + +start_sshd_copy +rm -f $SSHD_COPY + +copy_tests + +stop_sshd +fi diff --git a/aosp/external/openssh/regress/rekey.sh b/aosp/external/openssh/regress/rekey.sh new file mode 100644 index 0000000000000000000000000000000000000000..61723cd86608189c3e266274e3115e7620e54bb1 --- /dev/null +++ b/aosp/external/openssh/regress/rekey.sh @@ -0,0 +1,172 @@ +# $OpenBSD: rekey.sh,v 1.19 2021/07/19 05:08:54 dtucker Exp $ +# Placed in the Public Domain. + +tid="rekey" + +LOG=${TEST_SSH_LOGFILE} + +rm -f ${LOG} +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +# Test rekeying based on data volume only. +# Arguments will be passed to ssh. +ssh_data_rekeying() +{ + _kexopt=$1 ; shift + _opts="$@" + if ! test -z "$_kexopts" ; then + cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy + echo "$_kexopt" >> $OBJ/sshd_proxy + _opts="$_opts -o$_kexopt" + fi + rm -f ${COPY} ${LOG} + _opts="$_opts -oCompression=no" + ${SSH} <${DATA} $_opts -v -F $OBJ/ssh_proxy somehost "cat > ${COPY}" + if [ $? -ne 0 ]; then + fail "ssh failed ($@)" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy ($@)" + n=`grep 'NEWKEYS sent' ${LOG} | wc -l` + n=`expr $n - 1` + trace "$n rekeying(s)" + if [ $n -lt 1 ]; then + fail "no rekeying occurred ($@)" + fi +} + +increase_datafile_size 300 + +opts="" +for i in `${SSH} -Q kex`; do + opts="$opts KexAlgorithms=$i" +done +for i in `${SSH} -Q cipher`; do + opts="$opts Ciphers=$i" +done +for i in `${SSH} -Q mac`; do + opts="$opts MACs=$i" +done + +for opt in $opts; do + verbose "client rekey $opt" + ssh_data_rekeying "$opt" -oRekeyLimit=256k +done + +# AEAD ciphers are magical so test with all KexAlgorithms +if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then + for c in `${SSH} -Q cipher-auth`; do + for kex in `${SSH} -Q kex`; do + verbose "client rekey $c $kex" + ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c + done + done +fi + +for s in 16 1k 128k 256k; do + verbose "client rekeylimit ${s}" + ssh_data_rekeying "" -oCompression=no -oRekeyLimit=$s +done + +for s in 5 10; do + verbose "client rekeylimit default ${s}" + rm -f ${COPY} ${LOG} + ${SSH} < ${DATA} -oCompression=no -oRekeyLimit="default $s" -F \ + $OBJ/ssh_proxy somehost "cat >${COPY};sleep $s;sleep 10" + if [ $? -ne 0 ]; then + fail "ssh failed" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy" + n=`grep 'NEWKEYS sent' ${LOG} | wc -l` + n=`expr $n - 1` + trace "$n rekeying(s)" + if [ $n -lt 1 ]; then + fail "no rekeying occurred" + fi +done + +for s in 5 10; do + verbose "client rekeylimit default ${s} no data" + rm -f ${COPY} ${LOG} + ${SSH} -oCompression=no -oRekeyLimit="default $s" -F \ + $OBJ/ssh_proxy somehost "sleep $s;sleep 10" + if [ $? -ne 0 ]; then + fail "ssh failed" + fi + n=`grep 'NEWKEYS sent' ${LOG} | wc -l` + n=`expr $n - 1` + trace "$n rekeying(s)" + if [ $n -lt 1 ]; then + fail "no rekeying occurred" + fi +done + +for s in 16 1k 128k 256k; do + verbose "server rekeylimit ${s}" + cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy + echo "rekeylimit ${s}" >>$OBJ/sshd_proxy + rm -f ${COPY} ${LOG} + ${SSH} -oCompression=no -F $OBJ/ssh_proxy somehost "cat ${DATA}" \ + > ${COPY} + if [ $? -ne 0 ]; then + fail "ssh failed" + fi + cmp ${DATA} ${COPY} || fail "corrupted copy" + n=`grep 'NEWKEYS sent' ${LOG} | wc -l` + n=`expr $n - 1` + trace "$n rekeying(s)" + if [ $n -lt 1 ]; then + fail "no rekeying occurred" + fi +done + +for s in 5 10; do + verbose "server rekeylimit default ${s} no data" + cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy + echo "rekeylimit default ${s}" >>$OBJ/sshd_proxy + rm -f ${COPY} ${LOG} + ${SSH} -oCompression=no -F $OBJ/ssh_proxy somehost "sleep $s;sleep 10" + if [ $? -ne 0 ]; then + fail "ssh failed" + fi + n=`grep 'NEWKEYS sent' ${LOG} | wc -l` + n=`expr $n - 1` + trace "$n rekeying(s)" + if [ $n -lt 1 ]; then + fail "no rekeying occurred" + fi +done + +verbose "rekeylimit parsing" +for size in 16 1k 1K 1m 1M 1g 1G 4G 8G; do + for time in 1 1m 1M 1h 1H 1d 1D 1w 1W; do + case $size in + 16) bytes=16 ;; + 1k|1K) bytes=1024 ;; + 1m|1M) bytes=1048576 ;; + 1g|1G) bytes=1073741824 ;; + 4g|4G) bytes=4294967296 ;; + 8g|8G) bytes=8589934592 ;; + esac + case $time in + 1) seconds=1 ;; + 1m|1M) seconds=60 ;; + 1h|1H) seconds=3600 ;; + 1d|1D) seconds=86400 ;; + 1w|1W) seconds=604800 ;; + esac + + b=`$SUDO ${SSHD} -T -o "rekeylimit $size $time" -f $OBJ/sshd_proxy | \ + awk '/rekeylimit/{print $2}'` + s=`$SUDO ${SSHD} -T -o "rekeylimit $size $time" -f $OBJ/sshd_proxy | \ + awk '/rekeylimit/{print $3}'` + + if [ "$bytes" != "$b" ]; then + fatal "rekeylimit size: expected $bytes bytes got $b" + fi + if [ "$seconds" != "$s" ]; then + fatal "rekeylimit time: expected $time seconds got $s" + fi + done +done + +rm -f ${COPY} ${DATA} diff --git a/aosp/external/openssh/regress/rsa_openssh.prv b/aosp/external/openssh/regress/rsa_openssh.prv new file mode 100644 index 0000000000000000000000000000000000000000..2675555723b51a2540acc4c88c499a225d4eeec9 --- /dev/null +++ b/aosp/external/openssh/regress/rsa_openssh.prv @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWgIBAAKBgQDsilwKcaKN6wSMNd1WgQ9+HRqQEkD0kCTVttrazGu0OhBU3Uko ++dFD1Ip0CxdXmN25JQWxOYF7h/Ocu8P3jzv3RTX87xKR0YzlXTLX+SLtF/ySebS3 +xWPrlfRUDhh03hR5V+8xxvvy9widPYKw/oItwGSueOsEq1LTczCDv2dAjQIDAQAB +An8nH5VzvHkMbSqJ6eOYDsVwomRvYbH5IEaYl1x6VATITNvAu9kUdQ4NsSpuMc+7 +Jj9gKZvmO1y2YCKc0P/iO+i/eV0L+yQh1Rw18jQZll+12T+LZrKRav03YNvMx0gN +wqWY48Kt6hv2/N/ebQzKRe79+D0t2cTh92hT7xENFLIBAkEBGnoGKFjAUkJCwO1V +mzpUqMHpRZVOrqP9hUmPjzNJ5oBPFGe4+h1hoSRFOAzaNuZt8ssbqaLCkzB8bfzj +qhZqAQJBANZekuUpp8iBLeLSagw5FkcPwPzq6zfExbhvsZXb8Bo/4SflNs4JHXwI +7SD9Z8aJLvM4uQ/5M70lblDMQ40i3o0CQQDIJvBYBFL5tlOgakq/O7yi+wt0L5BZ +9H79w5rCSAA0IHRoK/qI1urHiHC3f3vbbLk5UStfrqEaND/mm0shyNIBAkBLsYdC +/ctt5Bc0wUGK4Vl5bBmj9LtrrMJ4FpBpLwj/69BwCuKoK9XKZ0h73p6XHveCEGRg +PIlFX4MtaoLrwgU9AkBV2k4dgIws+X8YX65EsyyFjnlDqX4x0nSOjQB1msIKfHBr +dh5XLDBTTCxnKhMJ0Yx/opgOvf09XHBFwaQntR5i +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/rsa_openssh.pub b/aosp/external/openssh/regress/rsa_openssh.pub new file mode 100644 index 0000000000000000000000000000000000000000..b504730f3bb8ac6d9e7f26020d60e93d2b517830 --- /dev/null +++ b/aosp/external/openssh/regress/rsa_openssh.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDsilwKcaKN6wSMNd1WgQ9+HRqQEkD0kCTVttrazGu0OhBU3Uko+dFD1Ip0CxdXmN25JQWxOYF7h/Ocu8P3jzv3RTX87xKR0YzlXTLX+SLtF/ySebS3xWPrlfRUDhh03hR5V+8xxvvy9widPYKw/oItwGSueOsEq1LTczCDv2dAjQ== diff --git a/aosp/external/openssh/regress/rsa_ssh2.prv b/aosp/external/openssh/regress/rsa_ssh2.prv new file mode 100644 index 0000000000000000000000000000000000000000..1ece3d7de5597fd6a6f02c7b2fefc57207765548 --- /dev/null +++ b/aosp/external/openssh/regress/rsa_ssh2.prv @@ -0,0 +1,16 @@ +---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- +Subject: ssh-keygen test +Comment: "1024-bit rsa, Sat Jun 23 2001 12:21:26 -0400" +P2/56wAAAi4AAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS +1wa2NzMXYyLW9hZXB9fQAAAARub25lAAAB3wAAAdsAAAARAQABAAAD9icflXO8eQxtKonp +45gOxXCiZG9hsfkgRpiXXHpUBMhM28C72RR1Dg2xKm4xz7smP2Apm+Y7XLZgIpzQ/+I76L +95XQv7JCHVHDXyNBmWX7XZP4tmspFq/Tdg28zHSA3CpZjjwq3qG/b8395tDMpF7v34PS3Z +xOH3aFPvEQ0UsgEAAAQA7IpcCnGijesEjDXdVoEPfh0akBJA9JAk1bba2sxrtDoQVN1JKP +nRQ9SKdAsXV5jduSUFsTmBe4fznLvD948790U1/O8SkdGM5V0y1/ki7Rf8knm0t8Vj65X0 +VA4YdN4UeVfvMcb78vcInT2CsP6CLcBkrnjrBKtS03Mwg79nQI0AAAH/VdpOHYCMLPl/GF ++uRLMshY55Q6l+MdJ0jo0AdZrCCnxwa3YeVywwU0wsZyoTCdGMf6KYDr39PVxwRcGkJ7Ue +YgAAAgDWXpLlKafIgS3i0moMORZHD8D86us3xMW4b7GV2/AaP+En5TbOCR18CO0g/WfGiS +7zOLkP+TO9JW5QzEONIt6NAAACAQEaegYoWMBSQkLA7VWbOlSowelFlU6uo/2FSY+PM0nm +gE8UZ7j6HWGhJEU4DNo25m3yyxuposKTMHxt/OOqFmoB +---- END SSH2 ENCRYPTED PRIVATE KEY ---- +--- diff --git a/aosp/external/openssh/regress/scp-ssh-wrapper.sh b/aosp/external/openssh/regress/scp-ssh-wrapper.sh new file mode 100644 index 0000000000000000000000000000000000000000..7fb21f424ebdbf797962b1ac9ba3e5354fd45923 --- /dev/null +++ b/aosp/external/openssh/regress/scp-ssh-wrapper.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# $OpenBSD: scp-ssh-wrapper.sh,v 1.4 2019/07/19 03:45:44 djm Exp $ +# Placed in the Public Domain. + +printname () { + NAME=$1 + save_IFS=$IFS + IFS=/ + set -- `echo "$NAME"` + IFS="$save_IFS" + while [ $# -ge 1 ] ; do + if [ "x$1" != "x" ]; then + echo "D0755 0 $1" + fi + shift; + done +} + +# Discard all but last argument. We use arg later. +while test "x$1" != "x"; do + arg="$1" + shift +done + +BAD="../../../../../../../../../../../../../${DIR}/dotpathdir" + +case "$SCPTESTMODE" in +badserver_0) + echo "D0755 0 /${DIR}/rootpathdir" + echo "C755 2 rootpathfile" + echo "X" + ;; +badserver_1) + echo "D0755 0 $BAD" + echo "C755 2 file" + echo "X" + ;; +badserver_2) + echo "D0755 0 $BAD" + echo "C755 2 file" + echo "X" + ;; +badserver_3) + printname $BAD + echo "C755 2 file" + echo "X" + ;; +badserver_4) + printname $BAD + echo "D0755 0 .." + echo "C755 2 file" + echo "X" + ;; +badserver_5) + echo "D0555 0 " + echo "X" + ;; +badserver_6) + echo "D0555 0 ." + echo "X" + ;; +badserver_7) + echo "C0755 2 extrafile" + echo "X" + ;; +*) + set -- $arg + shift + exec $SCP "$@" + ;; +esac diff --git a/aosp/external/openssh/regress/scp-uri.sh b/aosp/external/openssh/regress/scp-uri.sh new file mode 100644 index 0000000000000000000000000000000000000000..20ac3c89ec26b1e19bc01cf5c92dc2c66bf1ddc5 --- /dev/null +++ b/aosp/external/openssh/regress/scp-uri.sh @@ -0,0 +1,77 @@ +# $OpenBSD: scp-uri.sh,v 1.4 2021/08/10 03:35:45 djm Exp $ +# Placed in the Public Domain. + +tid="scp-uri" + +#set -x + +COPY2=${OBJ}/copy2 +DIR=${COPY}.dd +DIR2=${COPY}.dd2 + +SRC=`dirname ${SCRIPT}` +cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp +chmod 755 ${OBJ}/scp-ssh-wrapper.scp +export SCP # used in scp-ssh-wrapper.scp + +scpclean() { + rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} + mkdir ${DIR} ${DIR2} +} + +# Remove Port and User from ssh_config, we want to rely on the URI +cp $OBJ/ssh_config $OBJ/ssh_config.orig +egrep -v '^ +(Port|User) +.*$' $OBJ/ssh_config.orig > $OBJ/ssh_config + +for mode in scp sftp ; do + tag="$tid: $mode mode" + if test $mode = scp ; then + scpopts="-O -q -S ${OBJ}/scp-ssh-wrapper.scp" + else + scpopts="-s -D ${SFTPSERVER}" + fi + verbose "$tag: simple copy local file to remote file" + scpclean + $SCP $scpopts ${DATA} "scp://${USER}@somehost:${PORT}/${COPY}" || fail "copy failed" + cmp ${DATA} ${COPY} || fail "corrupted copy" + + verbose "$tag: simple copy remote file to local file" + scpclean + $SCP $scpopts "scp://${USER}@somehost:${PORT}/${DATA}" ${COPY} || fail "copy failed" + cmp ${DATA} ${COPY} || fail "corrupted copy" + + verbose "$tag: simple copy local file to remote dir" + scpclean + cp ${DATA} ${COPY} + $SCP $scpopts ${COPY} "scp://${USER}@somehost:${PORT}/${DIR}" || fail "copy failed" + cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + + verbose "$tag: simple copy remote file to local dir" + scpclean + cp ${DATA} ${COPY} + $SCP $scpopts "scp://${USER}@somehost:${PORT}/${COPY}" ${DIR} || fail "copy failed" + cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + + verbose "$tag: recursive local dir to remote dir" + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + $SCP $scpopts -r ${DIR} "scp://${USER}@somehost:${PORT}/${DIR2}" || fail "copy failed" + for i in $(cd ${DIR} && echo *); do + cmp ${DIR}/$i ${DIR2}/$i || fail "corrupted copy" + done + + verbose "$tag: recursive remote dir to local dir" + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + $SCP $scpopts -r "scp://${USER}@somehost:${PORT}/${DIR}" ${DIR2} || fail "copy failed" + for i in $(cd ${DIR} && echo *); do + cmp ${DIR}/$i ${DIR2}/$i || fail "corrupted copy" + done + + # TODO: scp -3 +done + +scpclean +rm -f ${OBJ}/scp-ssh-wrapper.exe diff --git a/aosp/external/openssh/regress/scp.sh b/aosp/external/openssh/regress/scp.sh new file mode 100644 index 0000000000000000000000000000000000000000..358a8df66b1c681ec44e41d23382694cc15982ba --- /dev/null +++ b/aosp/external/openssh/regress/scp.sh @@ -0,0 +1,143 @@ +# $OpenBSD: scp.sh,v 1.13 2021/08/10 03:35:45 djm Exp $ +# Placed in the Public Domain. + +tid="scp" + +#set -x + +# Figure out if diff understands "-N" +if diff -N ${SRC}/scp.sh ${SRC}/scp.sh 2>/dev/null; then + DIFFOPT="-rN" +else + DIFFOPT="-r" +fi + +COPY2=${OBJ}/copy2 +DIR=${COPY}.dd +DIR2=${COPY}.dd2 + +SRC=`dirname ${SCRIPT}` +cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp +chmod 755 ${OBJ}/scp-ssh-wrapper.scp +export SCP # used in scp-ssh-wrapper.scp + +scpclean() { + rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} + mkdir ${DIR} ${DIR2} + chmod 755 ${DIR} ${DIR2} +} + +for mode in scp sftp ; do + tag="$tid: $mode mode" + if test $mode = scp ; then + scpopts="-O -q -S ${OBJ}/scp-ssh-wrapper.scp" + else + scpopts="-s -D ${SFTPSERVER}" + fi + verbose "tid: simple copy local file to local file" + scpclean + $SCP $scpopts ${DATA} ${COPY} || fail "copy failed" + cmp ${DATA} ${COPY} || fail "corrupted copy" + + verbose "$tag: simple copy local file to remote file" + scpclean + $SCP $scpopts ${DATA} somehost:${COPY} || fail "copy failed" + cmp ${DATA} ${COPY} || fail "corrupted copy" + + verbose "$tag: simple copy remote file to local file" + scpclean + $SCP $scpopts somehost:${DATA} ${COPY} || fail "copy failed" + cmp ${DATA} ${COPY} || fail "corrupted copy" + + verbose "$tag: simple copy local file to remote dir" + scpclean + cp ${DATA} ${COPY} + $SCP $scpopts ${COPY} somehost:${DIR} || fail "copy failed" + cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + + verbose "$tag: simple copy local file to local dir" + scpclean + cp ${DATA} ${COPY} + $SCP $scpopts ${COPY} ${DIR} || fail "copy failed" + cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + + verbose "$tag: simple copy remote file to local dir" + scpclean + cp ${DATA} ${COPY} + $SCP $scpopts somehost:${COPY} ${DIR} || fail "copy failed" + cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + + verbose "$tag: recursive local dir to remote dir" + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + $SCP $scpopts -r ${DIR} somehost:${DIR2} || fail "copy failed" + diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" + + verbose "$tag: recursive local dir to local dir" + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + $SCP $scpopts -r ${DIR} ${DIR2} || fail "copy failed" + diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" + + verbose "$tag: recursive remote dir to local dir" + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + $SCP $scpopts -r somehost:${DIR} ${DIR2} || fail "copy failed" + diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" + + verbose "$tag: shell metacharacters" + scpclean + (cd ${DIR} && \ + touch '`touch metachartest`' && \ + $SCP $scpopts *metachar* ${DIR2} 2>/dev/null; \ + [ ! -f metachartest ] ) || fail "shell metacharacters" + + if [ ! -z "$SUDO" ]; then + verbose "$tag: skipped file after scp -p with failed chown+utimes" + scpclean + cp -p ${DATA} ${DIR}/copy + cp -p ${DATA} ${DIR}/copy2 + cp ${DATA} ${DIR2}/copy + chmod 660 ${DIR2}/copy + $SUDO chown root ${DIR2}/copy + $SCP -p $scpopts somehost:${DIR}/\* ${DIR2} >/dev/null 2>&1 + $SUDO diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" + $SUDO rm ${DIR2}/copy + fi + + for i in 0 1 2 3 4 5 6 7; do + verbose "$tag: disallow bad server #$i" + SCPTESTMODE=badserver_$i + export DIR SCPTESTMODE + scpclean + $SCP $scpopts somehost:${DATA} ${DIR} >/dev/null 2>/dev/null + [ -d {$DIR}/rootpathdir ] && fail "allows dir relative to root dir" + [ -d ${DIR}/dotpathdir ] && fail "allows dir creation in non-recursive mode" + + scpclean + $SCP -r $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null + [ -d ${DIR}/dotpathdir ] && fail "allows dir creation outside of subdir" + + scpclean + $SCP -pr $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null + [ ! -w ${DIR2} ] && fail "allows target root attribute change" + + scpclean + $SCP $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null + [ -e ${DIR2}/extrafile ] && fail "allows unauth object creation" + rm -f ${DIR2}/extrafile + done + + verbose "$tag: detect non-directory target" + scpclean + echo a > ${COPY} + echo b > ${COPY2} + $SCP $scpopts ${DATA} ${COPY} ${COPY2} + cmp ${COPY} ${COPY2} >/dev/null && fail "corrupt target" +done + +scpclean +rm -f ${OBJ}/scp-ssh-wrapper.scp diff --git a/aosp/external/openssh/regress/scp3.sh b/aosp/external/openssh/regress/scp3.sh new file mode 100644 index 0000000000000000000000000000000000000000..f71b1567755b7da6f8278fefc8fbe9c8ac6ad0d8 --- /dev/null +++ b/aosp/external/openssh/regress/scp3.sh @@ -0,0 +1,60 @@ +# $OpenBSD: scp3.sh,v 1.3 2021/08/10 03:35:45 djm Exp $ +# Placed in the Public Domain. + +tid="scp3" + +#set -x + +COPY2=${OBJ}/copy2 +DIR=${COPY}.dd +DIR2=${COPY}.dd2 + +SRC=`dirname ${SCRIPT}` +cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp +chmod 755 ${OBJ}/scp-ssh-wrapper.scp +export SCP # used in scp-ssh-wrapper.scp + +scpclean() { + rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} + mkdir ${DIR} ${DIR2} + chmod 755 ${DIR} ${DIR2} +} + +for mode in scp sftp ; do + scpopts="-F${OBJ}/ssh_proxy -S ${SSH} -q" + tag="$tid: $mode mode" + if test $mode = scp ; then + scpopts="$scpopts -O" + else + scpopts="-s -D ${SFTPSERVER}" + fi + + verbose "$tag: simple copy remote file to remote file" + scpclean + $SCP $scpopts -3 hostA:${DATA} hostB:${COPY} || fail "copy failed" + cmp ${DATA} ${COPY} || fail "corrupted copy" + + verbose "$tag: simple copy remote file to remote dir" + scpclean + cp ${DATA} ${COPY} + $SCP $scpopts -3 hostA:${COPY} hostB:${DIR} || fail "copy failed" + cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + + verbose "$tag: recursive remote dir to remote dir" + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + $SCP $scpopts -3r hostA:${DIR} hostB:${DIR2} || fail "copy failed" + diff -r ${DIR} ${DIR2} || fail "corrupted copy" + diff -r ${DIR2} ${DIR} || fail "corrupted copy" + + verbose "$tag: detect non-directory target" + scpclean + echo a > ${COPY} + echo b > ${COPY2} + $SCP $scpopts -3 hostA:${DATA} hostA:${COPY} hostB:${COPY2} + cmp ${COPY} ${COPY2} >/dev/null && fail "corrupt target" +done + +scpclean +rm -f ${OBJ}/scp-ssh-wrapper.exe diff --git a/aosp/external/openssh/regress/servcfginclude.sh b/aosp/external/openssh/regress/servcfginclude.sh new file mode 100644 index 0000000000000000000000000000000000000000..518a703d100efcdb4efa8c111302b16c17b1ce8d --- /dev/null +++ b/aosp/external/openssh/regress/servcfginclude.sh @@ -0,0 +1,188 @@ +# Placed in the Public Domain. + +tid="server config include" + +cat > $OBJ/sshd_config.i << _EOF +HostKey $OBJ/host.ssh-ed25519 +Match host a + Banner /aa + +Match host b + Banner /bb + Include $OBJ/sshd_config.i.* # comment + +Match host c + Include $OBJ/sshd_config.i.* # comment + Banner /cc + +Match host m + Include $OBJ/sshd_config.i.* + +Match Host d + Banner /dd # comment + +Match Host e + Banner /ee + Include $OBJ/sshd_config.i.* + +Match Host f + Include $OBJ/sshd_config.i.* + Banner /ff + +Match Host n + Include $OBJ/sshd_config.i.* +_EOF + +cat > $OBJ/sshd_config.i.0 << _EOF +Match host xxxxxx +_EOF + +cat > $OBJ/sshd_config.i.1 << _EOF +Match host a + Banner /aaa + +Match host b + Banner /bbb + +Match host c + Banner /ccc + +Match Host d + Banner /ddd + +Match Host e + Banner /eee + +Match Host f + Banner /fff +_EOF + +cat > $OBJ/sshd_config.i.2 << _EOF +Match host a + Banner /aaaa + +Match host b + Banner /bbbb + +Match host c # comment + Banner /cccc + +Match Host d + Banner /dddd + +Match Host e + Banner /eeee + +Match Host f + Banner /ffff + +Match all + Banner /xxxx +_EOF + +trial() { + _host="$1" + _exp="$2" + _desc="$3" + test -z "$_desc" && _desc="test match" + trace "$_desc host=$_host expect=$_exp" + ${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i -T \ + -C "host=$_host,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out || + fatal "ssh config parse failed: $_desc host=$_host expect=$_exp" + _got=`grep -i '^banner ' $OBJ/sshd_config.out | awk '{print $2}'` + if test "x$_exp" != "x$_got" ; then + fail "$desc_ host $_host include fail: expected $_exp got $_got" + fi +} + +trial a /aa +trial b /bb +trial c /ccc +trial d /dd +trial e /ee +trial f /fff +trial m /xxxx +trial n /xxxx +trial x none + +# Prepare an included config with an error. + +cat > $OBJ/sshd_config.i.3 << _EOF +Banner xxxx + Junk +_EOF + +trace "disallow invalid config host=a" +${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i \ + -C "host=a,user=test,addr=127.0.0.1" 2>/dev/null && \ + fail "sshd include allowed invalid config" + +trace "disallow invalid config host=x" +${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i \ + -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \ + fail "sshd include allowed invalid config" + +rm -f $OBJ/sshd_config.i.* + +# Ensure that a missing include is not fatal. +cat > $OBJ/sshd_config.i << _EOF +HostKey $OBJ/host.ssh-ed25519 +Include $OBJ/sshd_config.i.* +Banner /aa +_EOF + +trial a /aa "missing include non-fatal" + +# Ensure that Match/Host in an included config does not affect parent. +cat > $OBJ/sshd_config.i.x << _EOF +Match host x +_EOF + +trial a /aa "included file does not affect match state" + +# Ensure the empty include directive is not accepted +cat > $OBJ/sshd_config.i.x << _EOF +Include +_EOF + +trace "disallow invalid with no argument" +${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i.x -T \ + -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \ + fail "sshd allowed Include with no argument" + +# Ensure the Include before any Match block works as expected (bug #3122) +cat > $OBJ/sshd_config.i << _EOF +Banner /xx +HostKey $OBJ/host.ssh-ed25519 +Include $OBJ/sshd_config.i.2 +Match host a + Banner /aaaa +_EOF +cat > $OBJ/sshd_config.i.2 << _EOF +Match host a + Banner /aa +_EOF + +trace "Include before match blocks" +trial a /aa "included file before match blocks is properly evaluated" + +# Port in included file is correctly interpretted (bug #3169) +cat > $OBJ/sshd_config.i << _EOF +Include $OBJ/sshd_config.i.2 +Port 7722 +_EOF +cat > $OBJ/sshd_config.i.2 << _EOF +HostKey $OBJ/host.ssh-ed25519 +_EOF + +trace "Port after included files" +${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i -T \ + -C "host=x,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out || \ + fail "failed to parse Port after included files" +_port=`grep -i '^port ' $OBJ/sshd_config.out | awk '{print $2}'` +if test "x7722" != "x$_port" ; then + fail "The Port in included file was intertepretted wrongly. Expected 7722, got $_port" +fi + +# cleanup +rm -f $OBJ/sshd_config.i $OBJ/sshd_config.i.* $OBJ/sshd_config.out diff --git a/aosp/external/openssh/regress/setuid-allowed.c b/aosp/external/openssh/regress/setuid-allowed.c new file mode 100644 index 0000000000000000000000000000000000000000..d91d9f194d11e5896ad208af6942fe6206d35039 --- /dev/null +++ b/aosp/external/openssh/regress/setuid-allowed.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD$ */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_STATVFS_H +# include +#endif +#include +#include +#include +#include + +static void +usage(void) +{ + fprintf(stderr, "check-setuid [path]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *path = "."; + struct statvfs sb; + + if (argc > 2) + usage(); + else if (argc == 2) + path = argv[1]; + + if (statvfs(path, &sb) != 0) { + /* Don't return an error if the host doesn't support statvfs */ + if (errno == ENOSYS) + return 0; + fprintf(stderr, "statvfs for \"%s\" failed: %s\n", + path, strerror(errno)); + } + return (sb.f_flag & ST_NOSUID) ? 1 : 0; +} + + diff --git a/aosp/external/openssh/regress/sftp-badcmds.sh b/aosp/external/openssh/regress/sftp-badcmds.sh new file mode 100644 index 0000000000000000000000000000000000000000..5b016d558b3fefd647e48b5b23df61c58668189d --- /dev/null +++ b/aosp/external/openssh/regress/sftp-badcmds.sh @@ -0,0 +1,65 @@ +# $OpenBSD: sftp-badcmds.sh,v 1.7 2020/03/13 03:18:45 djm Exp $ +# Placed in the Public Domain. + +tid="sftp invalid commands" + +DATA2=/bin/sh${EXEEXT} +NONEXIST=/NONEXIST.$$ +GLOBFILES=`(cd /bin;echo l*)` + +rm -rf ${COPY} ${COPY}.1 ${COPY}.2 ${COPY}.dd + +rm -f ${COPY} +verbose "$tid: get nonexistent" +echo "get $NONEXIST $COPY" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get nonexistent failed" +test -f ${COPY} && fail "existing copy after get nonexistent" + +rm -f ${COPY}.dd/* +verbose "$tid: glob get to nonexistent directory" +echo "get /bin/l* $NONEXIST" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get nonexistent failed" +for x in $GLOBFILES; do + test -f ${COPY}.dd/$x && fail "existing copy after get nonexistent" +done + +rm -f ${COPY} +verbose "$tid: put nonexistent" +echo "put $NONEXIST $COPY" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "put nonexistent failed" +test -f ${COPY} && fail "existing copy after put nonexistent" + +rm -f ${COPY}.dd/* +verbose "$tid: glob put to nonexistent directory" +echo "put /bin/l* ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "put nonexistent failed" +for x in $GLOBFILES; do + test -f ${COPY}.dd/$x && fail "existing copy after nonexistent" +done + +rm -f ${COPY} +verbose "$tid: rename nonexistent" +echo "rename $NONEXIST ${COPY}.1" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "rename nonexist failed" +test -f ${COPY}.1 && fail "file exists after rename nonexistent" + +rm -rf ${COPY} ${COPY}.dd +cp $DATA $COPY +mkdir ${COPY}.dd +verbose "$tid: rename target exists (directory)" +echo "rename $COPY ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "rename target exists (directory) failed" +test -f ${COPY} || fail "oldname missing after rename target exists (directory)" +test -d ${COPY}.dd || fail "newname missing after rename target exists (directory)" +cmp $DATA ${COPY} >/dev/null 2>&1 || fail "corrupted oldname after rename target exists (directory)" + +rm -f ${COPY}.dd/* +rm -rf ${COPY} +cp ${DATA2} ${COPY} +verbose "$tid: glob put files to local file" +echo "put /bin/l* $COPY" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 +cmp ${DATA2} ${COPY} || fail "put succeeded when it should have failed" + +rm -rf ${COPY} ${COPY}.1 ${COPY}.2 ${COPY}.dd + + diff --git a/aosp/external/openssh/regress/sftp-batch.sh b/aosp/external/openssh/regress/sftp-batch.sh new file mode 100644 index 0000000000000000000000000000000000000000..41011549bc40faeac7c41720c26579b09be9f85b --- /dev/null +++ b/aosp/external/openssh/regress/sftp-batch.sh @@ -0,0 +1,55 @@ +# $OpenBSD: sftp-batch.sh,v 1.5 2013/05/17 04:29:14 dtucker Exp $ +# Placed in the Public Domain. + +tid="sftp batchfile" + +BATCH=${OBJ}/sftp.bb + +rm -rf ${COPY} ${COPY}.1 ${COPY}.2 ${COPY}.dd ${BATCH}.* + +cat << EOF > ${BATCH}.pass.1 + get $DATA $COPY + put ${COPY} ${COPY}.1 + rm ${COPY} + -put ${COPY} ${COPY}.2 +EOF + +cat << EOF > ${BATCH}.pass.2 + # This is a comment + + # That was a blank line + ls +EOF + +cat << EOF > ${BATCH}.fail.1 + get $DATA $COPY + put ${COPY} ${COPY}.3 + rm ${COPY}.* + # The next command should fail + put ${COPY}.3 ${COPY}.4 +EOF + +cat << EOF > ${BATCH}.fail.2 + # The next command should fail + jajajajaja +EOF + +verbose "$tid: good commands" +${SFTP} -b ${BATCH}.pass.1 -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "good commands failed" + +verbose "$tid: bad commands" +${SFTP} -b ${BATCH}.fail.1 -D ${SFTPSERVER} >/dev/null 2>&1 \ + && fail "bad commands succeeded" + +verbose "$tid: comments and blanks" +${SFTP} -b ${BATCH}.pass.2 -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "comments & blanks failed" + +verbose "$tid: junk command" +${SFTP} -b ${BATCH}.fail.2 -D ${SFTPSERVER} >/dev/null 2>&1 \ + && fail "junk command succeeded" + +rm -rf ${COPY} ${COPY}.1 ${COPY}.2 ${COPY}.dd ${BATCH}.* + + diff --git a/aosp/external/openssh/regress/sftp-chroot.sh b/aosp/external/openssh/regress/sftp-chroot.sh new file mode 100644 index 0000000000000000000000000000000000000000..a7766fe63a2ea646f7bbffd202305b22d1465823 --- /dev/null +++ b/aosp/external/openssh/regress/sftp-chroot.sh @@ -0,0 +1,28 @@ +# $OpenBSD: sftp-chroot.sh,v 1.8 2021/09/01 00:50:27 dtucker Exp $ +# Placed in the Public Domain. + +tid="sftp in chroot" + +CHROOT=/var/run +FILENAME=testdata_${USER}.$$ +PRIVDATA=${CHROOT}/${FILENAME} +trap "${SUDO} rm -f ${PRIVDATA}" 0 + +if [ -z "$SUDO" -a ! -w /var/run ]; then + skip "need SUDO to create file in /var/run, test won't work without" +fi + +if ! $OBJ/check-perm -m chroot "$CHROOT" ; then + skip "$CHROOT is unsuitable as ChrootDirectory" +fi + +$SUDO sh -c "echo mekmitastdigoat > $PRIVDATA" || \ + fatal "create $PRIVDATA failed" + +start_sshd -oChrootDirectory=$CHROOT -oForceCommand="internal-sftp -d /" + +verbose "test $tid: get" +${SFTP} -S "$SSH" -F $OBJ/ssh_config host:/${FILENAME} $COPY \ + >>$TEST_REGRESS_LOGFILE 2>&1 || \ + fatal "Fetch ${FILENAME} failed" +cmp $PRIVDATA $COPY || fail "$PRIVDATA $COPY differ" diff --git a/aosp/external/openssh/regress/sftp-cmds.sh b/aosp/external/openssh/regress/sftp-cmds.sh new file mode 100644 index 0000000000000000000000000000000000000000..1289c4089c6c87d3a0f1b90ec37abacf5fd0be68 --- /dev/null +++ b/aosp/external/openssh/regress/sftp-cmds.sh @@ -0,0 +1,228 @@ +# $OpenBSD: sftp-cmds.sh,v 1.14 2013/06/21 02:26:26 djm Exp $ +# Placed in the Public Domain. + +# XXX - TODO: +# - chmod / chown / chgrp +# - -p flag for get & put + +tid="sftp commands" + +# test that these files are readable! +for i in `(cd /bin;echo l*)` +do + if [ -r $i ]; then + GLOBFILES="$GLOBFILES $i" + fi +done + +# Path with embedded quote +QUOTECOPY=${COPY}".\"blah\"" +QUOTECOPY_ARG=${COPY}'.\"blah\"' +# File with spaces +SPACECOPY="${COPY} this has spaces.txt" +SPACECOPY_ARG="${COPY}\ this\ has\ spaces.txt" +# File with glob metacharacters +GLOBMETACOPY="${COPY} [metachar].txt" + +rm -rf ${COPY} ${COPY}.1 ${COPY}.2 ${COPY}.dd ${COPY}.dd2 +mkdir ${COPY}.dd + +verbose "$tid: lls" +(echo "lcd ${OBJ}" ; echo "lls") | ${SFTP} -D ${SFTPSERVER} 2>&1 | \ + grep copy.dd >/dev/null 2>&1 || fail "lls failed" + +verbose "$tid: lls w/path" +echo "lls ${OBJ}" | ${SFTP} -D ${SFTPSERVER} 2>&1 | \ + grep copy.dd >/dev/null 2>&1 || fail "lls w/path failed" + +verbose "$tid: ls" +echo "ls ${OBJ}" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "ls failed" +# XXX always successful + +verbose "$tid: shell" +echo "!echo hi there" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "shell failed" +# XXX always successful + +verbose "$tid: pwd" +echo "pwd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "pwd failed" +# XXX always successful + +verbose "$tid: lpwd" +echo "lpwd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "lpwd failed" +# XXX always successful + +verbose "$tid: quit" +echo "quit" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "quit failed" +# XXX always successful + +verbose "$tid: help" +echo "help" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "help failed" +# XXX always successful + +rm -f ${COPY} +verbose "$tid: get" +echo "get $DATA $COPY" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +cmp $DATA ${COPY} || fail "corrupted copy after get" + +rm -f ${COPY} +verbose "$tid: get quoted" +echo "get \"$DATA\" $COPY" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +cmp $DATA ${COPY} || fail "corrupted copy after get" + +rm -f ${QUOTECOPY} +cp $DATA ${QUOTECOPY} +verbose "$tid: get filename with quotes" +echo "get \"$QUOTECOPY_ARG\" ${COPY}" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +cmp ${COPY} ${QUOTECOPY} || fail "corrupted copy after get with quotes" +rm -f ${QUOTECOPY} ${COPY} + +rm -f "$SPACECOPY" ${COPY} +cp $DATA "$SPACECOPY" +verbose "$tid: get filename with spaces" +echo "get ${SPACECOPY_ARG} ${COPY}" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +cmp ${COPY} "$SPACECOPY" || fail "corrupted copy after get with spaces" + +rm -f "$GLOBMETACOPY" ${COPY} +cp $DATA "$GLOBMETACOPY" +verbose "$tid: get filename with glob metacharacters" +echo "get \"${GLOBMETACOPY}\" ${COPY}" | \ + ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "get failed" +cmp ${COPY} "$GLOBMETACOPY" || \ + fail "corrupted copy after get with glob metacharacters" + +rm -f ${COPY}.dd/* +verbose "$tid: get to directory" +echo "get $DATA ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +cmp $DATA ${COPY}.dd/$DATANAME || fail "corrupted copy after get" + +rm -f ${COPY}.dd/* +verbose "$tid: glob get to directory" +echo "get /bin/l* ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +for x in $GLOBFILES; do + cmp /bin/$x ${COPY}.dd/$x || fail "corrupted copy after get" +done + +rm -f ${COPY}.dd/* +verbose "$tid: get to local dir" +(echo "lcd ${COPY}.dd"; echo "get $DATA" ) | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +cmp $DATA ${COPY}.dd/$DATANAME || fail "corrupted copy after get" + +rm -f ${COPY}.dd/* +verbose "$tid: glob get to local dir" +(echo "lcd ${COPY}.dd"; echo "get /bin/l*") | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "get failed" +for x in $GLOBFILES; do + cmp /bin/$x ${COPY}.dd/$x || fail "corrupted copy after get" +done + +rm -f ${COPY} +verbose "$tid: put" +echo "put $DATA $COPY" | \ + ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "put failed" +cmp $DATA ${COPY} || fail "corrupted copy after put" + +rm -f ${QUOTECOPY} +verbose "$tid: put filename with quotes" +echo "put $DATA \"$QUOTECOPY_ARG\"" | \ + ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "put failed" +cmp $DATA ${QUOTECOPY} || fail "corrupted copy after put with quotes" + +rm -f "$SPACECOPY" +verbose "$tid: put filename with spaces" +echo "put $DATA ${SPACECOPY_ARG}" | \ + ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "put failed" +cmp $DATA "$SPACECOPY" || fail "corrupted copy after put with spaces" + +rm -f ${COPY}.dd/* +verbose "$tid: put to directory" +echo "put $DATA ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "put failed" +cmp $DATA ${COPY}.dd/$DATANAME || fail "corrupted copy after put" + +rm -f ${COPY}.dd/* +verbose "$tid: glob put to directory" +echo "put /bin/l? ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "put failed" +for x in $GLOBFILES; do + cmp /bin/$x ${COPY}.dd/$x || fail "corrupted copy after put" +done + +rm -f ${COPY}.dd/* +verbose "$tid: put to local dir" +(echo "cd ${COPY}.dd"; echo "put $DATA") | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "put failed" +cmp $DATA ${COPY}.dd/$DATANAME || fail "corrupted copy after put" + +rm -f ${COPY}.dd/* +verbose "$tid: glob put to local dir" +(echo "cd ${COPY}.dd"; echo "put /bin/l?") | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "put failed" +for x in $GLOBFILES; do + cmp /bin/$x ${COPY}.dd/$x || fail "corrupted copy after put" +done + +verbose "$tid: rename" +echo "rename $COPY ${COPY}.1" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "rename failed" +test -f ${COPY}.1 || fail "missing file after rename" +cmp $DATA ${COPY}.1 >/dev/null 2>&1 || fail "corrupted copy after rename" + +verbose "$tid: rename directory" +echo "rename ${COPY}.dd ${COPY}.dd2" | \ + ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || \ + fail "rename directory failed" +test -d ${COPY}.dd && fail "oldname exists after rename directory" +test -d ${COPY}.dd2 || fail "missing newname after rename directory" + +verbose "$tid: ln" +echo "ln ${COPY}.1 ${COPY}.2" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "ln failed" +test -f ${COPY}.2 || fail "missing file after ln" +cmp ${COPY}.1 ${COPY}.2 || fail "created file is not equal after ln" + +verbose "$tid: ln -s" +rm -f ${COPY}.2 +echo "ln -s ${COPY}.1 ${COPY}.2" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 || fail "ln -s failed" +test -h ${COPY}.2 || fail "missing file after ln -s" + +verbose "$tid: mkdir" +echo "mkdir ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "mkdir failed" +test -d ${COPY}.dd || fail "missing directory after mkdir" + +# XXX do more here +verbose "$tid: chdir" +echo "chdir ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "chdir failed" + +verbose "$tid: rmdir" +echo "rmdir ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "rmdir failed" +test -d ${COPY}.1 && fail "present directory after rmdir" + +verbose "$tid: lmkdir" +echo "lmkdir ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "lmkdir failed" +test -d ${COPY}.dd || fail "missing directory after lmkdir" + +# XXX do more here +verbose "$tid: lchdir" +echo "lchdir ${COPY}.dd" | ${SFTP} -D ${SFTPSERVER} >/dev/null 2>&1 \ + || fail "lchdir failed" + +rm -rf ${COPY} ${COPY}.1 ${COPY}.2 ${COPY}.dd ${COPY}.dd2 +rm -rf ${QUOTECOPY} "$SPACECOPY" "$GLOBMETACOPY" + + diff --git a/aosp/external/openssh/regress/sftp-glob.sh b/aosp/external/openssh/regress/sftp-glob.sh new file mode 100644 index 0000000000000000000000000000000000000000..8d4df2c98b5f2b6fe8dad8924ffa6bf825a46cfa --- /dev/null +++ b/aosp/external/openssh/regress/sftp-glob.sh @@ -0,0 +1,75 @@ +# $OpenBSD: sftp-glob.sh,v 1.4 2009/08/13 01:11:55 djm Exp $ +# Placed in the Public Domain. + +tid="sftp glob" + +config_defined FILESYSTEM_NO_BACKSLASH && nobs="not supported on this platform" + +sftp_ls() { + target=$1 + errtag=$2 + expected=$3 + unexpected=$4 + skip=$5 + if test "x$skip" != "x" ; then + verbose "$tid: $errtag (skipped: $skip)" + return + fi + verbose "$tid: $errtag" + printf "ls -l %s" "${target}" | \ + ${SFTP} -b - -D ${SFTPSERVER} 2>/dev/null | \ + grep -v "^sftp>" > ${RESULTS} + if [ $? -ne 0 ]; then + fail "$errtag failed" + fi + if test "x$expected" != "x" ; then + if fgrep "$expected" ${RESULTS} >/dev/null 2>&1 ; then + : + else + fail "$expected missing from $errtag results" + fi + fi + if test "x$unexpected" != "x" && \ + fgrep "$unexpected" ${RESULTS} >/dev/null 2>&1 ; then + fail "$unexpected present in $errtag results" + fi + rm -f ${RESULTS} +} + +BASE=${OBJ}/glob +RESULTS=${OBJ}/results +DIR=${BASE}/dir +DATA=${DIR}/file + +GLOB1="${DIR}/g-wild*" +GLOB2="${DIR}/g-wildx" +QUOTE="${DIR}/g-quote\"" +SLASH="${DIR}/g-sl\\ash" +ESLASH="${DIR}/g-slash\\" +QSLASH="${DIR}/g-qs\\\"" +SPACE="${DIR}/g-q space" + +rm -rf ${BASE} +mkdir -p ${DIR} +touch "${DATA}" "${GLOB1}" "${GLOB2}" "${QUOTE}" "${SPACE}" +test "x$nobs" = "x" && touch "${QSLASH}" "${ESLASH}" "${SLASH}" + +# target message expected unexpected +sftp_ls "${DIR}/fil*" "file glob" "${DATA}" "" +sftp_ls "${BASE}/d*" "dir glob" "`basename ${DATA}`" "" +sftp_ls "${DIR}/g-wild\"*\"" "quoted glob" "g-wild*" "g-wildx" +sftp_ls "${DIR}/g-wild\*" "escaped glob" "g-wild*" "g-wildx" +sftp_ls "${DIR}/g-quote\\\"" "escaped quote" "g-quote\"" "" +sftp_ls "\"${DIR}/g-quote\\\"\"" "quoted quote" "g-quote\"" "" +sftp_ls "'${DIR}/g-quote\"'" "single-quoted quote" "g-quote\"" "" +sftp_ls "${DIR}/g-q\\ space" "escaped space" "g-q space" "" +sftp_ls "'${DIR}/g-q space'" "quoted space" "g-q space" "" +sftp_ls "${DIR}/g-sl\\\\ash" "escaped slash" "g-sl\\ash" "" "$nobs" +sftp_ls "'${DIR}/g-sl\\\\ash'" "quoted slash" "g-sl\\ash" "" "$nobs" +sftp_ls "${DIR}/g-slash\\\\" "escaped slash at EOL" "g-slash\\" "" "$nobs" +sftp_ls "'${DIR}/g-slash\\\\'" "quoted slash at EOL" "g-slash\\" "" "$nobs" +sftp_ls "${DIR}/g-qs\\\\\\\"" "escaped slash+quote" "g-qs\\\"" "" "$nobs" +sftp_ls "'${DIR}/g-qs\\\\\"'" "quoted slash+quote" "g-qs\\\"" "" "$nobs" + +rm -rf ${BASE} + diff --git a/aosp/external/openssh/regress/sftp-perm.sh b/aosp/external/openssh/regress/sftp-perm.sh new file mode 100644 index 0000000000000000000000000000000000000000..de96a14da8e835fe22035177eaa783ef2adc3483 --- /dev/null +++ b/aosp/external/openssh/regress/sftp-perm.sh @@ -0,0 +1,271 @@ +# $OpenBSD: sftp-perm.sh,v 1.3 2021/03/31 21:59:26 djm Exp $ +# Placed in the Public Domain. + +tid="sftp permissions" + +SERVER_LOG=${OBJ}/sftp-server.log +CLIENT_LOG=${OBJ}/sftp.log +TEST_SFTP_SERVER=${OBJ}/sftp-server.sh + +prepare_server() { + printf "#!/bin/sh\nexec $SFTPSERVER -el debug3 $* 2>$SERVER_LOG\n" \ + > $TEST_SFTP_SERVER + chmod a+x $TEST_SFTP_SERVER +} + +run_client() { + echo "$@" | ${SFTP} -D ${TEST_SFTP_SERVER} -vvvb - >$CLIENT_LOG 2>&1 +} + +prepare_files() { + _prep="$1" + rm -f ${COPY} ${COPY}.1 + test -d ${COPY}.dd && { rmdir ${COPY}.dd || fatal "rmdir ${COPY}.dd"; } + test -z "$_prep" && return + sh -c "$_prep" || fail "preparation failed: \"$_prep\"" +} + +postcondition() { + _title="$1" + _check="$2" + test -z "$_check" && return + ${TEST_SHELL} -c "$_check" || fail "postcondition check failed: $_title" +} + +ro_test() { + _desc=$1 + _cmd="$2" + _prep="$3" + _expect_success_post="$4" + _expect_fail_post="$5" + verbose "$tid: read-only $_desc" + # Plain (no options, mostly to test that _cmd is good) + prepare_files "$_prep" + prepare_server + run_client "$_cmd" || fail "plain $_desc failed" + postcondition "$_desc no-readonly" "$_expect_success_post" + # Read-only enabled + prepare_files "$_prep" + prepare_server -R + run_client "$_cmd" && fail "read-only $_desc succeeded" + postcondition "$_desc readonly" "$_expect_fail_post" +} + +perm_test() { + _op=$1 + _whitelist_ops=$2 + _cmd="$3" + _prep="$4" + _expect_success_post="$5" + _expect_fail_post="$6" + verbose "$tid: explicit $_op" + # Plain (no options, mostly to test that _cmd is good) + prepare_files "$_prep" + prepare_server + run_client "$_cmd" || fail "plain $_op failed" + postcondition "$_op no white/blacklists" "$_expect_success_post" + # Whitelist + prepare_files "$_prep" + prepare_server -p $_op,$_whitelist_ops + run_client "$_cmd" || fail "whitelisted $_op failed" + postcondition "$_op whitelisted" "$_expect_success_post" + # Blacklist + prepare_files "$_prep" + prepare_server -P $_op + run_client "$_cmd" && fail "blacklisted $_op succeeded" + postcondition "$_op blacklisted" "$_expect_fail_post" + # Whitelist with op missing. + prepare_files "$_prep" + prepare_server -p $_whitelist_ops + run_client "$_cmd" && fail "no whitelist $_op succeeded" + postcondition "$_op not in whitelist" "$_expect_fail_post" +} + +ro_test \ + "upload" \ + "put $DATA $COPY" \ + "" \ + "cmp $DATA $COPY" \ + "test ! -f $COPY" + +ro_test \ + "setstat" \ + "chmod 0700 $COPY" \ + "touch $COPY; chmod 0400 $COPY" \ + "test -x $COPY" \ + "test ! -x $COPY" + +ro_test \ + "rm" \ + "rm $COPY" \ + "touch $COPY" \ + "test ! -f $COPY" \ + "test -f $COPY" + +ro_test \ + "mkdir" \ + "mkdir ${COPY}.dd" \ + "" \ + "test -d ${COPY}.dd" \ + "test ! -d ${COPY}.dd" + +ro_test \ + "rmdir" \ + "rmdir ${COPY}.dd" \ + "mkdir ${COPY}.dd" \ + "test ! -d ${COPY}.dd" \ + "test -d ${COPY}.dd" + +ro_test \ + "posix-rename" \ + "rename $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -f ${COPY}.1 -a ! -f $COPY" \ + "test -f $COPY -a ! -f ${COPY}.1" + +ro_test \ + "oldrename" \ + "rename -l $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -f ${COPY}.1 -a ! -f $COPY" \ + "test -f $COPY -a ! -f ${COPY}.1" + +ro_test \ + "symlink" \ + "ln -s $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -h ${COPY}.1" \ + "test ! -h ${COPY}.1" + +ro_test \ + "hardlink" \ + "ln $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -f ${COPY}.1" \ + "test ! -f ${COPY}.1" + +# Test explicit permissions + +perm_test \ + "open" \ + "realpath,stat,lstat,read,close" \ + "get $DATA $COPY" \ + "" \ + "cmp $DATA $COPY" \ + "! cmp $DATA $COPY 2>/dev/null" + +perm_test \ + "read" \ + "realpath,stat,lstat,open,close" \ + "get $DATA $COPY" \ + "" \ + "cmp $DATA $COPY" \ + "! cmp $DATA $COPY 2>/dev/null" + +perm_test \ + "write" \ + "realpath,stat,lstat,open,close" \ + "put $DATA $COPY" \ + "" \ + "cmp $DATA $COPY" \ + "! cmp $DATA $COPY 2>/dev/null" + +perm_test \ + "lstat" \ + "realpath,stat,open,read,close" \ + "get $DATA $COPY" \ + "" \ + "cmp $DATA $COPY" \ + "! cmp $DATA $COPY 2>/dev/null" + +perm_test \ + "opendir" \ + "realpath,readdir,stat,lstat" \ + "ls -ln $OBJ" + +perm_test \ + "readdir" \ + "realpath,opendir,stat,lstat" \ + "ls -ln $OBJ" + +perm_test \ + "setstat" \ + "realpath,stat,lstat" \ + "chmod 0700 $COPY" \ + "touch $COPY; chmod 0400 $COPY" \ + "test -x $COPY" \ + "test ! -x $COPY" + +perm_test \ + "remove" \ + "realpath,stat,lstat" \ + "rm $COPY" \ + "touch $COPY" \ + "test ! -f $COPY" \ + "test -f $COPY" + +perm_test \ + "mkdir" \ + "realpath,stat,lstat" \ + "mkdir ${COPY}.dd" \ + "" \ + "test -d ${COPY}.dd" \ + "test ! -d ${COPY}.dd" + +perm_test \ + "rmdir" \ + "realpath,stat,lstat" \ + "rmdir ${COPY}.dd" \ + "mkdir ${COPY}.dd" \ + "test ! -d ${COPY}.dd" \ + "test -d ${COPY}.dd" + +# Can't readily test this because the client falls back to traditional rename. +# XXX maybe there is a behaviorial difference we can test for? +#perm_test \ +# "posix-rename" \ +# "realpath,stat,lstat" \ +# "rename $COPY ${COPY}.1" \ +# "touch $COPY" \ +# "test -f ${COPY}.1 -a ! -f $COPY" \ +# "test -f $COPY -a ! -f ${COPY}.1" + +perm_test \ + "rename" \ + "realpath,stat,lstat" \ + "rename -l $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -f ${COPY}.1 -a ! -f $COPY" \ + "test -f $COPY -a ! -f ${COPY}.1" + +perm_test \ + "symlink" \ + "realpath,stat,lstat" \ + "ln -s $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -h ${COPY}.1" \ + "test ! -h ${COPY}.1" + +perm_test \ + "hardlink" \ + "realpath,stat,lstat" \ + "ln $COPY ${COPY}.1" \ + "touch $COPY" \ + "test -f ${COPY}.1" \ + "test ! -f ${COPY}.1" + +perm_test \ + "statvfs" \ + "realpath,stat,lstat" \ + "df /" + +# XXX need good tests for: +# fstat +# fsetstat +# realpath +# stat +# readlink +# fstatvfs + +rm -rf ${COPY} ${COPY}.1 ${COPY}.dd + diff --git a/aosp/external/openssh/regress/sftp-uri.sh b/aosp/external/openssh/regress/sftp-uri.sh new file mode 100644 index 0000000000000000000000000000000000000000..7be104dfbb4042cdd32e8957348930fe9b5544c7 --- /dev/null +++ b/aosp/external/openssh/regress/sftp-uri.sh @@ -0,0 +1,63 @@ +# $OpenBSD: sftp-uri.sh,v 1.1 2017/10/24 19:33:32 millert Exp $ +# Placed in the Public Domain. + +tid="sftp-uri" + +#set -x + +COPY2=${OBJ}/copy2 +DIR=${COPY}.dd +DIR2=${COPY}.dd2 +SRC=`dirname ${SCRIPT}` + +sftpclean() { + rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} + mkdir ${DIR} ${DIR2} +} + +start_sshd -oForceCommand="internal-sftp -d /" + +# Remove Port and User from ssh_config, we want to rely on the URI +cp $OBJ/ssh_config $OBJ/ssh_config.orig +egrep -v '^ +(Port|User) +.*$' $OBJ/ssh_config.orig > $OBJ/ssh_config + +verbose "$tid: non-interactive fetch to local file" +sftpclean +${SFTP} -q -S "$SSH" -F $OBJ/ssh_config "sftp://${USER}@somehost:${PORT}/${DATA}" ${COPY} || fail "copy failed" +cmp ${DATA} ${COPY} || fail "corrupted copy" + +verbose "$tid: non-interactive fetch to local dir" +sftpclean +cp ${DATA} ${COPY} +${SFTP} -q -S "$SSH" -F $OBJ/ssh_config "sftp://${USER}@somehost:${PORT}/${COPY}" ${DIR} || fail "copy failed" +cmp ${COPY} ${DIR}/copy || fail "corrupted copy" + +verbose "$tid: put to remote directory (trailing slash)" +sftpclean +${SFTP} -q -S "$SSH" -F $OBJ/ssh_config -b - \ + "sftp://${USER}@somehost:${PORT}/${DIR}/" > /dev/null 2>&1 << EOF + version + put ${DATA} copy +EOF +r=$? +if [ $r -ne 0 ]; then + fail "sftp failed with $r" +else + cmp ${DATA} ${DIR}/copy || fail "corrupted copy" +fi + +verbose "$tid: put to remote directory (no slash)" +sftpclean +${SFTP} -q -S "$SSH" -F $OBJ/ssh_config -b - \ + "sftp://${USER}@somehost:${PORT}/${DIR}" > /dev/null 2>&1 << EOF + version + put ${DATA} copy +EOF +r=$? +if [ $r -ne 0 ]; then + fail "sftp failed with $r" +else + cmp ${DATA} ${DIR}/copy || fail "corrupted copy" +fi + +sftpclean diff --git a/aosp/external/openssh/regress/sftp.sh b/aosp/external/openssh/regress/sftp.sh new file mode 100644 index 0000000000000000000000000000000000000000..a5c88f58435934eb249d6cb59fdf3c93cacadf77 --- /dev/null +++ b/aosp/external/openssh/regress/sftp.sh @@ -0,0 +1,32 @@ +# $OpenBSD: sftp.sh,v 1.6 2017/10/30 21:59:43 djm Exp $ +# Placed in the Public Domain. + +tid="basic sftp put/get" + +SFTPCMDFILE=${OBJ}/batch +cat >$SFTPCMDFILE < /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "sftp failed with $r" + else + cmp $DATA ${COPY}.1 || fail "corrupted copy after get" + cmp $DATA ${COPY}.2 || fail "corrupted copy after put" + fi + done +done +rm -f ${COPY}.1 ${COPY}.2 +rm -f $SFTPCMDFILE diff --git a/aosp/external/openssh/regress/ssh-com-client.sh b/aosp/external/openssh/regress/ssh-com-client.sh new file mode 100644 index 0000000000000000000000000000000000000000..e4f80cf0aadfd055a646e6c751d42fd987679250 --- /dev/null +++ b/aosp/external/openssh/regress/ssh-com-client.sh @@ -0,0 +1,130 @@ +# $OpenBSD: ssh-com-client.sh,v 1.7 2013/05/17 04:29:14 dtucker Exp $ +# Placed in the Public Domain. + +tid="connect with ssh.com client" + +#TEST_COMBASE=/path/to/ssh/com/binaries +if [ "X${TEST_COMBASE}" = "X" ]; then + fatal '$TEST_COMBASE is not set' +fi + +VERSIONS=" + 2.1.0 + 2.2.0 + 2.3.0 + 2.3.1 + 2.4.0 + 3.0.0 + 3.1.0 + 3.2.0 + 3.2.2 + 3.2.3 + 3.2.5 + 3.2.9 + 3.2.9.1 + 3.3.0" + +# 2.0.10 2.0.12 2.0.13 don't like the test setup + +# setup authorized keys +SRC=`dirname ${SCRIPT}` +cp ${SRC}/dsa_ssh2.prv ${OBJ}/id.com +chmod 600 ${OBJ}/id.com +${SSHKEYGEN} -i -f ${OBJ}/id.com > $OBJ/id.openssh +chmod 600 ${OBJ}/id.openssh +${SSHKEYGEN} -y -f ${OBJ}/id.openssh > $OBJ/authorized_keys_$USER +${SSHKEYGEN} -e -f ${OBJ}/id.openssh > $OBJ/id.com.pub +echo IdKey ${OBJ}/id.com > ${OBJ}/id.list + +# we need a DSA host key +t=dsa +rm -f ${OBJ}/$t ${OBJ}/$t.pub +${SSHKEYGEN} -q -N '' -t $t -f ${OBJ}/$t +$SUDO cp $OBJ/$t $OBJ/host.$t +echo HostKey $OBJ/host.$t >> $OBJ/sshd_config + +# add hostkeys to known hosts +mkdir -p ${OBJ}/${USER}/hostkeys +HK=${OBJ}/${USER}/hostkeys/key_${PORT}_127.0.0.1 +${SSHKEYGEN} -e -f ${OBJ}/rsa.pub > ${HK}.ssh-rsa.pub +${SSHKEYGEN} -e -f ${OBJ}/dsa.pub > ${HK}.ssh-dss.pub + +cat > ${OBJ}/ssh2_config << EOF +*: + QuietMode yes + StrictHostKeyChecking yes + Port ${PORT} + User ${USER} + Host 127.0.0.1 + IdentityFile ${OBJ}/id.list + RandomSeedFile ${OBJ}/random_seed + UserConfigDirectory ${OBJ}/%U + AuthenticationSuccessMsg no + BatchMode yes + ForwardX11 no +EOF + +# we need a real server (no ProxyConnect option) +start_sshd + +# go for it +for v in ${VERSIONS}; do + ssh2=${TEST_COMBASE}/${v}/ssh2 + if [ ! -x ${ssh2} ]; then + continue + fi + verbose "ssh2 ${v}" + key=ssh-dss + skipcat=0 + case $v in + 2.1.*|2.3.0) + skipcat=1 + ;; + 3.0.*) + key=ssh-rsa + ;; + esac + cp ${HK}.$key.pub ${HK}.pub + + # check exit status + ${ssh2} -q -F ${OBJ}/ssh2_config somehost exit 42 + r=$? + if [ $r -ne 42 ]; then + fail "ssh2 ${v} exit code test failed (got $r, expected 42)" + fi + + # data transfer + rm -f ${COPY} + ${ssh2} -F ${OBJ}/ssh2_config somehost cat ${DATA} > ${COPY} + if [ $? -ne 0 ]; then + fail "ssh2 ${v} cat test (receive) failed" + fi + cmp ${DATA} ${COPY} || fail "ssh2 ${v} cat test (receive) data mismatch" + + # data transfer, again + if [ $skipcat -eq 0 ]; then + rm -f ${COPY} + cat ${DATA} | \ + ${ssh2} -F ${OBJ}/ssh2_config host "cat > ${COPY}" + if [ $? -ne 0 ]; then + fail "ssh2 ${v} cat test (send) failed" + fi + cmp ${DATA} ${COPY} || \ + fail "ssh2 ${v} cat test (send) data mismatch" + fi + + # no stderr after eof + rm -f ${COPY} + ${ssh2} -F ${OBJ}/ssh2_config somehost \ + exec sh -c \'"exec > /dev/null; sleep 1; echo bla 1>&2; exit 0"\' \ + 2> /dev/null + if [ $? -ne 0 ]; then + fail "ssh2 ${v} stderr test failed" + fi +done + +rm -rf ${OBJ}/${USER} +for i in ssh2_config random_seed dsa.pub dsa host.dsa \ + id.list id.com id.com.pub id.openssh; do + rm -f ${OBJ}/$i +done diff --git a/aosp/external/openssh/regress/ssh-com-keygen.sh b/aosp/external/openssh/regress/ssh-com-keygen.sh new file mode 100644 index 0000000000000000000000000000000000000000..29b02d94617f7956e357c28c5979597dec3c6828 --- /dev/null +++ b/aosp/external/openssh/regress/ssh-com-keygen.sh @@ -0,0 +1,74 @@ +# $OpenBSD: ssh-com-keygen.sh,v 1.4 2004/02/24 17:06:52 markus Exp $ +# Placed in the Public Domain. + +tid="ssh.com key import" + +#TEST_COMBASE=/path/to/ssh/com/binaries +if [ "X${TEST_COMBASE}" = "X" ]; then + fatal '$TEST_COMBASE is not set' +fi + +VERSIONS=" + 2.0.10 + 2.0.12 + 2.0.13 + 2.1.0 + 2.2.0 + 2.3.0 + 2.3.1 + 2.4.0 + 3.0.0 + 3.1.0 + 3.2.0 + 3.2.2 + 3.2.3 + 3.2.5 + 3.2.9 + 3.2.9.1 + 3.3.0" + +COMPRV=${OBJ}/comkey +COMPUB=${COMPRV}.pub +OPENSSHPRV=${OBJ}/opensshkey +OPENSSHPUB=${OPENSSHPRV}.pub + +# go for it +for v in ${VERSIONS}; do + keygen=${TEST_COMBASE}/${v}/ssh-keygen2 + if [ ! -x ${keygen} ]; then + continue + fi + types="dss" + case $v in + 2.3.1|3.*) + types="$types rsa" + ;; + esac + for t in $types; do + verbose "ssh-keygen $v/$t" + rm -f $COMPRV $COMPUB $OPENSSHPRV $OPENSSHPUB + ${keygen} -q -P -t $t ${COMPRV} > /dev/null 2>&1 + if [ $? -ne 0 ]; then + fail "${keygen} -t $t failed" + continue + fi + ${SSHKEYGEN} -if ${COMPUB} > ${OPENSSHPUB} + if [ $? -ne 0 ]; then + fail "import public key ($v/$t) failed" + continue + fi + ${SSHKEYGEN} -if ${COMPRV} > ${OPENSSHPRV} + if [ $? -ne 0 ]; then + fail "import private key ($v/$t) failed" + continue + fi + chmod 600 ${OPENSSHPRV} + ${SSHKEYGEN} -yf ${OPENSSHPRV} |\ + diff - ${OPENSSHPUB} + if [ $? -ne 0 ]; then + fail "public keys ($v/$t) differ" + fi + done +done + +rm -f $COMPRV $COMPUB $OPENSSHPRV $OPENSSHPUB diff --git a/aosp/external/openssh/regress/ssh-com-sftp.sh b/aosp/external/openssh/regress/ssh-com-sftp.sh new file mode 100644 index 0000000000000000000000000000000000000000..fabfa4983704e849298a5593a3c33c7bc3dc42b2 --- /dev/null +++ b/aosp/external/openssh/regress/ssh-com-sftp.sh @@ -0,0 +1,65 @@ +# $OpenBSD: ssh-com-sftp.sh,v 1.7 2013/05/17 04:29:14 dtucker Exp $ +# Placed in the Public Domain. + +tid="basic sftp put/get with ssh.com server" + +SFTPCMDFILE=${OBJ}/batch + +cat >$SFTPCMDFILE < /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "sftp failed with $r" + else + cmp $DATA ${COPY}.1 || fail "corrupted copy after get" + cmp $DATA ${COPY}.2 || fail "corrupted copy after put" + fi + done + done +done +rm -f ${COPY}.1 ${COPY}.2 +rm -f $SFTPCMDFILE diff --git a/aosp/external/openssh/regress/ssh-com.sh b/aosp/external/openssh/regress/ssh-com.sh new file mode 100644 index 0000000000000000000000000000000000000000..b1a2505d11351c2100db7ba98bd27bfce02d7670 --- /dev/null +++ b/aosp/external/openssh/regress/ssh-com.sh @@ -0,0 +1,119 @@ +# $OpenBSD: ssh-com.sh,v 1.10 2017/05/08 01:52:49 djm Exp $ +# Placed in the Public Domain. + +tid="connect to ssh.com server" + +#TEST_COMBASE=/path/to/ssh/com/binaries +if [ "X${TEST_COMBASE}" = "X" ]; then + fatal '$TEST_COMBASE is not set' +fi + +VERSIONS=" + 2.0.12 + 2.0.13 + 2.1.0 + 2.2.0 + 2.3.0 + 2.4.0 + 3.0.0 + 3.1.0 + 3.2.0 + 3.2.2 + 3.2.3 + 3.2.5 + 3.2.9 + 3.2.9.1 + 3.3.0" +# 2.0.10 does not support UserConfigDirectory +# 2.3.1 requires a config in $HOME/.ssh2 + +SRC=`dirname ${SCRIPT}` + +# ssh.com +cat << EOF > $OBJ/sshd2_config +#*: + # Port and ListenAddress are not used. + QuietMode yes + Port 4343 + ListenAddress 127.0.0.1 + UserConfigDirectory ${OBJ}/%U + Ciphers AnyCipher + PubKeyAuthentication yes + #AllowedAuthentications publickey + AuthorizationFile authorization + HostKeyFile ${SRC}/dsa_ssh2.prv + PublicHostKeyFile ${SRC}/dsa_ssh2.pub + RandomSeedFile ${OBJ}/random_seed + MaxConnections 0 + PermitRootLogin yes + VerboseMode no + CheckMail no + Ssh1Compatibility no +EOF + +# create client config +sed "s/HostKeyAlias.*/HostKeyAlias ssh2-localhost-with-alias/" \ + < $OBJ/ssh_config > $OBJ/ssh_config_com + +# we need a DSA key for +rm -f ${OBJ}/dsa ${OBJ}/dsa.pub +${SSHKEYGEN} -q -N '' -t dsa -f ${OBJ}/dsa + +# setup userdir, try rsa first +mkdir -p ${OBJ}/${USER} +cp /dev/null ${OBJ}/${USER}/authorization +for t in rsa dsa; do + ${SSHKEYGEN} -e -f ${OBJ}/$t.pub > ${OBJ}/${USER}/$t.com + echo Key $t.com >> ${OBJ}/${USER}/authorization + echo IdentityFile ${OBJ}/$t >> ${OBJ}/ssh_config_com +done + +# convert and append DSA hostkey +( + printf 'ssh2-localhost-with-alias,127.0.0.1,::1 ' + ${SSHKEYGEN} -if ${SRC}/dsa_ssh2.pub +) >> $OBJ/known_hosts + +# go for it +for v in ${VERSIONS}; do + sshd2=${TEST_COMBASE}/${v}/sshd2 + if [ ! -x ${sshd2} ]; then + continue + fi + trace "sshd2 ${v}" + PROXY="proxycommand ${sshd2} -qif ${OBJ}/sshd2_config 2> /dev/null" + ${SSH} -qF ${OBJ}/ssh_config_com -o "${PROXY}" dummy exit 0 + if [ $? -ne 0 ]; then + fail "ssh connect to sshd2 ${v} failed" + fi + + ciphers="3des-cbc" + macs="hmac-md5" + case $v in + 2.4.*) + ciphers="$ciphers cast128-cbc" + macs="$macs hmac-sha1 hmac-sha1-96 hmac-md5-96" + ;; + 3.*) + ciphers="$ciphers aes128-cbc cast128-cbc" + macs="$macs hmac-sha1 hmac-sha1-96 hmac-md5-96" + ;; + esac + #ciphers="3des-cbc" + for m in $macs; do + for c in $ciphers; do + trace "sshd2 ${v} cipher $c mac $m" + verbose "test ${tid}: sshd2 ${v} cipher $c mac $m" + ${SSH} -c $c -m $m -qF ${OBJ}/ssh_config_com -o "${PROXY}" dummy exit 0 + if [ $? -ne 0 ]; then + fail "ssh connect to sshd2 ${v} with $c/$m failed" + fi + done + done +done + +rm -rf ${OBJ}/${USER} +for i in sshd_config_proxy ssh_config_proxy random_seed \ + sshd2_config dsa.pub dsa ssh_config_com; do + rm -f ${OBJ}/$i +done diff --git a/aosp/external/openssh/regress/ssh2putty.sh b/aosp/external/openssh/regress/ssh2putty.sh new file mode 100644 index 0000000000000000000000000000000000000000..9b08310391ca0feb818f92a1402afa3cfbed0d68 --- /dev/null +++ b/aosp/external/openssh/regress/ssh2putty.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# $OpenBSD: ssh2putty.sh,v 1.9 2021/07/25 12:13:03 dtucker Exp $ + +if test "x$1" = "x" -o "x$2" = "x" -o "x$3" = "x" ; then + echo "Usage: ssh2putty hostname port ssh-private-key" + exit 1 +fi + +HOST=$1 +PORT=$2 +KEYFILE=$3 + +OPENSSL_BIN="${OPENSSL_BIN:-openssl}" + +# XXX - support DSA keys too +if grep "BEGIN RSA PRIVATE KEY" $KEYFILE >/dev/null 2>&1 ; then + : +else + echo "Unsupported private key format" + exit 1 +fi + +public_exponent=` + $OPENSSL_BIN rsa -noout -text -in $KEYFILE | grep ^publicExponent | + sed 's/.*(//;s/).*//' +` +test $? -ne 0 && exit 1 + +modulus=` + $OPENSSL_BIN rsa -noout -modulus -in $KEYFILE | grep ^Modulus= | + sed 's/^Modulus=/0x/' | tr A-Z a-z +` +test $? -ne 0 && exit 1 + +echo "rsa2@$PORT:$HOST $public_exponent,$modulus" + diff --git a/aosp/external/openssh/regress/sshcfgparse.sh b/aosp/external/openssh/regress/sshcfgparse.sh new file mode 100644 index 0000000000000000000000000000000000000000..504853d32db5fd86512cf4222daa678997e7ad24 --- /dev/null +++ b/aosp/external/openssh/regress/sshcfgparse.sh @@ -0,0 +1,119 @@ +# $OpenBSD: sshcfgparse.sh,v 1.9 2021/06/08 07:05:27 dtucker Exp $ +# Placed in the Public Domain. + +tid="ssh config parse" + +dsa=0 +for t in $SSH_KEYTYPES; do + case "$t" in + ssh-dss) dsa=1 ;; + esac +done + +expect_result_present() { + _str="$1" ; shift + for _expect in "$@" ; do + echo "$f" | tr ',' '\n' | grep "^$_expect\$" >/dev/null + if test $? -ne 0 ; then + fail "missing expected \"$_expect\" from \"$_str\"" + fi + done +} +expect_result_absent() { + _str="$1" ; shift + for _expect in "$@" ; do + echo "$f" | tr ',' '\n' | grep "^$_expect\$" >/dev/null + if test $? -eq 0 ; then + fail "unexpected \"$_expect\" present in \"$_str\"" + fi + done +} + +verbose "reparse minimal config" +(${SSH} -G -F $OBJ/ssh_config somehost >$OBJ/ssh_config.1 && + ${SSH} -G -F $OBJ/ssh_config.1 somehost >$OBJ/ssh_config.2 && + diff $OBJ/ssh_config.1 $OBJ/ssh_config.2) || fail "failed to reparse minimal" + +verbose "ssh -W opts" +f=`${SSH} -GF $OBJ/ssh_config host | awk '/exitonforwardfailure/{print $2}'` +test "$f" = "no" || fail "exitonforwardfailure default" +f=`${SSH} -GF $OBJ/ssh_config -W a:1 h | awk '/exitonforwardfailure/{print $2}'` +test "$f" = "yes" || fail "exitonforwardfailure enable" +f=`${SSH} -GF $OBJ/ssh_config -W a:1 -o exitonforwardfailure=no h | \ + awk '/exitonforwardfailure/{print $2}'` +test "$f" = "no" || fail "exitonforwardfailure override" + +f=`${SSH} -GF $OBJ/ssh_config host | awk '/clearallforwardings/{print $2}'` +test "$f" = "no" || fail "clearallforwardings default" +f=`${SSH} -GF $OBJ/ssh_config -W a:1 h | awk '/clearallforwardings/{print $2}'` +test "$f" = "yes" || fail "clearallforwardings enable" +f=`${SSH} -GF $OBJ/ssh_config -W a:1 -o clearallforwardings=no h | \ + awk '/clearallforwardings/{print $2}'` +test "$f" = "no" || fail "clearallforwardings override" + +verbose "user first match" +user=`awk '$1=="User" {print $2}' $OBJ/ssh_config` +f=`${SSH} -GF $OBJ/ssh_config host | awk '/^user /{print $2}'` +test "$f" = "$user" || fail "user from config, expected '$user' got '$f'" +f=`${SSH} -GF $OBJ/ssh_config -o user=foo -l bar baz@host | awk '/^user /{print $2}'` +test "$f" = "foo" || fail "user first match -oUser, expected 'foo' got '$f' " +f=`${SSH} -GF $OBJ/ssh_config -lbar baz@host user=foo baz@host | awk '/^user /{print $2}'` +test "$f" = "bar" || fail "user first match -l, expected 'bar' got '$f'" +f=`${SSH} -GF $OBJ/ssh_config baz@host -o user=foo -l bar baz@host | awk '/^user /{print $2}'` +test "$f" = "baz" || fail "user first match user@host, expected 'baz' got '$f'" + +verbose "pubkeyacceptedalgorithms" +# Default set +f=`${SSH} -GF none host | awk '/^pubkeyacceptedalgorithms /{print $2}'` +expect_result_present "$f" "ssh-ed25519" "ssh-ed25519-cert-v01.*" +expect_result_absent "$f" "ssh-dss" +# Explicit override +f=`${SSH} -GF none -opubkeyacceptedalgorithms=ssh-ed25519 host | \ + awk '/^pubkeyacceptedalgorithms /{print $2}'` +expect_result_present "$f" "ssh-ed25519" +expect_result_absent "$f" "ssh-ed25519-cert-v01.*" "ssh-dss" +# Removal from default set +f=`${SSH} -GF none -opubkeyacceptedalgorithms=-ssh-ed25519-cert* host | \ + awk '/^pubkeyacceptedalgorithms /{print $2}'` +expect_result_present "$f" "ssh-ed25519" +expect_result_absent "$f" "ssh-ed25519-cert-v01.*" "ssh-dss" +f=`${SSH} -GF none -opubkeyacceptedalgorithms=-ssh-ed25519 host | \ + awk '/^pubkeyacceptedalgorithms /{print $2}'` +expect_result_present "$f" "ssh-ed25519-cert-v01.*" +expect_result_absent "$f" "ssh-ed25519" "ssh-dss" +# Append to default set. +# This is not tested when built !WITH_OPENSSL +if [ "$dsa" = "1" ]; then + f=`${SSH} -GF none -opubkeyacceptedalgorithms=+ssh-dss-cert* host | \ + awk '/^pubkeyacceptedalgorithms /{print $2}'` + expect_result_present "$f" "ssh-ed25519" "ssh-dss-cert-v01.*" + expect_result_absent "$f" "ssh-dss" + f=`${SSH} -GF none -opubkeyacceptedalgorithms=+ssh-dss host | \ + awk '/^pubkeyacceptedalgorithms /{print $2}'` + expect_result_present "$f" "ssh-ed25519" "ssh-ed25519-cert-v01.*" "ssh-dss" + expect_result_absent "$f" "ssh-dss-cert-v01.*" +fi + +verbose "agentforwarding" +f=`${SSH} -GF none host | awk '/^forwardagent /{print$2}'` +expect_result_present "$f" "no" +f=`${SSH} -GF none -oforwardagent=no host | awk '/^forwardagent /{print$2}'` +expect_result_present "$f" "no" +f=`${SSH} -GF none -oforwardagent=yes host | awk '/^forwardagent /{print$2}'` +expect_result_present "$f" "yes" +f=`${SSH} -GF none '-oforwardagent=SSH_AUTH_SOCK.forward' host | awk '/^forwardagent /{print$2}'` +expect_result_present "$f" "SSH_AUTH_SOCK.forward" + +verbose "command line override" +cat >$OBJ/ssh_config.0 <>$log +exec "$@" -E$log diff --git a/aosp/external/openssh/regress/sshfp-connect.sh b/aosp/external/openssh/regress/sshfp-connect.sh new file mode 100644 index 0000000000000000000000000000000000000000..f78646922efea07de58cfb95e5703263629e82cb --- /dev/null +++ b/aosp/external/openssh/regress/sshfp-connect.sh @@ -0,0 +1,66 @@ +# $OpenBSD: sshfp-connect.sh,v 1.4 2021/09/01 00:50:27 dtucker Exp $ +# Placed in the Public Domain. + +# This test requires external setup and thus is skipped unless +# TEST_SSH_SSHFP_DOMAIN is set. It requires: +# 1) A DNSSEC-enabled domain, which TEST_SSH_SSHFP_DOMAIN points to. +# 2) A DNSSEC-validating resolver such as unwind(8). +# 3) The following SSHFP records with fingerprints from rsa_openssh.pub +# in that domain that are expected to succeed: +# sshtest: valid sha1 and sha256 fingerprints. +# sshtest-sha{1,256}, : valid fingerprints for that type only. +# and the following records that are expected to fail: +# sshtest-bad: invalid sha1 fingerprint and good sha256 fingerprint +# sshtest-sha{1,256}-bad: invalid fingerprints for that type only. +# +# sshtest IN SSHFP 1 1 99C79CC09F5F81069CC017CDF9552CFC94B3B929 +# sshtest IN SSHFP 1 2 E30D6B9EB7A4DE495324E4D5870B8220577993EA6AF417E8E4A4F1C5 BF01A9B6 +# sshtest-sha1 IN SSHFP 1 1 99C79CC09F5F81069CC017CDF9552CFC94B3B929 +# sshtest-sha256 IN SSHFP 1 2 E30D6B9EB7A4DE495324E4D5870B8220577993EA6AF417E8E4A4F1C5 BF01A9B6 +# sshtest-bad IN SSHFP 1 2 E30D6B9EB7A4DE495324E4D5870B8220577993EA6AF417E8E4A4F1C5 BF01A9B6 +# sshtest-bad IN SSHFP 1 1 99C79CC09F5F81069CC017CDF9552CFC94B3B928 +# sshtest-sha1-bad IN SSHFP 1 1 99D79CC09F5F81069CC017CDF9552CFC94B3B929 +# sshtest-sha256-bad IN SSHFP 1 2 E30D6B9EB7A4DE495324E4D5870B8220577993EA6AF417E8E4A4F1C5 BF01A9B5 + +tid="sshfp connect" + +if ! $SSH -Q key-plain | grep ssh-rsa >/dev/null; then + skip "RSA keys not supported." +elif [ -z "${TEST_SSH_SSHFP_DOMAIN}" ]; then + skip "TEST_SSH_SSHFP_DOMAIN not set." +else + # Set RSA host key to match fingerprints above. + mv $OBJ/sshd_proxy $OBJ/sshd_proxy.orig + $SUDO cp $SRC/rsa_openssh.prv $OBJ/host.ssh-rsa + $SUDO chmod 600 $OBJ/host.ssh-rsa + sed -e "s|$OBJ/ssh-rsa|$OBJ/host.ssh-rsa|" \ + $OBJ/sshd_proxy.orig > $OBJ/sshd_proxy + + # Zero out known hosts and key aliases to force use of SSHFP records. + > $OBJ/known_hosts + mv $OBJ/ssh_proxy $OBJ/ssh_proxy.orig + sed -e "/HostKeyAlias.*localhost-with-alias/d" \ + -e "/Hostname.*127.0.0.1/d" \ + $OBJ/ssh_proxy.orig > $OBJ/ssh_proxy + + for n in sshtest sshtest-sha1 sshtest-sha256; do + trace "sshfp connect $n good fingerprint" + host="${n}.dtucker.net" + opts="-F $OBJ/ssh_proxy -o VerifyHostKeyDNS=yes " + opts="$opts -o HostKeyAlgorithms=rsa-sha2-512,rsa-sha2-256" + host="${n}.${TEST_SSH_SSHFP_DOMAIN}" + SSH_CONNECTION=`${SSH} $opts $host 'echo $SSH_CONNECTION'` + if [ $? -ne 0 ]; then + fail "ssh sshfp connect failed" + fi + if [ "$SSH_CONNECTION" != "UNKNOWN 65535 UNKNOWN 65535" ]; then + fail "bad SSH_CONNECTION: $SSH_CONNECTION" + fi + + trace "sshfp connect $n bad fingerprint" + host="${n}-bad.${TEST_SSH_SSHFP_DOMAIN}" + if ${SSH} $opts ${host} true; then + fail "sshfp-connect succeeded with bad SSHFP record" + fi + done +fi diff --git a/aosp/external/openssh/regress/sshsig.sh b/aosp/external/openssh/regress/sshsig.sh new file mode 100644 index 0000000000000000000000000000000000000000..d4daa5c9dbde9b3ff4d360b7a010b40ffbee5893 --- /dev/null +++ b/aosp/external/openssh/regress/sshsig.sh @@ -0,0 +1,476 @@ +# $OpenBSD: sshsig.sh,v 1.14 2022/02/01 23:37:15 djm Exp $ +# Placed in the Public Domain. + +tid="sshsig" + +DATA2=$OBJ/${DATANAME}.2 +cat ${DATA} ${DATA} > ${DATA2} + +rm -f $OBJ/sshsig-*.sig $OBJ/wrong-key* $OBJ/sigca-key* + +sig_namespace="test-$$" +sig_principal="user-$$@example.com" + +# Make a "wrong key" +${SSHKEYGEN} -q -t ed25519 -f $OBJ/wrong-key \ + -C "wrong trousers, Grommit" -N '' \ + || fatal "couldn't generate key" +WRONG=$OBJ/wrong-key.pub + +# Make a CA key. +${SSHKEYGEN} -q -t ed25519 -f $OBJ/sigca-key -C "CA" -N '' \ + || fatal "couldn't generate key" +CA_PRIV=$OBJ/sigca-key +CA_PUB=$OBJ/sigca-key.pub + +trace "start agent" +eval `${SSHAGENT} ${EXTRA_AGENT_ARGS} -s` > /dev/null +r=$? +if [ $r -ne 0 ]; then + fatal "could not start ssh-agent: exit code $r" +fi + +SIGNKEYS="$SSH_KEYTYPES" +verbose "$tid: make certificates" +for t in $SSH_KEYTYPES ; do + ${SSHKEYGEN} -q -s $CA_PRIV -z $$ \ + -I "regress signature key for $USER" \ + -V "19840101:19860101" \ + -n $sig_principal $OBJ/${t} || \ + fatal "couldn't sign ${t}" + SIGNKEYS="$SIGNKEYS ${t}-cert.pub" +done + +for t in $SIGNKEYS; do + verbose "$tid: check signature for $t" + keybase=`basename $t .pub` + privkey=${OBJ}/`basename $t -cert.pub` + sigfile=${OBJ}/sshsig-${keybase}.sig + sigfile_agent=${OBJ}/sshsig-agent-${keybase}.sig + pubkey=${OBJ}/${keybase}.pub + cert=${OBJ}/${keybase}-cert.pub + sigfile_cert=${OBJ}/sshsig-${keybase}-cert.sig + + ${SSHKEYGEN} -vvv -Y sign -f ${OBJ}/$t -n $sig_namespace \ + -Ohashalg=sha1 < $DATA > $sigfile 2>/dev/null && \ + fail "sign using $t with bad hash algorithm succeeded" + + for h in default sha256 sha512 ; do + case "$h" in + default) hashalg_arg="" ;; + *) hashalg_arg="-Ohashalg=$h" ;; + esac + ${SSHKEYGEN} -vvv -Y sign -f ${OBJ}/$t -n $sig_namespace \ + $hashalg_arg < $DATA > $sigfile 2>/dev/null || \ + fail "sign using $t / $h failed" + (printf "$sig_principal " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 || \ + fail "failed signature for $t / $h key" + done + + (printf "$sig_principal namespaces=\"$sig_namespace,whatever\" "; + cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 || \ + fail "failed signature for $t key w/ limited namespace" + + (printf "$sig_principal namespaces=\"$sig_namespace,whatever\" "; + cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -q -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -O print-pubkey \ + < $DATA | cut -d' ' -f1-2 > ${OBJ}/${keybase}-fromsig.pub || \ + fail "failed signature for $t key w/ print-pubkey" + cut -d' ' -f1-2 ${OBJ}/${keybase}.pub > ${OBJ}/${keybase}-strip.pub + diff -r ${OBJ}/${keybase}-strip.pub ${OBJ}/${keybase}-fromsig.pub || \ + fail "print-pubkey differs from signature key" + + # Invalid option + (printf "$sig_principal octopus " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with bad signers option" + + # Wrong key trusted. + (printf "$sig_principal " ; cat $WRONG) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with wrong key trusted" + + # incorrect data + (printf "$sig_principal " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA2 >/dev/null 2>&1 && \ + fail "passed signature for wrong data with $t key" + + # wrong principal in signers + (printf "josef.k@example.com " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with wrong principal" + + # wrong namespace + (printf "$sig_principal " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n COWS_COWS_COWS \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with wrong namespace" + + # namespace excluded by option + (printf "$sig_principal namespaces=\"whatever\" " ; + cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with excluded namespace" + + ( printf "$sig_principal " ; + printf "valid-after=\"19800101\",valid-before=\"19900101\" " ; + cat $pubkey) > $OBJ/allowed_signers + + # key lifespan valid + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19850101 \ + < $DATA >/dev/null 2>&1 || \ + fail "failed signature for $t key with valid expiry interval" + # key not yet valid + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19790101 \ + < $DATA >/dev/null 2>&1 && \ + fail "failed signature for $t not-yet-valid key" + # key expired + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19910101 \ + < $DATA >/dev/null 2>&1 && \ + fail "failed signature for $t with expired key" + # NB. assumes we're not running this test in the 1980s + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "failed signature for $t with expired key" + + # key lifespan valid + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19850101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed find-principals for $t key with valid expiry interval" + # key not yet valid + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19790101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "failed find-principals for $t not-yet-valid key" + # key expired + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19990101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "failed find-principals for $t with expired key" + # NB. assumes we're not running this test in the 1980s + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "failed find-principals for $t with expired key" + + # public key in revoked keys file + cat $pubkey > $OBJ/revoked_keys + (printf "$sig_principal namespaces=\"whatever\" " ; + cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -r $OBJ/revoked_keys \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key, but key is in revoked_keys" + + # public key not revoked, but others are present in revoked_keysfile + cat $WRONG > $OBJ/revoked_keys + (printf "$sig_principal " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -r $OBJ/revoked_keys \ + < $DATA >/dev/null 2>&1 || \ + fail "couldn't verify signature for $t key, but key not in revoked_keys" + + # check-novalidate with valid data + ${SSHKEYGEN} -vvv -Y check-novalidate -s $sigfile -n $sig_namespace \ + < $DATA >/dev/null 2>&1 || \ + fail "failed to check valid signature for $t key" + + # check-novalidate with invalid data + ${SSHKEYGEN} -vvv -Y check-novalidate -s $sigfile -n $sig_namespace \ + < $DATA2 >/dev/null 2>&1 && \ + fail "succeeded checking signature for $t key with invalid data" + + # find-principals with valid public key + (printf "$sig_principal " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed to find valid principals in allowed_signers" + + # find-principals with wrong key not in allowed_signers + (printf "$sig_principal " ; cat $WRONG) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "succeeded finding principal with invalid signers file" + + # find-principals with a configured namespace but none on command-line + (printf "$sig_principal " ; + printf "namespaces=\"test1,test2\" "; + cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed finding principal when namespaces are configured" + + # Check signing keys using ssh-agent. + ${SSHADD} -D >/dev/null 2>&1 # Remove all previously-loaded keys. + ${SSHADD} ${privkey} > /dev/null 2>&1 || fail "ssh-add failed" + + # Move private key to ensure agent key is used + mv ${privkey} ${privkey}.tmp + + ${SSHKEYGEN} -vvv -Y sign -f $pubkey -n $sig_namespace \ + < $DATA > $sigfile_agent 2>/dev/null || \ + fail "ssh-agent based sign using $pubkey failed" + ${SSHKEYGEN} -vvv -Y check-novalidate -s $sigfile_agent \ + -n $sig_namespace < $DATA >/dev/null 2>&1 || \ + fail "failed to check valid signature for $t key" + + # Move private key back + mv ${privkey}.tmp ${privkey} + + # Duplicate principals & keys in allowed_signers but with different validities + ( printf "$sig_principal " ; + printf "valid-after=\"19800101\",valid-before=\"19900101\" " ; + cat $pubkey; + printf "${sig_principal} " ; + printf "valid-after=\"19850101\",valid-before=\"20000101\" " ; + cat $pubkey) > $OBJ/allowed_signers + + # find-principals outside of any validity lifespan + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="20100101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "succeeded find-principals for $t verify-time outside of validity" + # find-principals matching only the first lifespan + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19830101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed find-principals for $t verify-time within first span" + # find-principals matching both lifespans + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19880101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed find-principals for $t verify-time within both spans" + # find-principals matching only the second lifespan + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19950101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed find-principals for $t verify-time within second span" + + # verify outside of any validity lifespan + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -Overify-time="20100101" -I $sig_principal \ + -r $OBJ/revoked_keys -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "succeeded verify for $t verify-time outside of validity" + # verify matching only the first lifespan + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -Overify-time="19830101" -I $sig_principal \ + -r $OBJ/revoked_keys -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 || \ + fail "failed verify for $t verify-time within first span" + # verify matching both lifespans + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -Overify-time="19880101" -I $sig_principal \ + -r $OBJ/revoked_keys -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 || \ + fail "failed verify for $t verify-time within both spans" + # verify matching only the second lifespan + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -Overify-time="19950101" -I $sig_principal \ + -r $OBJ/revoked_keys -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 || \ + fail "failed verify for $t verify-time within second span" + + # Remaining tests are for certificates only. + case "$keybase" in + *-cert) ;; + *) continue ;; + esac + + # Check key lifespan on find-principals when using the CA + ( printf "$sig_principal " ; + printf "cert-authority,valid-after=\"19800101\",valid-before=\"19900101\" "; + cat $CA_PUB) > $OBJ/allowed_signers + # key lifespan valid + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19850101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed find-principals for $t key with valid expiry interval" + # key not yet valid + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19790101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "failed find-principals for $t not-yet-valid key" + # key expired + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time="19990101" \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "failed find-principals for $t with expired key" + # NB. assumes we're not running this test in the 1980s + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -f $OBJ/allowed_signers >/dev/null 2>&1 && \ + fail "failed find-principals for $t with expired key" + + # correct CA key + (printf "$sig_principal cert-authority " ; + cat $CA_PUB) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19850101 \ + < $DATA >/dev/null 2>&1 || \ + fail "failed signature for $t cert" + + # find-principals + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time=19850101 \ + -f $OBJ/allowed_signers >/dev/null 2>&1 || \ + fail "failed find-principals for $t with ca key" + + # CA with wildcard principal + (printf "*@example.com cert-authority " ; + cat $CA_PUB) > $OBJ/allowed_signers + # find-principals CA with wildcard principal + ${SSHKEYGEN} -vvv -Y find-principals -s $sigfile \ + -Overify-time=19850101 \ + -f $OBJ/allowed_signers 2>/dev/null | \ + fgrep "$sig_principal" >/dev/null || \ + fail "failed find-principals for $t with ca key using wildcard principal" + + # verify CA with wildcard principal + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19850101 \ + < $DATA >/dev/null 2>&1 || \ + fail "failed signature for $t cert using wildcard principal" + + # signing key listed as cert-authority + (printf "$sig_principal cert-authority " ; + cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature with $t key listed as CA" + + # CA key not flagged cert-authority + (printf "$sig_principal " ; cat $CA_PUB) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t cert with CA not marked" + + # mismatch between cert principal and file + (printf "josef.k@example.com cert-authority " ; + cat $CA_PUB) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t cert with wrong principal" + + # Cert valid but CA revoked + cat $CA_PUB > $OBJ/revoked_keys + (printf "$sig_principal " ; cat $pubkey) > $OBJ/allowed_signers + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -r $OBJ/revoked_keys \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key, but CA key in revoked_keys" + + # Set lifespan of CA key and verify signed user certs behave accordingly + ( printf "$sig_principal " ; + printf "cert-authority,valid-after=\"19800101\",valid-before=\"19900101\" " ; + cat $CA_PUB) > $OBJ/allowed_signers + + # CA key lifespan valid + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19850101 \ + < $DATA >/dev/null 2>&1 >/dev/null 2>&1 || \ + fail "failed signature for $t key with valid CA expiry interval" + # CA lifespan is valid but user key not yet valid + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19810101 \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with valid CA expiry interval but not yet valid cert" + # CA lifespan is valid but user key expired + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19890101 \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with valid CA expiry interval but expired cert" + # CA key not yet valid + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19790101 \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t not-yet-valid CA key" + # CA key expired + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19910101 \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t with expired CA key" + # NB. assumes we're not running this test in the 1980s + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t with expired CA key" + + # Set lifespan of CA outside of the cert validity + ( printf "$sig_principal " ; + printf "cert-authority,valid-after=\"19800101\",valid-before=\"19820101\" " ; + cat $CA_PUB) > $OBJ/allowed_signers + # valid cert validity but expired CA + ${SSHKEYGEN} -vvv -Y verify -s $sigfile -n $sig_namespace \ + -I $sig_principal -f $OBJ/allowed_signers \ + -Overify-time=19840101 \ + < $DATA >/dev/null 2>&1 && \ + fail "accepted signature for $t key with expired CA but valid cert" + +done + +# Test key independant match-principals +( + printf "principal1 " ; cat $pubkey; + printf "princi* " ; cat $pubkey; + printf "unique " ; cat $pubkey; +) > $OBJ/allowed_signers + +verbose "$tid: match principals" +${SSHKEYGEN} -Y match-principals -f $OBJ/allowed_signers -I "unique" | \ + fgrep "unique" >/dev/null || \ + fail "faild to match static principal" + +${SSHKEYGEN} -Y match-principals -f $OBJ/allowed_signers -I "princip" | \ + fgrep "princi*" >/dev/null || \ + fail "faild to match wildcard principal" + +${SSHKEYGEN} -Y match-principals -f $OBJ/allowed_signers -I "principal1" | \ + fgrep -e "principal1" -e "princi*" >/dev/null || \ + fail "faild to match static and wildcard principal" +verbose "$tid: nomatch principals" +for x in princ prince unknown ; do + ${SSHKEYGEN} -Y match-principals -f $OBJ/allowed_signers \ + -I $x >/dev/null 2>&1 && \ + fail "succeeded to match unknown principal \"$x\"" +done + +trace "kill agent" +${SSHAGENT} -k > /dev/null + diff --git a/aosp/external/openssh/regress/stderr-after-eof.sh b/aosp/external/openssh/regress/stderr-after-eof.sh new file mode 100644 index 0000000000000000000000000000000000000000..9065245e84fb5dbdece9f72a2739ca2854b11fd6 --- /dev/null +++ b/aosp/external/openssh/regress/stderr-after-eof.sh @@ -0,0 +1,24 @@ +# $OpenBSD: stderr-after-eof.sh,v 1.3 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="stderr data after eof" + +# setup data +rm -f ${DATA} ${COPY} +cp /dev/null ${DATA} +for i in 1 2 3 4 5 6; do + (date;echo $i) | md5 >> ${DATA} +done + +${SSH} -F $OBJ/ssh_proxy otherhost \ + exec sh -c \'"exec > /dev/null; sleep 2; cat ${DATA} 1>&2 $s"\' \ + 2> ${COPY} +r=$? +if [ $r -ne 0 ]; then + fail "ssh failed with exit code $r" +fi +egrep 'Disconnecting: Received extended_data after EOF' ${COPY} && + fail "ext data received after eof" +cmp ${DATA} ${COPY} || fail "stderr corrupt" + +rm -f ${DATA} ${COPY} diff --git a/aosp/external/openssh/regress/stderr-data.sh b/aosp/external/openssh/regress/stderr-data.sh new file mode 100644 index 0000000000000000000000000000000000000000..0ceb72b3a298b67ebeb90eb659e09110aab4dd2d --- /dev/null +++ b/aosp/external/openssh/regress/stderr-data.sh @@ -0,0 +1,27 @@ +# $OpenBSD: stderr-data.sh,v 1.5 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="stderr data transfer" + +for n in '' -n; do + verbose "test $tid: ($n)" + ${SSH} $n -F $OBJ/ssh_proxy otherhost exec \ + sh -c \'"exec > /dev/null; sleep 3; cat ${DATA} 1>&2 $s"\' \ + 2> ${COPY} + r=$? + if [ $r -ne 0 ]; then + fail "ssh failed with exit code $r" + fi + cmp ${DATA} ${COPY} || fail "stderr corrupt" + rm -f ${COPY} + + ${SSH} $n -F $OBJ/ssh_proxy otherhost exec \ + sh -c \'"echo a; exec > /dev/null; sleep 3; cat ${DATA} 1>&2 $s"\' \ + > /dev/null 2> ${COPY} + r=$? + if [ $r -ne 0 ]; then + fail "ssh failed with exit code $r" + fi + cmp ${DATA} ${COPY} || fail "stderr corrupt" + rm -f ${COPY} +done diff --git a/aosp/external/openssh/regress/t11.ok b/aosp/external/openssh/regress/t11.ok new file mode 100644 index 0000000000000000000000000000000000000000..1925bb470fc0650f7fd74fbc21f77c6e52e48c04 --- /dev/null +++ b/aosp/external/openssh/regress/t11.ok @@ -0,0 +1 @@ +SHA256:4w1rnrek3klTJOTVhwuCIFd5k+pq9Bfo5KTxxb8BqbY diff --git a/aosp/external/openssh/regress/t4.ok b/aosp/external/openssh/regress/t4.ok new file mode 100644 index 0000000000000000000000000000000000000000..4631ea8c709430e8211a789d3b0750deb8980f4c --- /dev/null +++ b/aosp/external/openssh/regress/t4.ok @@ -0,0 +1 @@ +MD5:3b:dd:44:e9:49:18:84:95:f1:e7:33:6b:9d:93:b1:36 diff --git a/aosp/external/openssh/regress/t5.ok b/aosp/external/openssh/regress/t5.ok new file mode 100644 index 0000000000000000000000000000000000000000..bd622f300c07b072f424c21c58a71bf246dc23cc --- /dev/null +++ b/aosp/external/openssh/regress/t5.ok @@ -0,0 +1 @@ +xokes-lylis-byleh-zebib-kalus-bihas-tevah-haroz-suhar-foved-noxex diff --git a/aosp/external/openssh/regress/test-exec.sh b/aosp/external/openssh/regress/test-exec.sh new file mode 100644 index 0000000000000000000000000000000000000000..9fb02d1cbc8c1cf06c4452b1dc3d6fca55880118 --- /dev/null +++ b/aosp/external/openssh/regress/test-exec.sh @@ -0,0 +1,769 @@ +# $OpenBSD: test-exec.sh,v 1.89 2022/01/06 22:14:25 dtucker Exp $ +# Placed in the Public Domain. + +#SUDO=sudo + +if [ ! -x "$TEST_SSH_ELAPSED_TIMES" ]; then + STARTTIME=`date '+%s'` +fi + +if [ ! -z "$TEST_SSH_PORT" ]; then + PORT="$TEST_SSH_PORT" +else + PORT=4242 +fi + +OBJ=$1 +if [ "x$OBJ" = "x" ]; then + echo '$OBJ not defined' + exit 2 +fi +if [ ! -d $OBJ ]; then + echo "not a directory: $OBJ" + exit 2 +fi +SCRIPT=$2 +if [ "x$SCRIPT" = "x" ]; then + echo '$SCRIPT not defined' + exit 2 +fi +if [ ! -f $SCRIPT ]; then + echo "not a file: $SCRIPT" + exit 2 +fi +if $TEST_SHELL -n $SCRIPT; then + true +else + echo "syntax error in $SCRIPT" + exit 2 +fi +unset SSH_AUTH_SOCK + +# Portable-specific settings. + +if [ -x /usr/ucb/whoami ]; then + USER=`/usr/ucb/whoami` +elif whoami >/dev/null 2>&1; then + USER=`whoami` +elif logname >/dev/null 2>&1; then + USER=`logname` +else + USER=`id -un` +fi +if test -z "$LOGNAME"; then + LOGNAME="${USER}" + export LOGNAME +fi + +# Unbreak GNU head(1) +_POSIX2_VERSION=199209 +export _POSIX2_VERSION + +case `uname -s 2>/dev/null` in +OSF1*) + BIN_SH=xpg4 + export BIN_SH + ;; +CYGWIN*) + os=cygwin + ;; +esac + +# If configure tells us to use a different egrep, create a wrapper function +# to call it. This means we don't need to change all the tests that depend +# on a good implementation. +if test "x${EGREP}" != "x"; then + egrep () +{ + ${EGREP} "$@" +} +fi + +SRC=`dirname ${SCRIPT}` + +# defaults +SSH=ssh +SSHD=sshd +SSHAGENT=ssh-agent +SSHADD=ssh-add +SSHKEYGEN=ssh-keygen +SSHKEYSCAN=ssh-keyscan +SFTP=sftp +SFTPSERVER=/usr/libexec/openssh/sftp-server +SCP=scp + +# Set by make_tmpdir() on demand (below). +SSH_REGRESS_TMP= + +# Interop testing +PLINK=plink +PUTTYGEN=puttygen +CONCH=conch + +# Tools used by multiple tests +NC=$OBJ/netcat +OPENSSL_BIN="${OPENSSL_BIN:-openssl}" + +if [ "x$TEST_SSH_SSH" != "x" ]; then + SSH="${TEST_SSH_SSH}" +fi +if [ "x$TEST_SSH_SSHD" != "x" ]; then + SSHD="${TEST_SSH_SSHD}" +fi +if [ "x$TEST_SSH_SSHAGENT" != "x" ]; then + SSHAGENT="${TEST_SSH_SSHAGENT}" +fi +if [ "x$TEST_SSH_SSHADD" != "x" ]; then + SSHADD="${TEST_SSH_SSHADD}" +fi +if [ "x$TEST_SSH_SSHKEYGEN" != "x" ]; then + SSHKEYGEN="${TEST_SSH_SSHKEYGEN}" +fi +if [ "x$TEST_SSH_SSHKEYSCAN" != "x" ]; then + SSHKEYSCAN="${TEST_SSH_SSHKEYSCAN}" +fi +if [ "x$TEST_SSH_SFTP" != "x" ]; then + SFTP="${TEST_SSH_SFTP}" +fi +if [ "x$TEST_SSH_SFTPSERVER" != "x" ]; then + SFTPSERVER="${TEST_SSH_SFTPSERVER}" +fi +if [ "x$TEST_SSH_SCP" != "x" ]; then + SCP="${TEST_SSH_SCP}" +fi +if [ "x$TEST_SSH_PLINK" != "x" ]; then + # Find real binary, if it exists + case "${TEST_SSH_PLINK}" in + /*) PLINK="${TEST_SSH_PLINK}" ;; + *) PLINK=`which ${TEST_SSH_PLINK} 2>/dev/null` ;; + esac +fi +if [ "x$TEST_SSH_PUTTYGEN" != "x" ]; then + # Find real binary, if it exists + case "${TEST_SSH_PUTTYGEN}" in + /*) PUTTYGEN="${TEST_SSH_PUTTYGEN}" ;; + *) PUTTYGEN=`which ${TEST_SSH_PUTTYGEN} 2>/dev/null` ;; + esac +fi +if [ "x$TEST_SSH_CONCH" != "x" ]; then + # Find real binary, if it exists + case "${TEST_SSH_CONCH}" in + /*) CONCH="${TEST_SSH_CONCH}" ;; + *) CONCH=`which ${TEST_SSH_CONCH} 2>/dev/null` ;; + esac +fi +if [ "x$TEST_SSH_PKCS11_HELPER" != "x" ]; then + SSH_PKCS11_HELPER="${TEST_SSH_PKCS11_HELPER}" +fi +if [ "x$TEST_SSH_SK_HELPER" != "x" ]; then + SSH_SK_HELPER="${TEST_SSH_SK_HELPER}" +fi +if [ "x$TEST_SSH_OPENSSL" != "x" ]; then + OPENSSL_BIN="${TEST_SSH_OPENSSL}" +fi + +# Path to sshd must be absolute for rexec +case "$SSHD" in +/*) ;; +*) SSHD=`which $SSHD` ;; +esac + +case "$SSHAGENT" in +/*) ;; +*) SSHAGENT=`which $SSHAGENT` ;; +esac + +# Record the actual binaries used. +SSH_BIN=${SSH} +SSHD_BIN=${SSHD} +SSHAGENT_BIN=${SSHAGENT} +SSHADD_BIN=${SSHADD} +SSHKEYGEN_BIN=${SSHKEYGEN} +SSHKEYSCAN_BIN=${SSHKEYSCAN} +SFTP_BIN=${SFTP} +SFTPSERVER_BIN=${SFTPSERVER} +SCP_BIN=${SCP} + +if [ "x$USE_VALGRIND" != "x" ]; then + rm -rf $OBJ/valgrind-out $OBJ/valgrind-vgdb + mkdir -p $OBJ/valgrind-out $OBJ/valgrind-vgdb + # When using sudo ensure low-priv tests can write pipes and logs. + if [ "x$SUDO" != "x" ]; then + chmod 777 $OBJ/valgrind-out $OBJ/valgrind-vgdb + fi + VG_TEST=`basename $SCRIPT .sh` + + # Some tests are difficult to fix. + case "$VG_TEST" in + reexec) + VG_SKIP=1 ;; + sftp-chroot) + if [ "x${SUDO}" != "x" ]; then + VG_SKIP=1 + fi ;; + esac + + if [ x"$VG_SKIP" = "x" ]; then + VG_LEAK="--leak-check=no" + if [ x"$VALGRIND_CHECK_LEAKS" != "x" ]; then + VG_LEAK="--leak-check=full" + fi + VG_IGNORE="/bin/*,/sbin/*,/usr/*,/var/*" + VG_LOG="$OBJ/valgrind-out/${VG_TEST}." + VG_OPTS="--track-origins=yes $VG_LEAK" + VG_OPTS="$VG_OPTS --trace-children=yes" + VG_OPTS="$VG_OPTS --trace-children-skip=${VG_IGNORE}" + VG_OPTS="$VG_OPTS --vgdb-prefix=$OBJ/valgrind-vgdb/" + VG_PATH="valgrind" + if [ "x$VALGRIND_PATH" != "x" ]; then + VG_PATH="$VALGRIND_PATH" + fi + VG="$VG_PATH $VG_OPTS" + SSH="$VG --log-file=${VG_LOG}ssh.%p $SSH" + SSHD="$VG --log-file=${VG_LOG}sshd.%p $SSHD" + SSHAGENT="$VG --log-file=${VG_LOG}ssh-agent.%p $SSHAGENT" + SSHADD="$VG --log-file=${VG_LOG}ssh-add.%p $SSHADD" + SSHKEYGEN="$VG --log-file=${VG_LOG}ssh-keygen.%p $SSHKEYGEN" + SSHKEYSCAN="$VG --log-file=${VG_LOG}ssh-keyscan.%p $SSHKEYSCAN" + SFTP="$VG --log-file=${VG_LOG}sftp.%p ${SFTP}" + SCP="$VG --log-file=${VG_LOG}scp.%p $SCP" + cat > $OBJ/valgrind-sftp-server.sh << EOF +#!/bin/sh +exec $VG --log-file=${VG_LOG}sftp-server.%p $SFTPSERVER "\$@" +EOF + chmod a+rx $OBJ/valgrind-sftp-server.sh + SFTPSERVER="$OBJ/valgrind-sftp-server.sh" + fi +fi + +# Logfiles. +# SSH_LOGFILE should be the debug output of ssh(1) only +# SSHD_LOGFILE should be the debug output of sshd(8) only +# REGRESS_LOGFILE is the output of the test itself stdout and stderr +if [ "x$TEST_SSH_LOGFILE" = "x" ]; then + TEST_SSH_LOGFILE=$OBJ/ssh.log +fi +if [ "x$TEST_SSHD_LOGFILE" = "x" ]; then + TEST_SSHD_LOGFILE=$OBJ/sshd.log +fi +if [ "x$TEST_REGRESS_LOGFILE" = "x" ]; then + TEST_REGRESS_LOGFILE=$OBJ/regress.log +fi + +# truncate logfiles +>$TEST_SSH_LOGFILE +>$TEST_SSHD_LOGFILE +>$TEST_REGRESS_LOGFILE + +# Create wrapper ssh with logging. We can't just specify "SSH=ssh -E..." +# because sftp and scp don't handle spaces in arguments. scp and sftp like +# to use -q so we remove those to preserve our debug logging. In the rare +# instance where -q is desirable -qq is equivalent and is not removed. +SSHLOGWRAP=$OBJ/ssh-log-wrapper.sh +cat >$SSHLOGWRAP <>${TEST_SSH_LOGFILE} +for i in "\$@";do shift;case "\$i" in -q):;; *) set -- "\$@" "\$i";;esac;done +exec ${SSH} -E${TEST_SSH_LOGFILE} "\$@" +EOD + +chmod a+rx $OBJ/ssh-log-wrapper.sh +REAL_SSH="$SSH" +REAL_SSHD="$SSHD" +SSH="$SSHLOGWRAP" + +# Some test data. We make a copy because some tests will overwrite it. +# The tests may assume that $DATA exists and is writable and $COPY does +# not exist. Tests requiring larger data files can call increase_datafile_size +# [kbytes] to ensure the file is at least that large. +DATANAME=data +DATA=$OBJ/${DATANAME} +cat ${SSHAGENT_BIN} >${DATA} +chmod u+w ${DATA} +COPY=$OBJ/copy +rm -f ${COPY} + +increase_datafile_size() +{ + while [ `du -k ${DATA} | cut -f1` -lt $1 ]; do + cat ${SSHAGENT_BIN} >>${DATA} + done +} + +# these should be used in tests +export SSH SSHD SSHAGENT SSHADD SSHKEYGEN SSHKEYSCAN SFTP SFTPSERVER SCP +export SSH_PKCS11_HELPER SSH_SK_HELPER +#echo $SSH $SSHD $SSHAGENT $SSHADD $SSHKEYGEN $SSHKEYSCAN $SFTP $SFTPSERVER $SCP + +# Portable specific functions +have_prog() +{ + saved_IFS="$IFS" + IFS=":" + for i in $PATH + do + if [ -x $i/$1 ]; then + IFS="$saved_IFS" + return 0 + fi + done + IFS="$saved_IFS" + return 1 +} + +jot() { + awk "BEGIN { for (i = $2; i < $2 + $1; i++) { printf \"%d\n\", i } exit }" +} +if [ ! -x "`which rev`" ]; then +rev() +{ + awk '{for (i=length; i>0; i--) printf "%s", substr($0, i, 1); print ""}' +} +fi + +# Check whether preprocessor symbols are defined in config.h. +config_defined () +{ + str=$1 + while test "x$2" != "x" ; do + str="$str|$2" + shift + done + egrep "^#define.*($str)" ${BUILDDIR}/config.h >/dev/null 2>&1 +} + +md5 () { + if have_prog md5sum; then + md5sum + elif have_prog openssl; then + openssl md5 + elif have_prog cksum; then + cksum + elif have_prog sum; then + sum + elif [ -x ${OPENSSL_BIN} ]; then + ${OPENSSL_BIN} md5 + else + wc -c + fi +} + +# Some platforms don't have hostname at all, but on others uname -n doesn't +# provide the fully qualified name we need, so in the former case we create +# our own hostname function. +if ! have_prog hostname; then + hostname() { + uname -n + } +fi + +make_tmpdir () +{ + SSH_REGRESS_TMP="$($OBJ/mkdtemp openssh-XXXXXXXX)" || \ + fatal "failed to create temporary directory" +} +# End of portable specific functions + +stop_sshd () +{ + if [ -f $PIDFILE ]; then + pid=`$SUDO cat $PIDFILE` + if [ "X$pid" = "X" ]; then + echo no sshd running + else + if [ $pid -lt 2 ]; then + echo bad pid for sshd: $pid + else + $SUDO kill $pid + trace "wait for sshd to exit" + i=0; + while [ -f $PIDFILE -a $i -lt 5 ]; do + i=`expr $i + 1` + sleep $i + done + if test -f $PIDFILE; then + if $SUDO kill -0 $pid; then + echo "sshd didn't exit " \ + "port $PORT pid $pid" + else + echo "sshd died without cleanup" + fi + exit 1 + fi + fi + fi + fi +} + +# helper +cleanup () +{ + if [ "x$SSH_PID" != "x" ]; then + if [ $SSH_PID -lt 2 ]; then + echo bad pid for ssh: $SSH_PID + else + kill $SSH_PID + fi + fi + if [ "x$SSH_REGRESS_TMP" != "x" ]; then + rm -rf "$SSH_REGRESS_TMP" + fi + stop_sshd + if [ ! -z "$TEST_SSH_ELAPSED_TIMES" ]; then + now=`date '+%s'` + elapsed=$(($now - $STARTTIME)) + echo elapsed $elapsed `basename $SCRIPT .sh` + fi +} + +start_debug_log () +{ + echo "trace: $@" >$TEST_REGRESS_LOGFILE + echo "trace: $@" >$TEST_SSH_LOGFILE + echo "trace: $@" >$TEST_SSHD_LOGFILE +} + +save_debug_log () +{ + echo $@ >>$TEST_REGRESS_LOGFILE + echo $@ >>$TEST_SSH_LOGFILE + echo $@ >>$TEST_SSHD_LOGFILE + (cat $TEST_REGRESS_LOGFILE; echo) >>$OBJ/failed-regress.log + (cat $TEST_SSH_LOGFILE; echo) >>$OBJ/failed-ssh.log + (cat $TEST_SSHD_LOGFILE; echo) >>$OBJ/failed-sshd.log +} + +trace () +{ + start_debug_log $@ + if [ "X$TEST_SSH_TRACE" = "Xyes" ]; then + echo "$@" + fi +} + +verbose () +{ + start_debug_log $@ + if [ "X$TEST_SSH_QUIET" != "Xyes" ]; then + echo "$@" + fi +} + +fail () +{ + save_debug_log "FAIL: $@" + RESULT=1 + echo "$@" + if test "x$TEST_SSH_FAIL_FATAL" != "x" ; then + cleanup + exit $RESULT + fi +} + +fatal () +{ + save_debug_log "FATAL: $@" + printf "FATAL: " + fail "$@" + cleanup + exit $RESULT +} + +# Skip remaining tests in script. +skip () +{ + echo "SKIPPED: $@" + cleanup + exit $RESULT +} + +RESULT=0 +PIDFILE=$OBJ/pidfile + +trap fatal 3 2 + +# create server config +cat << EOF > $OBJ/sshd_config + StrictModes no + Port $PORT + AddressFamily inet + ListenAddress 127.0.0.1 + #ListenAddress ::1 + PidFile $PIDFILE + AuthorizedKeysFile $OBJ/authorized_keys_%u + LogLevel DEBUG3 + AcceptEnv _XXX_TEST_* + AcceptEnv _XXX_TEST + Subsystem sftp $SFTPSERVER +EOF + +# This may be necessary if /usr/src and/or /usr/obj are group-writable, +# but if you aren't careful with permissions then the unit tests could +# be abused to locally escalate privileges. +if [ ! -z "$TEST_SSH_UNSAFE_PERMISSIONS" ]; then + echo " StrictModes no" >> $OBJ/sshd_config +else + # check and warn if excessive permissions are likely to cause failures. + unsafe="" + dir="${OBJ}" + while test ${dir} != "/"; do + if test -d "${dir}" && ! test -h "${dir}"; then + perms=`ls -ld ${dir}` + case "${perms}" in + ?????w????*|????????w?*) unsafe="${unsafe} ${dir}" ;; + esac + fi + dir=`dirname ${dir}` + done + if ! test -z "${unsafe}"; then + cat <> $OBJ/sshd_config +fi + +if [ ! -z "$TEST_SSH_SSHD_CONFOPTS" ]; then + trace "adding sshd_config option $TEST_SSH_SSHD_CONFOPTS" + echo "$TEST_SSH_SSHD_CONFOPTS" >> $OBJ/sshd_config +fi + +# server config for proxy connects +cp $OBJ/sshd_config $OBJ/sshd_proxy + +# allow group-writable directories in proxy-mode +echo 'StrictModes no' >> $OBJ/sshd_proxy + +# create client config +cat << EOF > $OBJ/ssh_config +Host * + Hostname 127.0.0.1 + HostKeyAlias localhost-with-alias + Port $PORT + User $USER + GlobalKnownHostsFile $OBJ/known_hosts + UserKnownHostsFile $OBJ/known_hosts + PubkeyAuthentication yes + ChallengeResponseAuthentication no + PasswordAuthentication no + BatchMode yes + StrictHostKeyChecking yes + LogLevel DEBUG3 +EOF + +if [ ! -z "$TEST_SSH_SSH_CONFOPTS" ]; then + trace "adding ssh_config option $TEST_SSH_SSH_CONFOPTS" + echo "$TEST_SSH_SSH_CONFOPTS" >> $OBJ/ssh_config +fi + +rm -f $OBJ/known_hosts $OBJ/authorized_keys_$USER + +SSH_SK_PROVIDER= +if ! config_defined ENABLE_SK; then + trace skipping sk-dummy +elif [ -f "${SRC}/misc/sk-dummy/obj/sk-dummy.so" ] ; then + SSH_SK_PROVIDER="${SRC}/misc/sk-dummy/obj/sk-dummy.so" +elif [ -f "${OBJ}/misc/sk-dummy/sk-dummy.so" ] ; then + SSH_SK_PROVIDER="${OBJ}/misc/sk-dummy/sk-dummy.so" +elif [ -f "${SRC}/misc/sk-dummy/sk-dummy.so" ] ; then + SSH_SK_PROVIDER="${SRC}/misc/sk-dummy/sk-dummy.so" +fi +export SSH_SK_PROVIDER + +if ! test -z "$SSH_SK_PROVIDER"; then + EXTRA_AGENT_ARGS='-P/*' # XXX want realpath(1)... + echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/ssh_config + echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/sshd_config + echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/sshd_proxy +fi +export EXTRA_AGENT_ARGS + +maybe_filter_sk() { + if test -z "$SSH_SK_PROVIDER" ; then + grep -v ^sk + else + cat + fi +} + +SSH_KEYTYPES=`$SSH -Q key-plain | maybe_filter_sk` +SSH_HOSTKEY_TYPES=`$SSH -Q key-plain | maybe_filter_sk` + +for t in ${SSH_KEYTYPES}; do + # generate user key + if [ ! -f $OBJ/$t ] || [ ${SSHKEYGEN_BIN} -nt $OBJ/$t ]; then + trace "generating key type $t" + rm -f $OBJ/$t + ${SSHKEYGEN} -q -N '' -t $t -f $OBJ/$t ||\ + fail "ssh-keygen for $t failed" + else + trace "using cached key type $t" + fi + + # setup authorized keys + cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER + echo IdentityFile $OBJ/$t >> $OBJ/ssh_config +done + +for t in ${SSH_HOSTKEY_TYPES}; do + # known hosts file for client + ( + printf 'localhost-with-alias,127.0.0.1,::1 ' + cat $OBJ/$t.pub + ) >> $OBJ/known_hosts + + # use key as host key, too + (umask 077; $SUDO cp $OBJ/$t $OBJ/host.$t) + echo HostKey $OBJ/host.$t >> $OBJ/sshd_config + + # don't use SUDO for proxy connect + echo HostKey $OBJ/$t >> $OBJ/sshd_proxy +done +chmod 644 $OBJ/authorized_keys_$USER + +# Activate Twisted Conch tests if the binary is present +REGRESS_INTEROP_CONCH=no +if test -x "$CONCH" ; then + REGRESS_INTEROP_CONCH=yes +fi + +# If PuTTY is present, new enough and we are running a PuTTY test, prepare +# keys and configuration. +REGRESS_INTEROP_PUTTY=no +if test -x "$PUTTYGEN" -a -x "$PLINK" && + "$PUTTYGEN" --help 2>&1 | grep -- --new-passphrase >/dev/null; then + REGRESS_INTEROP_PUTTY=yes +fi +case "$SCRIPT" in +*putty*) ;; +*) REGRESS_INTEROP_PUTTY=no ;; +esac + +if test "$REGRESS_INTEROP_PUTTY" = "yes" ; then + mkdir -p ${OBJ}/.putty + + # Add a PuTTY key to authorized_keys + rm -f ${OBJ}/putty.rsa2 + if ! "$PUTTYGEN" -t rsa -o ${OBJ}/putty.rsa2 \ + --random-device=/dev/urandom \ + --new-passphrase /dev/null < /dev/null > /dev/null; then + echo "Your installed version of PuTTY is too old to support --new-passphrase, skipping test" >&2 + exit 1 + fi + "$PUTTYGEN" -O public-openssh ${OBJ}/putty.rsa2 \ + >> $OBJ/authorized_keys_$USER + + # Convert rsa2 host key to PuTTY format + cp $OBJ/ssh-rsa $OBJ/ssh-rsa_oldfmt + ${SSHKEYGEN} -p -N '' -m PEM -f $OBJ/ssh-rsa_oldfmt >/dev/null + ${SRC}/ssh2putty.sh 127.0.0.1 $PORT $OBJ/ssh-rsa_oldfmt > \ + ${OBJ}/.putty/sshhostkeys + ${SRC}/ssh2putty.sh 127.0.0.1 22 $OBJ/ssh-rsa_oldfmt >> \ + ${OBJ}/.putty/sshhostkeys + rm -f $OBJ/ssh-rsa_oldfmt + + # Setup proxied session + mkdir -p ${OBJ}/.putty/sessions + rm -f ${OBJ}/.putty/sessions/localhost_proxy + echo "Protocol=ssh" >> ${OBJ}/.putty/sessions/localhost_proxy + echo "HostName=127.0.0.1" >> ${OBJ}/.putty/sessions/localhost_proxy + echo "PortNumber=$PORT" >> ${OBJ}/.putty/sessions/localhost_proxy + echo "ProxyMethod=5" >> ${OBJ}/.putty/sessions/localhost_proxy + echo "ProxyTelnetCommand=sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy" >> ${OBJ}/.putty/sessions/localhost_proxy + echo "ProxyLocalhost=1" >> ${OBJ}/.putty/sessions/localhost_proxy + + PUTTYDIR=${OBJ}/.putty + export PUTTYDIR +fi + +# create a proxy version of the client config +( + cat $OBJ/ssh_config + echo proxycommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy +) > $OBJ/ssh_proxy + +# check proxy config +${SSHD} -t -f $OBJ/sshd_proxy || fatal "sshd_proxy broken" + +start_sshd () +{ + # start sshd + $SUDO ${SSHD} -f $OBJ/sshd_config "$@" -t || fatal "sshd_config broken" + $SUDO env SSH_SK_HELPER="$SSH_SK_HELPER" \ + ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE + + trace "wait for sshd" + i=0; + while [ ! -f $PIDFILE -a $i -lt 10 ]; do + i=`expr $i + 1` + sleep $i + done + + test -f $PIDFILE || fatal "no sshd running on port $PORT" +} + +# source test body +. $SCRIPT + +# kill sshd +cleanup + +if [ "x$USE_VALGRIND" != "x" ]; then + # If there is an EXIT trap handler, invoke it now. + # Some tests set these to clean up processes such as ssh-agent. We + # need to wait for all valgrind processes to complete so we can check + # their logs, but since the EXIT traps are not invoked until + # test-exec.sh exits, waiting here will deadlock. + # This is not very portable but then neither is valgrind itself. + # As a bonus, dash (as used on the runners) has a "trap" that doesn't + # work in a pipeline (hence the temp file) or a subshell. + exithandler="" + trap >/tmp/trap.$$ && exithandler=$(cat /tmp/trap.$$ | \ + awk -F "'" '/EXIT$/{print $2}') + rm -f /tmp/trap.$$ + if [ "x${exithandler}" != "x" ]; then + verbose invoking EXIT trap handler early: ${exithandler} + eval "${exithandler}" + trap '' EXIT + fi + + # wait for any running process to complete + wait; sleep 1 + VG_RESULTS=$(find $OBJ/valgrind-out -type f -print) + VG_RESULT_COUNT=0 + VG_FAIL_COUNT=0 + for i in $VG_RESULTS; do + if grep "ERROR SUMMARY" $i >/dev/null; then + VG_RESULT_COUNT=$(($VG_RESULT_COUNT + 1)) + if ! grep "ERROR SUMMARY: 0 errors" $i >/dev/null; then + VG_FAIL_COUNT=$(($VG_FAIL_COUNT + 1)) + RESULT=1 + verbose valgrind failure $i + cat $i + fi + fi + done + if [ x"$VG_SKIP" != "x" ]; then + verbose valgrind skipped + else + verbose valgrind results $VG_RESULT_COUNT failures $VG_FAIL_COUNT + fi +fi + +if [ $RESULT -eq 0 ]; then + verbose ok $tid +else + echo failed $tid +fi +exit $RESULT diff --git a/aosp/external/openssh/regress/transfer.sh b/aosp/external/openssh/regress/transfer.sh new file mode 100644 index 0000000000000000000000000000000000000000..cf174a00678dbfdf51a2ff4b3fc243b125637726 --- /dev/null +++ b/aosp/external/openssh/regress/transfer.sh @@ -0,0 +1,23 @@ +# $OpenBSD: transfer.sh,v 1.4 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="transfer data" + +rm -f ${COPY} +${SSH} -n -q -F $OBJ/ssh_proxy somehost cat ${DATA} > ${COPY} +if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" +fi +cmp ${DATA} ${COPY} || fail "corrupted copy" + +for s in 10 100 1k 32k 64k 128k 256k; do + trace "dd-size ${s}" + rm -f ${COPY} + dd if=$DATA obs=${s} 2> /dev/null | \ + ${SSH} -q -F $OBJ/ssh_proxy somehost "cat > ${COPY}" + if [ $? -ne 0 ]; then + fail "ssh cat $DATA failed" + fi + cmp $DATA ${COPY} || fail "corrupted copy" +done +rm -f ${COPY} diff --git a/aosp/external/openssh/regress/try-ciphers.sh b/aosp/external/openssh/regress/try-ciphers.sh new file mode 100644 index 0000000000000000000000000000000000000000..e04268ba35bf652e26c7ac1dcad5f9de9f60e7b6 --- /dev/null +++ b/aosp/external/openssh/regress/try-ciphers.sh @@ -0,0 +1,28 @@ +# $OpenBSD: try-ciphers.sh,v 1.26 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="try ciphers" + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +for c in `${SSH} -Q cipher`; do + n=0 + for m in `${SSH} -Q mac`; do + trace "cipher $c mac $m" + verbose "test $tid: cipher $c mac $m" + cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy + echo "Ciphers=$c" >> $OBJ/sshd_proxy + echo "MACs=$m" >> $OBJ/sshd_proxy + ${SSH} -F $OBJ/ssh_proxy -m $m -c $c somehost true + if [ $? -ne 0 ]; then + fail "ssh failed with mac $m cipher $c" + fi + # No point trying all MACs for AEAD ciphers since they + # are ignored. + if ${SSH} -Q cipher-auth | grep "^${c}\$" >/dev/null 2>&1 ; then + break + fi + n=`expr $n + 1` + done +done + diff --git a/aosp/external/openssh/regress/unittests/Makefile b/aosp/external/openssh/regress/unittests/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4d26b74770e29fa51d48eaf0945bfcea9aad0ab0 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/Makefile @@ -0,0 +1,7 @@ +# $OpenBSD: Makefile,v 1.12 2020/06/19 04:34:21 djm Exp $ + +REGRESS_FAIL_EARLY?= yes +SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion +SUBDIR+=authopt misc sshsig + +.include diff --git a/aosp/external/openssh/regress/unittests/Makefile.inc b/aosp/external/openssh/regress/unittests/Makefile.inc new file mode 100644 index 0000000000000000000000000000000000000000..370224aa5e366190b1c85ed0de2cc38d1b0e3129 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/Makefile.inc @@ -0,0 +1,89 @@ +# $OpenBSD: Makefile.inc,v 1.14 2019/11/25 10:32:35 djm Exp $ + +REGRESS_FAIL_EARLY?= yes + +.include +.include + +# User-settable options +UNITTEST_FAST?= no # Skip slow tests (e.g. less intensive fuzzing). +UNITTEST_SLOW?= no # Include slower tests (e.g. more intensive fuzzing). +UNITTEST_VERBOSE?= no # Verbose test output (inc. per-test names). + +MALLOC_OPTIONS?= CFGJRSUX +TEST_ENV?= MALLOC_OPTIONS=${MALLOC_OPTIONS} + +# XXX detect from ssh binary? +OPENSSL?= yes + +.if (${OPENSSL:L} == "yes") +CFLAGS+= -DWITH_OPENSSL +.endif + +# enable warnings +WARNINGS=Yes + +DEBUG=-g +CFLAGS+= -fstack-protector-all +CDIAGFLAGS= -Wall +CDIAGFLAGS+= -Wextra +CDIAGFLAGS+= -Werror +CDIAGFLAGS+= -Wchar-subscripts +CDIAGFLAGS+= -Wcomment +CDIAGFLAGS+= -Wformat +CDIAGFLAGS+= -Wformat-security +CDIAGFLAGS+= -Wimplicit +CDIAGFLAGS+= -Winline +CDIAGFLAGS+= -Wmissing-declarations +CDIAGFLAGS+= -Wmissing-prototypes +CDIAGFLAGS+= -Wparentheses +CDIAGFLAGS+= -Wpointer-arith +CDIAGFLAGS+= -Wreturn-type +CDIAGFLAGS+= -Wshadow +CDIAGFLAGS+= -Wsign-compare +CDIAGFLAGS+= -Wstrict-aliasing +CDIAGFLAGS+= -Wstrict-prototypes +CDIAGFLAGS+= -Wswitch +CDIAGFLAGS+= -Wtrigraphs +CDIAGFLAGS+= -Wuninitialized +CDIAGFLAGS+= -Wunused +CDIAGFLAGS+= -Wno-unused-parameter +.if ${COMPILER_VERSION:L} != "gcc3" +CDIAGFLAGS+= -Wold-style-definition +.endif + +SSHREL=../../../../../usr.bin/ssh + +CFLAGS+=-I${.CURDIR}/../test_helper -I${.CURDIR}/${SSHREL} + +.if exists(${.CURDIR}/../test_helper/${__objdir}) +LDADD+=-L${.CURDIR}/../test_helper/${__objdir} -ltest_helper +DPADD+=${.CURDIR}/../test_helper/${__objdir}/libtest_helper.a +.else +LDADD+=-L${.CURDIR}/../test_helper -ltest_helper +DPADD+=${.CURDIR}/../test_helper/libtest_helper.a +.endif + +.PATH: ${.CURDIR}/${SSHREL} + +LDADD+= -lutil +DPADD+= ${LIBUTIL} + +.if (${OPENSSL:L} == "yes") +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} +.endif + +LDADD+= -lfido2 -lcbor -lusbhid +DPADD+= ${LIBFIDO2} ${LIBCBOR} ${LIBUSBHID} + +UNITTEST_ARGS?= + +.if (${UNITTEST_VERBOSE:L} != "no") +UNITTEST_ARGS+= -v +.endif +.if (${UNITTEST_FAST:L} != "no") +UNITTEST_ARGS+= -f +.elif (${UNITTEST_SLOW:L} != "no") +UNITTEST_ARGS+= -F +.endif diff --git a/aosp/external/openssh/regress/unittests/authopt/Makefile b/aosp/external/openssh/regress/unittests/authopt/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..71a7be5bd2e688295e427b3f68d3761fc32db4a1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/Makefile @@ -0,0 +1,27 @@ +# $OpenBSD: Makefile,v 1.6 2021/01/09 12:24:30 dtucker Exp $ + +PROG=test_authopt +SRCS=tests.c + +SRCS+=auth-options.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c +SRCS+=ssh-dss.c ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c +SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c +SRCS+=addr.c addrmatch.c bitmap.c +SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c +SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c +SRCS+=ssh-ed25519-sk.c sk-usbhid.c + +SRCS+=digest-openssl.c +#SRCS+=digest-libc.c +SRCS+=utf8.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} -d ${.CURDIR}/testdata + +.include diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/all_permit.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/all_permit.cert new file mode 100644 index 0000000000000000000000000000000000000000..38ac5731852582d41faf8a18bb8e2ed220d5f795 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/all_permit.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIOv/h7mJS1WkRHukSvqPwKDiNVrcib/VqBLpbHW6xjWCAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQNe1XDN+J4Eb82TH5J5sYypcabocufjTFRfpU57K+csRP41Yo1FCSEWx95ilUuNvK9Iv3yFDOeVPzdqRqzWoHwE= user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/bad_sourceaddr.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/bad_sourceaddr.cert new file mode 100644 index 0000000000000000000000000000000000000000..9732745ac44f73aa687765b13710519975975515 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/bad_sourceaddr.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILFEJyunlz9scYU3mwbOEJoSSkeO1z20uNBw13tEn+lJAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAHwAAAA5zb3VyY2UtYWRkcmVzcwAAAAkAAAAFeHh4eHgAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEA5xY/OEAJ3tgg8/KJqaBR5KMdYYRDiMJ6u4VKS9lQOV1HJQvDDvjj3F5k53BIqTJRVQx242YWs+B3C4db/uLgB user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/force_command.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/force_command.cert new file mode 100644 index 0000000000000000000000000000000000000000..f7af27e43030142a56b98894289bbdd090ae2512 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/force_command.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJkpCeqaVl6qnp7qa90KehAmHFecx3HW8HZQ22KEqeKBAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAHAAAAA1mb3JjZS1jb21tYW5kAAAABwAAAANmb28AAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAxbhjgbXvfEumRP1E7VH8nUfuJyVlDChhCxiPg9Nvb9PFK8cHdDUEybDCzKCsIDieRc3mtLTyEu7Kb52va/B4C user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/host.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/host.cert new file mode 100644 index 0000000000000000000000000000000000000000..6326d0453497c6276995107829924f798d5505cb --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/host.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFWMw3ftP29RSefnxQwdvK1KiE2G9Y7rPRrJ7ZsrDiOeAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAACAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAKTMqwPkaBg23RS7/aj347dc2kY4bWt/sHwzREYSrKRqZ5RNBnSvZOQ8m5euMCEuf92bZ8VJEdF653jRiW6VoBA== user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/mktestdata.sh b/aosp/external/openssh/regress/unittests/authopt/testdata/mktestdata.sh new file mode 100644 index 0000000000000000000000000000000000000000..06a24e39002c522802ab732f07a2a16d8d79fee7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/mktestdata.sh @@ -0,0 +1,48 @@ +#/bin/sh + +set -xe + +rm -f ca_key ca_key.pub +rm -f user_key user_key.pub +rm -f *.cert + +ssh-keygen -q -f ca_key -t ed25519 -C CA -N '' +ssh-keygen -q -f user_key -t ed25519 -C "user key" -N '' + +sign() { + output=$1 + shift + set -xe + ssh-keygen -q -s ca_key -I user -n user \ + -V 19990101:19991231 -z 1 "$@" user_key.pub + mv user_key-cert.pub "$output" +} + +sign all_permit.cert -Opermit-agent-forwarding -Opermit-port-forwarding \ + -Opermit-pty -Opermit-user-rc -Opermit-X11-forwarding +sign no_permit.cert -Oclear + +sign no_agentfwd.cert -Ono-agent-forwarding +sign no_portfwd.cert -Ono-port-forwarding +sign no_pty.cert -Ono-pty +sign no_user_rc.cert -Ono-user-rc +sign no_x11fwd.cert -Ono-X11-forwarding + +sign only_agentfwd.cert -Oclear -Opermit-agent-forwarding +sign only_portfwd.cert -Oclear -Opermit-port-forwarding +sign only_pty.cert -Oclear -Opermit-pty +sign only_user_rc.cert -Oclear -Opermit-user-rc +sign only_x11fwd.cert -Oclear -Opermit-X11-forwarding + +sign force_command.cert -Oforce-command="foo" +sign sourceaddr.cert -Osource-address="127.0.0.1/32,::1/128" + +# ssh-keygen won't permit generation of certs with invalid source-address +# values, so we do it as a custom extension. +sign bad_sourceaddr.cert -Ocritical:source-address=xxxxx + +sign unknown_critical.cert -Ocritical:blah=foo + +sign host.cert -h + +rm -f user_key ca_key user_key.pub ca_key.pub diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/no_agentfwd.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/no_agentfwd.cert new file mode 100644 index 0000000000000000000000000000000000000000..bfa5c2e658cb311351447d38651022c900d60213 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/no_agentfwd.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIL2qEsLCVtKaBkbCrZicxbPUorcHHrQ8yw5h/26krTOlAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGMAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAdRhISpol01OwV30g39PM/JD1t35muskX4lyCcGpFQ08GQtBuHE/hABOp6apbGBJIC7CZYYF+uHkD7PfGU3NPAQ== user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/no_permit.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/no_permit.cert new file mode 100644 index 0000000000000000000000000000000000000000..351e138aeaa9e97231efa2537e9377a1fb467033 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/no_permit.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGVQtVgp9sD4sc8esIhVWbZaM8d0NxpX3UbEVzTHm9feAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAIKlI0TqqraKjYTjIuKhwoxAV/XnzWRJHq8lNs4aj5yDb84un2xXDF/0vXoLjPgVcLgEbksBKKn0i4whp+xn9Ag== user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/no_portfwd.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/no_portfwd.cert new file mode 100644 index 0000000000000000000000000000000000000000..9457dc34e76c1e0407a4d3038fcda215fdf73e87 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/no_portfwd.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIE6gC/QjjuzGWVDkr8ZyaHhja80V+lKLC/MvmEFa+CEBAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGQAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQEzpgckYlfc1BK1ir0reDSXo9OIDx4UoDMrNXrFO6I44NXoJJ4TlUUJH07WcKp/Xp5ESCdyVZtqwgHQxZr0+PwI= user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/no_pty.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/no_pty.cert new file mode 100644 index 0000000000000000000000000000000000000000..e8154ec7f8b6b12b8dba2469dccf17e37d8d30b7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/no_pty.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFFjhISpSDR3blDejuCf2T9Fe4aHW53jG7KOH2PV/E7jAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAHAAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQF5c4BdxVYgqbMGAep414IGFK4deCFBCeNUTOLpKodrfb1M0gS4d2qoeMxZvMv5yMf/viKl/gallHzEmcrEcIQY= user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/no_user_rc.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/no_user_rc.cert new file mode 100644 index 0000000000000000000000000000000000000000..6676a0cbd4cb1c145f8afc3b5c7c93b73d8bd2b7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/no_user_rc.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFUM0VLATkYh05QeS5uuhB1X50NMom3jTWeQUmrPQ1FwAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGwAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAcmJ3c2FCKJL9BCLv1Ij+uN1N+NWZmMXYionsSkv42Go4pMZiH3g8UfTd+OKq9Q7GAcCzGXa///6Dr/wqFssoDA== user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/no_x11fwd.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/no_x11fwd.cert new file mode 100644 index 0000000000000000000000000000000000000000..0aff9e6cfea8bc5a91a4e7c8941c9d5f8927f3ff --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/no_x11fwd.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPRKPAP+b5S+4zihdgoJrYNcMovFBgKZaJupIhN1kUvkAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGUAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAECMzj6VDfT+BJmIEo1qUKdr8VDLExF92K7KkbNxTH77n7uip7TL24HDfXjYBCvqxSSn9KAGBhnWsIC/GPx6A+cP user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/only_agentfwd.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/only_agentfwd.cert new file mode 100644 index 0000000000000000000000000000000000000000..3cf64b05c2de90b67b0b9da00ca8a2aba1b8e05b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/only_agentfwd.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIOvJ28yW5uvA7yxE3ySuyFvPjcRYKAr03CYr4okGTNIFAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAB8AAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQEG2uTgmOSk9dJ0s/Ol1EIERXFP9PF6AauF9t5jBMSthNyvSANSrC/1EIaf4TV5kMYfhZxJXoS0XHQjGndcq2AE= user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/only_portfwd.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/only_portfwd.cert new file mode 100644 index 0000000000000000000000000000000000000000..bb09c3a6335c47b25f58bc59762c76edae5f5d33 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/only_portfwd.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGPoYoExiSyHMyDEvOFgoNZXk5z91u7xq/7357X23TotAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAB4AAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAHN3YnwipcbDKVn+PObGSoaT9rwlau+yrPYZ50oetvCKng3RMjGaV+roqlv0vjjLcxE9J4Y0ti+9MXtQ0D7beBA== user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/only_pty.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/only_pty.cert new file mode 100644 index 0000000000000000000000000000000000000000..520c89f3be72bb16d5e15abe79e3514fb4d1cce5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/only_pty.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILvocWYto5Lg7P46YLbe7U4/b2h9Lr5rWqMZ4Cj4ra7RAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAABIAAAAKcGVybWl0LXB0eQAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABASv2xQvp+Y6E8dCf5pzg3MZaan5bl1ToYXNcmQ3ysGrk9Djkcu8m3TytDpF471KmUejxy/iF4xjs9CDpk7h+SBQ== user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/only_user_rc.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/only_user_rc.cert new file mode 100644 index 0000000000000000000000000000000000000000..fb49c35f3c83a6b212ee363940e1b241e41d001c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/only_user_rc.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJwsRZQ7kx4A8AQ0q/G/3i6sHM48kr4TxJtTcyy3lZAPAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAABYAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQDhgEXsvoHr21XrxmiZq/sIjWeYapp11XvEVkkTBPVhBnPwtrrUeJbPmGs3gmJkQdv8BYajYpT7TXEX8GvEeLwU= user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/only_x11fwd.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/only_x11fwd.cert new file mode 100644 index 0000000000000000000000000000000000000000..6715585a0f054c5a86d54373da8c247732056811 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/only_x11fwd.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIDAhZFZBl3eu8Qa8I5BaHCz/mpH8xCjaPusBwo1eJ9OGAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAB0AAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDysfgbhniX/zdA8576rrDJpaO2D7QtQse2KWIM9XmREPkLKeP6FKiXKKFcPQiMyV28rptfvK8bBXAiOvITSUgL user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/sourceaddr.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/sourceaddr.cert new file mode 100644 index 0000000000000000000000000000000000000000..0fcf7b182842f32adf0affa658a82e2609a55340 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/sourceaddr.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJ54qqoPs87gtjN1aJoLUn7ZTYUtcaGxkzLyJvRkYG7nAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAALgAAAA5zb3VyY2UtYWRkcmVzcwAAABgAAAAUMTI3LjAuMC4xLzMyLDo6MS8xMjgAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAppSUKQ/a9tw/HgIazWceCO3d48GU7mkV4iQMpWWs2nB1dFryY1GDtZrBggAjMviwmBXyM3jIk5vxJDINZXGQJ user key diff --git a/aosp/external/openssh/regress/unittests/authopt/testdata/unknown_critical.cert b/aosp/external/openssh/regress/unittests/authopt/testdata/unknown_critical.cert new file mode 100644 index 0000000000000000000000000000000000000000..216960ab3e3e0df9c08ee1cc257b6f5ae079b185 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/testdata/unknown_critical.cert @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIjs/wRAB/p5QShSfqoU9cWnCLT3lSveUirk61A27KxVAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAEwAAAARibGFoAAAABwAAAANmb28AAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDix3FV7JIBuHNAwtZOVIqGBq8lqhnEwP51DqPA43qt+Tzynm56EWxuFzgGehBPF3L8gl+fVqxIJmiQ9iHB0LUD user key diff --git a/aosp/external/openssh/regress/unittests/authopt/tests.c b/aosp/external/openssh/regress/unittests/authopt/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..d9e190305e761d7c1c80027b815f2e088f4af767 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/authopt/tests.c @@ -0,0 +1,578 @@ +/* $OpenBSD: tests.c,v 1.3 2021/12/14 21:25:27 deraadt Exp $ */ + +/* + * Regress test for keys options functions. + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "sshkey.h" +#include "authfile.h" +#include "auth-options.h" +#include "misc.h" +#include "log.h" + +static struct sshkey * +load_key(const char *name) +{ + struct sshkey *ret; + int r; + + r = sshkey_load_public(test_data_file(name), &ret, NULL); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(ret, NULL); + return ret; +} + +static struct sshauthopt * +default_authkey_opts(void) +{ + struct sshauthopt *ret = sshauthopt_new(); + + ASSERT_PTR_NE(ret, NULL); + ret->permit_port_forwarding_flag = 1; + ret->permit_agent_forwarding_flag = 1; + ret->permit_x11_forwarding_flag = 1; + ret->permit_pty_flag = 1; + ret->permit_user_rc = 1; + return ret; +} + +static struct sshauthopt * +default_authkey_restrict_opts(void) +{ + struct sshauthopt *ret = sshauthopt_new(); + + ASSERT_PTR_NE(ret, NULL); + ret->permit_port_forwarding_flag = 0; + ret->permit_agent_forwarding_flag = 0; + ret->permit_x11_forwarding_flag = 0; + ret->permit_pty_flag = 0; + ret->permit_user_rc = 0; + ret->restricted = 1; + return ret; +} + +static char ** +commasplit(const char *s, size_t *np) +{ + char *ocp, *cp, *cp2, **ret = NULL; + size_t n; + + ocp = cp = strdup(s); + ASSERT_PTR_NE(cp, NULL); + for (n = 0; (cp2 = strsep(&cp, ",")) != NULL;) { + ret = recallocarray(ret, n, n + 1, sizeof(*ret)); + ASSERT_PTR_NE(ret, NULL); + cp2 = strdup(cp2); + ASSERT_PTR_NE(cp2, NULL); + ret[n++] = cp2; + } + free(ocp); + *np = n; + return ret; +} + +static void +compare_opts(const struct sshauthopt *opts, + const struct sshauthopt *expected) +{ + size_t i; + + ASSERT_PTR_NE(opts, NULL); + ASSERT_PTR_NE(expected, NULL); + ASSERT_PTR_NE(expected, opts); /* bozo :) */ + +#define FLAG_EQ(x) ASSERT_INT_EQ(opts->x, expected->x) + FLAG_EQ(permit_port_forwarding_flag); + FLAG_EQ(permit_agent_forwarding_flag); + FLAG_EQ(permit_x11_forwarding_flag); + FLAG_EQ(permit_pty_flag); + FLAG_EQ(permit_user_rc); + FLAG_EQ(restricted); + FLAG_EQ(cert_authority); +#undef FLAG_EQ + +#define STR_EQ(x) \ + do { \ + if (expected->x == NULL) \ + ASSERT_PTR_EQ(opts->x, expected->x); \ + else \ + ASSERT_STRING_EQ(opts->x, expected->x); \ + } while (0) + STR_EQ(cert_principals); + STR_EQ(force_command); + STR_EQ(required_from_host_cert); + STR_EQ(required_from_host_keys); +#undef STR_EQ + +#define ARRAY_EQ(nx, x) \ + do { \ + ASSERT_SIZE_T_EQ(opts->nx, expected->nx); \ + if (expected->nx == 0) \ + break; \ + for (i = 0; i < expected->nx; i++) \ + ASSERT_STRING_EQ(opts->x[i], expected->x[i]); \ + } while (0) + ARRAY_EQ(nenv, env); + ARRAY_EQ(npermitopen, permitopen); +#undef ARRAY_EQ +} + +static void +test_authkeys_parse(void) +{ + struct sshauthopt *opts, *expected; + const char *errstr; + +#define FAIL_TEST(label, keywords) \ + do { \ + TEST_START("sshauthopt_parse invalid " label); \ + opts = sshauthopt_parse(keywords, &errstr); \ + ASSERT_PTR_EQ(opts, NULL); \ + ASSERT_PTR_NE(errstr, NULL); \ + TEST_DONE(); \ + } while (0) +#define CHECK_SUCCESS_AND_CLEANUP() \ + do { \ + if (errstr != NULL) \ + ASSERT_STRING_EQ(errstr, ""); \ + compare_opts(opts, expected); \ + sshauthopt_free(expected); \ + sshauthopt_free(opts); \ + } while (0) + + /* Basic tests */ + TEST_START("sshauthopt_parse empty"); + expected = default_authkey_opts(); + opts = sshauthopt_parse("", &errstr); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + TEST_START("sshauthopt_parse trailing whitespace"); + expected = default_authkey_opts(); + opts = sshauthopt_parse(" ", &errstr); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + TEST_START("sshauthopt_parse restrict"); + expected = default_authkey_restrict_opts(); + opts = sshauthopt_parse("restrict", &errstr); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + /* Invalid syntax */ + FAIL_TEST("trailing comma", "restrict,"); + FAIL_TEST("bare comma", ","); + FAIL_TEST("unknown option", "BLAH"); + FAIL_TEST("unknown option with trailing comma", "BLAH,"); + FAIL_TEST("unknown option with trailing whitespace", "BLAH "); + + /* force_tun_device */ + TEST_START("sshauthopt_parse tunnel explicit"); + expected = default_authkey_opts(); + expected->force_tun_device = 1; + opts = sshauthopt_parse("tunnel=\"1\"", &errstr); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + TEST_START("sshauthopt_parse tunnel any"); + expected = default_authkey_opts(); + expected->force_tun_device = SSH_TUNID_ANY; + opts = sshauthopt_parse("tunnel=\"any\"", &errstr); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + FAIL_TEST("tunnel", "tunnel=\"blah\""); + + /* Flag options */ +#define FLAG_TEST(keyword, var, val) \ + do { \ + TEST_START("sshauthopt_parse " keyword); \ + expected = default_authkey_opts(); \ + expected->var = val; \ + opts = sshauthopt_parse(keyword, &errstr); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + expected = default_authkey_restrict_opts(); \ + expected->var = val; \ + opts = sshauthopt_parse("restrict,"keyword, &errstr); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + TEST_DONE(); \ + } while (0) + /* Positive flags */ + FLAG_TEST("cert-authority", cert_authority, 1); + FLAG_TEST("port-forwarding", permit_port_forwarding_flag, 1); + FLAG_TEST("agent-forwarding", permit_agent_forwarding_flag, 1); + FLAG_TEST("x11-forwarding", permit_x11_forwarding_flag, 1); + FLAG_TEST("pty", permit_pty_flag, 1); + FLAG_TEST("user-rc", permit_user_rc, 1); + /* Negative flags */ + FLAG_TEST("no-port-forwarding", permit_port_forwarding_flag, 0); + FLAG_TEST("no-agent-forwarding", permit_agent_forwarding_flag, 0); + FLAG_TEST("no-x11-forwarding", permit_x11_forwarding_flag, 0); + FLAG_TEST("no-pty", permit_pty_flag, 0); + FLAG_TEST("no-user-rc", permit_user_rc, 0); +#undef FLAG_TEST + FAIL_TEST("no-cert-authority", "no-cert-authority"); + + /* String options */ +#define STRING_TEST(keyword, var, val) \ + do { \ + TEST_START("sshauthopt_parse " keyword); \ + expected = default_authkey_opts(); \ + expected->var = strdup(val); \ + ASSERT_PTR_NE(expected->var, NULL); \ + opts = sshauthopt_parse(keyword "=" #val, &errstr); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + expected = default_authkey_restrict_opts(); \ + expected->var = strdup(val); \ + ASSERT_PTR_NE(expected->var, NULL); \ + opts = sshauthopt_parse( \ + "restrict," keyword "=" #val ",restrict", &errstr); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + TEST_DONE(); \ + } while (0) + STRING_TEST("command", force_command, "/bin/true"); + STRING_TEST("principals", cert_principals, "gregor,josef,K"); + STRING_TEST("from", required_from_host_keys, "127.0.0.0/8"); +#undef STRING_TEST + FAIL_TEST("unquoted command", "command=oops"); + FAIL_TEST("unquoted principals", "principals=estragon"); + FAIL_TEST("unquoted from", "from=127.0.0.1"); + + /* String array option tests */ +#define ARRAY_TEST(label, keywords, var, nvar, val) \ + do { \ + TEST_START("sshauthopt_parse " label); \ + expected = default_authkey_opts(); \ + expected->var = commasplit(val, &expected->nvar); \ + ASSERT_PTR_NE(expected->var, NULL); \ + opts = sshauthopt_parse(keywords, &errstr); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + expected = default_authkey_restrict_opts(); \ + expected->var = commasplit(val, &expected->nvar); \ + ASSERT_PTR_NE(expected->var, NULL); \ + opts = sshauthopt_parse( \ + "restrict," keywords ",restrict", &errstr); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + TEST_DONE(); \ + } while (0) + ARRAY_TEST("environment", "environment=\"foo=1\",environment=\"bar=2\"", + env, nenv, "foo=1,bar=2"); + ARRAY_TEST("environment", "environment=\"foo=1\",environment=\"foo=2\"", + env, nenv, "foo=1"); + ARRAY_TEST("permitopen", "permitopen=\"foo:123\",permitopen=\"bar:*\"", + permitopen, npermitopen, "foo:123,bar:*"); +#undef ARRAY_TEST + FAIL_TEST("environment", "environment=\",=bah\""); + FAIL_TEST("permitopen port", "foo:bar"); + FAIL_TEST("permitopen missing port", "foo:"); + FAIL_TEST("permitopen missing port specification", "foo"); + FAIL_TEST("permitopen invalid host", "[:"); + +#undef CHECK_SUCCESS_AND_CLEANUP +#undef FAIL_TEST +} + +static void +test_cert_parse(void) +{ + struct sshkey *cert; + struct sshauthopt *opts, *expected; + +#define CHECK_SUCCESS_AND_CLEANUP() \ + do { \ + compare_opts(opts, expected); \ + sshauthopt_free(expected); \ + sshauthopt_free(opts); \ + sshkey_free(cert); \ + } while (0) +#define FLAG_TEST(keybase, var) \ + do { \ + TEST_START("sshauthopt_from_cert no_" keybase); \ + cert = load_key("no_" keybase ".cert"); \ + expected = default_authkey_opts(); \ + expected->var = 0; \ + opts = sshauthopt_from_cert(cert); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + TEST_DONE(); \ + TEST_START("sshauthopt_from_cert only_" keybase); \ + cert = load_key("only_" keybase ".cert"); \ + expected = sshauthopt_new(); \ + ASSERT_PTR_NE(expected, NULL); \ + expected->var = 1; \ + opts = sshauthopt_from_cert(cert); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + TEST_DONE(); \ + } while (0) + FLAG_TEST("agentfwd", permit_agent_forwarding_flag); + FLAG_TEST("portfwd", permit_port_forwarding_flag); + FLAG_TEST("pty", permit_pty_flag); + FLAG_TEST("user_rc", permit_user_rc); + FLAG_TEST("x11fwd", permit_x11_forwarding_flag); +#undef FLAG_TEST + + TEST_START("sshauthopt_from_cert all permitted"); + cert = load_key("all_permit.cert"); + expected = default_authkey_opts(); + opts = sshauthopt_from_cert(cert); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + TEST_START("sshauthopt_from_cert nothing permitted"); + cert = load_key("no_permit.cert"); + expected = sshauthopt_new(); + ASSERT_PTR_NE(expected, NULL); + opts = sshauthopt_from_cert(cert); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + TEST_START("sshauthopt_from_cert force-command"); + cert = load_key("force_command.cert"); + expected = default_authkey_opts(); + expected->force_command = strdup("foo"); + ASSERT_PTR_NE(expected->force_command, NULL); + opts = sshauthopt_from_cert(cert); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + TEST_START("sshauthopt_from_cert source-address"); + cert = load_key("sourceaddr.cert"); + expected = default_authkey_opts(); + expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128"); + ASSERT_PTR_NE(expected->required_from_host_cert, NULL); + opts = sshauthopt_from_cert(cert); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); +#undef CHECK_SUCCESS_AND_CLEANUP + +#define FAIL_TEST(keybase) \ + do { \ + TEST_START("sshauthopt_from_cert " keybase); \ + cert = load_key(keybase ".cert"); \ + opts = sshauthopt_from_cert(cert); \ + ASSERT_PTR_EQ(opts, NULL); \ + sshkey_free(cert); \ + TEST_DONE(); \ + } while (0) + FAIL_TEST("host"); + FAIL_TEST("bad_sourceaddr"); + FAIL_TEST("unknown_critical"); +#undef FAIL_TEST +} + +static void +test_merge(void) +{ + struct sshkey *cert; + struct sshauthopt *key_opts, *cert_opts, *merge_opts, *expected; + const char *errstr; + + /* + * Prepare for a test by making some key and cert options and + * attempting to merge them. + */ +#define PREPARE(label, keyname, keywords) \ + do { \ + expected = NULL; \ + TEST_START("sshauthopt_merge " label); \ + cert = load_key(keyname ".cert"); \ + cert_opts = sshauthopt_from_cert(cert); \ + ASSERT_PTR_NE(cert_opts, NULL); \ + key_opts = sshauthopt_parse(keywords, &errstr); \ + if (errstr != NULL) \ + ASSERT_STRING_EQ(errstr, ""); \ + ASSERT_PTR_NE(key_opts, NULL); \ + merge_opts = sshauthopt_merge(key_opts, \ + cert_opts, &errstr); \ + } while (0) + + /* Cleanup stuff allocated by PREPARE() */ +#define CLEANUP() \ + do { \ + sshauthopt_free(expected); \ + sshauthopt_free(merge_opts); \ + sshauthopt_free(key_opts); \ + sshauthopt_free(cert_opts); \ + sshkey_free(cert); \ + } while (0) + + /* Check the results of PREPARE() against expectation; calls CLEANUP */ +#define CHECK_SUCCESS_AND_CLEANUP() \ + do { \ + if (errstr != NULL) \ + ASSERT_STRING_EQ(errstr, ""); \ + compare_opts(merge_opts, expected); \ + CLEANUP(); \ + } while (0) + + /* Check a single case of merging of flag options */ +#define FLAG_CASE(keybase, label, keyname, keywords, mostly_off, var, val) \ + do { \ + PREPARE(keybase " " label, keyname, keywords); \ + expected = mostly_off ? \ + sshauthopt_new() : default_authkey_opts(); \ + expected->var = val; \ + ASSERT_PTR_NE(expected, NULL); \ + CHECK_SUCCESS_AND_CLEANUP(); \ + TEST_DONE(); \ + } while (0) + + /* + * Fairly exhaustive exercise of a flag option. Tests + * option both set and clear in certificate, set and clear in + * authorized_keys and set and cleared via restrict keyword. + */ +#define FLAG_TEST(keybase, keyword, var) \ + do { \ + FLAG_CASE(keybase, "keys:default,yes cert:default,no", \ + "no_" keybase, keyword, 0, var, 0); \ + FLAG_CASE(keybase,"keys:-*,yes cert:default,no", \ + "no_" keybase, "restrict," keyword, 1, var, 0); \ + FLAG_CASE(keybase, "keys:default,no cert:default,no", \ + "no_" keybase, "no-" keyword, 0, var, 0); \ + FLAG_CASE(keybase, "keys:-*,no cert:default,no", \ + "no_" keybase, "restrict,no-" keyword, 1, var, 0); \ + \ + FLAG_CASE(keybase, "keys:default,yes cert:-*,yes", \ + "only_" keybase, keyword, 1, var, 1); \ + FLAG_CASE(keybase,"keys:-*,yes cert:-*,yes", \ + "only_" keybase, "restrict," keyword, 1, var, 1); \ + FLAG_CASE(keybase, "keys:default,no cert:-*,yes", \ + "only_" keybase, "no-" keyword, 1, var, 0); \ + FLAG_CASE(keybase, "keys:-*,no cert:-*,yes", \ + "only_" keybase, "restrict,no-" keyword, 1, var, 0); \ + \ + FLAG_CASE(keybase, "keys:default,yes cert:-*", \ + "no_permit", keyword, 1, var, 0); \ + FLAG_CASE(keybase,"keys:-*,yes cert:-*", \ + "no_permit", "restrict," keyword, 1, var, 0); \ + FLAG_CASE(keybase, "keys:default,no cert:-*", \ + "no_permit", "no-" keyword, 1, var, 0); \ + FLAG_CASE(keybase, "keys:-*,no cert:-*", \ + "no_permit", "restrict,no-" keyword, 1, var, 0); \ + \ + FLAG_CASE(keybase, "keys:default,yes cert:*", \ + "all_permit", keyword, 0, var, 1); \ + FLAG_CASE(keybase,"keys:-*,yes cert:*", \ + "all_permit", "restrict," keyword, 1, var, 1); \ + FLAG_CASE(keybase, "keys:default,no cert:*", \ + "all_permit", "no-" keyword, 0, var, 0); \ + FLAG_CASE(keybase, "keys:-*,no cert:*", \ + "all_permit", "restrict,no-" keyword, 1, var, 0); \ + \ + } while (0) + FLAG_TEST("portfwd", "port-forwarding", permit_port_forwarding_flag); + FLAG_TEST("agentfwd", "agent-forwarding", permit_agent_forwarding_flag); + FLAG_TEST("pty", "pty", permit_pty_flag); + FLAG_TEST("user_rc", "user-rc", permit_user_rc); + FLAG_TEST("x11fwd", "x11-forwarding", permit_x11_forwarding_flag); +#undef FLAG_TEST + + PREPARE("source-address both", "sourceaddr", "from=\"127.0.0.1\""); + expected = default_authkey_opts(); + expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128"); + ASSERT_PTR_NE(expected->required_from_host_cert, NULL); + expected->required_from_host_keys = strdup("127.0.0.1"); + ASSERT_PTR_NE(expected->required_from_host_keys, NULL); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("source-address none", "all_permit", ""); + expected = default_authkey_opts(); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("source-address keys", "all_permit", "from=\"127.0.0.1\""); + expected = default_authkey_opts(); + expected->required_from_host_keys = strdup("127.0.0.1"); + ASSERT_PTR_NE(expected->required_from_host_keys, NULL); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("source-address cert", "sourceaddr", ""); + expected = default_authkey_opts(); + expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128"); + ASSERT_PTR_NE(expected->required_from_host_cert, NULL); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("force-command both", "force_command", "command=\"foo\""); + expected = default_authkey_opts(); + expected->force_command = strdup("foo"); + ASSERT_PTR_NE(expected->force_command, NULL); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("force-command none", "all_permit", ""); + expected = default_authkey_opts(); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("force-command keys", "all_permit", "command=\"bar\""); + expected = default_authkey_opts(); + expected->force_command = strdup("bar"); + ASSERT_PTR_NE(expected->force_command, NULL); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("force-command cert", "force_command", ""); + expected = default_authkey_opts(); + expected->force_command = strdup("foo"); + ASSERT_PTR_NE(expected->force_command, NULL); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("force-command mismatch", "force_command", "command=\"bar\""); + ASSERT_PTR_EQ(merge_opts, NULL); + CLEANUP(); + TEST_DONE(); + + PREPARE("tunnel", "all_permit", "tunnel=\"6\""); + expected = default_authkey_opts(); + expected->force_tun_device = 6; + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("permitopen", "all_permit", + "permitopen=\"127.0.0.1:*\",permitopen=\"127.0.0.1:123\""); + expected = default_authkey_opts(); + expected->permitopen = commasplit("127.0.0.1:*,127.0.0.1:123", + &expected->npermitopen); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); + + PREPARE("environment", "all_permit", + "environment=\"foo=a\",environment=\"bar=b\""); + expected = default_authkey_opts(); + expected->env = commasplit("foo=a,bar=b", &expected->nenv); + CHECK_SUCCESS_AND_CLEANUP(); + TEST_DONE(); +} + +void +tests(void) +{ + extern char *__progname; + LogLevel ll = test_is_verbose() ? + SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_QUIET; + + /* test_cert_parse() are a bit spammy to error() by default... */ + log_init(__progname, ll, SYSLOG_FACILITY_USER, 1); + + test_authkeys_parse(); + test_cert_parse(); + test_merge(); +} diff --git a/aosp/external/openssh/regress/unittests/bitmap/Makefile b/aosp/external/openssh/regress/unittests/bitmap/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fe30acc773944f8423c2b3113f8c2732b5cc3084 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/bitmap/Makefile @@ -0,0 +1,14 @@ +# $OpenBSD: Makefile,v 1.4 2017/12/21 00:41:22 djm Exp $ + +PROG=test_bitmap +SRCS=tests.c + +# From usr.sbin/ssh +SRCS+=bitmap.c atomicio.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include diff --git a/aosp/external/openssh/regress/unittests/bitmap/tests.c b/aosp/external/openssh/regress/unittests/bitmap/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..576b863f4066dd5366011214763c2eb7c4f716d8 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/bitmap/tests.c @@ -0,0 +1,138 @@ +/* $OpenBSD: tests.c,v 1.2 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for bitmap.h bitmap API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#ifdef WITH_OPENSSL +#include +#endif + +#include "../test_helper/test_helper.h" + +#include "bitmap.h" + +#define NTESTS 131 + +void +tests(void) +{ +#ifdef WITH_OPENSSL + struct bitmap *b; + BIGNUM *bn; + size_t len; + int i, j, k, n; + u_char bbuf[1024], bnbuf[1024]; + int r; + + TEST_START("bitmap_new"); + b = bitmap_new(); + ASSERT_PTR_NE(b, NULL); + bn = BN_new(); + ASSERT_PTR_NE(bn, NULL); + TEST_DONE(); + + TEST_START("bitmap_set_bit / bitmap_test_bit"); + for (i = -1; i < NTESTS; i++) { + for (j = -1; j < NTESTS; j++) { + for (k = -1; k < NTESTS; k++) { + bitmap_zero(b); + BN_clear(bn); + + test_subtest_info("set %d/%d/%d", i, j, k); + /* Set bits */ + if (i >= 0) { + ASSERT_INT_EQ(bitmap_set_bit(b, i), 0); + ASSERT_INT_EQ(BN_set_bit(bn, i), 1); + } + if (j >= 0) { + ASSERT_INT_EQ(bitmap_set_bit(b, j), 0); + ASSERT_INT_EQ(BN_set_bit(bn, j), 1); + } + if (k >= 0) { + ASSERT_INT_EQ(bitmap_set_bit(b, k), 0); + ASSERT_INT_EQ(BN_set_bit(bn, k), 1); + } + + /* Check perfect match between bitmap and bn */ + test_subtest_info("match %d/%d/%d", i, j, k); + for (n = 0; n < NTESTS; n++) { + ASSERT_INT_EQ(BN_is_bit_set(bn, n), + bitmap_test_bit(b, n)); + } + + /* Test length calculations */ + test_subtest_info("length %d/%d/%d", i, j, k); + ASSERT_INT_EQ(BN_num_bits(bn), + (int)bitmap_nbits(b)); + ASSERT_INT_EQ(BN_num_bytes(bn), + (int)bitmap_nbytes(b)); + + /* Test serialisation */ + test_subtest_info("serialise %d/%d/%d", + i, j, k); + len = bitmap_nbytes(b); + memset(bbuf, 0xfc, sizeof(bbuf)); + ASSERT_INT_EQ(bitmap_to_string(b, bbuf, + sizeof(bbuf)), 0); + for (n = len; n < (int)sizeof(bbuf); n++) + ASSERT_U8_EQ(bbuf[n], 0xfc); + r = BN_bn2bin(bn, bnbuf); + ASSERT_INT_GE(r, 0); + ASSERT_INT_EQ(r, (int)len); + ASSERT_MEM_EQ(bbuf, bnbuf, len); + + /* Test deserialisation */ + test_subtest_info("deserialise %d/%d/%d", + i, j, k); + bitmap_zero(b); + ASSERT_INT_EQ(bitmap_from_string(b, bnbuf, + len), 0); + for (n = 0; n < NTESTS; n++) { + ASSERT_INT_EQ(BN_is_bit_set(bn, n), + bitmap_test_bit(b, n)); + } + + /* Test clearing bits */ + test_subtest_info("clear %d/%d/%d", + i, j, k); + for (n = 0; n < NTESTS; n++) { + ASSERT_INT_EQ(bitmap_set_bit(b, n), 0); + ASSERT_INT_EQ(BN_set_bit(bn, n), 1); + } + if (i >= 0) { + bitmap_clear_bit(b, i); + BN_clear_bit(bn, i); + } + if (j >= 0) { + bitmap_clear_bit(b, j); + BN_clear_bit(bn, j); + } + if (k >= 0) { + bitmap_clear_bit(b, k); + BN_clear_bit(bn, k); + } + for (n = 0; n < NTESTS; n++) { + ASSERT_INT_EQ(BN_is_bit_set(bn, n), + bitmap_test_bit(b, n)); + } + } + } + } + bitmap_free(b); + BN_free(bn); + TEST_DONE(); +#endif +} + diff --git a/aosp/external/openssh/regress/unittests/conversion/Makefile b/aosp/external/openssh/regress/unittests/conversion/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5793c49348458257aace4a4f0e1925db373678ba --- /dev/null +++ b/aosp/external/openssh/regress/unittests/conversion/Makefile @@ -0,0 +1,16 @@ +# $OpenBSD: Makefile,v 1.4 2021/01/09 12:24:30 dtucker Exp $ + +PROG=test_conversion +SRCS=tests.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=atomicio.c misc.c xmalloc.c log.c uidswap.c cleanup.c fatal.c ssherr.c +SRCS+=match.c addr.c addrmatch.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include diff --git a/aosp/external/openssh/regress/unittests/conversion/tests.c b/aosp/external/openssh/regress/unittests/conversion/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..5b526f7afa07af227e3e427b74b6231b74625761 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/conversion/tests.c @@ -0,0 +1,52 @@ +/* $OpenBSD: tests.c,v 1.4 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for conversions + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "misc.h" + +void +tests(void) +{ + char buf[1024]; + + TEST_START("conversion_convtime"); + ASSERT_INT_EQ(convtime("0"), 0); + ASSERT_INT_EQ(convtime("1"), 1); + ASSERT_INT_EQ(convtime("1S"), 1); + /* from the examples in the comment above the function */ + ASSERT_INT_EQ(convtime("90m"), 5400); + ASSERT_INT_EQ(convtime("1h30m"), 5400); + ASSERT_INT_EQ(convtime("2d"), 172800); + ASSERT_INT_EQ(convtime("1w"), 604800); + + /* negative time is not allowed */ + ASSERT_INT_EQ(convtime("-7"), -1); + ASSERT_INT_EQ(convtime("-9d"), -1); + + /* overflow */ + snprintf(buf, sizeof buf, "%llu", (unsigned long long)INT_MAX); + ASSERT_INT_EQ(convtime(buf), INT_MAX); + snprintf(buf, sizeof buf, "%llu", (unsigned long long)INT_MAX + 1); + ASSERT_INT_EQ(convtime(buf), -1); + + /* overflow with multiplier */ + snprintf(buf, sizeof buf, "%lluM", (unsigned long long)INT_MAX/60 + 1); + ASSERT_INT_EQ(convtime(buf), -1); + ASSERT_INT_EQ(convtime("1000000000000000000000w"), -1); + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/hostkeys/Makefile b/aosp/external/openssh/regress/unittests/hostkeys/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..9a53423eeac0a300e3b14dab75daab3160deced3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/Makefile @@ -0,0 +1,25 @@ +# $OpenBSD: Makefile,v 1.9 2021/01/09 12:24:30 dtucker Exp $ + +PROG=test_hostkeys +SRCS=tests.c test_iterate.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c +SRCS+=ssh-dss.c ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c +SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c +SRCS+=addr.c addrmatch.c bitmap.c hostfile.c +SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c +SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c +SRCS+=ssh-ed25519-sk.c sk-usbhid.c + +SRCS+=digest-openssl.c +#SRCS+=digest-libc.c +SRCS+=utf8.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} -d ${.CURDIR}/testdata + +.include diff --git a/aosp/external/openssh/regress/unittests/hostkeys/mktestdata.sh b/aosp/external/openssh/regress/unittests/hostkeys/mktestdata.sh new file mode 100644 index 0000000000000000000000000000000000000000..5a46de990dcaeaac48bfbde6ecd21edccc04d3a0 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/mktestdata.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# $OpenBSD: mktestdata.sh,v 1.2 2017/04/30 23:33:48 djm Exp $ + +set -ex + +cd testdata + +rm -f rsa* dsa* ecdsa* ed25519* +rm -f known_hosts* + +gen_all() { + _n=$1 + _ecdsa_bits=256 + test "x$_n" = "x1" && _ecdsa_bits=384 + test "x$_n" = "x2" && _ecdsa_bits=521 + ssh-keygen -qt rsa -b 1024 -C "RSA #$_n" -N "" -f rsa_$_n + ssh-keygen -qt dsa -b 1024 -C "DSA #$_n" -N "" -f dsa_$_n + ssh-keygen -qt ecdsa -b $_ecdsa_bits -C "ECDSA #$_n" -N "" -f ecdsa_$_n + ssh-keygen -qt ed25519 -C "ED25519 #$_n" -N "" -f ed25519_$_n + # Don't need private keys + rm -f rsa_$_n dsa_$_n ecdsa_$_n ed25519_$_n +} + +hentries() { + _preamble=$1 + _kspec=$2 + for k in `ls -1 $_kspec | sort` ; do + printf "$_preamble " + cat $k + done + echo +} + +gen_all 1 +gen_all 2 +gen_all 3 +gen_all 4 +gen_all 5 +gen_all 6 + +# A section of known_hosts with hashed hostnames. +( + hentries "sisyphus.example.com" "*_5.pub" + hentries "prometheus.example.com,192.0.2.1,2001:db8::1" "*_6.pub" +) > known_hosts_hash_frag +ssh-keygen -Hf known_hosts_hash_frag +rm -f known_hosts_hash_frag.old + +# Populated known_hosts, including comments, hashed names and invalid lines +( + echo "# Plain host keys, plain host names" + hentries "sisyphus.example.com" "*_1.pub" + + echo "# Plain host keys, hostnames + addresses" + hentries "prometheus.example.com,192.0.2.1,2001:db8::1" "*_2.pub" + + echo "# Some hosts with wildcard names / IPs" + hentries "*.example.com,192.0.2.*,2001:*" "*_3.pub" + + echo "# Hashed hostname and address entries" + cat known_hosts_hash_frag + rm -f known_hosts_hash_frag + echo + + echo "# Revoked and CA keys" + printf "@revoked sisyphus.example.com " ; cat ed25519_4.pub + printf "@cert-authority prometheus.example.com " ; cat ecdsa_4.pub + printf "@cert-authority *.example.com " ; cat dsa_4.pub + + printf "\n" + echo "# Some invalid lines" + # Invalid marker + printf "@what sisyphus.example.com " ; cat dsa_1.pub + # Key missing + echo "sisyphus.example.com " + # Key blob missing + echo "prometheus.example.com ssh-ed25519 " + # Key blob truncated + echo "sisyphus.example.com ssh-dsa AAAATgAAAAdz" + # Invalid type + echo "sisyphus.example.com ssh-XXX AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg==" + # Type mismatch with blob + echo "prometheus.example.com ssh-rsa AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg==" +) > known_hosts + +echo OK diff --git a/aosp/external/openssh/regress/unittests/hostkeys/test_iterate.c b/aosp/external/openssh/regress/unittests/hostkeys/test_iterate.c new file mode 100644 index 0000000000000000000000000000000000000000..84f26b5c72f500683474f2df3bdc8fbb65c62df4 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/test_iterate.c @@ -0,0 +1,1117 @@ +/* $OpenBSD: test_iterate.c,v 1.8 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for hostfile.h hostkeys_foreach() + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "sshkey.h" +#include "authfile.h" +#include "hostfile.h" + +struct expected { + const char *key_file; /* Path for key, NULL for none */ + int no_parse_status; /* Expected status w/o key parsing */ + int no_parse_keytype; /* Expected keytype w/o key parsing */ + int match_host_p; /* Match 'prometheus.example.com' */ + int match_host_s; /* Match 'sisyphus.example.com' */ + int match_ipv4; /* Match '192.0.2.1' */ + int match_ipv6; /* Match '2001:db8::1' */ + int match_flags; /* Expected flags from match */ + struct hostkey_foreach_line l; /* Expected line contents */ +}; + +struct cbctx { + const struct expected *expected; + size_t nexpected; + size_t i; + int flags; + int match_host_p; + int match_host_s; + int match_ipv4; + int match_ipv6; +}; + +/* + * hostkeys_foreach() iterator callback that verifies the line passed + * against an array of expected entries. + */ +static int +check(struct hostkey_foreach_line *l, void *_ctx) +{ + struct cbctx *ctx = (struct cbctx *)_ctx; + const struct expected *expected; + int parse_key = (ctx->flags & HKF_WANT_PARSE_KEY) != 0; + const int matching = (ctx->flags & HKF_WANT_MATCH) != 0; + u_int expected_status, expected_match; + int expected_keytype, skip = 0; + + test_subtest_info("entry %zu/%zu, file line %ld", + ctx->i + 1, ctx->nexpected, l->linenum); + + for (;;) { + ASSERT_SIZE_T_LT(ctx->i, ctx->nexpected); + expected = ctx->expected + ctx->i++; + /* If we are matching host/IP then skip entries that don't */ + if (!matching) + break; + if (ctx->match_host_p && expected->match_host_p) + break; + if (ctx->match_host_s && expected->match_host_s) + break; + if (ctx->match_ipv4 && expected->match_ipv4) + break; + if (ctx->match_ipv6 && expected->match_ipv6) + break; + } + expected_status = (parse_key || expected->no_parse_status < 0) ? + expected->l.status : (u_int)expected->no_parse_status; + expected_match = expected->l.match; +#define UPDATE_MATCH_STATUS(x) do { \ + if (ctx->x && expected->x) { \ + expected_match |= expected->x; \ + if (expected_status == HKF_STATUS_OK) \ + expected_status = HKF_STATUS_MATCHED; \ + } \ + } while (0) + expected_keytype = (parse_key || expected->no_parse_keytype < 0) ? + expected->l.keytype : expected->no_parse_keytype; + +#ifndef OPENSSL_HAS_ECC + if (expected->l.keytype == KEY_ECDSA || + expected->no_parse_keytype == KEY_ECDSA) + skip = 1; +#endif /* OPENSSL_HAS_ECC */ +#ifndef WITH_OPENSSL + if (expected->l.keytype == KEY_DSA || + expected->no_parse_keytype == KEY_DSA || + expected->l.keytype == KEY_RSA || + expected->no_parse_keytype == KEY_RSA || + expected->l.keytype == KEY_ECDSA || + expected->no_parse_keytype == KEY_ECDSA) + skip = 1; +#endif /* WITH_OPENSSL */ + if (skip) { + expected_status = HKF_STATUS_INVALID; + expected_keytype = KEY_UNSPEC; + parse_key = 0; + } + UPDATE_MATCH_STATUS(match_host_p); + UPDATE_MATCH_STATUS(match_host_s); + UPDATE_MATCH_STATUS(match_ipv4); + UPDATE_MATCH_STATUS(match_ipv6); + + ASSERT_PTR_NE(l->path, NULL); /* Don't care about path */ + ASSERT_LONG_LONG_EQ(l->linenum, expected->l.linenum); + ASSERT_U_INT_EQ(l->status, expected_status); + ASSERT_U_INT_EQ(l->match, expected_match); + /* Not all test entries contain fulltext */ + if (expected->l.line != NULL) + ASSERT_STRING_EQ(l->line, expected->l.line); + ASSERT_INT_EQ(l->marker, expected->l.marker); + /* XXX we skip hashed hostnames for now; implement checking */ + if (expected->l.hosts != NULL) + ASSERT_STRING_EQ(l->hosts, expected->l.hosts); + /* Not all test entries contain raw keys */ + if (expected->l.rawkey != NULL) + ASSERT_STRING_EQ(l->rawkey, expected->l.rawkey); + /* XXX synthesise raw key for cases lacking and compare */ + ASSERT_INT_EQ(l->keytype, expected_keytype); + if (parse_key) { + if (expected->l.key == NULL) + ASSERT_PTR_EQ(l->key, NULL); + if (expected->l.key != NULL) { + ASSERT_PTR_NE(l->key, NULL); + ASSERT_INT_EQ(sshkey_equal(l->key, expected->l.key), 1); + } + } + if (parse_key && !(l->comment == NULL && expected->l.comment == NULL)) + ASSERT_STRING_EQ(l->comment, expected->l.comment); + return 0; +} + +/* Loads public keys for a set of expected results */ +static void +prepare_expected(struct expected *expected, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + if (expected[i].key_file == NULL) + continue; +#ifndef OPENSSL_HAS_ECC + if (expected[i].l.keytype == KEY_ECDSA) + continue; +#endif /* OPENSSL_HAS_ECC */ +#ifndef WITH_OPENSSL + switch (expected[i].l.keytype) { + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: + continue; + } +#endif /* WITH_OPENSSL */ + ASSERT_INT_EQ(sshkey_load_public( + test_data_file(expected[i].key_file), &expected[i].l.key, + NULL), 0); + } +} + +static void +cleanup_expected(struct expected *expected, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + sshkey_free(expected[i].l.key); + expected[i].l.key = NULL; + } +} + +struct expected expected_full[] = { + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, /* path, don't care */ + 1, /* line number */ + HKF_STATUS_COMMENT, /* status */ + 0, /* match flags */ + "# Plain host keys, plain host names", /* full line, optional */ + MRK_NONE, /* marker (CA / revoked) */ + NULL, /* hosts text */ + NULL, /* raw key, optional */ + KEY_UNSPEC, /* key type */ + NULL, /* deserialised key */ + NULL, /* comment */ + 0, /* note */ + } }, + { "dsa_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 2, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #1", + 0, + } }, + { "ecdsa_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 3, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #1", + 0, + } }, + { "ed25519_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 4, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #1", + 0, + } }, + { "rsa_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 5, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #1", + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 6, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 7, + HKF_STATUS_COMMENT, + 0, + "# Plain host keys, hostnames + addresses", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { "dsa_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 8, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "prometheus.example.com,192.0.2.1,2001:db8::1", + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #2", + 0, + } }, + { "ecdsa_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 9, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "prometheus.example.com,192.0.2.1,2001:db8::1", + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #2", + 0, + } }, + { "ed25519_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 10, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "prometheus.example.com,192.0.2.1,2001:db8::1", + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #2", + 0, + } }, + { "rsa_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 11, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "prometheus.example.com,192.0.2.1,2001:db8::1", + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #2", + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 12, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 13, + HKF_STATUS_COMMENT, + 0, + "# Some hosts with wildcard names / IPs", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { "dsa_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 14, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "*.example.com,192.0.2.*,2001:*", + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #3", + 0, + } }, + { "ecdsa_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 15, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "*.example.com,192.0.2.*,2001:*", + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #3", + 0, + } }, + { "ed25519_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 16, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "*.example.com,192.0.2.*,2001:*", + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #3", + 0, + } }, + { "rsa_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, { + NULL, + 17, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + "*.example.com,192.0.2.*,2001:*", + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #3", + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 18, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 19, + HKF_STATUS_COMMENT, + 0, + "# Hashed hostname and address entries", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { "dsa_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, { + NULL, + 20, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #5", + 0, + } }, + { "ecdsa_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, { + NULL, + 21, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #5", + 0, + } }, + { "ed25519_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, { + NULL, + 22, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #5", + 0, + } }, + { "rsa_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, { + NULL, + 23, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #5", + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 24, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + /* + * The next series have each key listed multiple times, as the + * hostname and addresses in the pre-hashed known_hosts are split + * to separate lines. + */ + { "dsa_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, { + NULL, + 25, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #6", + 0, + } }, + { "dsa_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, { + NULL, + 26, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #6", + 0, + } }, + { "dsa_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, { + NULL, + 27, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #6", + 0, + } }, + { "ecdsa_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, { + NULL, + 28, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #6", + 0, + } }, + { "ecdsa_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, { + NULL, + 29, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #6", + 0, + } }, + { "ecdsa_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, { + NULL, + 30, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #6", + 0, + } }, + { "ed25519_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, { + NULL, + 31, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #6", + 0, + } }, + { "ed25519_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, { + NULL, + 32, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #6", + 0, + } }, + { "ed25519_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, { + NULL, + 33, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #6", + 0, + } }, + { "rsa_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, { + NULL, + 34, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #6", + 0, + } }, + { "rsa_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, { + NULL, + 35, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #6", + 0, + } }, + { "rsa_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, { + NULL, + 36, + HKF_STATUS_OK, + 0, + NULL, + MRK_NONE, + NULL, + NULL, + KEY_RSA, + NULL, /* filled at runtime */ + "RSA #6", + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 37, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 38, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 39, + HKF_STATUS_COMMENT, + 0, + "# Revoked and CA keys", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { "ed25519_4.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 40, + HKF_STATUS_OK, + 0, + NULL, + MRK_REVOKE, + "sisyphus.example.com", + NULL, + KEY_ED25519, + NULL, /* filled at runtime */ + "ED25519 #4", + 0, + } }, + { "ecdsa_4.pub" , -1, -1, HKF_MATCH_HOST, 0, 0, 0, -1, { + NULL, + 41, + HKF_STATUS_OK, + 0, + NULL, + MRK_CA, + "prometheus.example.com", + NULL, + KEY_ECDSA, + NULL, /* filled at runtime */ + "ECDSA #4", + 0, + } }, + { "dsa_4.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 42, + HKF_STATUS_OK, + 0, + NULL, + MRK_CA, + "*.example.com", + NULL, + KEY_DSA, + NULL, /* filled at runtime */ + "DSA #4", + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 43, + HKF_STATUS_COMMENT, + 0, + "", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 44, + HKF_STATUS_COMMENT, + 0, + "# Some invalid lines", + MRK_NONE, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, 0, 0, 0, -1, { + NULL, + 45, + HKF_STATUS_INVALID, + 0, + NULL, + MRK_ERROR, + NULL, + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 46, + HKF_STATUS_INVALID, + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, HKF_MATCH_HOST, 0, 0, 0, -1, { + NULL, + 47, + HKF_STATUS_INVALID, + 0, + NULL, + MRK_NONE, + "prometheus.example.com", + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 48, + HKF_STATUS_INVALID, /* Would be ok if key not parsed */ + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_UNSPEC, + NULL, + NULL, + 0, + } }, + { NULL, -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, { + NULL, + 49, + HKF_STATUS_INVALID, + 0, + NULL, + MRK_NONE, + "sisyphus.example.com", + NULL, + KEY_UNSPEC, + NULL, /* filled at runtime */ + NULL, + 0, + } }, + { NULL, HKF_STATUS_OK, KEY_RSA, HKF_MATCH_HOST, 0, 0, 0, -1, { + NULL, + 50, + HKF_STATUS_INVALID, /* Would be ok if key not parsed */ + 0, + NULL, + MRK_NONE, + "prometheus.example.com", + NULL, + KEY_UNSPEC, + NULL, /* filled at runtime */ + NULL, + 0, + } }, +}; + +void test_iterate(void); + +void +test_iterate(void) +{ + struct cbctx ctx; + + TEST_START("hostkeys_iterate all with key parse"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_PARSE_KEY; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, NULL, NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate all without key parse"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, NULL, NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify host 1"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + ctx.match_host_p = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "prometheus.example.com", NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify host 2"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + ctx.match_host_s = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "sisyphus.example.com", NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match host 1"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + ctx.match_host_p = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "prometheus.example.com", NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match host 2"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + ctx.match_host_s = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "sisyphus.example.com", NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify host missing"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "actaeon.example.org", NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match host missing"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "actaeon.example.org", NULL, ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify IPv4"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + ctx.match_ipv4 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "tiresias.example.org", "192.0.2.1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify IPv6"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + ctx.match_ipv6 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "tiresias.example.org", "2001:db8::1", + ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match IPv4"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + ctx.match_ipv4 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "tiresias.example.org", "192.0.2.1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match IPv6"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + ctx.match_ipv6 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "tiresias.example.org", "2001:db8::1", + ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify addr missing"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "tiresias.example.org", "192.168.0.1", + ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match addr missing"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "tiresias.example.org", "::1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify host 2 and IPv4"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = 0; + ctx.match_host_s = 1; + ctx.match_ipv4 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "sisyphus.example.com", "192.0.2.1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match host 1 and IPv6"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH; + ctx.match_host_p = 1; + ctx.match_ipv6 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "prometheus.example.com", + "2001:db8::1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate specify host 2 and IPv4 w/ key parse"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_PARSE_KEY; + ctx.match_host_s = 1; + ctx.match_ipv4 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "sisyphus.example.com", "192.0.2.1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); + + TEST_START("hostkeys_iterate match host 1 and IPv6 w/ key parse"); + memset(&ctx, 0, sizeof(ctx)); + ctx.expected = expected_full; + ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full); + ctx.flags = HKF_WANT_MATCH|HKF_WANT_PARSE_KEY; + ctx.match_host_p = 1; + ctx.match_ipv6 = 1; + prepare_expected(expected_full, ctx.nexpected); + ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"), + check, &ctx, "prometheus.example.com", + "2001:db8::1", ctx.flags, 0), 0); + cleanup_expected(expected_full, ctx.nexpected); + TEST_DONE(); +} + diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_1.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..56e1e371462560587b719e77b89db467222a5a4c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_1.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAOqffHxEW4c+Z9q/r3l4sYK8F7qrBsU8XF9upGsW62T9InROFFq9IO0x3pQ6mDA0Wtw0sqcDmkPCHPyP4Ok/fU3/drLaZusHoVYu8pBBrWsIDrKgkeX9TEodBsSrYdl4Sqtqq9EZv9+DttV6LStZrgYyUTOKwOF95wGantpLynX5AAAAFQDdt+zjRNlETDsgmxcSYFgREirJrQAAAIBQlrPaiPhR24FhnMLcHH4016vL7AqDDID6Qw7PhbXGa4/XlxWMIigjBKrIPKvnZ6p712LSnCKtcbfdx0MtmJlNa01CYqPaRhgRaf+uGdvTkTUcdaq8R5lLJL+JMNwUhcC8ijm3NqEjXjffuebGe1EzIeiITbA7Nndcd+GytwRDegAAAIEAkRYPjSVcUxfUHhHdpP6V8CuY1+CYSs9EPJ7iiWTDuXWVIBTU32oJLAnrmAcOwtIzEfPvm+rff5FI/Yhon2pB3VTXhPPEBjYzE5qANanAT4e6tzAVc5f3DUhHaDknwRYfDz86GFvuLtDjeE/UZ9t6OofYoEsCBpYozLAprBvNIQY= DSA #1 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_2.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..394e0bf002559095373b98f6c621d262286e9196 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_2.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAI38Hy/61/O5Bp6yUG8J5XQCeNjRS0xvjlCdzKLyXCueMa+L+X2L/u9PWUsy5SVbTjGgpB8sF6UkCNsV+va7S8zCCHas2MZ7GPlxP6GZBkRPTIFR0N/Pu7wfBzDQz0t0iL4VmxBfTBQv/SxkGWZg+yHihIQP9fwdSAwD/7aVh6ItAAAAFQDSyihIUlINlswM0PJ8wXSti3yIMwAAAIB+oqzaB6ozqs8YxpN5oQOBa/9HEBQEsp8RSIlQmVubXRNgktp42n+Ii1waU9UUk8DX5ahhIeR6B7ojWkqmDAji4SKpoHf4kmr6HvYo85ZSTSx0W4YK/gJHSpDJwhlT52tAfb1JCbWSObjl09B4STv7KedCHcR5oXQvvrV+XoKOSAAAAIAue/EXrs2INw1RfaKNHC0oqOMxmRitv0BFMuNVPo1VDj39CE5kA7AHjwvS1TNeaHtK5Hhgeb6vsmLmNPTOc8xCob0ilyQbt9O0GbONeF2Ge7D2UJyULA/hxql+tCYFIC6yUrmo35fF9XiNisXLoaflk9fjp7ROWWVwnki/jstaQw== DSA #2 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_3.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_3.pub new file mode 100644 index 0000000000000000000000000000000000000000..e506ea42253a48f9234dd84240a04becf1dd8fab --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_3.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAI6lz2Ip9bzE7TGuDD4SjO9S4Ac90gq0h6ai1O06eI8t/Ot2uJ5Jk2QyVr2jvIZHDl/5bwBx7+5oyjlwRoUrAPPD814wf5tU2tSnmdu1Wbf0cBswif5q0r4tevzmopp/AtgH11QHo3u0/pfyJd10qBDLV2FaYSKMmZvyPfZJ0s9pAAAAFQD5Eqjl6Rx2qVePodD9OwAPT0bU6wAAAIAfnDm6csZF0sFaJR3NIJvaYgSGr8s7cqlsk2gLltB/1wOOO2yX+NeEC+B0H93hlMfaUsPa08bwgmYxnavSMqEBpmtPceefJiEd68zwYqXd38f88wyWZ9Z5iwaI/6OVZPHzCbDxOa4ewVTevRNYUKP1xUTZNT8/gSMfZLYPk4T2AQAAAIAUKroozRMyV+3V/rxt0gFnNxRXBKk+9cl3vgsQ7ktkI9cYg7V1T2K0XF21AVMK9gODszy6PBJjV6ruXBV6TRiqIbQauivp3bHHKYsG6wiJNqwdbVwIjfvv8nn1qFoZQLXG3sdONr9NwN8KzrX89OV0BlR2dVM5qqp+YxOXymP9yg== DSA #3 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_4.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_4.pub new file mode 100644 index 0000000000000000000000000000000000000000..8552c3819287ff5341ab062de4cd55a93ead0030 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_4.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAKvjnFHm0VvMr5h2Zu3nURsxQKGoxm+DCzYDxRYcilK07Cm5c4XTrFbA2X86+9sGs++W7QRMcTJUYIg0a+UtIMtAjwORd6ZPXM2K5dBW+gh1oHyvKi767tWX7I2c+1ZPJDY95mUUfZQUEfdy9eGDSBmw/pSsveQ1ur6XNUh/MtP/AAAAFQDHnXk/9jBJAdce1pHtLWnbdPSGdQAAAIEAm2OLy8tZBfiEO3c3X1yyB/GTcDwrQCqRMDkhnsmrliec3dWkOfNTzu+MrdvF8ymTWLEqPpbMheYtvNyZ3TF0HO5W7aVBpdGZbOdOAIfB+6skqGbI8A5Up1d7dak/bSsqL2r5NjwbDOdq+1hBzzvbl/qjh+sQarV2zHrpKoQaV28AAACANtkBVedBbqIAdphCrN/LbUi9WlyuF9UZz+tlpVLYrj8GJVwnplV2tvOmUw6yP5/pzCimTsao8dpL5PWxm7fKxLWVxA+lEsA4WeC885CiZn8xhdaJOCN+NyJ2bqkz+4VPI7oDGBm0aFwUqJn+M1PiSgvI50XdF2dBsFRTRNY0wzA= DSA #4 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_5.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_5.pub new file mode 100644 index 0000000000000000000000000000000000000000..149e1efd166bc696f4448b5d2a135ce808a46f23 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_5.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBALrFy7w5ihlaOG+qR+6fj+vm5EQaO3qwxgACLcgH+VfShuOG4mkx8qFJmf+OZ3fh5iKngjNZfKtfcqI7zHWdk6378TQfQC52/kbZukjNXOLCpyNkogahcjA00onIoTK1RUDuMW28edAHwPFbpttXDTaqis+8JPMY8hZwsZGENCzTAAAAFQD6+It5vozwGgaN9ROYPMlByhi6jwAAAIBz2mcAC694vNzz9b6614gkX9d9E99PzJYfU1MPkXDziKg7MrjBw7Opd5y1jL09S3iL6lSTlHkKwVKvQ3pOwWRwXXRrKVus4I0STveoApm526jmp6mY0YEtqR98vMJ0v97h1ydt8FikKlihefCsnXVicb8887PXs2Y8C6GuFT3tfQAAAIBbmHtV5tPcrMRDkULhaQ/Whap2VKvT2DUhIHA7lx6oy/KpkltOpxDZOIGUHKqffGbiR7Jh01/y090AY5L2eCf0S2Ytx93+eADwVVpJbFJo6zSwfeey2Gm6L2oA+rCz9zTdmtZoekpD3/RAOQjnJIAPwbs7mXwabZTw4xRtiYIRrw== DSA #5 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_6.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_6.pub new file mode 100644 index 0000000000000000000000000000000000000000..edbb97643d263c360b6ba482f1a6438ef048c57e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/dsa_6.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_1.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..16a535bccb0ac7326f3566cf0c67e90356735ba7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_1.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBF6yQEtD9yBw9gmDRf477WBBzvWhAa0ioBI3nbA4emKykj0RbuQd5C4XdQAEOZGzE7v//FcCjwB2wi+JH5eKkxCtN6CjohDASZ1huoIV2UVyYIicZJEEOg1IWjjphvaxtw== ECDSA #1 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_2.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..d2bad11e23798725a10846a55661f163ca5017d2 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_2.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAB8qVcXwgBM92NCmReQlPrZAoui4Bz/mW0VUBFOpHXXW1n+15b/Y7Pc6UBd/ITTZmaBciXY+PWaSBGdwc5GdqGdLgFyJ/QAGrFMPNpVutm/82gNQzlxpNwjbMcKyiZEXzSgnjS6DzMQ0WuSMdzIBXq8OW/Kafxg4ZkU6YqALUXxlQMZuQ== ECDSA #2 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_3.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_3.pub new file mode 100644 index 0000000000000000000000000000000000000000..e3ea9254ec1a9f6ee136c6c54933e5bee96aefe3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_3.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIb3BhJZk+vUQPg5TQc1koIzuGqloCq7wjr9LjlhG24IBeiFHLsdWw74HDlH4DrOmlxToVYk2lTdnjARleRByjk= ECDSA #3 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_4.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_4.pub new file mode 100644 index 0000000000000000000000000000000000000000..2d616f5c6de643ecad5afc4bf282d431c3455099 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_4.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHZd0OXHIWwK3xnjAdMZ1tojxWycdu38pORO/UX5cqsKMgGCKQVBWWO3TFk1ePkGIE9VMWT1hCGqWRRwYlH+dSE= ECDSA #4 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_5.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_5.pub new file mode 100644 index 0000000000000000000000000000000000000000..a3df9b3f40aa82dc64f95dfbd75322c028ddcc00 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_5.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIudcagzq4QPtP1jkpje34+0POLB0jwT64hqrbCqhTH2T800KDZ0h2vwlJYa3OP3Oqru9AB5pnuHsKw7mAhUGY= ECDSA #5 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_6.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_6.pub new file mode 100644 index 0000000000000000000000000000000000000000..139f5a7bf8a4bc2003fc7e29916c7090897059bb --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ecdsa_6.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_1.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..0b12efedbfdc4434e588e9d551283a3ec684cc4b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_1.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK9ks7jkua5YWIwByRnnnc6UPJQWI75O0e/UJdPYU1JI ED25519 #1 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_2.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..78e262bcc19527f3dc9537bb0f434686dec1ae8f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_2.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIBp6PVW0z2o9C4Ukv/JOgmK7QMFe1pD1s3ADFF7IQob ED25519 #2 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_3.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_3.pub new file mode 100644 index 0000000000000000000000000000000000000000..64e5f12a6209915dcd48ebc2fb1ef833e04cda45 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_3.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBlYfExtYZAPqYvYdrlpGlSWhh/XNHcH3v3c2JzsVNbB ED25519 #3 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_4.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_4.pub new file mode 100644 index 0000000000000000000000000000000000000000..47b6724ec89ee52f0645f0ca8bfb5a44df4641d7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_4.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDFP8L9REfN/iYy1KIRtFqSCn3V2+vOCpoZYENFGLdOF ED25519 #4 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_5.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_5.pub new file mode 100644 index 0000000000000000000000000000000000000000..72ccae6fed2e4b3aa5bdb0056986b6deec801df1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_5.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINf63qSV8rD57N+digID8t28WVhd3Yf2K2UhaoG8TsWQ ED25519 #5 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_6.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_6.pub new file mode 100644 index 0000000000000000000000000000000000000000..0f719731db29c6133b57d9d33c8d77ba0b841a2e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/ed25519_6.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/known_hosts b/aosp/external/openssh/regress/unittests/hostkeys/testdata/known_hosts new file mode 100644 index 0000000000000000000000000000000000000000..4446f45dffe8072b84b2960369961c021b72c6d5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/known_hosts @@ -0,0 +1,50 @@ +# Plain host keys, plain host names +sisyphus.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAOqffHxEW4c+Z9q/r3l4sYK8F7qrBsU8XF9upGsW62T9InROFFq9IO0x3pQ6mDA0Wtw0sqcDmkPCHPyP4Ok/fU3/drLaZusHoVYu8pBBrWsIDrKgkeX9TEodBsSrYdl4Sqtqq9EZv9+DttV6LStZrgYyUTOKwOF95wGantpLynX5AAAAFQDdt+zjRNlETDsgmxcSYFgREirJrQAAAIBQlrPaiPhR24FhnMLcHH4016vL7AqDDID6Qw7PhbXGa4/XlxWMIigjBKrIPKvnZ6p712LSnCKtcbfdx0MtmJlNa01CYqPaRhgRaf+uGdvTkTUcdaq8R5lLJL+JMNwUhcC8ijm3NqEjXjffuebGe1EzIeiITbA7Nndcd+GytwRDegAAAIEAkRYPjSVcUxfUHhHdpP6V8CuY1+CYSs9EPJ7iiWTDuXWVIBTU32oJLAnrmAcOwtIzEfPvm+rff5FI/Yhon2pB3VTXhPPEBjYzE5qANanAT4e6tzAVc5f3DUhHaDknwRYfDz86GFvuLtDjeE/UZ9t6OofYoEsCBpYozLAprBvNIQY= DSA #1 +sisyphus.example.com ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBF6yQEtD9yBw9gmDRf477WBBzvWhAa0ioBI3nbA4emKykj0RbuQd5C4XdQAEOZGzE7v//FcCjwB2wi+JH5eKkxCtN6CjohDASZ1huoIV2UVyYIicZJEEOg1IWjjphvaxtw== ECDSA #1 +sisyphus.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK9ks7jkua5YWIwByRnnnc6UPJQWI75O0e/UJdPYU1JI ED25519 #1 +sisyphus.example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDg4hB4vAZHJ0PVRiJajOv/GlytFWNpv5/9xgB9+5BIbvp8LOrFZ5D9K0Gsmwpd4G4rfaAz8j896DhMArg0vtkilIPPGt/6VzWMERgvaIQPJ/IE99X3+fjcAG56oAWwy29JX10lQMzBPU6XJIaN/zqpkb6qUBiAHBdLpxrFBBU0/w== RSA #1 + +# Plain host keys, hostnames + addresses +prometheus.example.com,192.0.2.1,2001:db8::1 ssh-dss AAAAB3NzaC1kc3MAAACBAI38Hy/61/O5Bp6yUG8J5XQCeNjRS0xvjlCdzKLyXCueMa+L+X2L/u9PWUsy5SVbTjGgpB8sF6UkCNsV+va7S8zCCHas2MZ7GPlxP6GZBkRPTIFR0N/Pu7wfBzDQz0t0iL4VmxBfTBQv/SxkGWZg+yHihIQP9fwdSAwD/7aVh6ItAAAAFQDSyihIUlINlswM0PJ8wXSti3yIMwAAAIB+oqzaB6ozqs8YxpN5oQOBa/9HEBQEsp8RSIlQmVubXRNgktp42n+Ii1waU9UUk8DX5ahhIeR6B7ojWkqmDAji4SKpoHf4kmr6HvYo85ZSTSx0W4YK/gJHSpDJwhlT52tAfb1JCbWSObjl09B4STv7KedCHcR5oXQvvrV+XoKOSAAAAIAue/EXrs2INw1RfaKNHC0oqOMxmRitv0BFMuNVPo1VDj39CE5kA7AHjwvS1TNeaHtK5Hhgeb6vsmLmNPTOc8xCob0ilyQbt9O0GbONeF2Ge7D2UJyULA/hxql+tCYFIC6yUrmo35fF9XiNisXLoaflk9fjp7ROWWVwnki/jstaQw== DSA #2 +prometheus.example.com,192.0.2.1,2001:db8::1 ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAB8qVcXwgBM92NCmReQlPrZAoui4Bz/mW0VUBFOpHXXW1n+15b/Y7Pc6UBd/ITTZmaBciXY+PWaSBGdwc5GdqGdLgFyJ/QAGrFMPNpVutm/82gNQzlxpNwjbMcKyiZEXzSgnjS6DzMQ0WuSMdzIBXq8OW/Kafxg4ZkU6YqALUXxlQMZuQ== ECDSA #2 +prometheus.example.com,192.0.2.1,2001:db8::1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIBp6PVW0z2o9C4Ukv/JOgmK7QMFe1pD1s3ADFF7IQob ED25519 #2 +prometheus.example.com,192.0.2.1,2001:db8::1 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDmbUhNabB5AmBDX6GNHZ3lbn7pRxqfpW+f53QqNGlK0sLV+0gkMIrOfUp1kdE2ZLE6tfzdicatj/RlH6/wuo4yyYb+Pyx3G0vxdmAIiA4aANq38XweDucBC0TZkRWVHK+Gs5V/uV0z7N0axJvkkJujMLvST3CRiiWwlficBc6yVQ== RSA #2 + +# Some hosts with wildcard names / IPs +*.example.com,192.0.2.*,2001:* ssh-dss AAAAB3NzaC1kc3MAAACBAI6lz2Ip9bzE7TGuDD4SjO9S4Ac90gq0h6ai1O06eI8t/Ot2uJ5Jk2QyVr2jvIZHDl/5bwBx7+5oyjlwRoUrAPPD814wf5tU2tSnmdu1Wbf0cBswif5q0r4tevzmopp/AtgH11QHo3u0/pfyJd10qBDLV2FaYSKMmZvyPfZJ0s9pAAAAFQD5Eqjl6Rx2qVePodD9OwAPT0bU6wAAAIAfnDm6csZF0sFaJR3NIJvaYgSGr8s7cqlsk2gLltB/1wOOO2yX+NeEC+B0H93hlMfaUsPa08bwgmYxnavSMqEBpmtPceefJiEd68zwYqXd38f88wyWZ9Z5iwaI/6OVZPHzCbDxOa4ewVTevRNYUKP1xUTZNT8/gSMfZLYPk4T2AQAAAIAUKroozRMyV+3V/rxt0gFnNxRXBKk+9cl3vgsQ7ktkI9cYg7V1T2K0XF21AVMK9gODszy6PBJjV6ruXBV6TRiqIbQauivp3bHHKYsG6wiJNqwdbVwIjfvv8nn1qFoZQLXG3sdONr9NwN8KzrX89OV0BlR2dVM5qqp+YxOXymP9yg== DSA #3 +*.example.com,192.0.2.*,2001:* ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIb3BhJZk+vUQPg5TQc1koIzuGqloCq7wjr9LjlhG24IBeiFHLsdWw74HDlH4DrOmlxToVYk2lTdnjARleRByjk= ECDSA #3 +*.example.com,192.0.2.*,2001:* ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBlYfExtYZAPqYvYdrlpGlSWhh/XNHcH3v3c2JzsVNbB ED25519 #3 +*.example.com,192.0.2.*,2001:* ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDX8F93W3SH4ZSus4XUQ2cw9dqcuyUETTlKEeGv3zlknV3YCoe2Mp04naDhiuwj8sOsytrZSESzLY1ZEyzrjxE6ZFVv8NKgck/AbRjcwlRFOcx9oKUxOrXRa0IoXlTq0kyjKCJfaHBKnGitZThknCPTbVmpATkm5xx6J0WEDozfoQ== RSA #3 + +# Hashed hostname and address entries +|1|z3xOIdT5ue3Vuf3MzT67kaioqjw=|GZhhe5uwDOBQrC9N4cCjpbLpSn4= ssh-dss AAAAB3NzaC1kc3MAAACBALrFy7w5ihlaOG+qR+6fj+vm5EQaO3qwxgACLcgH+VfShuOG4mkx8qFJmf+OZ3fh5iKngjNZfKtfcqI7zHWdk6378TQfQC52/kbZukjNXOLCpyNkogahcjA00onIoTK1RUDuMW28edAHwPFbpttXDTaqis+8JPMY8hZwsZGENCzTAAAAFQD6+It5vozwGgaN9ROYPMlByhi6jwAAAIBz2mcAC694vNzz9b6614gkX9d9E99PzJYfU1MPkXDziKg7MrjBw7Opd5y1jL09S3iL6lSTlHkKwVKvQ3pOwWRwXXRrKVus4I0STveoApm526jmp6mY0YEtqR98vMJ0v97h1ydt8FikKlihefCsnXVicb8887PXs2Y8C6GuFT3tfQAAAIBbmHtV5tPcrMRDkULhaQ/Whap2VKvT2DUhIHA7lx6oy/KpkltOpxDZOIGUHKqffGbiR7Jh01/y090AY5L2eCf0S2Ytx93+eADwVVpJbFJo6zSwfeey2Gm6L2oA+rCz9zTdmtZoekpD3/RAOQjnJIAPwbs7mXwabZTw4xRtiYIRrw== DSA #5 +|1|B7t/AYabn8zgwU47Cb4A/Nqt3eI=|arQPZyRphkzisr7w6wwikvhaOyE= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIudcagzq4QPtP1jkpje34+0POLB0jwT64hqrbCqhTH2T800KDZ0h2vwlJYa3OP3Oqru9AB5pnuHsKw7mAhUGY= ECDSA #5 +|1|JR81WxEocTP5d7goIRkl8fHBbno=|l6sj6FOsoXxgEZMzn/BnOfPKN68= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINf63qSV8rD57N+digID8t28WVhd3Yf2K2UhaoG8TsWQ ED25519 #5 +|1|W7x4zY6KtTZJgsopyOusJqvVPag=|QauLt7hKezBZFZi2i4Xopho7Nsk= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC/C15Q4sfnk7BZff1er8bscay+5s51oD4eWArlHWMK/ZfYeeTAccTy+7B7Jv+MS4nKCpflrvJI2RQz4kS8vF0ATdBbi4jeWefStlHNg0HLhnCY7NAfDIlRdaN9lm3Pqm2vmr+CkqwcJaSpycDg8nPN9yNAuD6pv7NDuUnECezojQ== RSA #5 + +|1|mxnU8luzqWLvfVi5qBm5xVIyCRM=|9Epopft7LBd80Bf6RmWPIpwa8yU= ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6 +|1|klvLmvh2vCpkNMDEjVvrE8SJWTg=|e/dqEEBLnbgqmwEesl4cDRu/7TM= ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6 +|1|wsk3ddB3UjuxEsoeNCeZjZ6NvZs=|O3O/q2Z/u7DrxoTiIq6kzCevQT0= ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6 +|1|B8epmkLSni+vGZDijr/EwxeR2k4=|7ct8yzNOVJhKm3ZD2w0XIT7df8E= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6 +|1|JojD885UhYhbCu571rgyM/5PpYU=|BJaU2aE1FebQZy3B5tzTDRWFRG0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6 +|1|5t7UDHDybVrDZVQPCpwdnr6nk4k=|EqJ73W/veIL3H2x+YWHcJxI5ETA= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6 +|1|OCcBfGc/b9+ip+W6Gp+3ftdluO4=|VbrKUdzOOtIBOOmEE+jlK4SD3Xc= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6 +|1|9fLN0YdP+BJ25lKuKvYuOdUo93w=|vZyr0rOiX01hv5XbghhHMW+Zb3U= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6 +|1|nc9RoaaQ0s5jdPxwlUmluGHU3uk=|un6OsJajokKQ3MgyS9mfDNeyP6U= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6 +|1|rsHB6juT9q6GOY91qOeOwL6TSJE=|ps/vXF9Izuues5PbOn887Gw/2Dg= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6 +|1|BsckdLH2aRyWQooRmv+Yo3t4dKg=|Lf3tJc5Iyx0KxNwAG89FsImsfEE= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6 +|1|plqkBA4hq7UATyd5+/Xl+zL7ghw=|stacofaUed46666mfqxp9gJFjt4= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6 + + +# Revoked and CA keys +@revoked sisyphus.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDFP8L9REfN/iYy1KIRtFqSCn3V2+vOCpoZYENFGLdOF ED25519 #4 +@cert-authority prometheus.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHZd0OXHIWwK3xnjAdMZ1tojxWycdu38pORO/UX5cqsKMgGCKQVBWWO3TFk1ePkGIE9VMWT1hCGqWRRwYlH+dSE= ECDSA #4 +@cert-authority *.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAKvjnFHm0VvMr5h2Zu3nURsxQKGoxm+DCzYDxRYcilK07Cm5c4XTrFbA2X86+9sGs++W7QRMcTJUYIg0a+UtIMtAjwORd6ZPXM2K5dBW+gh1oHyvKi767tWX7I2c+1ZPJDY95mUUfZQUEfdy9eGDSBmw/pSsveQ1ur6XNUh/MtP/AAAAFQDHnXk/9jBJAdce1pHtLWnbdPSGdQAAAIEAm2OLy8tZBfiEO3c3X1yyB/GTcDwrQCqRMDkhnsmrliec3dWkOfNTzu+MrdvF8ymTWLEqPpbMheYtvNyZ3TF0HO5W7aVBpdGZbOdOAIfB+6skqGbI8A5Up1d7dak/bSsqL2r5NjwbDOdq+1hBzzvbl/qjh+sQarV2zHrpKoQaV28AAACANtkBVedBbqIAdphCrN/LbUi9WlyuF9UZz+tlpVLYrj8GJVwnplV2tvOmUw6yP5/pzCimTsao8dpL5PWxm7fKxLWVxA+lEsA4WeC885CiZn8xhdaJOCN+NyJ2bqkz+4VPI7oDGBm0aFwUqJn+M1PiSgvI50XdF2dBsFRTRNY0wzA= DSA #4 + +# Some invalid lines +@what sisyphus.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAOqffHxEW4c+Z9q/r3l4sYK8F7qrBsU8XF9upGsW62T9InROFFq9IO0x3pQ6mDA0Wtw0sqcDmkPCHPyP4Ok/fU3/drLaZusHoVYu8pBBrWsIDrKgkeX9TEodBsSrYdl4Sqtqq9EZv9+DttV6LStZrgYyUTOKwOF95wGantpLynX5AAAAFQDdt+zjRNlETDsgmxcSYFgREirJrQAAAIBQlrPaiPhR24FhnMLcHH4016vL7AqDDID6Qw7PhbXGa4/XlxWMIigjBKrIPKvnZ6p712LSnCKtcbfdx0MtmJlNa01CYqPaRhgRaf+uGdvTkTUcdaq8R5lLJL+JMNwUhcC8ijm3NqEjXjffuebGe1EzIeiITbA7Nndcd+GytwRDegAAAIEAkRYPjSVcUxfUHhHdpP6V8CuY1+CYSs9EPJ7iiWTDuXWVIBTU32oJLAnrmAcOwtIzEfPvm+rff5FI/Yhon2pB3VTXhPPEBjYzE5qANanAT4e6tzAVc5f3DUhHaDknwRYfDz86GFvuLtDjeE/UZ9t6OofYoEsCBpYozLAprBvNIQY= DSA #1 +sisyphus.example.com +prometheus.example.com ssh-ed25519 +sisyphus.example.com ssh-dsa AAAATgAAAAdz +sisyphus.example.com ssh-XXX AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg== +prometheus.example.com ssh-rsa AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg== diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_1.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..772ce9c05bbb738c91b28dffe5c192128510f86e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_1.pub @@ -0,0 +1 @@ +1024 65537 153895431603677073925890314548566704948446776958334195280085080329934839226701954473292358821568047724356487621573742372399387931887004184139835510820577359977148363519970774657801798872789118894962853659233045778161859413980935372685480527355016624825696983269800574755126132814333241868538220824608980319407 RSA1 #1 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_2.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..78794b94153bf3a2c3b0ab48aa934f636523c9c5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_2.pub @@ -0,0 +1 @@ +1024 65537 135970715082947442639683969597180728933388298633245835186618852623800675939308729462220235058285909679252157995530180587329132927339620517781785310829060832352381015614725360278571924286986474946772141568893116432268565829418506866604294073334978275702221949783314402806080929601995102334442541344606109853641 RSA1 #2 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_3.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_3.pub new file mode 100644 index 0000000000000000000000000000000000000000..0c035fe0ac0f15fadb00f7af25c5c857b4feb8c5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_3.pub @@ -0,0 +1 @@ +1024 65537 125895605498029643697051635076028105429632810811904702876152645261610759866299221305725069141163240694267669117205342283569102183636228981857946763978553664895308762890072813014496700601576921921752482059207749978374872713540759920335553799711267170948655579130584031555334229966603000896364091459595522912269 RSA1 #3 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_4.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_4.pub new file mode 100644 index 0000000000000000000000000000000000000000..00064423e7f9c37c8cddcc0f5201d2db4a6e2906 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_4.pub @@ -0,0 +1 @@ +1024 65537 174143366122697048196335388217056770310345753698079464367148030836533360510864881734142526411160017107552815906024399248049666856133771656680462456979369587903909343046704480897527203474513676654933090991684252819423129896444427656841613263783484827101210734799449281639493127615902427443211183258155381810593 RSA1 #4 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_5.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_5.pub new file mode 100644 index 0000000000000000000000000000000000000000..bb53c2642dbe1782db417ba37975c9fb6b8c4952 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_5.pub @@ -0,0 +1 @@ +1024 65537 127931411493401587586867047972295564331543694182352197506125410692673654572057908999642645524647232712160516076508316152810117209181150078352725299319149726341058893406440426414316276977768958023952319602422835879783057966985348561111880658922724668687074412548487722084792283453716871417610020757212399252171 RSA1 #5 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_6.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_6.pub new file mode 100644 index 0000000000000000000000000000000000000000..85d6576b57ac3435cb73961ae6628a6a6cb9f7c0 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa1_6.pub @@ -0,0 +1 @@ +1024 65537 140883028436203600354693376066567741282115117509696517282419557936340193768851493584179972504103033755515036493433917203732876685813283050574208967197963391667532902202382549275760997891673884333346000558018002659506756213191532156293935482587878596032743105911487673274674568768638010598205190227631909167257 RSA1 #6 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_1.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..2b87885a19dd3f9e4a82b5f8f463f2f86e0ae03b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDg4hB4vAZHJ0PVRiJajOv/GlytFWNpv5/9xgB9+5BIbvp8LOrFZ5D9K0Gsmwpd4G4rfaAz8j896DhMArg0vtkilIPPGt/6VzWMERgvaIQPJ/IE99X3+fjcAG56oAWwy29JX10lQMzBPU6XJIaN/zqpkb6qUBiAHBdLpxrFBBU0/w== RSA #1 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_2.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..33f1fd93b58b8004206d9e6b7873d1ec2dcdb8e0 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_2.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDmbUhNabB5AmBDX6GNHZ3lbn7pRxqfpW+f53QqNGlK0sLV+0gkMIrOfUp1kdE2ZLE6tfzdicatj/RlH6/wuo4yyYb+Pyx3G0vxdmAIiA4aANq38XweDucBC0TZkRWVHK+Gs5V/uV0z7N0axJvkkJujMLvST3CRiiWwlficBc6yVQ== RSA #2 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_3.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_3.pub new file mode 100644 index 0000000000000000000000000000000000000000..c2f6b208ce7180549cca5cc13485a8eb823f9bc2 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_3.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDX8F93W3SH4ZSus4XUQ2cw9dqcuyUETTlKEeGv3zlknV3YCoe2Mp04naDhiuwj8sOsytrZSESzLY1ZEyzrjxE6ZFVv8NKgck/AbRjcwlRFOcx9oKUxOrXRa0IoXlTq0kyjKCJfaHBKnGitZThknCPTbVmpATkm5xx6J0WEDozfoQ== RSA #3 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_4.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_4.pub new file mode 100644 index 0000000000000000000000000000000000000000..35545a713aaa9c3d8fa93fd8d8fb2d6d43791629 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_4.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDI8AdjBAozcdRnIikVlt69iyDHKyrtxmpdkbRy9bWaL86OH+PTmLUk5e+T/ufiakpeE2pm0hkE3e4Sh/FsY+rsQdRoraWVNFfchcMeVlKvuy5RZN0ElvmaQebOJUeNeBn2LLw8aL8bJ4CP/bQRKrmrSSqjz3+4H9YNVyyk1OGBPQ== RSA #4 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_5.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_5.pub new file mode 100644 index 0000000000000000000000000000000000000000..befbaa7d93de33c30f1c3c4828ef3cea72d8507f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_5.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC/C15Q4sfnk7BZff1er8bscay+5s51oD4eWArlHWMK/ZfYeeTAccTy+7B7Jv+MS4nKCpflrvJI2RQz4kS8vF0ATdBbi4jeWefStlHNg0HLhnCY7NAfDIlRdaN9lm3Pqm2vmr+CkqwcJaSpycDg8nPN9yNAuD6pv7NDuUnECezojQ== RSA #5 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_6.pub b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_6.pub new file mode 100644 index 0000000000000000000000000000000000000000..393e1167200f9ee3445e53be0a69082086c426de --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/testdata/rsa_6.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6 diff --git a/aosp/external/openssh/regress/unittests/hostkeys/tests.c b/aosp/external/openssh/regress/unittests/hostkeys/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..92c7646ad164106b2cd604067bbd5c82840e32e3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/hostkeys/tests.c @@ -0,0 +1,16 @@ +/* $OpenBSD: tests.c,v 1.1 2015/02/16 22:18:34 djm Exp $ */ +/* + * Regress test for known_hosts-related API. + * + * Placed in the public domain + */ + +void tests(void); +void test_iterate(void); /* test_iterate.c */ + +void +tests(void) +{ + test_iterate(); +} + diff --git a/aosp/external/openssh/regress/unittests/kex/Makefile b/aosp/external/openssh/regress/unittests/kex/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..50b117c07851e2554f3789956e524e564654311f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/kex/Makefile @@ -0,0 +1,40 @@ +# $OpenBSD: Makefile,v 1.12 2021/01/09 12:24:30 dtucker Exp $ + +PROG=test_kex +SRCS=tests.c test_kex.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c +SRCS+=ssh-dss.c ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c +SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c +SRCS+=addr.c addrmatch.c bitmap.c packet.c dispatch.c canohost.c ssh_api.c +SRCS+=compat.c ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c +SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c +SRCS+=ssh-ed25519-sk.c sk-usbhid.c + +SRCS+= kex.c +SRCS+= dh.c +SRCS+= kexdh.c +SRCS+= kexecdh.c +SRCS+= kexgex.c +SRCS+= kexgexc.c +SRCS+= kexgexs.c +SRCS+= kexc25519.c +SRCS+= smult_curve25519_ref.c +SRCS+= kexgen.c +SRCS+= kexsntrup761x25519.c +SRCS+= sntrup761.c +SRCS+= utf8.c + +SRCS+=digest-openssl.c +#SRCS+=digest-libc.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include + +LDADD+=-lz diff --git a/aosp/external/openssh/regress/unittests/kex/test_kex.c b/aosp/external/openssh/regress/unittests/kex/test_kex.c new file mode 100644 index 0000000000000000000000000000000000000000..c26761ee7c48fc2e2171e265d6aeee9190117116 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/kex/test_kex.c @@ -0,0 +1,208 @@ +/* $OpenBSD: test_kex.c,v 1.6 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test KEX + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "ssh_api.h" +#include "sshbuf.h" +#include "packet.h" +#include "myproposal.h" + +void kex_tests(void); +static int do_debug = 0; + +static int +do_send_and_receive(struct ssh *from, struct ssh *to) +{ + u_char type; + size_t len; + const u_char *buf; + int r; + + for (;;) { + if ((r = ssh_packet_next(from, &type)) != 0) { + fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r)); + return r; + } + if (type != 0) + return 0; + buf = ssh_output_ptr(from, &len); + if (do_debug) + printf("%zu", len); + if (len == 0) + return 0; + if ((r = ssh_output_consume(from, len)) != 0 || + (r = ssh_input_append(to, buf, len)) != 0) + return r; + } +} + +static void +run_kex(struct ssh *client, struct ssh *server) +{ + int r = 0; + + while (!server->kex->done || !client->kex->done) { + if (do_debug) + printf(" S:"); + if ((r = do_send_and_receive(server, client))) + break; + if (do_debug) + printf(" C:"); + if ((r = do_send_and_receive(client, server))) + break; + } + if (do_debug) + printf("done: %s\n", ssh_err(r)); + ASSERT_INT_EQ(r, 0); + ASSERT_INT_EQ(server->kex->done, 1); + ASSERT_INT_EQ(client->kex->done, 1); +} + +static void +do_kex_with_key(char *kex, int keytype, int bits) +{ + struct ssh *client = NULL, *server = NULL, *server2 = NULL; + struct sshkey *private, *public; + struct sshbuf *state; + struct kex_params kex_params; + char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; + char *keyname = NULL; + + TEST_START("sshkey_generate"); + ASSERT_INT_EQ(sshkey_generate(keytype, bits, &private), 0); + TEST_DONE(); + + TEST_START("sshkey_from_private"); + ASSERT_INT_EQ(sshkey_from_private(private, &public), 0); + TEST_DONE(); + + TEST_START("ssh_init"); + memcpy(kex_params.proposal, myproposal, sizeof(myproposal)); + if (kex != NULL) + kex_params.proposal[PROPOSAL_KEX_ALGS] = kex; + keyname = strdup(sshkey_ssh_name(private)); + ASSERT_PTR_NE(keyname, NULL); + kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname; + ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0); + ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0); + ASSERT_PTR_NE(client, NULL); + ASSERT_PTR_NE(server, NULL); + TEST_DONE(); + + TEST_START("ssh_add_hostkey"); + ASSERT_INT_EQ(ssh_add_hostkey(server, private), 0); + ASSERT_INT_EQ(ssh_add_hostkey(client, public), 0); + TEST_DONE(); + + TEST_START("kex"); + run_kex(client, server); + TEST_DONE(); + + TEST_START("rekeying client"); + ASSERT_INT_EQ(kex_send_kexinit(client), 0); + run_kex(client, server); + TEST_DONE(); + + TEST_START("rekeying server"); + ASSERT_INT_EQ(kex_send_kexinit(server), 0); + run_kex(client, server); + TEST_DONE(); + + TEST_START("ssh_packet_get_state"); + state = sshbuf_new(); + ASSERT_PTR_NE(state, NULL); + ASSERT_INT_EQ(ssh_packet_get_state(server, state), 0); + ASSERT_INT_GE(sshbuf_len(state), 1); + TEST_DONE(); + + TEST_START("ssh_packet_set_state"); + server2 = NULL; + ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0); + ASSERT_PTR_NE(server2, NULL); + ASSERT_INT_EQ(ssh_add_hostkey(server2, private), 0); + ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0); + ASSERT_INT_EQ(sshbuf_len(state), 0); + sshbuf_free(state); + ASSERT_PTR_NE(server2->kex, NULL); + /* XXX we need to set the callbacks */ +#ifdef WITH_OPENSSL + server2->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; + server2->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; + server2->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + server2->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; +#ifdef OPENSSL_HAS_ECC + server2->kex->kex[KEX_ECDH_SHA2] = kex_gen_server; +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + server2->kex->kex[KEX_C25519_SHA256] = kex_gen_server; + server2->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; + server2->kex->load_host_public_key = server->kex->load_host_public_key; + server2->kex->load_host_private_key = server->kex->load_host_private_key; + server2->kex->sign = server->kex->sign; + TEST_DONE(); + + TEST_START("rekeying server2"); + ASSERT_INT_EQ(kex_send_kexinit(server2), 0); + run_kex(client, server2); + ASSERT_INT_EQ(kex_send_kexinit(client), 0); + run_kex(client, server2); + TEST_DONE(); + + TEST_START("cleanup"); + sshkey_free(private); + sshkey_free(public); + ssh_free(client); + ssh_free(server); + ssh_free(server2); + free(keyname); + TEST_DONE(); +} + +static void +do_kex(char *kex) +{ +#ifdef WITH_OPENSSL + do_kex_with_key(kex, KEY_RSA, 2048); + do_kex_with_key(kex, KEY_DSA, 1024); +#ifdef OPENSSL_HAS_ECC + do_kex_with_key(kex, KEY_ECDSA, 256); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + do_kex_with_key(kex, KEY_ED25519, 256); +} + +void +kex_tests(void) +{ + do_kex("curve25519-sha256@libssh.org"); +#ifdef WITH_OPENSSL +#ifdef OPENSSL_HAS_ECC + do_kex("ecdh-sha2-nistp256"); + do_kex("ecdh-sha2-nistp384"); + do_kex("ecdh-sha2-nistp521"); +#endif /* OPENSSL_HAS_ECC */ + do_kex("diffie-hellman-group-exchange-sha256"); + do_kex("diffie-hellman-group-exchange-sha1"); + do_kex("diffie-hellman-group14-sha1"); + do_kex("diffie-hellman-group1-sha1"); +# ifdef USE_SNTRUP761X25519 + do_kex("sntrup761x25519-sha512@openssh.com"); +# endif /* USE_SNTRUP761X25519 */ +#endif /* WITH_OPENSSL */ +} diff --git a/aosp/external/openssh/regress/unittests/kex/tests.c b/aosp/external/openssh/regress/unittests/kex/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..e7036ec17f7ba8d97d209ad29e066cd5841634b5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/kex/tests.c @@ -0,0 +1,14 @@ +/* $OpenBSD: tests.c,v 1.1 2015/01/15 23:41:29 markus Exp $ */ +/* + * Placed in the public domain + */ + +#include "../test_helper/test_helper.h" + +void kex_tests(void); + +void +tests(void) +{ + kex_tests(); +} diff --git a/aosp/external/openssh/regress/unittests/match/Makefile b/aosp/external/openssh/regress/unittests/match/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..939163d30ef56d4fa4d44fc04c3dc9dc93db504a --- /dev/null +++ b/aosp/external/openssh/regress/unittests/match/Makefile @@ -0,0 +1,16 @@ +# $OpenBSD: Makefile,v 1.5 2021/01/09 12:24:31 dtucker Exp $ + +PROG=test_match +SRCS=tests.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=match.c misc.c log.c uidswap.c fatal.c ssherr.c addrmatch.c xmalloc.c +SRCS+=cleanup.c atomicio.c addr.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include diff --git a/aosp/external/openssh/regress/unittests/match/tests.c b/aosp/external/openssh/regress/unittests/match/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..f00d1f9348fcfc29dad78a1081064526ec17a59e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/match/tests.c @@ -0,0 +1,131 @@ +/* $OpenBSD: tests.c,v 1.8 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for matching functions + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "match.h" + +void +tests(void) +{ + TEST_START("match_pattern"); + ASSERT_INT_EQ(match_pattern("", ""), 1); + ASSERT_INT_EQ(match_pattern("", "aaa"), 0); + ASSERT_INT_EQ(match_pattern("aaa", ""), 0); + ASSERT_INT_EQ(match_pattern("aaa", "aaaa"), 0); + ASSERT_INT_EQ(match_pattern("aaaa", "aaa"), 0); + TEST_DONE(); + + TEST_START("match_pattern wildcard"); + ASSERT_INT_EQ(match_pattern("", "*"), 1); + ASSERT_INT_EQ(match_pattern("a", "?"), 1); + ASSERT_INT_EQ(match_pattern("aa", "a?"), 1); + ASSERT_INT_EQ(match_pattern("a", "*"), 1); + ASSERT_INT_EQ(match_pattern("aa", "a*"), 1); + ASSERT_INT_EQ(match_pattern("aa", "?*"), 1); + ASSERT_INT_EQ(match_pattern("aa", "**"), 1); + ASSERT_INT_EQ(match_pattern("aa", "?a"), 1); + ASSERT_INT_EQ(match_pattern("aa", "*a"), 1); + ASSERT_INT_EQ(match_pattern("ba", "a?"), 0); + ASSERT_INT_EQ(match_pattern("ba", "a*"), 0); + ASSERT_INT_EQ(match_pattern("ab", "?a"), 0); + ASSERT_INT_EQ(match_pattern("ab", "*a"), 0); + TEST_DONE(); + + TEST_START("match_pattern_list"); + ASSERT_INT_EQ(match_pattern_list("", "", 0), 0); /* no patterns */ + ASSERT_INT_EQ(match_pattern_list("", "*", 0), 1); + ASSERT_INT_EQ(match_pattern_list("", "!*", 0), -1); + ASSERT_INT_EQ(match_pattern_list("", "!a,*", 0), 1); + ASSERT_INT_EQ(match_pattern_list("", "*,!a", 0), 1); + ASSERT_INT_EQ(match_pattern_list("", "a,!*", 0), -1); + ASSERT_INT_EQ(match_pattern_list("", "!*,a", 0), -1); + ASSERT_INT_EQ(match_pattern_list("a", "", 0), 0); + ASSERT_INT_EQ(match_pattern_list("a", "*", 0), 1); + ASSERT_INT_EQ(match_pattern_list("a", "!*", 0), -1); + ASSERT_INT_EQ(match_pattern_list("a", "!a", 0), -1); + /* XXX negated ASSERT_INT_EQ(match_pattern_list("a", "!b", 0), 1); */ + ASSERT_INT_EQ(match_pattern_list("a", "!a,*", 0), -1); + ASSERT_INT_EQ(match_pattern_list("b", "!a,*", 0), 1); + ASSERT_INT_EQ(match_pattern_list("a", "*,!a", 0), -1); + ASSERT_INT_EQ(match_pattern_list("b", "*,!a", 0), 1); + ASSERT_INT_EQ(match_pattern_list("a", "a,!*", 0), -1); + ASSERT_INT_EQ(match_pattern_list("b", "a,!*", 0), -1); + ASSERT_INT_EQ(match_pattern_list("a", "a,!a", 0), -1); + /* XXX negated ASSERT_INT_EQ(match_pattern_list("b", "a,!a", 0), 1); */ + ASSERT_INT_EQ(match_pattern_list("a", "!*,a", 0), -1); + ASSERT_INT_EQ(match_pattern_list("b", "!*,a", 0), -1); + TEST_DONE(); + + TEST_START("match_pattern_list lowercase"); + ASSERT_INT_EQ(match_pattern_list("abc", "ABC", 0), 0); + ASSERT_INT_EQ(match_pattern_list("ABC", "abc", 0), 0); + ASSERT_INT_EQ(match_pattern_list("abc", "ABC", 1), 1); + ASSERT_INT_EQ(match_pattern_list("ABC", "abc", 1), 0); + TEST_DONE(); + + TEST_START("addr_match_list"); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.1/44"), -2); + ASSERT_INT_EQ(addr_match_list(NULL, "127.0.0.1/44"), -2); + ASSERT_INT_EQ(addr_match_list("a", "*"), 0); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "*"), 1); + ASSERT_INT_EQ(addr_match_list(NULL, "*"), 0); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.1"), 1); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.2"), 0); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.1"), -1); + /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.2"), 1); */ + ASSERT_INT_EQ(addr_match_list("127.0.0.255", "127.0.0.0/24"), 1); + ASSERT_INT_EQ(addr_match_list("127.0.1.1", "127.0.0.0/24"), 0); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.0/24"), 1); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.1.0/24"), 0); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.0/24"), -1); + /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.1.0/24"), 1); */ + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,!127.0.0.1"), -1); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.1,10.0.0.1"), -1); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,127.0.0.2"), 0); + ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.2,10.0.0.1"), 0); + /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,!127.0.0.2"), 1); */ + /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.2,10.0.0.1"), 1); */ + TEST_DONE(); + +#define CHECK_FILTER(string,filter,expected) \ + do { \ + char *result = match_filter_denylist((string), (filter)); \ + ASSERT_STRING_EQ(result, expected); \ + free(result); \ + } while (0) + + TEST_START("match_filter_list"); + CHECK_FILTER("a,b,c", "", "a,b,c"); + CHECK_FILTER("a,b,c", "a", "b,c"); + CHECK_FILTER("a,b,c", "b", "a,c"); + CHECK_FILTER("a,b,c", "c", "a,b"); + CHECK_FILTER("a,b,c", "a,b", "c"); + CHECK_FILTER("a,b,c", "a,c", "b"); + CHECK_FILTER("a,b,c", "b,c", "a"); + CHECK_FILTER("a,b,c", "a,b,c", ""); + CHECK_FILTER("a,b,c", "b,c", "a"); + CHECK_FILTER("", "a,b,c", ""); + TEST_DONE(); +/* + * XXX TODO + * int match_host_and_ip(const char *, const char *, const char *); + * int match_user(const char *, const char *, const char *, const char *); + * char *match_list(const char *, const char *, u_int *); + * int addr_match_cidr_list(const char *, const char *); + */ +} diff --git a/aosp/external/openssh/regress/unittests/misc/Makefile b/aosp/external/openssh/regress/unittests/misc/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e8fe1a9531360341b87165ad88b8bea1af988db3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/Makefile @@ -0,0 +1,32 @@ +# $OpenBSD: Makefile,v 1.8 2022/02/04 07:53:44 dtucker Exp $ + +PROG=test_misc +SRCS=tests.c +SRCS+= test_convtime.c +SRCS+= test_expand.c +SRCS+= test_parse.c +SRCS+= test_argv.c +SRCS+= test_strdelim.c +SRCS+= test_hpdelim.c + +# From usr.bin/ssh/Makefile.inc +SRCS+= sshbuf.c +SRCS+= sshbuf-getput-basic.c +SRCS+= sshbuf-misc.c +SRCS+= ssherr.c +SRCS+= log.c +SRCS+= xmalloc.c +SRCS+= misc.c +SRCS+= match.c +SRCS+= addr.c +SRCS+= addrmatch.c + +# From usr.bin/ssh/sshd/Makefile +SRCS+= atomicio.c cleanup.c fatal.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include diff --git a/aosp/external/openssh/regress/unittests/misc/test_argv.c b/aosp/external/openssh/regress/unittests/misc/test_argv.c new file mode 100644 index 0000000000000000000000000000000000000000..682863e73ba91161ae5113e766c9359b90231ee1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/test_argv.c @@ -0,0 +1,186 @@ +/* $OpenBSD: test_argv.c,v 1.4 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for misc argv handling functions. + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" + +void test_argv(void); + +void +test_argv(void) +{ + char **av = NULL; + int ac = 0; + +#define RESET_ARGV() \ + do { \ + argv_free(av, ac); \ + av = NULL; \ + ac = -1; \ + } while (0) + + TEST_START("empty args"); + ASSERT_INT_EQ(argv_split("", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 0); + ASSERT_PTR_NE(av, NULL); + ASSERT_PTR_EQ(av[0], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split(" ", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 0); + ASSERT_PTR_NE(av, NULL); + ASSERT_PTR_EQ(av[0], NULL); + RESET_ARGV(); + TEST_DONE(); + + TEST_START("trivial args"); + ASSERT_INT_EQ(argv_split("leamas", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("smiley leamas", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 2); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley"); + ASSERT_STRING_EQ(av[1], "leamas"); + ASSERT_PTR_EQ(av[2], NULL); + RESET_ARGV(); + TEST_DONE(); + + TEST_START("quoted"); + ASSERT_INT_EQ(argv_split("\"smiley\"", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("leamas \" smiley \"", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 2); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas"); + ASSERT_STRING_EQ(av[1], " smiley "); + ASSERT_PTR_EQ(av[2], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("\"smiley leamas\"", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley leamas"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("smiley\" leamas\" liz", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 2); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley leamas"); + ASSERT_STRING_EQ(av[1], "liz"); + ASSERT_PTR_EQ(av[2], NULL); + RESET_ARGV(); + TEST_DONE(); + + TEST_START("escaped"); + ASSERT_INT_EQ(argv_split("\\\"smiley\\'", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "\"smiley'"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("'\\'smiley\\\"'", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "'smiley\""); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("smiley\\'s leamas\\'", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 2); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley's"); + ASSERT_STRING_EQ(av[1], "leamas'"); + ASSERT_PTR_EQ(av[2], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("leamas\\\\smiley", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas\\smiley"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("leamas\\\\ \\\\smiley", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 2); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas\\"); + ASSERT_STRING_EQ(av[1], "\\smiley"); + ASSERT_PTR_EQ(av[2], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("smiley\\ leamas", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley leamas"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + TEST_DONE(); + + TEST_START("quoted escaped"); + ASSERT_INT_EQ(argv_split("'smiley\\ leamas'", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley\\ leamas"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("\"smiley\\ leamas\"", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "smiley\\ leamas"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + TEST_DONE(); + + TEST_START("comments"); + ASSERT_INT_EQ(argv_split("# gold", &ac, &av, 0), 0); + ASSERT_INT_EQ(ac, 2); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "#"); + ASSERT_STRING_EQ(av[1], "gold"); + ASSERT_PTR_EQ(av[2], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("# gold", &ac, &av, 1), 0); + ASSERT_INT_EQ(ac, 0); + ASSERT_PTR_NE(av, NULL); + ASSERT_PTR_EQ(av[0], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("leamas#gold", &ac, &av, 1), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas#gold"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("\"leamas # gold\"", &ac, &av, 1), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas # gold"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + ASSERT_INT_EQ(argv_split("\"leamas\"#gold", &ac, &av, 1), 0); + ASSERT_INT_EQ(ac, 1); + ASSERT_PTR_NE(av, NULL); + ASSERT_STRING_EQ(av[0], "leamas#gold"); + ASSERT_PTR_EQ(av[1], NULL); + RESET_ARGV(); + TEST_DONE(); + + /* XXX test char *argv_assemble(int argc, char **argv) */ +} diff --git a/aosp/external/openssh/regress/unittests/misc/test_convtime.c b/aosp/external/openssh/regress/unittests/misc/test_convtime.c new file mode 100644 index 0000000000000000000000000000000000000000..ef6fd77deda5740532aefb03c77d8795b851bb22 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/test_convtime.c @@ -0,0 +1,59 @@ +/* $OpenBSD: test_convtime.c,v 1.2 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for misc time conversion functions. + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" + +void test_convtime(void); + +void +test_convtime(void) +{ + char buf[1024]; + + TEST_START("misc_convtime"); + ASSERT_INT_EQ(convtime("0"), 0); + ASSERT_INT_EQ(convtime("1"), 1); + ASSERT_INT_EQ(convtime("2s"), 2); + ASSERT_INT_EQ(convtime("3m"), 180); + ASSERT_INT_EQ(convtime("1m30"), 90); + ASSERT_INT_EQ(convtime("1m30s"), 90); + ASSERT_INT_EQ(convtime("1h1s"), 3601); + ASSERT_INT_EQ(convtime("1h30m"), 90 * 60); + ASSERT_INT_EQ(convtime("1d"), 24 * 60 * 60); + ASSERT_INT_EQ(convtime("1w"), 7 * 24 * 60 * 60); + ASSERT_INT_EQ(convtime("1w2d3h4m5"), 788645); + ASSERT_INT_EQ(convtime("1w2d3h4m5s"), 788645); + /* any negative number or error returns -1 */ + ASSERT_INT_EQ(convtime("-1"), -1); + ASSERT_INT_EQ(convtime(""), -1); + ASSERT_INT_EQ(convtime("trout"), -1); + ASSERT_INT_EQ(convtime("-77"), -1); + /* boundary conditions */ + snprintf(buf, sizeof buf, "%llu", (long long unsigned)INT_MAX); + ASSERT_INT_EQ(convtime(buf), INT_MAX); + snprintf(buf, sizeof buf, "%llu", (long long unsigned)INT_MAX + 1); + ASSERT_INT_EQ(convtime(buf), -1); + ASSERT_INT_EQ(convtime("3550w5d3h14m7s"), 2147483647); +#if INT_MAX == 2147483647 + ASSERT_INT_EQ(convtime("3550w5d3h14m8s"), -1); +#endif + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/misc/test_expand.c b/aosp/external/openssh/regress/unittests/misc/test_expand.c new file mode 100644 index 0000000000000000000000000000000000000000..6f2cd8adbedd342185ea95da03513b5b072984dd --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/test_expand.c @@ -0,0 +1,89 @@ +/* $OpenBSD: test_expand.c,v 1.3 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for misc string expansion functions. + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" + +void test_expand(void); + +void +test_expand(void) +{ + int parseerr; + char *ret; + + TEST_START("dollar_expand"); + ASSERT_INT_EQ(setenv("FOO", "bar", 1), 0); + ASSERT_INT_EQ(setenv("BAR", "baz", 1), 0); + (void)unsetenv("BAZ"); +#define ASSERT_DOLLAR_EQ(x, y) do { \ + char *str = dollar_expand(NULL, (x)); \ + ASSERT_STRING_EQ(str, (y)); \ + free(str); \ +} while(0) + ASSERT_DOLLAR_EQ("${FOO}", "bar"); + ASSERT_DOLLAR_EQ(" ${FOO}", " bar"); + ASSERT_DOLLAR_EQ("${FOO} ", "bar "); + ASSERT_DOLLAR_EQ(" ${FOO} ", " bar "); + ASSERT_DOLLAR_EQ("${FOO}${BAR}", "barbaz"); + ASSERT_DOLLAR_EQ(" ${FOO} ${BAR}", " bar baz"); + ASSERT_DOLLAR_EQ("${FOO}${BAR} ", "barbaz "); + ASSERT_DOLLAR_EQ(" ${FOO} ${BAR} ", " bar baz "); + ASSERT_DOLLAR_EQ("$", "$"); + ASSERT_DOLLAR_EQ(" $", " $"); + ASSERT_DOLLAR_EQ("$ ", "$ "); + + /* suppress error messages for error handing tests */ + log_init("test_misc", SYSLOG_LEVEL_QUIET, SYSLOG_FACILITY_AUTH, 1); + /* error checking, non existent variable */ + ret = dollar_expand(&parseerr, "a${BAZ}"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 0); + ret = dollar_expand(&parseerr, "${BAZ}b"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 0); + ret = dollar_expand(&parseerr, "a${BAZ}b"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 0); + /* invalid format */ + ret = dollar_expand(&parseerr, "${"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 1); + ret = dollar_expand(&parseerr, "${F"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 1); + ret = dollar_expand(&parseerr, "${FO"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 1); + /* empty variable name */ + ret = dollar_expand(&parseerr, "${}"); + ASSERT_PTR_EQ(ret, NULL); ASSERT_INT_EQ(parseerr, 1); + /* restore loglevel to default */ + log_init("test_misc", SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 1); + TEST_DONE(); + + TEST_START("percent_expand"); + ASSERT_STRING_EQ(percent_expand("%%", "%h", "foo", NULL), "%"); + ASSERT_STRING_EQ(percent_expand("%h", "h", "foo", NULL), "foo"); + ASSERT_STRING_EQ(percent_expand("%h ", "h", "foo", NULL), "foo "); + ASSERT_STRING_EQ(percent_expand(" %h", "h", "foo", NULL), " foo"); + ASSERT_STRING_EQ(percent_expand(" %h ", "h", "foo", NULL), " foo "); + ASSERT_STRING_EQ(percent_expand(" %a%b ", "a", "foo", "b", "bar", NULL), + " foobar "); + TEST_DONE(); + + TEST_START("percent_dollar_expand"); + ASSERT_STRING_EQ(percent_dollar_expand("%h${FOO}", "h", "foo", NULL), + "foobar"); + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/misc/test_hpdelim.c b/aosp/external/openssh/regress/unittests/misc/test_hpdelim.c new file mode 100644 index 0000000000000000000000000000000000000000..d423023dc3d2d9b41ecc40f1c30653d6f15e455c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/test_hpdelim.c @@ -0,0 +1,82 @@ +/* $OpenBSD: test_hpdelim.c,v 1.2 2022/02/06 22:58:33 dtucker Exp $ */ +/* + * Regress test for misc hpdelim() and co + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" +#include "xmalloc.h" + +void test_hpdelim(void); + +void +test_hpdelim(void) +{ + char *orig, *str, *cp, *port; + +#define START_STRING(x) orig = str = xstrdup(x) +#define DONE_STRING() free(orig) + + TEST_START("hpdelim host only"); + START_STRING("host"); + cp = hpdelim(&str); + ASSERT_STRING_EQ(cp, "host"); + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("hpdelim :port"); + START_STRING(":1234"); + cp = hpdelim(&str); + ASSERT_STRING_EQ(cp, ""); + ASSERT_PTR_NE(str, NULL); + port = hpdelim(&str); + ASSERT_STRING_EQ(port, "1234"); + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("hpdelim host:port"); + START_STRING("host:1234"); + cp = hpdelim(&str); + ASSERT_STRING_EQ(cp, "host"); + ASSERT_PTR_NE(str, NULL); + port = hpdelim(&str); + ASSERT_STRING_EQ(port, "1234"); + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("hpdelim [host]:port"); + START_STRING("[::1]:1234"); + cp = hpdelim(&str); + ASSERT_STRING_EQ(cp, "[::1]"); + ASSERT_PTR_NE(str, NULL); + port = hpdelim(&str); + ASSERT_STRING_EQ(port, "1234"); + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("hpdelim missing ] error"); + START_STRING("[::1:1234"); + cp = hpdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + +} diff --git a/aosp/external/openssh/regress/unittests/misc/test_parse.c b/aosp/external/openssh/regress/unittests/misc/test_parse.c new file mode 100644 index 0000000000000000000000000000000000000000..1f1ea31d149c251fb436f2ac5920448961f88ebf --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/test_parse.c @@ -0,0 +1,85 @@ +/* $OpenBSD: test_parse.c,v 1.2 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for misc user/host/URI parsing functions. + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" + +void test_parse(void); + +void +test_parse(void) +{ + int port; + char *user, *host, *path; + + TEST_START("misc_parse_user_host_path"); + ASSERT_INT_EQ(parse_user_host_path("someuser@some.host:some/path", + &user, &host, &path), 0); + ASSERT_STRING_EQ(user, "someuser"); + ASSERT_STRING_EQ(host, "some.host"); + ASSERT_STRING_EQ(path, "some/path"); + free(user); free(host); free(path); + TEST_DONE(); + + TEST_START("misc_parse_user_ipv4_path"); + ASSERT_INT_EQ(parse_user_host_path("someuser@1.22.33.144:some/path", + &user, &host, &path), 0); + ASSERT_STRING_EQ(user, "someuser"); + ASSERT_STRING_EQ(host, "1.22.33.144"); + ASSERT_STRING_EQ(path, "some/path"); + free(user); free(host); free(path); + TEST_DONE(); + + TEST_START("misc_parse_user_[ipv4]_path"); + ASSERT_INT_EQ(parse_user_host_path("someuser@[1.22.33.144]:some/path", + &user, &host, &path), 0); + ASSERT_STRING_EQ(user, "someuser"); + ASSERT_STRING_EQ(host, "1.22.33.144"); + ASSERT_STRING_EQ(path, "some/path"); + free(user); free(host); free(path); + TEST_DONE(); + + TEST_START("misc_parse_user_[ipv4]_nopath"); + ASSERT_INT_EQ(parse_user_host_path("someuser@[1.22.33.144]:", + &user, &host, &path), 0); + ASSERT_STRING_EQ(user, "someuser"); + ASSERT_STRING_EQ(host, "1.22.33.144"); + ASSERT_STRING_EQ(path, "."); + free(user); free(host); free(path); + TEST_DONE(); + + TEST_START("misc_parse_user_ipv6_path"); + ASSERT_INT_EQ(parse_user_host_path("someuser@[::1]:some/path", + &user, &host, &path), 0); + ASSERT_STRING_EQ(user, "someuser"); + ASSERT_STRING_EQ(host, "::1"); + ASSERT_STRING_EQ(path, "some/path"); + free(user); free(host); free(path); + TEST_DONE(); + + TEST_START("misc_parse_uri"); + ASSERT_INT_EQ(parse_uri("ssh", "ssh://someuser@some.host:22/some/path", + &user, &host, &port, &path), 0); + ASSERT_STRING_EQ(user, "someuser"); + ASSERT_STRING_EQ(host, "some.host"); + ASSERT_INT_EQ(port, 22); + ASSERT_STRING_EQ(path, "some/path"); + free(user); free(host); free(path); + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/misc/test_strdelim.c b/aosp/external/openssh/regress/unittests/misc/test_strdelim.c new file mode 100644 index 0000000000000000000000000000000000000000..f7bea4bfe8f7a54c9d5d1bc75534899ab4c91ebd --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/test_strdelim.c @@ -0,0 +1,201 @@ +/* $OpenBSD: test_strdelim.c,v 1.3 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for misc strdelim() and co + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" +#include "xmalloc.h" + +void test_strdelim(void); + +void +test_strdelim(void) +{ + char *orig, *str, *cp; + +#define START_STRING(x) orig = str = xstrdup(x) +#define DONE_STRING() free(orig) + + TEST_START("empty"); + START_STRING(""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); /* XXX arguable */ + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("whitespace"); + START_STRING(" "); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); /* XXX better as NULL */ + ASSERT_STRING_EQ(str, ""); + DONE_STRING(); + TEST_DONE(); + + TEST_START("trivial"); + START_STRING("blob"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob"); + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("trivial whitespace"); + START_STRING("blob "); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob"); + ASSERT_STRING_EQ(str, ""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); /* XXX better as NULL */ + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("multi"); + START_STRING("blob1 blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); + ASSERT_STRING_EQ(str, "blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); + ASSERT_PTR_EQ(str, NULL); + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("multi whitespace"); + START_STRING("blob1 blob2 "); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); + ASSERT_STRING_EQ(str, "blob2 "); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); /* XXX better as NULL */ + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("multi equals"); + START_STRING("blob1=blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); + ASSERT_STRING_EQ(str, "blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); + ASSERT_PTR_EQ(str, NULL); + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("multi too many equals"); + START_STRING("blob1==blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); /* XXX better returning NULL early */ + ASSERT_STRING_EQ(str, "=blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); + ASSERT_STRING_EQ(str, "blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); /* XXX should (but can't) reject */ + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("multi equals strdelimw"); + START_STRING("blob1=blob2"); + cp = strdelimw(&str); + ASSERT_STRING_EQ(cp, "blob1=blob2"); + ASSERT_PTR_EQ(str, NULL); + cp = strdelimw(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("quoted"); + START_STRING("\"blob\""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); /* XXX better as NULL */ + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("quoted multi"); + START_STRING("\"blob1\" blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); + ASSERT_STRING_EQ(str, "blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); + ASSERT_PTR_EQ(str, NULL); + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("quoted multi reverse"); + START_STRING("blob1 \"blob2\""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); + ASSERT_STRING_EQ(str, "\"blob2\""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); + ASSERT_STRING_EQ(str, ""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); /* XXX better as NULL */ + ASSERT_PTR_EQ(str, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("quoted multi middle"); + START_STRING("blob1 \"blob2\" blob3"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob1"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob2"); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob3"); + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("badquote"); + START_STRING("\"blob"); + cp = strdelim(&str); + ASSERT_PTR_EQ(cp, NULL); + DONE_STRING(); + TEST_DONE(); + + TEST_START("oops quote"); + START_STRING("\"blob\\\""); + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, "blob\\"); /* XXX wrong */ + cp = strdelim(&str); + ASSERT_STRING_EQ(cp, ""); + DONE_STRING(); + TEST_DONE(); + +} diff --git a/aosp/external/openssh/regress/unittests/misc/tests.c b/aosp/external/openssh/regress/unittests/misc/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..d52490e3b3ceb9dda61ff886a37378ed7ad6a80d --- /dev/null +++ b/aosp/external/openssh/regress/unittests/misc/tests.c @@ -0,0 +1,39 @@ +/* $OpenBSD: tests.c,v 1.9 2022/02/04 07:53:44 dtucker Exp $ */ +/* + * Regress test for misc helper functions. + * + * Placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" + +void test_parse(void); +void test_convtime(void); +void test_expand(void); +void test_argv(void); +void test_strdelim(void); +void test_hpdelim(void); + +void +tests(void) +{ + test_parse(); + test_convtime(); + test_expand(); + test_argv(); + test_strdelim(); + test_hpdelim(); +} diff --git a/aosp/external/openssh/regress/unittests/sshbuf/Makefile b/aosp/external/openssh/regress/unittests/sshbuf/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a8ddfaf7ed247ff93d1db4d7e0c407626c5182f7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/Makefile @@ -0,0 +1,22 @@ +# $OpenBSD: Makefile,v 1.10 2021/01/09 12:24:31 dtucker Exp $ + +# $OpenBSD: Makefile,v 1.8 2020/01/26 00:09:50 djm Exp $ + +PROG=test_sshbuf +SRCS=tests.c +SRCS+=test_sshbuf.c +SRCS+=test_sshbuf_getput_basic.c +SRCS+=test_sshbuf_getput_crypto.c +SRCS+=test_sshbuf_misc.c +SRCS+=test_sshbuf_fuzz.c +SRCS+=test_sshbuf_getput_fuzz.c +SRCS+=test_sshbuf_fixed.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c misc.c xmalloc.c log.c fatal.c ssherr.c cleanup.c +SRCS+=match.c addr.c addrmatch.c + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} ${UNITTEST_ARGS} + diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf.c new file mode 100644 index 0000000000000000000000000000000000000000..e22b390fe33d795cca65cae1697881367e9e4b80 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf.c @@ -0,0 +1,243 @@ +/* $OpenBSD: test_sshbuf.c,v 1.2 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#define SSHBUF_INTERNAL 1 /* access internals for testing */ +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "sshbuf.h" + +void sshbuf_tests(void); + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +void +sshbuf_tests(void) +{ + struct sshbuf *p1; + const u_char *cdp; + u_char *dp; + size_t sz; + int r; + + TEST_START("allocate sshbuf"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + TEST_DONE(); + + TEST_START("max size on fresh buffer"); + ASSERT_SIZE_T_GT(sshbuf_max_size(p1), 0); + TEST_DONE(); + + TEST_START("available on fresh buffer"); + ASSERT_SIZE_T_GT(sshbuf_avail(p1), 0); + TEST_DONE(); + + TEST_START("len = 0 on empty buffer"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + TEST_DONE(); + + TEST_START("set valid max size"); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 65536), 0); + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 65536); + TEST_DONE(); + + TEST_START("available on limited buffer"); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 65536); + TEST_DONE(); + + TEST_START("free"); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("consume on empty buffer"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_consume(p1, 0), 0); + ASSERT_INT_EQ(sshbuf_consume(p1, 1), SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("consume_end on empty buffer"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_consume_end(p1, 0), 0); + ASSERT_INT_EQ(sshbuf_consume_end(p1, 1), SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("reserve space"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + r = sshbuf_reserve(p1, 1, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + *dp = 0x11; + r = sshbuf_reserve(p1, 3, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + *dp++ = 0x22; + *dp++ = 0x33; + *dp++ = 0x44; + TEST_DONE(); + + TEST_START("sshbuf_len on filled buffer"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + TEST_DONE(); + + TEST_START("sshbuf_ptr on filled buffer"); + cdp = sshbuf_ptr(p1); + ASSERT_PTR_NE(cdp, NULL); + ASSERT_U8_EQ(cdp[0], 0x11); + ASSERT_U8_EQ(cdp[1], 0x22); + ASSERT_U8_EQ(cdp[2], 0x33); + ASSERT_U8_EQ(cdp[3], 0x44); + TEST_DONE(); + + TEST_START("consume on filled buffer"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + ASSERT_INT_EQ(sshbuf_consume(p1, 0), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + r = sshbuf_consume(p1, 64); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + ASSERT_INT_EQ(sshbuf_consume(p1, 1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 3); + cdp = sshbuf_ptr(p1); + ASSERT_PTR_NE(p1, NULL); + ASSERT_U8_EQ(cdp[0], 0x22); + ASSERT_INT_EQ(sshbuf_consume(p1, 2), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + cdp = sshbuf_ptr(p1); + ASSERT_PTR_NE(p1, NULL); + ASSERT_U8_EQ(cdp[0], 0x44); + r = sshbuf_consume(p1, 2); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + ASSERT_INT_EQ(sshbuf_consume(p1, 1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + r = sshbuf_consume(p1, 1); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("consume_end on filled buffer"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + r = sshbuf_reserve(p1, 4, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + *dp++ = 0x11; + *dp++ = 0x22; + *dp++ = 0x33; + *dp++ = 0x44; + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + r = sshbuf_consume_end(p1, 5); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + ASSERT_INT_EQ(sshbuf_consume_end(p1, 3), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + cdp = sshbuf_ptr(p1); + ASSERT_PTR_NE(cdp, NULL); + ASSERT_U8_EQ(*cdp, 0x11); + r = sshbuf_consume_end(p1, 2); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_consume_end(p1, 1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("fill limited buffer"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 1223), 0); + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 1223); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 1223); + r = sshbuf_reserve(p1, 1223, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + memset(dp, 0xd7, 1223); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1223); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 0); + r = sshbuf_reserve(p1, 1, &dp); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_PTR_EQ(dp, NULL); + TEST_DONE(); + + TEST_START("consume and force compaction"); + ASSERT_INT_EQ(sshbuf_consume(p1, 223), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1000); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 223); + r = sshbuf_reserve(p1, 224, &dp); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_PTR_EQ(dp, NULL); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1000); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 223); + r = sshbuf_reserve(p1, 223, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + memset(dp, 0x7d, 223); + cdp = sshbuf_ptr(p1); + ASSERT_PTR_NE(cdp, NULL); + ASSERT_MEM_FILLED_EQ(cdp, 0xd7, 1000); + ASSERT_MEM_FILLED_EQ(cdp + 1000, 0x7d, 223); + TEST_DONE(); + + TEST_START("resize full buffer"); + r = sshbuf_set_max_size(p1, 1000); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + sz = roundup(1223 + SSHBUF_SIZE_INC * 3, SSHBUF_SIZE_INC); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, sz), 0); + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), sz); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz - 1223); + ASSERT_INT_EQ(sshbuf_len(p1), 1223); + TEST_DONE(); + + /* NB. uses sshbuf internals */ + TEST_START("alloc chunking"); + r = sshbuf_reserve(p1, 1, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + *dp = 0xff; + cdp = sshbuf_ptr(p1); + ASSERT_PTR_NE(cdp, NULL); + ASSERT_MEM_FILLED_EQ(cdp, 0xd7, 1000); + ASSERT_MEM_FILLED_EQ(cdp + 1000, 0x7d, 223); + ASSERT_MEM_FILLED_EQ(cdp + 1223, 0xff, 1); + ASSERT_SIZE_T_EQ(sshbuf_alloc(p1) % SSHBUF_SIZE_INC, 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("reset buffer"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 1223), 0); + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 1223); + r = sshbuf_reserve(p1, 1223, &dp); + ASSERT_INT_EQ(r, 0); + ASSERT_PTR_NE(dp, NULL); + memset(dp, 0xd7, 1223); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1223); + sshbuf_reset(p1); + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 1223); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 1223); + sshbuf_free(p1); + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_fixed.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_fixed.c new file mode 100644 index 0000000000000000000000000000000000000000..dff77f0421527997688fdc5540c135db6b458bc1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_fixed.c @@ -0,0 +1,125 @@ +/* $OpenBSD: test_sshbuf_fixed.c,v 1.2 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#define SSHBUF_INTERNAL 1 /* access internals for testing */ +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "sshbuf.h" +#include "ssherr.h" + +void sshbuf_fixed(void); + +const u_char test_buf[] = "\x01\x12\x34\x56\x78\x00\x00\x00\x05hello"; + +void +sshbuf_fixed(void) +{ + struct sshbuf *p1, *p2, *p3; + u_char c; + char *s; + u_int i; + size_t l; + + TEST_START("sshbuf_from"); + p1 = sshbuf_from(test_buf, sizeof(test_buf)); + ASSERT_PTR_NE(p1, NULL); + ASSERT_PTR_EQ(sshbuf_mutable_ptr(p1), NULL); + ASSERT_INT_EQ(sshbuf_check_reserve(p1, 1), SSH_ERR_BUFFER_READ_ONLY); + ASSERT_INT_EQ(sshbuf_reserve(p1, 1, NULL), SSH_ERR_BUFFER_READ_ONLY); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 200), SSH_ERR_BUFFER_READ_ONLY); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), SSH_ERR_BUFFER_READ_ONLY); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 0); + ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_from data"); + p1 = sshbuf_from(test_buf, sizeof(test_buf) - 1); + ASSERT_PTR_NE(p1, NULL); + ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf); + ASSERT_INT_EQ(sshbuf_get_u8(p1, &c), 0); + ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf + 1); + ASSERT_U8_EQ(c, 1); + ASSERT_INT_EQ(sshbuf_get_u32(p1, &i), 0); + ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf + 5); + ASSERT_U32_EQ(i, 0x12345678); + ASSERT_INT_EQ(sshbuf_get_cstring(p1, &s, &l), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + ASSERT_STRING_EQ(s, "hello"); + ASSERT_SIZE_T_EQ(l, 5); + sshbuf_free(p1); + free(s); + TEST_DONE(); + + TEST_START("sshbuf_fromb "); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_U_INT_EQ(sshbuf_refcount(p1), 1); + ASSERT_PTR_EQ(sshbuf_parent(p1), NULL); + ASSERT_INT_EQ(sshbuf_put(p1, test_buf, sizeof(test_buf) - 1), 0); + p2 = sshbuf_fromb(p1); + ASSERT_PTR_NE(p2, NULL); + ASSERT_U_INT_EQ(sshbuf_refcount(p1), 2); + ASSERT_PTR_EQ(sshbuf_parent(p1), NULL); + ASSERT_PTR_EQ(sshbuf_parent(p2), p1); + ASSERT_PTR_EQ(sshbuf_ptr(p2), sshbuf_ptr(p1)); + ASSERT_PTR_NE(sshbuf_ptr(p1), NULL); + ASSERT_PTR_NE(sshbuf_ptr(p2), NULL); + ASSERT_PTR_EQ(sshbuf_mutable_ptr(p1), NULL); + ASSERT_PTR_EQ(sshbuf_mutable_ptr(p2), NULL); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sshbuf_len(p2)); + ASSERT_INT_EQ(sshbuf_get_u8(p2, &c), 0); + ASSERT_PTR_EQ(sshbuf_ptr(p2), sshbuf_ptr(p1) + 1); + ASSERT_U8_EQ(c, 1); + ASSERT_INT_EQ(sshbuf_get_u32(p2, &i), 0); + ASSERT_PTR_EQ(sshbuf_ptr(p2), sshbuf_ptr(p1) + 5); + ASSERT_U32_EQ(i, 0x12345678); + ASSERT_INT_EQ(sshbuf_get_cstring(p2, &s, &l), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p2), 0); + ASSERT_STRING_EQ(s, "hello"); + ASSERT_SIZE_T_EQ(l, 5); + sshbuf_free(p1); + ASSERT_U_INT_EQ(sshbuf_refcount(p1), 1); + sshbuf_free(p2); + free(s); + TEST_DONE(); + + TEST_START("sshbuf_froms"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x01), 0); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), 0); + ASSERT_INT_EQ(sshbuf_put_cstring(p1, "hello"), 0); + p2 = sshbuf_new(); + ASSERT_PTR_NE(p2, NULL); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(test_buf) - 1); + ASSERT_INT_EQ(sshbuf_put_stringb(p2, p1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p2), sizeof(test_buf) + 4 - 1); + ASSERT_INT_EQ(sshbuf_froms(p2, &p3), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p2), 0); + ASSERT_PTR_NE(p3, NULL); + ASSERT_PTR_NE(sshbuf_ptr(p3), NULL); + ASSERT_SIZE_T_EQ(sshbuf_len(p3), sizeof(test_buf) - 1); + ASSERT_MEM_EQ(sshbuf_ptr(p3), test_buf, sizeof(test_buf) - 1); + sshbuf_free(p3); + ASSERT_INT_EQ(sshbuf_put_stringb(p2, p1), 0); + ASSERT_INT_EQ(sshbuf_consume_end(p2, 1), 0); + ASSERT_INT_EQ(sshbuf_froms(p2, &p3), SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_PTR_EQ(p3, NULL); + sshbuf_free(p2); + sshbuf_free(p1); +} diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_fuzz.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_fuzz.c new file mode 100644 index 0000000000000000000000000000000000000000..c0b809dcde1b183385638772019bd1d14659e886 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_fuzz.c @@ -0,0 +1,131 @@ +/* $OpenBSD: test_sshbuf_fuzz.c,v 1.4 2021/12/18 06:53:59 anton Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "sshbuf.h" + +#define NUM_FUZZ_TESTS (1 << 18) + +void sshbuf_fuzz_tests(void); + +void +sshbuf_fuzz_tests(void) +{ + struct sshbuf *p1; + u_char *dp; + size_t sz, sz2, i, ntests = NUM_FUZZ_TESTS; + u_int32_t r; + int ret; + + if (test_is_fast()) + ntests >>= 2; + if (test_is_slow()) + ntests <<= 2; + + /* NB. uses sshbuf internals */ + TEST_START("fuzz alloc/dealloc"); + p1 = sshbuf_new(); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 16 * 1024), 0); + ASSERT_PTR_NE(p1, NULL); + ASSERT_PTR_NE(sshbuf_ptr(p1), NULL); + ASSERT_MEM_ZERO_NE(sshbuf_ptr(p1), sshbuf_len(p1)); + for (i = 0; i < ntests; i++) { + r = arc4random_uniform(10); + if (r == 0) { + /* 10% chance: small reserve */ + r = arc4random_uniform(10); + fuzz_reserve: + sz = sshbuf_avail(p1); + sz2 = sshbuf_len(p1); + ret = sshbuf_reserve(p1, r, &dp); + if (ret < 0) { + ASSERT_PTR_EQ(dp, NULL); + ASSERT_SIZE_T_LT(sz, r); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2); + } else { + ASSERT_PTR_NE(dp, NULL); + ASSERT_SIZE_T_GE(sz, r); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz - r); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2 + r); + memset(dp, arc4random_uniform(255) + 1, r); + } + } else if (r < 3) { + /* 20% chance: big reserve */ + r = arc4random_uniform(8 * 1024); + goto fuzz_reserve; + } else if (r == 3) { + /* 10% chance: small consume */ + r = arc4random_uniform(10); + fuzz_consume: + sz = sshbuf_avail(p1); + sz2 = sshbuf_len(p1); + /* 50% change consume from end, otherwise start */ + ret = ((arc4random() & 1) ? + sshbuf_consume : sshbuf_consume_end)(p1, r); + if (ret < 0) { + ASSERT_SIZE_T_LT(sz2, r); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2); + } else { + ASSERT_SIZE_T_GE(sz2, r); + ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz + r); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2 - r); + } + } else if (r < 8) { + /* 40% chance: big consume */ + r = arc4random_uniform(2 * 1024); + goto fuzz_consume; + } else if (r == 8) { + /* 10% chance: reset max size */ + r = arc4random_uniform(16 * 1024); + sz = sshbuf_max_size(p1); + if (sshbuf_set_max_size(p1, r) < 0) + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), sz); + else + ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), r); + } else { + if (arc4random_uniform(8192) == 0) { + /* tiny chance: new buffer */ + ASSERT_PTR_NE(sshbuf_ptr(p1), NULL); + ASSERT_MEM_ZERO_NE(sshbuf_ptr(p1), sshbuf_len(p1)); + sshbuf_free(p1); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, + 16 * 1024), 0); + } else { + /* Almost 10%: giant reserve */ + /* use arc4random_buf for r > 2^32 on 64 bit */ + arc4random_buf(&r, sizeof(r)); + while (r < SSHBUF_SIZE_MAX / 2) { + r <<= 1; + r |= arc4random() & 1; + } + goto fuzz_reserve; + } + } + ASSERT_PTR_NE(sshbuf_ptr(p1), NULL); + ASSERT_SIZE_T_LE(sshbuf_max_size(p1), 16 * 1024); + } + ASSERT_PTR_NE(sshbuf_ptr(p1), NULL); + ASSERT_MEM_ZERO_NE(sshbuf_ptr(p1), sshbuf_len(p1)); + sshbuf_free(p1); + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_basic.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_basic.c new file mode 100644 index 0000000000000000000000000000000000000000..3da413edd35c8d2fadcf2c9b068f0a8bca7595c7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_basic.c @@ -0,0 +1,712 @@ +/* $OpenBSD: test_sshbuf_getput_basic.c,v 1.3 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#include "../test_helper/test_helper.h" +#include "ssherr.h" +#include "sshbuf.h" + +void sshbuf_getput_basic_tests(void); + +void +sshbuf_getput_basic_tests(void) +{ + struct sshbuf *p1, *p2; + const u_char *cd; + u_char *d, d2[32], x[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x00, 0x99 + }; + u_int64_t v64; + u_int32_t v32; + u_int16_t v16; + u_char v8; + size_t s; + char *s2; + int r; + u_char bn1[] = { 0x00, 0x00, 0x00 }; + u_char bn2[] = { 0x00, 0x00, 0x01, 0x02 }; + u_char bn3[] = { 0x00, 0x80, 0x09 }; + u_char bn_exp1[] = { 0x00, 0x00, 0x00, 0x00 }; + u_char bn_exp2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 0x02 }; + u_char bn_exp3[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x09 }; + + TEST_START("PEEK_U64"); + ASSERT_U64_EQ(PEEK_U64(x), 0x1122334455667788ULL); + TEST_DONE(); + + TEST_START("PEEK_U32"); + ASSERT_U32_EQ(PEEK_U32(x), 0x11223344); + TEST_DONE(); + + TEST_START("PEEK_U16"); + ASSERT_U16_EQ(PEEK_U16(x), 0x1122); + TEST_DONE(); + + TEST_START("POKE_U64"); + bzero(d2, sizeof(d2)); + POKE_U64(d2, 0x1122334455667788ULL); + ASSERT_MEM_EQ(d2, x, 8); + TEST_DONE(); + + TEST_START("POKE_U32"); + bzero(d2, sizeof(d2)); + POKE_U32(d2, 0x11223344); + ASSERT_MEM_EQ(d2, x, 4); + TEST_DONE(); + + TEST_START("POKE_U16"); + bzero(d2, sizeof(d2)); + POKE_U16(d2, 0x1122); + ASSERT_MEM_EQ(d2, x, 2); + TEST_DONE(); + + TEST_START("sshbuf_put"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, 5), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 5); + cd = sshbuf_ptr(p1); + ASSERT_PTR_NE(cd, NULL); + ASSERT_U8_EQ(cd[0], 0x11); + ASSERT_U8_EQ(cd[1], 0x22); + ASSERT_U8_EQ(cd[2], 0x33); + ASSERT_U8_EQ(cd[3], 0x44); + ASSERT_U8_EQ(cd[4], 0x55); + TEST_DONE(); + + TEST_START("sshbuf_get"); + ASSERT_INT_EQ(sshbuf_get(p1, d2, 4), 0); + ASSERT_MEM_EQ(d2, x, 4); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + ASSERT_U8_EQ(*(sshbuf_ptr(p1)), 0x55); + TEST_DONE(); + + TEST_START("sshbuf_get truncated"); + r = sshbuf_get(p1, d2, 4); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + ASSERT_U8_EQ(*(sshbuf_ptr(p1)), 0x55); + TEST_DONE(); + + TEST_START("sshbuf_put truncated"); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 4), 0); + r = sshbuf_put(p1, x, 5); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_u64"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, 10), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 10); + ASSERT_INT_EQ(sshbuf_get_u64(p1, &v64), 0); + ASSERT_U64_EQ(v64, 0x1122334455667788ULL); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + TEST_DONE(); + + TEST_START("sshbuf_get_u64 truncated"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + r = sshbuf_get_u64(p1, &v64); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_u32"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, 10), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 10); + ASSERT_INT_EQ(sshbuf_get_u32(p1, &v32), 0); + ASSERT_U32_EQ(v32, 0x11223344); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 6); + ASSERT_INT_EQ(sshbuf_get_u32(p1, &v32), 0); + ASSERT_U32_EQ(v32, 0x55667788); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + TEST_DONE(); + + TEST_START("sshbuf_get_u32 truncated"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + r = sshbuf_get_u32(p1, &v32); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_u16"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, 9), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 9); + ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0); + ASSERT_U16_EQ(v16, 0x1122); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 7); + ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0); + ASSERT_U16_EQ(v16, 0x3344); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 5); + ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0); + ASSERT_U16_EQ(v16, 0x5566); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 3); + ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0); + ASSERT_U16_EQ(v16, 0x7788); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + TEST_DONE(); + + TEST_START("sshbuf_get_u16 truncated"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + r = sshbuf_get_u16(p1, &v16); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_u8"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, 2), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + ASSERT_INT_EQ(sshbuf_get_u8(p1, &v8), 0); + ASSERT_U8_EQ(v8, 0x11); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + ASSERT_INT_EQ(sshbuf_get_u8(p1, &v8), 0); + ASSERT_U8_EQ(v8, 0x22); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + TEST_DONE(); + + TEST_START("sshbuf_get_u8 truncated"); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + r = sshbuf_get_u8(p1, &v8); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u64"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u64(p1, 0x1122334455667788ULL), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 8); + ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 8); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u64 exact"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 8), 0); + ASSERT_INT_EQ(sshbuf_put_u64(p1, 0x1122334455667788ULL), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 8); + ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 8); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u64 limited"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 7), 0); + r = sshbuf_put_u64(p1, 0x1122334455667788ULL); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u32"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x11223344), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 4); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u32 exact"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 4), 0); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x11223344), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 4); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u32 limited"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 3), 0); + r = sshbuf_put_u32(p1, 0x11223344); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u16"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u16(p1, 0x1122), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u16"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 2), 0); + ASSERT_INT_EQ(sshbuf_put_u16(p1, 0x1122), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_u16 limited"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, 1), 0); + r = sshbuf_put_u16(p1, 0x1122); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_string"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4 + 4); + ASSERT_INT_EQ(sshbuf_get_string(p1, &d, &s), 0); + ASSERT_SIZE_T_EQ(s, sizeof(x)); + ASSERT_MEM_EQ(d, x, sizeof(x)); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + free(d); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_string exact"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(x) + 4), 0); + ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + ASSERT_INT_EQ(sshbuf_get_string(p1, &d, &s), 0); + ASSERT_SIZE_T_EQ(s, sizeof(x)); + ASSERT_MEM_EQ(d, x, sizeof(x)); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + free(d); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_string truncated"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + ASSERT_INT_EQ(sshbuf_consume_end(p1, 1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 3); + r = sshbuf_get_string(p1, &d, &s); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 3); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_string giant"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0xffffffff), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + r = sshbuf_get_string(p1, &d, &s); + ASSERT_INT_EQ(r, SSH_ERR_STRING_TOO_LARGE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_cstring giant"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0xffffffff), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + r = sshbuf_get_cstring(p1, &s2, &s); + ASSERT_INT_EQ(r, SSH_ERR_STRING_TOO_LARGE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_cstring embedded \\0"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + r = sshbuf_get_cstring(p1, &s2, NULL); + ASSERT_INT_EQ(r, SSH_ERR_INVALID_FORMAT); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_cstring trailing \\0"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x) - 1), 0); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x) - 1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4 - 1); + ASSERT_INT_EQ(sshbuf_get_cstring(p1, &s2, &s), 0); + ASSERT_SIZE_T_EQ(s, sizeof(x) - 1); + ASSERT_MEM_EQ(s2, x, s); + free(s2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_string"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_string(p1, x, sizeof(x)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4); + ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), sizeof(x)); + ASSERT_MEM_EQ(sshbuf_ptr(p1) + 4, x, sizeof(x)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_string limited"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(x) + 4 - 1), 0); + r = sshbuf_put_string(p1, x, sizeof(x)); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_string giant"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + r = sshbuf_put_string(p1, (void *)0x01, 0xfffffffc); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_putf"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + r = sshbuf_putf(p1, "%s %d %x", "hello", 23, 0x5f); + ASSERT_INT_EQ(r, 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 11); + ASSERT_MEM_EQ(sshbuf_ptr(p1), "hello 23 5f", 11); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_putb"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + p2 = sshbuf_new(); + ASSERT_PTR_NE(p2, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, "blahblahblah", 12), 0); + ASSERT_INT_EQ(sshbuf_putb(p2, p1), 0); + sshbuf_free(p1); + ASSERT_SIZE_T_EQ(sshbuf_len(p2), 12); + ASSERT_MEM_EQ(sshbuf_ptr(p2), "blahblahblah", 12); + sshbuf_free(p2); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2_bytes empty buf"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, NULL, 0), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp1)); + ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp1, sizeof(bn_exp1)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2_bytes all zeroes"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn1, sizeof(bn1)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp1)); + ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp1, sizeof(bn_exp1)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2_bytes simple"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn2+2, sizeof(bn2)-2), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp2)); + ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp2, sizeof(bn_exp2)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2_bytes leading zero"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn2, sizeof(bn2)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp2)); + ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp2, sizeof(bn_exp2)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2_bytes neg"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn3+1, sizeof(bn3)-1), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp3)); + ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp3, sizeof(bn_exp3)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2_bytes neg and leading zero"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn3, sizeof(bn3)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp3)); + ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp3, sizeof(bn_exp3)); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_peek_u64"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_peek_u64(p1, 0, &v64), 0); + ASSERT_U64_EQ(v64, 0x1122334455667788ULL); + ASSERT_INT_EQ(sshbuf_peek_u64(p1, 2, &v64), 0); + ASSERT_U64_EQ(v64, 0x3344556677880099ULL); + ASSERT_INT_EQ(sshbuf_peek_u64(p1, 3, &v64), SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u64(p1, sizeof(x), &v64), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u64(p1, 1000, &v64), + SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_peek_u32"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_peek_u32(p1, 0, &v32), 0); + ASSERT_U32_EQ(v32, 0x11223344); + ASSERT_INT_EQ(sshbuf_peek_u32(p1, 6, &v32), 0); + ASSERT_U32_EQ(v32, 0x77880099); + ASSERT_INT_EQ(sshbuf_peek_u32(p1, 7, &v32), SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u32(p1, sizeof(x), &v32), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u32(p1, 1000, &v32), + SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_peek_u16"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_peek_u16(p1, 0, &v16), 0); + ASSERT_U16_EQ(v16, 0x1122); + ASSERT_INT_EQ(sshbuf_peek_u16(p1, 8, &v16), 0); + ASSERT_U16_EQ(v16, 0x99); + ASSERT_INT_EQ(sshbuf_peek_u16(p1, 9, &v16), SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u16(p1, sizeof(x), &v16), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u16(p1, 1000, &v16), + SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_peek_u8"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0); + ASSERT_INT_EQ(sshbuf_peek_u8(p1, 0, &v8), 0); + ASSERT_U8_EQ(v8, 0x11); + ASSERT_INT_EQ(sshbuf_peek_u8(p1, 9, &v8), 0); + ASSERT_U8_EQ(v8, 0x99); + ASSERT_INT_EQ(sshbuf_peek_u8(p1, sizeof(x), &v8), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_peek_u8(p1, 1000, &v8), + SSH_ERR_MESSAGE_INCOMPLETE); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_poke_u64"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke at start of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u64(p1, 0, 0xa1b2c3d4e5f60718ULL), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "a1b2c3d4e5f607180000"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke aligned with end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u64(p1, 2, 0xa1b2c3d4e5f60718ULL), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "0000a1b2c3d4e5f60718"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke past end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u64(p1, 3, 0xa1b2c3d4e5f60718ULL), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u64(p1, 10, 0xa1b2c3d4e5f60718ULL), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u64(p1, 1000, 0xa1b2c3d4e5f60718ULL), + SSH_ERR_NO_BUFFER_SPACE); + /* ensure failed pokes do not modify buffer */ + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "00000000000000000000"); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_poke_u32"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke at start of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u32(p1, 0, 0xa1b2c3d4), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "a1b2c3d4000000000000"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke aligned with end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u32(p1, 6, 0xa1b2c3d4), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "000000000000a1b2c3d4"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke past end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u32(p1, 7, 0xa1b2c3d4), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u32(p1, 10, 0xa1b2c3d4), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u32(p1, 1000, 0xa1b2c3d4), + SSH_ERR_NO_BUFFER_SPACE); + /* ensure failed pokes do not modify buffer */ + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "00000000000000000000"); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_poke_u16"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke at start of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u16(p1, 0, 0xa1b2), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "a1b20000000000000000"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke aligned with end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u16(p1, 8, 0xa1b2), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "0000000000000000a1b2"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke past end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u16(p1, 9, 0xa1b2), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u16(p1, 10, 0xa1b2), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u16(p1, 1000, 0xa1b2), + SSH_ERR_NO_BUFFER_SPACE); + /* ensure failed pokes do not modify buffer */ + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "00000000000000000000"); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_poke_u8"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke at start of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u8(p1, 0, 0xa1), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "a1000000000000000000"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke aligned with end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u8(p1, 9, 0xa1), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "000000000000000000a1"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke past end of buffer */ + ASSERT_INT_EQ(sshbuf_poke_u8(p1, 10, 0xa1), SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke_u8(p1, 1000, 0xa1), SSH_ERR_NO_BUFFER_SPACE); + /* ensure failed pokes do not modify buffer */ + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "00000000000000000000"); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_poke"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke at start of buffer */ + ASSERT_INT_EQ(sshbuf_poke(p1, 0, "hello!", 6), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "68656c6c6f2100000000"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke aligned with end of buffer */ + ASSERT_INT_EQ(sshbuf_poke(p1, 4, "hello!", 6), 0); + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "0000000068656c6c6f21"); + free(s2); + sshbuf_reset(p1); + ASSERT_INT_EQ(sshbuf_reserve(p1, 10, NULL), 0); + /* poke past end of buffer */ + ASSERT_INT_EQ(sshbuf_poke(p1, 7, "hello!", 6), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke(p1, 10, "hello!", 6), + SSH_ERR_NO_BUFFER_SPACE); + ASSERT_INT_EQ(sshbuf_poke(p1, 1000, "hello!", 6), + SSH_ERR_NO_BUFFER_SPACE); + /* ensure failed pokes do not modify buffer */ + s2 = sshbuf_dtob16(p1); + ASSERT_PTR_NE(s2, NULL); + ASSERT_STRING_EQ(s2, "00000000000000000000"); + sshbuf_free(p1); + TEST_DONE(); +} diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_crypto.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_crypto.c new file mode 100644 index 0000000000000000000000000000000000000000..e3620e97fe991fbeee0519ab80547efde98522cb --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_crypto.c @@ -0,0 +1,280 @@ +/* $OpenBSD: test_sshbuf_getput_crypto.c,v 1.3 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#include +#include +#ifdef OPENSSL_HAS_NISTP256 +# include +#endif + +#include "../test_helper/test_helper.h" +#include "ssherr.h" +#include "sshbuf.h" + +void sshbuf_getput_crypto_tests(void); + +void +sshbuf_getput_crypto_tests(void) +{ + struct sshbuf *p1; + BIGNUM *bn, *bn2; + const char *hexbn1 = "0102030405060708090a0b0c0d0e0f10"; + /* This one has MSB set to test bignum2 encoding negative-avoidance */ + const char *hexbn2 = "f0e0d0c0b0a0908070605040302010007fff11"; + u_char expbn1[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + }; + u_char expbn2[] = { + 0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80, + 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00, + 0x7f, 0xff, 0x11 + }; +#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) + const u_char *d; + size_t s; + BIGNUM *bn_x, *bn_y; + int ec256_nid = NID_X9_62_prime256v1; + char *ec256_x = "0C828004839D0106AA59575216191357" + "34B451459DADB586677EF9DF55784999"; + char *ec256_y = "4D196B50F0B4E94B3C73E3A9D4CD9DF2" + "C8F9A35E42BDD047550F69D80EC23CD4"; + u_char expec256[] = { + 0x04, + 0x0c, 0x82, 0x80, 0x04, 0x83, 0x9d, 0x01, 0x06, + 0xaa, 0x59, 0x57, 0x52, 0x16, 0x19, 0x13, 0x57, + 0x34, 0xb4, 0x51, 0x45, 0x9d, 0xad, 0xb5, 0x86, + 0x67, 0x7e, 0xf9, 0xdf, 0x55, 0x78, 0x49, 0x99, + 0x4d, 0x19, 0x6b, 0x50, 0xf0, 0xb4, 0xe9, 0x4b, + 0x3c, 0x73, 0xe3, 0xa9, 0xd4, 0xcd, 0x9d, 0xf2, + 0xc8, 0xf9, 0xa3, 0x5e, 0x42, 0xbd, 0xd0, 0x47, + 0x55, 0x0f, 0x69, 0xd8, 0x0e, 0xc2, 0x3c, 0xd4 + }; + EC_KEY *eck; + EC_POINT *ecp; +#endif + int r; + +#define MKBN(b, bnn) \ + do { \ + bnn = NULL; \ + ASSERT_INT_GT(BN_hex2bn(&bnn, b), 0); \ + } while (0) + + TEST_START("sshbuf_put_bignum2"); + MKBN(hexbn1, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2(p1, bn), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn1) + 4); + ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), (u_int32_t)BN_num_bytes(bn)); + ASSERT_MEM_EQ(sshbuf_ptr(p1) + 4, expbn1, sizeof(expbn1)); + BN_free(bn); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2 limited"); + MKBN(hexbn1, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(expbn1) + 3), 0); + r = sshbuf_put_bignum2(p1, bn); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + BN_free(bn); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2 bn2"); + MKBN(hexbn2, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_bignum2(p1, bn), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 4 + 1); /* MSB */ + ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), (u_int32_t)BN_num_bytes(bn) + 1); + ASSERT_U8_EQ(*(sshbuf_ptr(p1) + 4), 0x00); + ASSERT_MEM_EQ(sshbuf_ptr(p1) + 5, expbn2, sizeof(expbn2)); + BN_free(bn); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_put_bignum2 bn2 limited"); + MKBN(hexbn2, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(expbn2) + 3), 0); + r = sshbuf_put_bignum2(p1, bn); + ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0); + BN_free(bn); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_bignum2"); + MKBN(hexbn1, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, expbn1, sizeof(expbn1)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4 + sizeof(expbn1)); + ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xd00f), 0); + bn2 = NULL; + ASSERT_INT_EQ(sshbuf_get_bignum2(p1, &bn2), 0); + ASSERT_BIGNUM_EQ(bn, bn2); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + BN_free(bn); + BN_free(bn2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_bignum2 truncated"); + MKBN(hexbn1, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, expbn1, sizeof(expbn1) - 1), 0); + bn2 = NULL; + r = sshbuf_get_bignum2(p1, &bn2); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn1) + 3); + BN_free(bn); + BN_free(bn2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_bignum2 giant"); + MKBN(hexbn1, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 65536), 0); + ASSERT_INT_EQ(sshbuf_reserve(p1, 65536, NULL), 0); + bn2 = NULL; + r = sshbuf_get_bignum2(p1, &bn2); + ASSERT_INT_EQ(r, SSH_ERR_BIGNUM_TOO_LARGE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 65536 + 4); + BN_free(bn); + BN_free(bn2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_bignum2 bn2"); + MKBN(hexbn2, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn) + 1), 0); /* MSB */ + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x00), 0); + ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4 + 1 + sizeof(expbn2)); + ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xd00f), 0); + bn2 = NULL; + ASSERT_INT_EQ(sshbuf_get_bignum2(p1, &bn2), 0); + ASSERT_BIGNUM_EQ(bn, bn2); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + BN_free(bn); + BN_free(bn2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_bignum2 bn2 truncated"); + MKBN(hexbn2, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn) + 1), 0); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x00), 0); + ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2) - 1), 0); + bn2 = NULL; + r = sshbuf_get_bignum2(p1, &bn2); + ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 1 + 4 - 1); + BN_free(bn); + BN_free(bn2); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_get_bignum2 bn2 negative"); + MKBN(hexbn2, bn); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn)), 0); + ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2)), 0); + bn2 = NULL; + r = sshbuf_get_bignum2(p1, &bn2); + ASSERT_INT_EQ(r, SSH_ERR_BIGNUM_IS_NEGATIVE); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 4); + BN_free(bn); + BN_free(bn2); + sshbuf_free(p1); + TEST_DONE(); + +#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) + TEST_START("sshbuf_put_ec"); + eck = EC_KEY_new_by_curve_name(ec256_nid); + ASSERT_PTR_NE(eck, NULL); + ecp = EC_POINT_new(EC_KEY_get0_group(eck)); + ASSERT_PTR_NE(ecp, NULL); + MKBN(ec256_x, bn_x); + MKBN(ec256_y, bn_y); + ASSERT_INT_EQ(EC_POINT_set_affine_coordinates_GFp( + EC_KEY_get0_group(eck), ecp, bn_x, bn_y, NULL), 1); + ASSERT_INT_EQ(EC_KEY_set_public_key(eck, ecp), 1); + BN_free(bn_x); + BN_free(bn_y); + EC_POINT_free(ecp); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_eckey(p1, eck), 0); + ASSERT_INT_EQ(sshbuf_get_string_direct(p1, &d, &s), 0); + ASSERT_SIZE_T_EQ(s, sizeof(expec256)); + ASSERT_MEM_EQ(d, expec256, sizeof(expec256)); + sshbuf_free(p1); + EC_KEY_free(eck); + TEST_DONE(); + + TEST_START("sshbuf_get_ec"); + eck = EC_KEY_new_by_curve_name(ec256_nid); + ASSERT_PTR_NE(eck, NULL); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_string(p1, expec256, sizeof(expec256)), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expec256) + 4); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x00), 0); + ASSERT_INT_EQ(sshbuf_get_eckey(p1, eck), 0); + bn_x = BN_new(); + bn_y = BN_new(); + ASSERT_PTR_NE(bn_x, NULL); + ASSERT_PTR_NE(bn_y, NULL); + ASSERT_INT_EQ(EC_POINT_get_affine_coordinates_GFp( + EC_KEY_get0_group(eck), EC_KEY_get0_public_key(eck), + bn_x, bn_y, NULL), 1); + MKBN(ec256_x, bn); + MKBN(ec256_y, bn2); + ASSERT_INT_EQ(BN_cmp(bn_x, bn), 0); + ASSERT_INT_EQ(BN_cmp(bn_y, bn2), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + sshbuf_free(p1); + EC_KEY_free(eck); + BN_free(bn_x); + BN_free(bn_y); + BN_free(bn); + BN_free(bn2); + TEST_DONE(); +#endif +} + +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c new file mode 100644 index 0000000000000000000000000000000000000000..3b4895895ef1c52c3021401ca23ce19f49c0b952 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c @@ -0,0 +1,132 @@ +/* $OpenBSD: test_sshbuf_getput_fuzz.c,v 1.5 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#ifdef OPENSSL_HAS_NISTP256 +# include +#endif +#endif + +#include "../test_helper/test_helper.h" +#include "ssherr.h" +#include "sshbuf.h" + +void sshbuf_getput_fuzz_tests(void); + +static void +attempt_parse_blob(u_char *blob, size_t len) +{ + struct sshbuf *p1; +#ifdef WITH_OPENSSL + BIGNUM *bn; +#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) + EC_KEY *eck; +#endif /* defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) */ +#endif /* WITH_OPENSSL */ + u_char *s; + size_t l; + u_int8_t u8; + u_int16_t u16; + u_int32_t u32; + u_int64_t u64; + + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put(p1, blob, len), 0); + sshbuf_get_u8(p1, &u8); + sshbuf_get_u16(p1, &u16); + sshbuf_get_u32(p1, &u32); + sshbuf_get_u64(p1, &u64); + if (sshbuf_get_string(p1, &s, &l) == 0) { + bzero(s, l); + free(s); + } +#ifdef WITH_OPENSSL + bn = NULL; + sshbuf_get_bignum2(p1, &bn); + BN_clear_free(bn); +#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) + eck = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + ASSERT_PTR_NE(eck, NULL); + sshbuf_get_eckey(p1, eck); + EC_KEY_free(eck); +#endif /* defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) */ +#endif /* WITH_OPENSSL */ + sshbuf_free(p1); +} + + +static void +onerror(void *fuzz) +{ + fprintf(stderr, "Failed during fuzz:\n"); + fuzz_dump((struct fuzz *)fuzz); +} + +void +sshbuf_getput_fuzz_tests(void) +{ + u_char blob[] = { + /* u8 */ + 0xd0, + /* u16 */ + 0xc0, 0xde, + /* u32 */ + 0xfa, 0xce, 0xde, 0xad, + /* u64 */ + 0xfe, 0xed, 0xac, 0x1d, 0x1f, 0x1c, 0xbe, 0xef, + /* string */ + 0x00, 0x00, 0x00, 0x09, + 'O', ' ', 'G', 'o', 'r', 'g', 'o', 'n', '!', + /* bignum2 */ + 0x00, 0x00, 0x00, 0x14, + 0x00, + 0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80, + 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00, + 0x7f, 0xff, 0x11, + /* EC point (NIST-256 curve) */ + 0x00, 0x00, 0x00, 0x41, + 0x04, + 0x0c, 0x82, 0x80, 0x04, 0x83, 0x9d, 0x01, 0x06, + 0xaa, 0x59, 0x57, 0x52, 0x16, 0x19, 0x13, 0x57, + 0x34, 0xb4, 0x51, 0x45, 0x9d, 0xad, 0xb5, 0x86, + 0x67, 0x7e, 0xf9, 0xdf, 0x55, 0x78, 0x49, 0x99, + 0x4d, 0x19, 0x6b, 0x50, 0xf0, 0xb4, 0xe9, 0x4b, + 0x3c, 0x73, 0xe3, 0xa9, 0xd4, 0xcd, 0x9d, 0xf2, + 0xc8, 0xf9, 0xa3, 0x5e, 0x42, 0xbd, 0xd0, 0x47, + 0x55, 0x0f, 0x69, 0xd8, 0x0e, 0xc2, 0x3c, 0xd4, + }; + struct fuzz *fuzz; + u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | + FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | + FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; + + if (test_is_fast()) + fuzzers &= ~(FUZZ_2_BYTE_FLIP|FUZZ_2_BIT_FLIP); + + TEST_START("fuzz blob parsing"); + fuzz = fuzz_begin(fuzzers, blob, sizeof(blob)); + TEST_ONERROR(onerror, fuzz); + for(; !fuzz_done(fuzz); fuzz_next(fuzz)) + attempt_parse_blob(blob, sizeof(blob)); + fuzz_cleanup(fuzz); + TEST_DONE(); + TEST_ONERROR(NULL, NULL); +} + diff --git a/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_misc.c b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_misc.c new file mode 100644 index 0000000000000000000000000000000000000000..249ecf235764a28a0557b7b39604846cea8652b5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/test_sshbuf_misc.c @@ -0,0 +1,217 @@ +/* $OpenBSD: test_sshbuf_misc.c,v 1.5 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "sshbuf.h" +#include "ssherr.h" + +void sshbuf_misc_tests(void); + +void +sshbuf_misc_tests(void) +{ + struct sshbuf *p1; + char tmp[512], msg[] = "imploring ping silence ping over", *p; + FILE *out; + size_t sz; + + TEST_START("sshbuf_dump"); + out = tmpfile(); + ASSERT_PTR_NE(out, NULL); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), 0); + sshbuf_dump(p1, out); + fflush(out); + rewind(out); + sz = fread(tmp, 1, sizeof(tmp), out); + ASSERT_INT_EQ(ferror(out), 0); + ASSERT_INT_NE(feof(out), 0); + ASSERT_SIZE_T_GT(sz, 0); + tmp[sz] = '\0'; + ASSERT_PTR_NE(strstr(tmp, "12 34 56 78"), NULL); + fclose(out); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_dtob16"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), 0); + p = sshbuf_dtob16(p1); + ASSERT_PTR_NE(p, NULL); + ASSERT_STRING_EQ(p, "12345678"); + free(p); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_dtob64_string len 1"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0); + p = sshbuf_dtob64_string(p1, 0); + ASSERT_PTR_NE(p, NULL); + ASSERT_STRING_EQ(p, "EQ=="); + free(p); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_dtob64_string len 2"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x22), 0); + p = sshbuf_dtob64_string(p1, 0); + ASSERT_PTR_NE(p, NULL); + ASSERT_STRING_EQ(p, "ESI="); + free(p); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_dtob64_string len 3"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x22), 0); + ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x33), 0); + p = sshbuf_dtob64_string(p1, 0); + ASSERT_PTR_NE(p, NULL); + ASSERT_STRING_EQ(p, "ESIz"); + free(p); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_dtob64_string len 8191"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_reserve(p1, 8192, NULL), 0); + bzero(sshbuf_mutable_ptr(p1), 8192); + p = sshbuf_dtob64_string(p1, 0); + ASSERT_PTR_NE(p, NULL); + ASSERT_SIZE_T_EQ(strlen(p), ((8191 + 2) / 3) * 4); + free(p); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_b64tod len 1"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_b64tod(p1, "0A=="), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1); + ASSERT_U8_EQ(*sshbuf_ptr(p1), 0xd0); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_b64tod len 2"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_b64tod(p1, "0A8="), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2); + ASSERT_U16_EQ(PEEK_U16(sshbuf_ptr(p1)), 0xd00f); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_b64tod len 4"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_b64tod(p1, "0A/QDw=="), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4); + ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), 0xd00fd00f); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_dup_string"); + p1 = sshbuf_new(); + ASSERT_PTR_NE(p1, NULL); + /* Check empty buffer */ + p = sshbuf_dup_string(p1); + ASSERT_PTR_NE(p, NULL); + ASSERT_SIZE_T_EQ(strlen(p), 0); + free(p); + /* Check buffer with string */ + ASSERT_INT_EQ(sshbuf_put(p1, "quad1", strlen("quad1")), 0); + p = sshbuf_dup_string(p1); + ASSERT_PTR_NE(p, NULL); + ASSERT_SIZE_T_EQ(strlen(p), strlen("quad1")); + ASSERT_STRING_EQ(p, "quad1"); + free(p); + /* Check buffer with terminating nul */ + ASSERT_INT_EQ(sshbuf_put(p1, "\0", 1), 0); + p = sshbuf_dup_string(p1); + ASSERT_PTR_NE(p, NULL); + ASSERT_SIZE_T_EQ(strlen(p), strlen("quad1")); + ASSERT_STRING_EQ(p, "quad1"); + free(p); + /* Check buffer with data after nul (expect failure) */ + ASSERT_INT_EQ(sshbuf_put(p1, "quad2", strlen("quad2")), 0); + p = sshbuf_dup_string(p1); + ASSERT_PTR_EQ(p, NULL); + sshbuf_free(p1); + TEST_DONE(); + + TEST_START("sshbuf_cmp"); + p1 = sshbuf_from(msg, sizeof(msg) - 1); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "i", 1), 0); + ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "j", 1), SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "imploring", 9), 0); + ASSERT_INT_EQ(sshbuf_cmp(p1, 0, "implored", 9), SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_cmp(p1, 10, "ping", 4), 0); + ASSERT_INT_EQ(sshbuf_cmp(p1, 10, "ring", 4), SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_cmp(p1, 28, "over", 4), 0); + ASSERT_INT_EQ(sshbuf_cmp(p1, 28, "rove", 4), SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_cmp(p1, 28, "overt", 5), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_cmp(p1, 32, "ping", 4), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_cmp(p1, 1000, "silence", 7), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_cmp(p1, 0, msg, sizeof(msg) - 1), 0); + TEST_DONE(); + + TEST_START("sshbuf_find"); + p1 = sshbuf_from(msg, sizeof(msg) - 1); + ASSERT_PTR_NE(p1, NULL); + ASSERT_INT_EQ(sshbuf_find(p1, 0, "i", 1, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 0); + ASSERT_INT_EQ(sshbuf_find(p1, 0, "j", 1, &sz), SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_find(p1, 0, "imploring", 9, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 0); + ASSERT_INT_EQ(sshbuf_find(p1, 0, "implored", 9, &sz), + SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_find(p1, 3, "ping", 4, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 10); + ASSERT_INT_EQ(sshbuf_find(p1, 11, "ping", 4, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 23); + ASSERT_INT_EQ(sshbuf_find(p1, 20, "over", 4, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 28); + ASSERT_INT_EQ(sshbuf_find(p1, 28, "over", 4, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 28); + ASSERT_INT_EQ(sshbuf_find(p1, 28, "rove", 4, &sz), + SSH_ERR_INVALID_FORMAT); + ASSERT_INT_EQ(sshbuf_find(p1, 28, "overt", 5, &sz), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_find(p1, 32, "ping", 4, &sz), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_find(p1, 1000, "silence", 7, &sz), + SSH_ERR_MESSAGE_INCOMPLETE); + ASSERT_INT_EQ(sshbuf_find(p1, 0, msg + 1, sizeof(msg) - 2, &sz), 0); + ASSERT_SIZE_T_EQ(sz, 1); + TEST_DONE(); +} + diff --git a/aosp/external/openssh/regress/unittests/sshbuf/tests.c b/aosp/external/openssh/regress/unittests/sshbuf/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..29916a10bc5bcee64ca9c510e51434158615f331 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshbuf/tests.c @@ -0,0 +1,30 @@ +/* $OpenBSD: tests.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "../test_helper/test_helper.h" + +void sshbuf_tests(void); +void sshbuf_getput_basic_tests(void); +void sshbuf_getput_crypto_tests(void); +void sshbuf_misc_tests(void); +void sshbuf_fuzz_tests(void); +void sshbuf_getput_fuzz_tests(void); +void sshbuf_fixed(void); + +void +tests(void) +{ + sshbuf_tests(); + sshbuf_getput_basic_tests(); +#ifdef WITH_OPENSSL + sshbuf_getput_crypto_tests(); +#endif + sshbuf_misc_tests(); + sshbuf_fuzz_tests(); + sshbuf_getput_fuzz_tests(); + sshbuf_fixed(); +} diff --git a/aosp/external/openssh/regress/unittests/sshkey/Makefile b/aosp/external/openssh/regress/unittests/sshkey/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d4a892375bd7d963823c71132d8ca2b8face20aa --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/Makefile @@ -0,0 +1,26 @@ +# $OpenBSD: Makefile,v 1.11 2021/01/09 12:24:31 dtucker Exp $ + +PROG=test_sshkey +SRCS=tests.c test_sshkey.c test_file.c test_fuzz.c common.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c +SRCS+=ssh-dss.c ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c +SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c +SRCS+=addr.c addrmatch.c bitmap.c +SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c +SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c +SRCS+=ssh-ed25519-sk.c sk-usbhid.c + +SRCS+=digest-openssl.c +#SRCS+=digest-libc.c +SRCS+=utf8.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} ${UNITTEST_ARGS} -d ${.CURDIR}/testdata + +.include + diff --git a/aosp/external/openssh/regress/unittests/sshkey/common.c b/aosp/external/openssh/regress/unittests/sshkey/common.c new file mode 100644 index 0000000000000000000000000000000000000000..51b0d92e1d0734bd0ab7078b94a5e9d1422a162b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/common.c @@ -0,0 +1,163 @@ +/* $OpenBSD: common.c,v 1.5 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Helpers for key API tests + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#ifdef OPENSSL_HAS_NISTP256 +# include +#endif /* OPENSSL_HAS_NISTP256 */ +#endif /* WITH_OPENSSL */ + +#include "openbsd-compat/openssl-compat.h" + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "authfile.h" +#include "sshkey.h" +#include "sshbuf.h" + +#include "common.h" + +struct sshbuf * +load_file(const char *name) +{ + struct sshbuf *ret = NULL; + + ASSERT_INT_EQ(sshbuf_load_file(test_data_file(name), &ret), 0); + ASSERT_PTR_NE(ret, NULL); + return ret; +} + +struct sshbuf * +load_text_file(const char *name) +{ + struct sshbuf *ret = load_file(name); + const u_char *p; + + /* Trim whitespace at EOL */ + for (p = sshbuf_ptr(ret); sshbuf_len(ret) > 0;) { + if (p[sshbuf_len(ret) - 1] == '\r' || + p[sshbuf_len(ret) - 1] == '\t' || + p[sshbuf_len(ret) - 1] == ' ' || + p[sshbuf_len(ret) - 1] == '\n') + ASSERT_INT_EQ(sshbuf_consume_end(ret, 1), 0); + else + break; + } + /* \0 terminate */ + ASSERT_INT_EQ(sshbuf_put_u8(ret, 0), 0); + return ret; +} + +#ifdef WITH_OPENSSL +BIGNUM * +load_bignum(const char *name) +{ + BIGNUM *ret = NULL; + struct sshbuf *buf; + + buf = load_text_file(name); + ASSERT_INT_NE(BN_hex2bn(&ret, (const char *)sshbuf_ptr(buf)), 0); + sshbuf_free(buf); + return ret; +} + +const BIGNUM * +rsa_n(struct sshkey *k) +{ + const BIGNUM *n = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->rsa, NULL); + RSA_get0_key(k->rsa, &n, NULL, NULL); + return n; +} + +const BIGNUM * +rsa_e(struct sshkey *k) +{ + const BIGNUM *e = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->rsa, NULL); + RSA_get0_key(k->rsa, NULL, &e, NULL); + return e; +} + +const BIGNUM * +rsa_p(struct sshkey *k) +{ + const BIGNUM *p = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->rsa, NULL); + RSA_get0_factors(k->rsa, &p, NULL); + return p; +} + +const BIGNUM * +rsa_q(struct sshkey *k) +{ + const BIGNUM *q = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->rsa, NULL); + RSA_get0_factors(k->rsa, NULL, &q); + return q; +} + +const BIGNUM * +dsa_g(struct sshkey *k) +{ + const BIGNUM *g = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->dsa, NULL); + DSA_get0_pqg(k->dsa, NULL, NULL, &g); + return g; +} + +const BIGNUM * +dsa_pub_key(struct sshkey *k) +{ + const BIGNUM *pub_key = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->dsa, NULL); + DSA_get0_key(k->dsa, &pub_key, NULL); + return pub_key; +} + +const BIGNUM * +dsa_priv_key(struct sshkey *k) +{ + const BIGNUM *priv_key = NULL; + + ASSERT_PTR_NE(k, NULL); + ASSERT_PTR_NE(k->dsa, NULL); + DSA_get0_key(k->dsa, NULL, &priv_key); + return priv_key; +} +#endif /* WITH_OPENSSL */ + diff --git a/aosp/external/openssh/regress/unittests/sshkey/common.h b/aosp/external/openssh/regress/unittests/sshkey/common.h new file mode 100644 index 0000000000000000000000000000000000000000..7a514fdc8fe6af4f70288494c119f1d85149d228 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/common.h @@ -0,0 +1,25 @@ +/* $OpenBSD: common.h,v 1.2 2018/09/13 09:03:20 djm Exp $ */ +/* + * Helpers for key API tests + * + * Placed in the public domain + */ + +/* Load a binary file into a buffer */ +struct sshbuf *load_file(const char *name); + +/* Load a text file into a buffer */ +struct sshbuf *load_text_file(const char *name); + +/* Load a bignum from a file */ +BIGNUM *load_bignum(const char *name); + +/* Accessors for key components */ +const BIGNUM *rsa_n(struct sshkey *k); +const BIGNUM *rsa_e(struct sshkey *k); +const BIGNUM *rsa_p(struct sshkey *k); +const BIGNUM *rsa_q(struct sshkey *k); +const BIGNUM *dsa_g(struct sshkey *k); +const BIGNUM *dsa_pub_key(struct sshkey *k); +const BIGNUM *dsa_priv_key(struct sshkey *k); + diff --git a/aosp/external/openssh/regress/unittests/sshkey/mktestdata.sh b/aosp/external/openssh/regress/unittests/sshkey/mktestdata.sh new file mode 100644 index 0000000000000000000000000000000000000000..fcd78e990e8b1f3d006c1f9d131b5c3f22478e49 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/mktestdata.sh @@ -0,0 +1,222 @@ +#!/bin/sh +# $OpenBSD: mktestdata.sh,v 1.11 2020/06/19 03:48:49 djm Exp $ + +PW=mekmitasdigoat + +rsa_params() { + _in="$1" + _outbase="$2" + set -e + openssl rsa -noout -text -in $_in | \ + awk '/^modulus:$/,/^publicExponent:/' | \ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.n + openssl rsa -noout -text -in $_in | \ + awk '/^prime1:$/,/^prime2:/' | \ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.p + openssl rsa -noout -text -in $_in | \ + awk '/^prime2:$/,/^exponent1:/' | \ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.q + for x in n p q ; do + echo "" >> ${_outbase}.$x + echo ============ ${_outbase}.$x + cat ${_outbase}.$x + echo ============ + done +} + +dsa_params() { + _in="$1" + _outbase="$2" + set -e + openssl dsa -noout -text -in $_in | \ + awk '/^priv:$/,/^pub:/' | \ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.priv + openssl dsa -noout -text -in $_in | \ + awk '/^pub:/,/^P:/' | #\ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.pub + openssl dsa -noout -text -in $_in | \ + awk '/^G:/,0' | \ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.g + for x in priv pub g ; do + echo "" >> ${_outbase}.$x + echo ============ ${_outbase}.$x + cat ${_outbase}.$x + echo ============ + done +} + +ecdsa_params() { + _in="$1" + _outbase="$2" + set -e + openssl ec -noout -text -in $_in | \ + awk '/^priv:$/,/^pub:/' | \ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.priv + openssl ec -noout -text -in $_in | \ + awk '/^pub:/,/^ASN1 OID:/' | #\ + grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.pub + openssl ec -noout -text -in $_in | \ + grep "ASN1 OID:" | \ + sed 's/.*: //;s/ *$//' | tr -d '\n' > ${_outbase}.curve + for x in priv pub curve ; do + echo "" >> ${_outbase}.$x + echo ============ ${_outbase}.$x + cat ${_outbase}.$x + echo ============ + done +} + +set -ex + +cd testdata + +if [ -f ../../../misc/sk-dummy/sk-dummy.so ] ; then + SK_DUMMY=../../../misc/sk-dummy/sk-dummy.so +elif [ -f ../../../misc/sk-dummy/obj/sk-dummy.so ] ; then + SK_DUMMY=../../../misc/sk-dummy/obj/sk-dummy.so +else + echo "Can't find sk-dummy.so" 1>&2 + exit 1 +fi + +rm -f rsa_1 dsa_1 ecdsa_1 ed25519_1 +rm -f rsa_2 dsa_2 ecdsa_2 ed25519_2 +rm -f rsa_n dsa_n ecdsa_n # new-format keys +rm -f rsa_1_pw dsa_1_pw ecdsa_1_pw ed25519_1_pw +rm -f rsa_n_pw dsa_n_pw ecdsa_n_pw +rm -f pw *.pub *.bn.* *.param.* *.fp *.fp.bb + +ssh-keygen -t rsa -b 1024 -C "RSA test key #1" -N "" -f rsa_1 -m PEM +ssh-keygen -t dsa -b 1024 -C "DSA test key #1" -N "" -f dsa_1 -m PEM +ssh-keygen -t ecdsa -b 256 -C "ECDSA test key #1" -N "" -f ecdsa_1 -m PEM +ssh-keygen -t ed25519 -C "ED25519 test key #1" -N "" -f ed25519_1 +ssh-keygen -w "$SK_DUMMY" -t ecdsa-sk -C "ECDSA-SK test key #1" \ + -N "" -f ecdsa_sk1 +ssh-keygen -w "$SK_DUMMY" -t ed25519-sk -C "ED25519-SK test key #1" \ + -N "" -f ed25519_sk1 + + +ssh-keygen -t rsa -b 2048 -C "RSA test key #2" -N "" -f rsa_2 -m PEM +ssh-keygen -t dsa -b 1024 -C "DSA test key #2" -N "" -f dsa_2 -m PEM +ssh-keygen -t ecdsa -b 521 -C "ECDSA test key #2" -N "" -f ecdsa_2 -m PEM +ssh-keygen -t ed25519 -C "ED25519 test key #2" -N "" -f ed25519_2 +ssh-keygen -w "$SK_DUMMY" -t ecdsa-sk -C "ECDSA-SK test key #2" \ + -N "" -f ecdsa_sk2 +ssh-keygen -w "$SK_DUMMY" -t ed25519-sk -C "ED25519-SK test key #2" \ + -N "" -f ed25519_sk2 + +cp rsa_1 rsa_n +cp dsa_1 dsa_n +cp ecdsa_1 ecdsa_n + +ssh-keygen -pf rsa_n -N "" +ssh-keygen -pf dsa_n -N "" +ssh-keygen -pf ecdsa_n -N "" + +cp rsa_1 rsa_1_pw +cp dsa_1 dsa_1_pw +cp ecdsa_1 ecdsa_1_pw +cp ed25519_1 ed25519_1_pw +cp ecdsa_sk1 ecdsa_sk1_pw +cp ed25519_sk1 ed25519_sk1_pw +cp rsa_1 rsa_n_pw +cp dsa_1 dsa_n_pw +cp ecdsa_1 ecdsa_n_pw + +ssh-keygen -pf rsa_1_pw -m PEM -N "$PW" +ssh-keygen -pf dsa_1_pw -m PEM -N "$PW" +ssh-keygen -pf ecdsa_1_pw -m PEM -N "$PW" +ssh-keygen -pf ed25519_1_pw -N "$PW" +ssh-keygen -pf ecdsa_sk1_pw -m PEM -N "$PW" +ssh-keygen -pf ed25519_sk1_pw -N "$PW" +ssh-keygen -pf rsa_n_pw -N "$PW" +ssh-keygen -pf dsa_n_pw -N "$PW" +ssh-keygen -pf ecdsa_n_pw -N "$PW" + +rsa_params rsa_1 rsa_1.param +rsa_params rsa_2 rsa_2.param +dsa_params dsa_1 dsa_1.param +dsa_params dsa_1 dsa_1.param +ecdsa_params ecdsa_1 ecdsa_1.param +ecdsa_params ecdsa_2 ecdsa_2.param +# XXX ed25519, *sk params + +ssh-keygen -s rsa_2 -I hugo -n user1,user2 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 1 rsa_1.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 2 dsa_1.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 3 ecdsa_1.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 4 ed25519_1.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 4 ecdsa_sk1.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 4 ed25519_sk1.pub + + +# Make a few RSA variant signature too. +cp rsa_1 rsa_1_sha1 +cp rsa_1 rsa_1_sha512 +cp rsa_1.pub rsa_1_sha1.pub +cp rsa_1.pub rsa_1_sha512.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 -t ssh-rsa \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 1 rsa_1_sha1.pub +ssh-keygen -s rsa_2 -I hugo -n user1,user2 -t rsa-sha2-512 \ + -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \ + -V 19990101:20110101 -z 1 rsa_1_sha512.pub + +ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \ + -V 19990101:20110101 -z 5 rsa_1.pub +ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \ + -V 19990101:20110101 -z 6 dsa_1.pub +ssh-keygen -s ecdsa_1 -I julius -n host1,host2 -h \ + -V 19990101:20110101 -z 7 ecdsa_1.pub +ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \ + -V 19990101:20110101 -z 8 ed25519_1.pub +ssh-keygen -s ecdsa_1 -I julius -n host1,host2 -h \ + -V 19990101:20110101 -z 7 ecdsa_sk1.pub +ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \ + -V 19990101:20110101 -z 8 ed25519_sk1.pub + +ssh-keygen -lf rsa_1 | awk '{print $2}' > rsa_1.fp +ssh-keygen -lf dsa_1 | awk '{print $2}' > dsa_1.fp +ssh-keygen -lf ecdsa_1 | awk '{print $2}' > ecdsa_1.fp +ssh-keygen -lf ed25519_1 | awk '{print $2}' > ed25519_1.fp +ssh-keygen -lf ecdsa_sk1 | awk '{print $2}' > ecdsa_sk1.fp +ssh-keygen -lf ed25519_sk1 | awk '{print $2}' > ed25519_sk1.fp +ssh-keygen -lf rsa_2 | awk '{print $2}' > rsa_2.fp +ssh-keygen -lf dsa_2 | awk '{print $2}' > dsa_2.fp +ssh-keygen -lf ecdsa_2 | awk '{print $2}' > ecdsa_2.fp +ssh-keygen -lf ed25519_2 | awk '{print $2}' > ed25519_2.fp +ssh-keygen -lf ecdsa_sk2 | awk '{print $2}' > ecdsa_sk2.fp +ssh-keygen -lf ed25519_sk2 | awk '{print $2}' > ed25519_sk2.fp + +ssh-keygen -lf rsa_1-cert.pub | awk '{print $2}' > rsa_1-cert.fp +ssh-keygen -lf dsa_1-cert.pub | awk '{print $2}' > dsa_1-cert.fp +ssh-keygen -lf ecdsa_1-cert.pub | awk '{print $2}' > ecdsa_1-cert.fp +ssh-keygen -lf ed25519_1-cert.pub | awk '{print $2}' > ed25519_1-cert.fp +ssh-keygen -lf ecdsa_sk1-cert.pub | awk '{print $2}' > ecdsa_sk1-cert.fp +ssh-keygen -lf ed25519_sk1-cert.pub | awk '{print $2}' > ed25519_sk1-cert.fp + +ssh-keygen -Bf rsa_1 | awk '{print $2}' > rsa_1.fp.bb +ssh-keygen -Bf dsa_1 | awk '{print $2}' > dsa_1.fp.bb +ssh-keygen -Bf ecdsa_1 | awk '{print $2}' > ecdsa_1.fp.bb +ssh-keygen -Bf ed25519_1 | awk '{print $2}' > ed25519_1.fp.bb +ssh-keygen -Bf ecdsa_sk1 | awk '{print $2}' > ecdsa_sk1.fp.bb +ssh-keygen -Bf ed25519_sk1 | awk '{print $2}' > ed25519_sk1.fp.bb +ssh-keygen -Bf rsa_2 | awk '{print $2}' > rsa_2.fp.bb +ssh-keygen -Bf dsa_2 | awk '{print $2}' > dsa_2.fp.bb +ssh-keygen -Bf ecdsa_2 | awk '{print $2}' > ecdsa_2.fp.bb +ssh-keygen -Bf ed25519_2 | awk '{print $2}' > ed25519_2.fp.bb +ssh-keygen -Bf ecdsa_sk2 | awk '{print $2}' > ecdsa_sk2.fp.bb +ssh-keygen -Bf ed25519_sk2 | awk '{print $2}' > ed25519_sk2.fp.bb + +echo "$PW" > pw diff --git a/aosp/external/openssh/regress/unittests/sshkey/test_file.c b/aosp/external/openssh/regress/unittests/sshkey/test_file.c new file mode 100644 index 0000000000000000000000000000000000000000..497ab6dded1d0fa841cd74cbf4819578aee3c5a3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/test_file.c @@ -0,0 +1,559 @@ +/* $OpenBSD: test_file.c,v 1.10 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshkey.h key management API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#ifdef OPENSSL_HAS_NISTP256 +# include +#endif /* OPENSSL_HAS_NISTP256 */ +#endif /* WITH_OPENSSL */ + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "authfile.h" +#include "sshkey.h" +#include "sshbuf.h" +#include "digest.h" + +#include "common.h" + +void sshkey_file_tests(void); + +void +sshkey_file_tests(void) +{ + struct sshkey *k1, *k2; + struct sshbuf *buf, *pw; +#ifdef WITH_OPENSSL + BIGNUM *a, *b, *c; +#endif + char *cp; + + TEST_START("load passphrase"); + pw = load_text_file("pw"); + TEST_DONE(); + + +#ifdef WITH_OPENSSL + TEST_START("parse RSA from private"); + buf = load_file("rsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k1, NULL); + a = load_bignum("rsa_1.param.n"); + b = load_bignum("rsa_1.param.p"); + c = load_bignum("rsa_1.param.q"); + ASSERT_BIGNUM_EQ(rsa_n(k1), a); + ASSERT_BIGNUM_EQ(rsa_p(k1), b); + ASSERT_BIGNUM_EQ(rsa_q(k1), c); + BN_free(a); + BN_free(b); + BN_free(c); + TEST_DONE(); + + TEST_START("parse RSA from private w/ passphrase"); + buf = load_file("rsa_1_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("parse RSA from new-format"); + buf = load_file("rsa_n"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("parse RSA from new-format w/ passphrase"); + buf = load_file("rsa_n_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load RSA from public"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2, + NULL), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load RSA cert with SHA1 signature"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1_sha1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_RSA_CERT); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + ASSERT_STRING_EQ(k2->cert->signature_type, "ssh-rsa"); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load RSA cert with SHA512 signature"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1_sha512"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_RSA_CERT); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + ASSERT_STRING_EQ(k2->cert->signature_type, "rsa-sha2-512"); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load RSA cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_RSA_CERT); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 0); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + TEST_DONE(); + + TEST_START("RSA key hex fingerprint"); + buf = load_text_file("rsa_1.fp"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + TEST_START("RSA cert hex fingerprint"); + buf = load_text_file("rsa_1-cert.fp"); + cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("RSA key bubblebabble fingerprint"); + buf = load_text_file("rsa_1.fp.bb"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + sshkey_free(k1); + + TEST_START("parse DSA from private"); + buf = load_file("dsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k1, NULL); + a = load_bignum("dsa_1.param.g"); + b = load_bignum("dsa_1.param.priv"); + c = load_bignum("dsa_1.param.pub"); + ASSERT_BIGNUM_EQ(dsa_g(k1), a); + ASSERT_BIGNUM_EQ(dsa_priv_key(k1), b); + ASSERT_BIGNUM_EQ(dsa_pub_key(k1), c); + BN_free(a); + BN_free(b); + BN_free(c); + TEST_DONE(); + + TEST_START("parse DSA from private w/ passphrase"); + buf = load_file("dsa_1_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("parse DSA from new-format"); + buf = load_file("dsa_n"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("parse DSA from new-format w/ passphrase"); + buf = load_file("dsa_n_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load DSA from public"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_1.pub"), &k2, + NULL), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load DSA cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_DSA_CERT); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 0); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + TEST_DONE(); + + TEST_START("DSA key hex fingerprint"); + buf = load_text_file("dsa_1.fp"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + TEST_START("DSA cert hex fingerprint"); + buf = load_text_file("dsa_1-cert.fp"); + cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("DSA key bubblebabble fingerprint"); + buf = load_text_file("dsa_1.fp.bb"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + sshkey_free(k1); + +#ifdef OPENSSL_HAS_ECC + TEST_START("parse ECDSA from private"); + buf = load_file("ecdsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k1, NULL); + buf = load_text_file("ecdsa_1.param.curve"); + ASSERT_STRING_EQ((const char *)sshbuf_ptr(buf), + OBJ_nid2sn(k1->ecdsa_nid)); + sshbuf_free(buf); + a = load_bignum("ecdsa_1.param.priv"); + b = load_bignum("ecdsa_1.param.pub"); + c = EC_POINT_point2bn(EC_KEY_get0_group(k1->ecdsa), + EC_KEY_get0_public_key(k1->ecdsa), POINT_CONVERSION_UNCOMPRESSED, + NULL, NULL); + ASSERT_PTR_NE(c, NULL); + ASSERT_BIGNUM_EQ(EC_KEY_get0_private_key(k1->ecdsa), a); + ASSERT_BIGNUM_EQ(b, c); + BN_free(a); + BN_free(b); + BN_free(c); + TEST_DONE(); + + TEST_START("parse ECDSA from private w/ passphrase"); + buf = load_file("ecdsa_1_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("parse ECDSA from new-format"); + buf = load_file("ecdsa_n"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("parse ECDSA from new-format w/ passphrase"); + buf = load_file("ecdsa_n_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load ECDSA from public"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_1.pub"), &k2, + NULL), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load ECDSA cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_ECDSA_CERT); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 0); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + TEST_DONE(); + + TEST_START("ECDSA key hex fingerprint"); + buf = load_text_file("ecdsa_1.fp"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + TEST_START("ECDSA cert hex fingerprint"); + buf = load_text_file("ecdsa_1-cert.fp"); + cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("ECDSA key bubblebabble fingerprint"); + buf = load_text_file("ecdsa_1.fp.bb"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + sshkey_free(k1); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("parse Ed25519 from private"); + buf = load_file("ed25519_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k1, NULL); + ASSERT_INT_EQ(k1->type, KEY_ED25519); + /* XXX check key contents */ + TEST_DONE(); + + TEST_START("parse Ed25519 from private w/ passphrase"); + buf = load_file("ed25519_1_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load Ed25519 from public"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_1.pub"), &k2, + NULL), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load Ed25519 cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_ED25519_CERT); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 0); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + TEST_DONE(); + + TEST_START("Ed25519 key hex fingerprint"); + buf = load_text_file("ed25519_1.fp"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + TEST_START("Ed25519 cert hex fingerprint"); + buf = load_text_file("ed25519_1-cert.fp"); + cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("Ed25519 key bubblebabble fingerprint"); + buf = load_text_file("ed25519_1.fp.bb"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + sshkey_free(k1); + +#ifdef ENABLE_SK +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + TEST_START("parse ECDSA-SK from private"); + buf = load_file("ecdsa_sk1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k1, NULL); + ASSERT_INT_EQ(k1->type, KEY_ECDSA_SK); + TEST_DONE(); + + TEST_START("parse ECDSA-SK from private w/ passphrase"); + buf = load_file("ecdsa_sk1_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load ECDSA-SK from public"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_sk1.pub"), &k2, + NULL), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load ECDSA-SK cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_sk1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_ECDSA_SK_CERT); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 0); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + TEST_DONE(); + + TEST_START("ECDSA-SK key hex fingerprint"); + buf = load_text_file("ecdsa_sk1.fp"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + TEST_START("ECDSA-SK cert hex fingerprint"); + buf = load_text_file("ecdsa_sk1-cert.fp"); + cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("ECDSA-SK key bubblebabble fingerprint"); + buf = load_text_file("ecdsa_sk1.fp.bb"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + sshkey_free(k1); +#endif + + TEST_START("parse Ed25519-SK from private"); + buf = load_file("ed25519_sk1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k1, NULL); + ASSERT_INT_EQ(k1->type, KEY_ED25519_SK); + /* XXX check key contents */ + TEST_DONE(); + + TEST_START("parse Ed25519-SK from private w/ passphrase"); + buf = load_file("ed25519_sk1_pw"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, + (const char *)sshbuf_ptr(pw), &k2, NULL), 0); + sshbuf_free(buf); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load Ed25519-SK from public"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_sk1.pub"), + &k2, NULL), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("load Ed25519-SK cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_sk1"), &k2), 0); + ASSERT_PTR_NE(k2, NULL); + ASSERT_INT_EQ(k2->type, KEY_ED25519_SK_CERT); + ASSERT_INT_EQ(sshkey_equal(k1, k2), 0); + ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1); + TEST_DONE(); + + TEST_START("Ed25519-SK key hex fingerprint"); + buf = load_text_file("ed25519_sk1.fp"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + TEST_START("Ed25519-SK cert hex fingerprint"); + buf = load_text_file("ed25519_sk1-cert.fp"); + cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("Ed25519-SK key bubblebabble fingerprint"); + buf = load_text_file("ed25519_sk1.fp.bb"); + cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE); + ASSERT_PTR_NE(cp, NULL); + ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf)); + sshbuf_free(buf); + free(cp); + TEST_DONE(); + + sshkey_free(k1); +#endif /* ENABLE_SK */ + + sshbuf_free(pw); + +} diff --git a/aosp/external/openssh/regress/unittests/sshkey/test_fuzz.c b/aosp/external/openssh/regress/unittests/sshkey/test_fuzz.c new file mode 100644 index 0000000000000000000000000000000000000000..2fae19dcfe080470b181a8d9218d2b57a42b0324 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/test_fuzz.c @@ -0,0 +1,391 @@ +/* $OpenBSD: test_fuzz.c,v 1.13 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Fuzz tests for key parsing + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#ifdef OPENSSL_HAS_NISTP256 +# include +#endif +#endif + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "authfile.h" +#include "sshkey.h" +#include "sshbuf.h" + +#include "common.h" + +void sshkey_fuzz_tests(void); + +static void +onerror(void *fuzz) +{ + fprintf(stderr, "Failed during fuzz:\n"); + fuzz_dump((struct fuzz *)fuzz); +} + +static void +public_fuzz(struct sshkey *k) +{ + struct sshkey *k1; + struct sshbuf *buf; + struct fuzz *fuzz; + u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | + FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; + + if (test_is_fast()) + fuzzers &= ~FUZZ_1_BIT_FLIP; + if (test_is_slow()) + fuzzers |= FUZZ_2_BIT_FLIP | FUZZ_2_BYTE_FLIP; + ASSERT_PTR_NE(buf = sshbuf_new(), NULL); + ASSERT_INT_EQ(sshkey_putb(k, buf), 0); + fuzz = fuzz_begin(fuzzers, sshbuf_mutable_ptr(buf), sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(buf), sshbuf_len(buf), + &k1), 0); + sshkey_free(k1); + sshbuf_free(buf); + TEST_ONERROR(onerror, fuzz); + for(; !fuzz_done(fuzz); fuzz_next(fuzz)) { + if (sshkey_from_blob(fuzz_ptr(fuzz), fuzz_len(fuzz), &k1) == 0) + sshkey_free(k1); + } + fuzz_cleanup(fuzz); +} + +static void +sig_fuzz(struct sshkey *k, const char *sig_alg) +{ + struct fuzz *fuzz; + u_char *sig, c[] = "some junk to be signed"; + size_t l; + u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | + FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; + + if (test_is_fast()) + fuzzers &= ~FUZZ_2_BYTE_FLIP; + if (test_is_slow()) + fuzzers |= FUZZ_2_BIT_FLIP; + + ASSERT_INT_EQ(sshkey_sign(k, &sig, &l, c, sizeof(c), + sig_alg, NULL, NULL, 0), 0); + ASSERT_SIZE_T_GT(l, 0); + fuzz = fuzz_begin(fuzzers, sig, l); + ASSERT_INT_EQ(sshkey_verify(k, sig, l, c, sizeof(c), NULL, 0, NULL), 0); + free(sig); + TEST_ONERROR(onerror, fuzz); + for(; !fuzz_done(fuzz); fuzz_next(fuzz)) { + /* Ensure 1-bit difference at least */ + if (fuzz_matches_original(fuzz)) + continue; + ASSERT_INT_NE(sshkey_verify(k, fuzz_ptr(fuzz), fuzz_len(fuzz), + c, sizeof(c), NULL, 0, NULL), 0); + } + fuzz_cleanup(fuzz); +} + +#define NUM_FAST_BASE64_TESTS 1024 + +void +sshkey_fuzz_tests(void) +{ + struct sshkey *k1; + struct sshbuf *buf, *fuzzed; + struct fuzz *fuzz; + int r, i; + +#ifdef WITH_OPENSSL + TEST_START("fuzz RSA private"); + buf = load_file("rsa_1"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); + + TEST_START("fuzz RSA new-format private"); + buf = load_file("rsa_n"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); + + TEST_START("fuzz DSA private"); + buf = load_file("dsa_1"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); + + TEST_START("fuzz DSA new-format private"); + buf = load_file("dsa_n"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("fuzz ECDSA private"); + buf = load_file("ecdsa_1"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); + + TEST_START("fuzz ECDSA new-format private"); + buf = load_file("ecdsa_n"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("fuzz Ed25519 private"); + buf = load_file("ed25519_1"); + fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshkey_free(k1); + sshbuf_free(buf); + ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL); + TEST_ONERROR(onerror, fuzz); + for(i = 0; !fuzz_done(fuzz); i++, fuzz_next(fuzz)) { + r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz)); + ASSERT_INT_EQ(r, 0); + if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0) + sshkey_free(k1); + sshbuf_reset(fuzzed); + if (test_is_fast() && i >= NUM_FAST_BASE64_TESTS) + break; + } + sshbuf_free(fuzzed); + fuzz_cleanup(fuzz); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("fuzz RSA public"); + buf = load_file("rsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz RSA cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz DSA public"); + buf = load_file("dsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz DSA cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k1), 0); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("fuzz ECDSA public"); + buf = load_file("ecdsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz ECDSA cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k1), 0); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("fuzz Ed25519 public"); + buf = load_file("ed25519_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz Ed25519 cert"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k1), 0); + public_fuzz(k1); + sshkey_free(k1); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("fuzz RSA sig"); + buf = load_file("rsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + sig_fuzz(k1, "ssh-rsa"); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz RSA SHA256 sig"); + buf = load_file("rsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + sig_fuzz(k1, "rsa-sha2-256"); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz RSA SHA512 sig"); + buf = load_file("rsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + sig_fuzz(k1, "rsa-sha2-512"); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("fuzz DSA sig"); + buf = load_file("dsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + sig_fuzz(k1, NULL); + sshkey_free(k1); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("fuzz ECDSA sig"); + buf = load_file("ecdsa_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + sig_fuzz(k1, NULL); + sshkey_free(k1); + TEST_DONE(); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("fuzz Ed25519 sig"); + buf = load_file("ed25519_1"); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); + sshbuf_free(buf); + sig_fuzz(k1, NULL); + sshkey_free(k1); + TEST_DONE(); + +/* XXX fuzz decoded new-format blobs too */ +/* XXX fuzz XMSS too */ + +} diff --git a/aosp/external/openssh/regress/unittests/sshkey/test_sshkey.c b/aosp/external/openssh/regress/unittests/sshkey/test_sshkey.c new file mode 100644 index 0000000000000000000000000000000000000000..982907ce7f0cc4d06b43030fce41007845cab3bc --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/test_sshkey.c @@ -0,0 +1,528 @@ +/* $OpenBSD: test_sshkey.c,v 1.22 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshkey.h key management API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256) +# include +#endif +#endif + +#include "../test_helper/test_helper.h" + +#include "ssherr.h" +#include "sshbuf.h" +#define SSHBUF_INTERNAL 1 /* access internals for testing */ +#include "sshkey.h" + +#include "authfile.h" +#include "common.h" +#include "ssh2.h" + +void sshkey_tests(void); + +static void +put_opt(struct sshbuf *b, const char *name, const char *value) +{ + struct sshbuf *sect; + + sect = sshbuf_new(); + ASSERT_PTR_NE(sect, NULL); + ASSERT_INT_EQ(sshbuf_put_cstring(b, name), 0); + if (value != NULL) + ASSERT_INT_EQ(sshbuf_put_cstring(sect, value), 0); + ASSERT_INT_EQ(sshbuf_put_stringb(b, sect), 0); + sshbuf_free(sect); +} + +#ifdef WITH_OPENSSL +static void +build_cert(struct sshbuf *b, struct sshkey *k, const char *type, + struct sshkey *sign_key, struct sshkey *ca_key, + const char *sig_alg) +{ + struct sshbuf *ca_buf, *pk, *principals, *critopts, *exts; + u_char *sigblob; + size_t siglen; + + ca_buf = sshbuf_new(); + ASSERT_PTR_NE(ca_buf, NULL); + ASSERT_INT_EQ(sshkey_putb(ca_key, ca_buf), 0); + + /* + * Get the public key serialisation by rendering the key and skipping + * the type string. This is a bit of a hack :/ + */ + pk = sshbuf_new(); + ASSERT_PTR_NE(pk, NULL); + ASSERT_INT_EQ(sshkey_putb_plain(k, pk), 0); + ASSERT_INT_EQ(sshbuf_skip_string(pk), 0); + + principals = sshbuf_new(); + ASSERT_PTR_NE(principals, NULL); + ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gsamsa"), 0); + ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gregor"), 0); + + critopts = sshbuf_new(); + ASSERT_PTR_NE(critopts, NULL); + put_opt(critopts, "force-command", "/usr/local/bin/nethack"); + put_opt(critopts, "source-address", "192.168.0.0/24,127.0.0.1,::1"); + + exts = sshbuf_new(); + ASSERT_PTR_NE(exts, NULL); + put_opt(critopts, "permit-X11-forwarding", NULL); + + ASSERT_INT_EQ(sshbuf_put_cstring(b, type), 0); + ASSERT_INT_EQ(sshbuf_put_cstring(b, "noncenoncenonce!"), 0); /* nonce */ + ASSERT_INT_EQ(sshbuf_putb(b, pk), 0); /* public key serialisation */ + ASSERT_INT_EQ(sshbuf_put_u64(b, 1234), 0); /* serial */ + ASSERT_INT_EQ(sshbuf_put_u32(b, SSH2_CERT_TYPE_USER), 0); /* type */ + ASSERT_INT_EQ(sshbuf_put_cstring(b, "gregor"), 0); /* key ID */ + ASSERT_INT_EQ(sshbuf_put_stringb(b, principals), 0); /* principals */ + ASSERT_INT_EQ(sshbuf_put_u64(b, 0), 0); /* start */ + ASSERT_INT_EQ(sshbuf_put_u64(b, 0xffffffffffffffffULL), 0); /* end */ + ASSERT_INT_EQ(sshbuf_put_stringb(b, critopts), 0); /* options */ + ASSERT_INT_EQ(sshbuf_put_stringb(b, exts), 0); /* extensions */ + ASSERT_INT_EQ(sshbuf_put_string(b, NULL, 0), 0); /* reserved */ + ASSERT_INT_EQ(sshbuf_put_stringb(b, ca_buf), 0); /* signature key */ + ASSERT_INT_EQ(sshkey_sign(sign_key, &sigblob, &siglen, + sshbuf_ptr(b), sshbuf_len(b), sig_alg, NULL, NULL, 0), 0); + ASSERT_INT_EQ(sshbuf_put_string(b, sigblob, siglen), 0); /* signature */ + + free(sigblob); + sshbuf_free(ca_buf); + sshbuf_free(exts); + sshbuf_free(critopts); + sshbuf_free(principals); + sshbuf_free(pk); +} +#endif /* WITH_OPENSSL */ + +static void +signature_test(struct sshkey *k, struct sshkey *bad, const char *sig_alg, + const u_char *d, size_t l) +{ + size_t len; + u_char *sig; + + ASSERT_INT_EQ(sshkey_sign(k, &sig, &len, d, l, sig_alg, + NULL, NULL, 0), 0); + ASSERT_SIZE_T_GT(len, 8); + ASSERT_PTR_NE(sig, NULL); + ASSERT_INT_EQ(sshkey_verify(k, sig, len, d, l, NULL, 0, NULL), 0); + ASSERT_INT_NE(sshkey_verify(bad, sig, len, d, l, NULL, 0, NULL), 0); + /* Fuzz test is more comprehensive, this is just a smoke test */ + sig[len - 5] ^= 0x10; + ASSERT_INT_NE(sshkey_verify(k, sig, len, d, l, NULL, 0, NULL), 0); + free(sig); +} + +static void +banana(u_char *s, size_t l) +{ + size_t o; + const u_char the_banana[] = { 'b', 'a', 'n', 'a', 'n', 'a' }; + + for (o = 0; o < l; o += sizeof(the_banana)) { + if (l - o < sizeof(the_banana)) { + memcpy(s + o, "nanananana", l - o); + break; + } + memcpy(s + o, banana, sizeof(the_banana)); + } +} + +static void +signature_tests(struct sshkey *k, struct sshkey *bad, const char *sig_alg) +{ + u_char i, buf[2049]; + size_t lens[] = { + 1, 2, 7, 8, 9, 15, 16, 17, 31, 32, 33, 127, 128, 129, + 255, 256, 257, 1023, 1024, 1025, 2047, 2048, 2049 + }; + + for (i = 0; i < (sizeof(lens)/sizeof(lens[0])); i++) { + test_subtest_info("%s key, banana length %zu", + sshkey_type(k), lens[i]); + banana(buf, lens[i]); + signature_test(k, bad, sig_alg, buf, lens[i]); + } +} + +static struct sshkey * +get_private(const char *n) +{ + struct sshbuf *b; + struct sshkey *ret; + + b = load_file(n); + ASSERT_INT_EQ(sshkey_parse_private_fileblob(b, "", &ret, NULL), 0); + sshbuf_free(b); + return ret; +} + +void +sshkey_tests(void) +{ + struct sshkey *k1, *k2, *k3, *kf; +#ifdef WITH_OPENSSL + struct sshkey *k4, *kr, *kd; +#ifdef OPENSSL_HAS_ECC + struct sshkey *ke; +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + struct sshbuf *b; + + TEST_START("new invalid"); + k1 = sshkey_new(-42); + ASSERT_PTR_EQ(k1, NULL); + TEST_DONE(); + + TEST_START("new/free KEY_UNSPEC"); + k1 = sshkey_new(KEY_UNSPEC); + ASSERT_PTR_NE(k1, NULL); + sshkey_free(k1); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("new/free KEY_RSA"); + k1 = sshkey_new(KEY_RSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(k1->rsa, NULL); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("new/free KEY_DSA"); + k1 = sshkey_new(KEY_DSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(k1->dsa, NULL); + sshkey_free(k1); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("new/free KEY_ECDSA"); + k1 = sshkey_new(KEY_ECDSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_EQ(k1->ecdsa, NULL); /* Can't allocate without NID */ + sshkey_free(k1); + TEST_DONE(); +#endif + + TEST_START("new/free KEY_ED25519"); + k1 = sshkey_new(KEY_ED25519); + ASSERT_PTR_NE(k1, NULL); + /* These should be blank until key loaded or generated */ + ASSERT_PTR_EQ(k1->ed25519_sk, NULL); + ASSERT_PTR_EQ(k1->ed25519_pk, NULL); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("generate KEY_RSA too small modulus"); + ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 128, &k1), + SSH_ERR_KEY_LENGTH); + ASSERT_PTR_EQ(k1, NULL); + TEST_DONE(); + + TEST_START("generate KEY_RSA too large modulus"); + ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1 << 20, &k1), + SSH_ERR_KEY_LENGTH); + ASSERT_PTR_EQ(k1, NULL); + TEST_DONE(); + + TEST_START("generate KEY_DSA wrong bits"); + ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 2048, &k1), + SSH_ERR_KEY_LENGTH); + ASSERT_PTR_EQ(k1, NULL); + sshkey_free(k1); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("generate KEY_ECDSA wrong bits"); + ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 42, &k1), + SSH_ERR_KEY_LENGTH); + ASSERT_PTR_EQ(k1, NULL); + sshkey_free(k1); + TEST_DONE(); +#endif + + TEST_START("generate KEY_RSA"); + ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 767, &kr), + SSH_ERR_KEY_LENGTH); + ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &kr), 0); + ASSERT_PTR_NE(kr, NULL); + ASSERT_PTR_NE(kr->rsa, NULL); + ASSERT_PTR_NE(rsa_n(kr), NULL); + ASSERT_PTR_NE(rsa_e(kr), NULL); + ASSERT_PTR_NE(rsa_p(kr), NULL); + ASSERT_INT_EQ(BN_num_bits(rsa_n(kr)), 1024); + TEST_DONE(); + + TEST_START("generate KEY_DSA"); + ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0); + ASSERT_PTR_NE(kd, NULL); + ASSERT_PTR_NE(kd->dsa, NULL); + ASSERT_PTR_NE(dsa_g(kd), NULL); + ASSERT_PTR_NE(dsa_priv_key(kd), NULL); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("generate KEY_ECDSA"); + ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &ke), 0); + ASSERT_PTR_NE(ke, NULL); + ASSERT_PTR_NE(ke->ecdsa, NULL); + ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL); + ASSERT_PTR_NE(EC_KEY_get0_private_key(ke->ecdsa), NULL); + TEST_DONE(); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("generate KEY_ED25519"); + ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &kf), 0); + ASSERT_PTR_NE(kf, NULL); + ASSERT_INT_EQ(kf->type, KEY_ED25519); + ASSERT_PTR_NE(kf->ed25519_pk, NULL); + ASSERT_PTR_NE(kf->ed25519_sk, NULL); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("demote KEY_RSA"); + ASSERT_INT_EQ(sshkey_from_private(kr, &k1), 0); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(kr, k1); + ASSERT_INT_EQ(k1->type, KEY_RSA); + ASSERT_PTR_NE(k1->rsa, NULL); + ASSERT_PTR_NE(rsa_n(k1), NULL); + ASSERT_PTR_NE(rsa_e(k1), NULL); + ASSERT_PTR_EQ(rsa_p(k1), NULL); + TEST_DONE(); + + TEST_START("equal KEY_RSA/demoted KEY_RSA"); + ASSERT_INT_EQ(sshkey_equal(kr, k1), 1); + sshkey_free(k1); + TEST_DONE(); + + TEST_START("demote KEY_DSA"); + ASSERT_INT_EQ(sshkey_from_private(kd, &k1), 0); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(kd, k1); + ASSERT_INT_EQ(k1->type, KEY_DSA); + ASSERT_PTR_NE(k1->dsa, NULL); + ASSERT_PTR_NE(dsa_g(k1), NULL); + ASSERT_PTR_EQ(dsa_priv_key(k1), NULL); + TEST_DONE(); + + TEST_START("equal KEY_DSA/demoted KEY_DSA"); + ASSERT_INT_EQ(sshkey_equal(kd, k1), 1); + sshkey_free(k1); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("demote KEY_ECDSA"); + ASSERT_INT_EQ(sshkey_from_private(ke, &k1), 0); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(ke, k1); + ASSERT_INT_EQ(k1->type, KEY_ECDSA); + ASSERT_PTR_NE(k1->ecdsa, NULL); + ASSERT_INT_EQ(k1->ecdsa_nid, ke->ecdsa_nid); + ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL); + ASSERT_PTR_EQ(EC_KEY_get0_private_key(k1->ecdsa), NULL); + TEST_DONE(); + + TEST_START("equal KEY_ECDSA/demoted KEY_ECDSA"); + ASSERT_INT_EQ(sshkey_equal(ke, k1), 1); + sshkey_free(k1); + TEST_DONE(); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("demote KEY_ED25519"); + ASSERT_INT_EQ(sshkey_from_private(kf, &k1), 0); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(kf, k1); + ASSERT_INT_EQ(k1->type, KEY_ED25519); + ASSERT_PTR_NE(k1->ed25519_pk, NULL); + ASSERT_PTR_EQ(k1->ed25519_sk, NULL); + TEST_DONE(); + + TEST_START("equal KEY_ED25519/demoted KEY_ED25519"); + ASSERT_INT_EQ(sshkey_equal(kf, k1), 1); + sshkey_free(k1); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("equal mismatched key types"); + ASSERT_INT_EQ(sshkey_equal(kd, kr), 0); +#ifdef OPENSSL_HAS_ECC + ASSERT_INT_EQ(sshkey_equal(kd, ke), 0); + ASSERT_INT_EQ(sshkey_equal(kr, ke), 0); + ASSERT_INT_EQ(sshkey_equal(ke, kf), 0); +#endif /* OPENSSL_HAS_ECC */ + ASSERT_INT_EQ(sshkey_equal(kd, kf), 0); + TEST_DONE(); +#endif /* WITH_OPENSSL */ + + TEST_START("equal different keys"); +#ifdef WITH_OPENSSL + ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &k1), 0); + ASSERT_INT_EQ(sshkey_equal(kr, k1), 0); + sshkey_free(k1); + ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &k1), 0); + ASSERT_INT_EQ(sshkey_equal(kd, k1), 0); + sshkey_free(k1); +#ifdef OPENSSL_HAS_ECC + ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &k1), 0); + ASSERT_INT_EQ(sshkey_equal(ke, k1), 0); + sshkey_free(k1); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &k1), 0); + ASSERT_INT_EQ(sshkey_equal(kf, k1), 0); + sshkey_free(k1); + TEST_DONE(); + +#ifdef WITH_OPENSSL + sshkey_free(kr); + sshkey_free(kd); +#ifdef OPENSSL_HAS_ECC + sshkey_free(ke); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + sshkey_free(kf); + + TEST_START("certify key"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_1.pub"), + &k1, NULL), 0); + k2 = get_private("ed25519_2"); + ASSERT_INT_EQ(sshkey_to_certified(k1), 0); + ASSERT_PTR_NE(k1->cert, NULL); + k1->cert->type = SSH2_CERT_TYPE_USER; + k1->cert->serial = 1234; + k1->cert->key_id = strdup("estragon"); + ASSERT_PTR_NE(k1->cert->key_id, NULL); + k1->cert->principals = calloc(4, sizeof(*k1->cert->principals)); + ASSERT_PTR_NE(k1->cert->principals, NULL); + k1->cert->principals[0] = strdup("estragon"); + k1->cert->principals[1] = strdup("vladimir"); + k1->cert->principals[2] = strdup("pozzo"); + k1->cert->principals[3] = strdup("lucky"); + ASSERT_PTR_NE(k1->cert->principals[0], NULL); + ASSERT_PTR_NE(k1->cert->principals[1], NULL); + ASSERT_PTR_NE(k1->cert->principals[2], NULL); + ASSERT_PTR_NE(k1->cert->principals[3], NULL); + k1->cert->nprincipals = 4; + k1->cert->valid_after = 0; + k1->cert->valid_before = (u_int64_t)-1; + sshbuf_free(k1->cert->critical); + k1->cert->critical = sshbuf_new(); + ASSERT_PTR_NE(k1->cert->critical, NULL); + sshbuf_free(k1->cert->extensions); + k1->cert->extensions = sshbuf_new(); + ASSERT_PTR_NE(k1->cert->extensions, NULL); + put_opt(k1->cert->critical, "force-command", "/usr/bin/true"); + put_opt(k1->cert->critical, "source-address", "127.0.0.1"); + put_opt(k1->cert->extensions, "permit-X11-forwarding", NULL); + put_opt(k1->cert->extensions, "permit-agent-forwarding", NULL); + ASSERT_INT_EQ(sshkey_from_private(k2, &k1->cert->signature_key), 0); + ASSERT_INT_EQ(sshkey_certify(k1, k2, NULL, NULL, NULL), 0); + b = sshbuf_new(); + ASSERT_PTR_NE(b, NULL); + ASSERT_INT_EQ(sshkey_putb(k1, b), 0); + ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k3), 0); + + sshkey_free(k1); + sshkey_free(k2); + sshkey_free(k3); + sshbuf_reset(b); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("sign and verify RSA"); + k1 = get_private("rsa_1"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2, + NULL), 0); + signature_tests(k1, k2, "ssh-rsa"); + sshkey_free(k1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("sign and verify RSA-SHA256"); + k1 = get_private("rsa_1"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2, + NULL), 0); + signature_tests(k1, k2, "rsa-sha2-256"); + sshkey_free(k1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("sign and verify RSA-SHA512"); + k1 = get_private("rsa_1"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2, + NULL), 0); + signature_tests(k1, k2, "rsa-sha2-512"); + sshkey_free(k1); + sshkey_free(k2); + TEST_DONE(); + + TEST_START("sign and verify DSA"); + k1 = get_private("dsa_1"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_2.pub"), &k2, + NULL), 0); + signature_tests(k1, k2, NULL); + sshkey_free(k1); + sshkey_free(k2); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("sign and verify ECDSA"); + k1 = get_private("ecdsa_1"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_2.pub"), &k2, + NULL), 0); + signature_tests(k1, k2, NULL); + sshkey_free(k1); + sshkey_free(k2); + TEST_DONE(); +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + + TEST_START("sign and verify ED25519"); + k1 = get_private("ed25519_1"); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_2.pub"), &k2, + NULL), 0); + signature_tests(k1, k2, NULL); + sshkey_free(k1); + sshkey_free(k2); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("nested certificate"); + ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0); + ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2, + NULL), 0); + k3 = get_private("rsa_1"); + build_cert(b, k2, "ssh-rsa-cert-v01@openssh.com", k3, k1, NULL); + ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k4), + SSH_ERR_KEY_CERT_INVALID_SIGN_KEY); + ASSERT_PTR_EQ(k4, NULL); + sshkey_free(k1); + sshkey_free(k2); + sshkey_free(k3); + sshbuf_free(b); + TEST_DONE(); +#endif /* WITH_OPENSSL */ +} diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1 new file mode 100644 index 0000000000000000000000000000000000000000..d3f24824f8d51045beaca53b439fbe92463dbbc2 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1 @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBvAIBAAKBgQD6kutNFRsHTwEAv6d39Lhsqy1apdHBZ9c2HfyRr7WmypyGIy2m +Ka43vzXI8CNwmRSYs+A6d0vJC7Pl+f9QzJ/04NWOA+MiwfurwrR3CRe61QRYb8Py +mcHOxueHs95IcjrbIPNn86cjnPP5qvv/guUzCjuww4zBdJOXpligrGt2XwIVAKMD +/50qQy7j8JaMk+1+Xtg1pK01AoGBAO7l9QVVbSSoy5lq6cOtvpf8UlwOa6+zBwbl +o4gmFd1RwX1yWkA8kQ7RrhCSg8Hc6mIGnKRgKRli/3LgbSfZ0obFJehkRtEWtN4P +h8fVUeS74iQbIwFQeKlYHIlNTRoGtAbdi3nHdV+BBkEQc1V3rjqYqhjOoz/yNsgz +LND26HrdAoGBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxb +OXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joo +t+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAhRYIbQ5 +KfXsZuBPuWe5FJz3ldaEgw== +-----END DSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1-cert.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1-cert.fp new file mode 100644 index 0000000000000000000000000000000000000000..75ff0e9cd9f7555b118fd4a47f434536e5e86c2a --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1-cert.fp @@ -0,0 +1 @@ +SHA256:kOLgXSoAT8O5T6r36n5NJUYigbux1d7gdH/rmWiJm6s diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..e768db1e7bad029f0886cb34df97657c0d4f4042 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1-cert.pub @@ -0,0 +1 @@ +ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgdTlbNU9Hn9Qng3FHxwH971bxCIoq1ern/QWFFDWXgmYAAACBAPqS600VGwdPAQC/p3f0uGyrLVql0cFn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y4D4yLB+6vCtHcJF7rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0k5emWKCsa3ZfAAAAFQCjA/+dKkMu4/CWjJPtfl7YNaStNQAAAIEA7uX1BVVtJKjLmWrpw62+l/xSXA5rr7MHBuWjiCYV3VHBfXJaQDyRDtGuEJKDwdzqYgacpGApGWL/cuBtJ9nShsUl6GRG0Ra03g+Hx9VR5LviJBsjAVB4qVgciU1NGga0Bt2Lecd1X4EGQRBzVXeuOpiqGM6jP/I2yDMs0Pboet0AAACBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxbOXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joot+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAAAAAAAAAAYAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zdDEAAAAFaG9zdDIAAAAANowB8AAAAABNHmBwAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQAAAFMAAAALc3NoLWVkMjU1MTkAAABAh/z1LIdNL1b66tQ8t9DY9BTB3BQKpTKmc7ezyFKLwl96yaIniZwD9Ticdbe/8i/Li3uCFE3EAt8NAIv9zff8Bg== DSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.fp new file mode 100644 index 0000000000000000000000000000000000000000..75ff0e9cd9f7555b118fd4a47f434536e5e86c2a --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.fp @@ -0,0 +1 @@ +SHA256:kOLgXSoAT8O5T6r36n5NJUYigbux1d7gdH/rmWiJm6s diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..ba37776ee30a0ef982fc80ec72307c044e96c4bb --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.fp.bb @@ -0,0 +1 @@ +xetag-todiz-mifah-torec-mynyv-cyvit-gopon-pygag-rupic-cenav-bexax diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.g b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.g new file mode 100644 index 0000000000000000000000000000000000000000..e51c3f9fd1b4cbfcfe9a119416728a3efd7a0f71 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.g @@ -0,0 +1 @@ +00eee5f505556d24a8cb996ae9c3adbe97fc525c0e6bafb30706e5a3882615dd51c17d725a403c910ed1ae109283c1dcea62069ca460291962ff72e06d27d9d286c525e86446d116b4de0f87c7d551e4bbe2241b23015078a9581c894d4d1a06b406dd8b79c7755f81064110735577ae3a98aa18cea33ff236c8332cd0f6e87add diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.priv b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.priv new file mode 100644 index 0000000000000000000000000000000000000000..4f743314c76e87c0434ff8180280805f1fe16fe1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.priv @@ -0,0 +1 @@ +5821b43929f5ec66e04fb967b9149cf795d68483 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.pub new file mode 100644 index 0000000000000000000000000000000000000000..ba0313beec48bb197a7b600a9ef37ee2c1bc8533 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.param.pub @@ -0,0 +1 @@ +00e757a727e6a1b10168ea9902ebe08f53f4ba18c6d8fdf551fbabbf6d8558f054dc0f6aae4c5b397c04d0bc2f8c2bebb1057f96b621273fed8b2b38d1579a86e956644e520073171887fde4b88b4a0697323928ee3a28b7e2caf3896d2f29b067840c9d88e765249c95fd54bb240c714b5bdf8f88d2ef58727ca1a7699216c42d diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..41cae2f69f52a5996187c53efeb60f096073250a --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAPqS600VGwdPAQC/p3f0uGyrLVql0cFn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y4D4yLB+6vCtHcJF7rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0k5emWKCsa3ZfAAAAFQCjA/+dKkMu4/CWjJPtfl7YNaStNQAAAIEA7uX1BVVtJKjLmWrpw62+l/xSXA5rr7MHBuWjiCYV3VHBfXJaQDyRDtGuEJKDwdzqYgacpGApGWL/cuBtJ9nShsUl6GRG0Ra03g+Hx9VR5LviJBsjAVB4qVgciU1NGga0Bt2Lecd1X4EGQRBzVXeuOpiqGM6jP/I2yDMs0Pboet0AAACBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxbOXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joot+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQt DSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1_pw new file mode 100644 index 0000000000000000000000000000000000000000..24c73039fe1a4f1905240a0aa36d63f9b7747dfc --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_1_pw @@ -0,0 +1,15 @@ +-----BEGIN DSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,BC8386C373B22EB7F00ADC821D5D8BE9 + ++HDV2DQ09sxrIAeXTz9r3YFuPRa2hk1+NGcr3ETkXbC6KiZ14wpTnGTloKwaQjIW +eXTa9mpCOWAoohgvsVb+hOuOlP7AfeHu1IXV4EAS+GDpkiV5UxlCXXwqlD75Buu4 +wwDd/p4SWzILH3WGjDk5JIXoxWNY13LHwC7Q6gtGJx4AicUG7YBRTXMIBDa/Kh77 +6o2rFETKmp4VHBvHbakmiETfptdM8bbWxKWeY2vakThyESgeofsLoTOQCIwlEfJC +s2D/KYL65C8VbHYgIoSLTQnooO45DDyxIuhCqP+H23mhv9vB1Od3nc2atgHj/XFs +dcOPFkF/msDRYqxY3V0AS6+jpKwFodZ7g/hyGcyPxOkzlJVuKoKuH6P5PyQ69Gx0 +iqri0xEPyABr7kGlXNrjjctojX+B4WwSnjg/2euXXWFXCRalIdA7ErATTiQbGOx7 +Vd6Gn8PZbSy1MkqEDrZRip0pfAFJYI/8GXPC75BpnRsrVlfhtrngbW+kBP35LzaN +l2K+RQ3gSB3iFoqNb1Kuu6T5MZlyVl5H2dVlJSeb1euQ2OycXdDoFTyJ4AiyWS7w +Vlh8zeJnso5QRDjMwx99pZilbbuFGSLsahiGEveFc6o= +-----END DSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2 b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2 new file mode 100644 index 0000000000000000000000000000000000000000..3cc9631afa0f20a8019e3ae1bdb317ebeff6c28d --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2 @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBvQIBAAKBgQCbyPXNdHeLsjpobPVCMkfagBkt15Zsltqf/PGNP1y1cuz7rsTX +ZekQwUkSTNm5coqXe+ZOw2O4tjobJDd60I1/VPgaB0NYlQR9Hn87M284WD4f6VY+ +aunHmP134a8ybG5G4NqVNF3ihvxAR2pVITqb7kE46r2uYZNcNlHI8voRCwIVAMcP +bwqFNsQbH5pJyZW30wj4KVZ3AoGBAIK98BVeKQVf8qDFqx9ovMuNgVSxpd+N0Yta +5ZEy1OI2ziu5RhjueIM2K7Gq2Mnp38ob1AM53BUxqlcBJaHEDa6rj6yvuMgW9oCJ +dImBM8sIFxfBbXNbpJiMaDwa6WyT84OkpDE6uuAepTMnWOUWkUVkAiyokHDUGXkG +GyoQblbXAoGBAIsf7TaZ804sUWwRV0wI8DYx+hxD5QdrfYPYMtL2fHn3lICimGt0 +FTtUZ25jKg0E0DMBPdET6ZEHB3ZZkR8hFoUzZhdnyJMu3UjVtgaV88Ue3PrXxchk +0W2jHPaAgQU3JIWzo8HFIFqvC/HEL+EyW3rBTY2uXM3XGI+YcWSA4ZrZAhUAsY2f +bDFNzgZ4DaZ9wLRzTgOswPU= +-----END DSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.fp new file mode 100644 index 0000000000000000000000000000000000000000..51fbeb4d8ce1965167beaf8f684c2e3e7abbddd4 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.fp @@ -0,0 +1 @@ +SHA256:ecwhWcXgpdBxZ2e+OjpRRY7dqXHHCD62BGtoVQQBwCk diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..4d908ee309777e5db51d362bca57dba08afd0fa8 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.fp.bb @@ -0,0 +1 @@ +xeser-megad-pocan-rozit-belup-tapoh-fapif-kyvit-vonav-cehab-naxax diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..77bb555d595f2e825e91fbdeb92ec1b6154cabf5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_2.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAJvI9c10d4uyOmhs9UIyR9qAGS3XlmyW2p/88Y0/XLVy7PuuxNdl6RDBSRJM2blyipd75k7DY7i2OhskN3rQjX9U+BoHQ1iVBH0efzszbzhYPh/pVj5q6ceY/XfhrzJsbkbg2pU0XeKG/EBHalUhOpvuQTjqva5hk1w2Ucjy+hELAAAAFQDHD28KhTbEGx+aScmVt9MI+ClWdwAAAIEAgr3wFV4pBV/yoMWrH2i8y42BVLGl343Ri1rlkTLU4jbOK7lGGO54gzYrsarYyenfyhvUAzncFTGqVwElocQNrquPrK+4yBb2gIl0iYEzywgXF8Ftc1ukmIxoPBrpbJPzg6SkMTq64B6lMydY5RaRRWQCLKiQcNQZeQYbKhBuVtcAAACBAIsf7TaZ804sUWwRV0wI8DYx+hxD5QdrfYPYMtL2fHn3lICimGt0FTtUZ25jKg0E0DMBPdET6ZEHB3ZZkR8hFoUzZhdnyJMu3UjVtgaV88Ue3PrXxchk0W2jHPaAgQU3JIWzo8HFIFqvC/HEL+EyW3rBTY2uXM3XGI+YcWSA4ZrZ DSA test key #2 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_n b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_n new file mode 100644 index 0000000000000000000000000000000000000000..657624e0e72f235b054f74029c8ad5ade187d24b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_n @@ -0,0 +1,21 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABswAAAAdzc2gtZH +NzAAAAgQD6kutNFRsHTwEAv6d39Lhsqy1apdHBZ9c2HfyRr7WmypyGIy2mKa43vzXI8CNw +mRSYs+A6d0vJC7Pl+f9QzJ/04NWOA+MiwfurwrR3CRe61QRYb8PymcHOxueHs95IcjrbIP +Nn86cjnPP5qvv/guUzCjuww4zBdJOXpligrGt2XwAAABUAowP/nSpDLuPwloyT7X5e2DWk +rTUAAACBAO7l9QVVbSSoy5lq6cOtvpf8UlwOa6+zBwblo4gmFd1RwX1yWkA8kQ7RrhCSg8 +Hc6mIGnKRgKRli/3LgbSfZ0obFJehkRtEWtN4Ph8fVUeS74iQbIwFQeKlYHIlNTRoGtAbd +i3nHdV+BBkEQc1V3rjqYqhjOoz/yNsgzLND26HrdAAAAgQDnV6cn5qGxAWjqmQLr4I9T9L +oYxtj99VH7q79thVjwVNwPaq5MWzl8BNC8L4wr67EFf5a2ISc/7YsrONFXmobpVmROUgBz +FxiH/eS4i0oGlzI5KO46KLfiyvOJbS8psGeEDJ2I52UknJX9VLskDHFLW9+PiNLvWHJ8oa +dpkhbELQAAAdhWTOFbVkzhWwAAAAdzc2gtZHNzAAAAgQD6kutNFRsHTwEAv6d39Lhsqy1a +pdHBZ9c2HfyRr7WmypyGIy2mKa43vzXI8CNwmRSYs+A6d0vJC7Pl+f9QzJ/04NWOA+Miwf +urwrR3CRe61QRYb8PymcHOxueHs95IcjrbIPNn86cjnPP5qvv/guUzCjuww4zBdJOXplig +rGt2XwAAABUAowP/nSpDLuPwloyT7X5e2DWkrTUAAACBAO7l9QVVbSSoy5lq6cOtvpf8Ul +wOa6+zBwblo4gmFd1RwX1yWkA8kQ7RrhCSg8Hc6mIGnKRgKRli/3LgbSfZ0obFJehkRtEW +tN4Ph8fVUeS74iQbIwFQeKlYHIlNTRoGtAbdi3nHdV+BBkEQc1V3rjqYqhjOoz/yNsgzLN +D26HrdAAAAgQDnV6cn5qGxAWjqmQLr4I9T9LoYxtj99VH7q79thVjwVNwPaq5MWzl8BNC8 +L4wr67EFf5a2ISc/7YsrONFXmobpVmROUgBzFxiH/eS4i0oGlzI5KO46KLfiyvOJbS8psG +eEDJ2I52UknJX9VLskDHFLW9+PiNLvWHJ8oadpkhbELQAAABRYIbQ5KfXsZuBPuWe5FJz3 +ldaEgwAAAAAB +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_n_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_n_pw new file mode 100644 index 0000000000000000000000000000000000000000..24ac299a482d31c99c88e0b2f47037890a3d468e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/dsa_n_pw @@ -0,0 +1,21 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABCVs+LsMJ +wnB5zM9U9pTXrGAAAAEAAAAAEAAAGzAAAAB3NzaC1kc3MAAACBAPqS600VGwdPAQC/p3f0 +uGyrLVql0cFn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y +4D4yLB+6vCtHcJF7rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0 +k5emWKCsa3ZfAAAAFQCjA/+dKkMu4/CWjJPtfl7YNaStNQAAAIEA7uX1BVVtJKjLmWrpw6 +2+l/xSXA5rr7MHBuWjiCYV3VHBfXJaQDyRDtGuEJKDwdzqYgacpGApGWL/cuBtJ9nShsUl +6GRG0Ra03g+Hx9VR5LviJBsjAVB4qVgciU1NGga0Bt2Lecd1X4EGQRBzVXeuOpiqGM6jP/ +I2yDMs0Pboet0AAACBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxb +OXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joot+LK84 +ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAAAB4HiOcRW4w+sIqBL0 +TPVbf0glN1hUi0rcE63Pqxmvxb8LkldC4IxAUagPrjhNAEW2AY42+CvPrtGB1z7gDADAIW +xZX6wKwIcXP0Qh+xHE12F4u6mwfasssnAp4t1Ki8uCjMjnimgb3KdWpp0kiUV0oR062TXV +PAdfrWjaq4fw0KOqbHIAG/v36AqzuqjSTfDbqvLZM3y0gp2Q1RxaQVJA5ZIKKyqRyFX7sr +BaEIyCgeE3hM0EB7BycY1oIcS/eNxrACBWVJCENl5N7LtEYXNX7TANFniztfXzwaqGTT6A +fCfbW4gz1UKldLUBzbIrPwMWlirAstbHvOf/2Iay2pNAs/SHhI0aF2jsGfvv5/D6N+r9dG +B2SgDKBg7pywMH1DTvg6YT3P4GjCx0GUHqRCFLvD1rDdk4KSjvaRMpVq1PJ0/Wv6UGtsMS +TR0PaEHDRNZqAX4YxqujnWrGKuRJhuz0eUvp7fZvbWHtiAMKV7368kkeUmkOHanb+TS+zs +KINX8ev8zJZ6WVr8Vl+IQavpv0i2bXwS6QqbEuifpv/+uBb7pqRiU4u8en0eMdX1bZoTPM +R6xHCnGD/Jpb3zS91Ya57T6CiXZ12KCaL6nWGnCkZVpzkfJ2HjFklWSWBQ6uyaosDQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1 new file mode 100644 index 0000000000000000000000000000000000000000..80382b62d2db8dc563abe7949766710f268d3bfd --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPPNyUAnjvFr+eT/7t/IyjuQQd/aLFiTY92LB9gIjyrMoAoGCCqGSM49 +AwEHoUQDQgAEDFlblkOrW9ydKVhtM+9AY3c9saBE7SG3lFx38nBavkADDaI9jh3/ +kvG/Jt9vpm22qwoklTCGDfzCkXkIKaWlBw== +-----END EC PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp new file mode 100644 index 0000000000000000000000000000000000000000..e48304f6fcea977c268ea224e7242d6d4fdb4b15 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp @@ -0,0 +1 @@ +SHA256:8ty77fOpABat1y88aNdclQTfU+lVvWe7jYZGw8VYtfg diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..55e2a2568c153d274216fb50dd5c9eee5527e717 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1-cert.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgOtFRnMigkGliaYfPmX5IidVWfV3tRH6lqRXv0l8bvKoAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQcAAAAAAAAABwAAAAIAAAAGanVsaXVzAAAAEgAAAAVob3N0MQAAAAVob3N0MgAAAAA2jAHwAAAAAE0eYHAAAAAAAAAAAAAAAAAAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQcAAABkAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABJAAAAIHbxGwTnue7KxhHXGFvRcxBnekhQ3Qx84vV/Vs4oVCrpAAAAIQC7vk2+d14aS7td7kVXLQn392oALjEBzMZoDvT1vT/zOA== ECDSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.fp new file mode 100644 index 0000000000000000000000000000000000000000..e48304f6fcea977c268ea224e7242d6d4fdb4b15 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.fp @@ -0,0 +1 @@ +SHA256:8ty77fOpABat1y88aNdclQTfU+lVvWe7jYZGw8VYtfg diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..fa23c33c2967e8275c9d0d3c4681a4342b87a4f4 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.fp.bb @@ -0,0 +1 @@ +xibah-vocun-sogyn-byhen-rivem-hegyh-luneh-dozyr-vatyf-dufid-myxyx diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.curve b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.curve new file mode 100644 index 0000000000000000000000000000000000000000..fa04004670cc8a21870eb2c07debf93bdfedadf9 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.curve @@ -0,0 +1 @@ +prime256v1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.priv b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.priv new file mode 100644 index 0000000000000000000000000000000000000000..dc908ad7197a9632e4f091d58b9a8d6b229d8598 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.priv @@ -0,0 +1 @@ +00f3cdc940278ef16bf9e4ffeedfc8ca3b9041dfda2c589363dd8b07d8088f2acc diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.pub new file mode 100644 index 0000000000000000000000000000000000000000..71c9584d66f5b6dbd665a39f17041eb05454c8f7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.param.pub @@ -0,0 +1 @@ +040c595b9643ab5bdc9d29586d33ef4063773db1a044ed21b7945c77f2705abe40030da23d8e1dff92f1bf26df6fa66db6ab0a249530860dfcc291790829a5a507 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..84a71f976560c84e6f8748dfff55b15032390fa1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQc= ECDSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1_pw new file mode 100644 index 0000000000000000000000000000000000000000..5c83a658c2f4d5e255d23a7e8fae2f684e61cce3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_1_pw @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,7BA38DE00F67851E4207216809C3BB15 + +8QkFoZHQkj9a2mt032sp+WKaJ1fwteqWDd4RpAW9OzDgqzMx1QO43qJgBDTfhzjt +M2Q8YfiGjfBEYpg4kCbacfcV68DEV4z6Ll7rIzzzO7OfWUNL++brD64vKx4z6f46 ++sn4nbZTXilpkzi/nmPDVzrNmTSywA8T7Yf0QcBUxks= +-----END EC PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2 new file mode 100644 index 0000000000000000000000000000000000000000..0f4e844d1ead7a18a94625965ce9ed147ef8f38f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBqBtN7e6Essd3dlsgISViPCXXC0atlNkGtoMgSQdBTKVUfeJOi4lc +RZaXJdXnqWUqI/KEsH8h8QN4YcB8ugmAcc+gBwYFK4EEACOhgYkDgYYABAHZ2VNy +oDedBwqsdzY+kkNptc9DrtRCVmO6cULLj+691MhItqVqTMJbTFlI4MnAg9PoGTF/ +0KmLJfy8vSffXGKqqwGKcFNtd1XCo+7Qu9tXbxron9g6Dmu7y8jaLkixcwZwnwLs +6GmA9qZGuiAfOGV0Gf9/u98sr+vikOa4Ow5JFDTw5g== +-----END EC PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.fp new file mode 100644 index 0000000000000000000000000000000000000000..581e48aeca1efe7a5b880623f34b6f459f2f6d14 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.fp @@ -0,0 +1 @@ +SHA256:ed8YniRHA6qCrErCRnzrWxPHxYuA62a+CAFYUVxJgaI diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..e1cc664c2d658c140e48c58db97f8188e30b8045 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.fp.bb @@ -0,0 +1 @@ +xufag-danul-putub-mokin-pugaz-covid-dofag-nihuz-sysab-genar-zaxyx diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.curve b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.curve new file mode 100644 index 0000000000000000000000000000000000000000..617ea2fb8ca59576c9025a7cfe98e614e9c9f627 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.curve @@ -0,0 +1 @@ +secp521r1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.priv b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.priv new file mode 100644 index 0000000000000000000000000000000000000000..dd898d9ab9bbcbece1ac92a791832f1fec2d2917 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.priv @@ -0,0 +1 @@ +01a81b4dedee84b2c777765b202125623c25d70b46ad94d906b683204907414ca5547de24e8b895c45969725d5e7a9652a23f284b07f21f1037861c07cba098071cf diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.pub new file mode 100644 index 0000000000000000000000000000000000000000..94301c9918e503339c4bfad6b58877a10568c953 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.param.pub @@ -0,0 +1 @@ +0401d9d95372a0379d070aac77363e924369b5cf43aed4425663ba7142cb8feebdd4c848b6a56a4cc25b4c5948e0c9c083d3e819317fd0a98b25fcbcbd27df5c62aaab018a70536d7755c2a3eed0bbdb576f1ae89fd83a0e6bbbcbc8da2e48b17306709f02ece86980f6a646ba201f38657419ff7fbbdf2cafebe290e6b83b0e491434f0e6 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..be9d84bf521e8a7bc4ae5dd72064d48079759452 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_2.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHZ2VNyoDedBwqsdzY+kkNptc9DrtRCVmO6cULLj+691MhItqVqTMJbTFlI4MnAg9PoGTF/0KmLJfy8vSffXGKqqwGKcFNtd1XCo+7Qu9tXbxron9g6Dmu7y8jaLkixcwZwnwLs6GmA9qZGuiAfOGV0Gf9/u98sr+vikOa4Ow5JFDTw5g== ECDSA test key #2 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_n b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_n new file mode 100644 index 0000000000000000000000000000000000000000..9694f32e44074f518f2ecb1b1fc551135283768f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_n @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQMWVuWQ6tb3J0pWG0z70Bjdz2xoETt +IbeUXHfycFq+QAMNoj2OHf+S8b8m32+mbbarCiSVMIYN/MKReQgppaUHAAAAoFrmmZBa5p +mQAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlY +bTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQ +cAAAAhAPPNyUAnjvFr+eT/7t/IyjuQQd/aLFiTY92LB9gIjyrMAAAAAAECAwQFBgc= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_n_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_n_pw new file mode 100644 index 0000000000000000000000000000000000000000..36b7fa78772e59051236498dcdd6229f6bbb36dc --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_n_pw @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABC4UwEov5 +z0RrCm7AMCxbuiAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz +dHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvy +bfb6ZttqsKJJUwhg38wpF5CCmlpQcAAACgbCnAklQTHrf5qiHiMxKYwQJ7k/X9mp4fXD4v +xUbgNZiXSxN26mn8mC2rH+WA6Lk3CexR/hrtLI2ndpBsYu1h6HhVkOwwm3Kd/PMKArCupW +l6sYEabrT0EghXR/3aDEZvj79hgKSdu3RpayLvMdbCR8k1cg0/mDmR9hicWfeJ61n/IH05 +tUR268+0BVRW9kDhh/cuv8tVY4L09jCCQ6CpsA== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1 new file mode 100644 index 0000000000000000000000000000000000000000..b51fb73d638613231a1cd7ed3477c114a4d93d69 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1 @@ -0,0 +1,13 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2 +RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQRnVT5Cji1D +Ge2+q2X0vATh6LYnODV+DJrshJorr5GnipW29RfuaDXs0WB6XBej9dOLazVRDjQrtV19Qg +O6cfkFAAAABHNzaDoAAAGQuPdnP7j3Zz8AAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv +cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEEZ1U+Qo4tQxntvqtl9LwE4ei2Jzg1fgya7I +SaK6+Rp4qVtvUX7mg17NFgelwXo/XTi2s1UQ40K7VdfUIDunH5BQAAAARzc2g6AQAAAOMt +LS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUhjQ0FRRUVJRURmVFB4YzA0alN5Zk +Z5NlhoV1pTVlpzcnU5ZFlaSVpTOWhjeVFhcDlVT29Bb0dDQ3FHU000OQpBd0VIb1VRRFFn +QUVaMVUrUW80dFF4bnR2cXRsOUx3RTRlaTJKemcxZmd5YTdJU2FLNitScDRxVnR2VVg3bW +cxCjdORmdlbHdYby9YVGkyczFVUTQwSzdWZGZVSUR1bkg1QlE9PQotLS0tLUVORCBFQyBQ +UklWQVRFIEtFWS0tLS0tCgAAAAAAAAAURUNEU0EtU0sgdGVzdCBrZXkgIzEBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1-cert.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1-cert.fp new file mode 100644 index 0000000000000000000000000000000000000000..d1921451d740b3c3c614bb7af15e1af19a99c42a --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1-cert.fp @@ -0,0 +1 @@ +SHA256:Go7HO0CVPYG+BSDSk9ZUJBKGSrtBExp6obTa9iqzIUo diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..9586c61a7d69f84815dced911d190854a263cc3c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1-cert.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAK3NrLWVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgE012YoSBE9hEC2FRzblcSx784JNo2A4g611A7I75YMMAAAAIbmlzdHAyNTYAAABBBGdVPkKOLUMZ7b6rZfS8BOHotic4NX4MmuyEmiuvkaeKlbb1F+5oNezRYHpcF6P104trNVEONCu1XX1CA7px+QUAAAAEc3NoOgAAAAAAAAAHAAAAAgAAAAZqdWxpdXMAAAASAAAABWhvc3QxAAAABWhvc3QyAAAAADaLg2AAAAAATR3h4AAAAAAAAAAAAAAAAAAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEEAlTtPiWUHubBeCys4Xp0QF91dYARpkyqtCnzg10HRS+ZDgkMrSUvPPG+Ge8iqtnB951MBxDq9FqDFIkhQBYXDAAAAGQAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAEkAAAAhALY+eXRJjVGnMk38Sm5S+H5CloNq757ypsoxt+WYoadtAAAAIA42/mAhUfLij1GY7wl+OFrI+icB/t4tGiEUZmhx6Foo ECDSA-SK test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.fp new file mode 100644 index 0000000000000000000000000000000000000000..d1921451d740b3c3c614bb7af15e1af19a99c42a --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.fp @@ -0,0 +1 @@ +SHA256:Go7HO0CVPYG+BSDSk9ZUJBKGSrtBExp6obTa9iqzIUo diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..cb9f4dd0dc8de7b605eaad5a68f18778969605ee --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.fp.bb @@ -0,0 +1 @@ +xovem-sacac-dageg-vovoc-symyz-bozal-cibiv-cyvat-vylyn-romib-hoxax diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.pub new file mode 100644 index 0000000000000000000000000000000000000000..c3b21e02b1f3c259b8d7b98bb78936bb7e805e92 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGdVPkKOLUMZ7b6rZfS8BOHotic4NX4MmuyEmiuvkaeKlbb1F+5oNezRYHpcF6P104trNVEONCu1XX1CA7px+QUAAAAEc3NoOg== ECDSA-SK test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1_pw new file mode 100644 index 0000000000000000000000000000000000000000..4fa23a7383e3465698874ca9806e3a740efead2d --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk1_pw @@ -0,0 +1,14 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB6vcJVx2 +cPc7yYRROup8VnAAAAEAAAAAEAAAB/AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3Bl +bnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGdVPkKOLUMZ7b6rZfS8BOHotic4NX4MmuyEmi +uvkaeKlbb1F+5oNezRYHpcF6P104trNVEONCu1XX1CA7px+QUAAAAEc3NoOgAAAZBrvCxe +xFz0bvzXwaPhrUHBeNCoZy/wNKDx0kxlxUPuA+lgOvy5l3lT3yxxd0qj5PQB+NTcuz8AAE +1f7aSWQNZSifox3COsBGoHV9C8i+glcxiBKheAZD+EBnRGjG8kbcaLhuYDW/I39qNe8lHW +YSDjmvsT55Hy0IAtVRAXizDoXKNdFPTZisC67WyOSJ3ED7Fy4bfT4ApbvhoFTwjikZBEhy +LOad1sbJa4eT19TsskYfQdnJf8sjAmCMOZY4ZV0FiNW5XZOp8nIal1oyULPfzTAm6oaeFN +0ImCSU3U8h4wUQ8q/3XvBWtTKycZaoou0AwPoP0QN95Ywte7FHezNPb/n8KD7k0S6h9XAX +UcBeCe5NHyov/0ZzA2p737hzm3w+MXGOboTQMu8WFXeGh4m7QH2o8ZJdgBhM5JF17uii+Q +ppGoPWHf33MXwB3wxWmKZ0ua0f9AVLkQ2DfFszUoBJE/kcHRd4kj4Q4FWXeMBN0GoH8gdE +gRWIlxn2/FAOce/BFPzzdP87H0jwz7SdcuVO1L +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2 new file mode 100644 index 0000000000000000000000000000000000000000..19db5a3f5690e5c3e12f6b3f4da5818994e55df2 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2 @@ -0,0 +1,13 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2 +RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQSTl+SR6rTg +lOZmcQkCtJ3Pd+lWinezo/gHk4oZdZcTQsmEYs766BlWGuB2Bz3qQRLa6cXsP+4K9kAjAJ +7zdoFUAAAABHNzaDoAAAGQ1qllJtapZSYAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv +cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEEk5fkkeq04JTmZnEJArSdz3fpVop3s6P4B5 +OKGXWXE0LJhGLO+ugZVhrgdgc96kES2unF7D/uCvZAIwCe83aBVAAAAARzc2g6AQAAAOMt +LS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUhjQ0FRRUVJSkxwVkxnSTVvdkRlOW +VMWmZodCs5WWlMaitnam0rTXhHTXg5NndiRWw0Wm9Bb0dDQ3FHU000OQpBd0VIb1VRRFFn +QUVrNWZra2VxMDRKVG1abkVKQXJTZHozZnBWb3AzczZQNEI1T0tHWFdYRTBMSmhHTE8rdW +daClZocmdkZ2M5NmtFUzJ1bkY3RC91Q3ZaQUl3Q2U4M2FCVkE9PQotLS0tLUVORCBFQyBQ +UklWQVRFIEtFWS0tLS0tCgAAAAAAAAAURUNEU0EtU0sgdGVzdCBrZXkgIzIBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.fp new file mode 100644 index 0000000000000000000000000000000000000000..1bc99ea0d7a4307c605ad7c132df15351bdc9def --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.fp @@ -0,0 +1 @@ +SHA256:pz8VkgtRY3r50F4zSuzRlmq9c6vPTpJXLKKOgkyUcKE diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..bfee7658a6063e57f56e6f7ca33ccfef15e3b305 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.fp.bb @@ -0,0 +1 @@ +xobel-gavur-gorym-pedop-rarob-bunek-gucer-lofeg-syhaf-fylur-zoxix diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.pub new file mode 100644 index 0000000000000000000000000000000000000000..2629d9509ed2b0055b93e9969821fe1ff1efb0d1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ecdsa_sk2.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBJOX5JHqtOCU5mZxCQK0nc936VaKd7Oj+AeTihl1lxNCyYRizvroGVYa4HYHPepBEtrpxew/7gr2QCMAnvN2gVQAAAAEc3NoOg== ECDSA-SK test key #2 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1 new file mode 100644 index 0000000000000000000000000000000000000000..6b0ae01cf6892de68ac21f130ab4da0b09a6bea0 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQAAAJjnj4Ao54+A +KAAAAAtzc2gtZWQyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQ +AAAED3KgoDbjR54V7bdNpfKlQY5m20UK1QaHytkCR+6rZEDFOG6kY7Rf4UtCFvPwKgo/Bz +tXck2xC4a2WyA34XtIwZAAAAE0VEMjU1MTkgdGVzdCBrZXkgIzEBAg== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1-cert.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1-cert.fp new file mode 100644 index 0000000000000000000000000000000000000000..a9674e2b02b6850d41ac11029daaa2711ffb8026 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1-cert.fp @@ -0,0 +1 @@ +SHA256:L3k/oJubblSY0lB9Ulsl7emDMnRPKm/8udf2ccwk560 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..649b4e80452d1a05ccc8eee7171bf5c8a36bc9c1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1-cert.pub @@ -0,0 +1 @@ +ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIxzuxl4z3uwAIslne8Huft+1n1IhHAlNbWZkQyyECCGAAAAIFOG6kY7Rf4UtCFvPwKgo/BztXck2xC4a2WyA34XtIwZAAAAAAAAAAgAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zdDEAAAAFaG9zdDIAAAAANowB8AAAAABNHmBwAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQAAAFMAAAALc3NoLWVkMjU1MTkAAABABGTn+Bmz86Ajk+iqKCSdP5NClsYzn4alJd0V5bizhP0Kumc/HbqQfSt684J1WdSzih+EjvnTgBhK9jTBKb90AQ== ED25519 test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.fp new file mode 100644 index 0000000000000000000000000000000000000000..a9674e2b02b6850d41ac11029daaa2711ffb8026 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.fp @@ -0,0 +1 @@ +SHA256:L3k/oJubblSY0lB9Ulsl7emDMnRPKm/8udf2ccwk560 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..309f2dafc56a705fe61fd9156e99c670c85db62e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.fp.bb @@ -0,0 +1 @@ +xubop-rekyd-bakal-nubuf-pahaf-gicuh-logeb-gocif-petod-galip-fuxux diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..e533059eaab6feabdf668a7e3d77409af4fe6a73 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFOG6kY7Rf4UtCFvPwKgo/BztXck2xC4a2WyA34XtIwZ ED25519 test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1_pw new file mode 100644 index 0000000000000000000000000000000000000000..da94d2b8e2e4bb8b992be9429ab7904867740944 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_1_pw @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDKT56mBA +tXIMsWqmuuA2gdAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIFOG6kY7Rf4UtCFv +PwKgo/BztXck2xC4a2WyA34XtIwZAAAAoC13U47yfUOSZJePNUAwWXuFOk3aOKwPM5PMvK +0zwRnMZZjgn+tsMAYPwhsT3Mx3h5QzvVGFyFEqsiK7j4vAotD+LVQeBN5TwWbUBx4lnoGs +3iAfYVDakO/gNvVBDDGOqv5kdCc4cgn5HacjHQLKOAx6KzHe7JFn7uCywMdVVQjlpI6LHb +mHkaKiVX/C2oiRnsoe17HZ8Fxyt3vd1qNM8BE= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2 new file mode 100644 index 0000000000000000000000000000000000000000..e4aed6398d21d05b7c7b0eda4ec9124d8aac995c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACDPVKyLnm3eZE0lm0IfM3Uy9AsdGSBtozcoCt21blYBCwAAAJix1mBGsdZg +RgAAAAtzc2gtZWQyNTUxOQAAACDPVKyLnm3eZE0lm0IfM3Uy9AsdGSBtozcoCt21blYBCw +AAAECZEQHXs18o3DKjhUYaTyt+bUbhqfMeqmsKjYyFvzGVgs9UrIuebd5kTSWbQh8zdTL0 +Cx0ZIG2jNygK3bVuVgELAAAAE0VEMjU1MTkgdGVzdCBrZXkgIzEBAg== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.fp new file mode 100644 index 0000000000000000000000000000000000000000..04966267257fb2e2386964eb3cff4cbfab36df34 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.fp @@ -0,0 +1 @@ +SHA256:vMbaARqVciRgXyZPNHDo+P5p5WK5yWG1Oo6VC35Bomw diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..abba78901548de3911cfaf523c0897064959b067 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.fp.bb @@ -0,0 +1 @@ +xuces-bapyb-vikob-zesyv-budod-nupip-kebon-tacyc-fofed-lezic-soxax diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..af34236e712d47f0e2fc0119c69752ac68208181 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_2.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9UrIuebd5kTSWbQh8zdTL0Cx0ZIG2jNygK3bVuVgEL ED25519 test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1 new file mode 100644 index 0000000000000000000000000000000000000000..4196d9c6a2defbc52fb0fa0405ffec774970bfbe --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1 @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2 +gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACAhaP5OS1PPOt7uumAvXlDtte9EHbqIT1EZEJ2y +2v3XMwAAAARzc2g6AAAAuBocY6UaHGOlAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2 +9tAAAAICFo/k5LU8863u66YC9eUO2170QduohPURkQnbLa/dczAAAABHNzaDoBAAAAQJYq +lGHhFoA25/q8X/rdTqDAb7dhqs4ehhd/w8x99CwiIWj+TktTzzre7rpgL15Q7bXvRB26iE +9RGRCdstr91zMAAAAAAAAAFkVEMjU1MTktU0sgdGVzdCBrZXkgIzEBAgM= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1-cert.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1-cert.fp new file mode 100644 index 0000000000000000000000000000000000000000..a6bb1a99cb3289925ee06821b05619f46951985c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1-cert.fp @@ -0,0 +1 @@ +SHA256:6WZVJ44bqhAWLVP4Ns0TDkoSQSsZo/h2K+mEvOaNFbw diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..3c72c268df9459713a6dfb095d84c2a9223383d7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1-cert.pub @@ -0,0 +1 @@ +sk-ssh-ed25519-cert-v01@openssh.com AAAAI3NrLXNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJr7CuMntQKvHoUshx374fJLFEkyxKsEOBA1H6hk5scoAAAAICFo/k5LU8863u66YC9eUO2170QduohPURkQnbLa/dczAAAABHNzaDoAAAAAAAAACAAAAAIAAAAGanVsaXVzAAAAEgAAAAVob3N0MQAAAAVob3N0MgAAAAA2i4NgAAAAAE0d4eAAAAAAAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIOo/0xneV3iM2qWEo5RUwvUYa2bjff292T5vvuXRomGQAAAAUwAAAAtzc2gtZWQyNTUxOQAAAECgsRGLDh1SI3m66MRp9D2iLP4wabQ0OrDgGidk7LsVn2XZHV5jBZN1RtNfe6PBMeVzfRtGUzOg18sO7H7uU+EC ED25519-SK test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.fp new file mode 100644 index 0000000000000000000000000000000000000000..a6bb1a99cb3289925ee06821b05619f46951985c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.fp @@ -0,0 +1 @@ +SHA256:6WZVJ44bqhAWLVP4Ns0TDkoSQSsZo/h2K+mEvOaNFbw diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..1bfe20a4803acdd86ccf2fe7a8c414a2be0d62d0 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.fp.bb @@ -0,0 +1 @@ +xucac-vusip-tydoz-dudad-nerif-raran-tezun-cogyd-pamoh-bahef-ruxix diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.pub new file mode 100644 index 0000000000000000000000000000000000000000..60fe00c3949b3ebba61564586eb019615f39691f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1.pub @@ -0,0 +1 @@ +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAICFo/k5LU8863u66YC9eUO2170QduohPURkQnbLa/dczAAAABHNzaDo= ED25519-SK test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1_pw new file mode 100644 index 0000000000000000000000000000000000000000..1c29ff07fe5cafb0013ffd4c485ba5eaa824306d --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk1_pw @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDr5R9Yf/ +ucEh0Ns6c34tcIAAAAEAAAAAEAAABKAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t +AAAAICFo/k5LU8863u66YC9eUO2170QduohPURkQnbLa/dczAAAABHNzaDoAAADA2T6owx +OSgKz4DvLnS3UJ/renbuew5mbkIWB1/y8xd3y5Usm08iUCAlKxep9dVRQvmyoTrc/7rHOM +DkokNw+WgKambnlYT/9QfqViZ9iCBtbdmhLM6ksUCgQefvquRyXoJxlWstjXUll6Ru+ZbT +H//Ss8C1bYtAiXR68OQ+rhDrvQxA9P8J1sGIlkuV3h8YXddSpyBW2Sn0LTHHBXYZo86cXZ +G4Lnc8aGYm65eqdHgkfRmht3eS8DTdzEBfBNH5Ml +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2 b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2 new file mode 100644 index 0000000000000000000000000000000000000000..b9b748966bcacc5633548e704632f6a1cf75d503 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2 @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2 +gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACAV8fu1Sc31QLK2R/zGPdN3ve5xuFvDc7mEAWxb +aI+YcwAAAARzc2g6AAAAuJCMX5uQjF+bAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2 +9tAAAAIBXx+7VJzfVAsrZH/MY903e97nG4W8NzuYQBbFtoj5hzAAAABHNzaDoBAAAAQObE +PajcKI1W30EKOhBb6u+Fgx464kf7EjnqDSg4l7gAFfH7tUnN9UCytkf8xj3Td73ucbhbw3 +O5hAFsW2iPmHMAAAAAAAAAFkVEMjU1MTktU0sgdGVzdCBrZXkgIzIBAgM= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.fp new file mode 100644 index 0000000000000000000000000000000000000000..1c4369a00768f0c69f0aa156a779bfff2779b370 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.fp @@ -0,0 +1 @@ +SHA256:b9BVPS5vuU4yu/FgweojLLg6zbfmBBoWLUgibdxxsoo diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..f5fd9efd8f9f052154f5f43898da7e0686ed077b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.fp.bb @@ -0,0 +1 @@ +xemac-tizim-dihep-supar-zupib-cukak-pasis-febeg-dyguv-hutec-dyxox diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.pub new file mode 100644 index 0000000000000000000000000000000000000000..c7ed9f524a491e470cf9397ae65e379114d8186f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/ed25519_sk2.pub @@ -0,0 +1 @@ +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBXx+7VJzfVAsrZH/MY903e97nG4W8NzuYQBbFtoj5hzAAAABHNzaDo= ED25519-SK test key #2 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/pw new file mode 100644 index 0000000000000000000000000000000000000000..8a1dff98aa920a0e1d9fcd9d16209daffa405de9 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/pw @@ -0,0 +1 @@ +mekmitasdigoat diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1 new file mode 100644 index 0000000000000000000000000000000000000000..5de3f8422e897ed7637e756b877084f1cd6e878e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u +d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd +NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB +AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ +GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj +Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv +Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i +PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa +WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj +mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi +C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e +NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/ +h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs= +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1-cert.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1-cert.fp new file mode 100644 index 0000000000000000000000000000000000000000..79f380a250857967a783fae53830b73e90da5cd9 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1-cert.fp @@ -0,0 +1 @@ +SHA256:l6itGumSMcRBBAFteCgmjQBIXqLK/jFGUH3viHX1RmE diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..3bacf3c8fe5b01fa4a511551b05affcb9ae832a5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1-cert.pub @@ -0,0 +1 @@ +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg98LhS2EHxLOWCLopZPwHdg/RJXusnkOqQXSc9R7aITkAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wAAAAAAAAAFAAAAAgAAAAZqdWxpdXMAAAASAAAABWhvc3QxAAAABWhvc3QyAAAAADaMAfAAAAAATR5gcAAAAAAAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgU4bqRjtF/hS0IW8/AqCj8HO1dyTbELhrZbIDfhe0jBkAAABTAAAAC3NzaC1lZDI1NTE5AAAAQI3QGlUCzC07KorupxpDkkGy6tniaZ8EvBflzvv+itXWNchGvfUeHmVT6aX0sRqehdz/lR+GmXRoZBhofwh0qAM= RSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.fp new file mode 100644 index 0000000000000000000000000000000000000000..79f380a250857967a783fae53830b73e90da5cd9 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.fp @@ -0,0 +1 @@ +SHA256:l6itGumSMcRBBAFteCgmjQBIXqLK/jFGUH3viHX1RmE diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..45bacd5193e3987b3d6cc3bbed7e6afeb860b134 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.fp.bb @@ -0,0 +1 @@ +xosis-fodod-votot-dibum-ryvac-rediz-naruf-votun-kevis-halis-gexux diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.n b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.n new file mode 100644 index 0000000000000000000000000000000000000000..4933712134154f52a7990de727fed78a505a8e59 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.n @@ -0,0 +1 @@ +00cb5799544edec5ac00ec781fc21a1119ce9a288e3116e72f3e78fbcba6998adcc98c235f2e77abf1ce92b76f064b624552c9f2582341e622e1a176eef232b5bac1bf3881babc0b7d57a1ef4439170852e192bc329d3523354a39610eab916e50c507c913a2a5f2c7596aad779c5f297121438bd2313ebb4ad4d7debba43271fb diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.p b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.p new file mode 100644 index 0000000000000000000000000000000000000000..4783d215e8c2d3c6d85dd74524fd4a62bb1b29a9 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.p @@ -0,0 +1 @@ +00f56077ef05be4574906f52175ed8897e8da1626bc9b055534d6a20f6102c57c7a0b31b3e7864b101f4d3d57c35192bc949e14243a9e956361de23c5dd179f583 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.q b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.q new file mode 100644 index 0000000000000000000000000000000000000000..00fc8a2dfe443fa9b5bc036202f6981bcdc452e4 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.param.q @@ -0,0 +1 @@ +00d42541cc014be26370a657953f3adfcbaf713f781639adba33133f184eb3c7d8c2ec7810dca3da58a746fc5b87b34f396934436832e619c67c22f32ebbb36029 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.pub new file mode 100644 index 0000000000000000000000000000000000000000..23ef872e0f58c802720bbcdb8cca8b28d5799ecc --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+w== RSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_pw new file mode 100644 index 0000000000000000000000000000000000000000..b4c067458283fea6afd4f3f58681f669e5603afd --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_pw @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,0C3F819F6EEA66A471BAEEDDA8171606 + +AhQNxgw7Z2un3dpm6KPHF1u5qVvOczm0yiTyPK4U11B3TTRhXOHdzPLAcKMX71Xq +fmLm2/JIZATUbLTaysLKIQlmAgtpmXoKLv9b90R3AXLophgToZzOLpvlQTCt+y9G +0E3QQZG/LFy9BLNyw6uD5cy0RHT3FQb5VQDwfBvR/I+K3qWBFLlb7Rw9bCujYczu +D3bimcDj/k6YkrWVsEa81Ch5RF2RClOYufti6bsvc4xIsB0Kd++vokER+kXFuQqf +Tl0Jz+SG0kr9QtjVvkhBtSxzJ6/olAosoUySQ5hqsB8iECufBgp1KelXqsHFJQXy +gCvVmGiivFUinX0rKOuWCHTplsSKQ9BnPSwDAAs8A7ZLcTXcLs/hMQ5r6fmOYfNN +YthhjZyE2ciJO0lydGJUJMb5aJUak0rl+uINRlYCHTRLVwmCOmpfqz9SfcJb1ieU +4Us8NR+pXJar4U0+C2wVlNJkAdpL6GvYxN6vp7vLa+BiFwIZOQozswacIZk/ScXm +QL9rmWug51RCmDeenX46WTEZeB0o0+xi60sDEDhhe4+iNYcJu5L0BJ5lqRFe3I5n +HRRv1mBEjbF2fDcg/ChYfOXsc4gDivH2nObabeASuMFZyadmXfA8tnXRZf+7Wuy/ +LZGYbM2xLeEyV3ss16WBHuIqexDt04OEZvs0jN90zj6Yv7qKCB975bdOcuKkN2Nn +n9lA11R2pgsCs6COp9rYiWXkXZeDf3sW6kdcEV+/SzkVsv4JlHcsIzgk4WGVF/E/ +ZkU4J9AvSdJPzEQDM+yszp0eeUow4+SAgpuNTqZiUO/2UUVbsr3qvlYMoCixhFAN +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1 b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1 new file mode 100644 index 0000000000000000000000000000000000000000..5de3f8422e897ed7637e756b877084f1cd6e878e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u +d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd +NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB +AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ +GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj +Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv +Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i +PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa +WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj +mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi +C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e +NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/ +h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs= +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..ff49d7598dde1de6824e864dd8d2a0fa17c3a1cb --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub @@ -0,0 +1 @@ +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgy5PGFfSaEuSuXsjvKlMZGXYD0xlnqdZftuW9tMkUYz4AAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wAAAAAAAAABAAAAAQAAAARodWdvAAAAEgAAAAV1c2VyMQAAAAV1c2VyMgAAAAA2i4NgAAAAAE0d4eAAAABEAAAADWZvcmNlLWNvbW1hbmQAAAALAAAABy9iaW4vbHMAAAAOc291cmNlLWFkZHJlc3MAAAAOAAAACjEwLjAuMC4wLzgAAABkAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQD00RRenvxICSYvj54CPiYHM86OT5xwI9XORNH6Zkl3JPCQkAEdQ3hyfhraROaHsSv43wJcKyKrEg5XUZ8fZ/BoKIGU4Rd5AmL9wyPGv2RVY7gWELqXVSpu89R2tQJRmMVMD38CH0wqCTuoZirlKMTen6yfgYuFEpuqar0uOIeAyaQG6/9rVKWK36tcfM7YXx8fmGSN4eK/JhWDDjlo28YJ7ZFF9umh5baZG2Ai/vL3BJ7C3pqaEQNdKj8XqaSoDvFWKfOujk1620Rcuj3W0D0dvp/rH8xz8YkM1dMqGlYIZ4nrF5acB58Nk5FYBjtj1hu4DGEQlWL1Avk1agU4DQLrAAABDwAAAAdzc2gtcnNhAAABAF5BtPY8FbmIekK/zNq6/Lp5agKT5zEVxqAyZKhp75bLRP+kOMZBVB9ZWrekZk6IAVAOCZGQzTsD4mxIQsxBLl8k5hvEWb90/+w9/BzW9ScOGQe+y0COa0QWWR7L3k1S8WX2oAGvtDWOj7Md85nij4ZSU9/QQQFVDF8VilWPSMxUf/3I1fqyDq7AWcZkGk/bFUN6K6RsCSxIPlGmKt0IauyvSMI2IT0XeRT242RngeeUW8vFrn9TXy9YxJRW+cSeLKCuu8agBYyQMXWQ+q39eZZqVYSoo7nFEEhtaLs8d6jzgGkcE9wGJ9KLgfY/mG2vX3gI4IzncKkVJRoeiDzXFIk= RSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1.pub new file mode 100644 index 0000000000000000000000000000000000000000..23ef872e0f58c802720bbcdb8cca8b28d5799ecc --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+w== RSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512 b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512 new file mode 100644 index 0000000000000000000000000000000000000000..5de3f8422e897ed7637e756b877084f1cd6e878e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u +d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd +NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB +AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ +GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj +Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv +Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i +PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa +WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj +mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi +C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e +NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/ +h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs= +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub new file mode 100644 index 0000000000000000000000000000000000000000..47451968fb8bb3fe6a48e23783909213404133c3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub @@ -0,0 +1 @@ +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg/bUEmnMYHxlv1N7iXvnYPYdzDjlTRKoaIGEPkaQQQDwAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wAAAAAAAAABAAAAAQAAAARodWdvAAAAEgAAAAV1c2VyMQAAAAV1c2VyMgAAAAA2i4NgAAAAAE0d4eAAAABEAAAADWZvcmNlLWNvbW1hbmQAAAALAAAABy9iaW4vbHMAAAAOc291cmNlLWFkZHJlc3MAAAAOAAAACjEwLjAuMC4wLzgAAABkAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQD00RRenvxICSYvj54CPiYHM86OT5xwI9XORNH6Zkl3JPCQkAEdQ3hyfhraROaHsSv43wJcKyKrEg5XUZ8fZ/BoKIGU4Rd5AmL9wyPGv2RVY7gWELqXVSpu89R2tQJRmMVMD38CH0wqCTuoZirlKMTen6yfgYuFEpuqar0uOIeAyaQG6/9rVKWK36tcfM7YXx8fmGSN4eK/JhWDDjlo28YJ7ZFF9umh5baZG2Ai/vL3BJ7C3pqaEQNdKj8XqaSoDvFWKfOujk1620Rcuj3W0D0dvp/rH8xz8YkM1dMqGlYIZ4nrF5acB58Nk5FYBjtj1hu4DGEQlWL1Avk1agU4DQLrAAABFAAAAAxyc2Etc2hhMi01MTIAAAEA7/GoZsJqrq4xYotsRbpM8arZDjCzT6kohXeD/GVy26s5E/YWXRYCrOMIzSZxjuN5rAaNRW8ffxq14JyI94566Kg2OeoxQ6rK/dTqkk7I1RyypSXunT3I4++RPs1Q+hu9eS/WBzur0/D3dMejhuc3IBg6iB0481I4pGBGcD8/KjQFfhlCuGVXwB1ALk2zfXFT1HYYrs6bYZuQqjgvArnjYJ0do3fTSDC20/ydV4BHnI3fVAY2THVjX45V2ppPadl/rpczaJqW1ZtpnpJkV8Un316stQSD0xLHUDjp89O6d9Yq5S0kDdfwTRJIPm9f2cGNakJwN5qzmmmdDroRKODYcg== RSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512.pub new file mode 100644 index 0000000000000000000000000000000000000000..23ef872e0f58c802720bbcdb8cca8b28d5799ecc --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_1_sha512.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+w== RSA test key #1 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2 b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2 new file mode 100644 index 0000000000000000000000000000000000000000..2441d52bf5520ba0c1bbf4e440d3c59cd428fa93 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA9NEUXp78SAkmL4+eAj4mBzPOjk+ccCPVzkTR+mZJdyTwkJAB +HUN4cn4a2kTmh7Er+N8CXCsiqxIOV1GfH2fwaCiBlOEXeQJi/cMjxr9kVWO4FhC6 +l1UqbvPUdrUCUZjFTA9/Ah9MKgk7qGYq5SjE3p+sn4GLhRKbqmq9LjiHgMmkBuv/ +a1Slit+rXHzO2F8fH5hkjeHivyYVgw45aNvGCe2RRfbpoeW2mRtgIv7y9wSewt6a +mhEDXSo/F6mkqA7xVinzro5NettEXLo91tA9Hb6f6x/Mc/GJDNXTKhpWCGeJ6xeW +nAefDZORWAY7Y9YbuAxhEJVi9QL5NWoFOA0C6wIDAQABAoIBAQDtRGVVfwhKWHOl +zK76xXjdqhwaWJXpKRHiI1jOMawpyKdNtAMgdW+apxUnTXePMurG/HuxEC09VvaH +MhfhvD6G9BsCS1UQdnuyLRnTWVLIXyjeWcA9QtEpTy8vDSb+Je2xVaNmTybl5qTn +BH22Mtj6Wg5XWJn7kplDhMdssGTDLsSCMw/rcxe9iT2qOKyltQal23RHzR7SijGp +QTtBp2SDGhvMZcyGuyMqJ084W8sdJpbyVzdDim2iaZdHlk7uvW2n0HcJ56I6yhIq +2U8wfgEEwydGVGHgmQNJ/n+SiT/hv6g5ebhDS46X9F9m5CHDwhdr0DrhPBVSsdhl +1HeJ0+FhAoGBAPuC3uNHToiJis688juKlwc3SQ6ger5ffAg3yaNhEcpHkvOtdZlF +/CfX94xazMov/YqFwkvpSSdKsX+PeXuaqnb1hPKNYX5t45U9RjB/ox7BIQj/2rPx +Bfs99UFW9HKP4HsVmLu1xeJg1Pc9iylTK/xrnwfYiZ+H7IGVccizjnqHAoGBAPkv +n1flAdxBzJH/O0rXoig2EtZsDRMPY51MGDdqVOW14ZOfTVlmu0OSnkSKQm2twfro +TPDVb2TY3wTRutz8H9yOFW1c1Nz4YOyTb8FmJhE2FWAQ9t8QpwUlhn15if72dS/Y +22+vP+AYu7wfqGL7QVVEXho5hGjXi053iEvfXBl9AoGAeZISpo1LGphRLgkKlVky +E1zXxWgwrGB/FYHRx1UeQkZCc+K+Wy4G6kNr9r3VC04TIafx+Lt0jrd+AIibUfG6 +v/GBJ7TLEU+QmAycJskrUaxMiYsSbbPtDjoumDytv8pn2VbhEqqUUg44IqHu6DS5 +qDNlFWfHbgNHgIN6EmcoUXUCgYEAi2G57X4pRjx/4wIy9jAbggaNDuctgQXQoIGZ +4hVWG49a+CnZKDKweKGgaZI0igjxQhmCQAwC3RP520Y9EbLtV38aOSv93QQJowrt +Le6nSGVKG4whqrAz3EsbKUA8kiLldbgFNjl+ryjmidnjZEpKRxmQ0XZuu/4k6+Us +ldQAPjkCgYBwjSm5eDUtK2eEPaBtbJykV05CTv5rn6CKC9L7ZBTkCcdU1hxeqe99 +wb22decnNawGRP1a5cGwqKJPOfkgybJVkdr6aqQW8ClzdFSaenjzs+nVW+T9JTXf +9lFpIZg5kN/geld3B9B4C99riTM0jg9hbe2RQvpLRTrZbnWMA1XoRw== +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.fp b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.fp new file mode 100644 index 0000000000000000000000000000000000000000..4659639db524ee746eaa71926ba593eddde96228 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.fp @@ -0,0 +1 @@ +SHA256:NoQh0XBUuYUSWqnzOzOBnfpgJTRWLMj7BlWAb8IbjeE diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.fp.bb b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.fp.bb new file mode 100644 index 0000000000000000000000000000000000000000..e9d1e4a57dcb90e1aee1eee9fa02fc0655fe292f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.fp.bb @@ -0,0 +1 @@ +xogit-gupof-mydon-hocep-zuval-feson-rarif-cefar-tobar-ryvap-kuxex diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.n b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.n new file mode 100644 index 0000000000000000000000000000000000000000..a669dbf0be05d4be76caa9904b66a90dd8456474 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.n @@ -0,0 +1 @@ +00f4d1145e9efc4809262f8f9e023e260733ce8e4f9c7023d5ce44d1fa66497724f09090011d4378727e1ada44e687b12bf8df025c2b22ab120e57519f1f67f068288194e117790262fdc323c6bf645563b81610ba97552a6ef3d476b5025198c54c0f7f021f4c2a093ba8662ae528c4de9fac9f818b85129baa6abd2e388780c9a406ebff6b54a58adfab5c7cced85f1f1f98648de1e2bf2615830e3968dbc609ed9145f6e9a1e5b6991b6022fef2f7049ec2de9a9a11035d2a3f17a9a4a80ef15629f3ae8e4d7adb445cba3dd6d03d1dbe9feb1fcc73f1890cd5d32a1a56086789eb17969c079f0d939158063b63d61bb80c61109562f502f9356a05380d02eb diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.p b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.p new file mode 100644 index 0000000000000000000000000000000000000000..be7c1c36cdca1a4dc505b1816fbabff0743d5e22 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.p @@ -0,0 +1 @@ +00fb82dee3474e88898acebcf23b8a970737490ea07abe5f7c0837c9a36111ca4792f3ad759945fc27d7f78c5accca2ffd8a85c24be949274ab17f8f797b9aaa76f584f28d617e6de3953d46307fa31ec12108ffdab3f105fb3df54156f4728fe07b1598bbb5c5e260d4f73d8b29532bfc6b9f07d8899f87ec819571c8b38e7a87 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.q b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.q new file mode 100644 index 0000000000000000000000000000000000000000..6f2c542dfb292e4751c6b95a1e88e125f913df0b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.param.q @@ -0,0 +1 @@ +00f92f9f57e501dc41cc91ff3b4ad7a2283612d66c0d130f639d4c18376a54e5b5e1939f4d5966bb43929e448a426dadc1fae84cf0d56f64d8df04d1badcfc1fdc8e156d5cd4dcf860ec936fc166261136156010f6df10a70525867d7989fef6752fd8db6faf3fe018bbbc1fa862fb4155445e1a398468d78b4e77884bdf5c197d diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.pub b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.pub new file mode 100644 index 0000000000000000000000000000000000000000..3322fbc9b820f4b044908696bd355134df62d1b7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_2.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD00RRenvxICSYvj54CPiYHM86OT5xwI9XORNH6Zkl3JPCQkAEdQ3hyfhraROaHsSv43wJcKyKrEg5XUZ8fZ/BoKIGU4Rd5AmL9wyPGv2RVY7gWELqXVSpu89R2tQJRmMVMD38CH0wqCTuoZirlKMTen6yfgYuFEpuqar0uOIeAyaQG6/9rVKWK36tcfM7YXx8fmGSN4eK/JhWDDjlo28YJ7ZFF9umh5baZG2Ai/vL3BJ7C3pqaEQNdKj8XqaSoDvFWKfOujk1620Rcuj3W0D0dvp/rH8xz8YkM1dMqGlYIZ4nrF5acB58Nk5FYBjtj1hu4DGEQlWL1Avk1agU4DQLr RSA test key #2 diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_n b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_n new file mode 100644 index 0000000000000000000000000000000000000000..b8e585e5188a4fdf721acdc88e75491c8ef403b7 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_n @@ -0,0 +1,16 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAIEAy1eZVE7exawA7HgfwhoRGc6aKI4xFucvPnj7y6aZitzJjCNfLner +8c6St28GS2JFUsnyWCNB5iLhoXbu8jK1usG/OIG6vAt9V6HvRDkXCFLhkrwynTUjNUo5YQ +6rkW5QxQfJE6Kl8sdZaq13nF8pcSFDi9IxPrtK1Nfeu6QycfsAAAH4to4I7raOCO4AAAAH +c3NoLXJzYQAAAIEAy1eZVE7exawA7HgfwhoRGc6aKI4xFucvPnj7y6aZitzJjCNfLner8c +6St28GS2JFUsnyWCNB5iLhoXbu8jK1usG/OIG6vAt9V6HvRDkXCFLhkrwynTUjNUo5YQ6r +kW5QxQfJE6Kl8sdZaq13nF8pcSFDi9IxPrtK1Nfeu6QycfsAAAADAQABAAAAgF8o+ZqY5m +w/mJcRiFs/86zOIRrFoHeFbXihCcU+jDCOLswkaZDHdHJPKB4sGRgCP0sFMyLILTjULh9w +F1bFIIIVuGJ5/vJLBL9CGfdfFgzA8Kr6pMq1c7DrGc6mIz3/A1AygqcBY55ZJydOMr1gWb +1YVrWODomfBldE7bLt5PhhAAAAQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/ +h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZsAAABBAPVgd+8FvkV0kG9SF17YiX +6NoWJrybBVU01qIPYQLFfHoLMbPnhksQH009V8NRkryUnhQkOp6VY2HeI8XdF59YMAAABB +ANQlQcwBS+JjcKZXlT8638uvcT94FjmtujMTPxhOs8fYwux4ENyj2linRvxbh7NPOWk0Q2 +gy5hnGfCLzLruzYCkAAAAAAQID +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_n_pw b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_n_pw new file mode 100644 index 0000000000000000000000000000000000000000..dc18373bca8fd82ab29975bcc4ec1afd06865e2e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/testdata/rsa_n_pw @@ -0,0 +1,17 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABAFw/Wg/V +I5SAXWj/HJr9qeAAAAEAAAAAEAAACXAAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7F +rADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu +7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylx +IUOL0jE+u0rU1967pDJx+wAAAgD1iSGiMlMJt2VH4kx5yr0wCJS+4UOmX0bxKO7UH5Jcul +K5eaSe5ZoKE7hTYBaz0K5dRF/0fqLsvVZlE4quDjFLN6Hyavgn2W/QM7SUqBHgRMal9pgH +LnxX6mFNWJ+4yb7f3bcbVIdgmMm3sT9Xjwaf5xgzNlR2mkUWtFwjyQh6FxUo5apNzqNBwO +l2Q4xfmyZTp1s++pStQ/su6obXpxnE2Nx5G/D84ZL5iWl+njUy/MvJTazHRbiTSyihU+UA +mUr5ZNuP3WUYY+h3KVlHpYHJYB7l3AMTKuPMFLhY9V7BJ+DuKPaqBgX4hvRzY0eVQiFr61 +ovjWjvfu1ulx550JqdYCgH2PpP0E89OQne35Cxs9QPThfe8DKojC9YquYh9zmVTvr7kNiE +Soluk/7oKpQIDaC+/SRk7AJ2e3Cbt1lXyGNn37PuqaaC/apaF/DOD6Yig9aClS7jOUrT96 +56trFAYfHEIKbRCUSMCiM1+x6HOLYf5ROrGE9KxT3kUD9XMsMpTva+cPpHUpbGpXcYE10N +MyYDz+V5M2/ZoIdEhscJNQ3UnhaZpeEaqcOyNyo90n3Dnaw/WpMDD/kNMGfm8daTaYInnQ +QnwA2gwlYfpTAqxE71oXgOuGmtA0yqJB4778Xq26Pb+B7/mZZZe6n0FVmiNC+ZG37ZGOw/ +iGL9e2Sxzw== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshkey/tests.c b/aosp/external/openssh/regress/unittests/sshkey/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..78aa9223d42bd557518177f152cd755780c6efe6 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshkey/tests.c @@ -0,0 +1,22 @@ +/* $OpenBSD: tests.c,v 1.1 2014/06/24 01:14:18 djm Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include "../test_helper/test_helper.h" + +void sshkey_tests(void); +void sshkey_file_tests(void); +void sshkey_fuzz_tests(void); + +void +tests(void) +{ + sshkey_tests(); + sshkey_file_tests(); + sshkey_fuzz_tests(); +} diff --git a/aosp/external/openssh/regress/unittests/sshsig/Makefile b/aosp/external/openssh/regress/unittests/sshsig/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..65564d1b278bcc6c75bb9ddc5feabb7cfd187891 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/Makefile @@ -0,0 +1,25 @@ +# $OpenBSD: Makefile,v 1.2 2021/01/09 12:24:31 dtucker Exp $ + +PROG=test_sshsig +SRCS=tests.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c +SRCS+=ssh-dss.c ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c +SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c +SRCS+=addr.c addrmatch.c bitmap.c sshsig.c +SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c +SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c +SRCS+=ssh-ed25519-sk.c sk-usbhid.c + +SRCS+=digest-openssl.c +#SRCS+=digest-libc.c +SRCS+=utf8.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} ${UNITTEST_ARGS} -d ${.CURDIR}/testdata + +.include diff --git a/aosp/external/openssh/regress/unittests/sshsig/mktestdata.sh b/aosp/external/openssh/regress/unittests/sshsig/mktestdata.sh new file mode 100644 index 0000000000000000000000000000000000000000..d2300f9c6ee1afd089dc70484bad09172cd12fd8 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/mktestdata.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# $OpenBSD: mktestdata.sh,v 1.1 2020/06/19 04:32:09 djm Exp $ + +NAMESPACE=unittest + +set -ex + +cd testdata + +if [ -f ../../../misc/sk-dummy/sk-dummy.so ] ; then + SK_DUMMY=../../../misc/sk-dummy/sk-dummy.so +elif [ -f ../../../misc/sk-dummy/obj/sk-dummy.so ] ; then + SK_DUMMY=../../../misc/sk-dummy/obj/sk-dummy.so +else + echo "Can't find sk-dummy.so" 1>&2 + exit 1 +fi + +rm -f signed-data namespace +rm -f rsa dsa ecdsa ed25519 ecdsa_sk ed25519_sk +rm -f rsa.sig dsa.sig ecdsa.sig ed25519.sig ecdsa_sk.sig ed25519_sk.sig + +printf "This is a test, this is only a test" > signed-data +printf "$NAMESPACE" > namespace + +ssh-keygen -t rsa -C "RSA test" -N "" -f rsa -m PEM +ssh-keygen -t dsa -C "DSA test" -N "" -f dsa -m PEM +ssh-keygen -t ecdsa -C "ECDSA test" -N "" -f ecdsa -m PEM +ssh-keygen -t ed25519 -C "ED25519 test key" -N "" -f ed25519 +ssh-keygen -w "$SK_DUMMY" -t ecdsa-sk -C "ECDSA-SK test key" \ + -N "" -f ecdsa_sk +ssh-keygen -w "$SK_DUMMY" -t ed25519-sk -C "ED25519-SK test key" \ + -N "" -f ed25519_sk + +ssh-keygen -Y sign -f rsa -n $NAMESPACE - < signed-data > rsa.sig +ssh-keygen -Y sign -f dsa -n $NAMESPACE - < signed-data > dsa.sig +ssh-keygen -Y sign -f ecdsa -n $NAMESPACE - < signed-data > ecdsa.sig +ssh-keygen -Y sign -f ed25519 -n $NAMESPACE - < signed-data > ed25519.sig +ssh-keygen -w "$SK_DUMMY" \ + -Y sign -f ecdsa_sk -n $NAMESPACE - < signed-data > ecdsa_sk.sig +ssh-keygen -w "$SK_DUMMY" \ + -Y sign -f ed25519_sk -n $NAMESPACE - < signed-data > ed25519_sk.sig diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa b/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa new file mode 100644 index 0000000000000000000000000000000000000000..7c0063efcdf5778eb2d294619b3ff53588f206f1 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQCXpndQdz2mQVnk+lYOF3nxDT+h6SiJmUvBFhnFWBv8tG4pTOkb +EwGufLEzGpzjTj+3bjVau7LFt37AFrqs4Num272BWNsYNIjOlGPgq7Xjv32FN00x +JYh1DoRs1cGGnvohlsWEamGGhTHD1a9ipctPEBV+NrxtZMrl+pO/ZZg8vQIVAKJB +P3iNYSpSuW74+q4WxLCuK8O3AoGAQldE+BIuxlvoG1IFiWesx0CU+H2KO0SEZc9A +SX/qjOabh0Fb78ofTlEf9gWHFfat8SvSJQIOPMVlb76Lio8AAMT8Eaa/qQKKYmQL +dNq4MLhhjxx5KLGt6J2JyFPExCv+qnHYHD59ngtLwKyqGjpSC8LPLktdXn8W/Aad +Ly1K7+MCgYBsMHBczhSeUh8w7i20CVg4OlNTmfJRVU2tO6OpMxZ/quitRm3hLKSN +u4xRkvHJwi4LhQtv1SXvLI5gs5P3gCG8tsIAiyCqLinHha63iBdJpqhnV/x/j7dB +yJr3xJbnmLdWLkkCtNk1Ir1/CuEz+ufAyLGdKWksEAu1UUlb501BkwIVAILIa3Rg +0h7J9lQpHJphvF3K0M1T +-----END DSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa.pub new file mode 100644 index 0000000000000000000000000000000000000000..e77aa7ef41a003a6524671b0585c434d322fd5df --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAJemd1B3PaZBWeT6Vg4XefENP6HpKImZS8EWGcVYG/y0bilM6RsTAa58sTManONOP7duNVq7ssW3fsAWuqzg26bbvYFY2xg0iM6UY+CrteO/fYU3TTEliHUOhGzVwYae+iGWxYRqYYaFMcPVr2Kly08QFX42vG1kyuX6k79lmDy9AAAAFQCiQT94jWEqUrlu+PquFsSwrivDtwAAAIBCV0T4Ei7GW+gbUgWJZ6zHQJT4fYo7RIRlz0BJf+qM5puHQVvvyh9OUR/2BYcV9q3xK9IlAg48xWVvvouKjwAAxPwRpr+pAopiZAt02rgwuGGPHHkosa3onYnIU8TEK/6qcdgcPn2eC0vArKoaOlILws8uS11efxb8Bp0vLUrv4wAAAIBsMHBczhSeUh8w7i20CVg4OlNTmfJRVU2tO6OpMxZ/quitRm3hLKSNu4xRkvHJwi4LhQtv1SXvLI5gs5P3gCG8tsIAiyCqLinHha63iBdJpqhnV/x/j7dByJr3xJbnmLdWLkkCtNk1Ir1/CuEz+ufAyLGdKWksEAu1UUlb501Bkw== DSA test diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa.sig new file mode 100644 index 0000000000000000000000000000000000000000..0b14ad6b8a7bbc55e926542b78182872731a527d --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/dsa.sig @@ -0,0 +1,13 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAAbEAAAAHc3NoLWRzcwAAAIEAl6Z3UHc9pkFZ5PpWDhd58Q0/oekoiZ +lLwRYZxVgb/LRuKUzpGxMBrnyxMxqc404/t241Wruyxbd+wBa6rODbptu9gVjbGDSIzpRj +4Ku14799hTdNMSWIdQ6EbNXBhp76IZbFhGphhoUxw9WvYqXLTxAVfja8bWTK5fqTv2WYPL +0AAAAVAKJBP3iNYSpSuW74+q4WxLCuK8O3AAAAgEJXRPgSLsZb6BtSBYlnrMdAlPh9ijtE +hGXPQEl/6ozmm4dBW+/KH05RH/YFhxX2rfEr0iUCDjzFZW++i4qPAADE/BGmv6kCimJkC3 +TauDC4YY8ceSixreidichTxMQr/qpx2Bw+fZ4LS8Csqho6UgvCzy5LXV5/FvwGnS8tSu/j +AAAAgGwwcFzOFJ5SHzDuLbQJWDg6U1OZ8lFVTa07o6kzFn+q6K1GbeEspI27jFGS8cnCLg +uFC2/VJe8sjmCzk/eAIby2wgCLIKouKceFrreIF0mmqGdX/H+Pt0HImvfElueYt1YuSQK0 +2TUivX8K4TP658DIsZ0paSwQC7VRSVvnTUGTAAAACHVuaXR0ZXN0AAAAAAAAAAZzaGE1MT +IAAAA3AAAAB3NzaC1kc3MAAAAodi5lr0pqBpO76OY4N1CtfR85BCgZ95qfVjP/e9lToj0q +lwjSJJXUjw== +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa new file mode 100644 index 0000000000000000000000000000000000000000..55fb440e01d49f0ed443e53d6324dbb78487c794 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIFg0ZCSEB5LNeLsXYL25g3kqEWsqh52DR+yNOjyQJqyZoAoGCCqGSM49 +AwEHoUQDQgAE3sud88FV0N8FPspZSV7LWqj6uPPLRZiSsenNuEYAteWPyDgrZsWb +LzXBuUJucepaCNuW/QWgHBRbrjWj3ERm3A== +-----END EC PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa.pub new file mode 100644 index 0000000000000000000000000000000000000000..14ec6cf1230c05ac4c405d5a78d082aaced8447e --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBN7LnfPBVdDfBT7KWUley1qo+rjzy0WYkrHpzbhGALXlj8g4K2bFmy81wblCbnHqWgjblv0FoBwUW641o9xEZtw= ECDSA test diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa.sig new file mode 100644 index 0000000000000000000000000000000000000000..79781570cda194ff589e1b2f0bc9f156e17c3e3c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa.sig @@ -0,0 +1,7 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAE +EE3sud88FV0N8FPspZSV7LWqj6uPPLRZiSsenNuEYAteWPyDgrZsWbLzXBuUJucepaCNuW +/QWgHBRbrjWj3ERm3AAAAAh1bml0dGVzdAAAAAAAAAAGc2hhNTEyAAAAZQAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAASgAAACEAycVNsTlE+XEZYyYiDxWZlliruf/pPMhEEMR/XLdQ +a4MAAAAhALQt+5gES7L3uKGptHB6UZQMuZ2WyI0C6FJs4v6AtMIU +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk new file mode 100644 index 0000000000000000000000000000000000000000..62ae44cb09ee047f173ed59c2f21478f800997ce --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk @@ -0,0 +1,13 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2 +RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQSg1WuY0XE+ +VexOsrJsFYuxyVoe6eQ/oXmyz2pEHKZw9moyWehv+Fs7oZWFp3JVmOtybKQ6dvfUZYauQE +/Ov4PAAAAABHNzaDoAAAGI6iV41+oleNcAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv +cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEEoNVrmNFxPlXsTrKybBWLsclaHunkP6F5ss +9qRBymcPZqMlnob/hbO6GVhadyVZjrcmykOnb31GWGrkBPzr+DwAAAAARzc2g6AQAAAOMt +LS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUhjQ0FRRUVJQm9oeW54M2tpTFVEeS +t5UjU3WXBXSU5KektnU1p6WnV2VTljYXFla3JGcW9Bb0dDQ3FHU000OQpBd0VIb1VRRFFn +QUVvTlZybU5GeFBsWHNUckt5YkJXTHNjbGFIdW5rUDZGNXNzOXFSQnltY1BacU1sbm9iL2 +hiCk82R1ZoYWR5VlpqcmNteWtPbmIzMUdXR3JrQlB6citEd0E9PQotLS0tLUVORCBFQyBQ +UklWQVRFIEtFWS0tLS0tCgAAAAAAAAARRUNEU0EtU0sgdGVzdCBrZXk= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk.pub new file mode 100644 index 0000000000000000000000000000000000000000..385ebf15b142870695022fa480a3192024562ae3 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBKDVa5jRcT5V7E6ysmwVi7HJWh7p5D+hebLPakQcpnD2ajJZ6G/4WzuhlYWnclWY63JspDp299Rlhq5AT86/g8AAAAAEc3NoOg== ECDSA-SK test key diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk.sig new file mode 100644 index 0000000000000000000000000000000000000000..86de36063174fa2c3f22c4f8d704a1a765017383 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk.sig @@ -0,0 +1,8 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAAH8AAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBvcGVuc3NoLmNvbQ +AAAAhuaXN0cDI1NgAAAEEEoNVrmNFxPlXsTrKybBWLsclaHunkP6F5ss9qRBymcPZqMlno +b/hbO6GVhadyVZjrcmykOnb31GWGrkBPzr+DwAAAAARzc2g6AAAACHVuaXR0ZXN0AAAAAA +AAAAZzaGE1MTIAAAB3AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20A +AABIAAAAIHohGwyy8iKT3zwd1TYA9V/Ioo7h/3zCJUtyq/Qigt/HAAAAIGzidTwq7D/kFa +7Xjcp/KkdbIs4MfQpfAW/0OciajlpzARI0Vng= +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.pub new file mode 100644 index 0000000000000000000000000000000000000000..1597302ce70d11b978a76fad4278a6f1a5f2b8ed --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.pub @@ -0,0 +1 @@ +sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBBRGwDjs4HhJFcn4tJ5Gr72KcmRmCS1OirETxaXvnsNApgoOLF1a/7rxldfSMHm73eT1nhHe97W8qicPPEAKDJQAAAALbWluZHJvdC5vcmc= diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.sig new file mode 100644 index 0000000000000000000000000000000000000000..4bdd8edc681adedccbb481529329e6473bd7e288 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.sig @@ -0,0 +1,13 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAAIYAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBvcGVuc3NoLmNvbQ +AAAAhuaXN0cDI1NgAAAEEEFEbAOOzgeEkVyfi0nkavvYpyZGYJLU6KsRPFpe+ew0CmCg4s +XVr/uvGV19Iwebvd5PWeEd73tbyqJw88QAoMlAAAAAttaW5kcm90Lm9yZwAAAAh1bml0dG +VzdAAAAAAAAAAGc2hhNTEyAAABhwAAACt3ZWJhdXRobi1zay1lY2RzYS1zaGEyLW5pc3Rw +MjU2QG9wZW5zc2guY29tAAAASQAAACBj2oMT9tb5wRXe6mdmf4/lgAO8wrgr95ouozwNg4 +itnQAAACEAtU9g5wz3HchUiLfLD6plr9T4TiJ32lVCrATSjpiy0SMBAAADHwAAABdodHRw +czovL3d3dy5taW5kcm90Lm9yZwAAAON7InR5cGUiOiJ3ZWJhdXRobi5nZXQiLCJjaGFsbG +VuZ2UiOiJVMU5JVTBsSEFBQUFDSFZ1YVhSMFpYTjBBQUFBQUFBQUFBWnphR0UxTVRJQUFB +QkFMTHU4WmdjU3h0Nk1zRlV6dWlaZ0c2R3dNZEo5ZDd4ZUU3WW9SSXcwZzlpSEpfd3NGRD +cxbzRXbHllenZGV0VqYnFRMHFDN0Z3R3Bqa2pVUVAtTmQ2dyIsIm9yaWdpbiI6Imh0dHBz +Oi8vd3d3Lm1pbmRyb3Qub3JnIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQAAAAA= +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519 b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519 new file mode 100644 index 0000000000000000000000000000000000000000..b44a63d3ea60f92933b86225c0a4888d70d6cf62 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCJYs0iDdw0Fe/FTzY1b78I4H/j+R6mz2AmLtwTjHYwBAAAAJjpGas/6Rmr +PwAAAAtzc2gtZWQyNTUxOQAAACCJYs0iDdw0Fe/FTzY1b78I4H/j+R6mz2AmLtwTjHYwBA +AAAEDpSKRA1QKW6kYiQftGRWh+H0fNekzYLG6c3bzseoCpEolizSIN3DQV78VPNjVvvwjg +f+P5HqbPYCYu3BOMdjAEAAAAEEVEMjU1MTkgdGVzdCBrZXkBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519.pub new file mode 100644 index 0000000000000000000000000000000000000000..b078e4516fbe44d2cf03e32a98e624bcb2ea6875 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIlizSIN3DQV78VPNjVvvwjgf+P5HqbPYCYu3BOMdjAE ED25519 test key diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519.sig new file mode 100644 index 0000000000000000000000000000000000000000..8e8ff2a8ac197df506ee00eef1d398a8090609c5 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519.sig @@ -0,0 +1,6 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgiWLNIg3cNBXvxU82NW+/COB/4/ +keps9gJi7cE4x2MAQAAAAIdW5pdHRlc3QAAAAAAAAABnNoYTUxMgAAAFMAAAALc3NoLWVk +MjU1MTkAAABAihQsbUzuNEFflk5Tw1+H9aLS7tZQk0RG8KW1DtOmDYYnWe3D3UKiG3fcJa +DNg4vBWp1j1gLRiBMOF+gwYNegDg== +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk new file mode 100644 index 0000000000000000000000000000000000000000..3a434ecb941737ad0cff100396bb5634df3f5230 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2 +gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACCbGg2F0GK7nOm4pQmAyCuGEjnhvs5q0TtjPbdN +//+yxwAAAARzc2g6AAAAuBw56jAcOeowAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2 +9tAAAAIJsaDYXQYruc6bilCYDIK4YSOeG+zmrRO2M9t03//7LHAAAABHNzaDoBAAAAQFXc +6dCwWewIk1EBofAouGZApW8+s0XekXenxtb78+x0mxoNhdBiu5zpuKUJgMgrhhI54b7Oat +E7Yz23Tf//sscAAAAAAAAAE0VEMjU1MTktU0sgdGVzdCBrZXkBAgMEBQY= +-----END OPENSSH PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk.pub new file mode 100644 index 0000000000000000000000000000000000000000..71051ec3b2172d27da6c689cbe487636b0ce2a5c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk.pub @@ -0,0 +1 @@ +sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJsaDYXQYruc6bilCYDIK4YSOeG+zmrRO2M9t03//7LHAAAABHNzaDo= ED25519-SK test key diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk.sig new file mode 100644 index 0000000000000000000000000000000000000000..49b6818da59f71792d89cdfa830e038e7938392c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/ed25519_sk.sig @@ -0,0 +1,7 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAAEoAAAAac2stc3NoLWVkMjU1MTlAb3BlbnNzaC5jb20AAAAgmxoNhd +Biu5zpuKUJgMgrhhI54b7OatE7Yz23Tf//sscAAAAEc3NoOgAAAAh1bml0dGVzdAAAAAAA +AAAGc2hhNTEyAAAAZwAAABpzay1zc2gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAAEAi+7eTjW +/+LQ2M+sCD+KFtH1n7VFFJon/SZFsxODyV8cWTlFKj617Ys1Ur5TV6uaEXQhck8rBA2oQI +HTPANLIPARI0Vng= +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/namespace b/aosp/external/openssh/regress/unittests/sshsig/testdata/namespace new file mode 100644 index 0000000000000000000000000000000000000000..1570cd548baa9998c08cd500e88daa254dbbe66c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/namespace @@ -0,0 +1 @@ +unittest \ No newline at end of file diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa b/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa new file mode 100644 index 0000000000000000000000000000000000000000..228fad7978e9c1f67632aed0f0be0d34d3091eee --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4wIBAAKCAYEA386lmjRHtJpyj87BrS+ssMmtvc/1SPN0gXTPs9jZ1hYAq98P +ca3/RYVM4HaSu6COztQJ2ZnZD3Te/XeBnIU2mfuvQEl+DiwisGeNglVyRCi7787f +PFFfcxzZfDa7EB2qY8S3oaSGZK8QqzuGwmGAImjlQXz6J+HCd/eD/58GoCSSirIE +CFWCAt+uNrOC/EmgAzsbfcfaIbbVzA40tlgU3hO2J42kddz8CisDTtDKQABFcOaQ +ZycSfn7HDP+WgXLXXBUI9wVM1Tif1f+9MX08xIsvCvGzo7yLgbbTFLSGr5SkA+tO +rYuoA7V8fge0id/3pnVtG1Ui3I7vejeAwf0HZqtFeBEnOwkIJFmZeMtFeOVf+4ki +4h1rDqAvSscNvMtLp6OXpbAATATAuEWEkIQBl1rngnEe0iC9iU9itKMW6qJ4FtIb +4ACH1EoU1x8vqrFecg2hvqfk5CZBJIbV28JFuGjac3OxBZ17Fqb8ljomUir1GrET +2z66NMgb5TjDD7BVAgMBAAECggGACfjDGCPMLhfTkuS7bGP7ZcUWoKZrX1y5jCsQ +NgsraYaBcSb3ITGHdimCS139G68DreN0rOVV7oJejRyOAdNNo367SDn+C9ObmBCF +FZGJDdBiz0SAXceiYRaf+hDWNNmdheR16hXShxnlvDtivbZqZx4VWN2gp7Y/W+kD +UJhdSzVV8igMVfK5YDdnI7jL1UHSh1JS3z/QUEA9NmJLpvQ1uc9XBlwhP78g27Me +6pwS5tccQPOE65OqF0i+xa19nzbmnC940Y34yZeI/UE+PYaO2+asapvOfu/sboBH +Yb5BuWXVEkSeRWI23SpuZbmfNTtVgiRoRqOvqM4G88LkhYjZ6xpDggxQwJiShiiD +oWCucs0v3pX8H8/LbGs8l50SGI5nzUqAdZ7/QQucU/GuDiQtampntkLEDgf9KIw/ +SDrtCw1E9fnCWj4Z71IYfepY9bVY6QUEcfTdnDcYSY1Z5tVpzeMHVLeo0lbNVZv9 +2qmPnjjP/IvWbjjwu/PHpUWkUs0BAoHBAPx4YwPXWYgWnesMKXkjAHyO5KA4EyBr ++rcEmOZkZDibC8PKYzIK2ztptuthahVovW20R/QJhJkO5teGZMeGPFq+floCeC5P +la9CEYGYcTrzgSe1QM9IGMr1vGI1KIWck7VkJ0bkKoY40uIJSVZxnyG9pEpcwYSp +tnOqA/f5YZUFctWvXUz46OfiLKstXLrcrGIU7YRmLv2rW9twnpJYTzE98g3KpVJ2 +TI1pyvrDTdGeAQUTGCAjpviY6XR5d020vQKBwQDi76wsGLQ3XLI+OAE95Ljo0Mcl ++KdJPVVQPq/VcjKgZQndFloflMRrmgNHme9gmsHOrf8DLZvEDbtT+gbmWslMFZQ9 +om1kR404gfuGmfIYdBdOwWjuBLsZs3pfqDB4Xa3NkxljwOMYTp035n0r2UMFaSy3 +gvpW7fsdPOGAJsqNhSw/JNHcokHeBm7VbV0aD7tSyIghmARb5c98fmrSPbiEo8mP +ITIZlgbfZCq2KuXY4q16R3QvlpuSwitVobLR/3kCgcEAueH5JM7dQHFGe9RMhL/c +j9i1Q7GFg4183lsoKBkqIPMmylSsjB+qIihHYS4r6O9g6PCfOXH4iqiKFY0BjlWr +AjTW2naO/aniz1KZiQ0v8PNv2Eh/Gx4+AtDCjpwM5bLOnfLLaEp9dK1JttqXgGnP +fAwgdg+s+3votWgr29tkmU+VqPagfxeUg4Xm1XFkoL/wu5Yk+iIx3trXms1kMuOK +CvtMyBK3fetTmZqWs+Iv3XGz1oSkcqVNPiN3XyY/TJsRAoG/Q17jvjOXTNg4EkCO +HdHJE1Tnyl4HS7bpnOj/Sl6cqQFV7Ey2dKm1pjwSvS714bgP0UvWaRshIxLwif2w +DrLlD7FYUPPnhd24Dw6HnW4WcSwFv1uryv2cjgS6T6ueuB0Xe/AvmW2p/Y1ZHz9N +6baWLwUKQXCg4S3FXui0CVd6yoi+mgBUTSveYguG29WbziDde7YMs+xtXtravhrJ +m6C3Jql5LQSt2uqvH6KdC3ewxLKGzcZot7f+d5MtSj6216ECgcEA9PGmWeUkhVuW +Xz2c9iBeHwCtmDso7gVwxNnHqdqirB4f1nDCGbrJS7hz5Ss7/wfzekP2W5if2P6U +JPUdfykAQgALNn1twAtj1a+UAp31ZWu8JK/Qzt4hLJPBxzMo7MenJq189JmYmDnm +6D5d9vDLCW15gCZua89GZa8K8V50lYyeHBOHAyzNTfNlnMBkHyP645+nqpuEWzIT +3mCe2OAbl60o8VvvVUlAQyQ/ObLq37HHEoDu0U/YAnP157cxpa84 +-----END RSA PRIVATE KEY----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa.pub b/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa.pub new file mode 100644 index 0000000000000000000000000000000000000000..30142ac0aee334bb07a6c6b98e20c3c153adbb66 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDfzqWaNEe0mnKPzsGtL6ywya29z/VI83SBdM+z2NnWFgCr3w9xrf9FhUzgdpK7oI7O1AnZmdkPdN79d4GchTaZ+69ASX4OLCKwZ42CVXJEKLvvzt88UV9zHNl8NrsQHapjxLehpIZkrxCrO4bCYYAiaOVBfPon4cJ394P/nwagJJKKsgQIVYIC3642s4L8SaADOxt9x9ohttXMDjS2WBTeE7YnjaR13PwKKwNO0MpAAEVw5pBnJxJ+fscM/5aBctdcFQj3BUzVOJ/V/70xfTzEiy8K8bOjvIuBttMUtIavlKQD606ti6gDtXx+B7SJ3/emdW0bVSLcju96N4DB/Qdmq0V4ESc7CQgkWZl4y0V45V/7iSLiHWsOoC9Kxw28y0uno5elsABMBMC4RYSQhAGXWueCcR7SIL2JT2K0oxbqongW0hvgAIfUShTXHy+qsV5yDaG+p+TkJkEkhtXbwkW4aNpzc7EFnXsWpvyWOiZSKvUasRPbPro0yBvlOMMPsFU= RSA test diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa.sig b/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa.sig new file mode 100644 index 0000000000000000000000000000000000000000..15a032e0100bfb7abe3352a1d914f958f75d4866 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/rsa.sig @@ -0,0 +1,19 @@ +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAN/OpZo0R7Saco/Owa0vrL +DJrb3P9UjzdIF0z7PY2dYWAKvfD3Gt/0WFTOB2krugjs7UCdmZ2Q903v13gZyFNpn7r0BJ +fg4sIrBnjYJVckQou+/O3zxRX3Mc2Xw2uxAdqmPEt6GkhmSvEKs7hsJhgCJo5UF8+ifhwn +f3g/+fBqAkkoqyBAhVggLfrjazgvxJoAM7G33H2iG21cwONLZYFN4TtieNpHXc/AorA07Q +ykAARXDmkGcnEn5+xwz/loFy11wVCPcFTNU4n9X/vTF9PMSLLwrxs6O8i4G20xS0hq+UpA +PrTq2LqAO1fH4HtInf96Z1bRtVItyO73o3gMH9B2arRXgRJzsJCCRZmXjLRXjlX/uJIuId +aw6gL0rHDbzLS6ejl6WwAEwEwLhFhJCEAZda54JxHtIgvYlPYrSjFuqieBbSG+AAh9RKFN +cfL6qxXnINob6n5OQmQSSG1dvCRbho2nNzsQWdexam/JY6JlIq9RqxE9s+ujTIG+U4ww+w +VQAAAAh1bml0dGVzdAAAAAAAAAAGc2hhNTEyAAABlAAAAAxyc2Etc2hhMi01MTIAAAGACi +nEpBrQxZi0yOrrT6h98JFfZh0XXioih4fzmvtoV0yOReWClS+otGgXoJyZHcbaKNOjDwSM +rIkUoX6OUJmtHYP0HRELnKw35m33LdBPXpFGS4tRS7NeSpvc04KtjT6jYXY9FjWy5hcn17 +Sxc/3DnJqLgJBur8acY7FeIzpWmKixPd/dGkEjdWoD9gO6szLczGuQgrOdYmSRL4yKadTJ +lVjz5OSeKSYYGQy33US2XQassRRNYf4e9byTA3DKvHa/OcTt7lFerea0kZdDpAboqffz7T +Yaw/hFskAYLIEdTW3aoXBGHSOvu8AkDOtb7qwuxGSQ27pjkDLDNsp1ceCFaCaQ6X83RZuK +ACv9JUBI5KaSf81e0bs0KezJKkhB9czeZ6dk96qISbgayEBnvhYgXvUDKtHn7HzNlCJKfK +5ABhNxfGG2CD+NKqcrndwFgS1sQO3hbA84zPQb26ShBovT8ytHBmW1F8ZK4O9Bz61Q6EZK +vs/u6xP6LUean/so5daa +-----END SSH SIGNATURE----- diff --git a/aosp/external/openssh/regress/unittests/sshsig/testdata/signed-data b/aosp/external/openssh/regress/unittests/sshsig/testdata/signed-data new file mode 100644 index 0000000000000000000000000000000000000000..7df4bedd135c385ed6e5cf99c901e3ad6e44a043 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/testdata/signed-data @@ -0,0 +1 @@ +This is a test, this is only a test \ No newline at end of file diff --git a/aosp/external/openssh/regress/unittests/sshsig/tests.c b/aosp/external/openssh/regress/unittests/sshsig/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..fdc3baeb7634759b72e23a4af5755976d4f3b1d8 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/tests.c @@ -0,0 +1,142 @@ +/* $OpenBSD: tests.c,v 1.3 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Regress test for sshbuf.h buffer API + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#endif + +#include "ssherr.h" +#include "authfile.h" +#include "sshkey.h" +#include "sshbuf.h" +#include "sshsig.h" +#include "log.h" + +#include "../test_helper/test_helper.h" + +static struct sshbuf * +load_file(const char *name) +{ + struct sshbuf *ret = NULL; + + ASSERT_INT_EQ(sshbuf_load_file(test_data_file(name), &ret), 0); + ASSERT_PTR_NE(ret, NULL); + return ret; +} + +static struct sshkey * +load_key(const char *name) +{ + struct sshkey *ret = NULL; + ASSERT_INT_EQ(sshkey_load_public(test_data_file(name), &ret, NULL), 0); + ASSERT_PTR_NE(ret, NULL); + return ret; +} + +static void +check_sig(const char *keyname, const char *signame, const struct sshbuf *msg, + const char *namespace) +{ + struct sshkey *k, *sign_key; + struct sshbuf *sig, *rawsig; + struct sshkey_sig_details *sig_details; + + k = load_key(keyname); + sig = load_file(signame); + sign_key = NULL; + sig_details = NULL; + rawsig = NULL; + ASSERT_INT_EQ(sshsig_dearmor(sig, &rawsig), 0); + ASSERT_INT_EQ(sshsig_verifyb(rawsig, msg, namespace, + &sign_key, &sig_details), 0); + ASSERT_INT_EQ(sshkey_equal(k, sign_key), 1); + sshkey_free(k); + sshkey_free(sign_key); + sshkey_sig_details_free(sig_details); + sshbuf_free(sig); + sshbuf_free(rawsig); +} + +void +tests(void) +{ + struct sshbuf *msg; + char *namespace; + +#if 0 + log_init("test_sshsig", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 1); +#endif + +#ifdef WITH_OPENSSL + OpenSSL_add_all_algorithms(); + ERR_load_CRYPTO_strings(); +#endif + + TEST_START("load data"); + msg = load_file("namespace"); + namespace = sshbuf_dup_string(msg); + ASSERT_PTR_NE(namespace, NULL); + sshbuf_free(msg); + msg = load_file("signed-data"); + TEST_DONE(); + +#ifdef WITH_OPENSSL + TEST_START("check RSA signature"); + check_sig("rsa.pub", "rsa.sig", msg, namespace); + TEST_DONE(); + + TEST_START("check DSA signature"); + check_sig("dsa.pub", "dsa.sig", msg, namespace); + TEST_DONE(); + +#ifdef OPENSSL_HAS_ECC + TEST_START("check ECDSA signature"); + check_sig("ecdsa.pub", "ecdsa.sig", msg, namespace); + TEST_DONE(); +#endif +#endif + + TEST_START("check ED25519 signature"); + check_sig("ed25519.pub", "ed25519.sig", msg, namespace); + TEST_DONE(); + +#ifdef ENABLE_SK +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + TEST_START("check ECDSA-SK signature"); + check_sig("ecdsa_sk.pub", "ecdsa_sk.sig", msg, namespace); + TEST_DONE(); +#endif + + TEST_START("check ED25519-SK signature"); + check_sig("ed25519_sk.pub", "ed25519_sk.sig", msg, namespace); + TEST_DONE(); + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + TEST_START("check ECDSA-SK webauthn signature"); + check_sig("ecdsa_sk_webauthn.pub", "ecdsa_sk_webauthn.sig", + msg, namespace); + TEST_DONE(); +#endif +#endif /* ENABLE_SK */ + + sshbuf_free(msg); + free(namespace); +} diff --git a/aosp/external/openssh/regress/unittests/sshsig/webauthn.html b/aosp/external/openssh/regress/unittests/sshsig/webauthn.html new file mode 100644 index 0000000000000000000000000000000000000000..5c9a32e9e47f8f38b84b03949f0590777ac1843f --- /dev/null +++ b/aosp/external/openssh/regress/unittests/sshsig/webauthn.html @@ -0,0 +1,766 @@ + + + +webauthn test + + +

webauthn test

+

+This is a demo/test page for generating FIDO keys and signatures in SSH +formats. The page initially displays a form to generate a FIDO key and +convert it to a SSH public key. +

+

+Once a key has been generated, an additional form will be displayed to +allow signing of data using the just-generated key. The data may be signed +as either a raw SSH signature or wrapped in a sshsig message (the latter is +easier to test using command-line tools. +

+

+Lots of debugging is printed along the way. +

+

Enroll

+ +
+ + + + + + +
Username:
+
+ + + + + + + diff --git a/aosp/external/openssh/regress/unittests/test_helper/Makefile b/aosp/external/openssh/regress/unittests/test_helper/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..78026e6533da1f1d06be8f4d504512307a4bca7b --- /dev/null +++ b/aosp/external/openssh/regress/unittests/test_helper/Makefile @@ -0,0 +1,15 @@ +# $OpenBSD: Makefile,v 1.3 2016/07/04 18:01:44 guenther Exp $ + +LIB= test_helper +SRCS= test_helper.c fuzz.c + +NOPROFILE= yes +NOPIC= yes + +# Hack to allow building with SUBDIR in ../../Makefile +regress: all + +install: + @echo -n + +.include diff --git a/aosp/external/openssh/regress/unittests/test_helper/fuzz.c b/aosp/external/openssh/regress/unittests/test_helper/fuzz.c new file mode 100644 index 0000000000000000000000000000000000000000..78b36654defcd527f20a7f71e56a847c7881bd54 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/test_helper/fuzz.c @@ -0,0 +1,438 @@ +/* $OpenBSD: fuzz.c,v 1.8 2015/03/03 20:42:49 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Utility functions/framework for fuzz tests */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include + +#include "test_helper.h" +#include "atomicio.h" + +/* #define FUZZ_DEBUG */ + +#ifdef FUZZ_DEBUG +# define FUZZ_DBG(x) do { \ + printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \ + printf x; \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +#else +# define FUZZ_DBG(x) +#endif + +/* For brevity later */ +typedef unsigned long long fuzz_ullong; + +/* For base-64 fuzzing */ +static const char fuzz_b64chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +struct fuzz { + /* Fuzz method currently in use */ + int strategy; + + /* Fuzz methods remaining */ + int strategies; + + /* Original seed data blob */ + void *seed; + size_t slen; + + /* Current working copy of seed with fuzz mutations applied */ + u_char *fuzzed; + + /* Used by fuzz methods */ + size_t o1, o2; +}; + +static const char * +fuzz_ntop(u_int n) +{ + switch (n) { + case 0: + return "NONE"; + case FUZZ_1_BIT_FLIP: + return "FUZZ_1_BIT_FLIP"; + case FUZZ_2_BIT_FLIP: + return "FUZZ_2_BIT_FLIP"; + case FUZZ_1_BYTE_FLIP: + return "FUZZ_1_BYTE_FLIP"; + case FUZZ_2_BYTE_FLIP: + return "FUZZ_2_BYTE_FLIP"; + case FUZZ_TRUNCATE_START: + return "FUZZ_TRUNCATE_START"; + case FUZZ_TRUNCATE_END: + return "FUZZ_TRUNCATE_END"; + case FUZZ_BASE64: + return "FUZZ_BASE64"; + default: + abort(); + } +} + +static int +fuzz_fmt(struct fuzz *fuzz, char *s, size_t n) +{ + if (fuzz == NULL) + return -1; + + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + snprintf(s, n, "%s case %zu of %zu (bit: %zu)\n", + fuzz_ntop(fuzz->strategy), + fuzz->o1, fuzz->slen * 8, fuzz->o1); + return 0; + case FUZZ_2_BIT_FLIP: + snprintf(s, n, "%s case %llu of %llu (bits: %zu, %zu)\n", + fuzz_ntop(fuzz->strategy), + (((fuzz_ullong)fuzz->o2) * fuzz->slen * 8) + fuzz->o1, + ((fuzz_ullong)fuzz->slen * 8) * fuzz->slen * 8, + fuzz->o1, fuzz->o2); + return 0; + case FUZZ_1_BYTE_FLIP: + snprintf(s, n, "%s case %zu of %zu (byte: %zu)\n", + fuzz_ntop(fuzz->strategy), + fuzz->o1, fuzz->slen, fuzz->o1); + return 0; + case FUZZ_2_BYTE_FLIP: + snprintf(s, n, "%s case %llu of %llu (bytes: %zu, %zu)\n", + fuzz_ntop(fuzz->strategy), + (((fuzz_ullong)fuzz->o2) * fuzz->slen) + fuzz->o1, + ((fuzz_ullong)fuzz->slen) * fuzz->slen, + fuzz->o1, fuzz->o2); + return 0; + case FUZZ_TRUNCATE_START: + snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n", + fuzz_ntop(fuzz->strategy), + fuzz->o1, fuzz->slen, fuzz->o1); + return 0; + case FUZZ_TRUNCATE_END: + snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n", + fuzz_ntop(fuzz->strategy), + fuzz->o1, fuzz->slen, fuzz->o1); + return 0; + case FUZZ_BASE64: + assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1); + snprintf(s, n, "%s case %llu of %llu (offset: %zu char: %c)\n", + fuzz_ntop(fuzz->strategy), + (fuzz->o1 * (fuzz_ullong)64) + fuzz->o2, + fuzz->slen * (fuzz_ullong)64, fuzz->o1, + fuzz_b64chars[fuzz->o2]); + return 0; + default: + return -1; + abort(); + } +} + +static void +dump(u_char *p, size_t len) +{ + size_t i, j; + + for (i = 0; i < len; i += 16) { + fprintf(stderr, "%.4zd: ", i); + for (j = i; j < i + 16; j++) { + if (j < len) + fprintf(stderr, "%02x ", p[j]); + else + fprintf(stderr, " "); + } + fprintf(stderr, " "); + for (j = i; j < i + 16; j++) { + if (j < len) { + if (isascii(p[j]) && isprint(p[j])) + fprintf(stderr, "%c", p[j]); + else + fprintf(stderr, "."); + } + } + fprintf(stderr, "\n"); + } +} + +void +fuzz_dump(struct fuzz *fuzz) +{ + char buf[256]; + + if (fuzz_fmt(fuzz, buf, sizeof(buf)) != 0) { + fprintf(stderr, "%s: fuzz invalid\n", __func__); + abort(); + } + fputs(buf, stderr); + fprintf(stderr, "fuzz original %p len = %zu\n", fuzz->seed, fuzz->slen); + dump(fuzz->seed, fuzz->slen); + fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, fuzz_len(fuzz)); + dump(fuzz_ptr(fuzz), fuzz_len(fuzz)); +} + +static struct fuzz *last_fuzz; + +static void +siginfo(int unused __attribute__((__unused__))) +{ + char buf[256]; + + test_info(buf, sizeof(buf)); + atomicio(vwrite, STDERR_FILENO, buf, strlen(buf)); + if (last_fuzz != NULL) { + fuzz_fmt(last_fuzz, buf, sizeof(buf)); + atomicio(vwrite, STDERR_FILENO, buf, strlen(buf)); + } +} + +struct fuzz * +fuzz_begin(u_int strategies, const void *p, size_t l) +{ + struct fuzz *ret = calloc(sizeof(*ret), 1); + + assert(p != NULL); + assert(ret != NULL); + ret->seed = malloc(l); + assert(ret->seed != NULL); + memcpy(ret->seed, p, l); + ret->slen = l; + ret->strategies = strategies; + + assert(ret->slen < SIZE_MAX / 8); + assert(ret->strategies <= (FUZZ_MAX|(FUZZ_MAX-1))); + + FUZZ_DBG(("begin, ret = %p", ret)); + + fuzz_next(ret); + + last_fuzz = ret; +#ifdef SIGINFO + signal(SIGINFO, siginfo); +#endif + signal(SIGUSR1, siginfo); + + return ret; +} + +void +fuzz_cleanup(struct fuzz *fuzz) +{ + FUZZ_DBG(("cleanup, fuzz = %p", fuzz)); + last_fuzz = NULL; +#ifdef SIGINFO + signal(SIGINFO, SIG_DFL); +#endif + signal(SIGUSR1, SIG_DFL); + assert(fuzz != NULL); + assert(fuzz->seed != NULL); + assert(fuzz->fuzzed != NULL); + free(fuzz->seed); + free(fuzz->fuzzed); + free(fuzz); +} + +static int +fuzz_strategy_done(struct fuzz *fuzz) +{ + FUZZ_DBG(("fuzz = %p, strategy = %s, o1 = %zu, o2 = %zu, slen = %zu", + fuzz, fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->o2, fuzz->slen)); + + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + return fuzz->o1 >= fuzz->slen * 8; + case FUZZ_2_BIT_FLIP: + return fuzz->o2 >= fuzz->slen * 8; + case FUZZ_2_BYTE_FLIP: + return fuzz->o2 >= fuzz->slen; + case FUZZ_1_BYTE_FLIP: + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + case FUZZ_BASE64: + return fuzz->o1 >= fuzz->slen; + default: + abort(); + } +} + +void +fuzz_next(struct fuzz *fuzz) +{ + u_int i; + + FUZZ_DBG(("start, fuzz = %p, strategy = %s, strategies = 0x%lx, " + "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy), + (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen)); + + if (fuzz->strategy == 0 || fuzz_strategy_done(fuzz)) { + /* If we are just starting out, we need to allocate too */ + if (fuzz->fuzzed == NULL) { + FUZZ_DBG(("alloc")); + fuzz->fuzzed = calloc(fuzz->slen, 1); + } + /* Pick next strategy */ + FUZZ_DBG(("advance")); + for (i = 1; i <= FUZZ_MAX; i <<= 1) { + if ((fuzz->strategies & i) != 0) { + fuzz->strategy = i; + break; + } + } + FUZZ_DBG(("selected = %u", fuzz->strategy)); + if (fuzz->strategy == 0) { + FUZZ_DBG(("done, no more strategies")); + return; + } + fuzz->strategies &= ~(fuzz->strategy); + fuzz->o1 = fuzz->o2 = 0; + } + + assert(fuzz->fuzzed != NULL); + + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + assert(fuzz->o1 / 8 < fuzz->slen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); + fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8); + fuzz->o1++; + break; + case FUZZ_2_BIT_FLIP: + assert(fuzz->o1 / 8 < fuzz->slen); + assert(fuzz->o2 / 8 < fuzz->slen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); + fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8); + fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8); + fuzz->o1++; + if (fuzz->o1 >= fuzz->slen * 8) { + fuzz->o1 = 0; + fuzz->o2++; + } + break; + case FUZZ_1_BYTE_FLIP: + assert(fuzz->o1 < fuzz->slen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); + fuzz->fuzzed[fuzz->o1] ^= 0xff; + fuzz->o1++; + break; + case FUZZ_2_BYTE_FLIP: + assert(fuzz->o1 < fuzz->slen); + assert(fuzz->o2 < fuzz->slen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); + fuzz->fuzzed[fuzz->o1] ^= 0xff; + fuzz->fuzzed[fuzz->o2] ^= 0xff; + fuzz->o1++; + if (fuzz->o1 >= fuzz->slen) { + fuzz->o1 = 0; + fuzz->o2++; + } + break; + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + assert(fuzz->o1 < fuzz->slen); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); + fuzz->o1++; + break; + case FUZZ_BASE64: + assert(fuzz->o1 < fuzz->slen); + assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1); + memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); + fuzz->fuzzed[fuzz->o1] = fuzz_b64chars[fuzz->o2]; + fuzz->o2++; + if (fuzz->o2 >= sizeof(fuzz_b64chars) - 1) { + fuzz->o2 = 0; + fuzz->o1++; + } + break; + default: + abort(); + } + + FUZZ_DBG(("done, fuzz = %p, strategy = %s, strategies = 0x%lx, " + "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy), + (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen)); +} + +int +fuzz_matches_original(struct fuzz *fuzz) +{ + if (fuzz_len(fuzz) != fuzz->slen) + return 0; + return memcmp(fuzz_ptr(fuzz), fuzz->seed, fuzz->slen) == 0; +} + +int +fuzz_done(struct fuzz *fuzz) +{ + FUZZ_DBG(("fuzz = %p, strategies = 0x%lx", fuzz, + (u_long)fuzz->strategies)); + + return fuzz_strategy_done(fuzz) && fuzz->strategies == 0; +} + +size_t +fuzz_len(struct fuzz *fuzz) +{ + assert(fuzz->fuzzed != NULL); + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + case FUZZ_2_BIT_FLIP: + case FUZZ_1_BYTE_FLIP: + case FUZZ_2_BYTE_FLIP: + case FUZZ_BASE64: + return fuzz->slen; + case FUZZ_TRUNCATE_START: + case FUZZ_TRUNCATE_END: + assert(fuzz->o1 <= fuzz->slen); + return fuzz->slen - fuzz->o1; + default: + abort(); + } +} + +u_char * +fuzz_ptr(struct fuzz *fuzz) +{ + assert(fuzz->fuzzed != NULL); + switch (fuzz->strategy) { + case FUZZ_1_BIT_FLIP: + case FUZZ_2_BIT_FLIP: + case FUZZ_1_BYTE_FLIP: + case FUZZ_2_BYTE_FLIP: + case FUZZ_BASE64: + return fuzz->fuzzed; + case FUZZ_TRUNCATE_START: + assert(fuzz->o1 <= fuzz->slen); + return fuzz->fuzzed + fuzz->o1; + case FUZZ_TRUNCATE_END: + assert(fuzz->o1 <= fuzz->slen); + return fuzz->fuzzed; + default: + abort(); + } +} + diff --git a/aosp/external/openssh/regress/unittests/test_helper/test_helper.c b/aosp/external/openssh/regress/unittests/test_helper/test_helper.c new file mode 100644 index 0000000000000000000000000000000000000000..6461d7ffc642f4b0ffd67a5de87724494c931df6 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/test_helper/test_helper.c @@ -0,0 +1,595 @@ +/* $OpenBSD: test_helper.c,v 1.13 2021/12/14 21:25:27 deraadt Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Utility functions/framework for regress tests */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#endif + +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +# include +#endif + +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + +#include "entropy.h" +#include "test_helper.h" +#include "atomicio.h" + +#define TEST_CHECK_INT(r, pred) do { \ + switch (pred) { \ + case TEST_EQ: \ + if (r == 0) \ + return; \ + break; \ + case TEST_NE: \ + if (r != 0) \ + return; \ + break; \ + case TEST_LT: \ + if (r < 0) \ + return; \ + break; \ + case TEST_LE: \ + if (r <= 0) \ + return; \ + break; \ + case TEST_GT: \ + if (r > 0) \ + return; \ + break; \ + case TEST_GE: \ + if (r >= 0) \ + return; \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +#define TEST_CHECK(x1, x2, pred) do { \ + switch (pred) { \ + case TEST_EQ: \ + if (x1 == x2) \ + return; \ + break; \ + case TEST_NE: \ + if (x1 != x2) \ + return; \ + break; \ + case TEST_LT: \ + if (x1 < x2) \ + return; \ + break; \ + case TEST_LE: \ + if (x1 <= x2) \ + return; \ + break; \ + case TEST_GT: \ + if (x1 > x2) \ + return; \ + break; \ + case TEST_GE: \ + if (x1 >= x2) \ + return; \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +extern char *__progname; + +static int verbose_mode = 0; +static int quiet_mode = 0; +static char *active_test_name = NULL; +static u_int test_number = 0; +static test_onerror_func_t *test_onerror = NULL; +static void *onerror_ctx = NULL; +static const char *data_dir = NULL; +static char subtest_info[512]; +static int fast = 0; +static int slow = 0; + +int +main(int argc, char **argv) +{ + int ch; + + seed_rng(); +#ifdef WITH_OPENSSL + ERR_load_CRYPTO_strings(); +#endif + + /* Handle systems without __progname */ + if (__progname == NULL) { + __progname = strrchr(argv[0], '/'); + if (__progname == NULL || __progname[1] == '\0') + __progname = argv[0]; + else + __progname++; + if ((__progname = strdup(__progname)) == NULL) { + fprintf(stderr, "strdup failed\n"); + exit(1); + } + } + + while ((ch = getopt(argc, argv, "Ffvqd:")) != -1) { + switch (ch) { + case 'F': + slow = 1; + break; + case 'f': + fast = 1; + break; + case 'd': + data_dir = optarg; + break; + case 'q': + verbose_mode = 0; + quiet_mode = 1; + break; + case 'v': + verbose_mode = 1; + quiet_mode = 0; + break; + default: + fprintf(stderr, "Unrecognised command line option\n"); + fprintf(stderr, "Usage: %s [-v]\n", __progname); + exit(1); + } + } + setvbuf(stdout, NULL, _IONBF, 0); + if (!quiet_mode) + printf("%s: ", __progname); + if (verbose_mode) + printf("\n"); + + tests(); + + if (!quiet_mode) + printf(" %u tests ok\n", test_number); + return 0; +} + +int +test_is_verbose(void) +{ + return verbose_mode; +} + +int +test_is_quiet(void) +{ + return quiet_mode; +} + +int +test_is_fast(void) +{ + return fast; +} + +int +test_is_slow(void) +{ + return slow; +} + +const char * +test_data_file(const char *name) +{ + static char ret[PATH_MAX]; + + if (data_dir != NULL) + snprintf(ret, sizeof(ret), "%s/%s", data_dir, name); + else + strlcpy(ret, name, sizeof(ret)); + if (access(ret, F_OK) != 0) { + fprintf(stderr, "Cannot access data file %s: %s\n", + ret, strerror(errno)); + exit(1); + } + return ret; +} + +void +test_info(char *s, size_t len) +{ + snprintf(s, len, "In test %u: \"%s\"%s%s\n", test_number, + active_test_name == NULL ? "" : active_test_name, + *subtest_info != '\0' ? " - " : "", subtest_info); +} + +static void +siginfo(int unused __attribute__((__unused__))) +{ + char buf[256]; + + test_info(buf, sizeof(buf)); + atomicio(vwrite, STDERR_FILENO, buf, strlen(buf)); +} + +void +test_start(const char *n) +{ + assert(active_test_name == NULL); + assert((active_test_name = strdup(n)) != NULL); + *subtest_info = '\0'; + if (verbose_mode) + printf("test %u - \"%s\": ", test_number, active_test_name); + test_number++; +#ifdef SIGINFO + signal(SIGINFO, siginfo); +#endif + signal(SIGUSR1, siginfo); +} + +void +set_onerror_func(test_onerror_func_t *f, void *ctx) +{ + test_onerror = f; + onerror_ctx = ctx; +} + +void +test_done(void) +{ + *subtest_info = '\0'; + assert(active_test_name != NULL); + free(active_test_name); + active_test_name = NULL; + if (verbose_mode) + printf("OK\n"); + else if (!quiet_mode) { + printf("."); + fflush(stdout); + } +} + +void +test_subtest_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(subtest_info, sizeof(subtest_info), fmt, ap); + va_end(ap); +} + +void +ssl_err_check(const char *file, int line) +{ +#ifdef WITH_OPENSSL + long openssl_error = ERR_get_error(); + + if (openssl_error == 0) + return; + + fprintf(stderr, "\n%s:%d: uncaught OpenSSL error: %s", + file, line, ERR_error_string(openssl_error, NULL)); +#else /* WITH_OPENSSL */ + fprintf(stderr, "\n%s:%d: uncaught OpenSSL error ", + file, line); +#endif /* WITH_OPENSSL */ + abort(); +} + +static const char * +pred_name(enum test_predicate p) +{ + switch (p) { + case TEST_EQ: + return "EQ"; + case TEST_NE: + return "NE"; + case TEST_LT: + return "LT"; + case TEST_LE: + return "LE"; + case TEST_GT: + return "GT"; + case TEST_GE: + return "GE"; + default: + return "UNKNOWN"; + } +} + +static void +test_die(void) +{ + if (test_onerror != NULL) + test_onerror(onerror_ctx); + abort(); +} + +static void +test_header(const char *file, int line, const char *a1, const char *a2, + const char *name, enum test_predicate pred) +{ + fprintf(stderr, "\n%s:%d test #%u \"%s\"%s%s\n", + file, line, test_number, active_test_name, + *subtest_info != '\0' ? " - " : "", subtest_info); + fprintf(stderr, "ASSERT_%s_%s(%s%s%s) failed:\n", + name, pred_name(pred), a1, + a2 != NULL ? ", " : "", a2 != NULL ? a2 : ""); +} + +#ifdef WITH_OPENSSL +void +assert_bignum(const char *file, int line, const char *a1, const char *a2, + const BIGNUM *aa1, const BIGNUM *aa2, enum test_predicate pred) +{ + int r = BN_cmp(aa1, aa2); + + TEST_CHECK_INT(r, pred); + test_header(file, line, a1, a2, "BIGNUM", pred); + fprintf(stderr, "%12s = 0x%s\n", a1, BN_bn2hex(aa1)); + fprintf(stderr, "%12s = 0x%s\n", a2, BN_bn2hex(aa2)); + test_die(); +} +#endif + +void +assert_string(const char *file, int line, const char *a1, const char *a2, + const char *aa1, const char *aa2, enum test_predicate pred) +{ + int r; + + /* Verify pointers are not NULL */ + assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE); + assert_ptr(file, line, a2, "NULL", aa2, NULL, TEST_NE); + + r = strcmp(aa1, aa2); + TEST_CHECK_INT(r, pred); + test_header(file, line, a1, a2, "STRING", pred); + fprintf(stderr, "%12s = %s (len %zu)\n", a1, aa1, strlen(aa1)); + fprintf(stderr, "%12s = %s (len %zu)\n", a2, aa2, strlen(aa2)); + test_die(); +} + +static char * +tohex(const void *_s, size_t l) +{ + u_int8_t *s = (u_int8_t *)_s; + size_t i, j; + const char *hex = "0123456789abcdef"; + char *r = malloc((l * 2) + 1); + + assert(r != NULL); + for (i = j = 0; i < l; i++) { + r[j++] = hex[(s[i] >> 4) & 0xf]; + r[j++] = hex[s[i] & 0xf]; + } + r[j] = '\0'; + return r; +} + +void +assert_mem(const char *file, int line, const char *a1, const char *a2, + const void *aa1, const void *aa2, size_t l, enum test_predicate pred) +{ + int r; + char *aa1_tohex = NULL; + char *aa2_tohex = NULL; + + if (l == 0) + return; + /* If length is >0, then verify pointers are not NULL */ + assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE); + assert_ptr(file, line, a2, "NULL", aa2, NULL, TEST_NE); + + r = memcmp(aa1, aa2, l); + TEST_CHECK_INT(r, pred); + test_header(file, line, a1, a2, "STRING", pred); + aa1_tohex = tohex(aa1, MINIMUM(l, 256)); + aa2_tohex = tohex(aa2, MINIMUM(l, 256)); + fprintf(stderr, "%12s = %s (len %zu)\n", a1, aa1_tohex, l); + fprintf(stderr, "%12s = %s (len %zu)\n", a2, aa2_tohex, l); + free(aa1_tohex); + free(aa2_tohex); + test_die(); +} + +static int +memvalcmp(const u_int8_t *s, u_char v, size_t l, size_t *where) +{ + size_t i; + + for (i = 0; i < l; i++) { + if (s[i] != v) { + *where = i; + return 1; + } + } + return 0; +} + +void +assert_mem_filled(const char *file, int line, const char *a1, + const void *aa1, u_char v, size_t l, enum test_predicate pred) +{ + size_t where = -1; + int r; + char tmp[64]; + char *aa1_tohex = NULL; + + if (l == 0) + return; + /* If length is >0, then verify the pointer is not NULL */ + assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE); + + r = memvalcmp(aa1, v, l, &where); + TEST_CHECK_INT(r, pred); + test_header(file, line, a1, NULL, "MEM_ZERO", pred); + aa1_tohex = tohex(aa1, MINIMUM(l, 20)); + fprintf(stderr, "%20s = %s%s (len %zu)\n", a1, + aa1_tohex, l > 20 ? "..." : "", l); + free(aa1_tohex); + snprintf(tmp, sizeof(tmp), "(%s)[%zu]", a1, where); + fprintf(stderr, "%20s = 0x%02x (expected 0x%02x)\n", tmp, + ((u_char *)aa1)[where], v); + test_die(); +} + +void +assert_int(const char *file, int line, const char *a1, const char *a2, + int aa1, int aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "INT", pred); + fprintf(stderr, "%12s = %d\n", a1, aa1); + fprintf(stderr, "%12s = %d\n", a2, aa2); + test_die(); +} + +void +assert_size_t(const char *file, int line, const char *a1, const char *a2, + size_t aa1, size_t aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "SIZE_T", pred); + fprintf(stderr, "%12s = %zu\n", a1, aa1); + fprintf(stderr, "%12s = %zu\n", a2, aa2); + test_die(); +} + +void +assert_u_int(const char *file, int line, const char *a1, const char *a2, + u_int aa1, u_int aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "U_INT", pred); + fprintf(stderr, "%12s = %u / 0x%x\n", a1, aa1, aa1); + fprintf(stderr, "%12s = %u / 0x%x\n", a2, aa2, aa2); + test_die(); +} + +void +assert_long(const char *file, int line, const char *a1, const char *a2, + long aa1, long aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "LONG", pred); + fprintf(stderr, "%12s = %ld / 0x%lx\n", a1, aa1, aa1); + fprintf(stderr, "%12s = %ld / 0x%lx\n", a2, aa2, aa2); + test_die(); +} + +void +assert_long_long(const char *file, int line, const char *a1, const char *a2, + long long aa1, long long aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "LONG LONG", pred); + fprintf(stderr, "%12s = %lld / 0x%llx\n", a1, aa1, aa1); + fprintf(stderr, "%12s = %lld / 0x%llx\n", a2, aa2, aa2); + test_die(); +} + +void +assert_char(const char *file, int line, const char *a1, const char *a2, + char aa1, char aa2, enum test_predicate pred) +{ + char buf[8]; + + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "CHAR", pred); + fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1, + vis(buf, aa1, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa1); + fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1, + vis(buf, aa2, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa2); + test_die(); +} + +void +assert_u8(const char *file, int line, const char *a1, const char *a2, + u_int8_t aa1, u_int8_t aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "U8", pred); + fprintf(stderr, "%12s = 0x%02x %u\n", a1, aa1, aa1); + fprintf(stderr, "%12s = 0x%02x %u\n", a2, aa2, aa2); + test_die(); +} + +void +assert_u16(const char *file, int line, const char *a1, const char *a2, + u_int16_t aa1, u_int16_t aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "U16", pred); + fprintf(stderr, "%12s = 0x%04x %u\n", a1, aa1, aa1); + fprintf(stderr, "%12s = 0x%04x %u\n", a2, aa2, aa2); + test_die(); +} + +void +assert_u32(const char *file, int line, const char *a1, const char *a2, + u_int32_t aa1, u_int32_t aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "U32", pred); + fprintf(stderr, "%12s = 0x%08x %u\n", a1, aa1, aa1); + fprintf(stderr, "%12s = 0x%08x %u\n", a2, aa2, aa2); + test_die(); +} + +void +assert_u64(const char *file, int line, const char *a1, const char *a2, + u_int64_t aa1, u_int64_t aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "U64", pred); + fprintf(stderr, "%12s = 0x%016llx %llu\n", a1, + (unsigned long long)aa1, (unsigned long long)aa1); + fprintf(stderr, "%12s = 0x%016llx %llu\n", a2, + (unsigned long long)aa2, (unsigned long long)aa2); + test_die(); +} + +void +assert_ptr(const char *file, int line, const char *a1, const char *a2, + const void *aa1, const void *aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "PTR", pred); + fprintf(stderr, "%12s = %p\n", a1, aa1); + fprintf(stderr, "%12s = %p\n", a2, aa2); + test_die(); +} + diff --git a/aosp/external/openssh/regress/unittests/test_helper/test_helper.h b/aosp/external/openssh/regress/unittests/test_helper/test_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..66302201cec34cdb0c29096d6e62e2c877caa332 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/test_helper/test_helper.h @@ -0,0 +1,326 @@ +/* $OpenBSD: test_helper.h,v 1.9 2018/10/17 23:28:05 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Utility functions/framework for regress tests */ + +#ifndef _TEST_HELPER_H +#define _TEST_HELPER_H + +#include "includes.h" + +#include +#ifdef HAVE_STDINT_H +# include +#endif + +#ifdef WITH_OPENSSL +#include +#include +#endif + +enum test_predicate { + TEST_EQ, TEST_NE, TEST_LT, TEST_LE, TEST_GT, TEST_GE +}; +typedef void (test_onerror_func_t)(void *); + +/* Supplied by test suite */ +void tests(void); + +const char *test_data_file(const char *name); +void test_start(const char *n); +void test_info(char *s, size_t len); +void set_onerror_func(test_onerror_func_t *f, void *ctx); +void test_done(void); +int test_is_verbose(void); +int test_is_quiet(void); +int test_is_fast(void); +int test_is_slow(void); +void test_subtest_info(const char *fmt, ...) + __attribute__((format(printf, 1, 2))); +void ssl_err_check(const char *file, int line); +#ifdef WITH_OPENSSL +void assert_bignum(const char *file, int line, + const char *a1, const char *a2, + const BIGNUM *aa1, const BIGNUM *aa2, enum test_predicate pred); +#endif +void assert_string(const char *file, int line, + const char *a1, const char *a2, + const char *aa1, const char *aa2, enum test_predicate pred); +void assert_mem(const char *file, int line, + const char *a1, const char *a2, + const void *aa1, const void *aa2, size_t l, enum test_predicate pred); +void assert_mem_filled(const char *file, int line, + const char *a1, + const void *aa1, u_char v, size_t l, enum test_predicate pred); +void assert_int(const char *file, int line, + const char *a1, const char *a2, + int aa1, int aa2, enum test_predicate pred); +void assert_size_t(const char *file, int line, + const char *a1, const char *a2, + size_t aa1, size_t aa2, enum test_predicate pred); +void assert_u_int(const char *file, int line, + const char *a1, const char *a2, + u_int aa1, u_int aa2, enum test_predicate pred); +void assert_long(const char *file, int line, + const char *a1, const char *a2, + long aa1, long aa2, enum test_predicate pred); +void assert_long_long(const char *file, int line, + const char *a1, const char *a2, + long long aa1, long long aa2, enum test_predicate pred); +void assert_char(const char *file, int line, + const char *a1, const char *a2, + char aa1, char aa2, enum test_predicate pred); +void assert_ptr(const char *file, int line, + const char *a1, const char *a2, + const void *aa1, const void *aa2, enum test_predicate pred); +void assert_u8(const char *file, int line, + const char *a1, const char *a2, + u_int8_t aa1, u_int8_t aa2, enum test_predicate pred); +void assert_u16(const char *file, int line, + const char *a1, const char *a2, + u_int16_t aa1, u_int16_t aa2, enum test_predicate pred); +void assert_u32(const char *file, int line, + const char *a1, const char *a2, + u_int32_t aa1, u_int32_t aa2, enum test_predicate pred); +void assert_u64(const char *file, int line, + const char *a1, const char *a2, + u_int64_t aa1, u_int64_t aa2, enum test_predicate pred); + +#define TEST_START(n) test_start(n) +#define TEST_DONE() test_done() +#define TEST_ONERROR(f, c) set_onerror_func(f, c) +#define SSL_ERR_CHECK() ssl_err_check(__FILE__, __LINE__) + +#define ASSERT_BIGNUM_EQ(a1, a2) \ + assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_STRING_EQ(a1, a2) \ + assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_MEM_EQ(a1, a2, l) \ + assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_EQ) +#define ASSERT_MEM_FILLED_EQ(a1, c, l) \ + assert_mem_filled(__FILE__, __LINE__, #a1, a1, c, l, TEST_EQ) +#define ASSERT_MEM_ZERO_EQ(a1, l) \ + assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_EQ) +#define ASSERT_INT_EQ(a1, a2) \ + assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_SIZE_T_EQ(a1, a2) \ + assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_U_INT_EQ(a1, a2) \ + assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_LONG_EQ(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_LONG_LONG_EQ(a1, a2) \ + assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_CHAR_EQ(a1, a2) \ + assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_PTR_EQ(a1, a2) \ + assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_U8_EQ(a1, a2) \ + assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_U16_EQ(a1, a2) \ + assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_U32_EQ(a1, a2) \ + assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_U64_EQ(a1, a2) \ + assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) + +#define ASSERT_BIGNUM_NE(a1, a2) \ + assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_STRING_NE(a1, a2) \ + assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_MEM_NE(a1, a2, l) \ + assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_NE) +#define ASSERT_MEM_ZERO_NE(a1, l) \ + assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_NE) +#define ASSERT_INT_NE(a1, a2) \ + assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_SIZE_T_NE(a1, a2) \ + assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_U_INT_NE(a1, a2) \ + assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_LONG_NE(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_LONG_LONG_NE(a1, a2) \ + assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_CHAR_NE(a1, a2) \ + assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_PTR_NE(a1, a2) \ + assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_U8_NE(a1, a2) \ + assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_U16_NE(a1, a2) \ + assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_U32_NE(a1, a2) \ + assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_U64_NE(a1, a2) \ + assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) + +#define ASSERT_BIGNUM_LT(a1, a2) \ + assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_STRING_LT(a1, a2) \ + assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_MEM_LT(a1, a2, l) \ + assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LT) +#define ASSERT_INT_LT(a1, a2) \ + assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_SIZE_T_LT(a1, a2) \ + assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_U_INT_LT(a1, a2) \ + assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_LONG_LT(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_LONG_LONG_LT(a1, a2) \ + assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_CHAR_LT(a1, a2) \ + assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_PTR_LT(a1, a2) \ + assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_U8_LT(a1, a2) \ + assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_U16_LT(a1, a2) \ + assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_U32_LT(a1, a2) \ + assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_U64_LT(a1, a2) \ + assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) + +#define ASSERT_BIGNUM_LE(a1, a2) \ + assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_STRING_LE(a1, a2) \ + assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_MEM_LE(a1, a2, l) \ + assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LE) +#define ASSERT_INT_LE(a1, a2) \ + assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_SIZE_T_LE(a1, a2) \ + assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_U_INT_LE(a1, a2) \ + assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_LONG_LE(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_LONG_LONG_LE(a1, a2) \ + assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_CHAR_LE(a1, a2) \ + assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_PTR_LE(a1, a2) \ + assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_U8_LE(a1, a2) \ + assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_U16_LE(a1, a2) \ + assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_U32_LE(a1, a2) \ + assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_U64_LE(a1, a2) \ + assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) + +#define ASSERT_BIGNUM_GT(a1, a2) \ + assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_STRING_GT(a1, a2) \ + assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_MEM_GT(a1, a2, l) \ + assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GT) +#define ASSERT_INT_GT(a1, a2) \ + assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_SIZE_T_GT(a1, a2) \ + assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_U_INT_GT(a1, a2) \ + assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_LONG_GT(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_LONG_LONG_GT(a1, a2) \ + assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_CHAR_GT(a1, a2) \ + assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_PTR_GT(a1, a2) \ + assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_U8_GT(a1, a2) \ + assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_U16_GT(a1, a2) \ + assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_U32_GT(a1, a2) \ + assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_U64_GT(a1, a2) \ + assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) + +#define ASSERT_BIGNUM_GE(a1, a2) \ + assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_STRING_GE(a1, a2) \ + assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_MEM_GE(a1, a2, l) \ + assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GE) +#define ASSERT_INT_GE(a1, a2) \ + assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_SIZE_T_GE(a1, a2) \ + assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_U_INT_GE(a1, a2) \ + assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_LONG_GE(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_LONG_LONG_GE(a1, a2) \ + assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_CHAR_GE(a1, a2) \ + assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_PTR_GE(a1, a2) \ + assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_U8_GE(a1, a2) \ + assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_U16_GE(a1, a2) \ + assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_U32_GE(a1, a2) \ + assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_U64_GE(a1, a2) \ + assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) + +/* Fuzzing support */ + +struct fuzz; +#define FUZZ_1_BIT_FLIP 0x00000001 /* Flip one bit at a time */ +#define FUZZ_2_BIT_FLIP 0x00000002 /* Flip two bits at a time */ +#define FUZZ_1_BYTE_FLIP 0x00000004 /* Flip one byte at a time */ +#define FUZZ_2_BYTE_FLIP 0x00000008 /* Flip two bytes at a time */ +#define FUZZ_TRUNCATE_START 0x00000010 /* Truncate from beginning */ +#define FUZZ_TRUNCATE_END 0x00000020 /* Truncate from end */ +#define FUZZ_BASE64 0x00000040 /* Try all base64 chars */ +#define FUZZ_MAX FUZZ_BASE64 + +/* Start fuzzing a blob of data with selected strategies (bitmask) */ +struct fuzz *fuzz_begin(u_int strategies, const void *p, size_t l); + +/* Free a fuzz context */ +void fuzz_cleanup(struct fuzz *fuzz); + +/* Prepare the next fuzz case in the series */ +void fuzz_next(struct fuzz *fuzz); + +/* + * Check whether this fuzz case is identical to the original + * This is slow, but useful if the caller needs to ensure that all tests + * generated change the input (e.g. when fuzzing signatures). + */ +int fuzz_matches_original(struct fuzz *fuzz); + +/* Determine whether the current fuzz sequence is exhausted (nonzero = yes) */ +int fuzz_done(struct fuzz *fuzz); + +/* Return the length and a pointer to the current fuzzed case */ +size_t fuzz_len(struct fuzz *fuzz); +u_char *fuzz_ptr(struct fuzz *fuzz); + +/* Dump the current fuzz case to stderr */ +void fuzz_dump(struct fuzz *fuzz); + +#endif /* _TEST_HELPER_H */ diff --git a/aosp/external/openssh/regress/unittests/utf8/Makefile b/aosp/external/openssh/regress/unittests/utf8/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f8eec0484f8fa4200bfd34b22ce901f92f9e8f5c --- /dev/null +++ b/aosp/external/openssh/regress/unittests/utf8/Makefile @@ -0,0 +1,14 @@ +# $OpenBSD: Makefile,v 1.5 2017/12/21 00:41:22 djm Exp $ + +PROG=test_utf8 +SRCS=tests.c + +# From usr.bin/ssh +SRCS+=utf8.c atomicio.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include diff --git a/aosp/external/openssh/regress/unittests/utf8/tests.c b/aosp/external/openssh/regress/unittests/utf8/tests.c new file mode 100644 index 0000000000000000000000000000000000000000..8cf524ddb2106ba4e9a6faa042a839d70f0107e2 --- /dev/null +++ b/aosp/external/openssh/regress/unittests/utf8/tests.c @@ -0,0 +1,104 @@ +/* $OpenBSD: tests.c,v 1.4 2017/02/19 00:11:29 djm Exp $ */ +/* + * Regress test for the utf8.h *mprintf() API + * + * Written by Ingo Schwarze in 2016 + * and placed in the public domain. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include "../test_helper/test_helper.h" + +#include "utf8.h" + +static void +badarg(void) +{ + char buf[16]; + int len, width; + + width = 1; + TEST_START("utf8_badarg"); + len = snmprintf(buf, sizeof(buf), &width, "\377"); + ASSERT_INT_EQ(len, -1); + ASSERT_STRING_EQ(buf, ""); + ASSERT_INT_EQ(width, 0); + TEST_DONE(); +} + +static void +one(int utf8, const char *name, const char *mbs, int width, + int wantwidth, int wantlen, const char *wants) +{ + char buf[16]; + int *wp; + int len; + + if (wantlen == -2) + wantlen = strlen(wants); + (void)strlcpy(buf, utf8 ? "utf8_" : "c_", sizeof(buf)); + (void)strlcat(buf, name, sizeof(buf)); + TEST_START(buf); + wp = wantwidth == -2 ? NULL : &width; + len = snmprintf(buf, sizeof(buf), wp, "%s", mbs); + ASSERT_INT_EQ(len, wantlen); + ASSERT_STRING_EQ(buf, wants); + ASSERT_INT_EQ(width, wantwidth); + TEST_DONE(); +} + +void +tests(void) +{ + char *loc; + + TEST_START("utf8_setlocale"); + loc = setlocale(LC_CTYPE, "en_US.UTF-8"); + ASSERT_PTR_NE(loc, NULL); + TEST_DONE(); + + badarg(); + one(1, "empty", "", 2, 0, 0, ""); + one(1, "ascii", "x", -2, -2, -2, "x"); + one(1, "newline", "a\nb", -2, -2, -2, "a\nb"); + one(1, "cr", "a\rb", -2, -2, -2, "a\rb"); + one(1, "tab", "a\tb", -2, -2, -2, "a\tb"); + one(1, "esc", "\033x", -2, -2, -2, "\\033x"); + one(1, "inv_badbyte", "\377x", -2, -2, -2, "\\377x"); + one(1, "inv_nocont", "\341x", -2, -2, -2, "\\341x"); + one(1, "inv_nolead", "a\200b", -2, -2, -2, "a\\200b"); + one(1, "sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345"); + one(1, "sz_esc", "123456789012\033", -2, -2, 16, "123456789012"); + one(1, "width_ascii", "123", 2, 2, -1, "12"); + one(1, "width_double", "a\343\201\201", 2, 1, -1, "a"); + one(1, "double_fit", "a\343\201\201", 3, 3, 4, "a\343\201\201"); + one(1, "double_spc", "a\343\201\201", 4, 3, 4, "a\343\201\201"); + + TEST_START("C_setlocale"); + loc = setlocale(LC_CTYPE, "C"); + ASSERT_PTR_NE(loc, NULL); + TEST_DONE(); + + badarg(); + one(0, "empty", "", 2, 0, 0, ""); + one(0, "ascii", "x", -2, -2, -2, "x"); + one(0, "newline", "a\nb", -2, -2, -2, "a\nb"); + one(0, "cr", "a\rb", -2, -2, -2, "a\rb"); + one(0, "tab", "a\tb", -2, -2, -2, "a\tb"); + one(0, "esc", "\033x", -2, -2, -2, "\\033x"); + one(0, "inv_badbyte", "\377x", -2, -2, -2, "\\377x"); + one(0, "inv_nocont", "\341x", -2, -2, -2, "\\341x"); + one(0, "inv_nolead", "a\200b", -2, -2, -2, "a\\200b"); + one(0, "sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345"); + one(0, "sz_esc", "123456789012\033", -2, -2, 16, "123456789012"); + one(0, "width_ascii", "123", 2, 2, -1, "12"); + one(0, "width_double", "a\343\201\201", 2, 1, -1, "a"); + one(0, "double_fit", "a\343\201\201", 7, 5, -1, "a\\343"); + one(0, "double_spc", "a\343\201\201", 13, 13, 13, "a\\343\\201\\201"); +} diff --git a/aosp/external/openssh/regress/valgrind-unit.sh b/aosp/external/openssh/regress/valgrind-unit.sh new file mode 100644 index 0000000000000000000000000000000000000000..193289e6b78e42ea46528a2eb19107fddb5366ba --- /dev/null +++ b/aosp/external/openssh/regress/valgrind-unit.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +UNIT_BINARY="$1" +shift +UNIT_ARGS="$@" + +test "x$OBJ" = "x" && OBJ=$PWD + +# This mostly replicates the logic in test-exec.sh for running the +# regress tests under valgrind, except that we unconditionally enable +# leak checking because the unit tests should be clean. +VG_LEAK="--leak-check=full" +VG_TEST=`basename $UNIT_BINARY` +VG_LOG="$OBJ/valgrind-out/${VG_TEST}.%p" +VG_OPTS="--track-origins=yes $VG_LEAK --log-file=${VG_LOG}" +VG_OPTS="$VG_OPTS --trace-children=yes" +VG_PATH="valgrind" +if [ "x$VALGRIND_PATH" != "x" ]; then + VG_PATH="$VALGRIND_PATH" +fi + +mkdir -p "$OBJ/valgrind-out" + +exec $VG_PATH $VG_OPTS $UNIT_BINARY $UNIT_ARGS diff --git a/aosp/external/openssh/regress/yes-head.sh b/aosp/external/openssh/regress/yes-head.sh new file mode 100644 index 0000000000000000000000000000000000000000..2759eb8ce59d5bab24cc2309d18dfc30defc9cc2 --- /dev/null +++ b/aosp/external/openssh/regress/yes-head.sh @@ -0,0 +1,13 @@ +# $OpenBSD: yes-head.sh,v 1.6 2017/04/30 23:34:55 djm Exp $ +# Placed in the Public Domain. + +tid="yes pipe head" + +lines=`${SSH} -F $OBJ/ssh_proxy thishost 'sh -c "while true;do echo yes;done | _POSIX2_VERSION=199209 head -2000"' | (sleep 3 ; wc -l)` +if [ $? -ne 0 ]; then + fail "yes|head test failed" + lines = 0; +fi +if [ $lines -ne 2000 ]; then + fail "yes|head returns $lines lines instead of 2000" +fi diff --git a/aosp/external/openssh/rijndael.c b/aosp/external/openssh/rijndael.c new file mode 100644 index 0000000000000000000000000000000000000000..40ab7b1f51030336699afad670a5b154376c6918 --- /dev/null +++ b/aosp/external/openssh/rijndael.c @@ -0,0 +1,1129 @@ +/* $OpenBSD: rijndael.c,v 1.20 2015/03/16 11:09:52 djm Exp $ */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include "rijndael.h" + +#undef FULL_UNROLL + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +#if 0 +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u8 Td4[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +#endif +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int +rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) +{ + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te0[(temp ) & 0xff] & 0x0000ff00) ^ + (Te1[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te0[(temp ) & 0xff] & 0x0000ff00) ^ + (Te1[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te0[(temp ) & 0xff] & 0x0000ff00) ^ + (Te1[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te2[(temp >> 24) ] & 0xff000000) ^ + (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + rk += 8; + } + } + return 0; +} + +#if 0 +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int +rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) +{ + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te1[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te1[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te1[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te1[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[3] ) & 0xff] & 0xff]; + } + return Nr; +} +#endif + +void +rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], + u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te2[(t0 >> 24) ] & 0xff000000) ^ + (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te2[(t1 >> 24) ] & 0xff000000) ^ + (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te2[(t2 >> 24) ] & 0xff000000) ^ + (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te2[(t3 >> 24) ] & 0xff000000) ^ + (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +#if 0 +static void +rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], + u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] << 24) ^ + (Td4[(t3 >> 16) & 0xff] << 16) ^ + (Td4[(t2 >> 8) & 0xff] << 8) ^ + (Td4[(t1 ) & 0xff]) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] << 24) ^ + (Td4[(t0 >> 16) & 0xff] << 16) ^ + (Td4[(t3 >> 8) & 0xff] << 8) ^ + (Td4[(t2 ) & 0xff]) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] << 24) ^ + (Td4[(t1 >> 16) & 0xff] << 16) ^ + (Td4[(t0 >> 8) & 0xff] << 8) ^ + (Td4[(t3 ) & 0xff]) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] << 24) ^ + (Td4[(t2 >> 16) & 0xff] << 16) ^ + (Td4[(t1 >> 8) & 0xff] << 8) ^ + (Td4[(t0 ) & 0xff]) ^ + rk[3]; + PUTU32(pt + 12, s3); +} +#endif diff --git a/aosp/external/openssh/rijndael.h b/aosp/external/openssh/rijndael.h new file mode 100644 index 0000000000000000000000000000000000000000..e04324b612924364ba3bf4dae43e0c746d8ad49e --- /dev/null +++ b/aosp/external/openssh/rijndael.h @@ -0,0 +1,55 @@ +/* $OpenBSD: rijndael.h,v 1.15 2021/09/28 11:14:50 dtucker Exp $ */ + +/** + * rijndael-alg-fst.h + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _PRIVATE_RIJNDAEL_H +#define _PRIVATE_RIJNDAEL_H + +#define AES_MAXKEYBITS (256) +#define AES_MAXKEYBYTES (AES_MAXKEYBITS/8) +/* for 256-bit keys, fewer for less */ +#define AES_MAXROUNDS 14 + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +int rijndaelKeySetupEnc(unsigned int [], const unsigned char [], int); +void rijndaelEncrypt(const unsigned int [], int, const u8 [16], u8 [16]); + +/* The structure for key information */ +typedef struct { + int decrypt; + int Nr; /* key-length-dependent number of rounds */ + u32 ek[4*(AES_MAXROUNDS + 1)]; /* encrypt key schedule */ + u32 dk[4*(AES_MAXROUNDS + 1)]; /* decrypt key schedule */ +} rijndael_ctx; + +void rijndael_set_key(rijndael_ctx *, u_char *, int, int); +void rijndael_decrypt(rijndael_ctx *, u_char *, u_char *); +void rijndael_encrypt(rijndael_ctx *, u_char *, u_char *); + +#endif /* _PRIVATE_RIJNDAEL_H */ diff --git a/aosp/external/openssh/sandbox-capsicum.c b/aosp/external/openssh/sandbox-capsicum.c new file mode 100644 index 0000000000000000000000000000000000000000..883be185815a4d77afed570fc87e3f7f724404ed --- /dev/null +++ b/aosp/external/openssh/sandbox-capsicum.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011 Dag-Erling Smorgrav + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_CAPSICUM + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "monitor.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +/* + * Capsicum sandbox that sets zero nfiles, nprocs and filesize rlimits, + * limits rights on stdout, stdin, stderr, monitor and switches to + * capability mode. + */ + +struct ssh_sandbox { + struct monitor *monitor; + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + debug3("%s: preparing capsicum sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + box->monitor = monitor; + box->child_pid = 0; + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + struct rlimit rl_zero; + cap_rights_t rights; + + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + + if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", + __func__, strerror(errno)); +#ifndef SANDBOX_SKIP_RLIMIT_NOFILE + if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", + __func__, strerror(errno)); +#endif + if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", + __func__, strerror(errno)); + + cap_rights_init(&rights); + + if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS) + fatal("can't limit stdin: %m"); + if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) + fatal("can't limit stdout: %m"); + if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS) + fatal("can't limit stderr: %m"); + + cap_rights_init(&rights, CAP_READ, CAP_WRITE); + if (cap_rights_limit(box->monitor->m_recvfd, &rights) < 0 && + errno != ENOSYS) + fatal("%s: failed to limit the network socket", __func__); + cap_rights_init(&rights, CAP_WRITE); + if (cap_rights_limit(box->monitor->m_log_sendfd, &rights) < 0 && + errno != ENOSYS) + fatal("%s: failed to limit the logging socket", __func__); + if (cap_enter() < 0 && errno != ENOSYS) + fatal("%s: failed to enter capability mode", __func__); + +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; +} + +#endif /* SANDBOX_CAPSICUM */ diff --git a/aosp/external/openssh/sandbox-darwin.c b/aosp/external/openssh/sandbox-darwin.c new file mode 100644 index 0000000000000000000000000000000000000000..59b4d286eea54d62a094e5dab4017682509b45b6 --- /dev/null +++ b/aosp/external/openssh/sandbox-darwin.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_DARWIN + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ssh-sandbox.h" +#include "monitor.h" +#include "xmalloc.h" + +/* Darwin/OS X sandbox */ + +struct ssh_sandbox { + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + debug3("%s: preparing Darwin sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + box->child_pid = 0; + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + char *errmsg; + struct rlimit rl_zero; + + debug3("%s: starting Darwin sandbox", __func__); + if (sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, + &errmsg) == -1) + fatal("%s: sandbox_init: %s", __func__, errmsg); + + /* + * The kSBXProfilePureComputation still allows sockets, so + * we must disable these using rlimit. + */ + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", + __func__, strerror(errno)); + if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", + __func__, strerror(errno)); + if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", + __func__, strerror(errno)); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; +} + +#endif /* SANDBOX_DARWIN */ diff --git a/aosp/external/openssh/sandbox-null.c b/aosp/external/openssh/sandbox-null.c new file mode 100644 index 0000000000000000000000000000000000000000..d4cb9188b61dfd89fc3877863c85a56c63ded35c --- /dev/null +++ b/aosp/external/openssh/sandbox-null.c @@ -0,0 +1,72 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_NULL + +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +/* dummy sandbox */ + +struct ssh_sandbox { + int junk; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + box = xcalloc(1, sizeof(*box)); + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + /* Nothing to do here */ +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + /* Nothing to do here */ +} + +#endif /* SANDBOX_NULL */ diff --git a/aosp/external/openssh/sandbox-pledge.c b/aosp/external/openssh/sandbox-pledge.c new file mode 100644 index 0000000000000000000000000000000000000000..302f1cfedd3dab43e7f53870b37515f0ad6970b7 --- /dev/null +++ b/aosp/external/openssh/sandbox-pledge.c @@ -0,0 +1,77 @@ +/* $OpenBSD: sandbox-pledge.c,v 1.2 2020/10/18 11:32:01 djm Exp $ */ +/* + * Copyright (c) 2015 Theo de Raadt + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_PLEDGE + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +struct ssh_sandbox { + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *m) +{ + struct ssh_sandbox *box; + + debug3_f("preparing pledge sandbox"); + box = xcalloc(1, sizeof(*box)); + box->child_pid = 0; + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + if (pledge("stdio", NULL) == -1) + fatal_f("pledge()"); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3_f("finished"); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; + /* Nothing to do here */ +} + +#endif /* SANDBOX_PLEDGE */ diff --git a/aosp/external/openssh/sandbox-rlimit.c b/aosp/external/openssh/sandbox-rlimit.c new file mode 100644 index 0000000000000000000000000000000000000000..26c61d26481700527ec76403ba19b8fbda0b402b --- /dev/null +++ b/aosp/external/openssh/sandbox-rlimit.c @@ -0,0 +1,96 @@ +/* $OpenBSD: sandbox-rlimit.c,v 1.5 2020/10/18 11:32:01 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_RLIMIT + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +/* Minimal sandbox that sets zero nfiles, nprocs and filesize rlimits */ + +struct ssh_sandbox { + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + debug3_f("preparing rlimit sandbox"); + box = xcalloc(1, sizeof(*box)); + box->child_pid = 0; + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + struct rlimit rl_zero; + + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + +#ifndef SANDBOX_SKIP_RLIMIT_FSIZE + if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) + fatal_f("setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", + strerror(errno)); +#endif +#ifndef SANDBOX_SKIP_RLIMIT_NOFILE + if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) + fatal_f("setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", + strerror(errno)); +#endif +#ifdef HAVE_RLIMIT_NPROC + if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) + fatal_f("setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", + strerror(errno)); +#endif +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3_f("finished"); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; +} + +#endif /* SANDBOX_RLIMIT */ diff --git a/aosp/external/openssh/sandbox-seccomp-filter.c b/aosp/external/openssh/sandbox-seccomp-filter.c new file mode 100644 index 0000000000000000000000000000000000000000..4ce80cb2a739ddc48d333e6fd2bbe88c69c92112 --- /dev/null +++ b/aosp/external/openssh/sandbox-seccomp-filter.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2012 Will Drewry + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose + * filter breakage during development. *Do not* use this in production, + * as it relies on making library calls that are unsafe in signal context. + * + * Instead, live systems the auditctl(8) may be used to monitor failures. + * E.g. + * auditctl -a task,always -F uid= + */ +/* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */ + +/* XXX it should be possible to do logging via the log socket safely */ + +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG +/* Use the kernel headers in case of an older toolchain. */ +# include +# define __have_siginfo_t 1 +# define __have_sigval_t 1 +# define __have_sigevent_t 1 +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ + +#include "includes.h" + +#ifdef SANDBOX_SECCOMP_FILTER + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#ifdef __s390__ +#include +#endif + +#include +#include +#include +#include /* for offsetof */ +#include +#include +#include +#include + +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +/* Linux seccomp_filter sandbox */ +#define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL + +/* Use a signal handler to emit violations when debugging */ +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG +# undef SECCOMP_FILTER_FAIL +# define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define ARG_LO_OFFSET 0 +# define ARG_HI_OFFSET sizeof(uint32_t) +#elif __BYTE_ORDER == __BIG_ENDIAN +# define ARG_LO_OFFSET sizeof(uint32_t) +# define ARG_HI_OFFSET 0 +#else +#error "Unknown endianness" +#endif + +/* Simple helpers to avoid manual errors (but larger BPF programs). */ +#define SC_DENY(_nr, _errno) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno)) +#define SC_ALLOW(_nr) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) +#define SC_ALLOW_ARG(_nr, _arg_nr, _arg_val) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 6), \ + /* load and test syscall argument, low word */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, args[(_arg_nr)]) + ARG_LO_OFFSET), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, \ + ((_arg_val) & 0xFFFFFFFF), 0, 3), \ + /* load and test syscall argument, high word */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, args[(_arg_nr)]) + ARG_HI_OFFSET), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, \ + (((uint32_t)((uint64_t)(_arg_val) >> 32)) & 0xFFFFFFFF), 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), \ + /* reload syscall number; all rules expect it in accumulator */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, nr)) +/* Allow if syscall argument contains only values in mask */ +#define SC_ALLOW_ARG_MASK(_nr, _arg_nr, _arg_mask) \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 8), \ + /* load, mask and test syscall argument, low word */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, args[(_arg_nr)]) + ARG_LO_OFFSET), \ + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, ~((_arg_mask) & 0xFFFFFFFF)), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 4), \ + /* load, mask and test syscall argument, high word */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, args[(_arg_nr)]) + ARG_HI_OFFSET), \ + BPF_STMT(BPF_ALU+BPF_AND+BPF_K, \ + ~(((uint32_t)((uint64_t)(_arg_mask) >> 32)) & 0xFFFFFFFF)), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 1), \ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), \ + /* reload syscall number; all rules expect it in accumulator */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, nr)) + +/* Syscall filtering set for preauth. */ +static const struct sock_filter preauth_insns[] = { + /* Ensure the syscall arch convention is as expected. */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, + offsetof(struct seccomp_data, arch)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), + BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), + /* Load the syscall number for checking. */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, + offsetof(struct seccomp_data, nr)), + + /* Syscalls to non-fatally deny */ +#ifdef __NR_lstat + SC_DENY(__NR_lstat, EACCES), +#endif +#ifdef __NR_lstat64 + SC_DENY(__NR_lstat64, EACCES), +#endif +#ifdef __NR_fstat + SC_DENY(__NR_fstat, EACCES), +#endif +#ifdef __NR_fstat64 + SC_DENY(__NR_fstat64, EACCES), +#endif +#ifdef __NR_fstatat64 + SC_DENY(__NR_fstatat64, EACCES), +#endif +#ifdef __NR_open + SC_DENY(__NR_open, EACCES), +#endif +#ifdef __NR_openat + SC_DENY(__NR_openat, EACCES), +#endif +#ifdef __NR_newfstatat + SC_DENY(__NR_newfstatat, EACCES), +#endif +#ifdef __NR_stat + SC_DENY(__NR_stat, EACCES), +#endif +#ifdef __NR_stat64 + SC_DENY(__NR_stat64, EACCES), +#endif +#ifdef __NR_shmget + SC_DENY(__NR_shmget, EACCES), +#endif +#ifdef __NR_shmat + SC_DENY(__NR_shmat, EACCES), +#endif +#ifdef __NR_shmdt + SC_DENY(__NR_shmdt, EACCES), +#endif +#ifdef __NR_ipc + SC_DENY(__NR_ipc, EACCES), +#endif +#ifdef __NR_statx + SC_DENY(__NR_statx, EACCES), +#endif + + /* Syscalls to permit */ +#ifdef __NR_brk + SC_ALLOW(__NR_brk), +#endif +#ifdef __NR_clock_gettime + SC_ALLOW(__NR_clock_gettime), +#endif +#ifdef __NR_clock_gettime64 + SC_ALLOW(__NR_clock_gettime64), +#endif +#ifdef __NR_close + SC_ALLOW(__NR_close), +#endif +#ifdef __NR_exit + SC_ALLOW(__NR_exit), +#endif +#ifdef __NR_exit_group + SC_ALLOW(__NR_exit_group), +#endif +#ifdef __NR_futex + SC_ALLOW(__NR_futex), +#endif +#ifdef __NR_futex_time64 + SC_ALLOW(__NR_futex_time64), +#endif +#ifdef __NR_geteuid + SC_ALLOW(__NR_geteuid), +#endif +#ifdef __NR_geteuid32 + SC_ALLOW(__NR_geteuid32), +#endif +#ifdef __NR_getpgid + SC_ALLOW(__NR_getpgid), +#endif +#ifdef __NR_getpid + SC_ALLOW(__NR_getpid), +#endif +#ifdef __NR_getrandom + SC_ALLOW(__NR_getrandom), +#endif +#ifdef __NR_gettid + SC_ALLOW(__NR_gettid), +#endif +#ifdef __NR_gettimeofday + SC_ALLOW(__NR_gettimeofday), +#endif +#ifdef __NR_getuid + SC_ALLOW(__NR_getuid), +#endif +#ifdef __NR_getuid32 + SC_ALLOW(__NR_getuid32), +#endif +#ifdef __NR_madvise + SC_ALLOW(__NR_madvise), +#endif +#ifdef __NR_mmap + SC_ALLOW_ARG_MASK(__NR_mmap, 2, PROT_READ|PROT_WRITE|PROT_NONE), +#endif +#ifdef __NR_mmap2 + SC_ALLOW_ARG_MASK(__NR_mmap2, 2, PROT_READ|PROT_WRITE|PROT_NONE), +#endif +#ifdef __NR_mprotect + SC_ALLOW_ARG_MASK(__NR_mprotect, 2, PROT_READ|PROT_WRITE|PROT_NONE), +#endif +#ifdef __NR_mremap + SC_ALLOW(__NR_mremap), +#endif +#ifdef __NR_munmap + SC_ALLOW(__NR_munmap), +#endif +#ifdef __NR_nanosleep + SC_ALLOW(__NR_nanosleep), +#endif +#ifdef __NR_clock_nanosleep + SC_ALLOW(__NR_clock_nanosleep), +#endif +#ifdef __NR_clock_nanosleep_time64 + SC_ALLOW(__NR_clock_nanosleep_time64), +#endif +#ifdef __NR_clock_gettime64 + SC_ALLOW(__NR_clock_gettime64), +#endif +#ifdef __NR__newselect + SC_ALLOW(__NR__newselect), +#endif +#ifdef __NR_ppoll + SC_ALLOW(__NR_ppoll), +#endif +#ifdef __NR_ppoll_time64 + SC_ALLOW(__NR_ppoll_time64), +#endif +#ifdef __NR_poll + SC_ALLOW(__NR_poll), +#endif +#ifdef __NR_pselect6 + SC_ALLOW(__NR_pselect6), +#endif +#ifdef __NR_pselect6_time64 + SC_ALLOW(__NR_pselect6_time64), +#endif +#ifdef __NR_read + SC_ALLOW(__NR_read), +#endif +#ifdef __NR_rt_sigprocmask + SC_ALLOW(__NR_rt_sigprocmask), +#endif +#ifdef __NR_select + SC_ALLOW(__NR_select), +#endif +#ifdef __NR_shutdown + SC_ALLOW(__NR_shutdown), +#endif +#ifdef __NR_sigprocmask + SC_ALLOW(__NR_sigprocmask), +#endif +#ifdef __NR_time + SC_ALLOW(__NR_time), +#endif +#ifdef __NR_write + SC_ALLOW(__NR_write), +#endif +#ifdef __NR_socketcall + SC_ALLOW_ARG(__NR_socketcall, 0, SYS_SHUTDOWN), + SC_DENY(__NR_socketcall, EACCES), +#endif +#if defined(__NR_ioctl) && defined(__s390__) + /* Allow ioctls for ICA crypto card on s390 */ + SC_ALLOW_ARG(__NR_ioctl, 1, Z90STAT_STATUS_MASK), + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSAMODEXPO), + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSACRT), + SC_ALLOW_ARG(__NR_ioctl, 1, ZSECSENDCPRB), + /* Allow ioctls for EP11 crypto card on s390 */ + SC_ALLOW_ARG(__NR_ioctl, 1, ZSENDEP11CPRB), +#endif +#if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT) + /* + * On Linux x32, the clock_gettime VDSO falls back to the + * x86-64 syscall under some circumstances, e.g. + * https://bugs.debian.org/849923 + */ + SC_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT), +#endif + + /* Default deny */ + BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), +}; + +static const struct sock_fprog preauth_program = { + .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), + .filter = (struct sock_filter *)preauth_insns, +}; + +struct ssh_sandbox { + pid_t child_pid; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + /* + * Strictly, we don't need to maintain any state here but we need + * to return non-NULL to satisfy the API. + */ + debug3("%s: preparing seccomp filter sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + box->child_pid = 0; + + return box; +} + +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG +extern struct monitor *pmonitor; +void mm_log_handler(LogLevel level, const char *msg, void *ctx); + +static void +ssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) +{ + char msg[256]; + + snprintf(msg, sizeof(msg), + "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", + __func__, info->si_arch, info->si_syscall, info->si_call_addr); + mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); + _exit(1); +} + +static void +ssh_sandbox_child_debugging(void) +{ + struct sigaction act; + sigset_t mask; + + debug3("%s: installing SIGSYS handler", __func__); + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + + act.sa_sigaction = &ssh_sandbox_violation; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) == -1) + fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); + if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) + fatal("%s: sigprocmask(SIGSYS): %s", + __func__, strerror(errno)); +} +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + struct rlimit rl_zero, rl_one = {.rlim_cur = 1, .rlim_max = 1}; + int nnp_failed = 0; + + /* Set rlimits for completeness if possible. */ + rl_zero.rlim_cur = rl_zero.rlim_max = 0; + if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", + __func__, strerror(errno)); + /* + * Cannot use zero for nfds, because poll(2) will fail with + * errno=EINVAL if npfds>RLIMIT_NOFILE. + */ + if (setrlimit(RLIMIT_NOFILE, &rl_one) == -1) + fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", + __func__, strerror(errno)); + if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) + fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", + __func__, strerror(errno)); + +#ifdef SANDBOX_SECCOMP_FILTER_DEBUG + ssh_sandbox_child_debugging(); +#endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ + + debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { + debug("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", + __func__, strerror(errno)); + nnp_failed = 1; + } + debug3("%s: attaching seccomp filter program", __func__); + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) + debug("%s: prctl(PR_SET_SECCOMP): %s", + __func__, strerror(errno)); + else if (nnp_failed) + fatal("%s: SECCOMP_MODE_FILTER activated but " + "PR_SET_NO_NEW_PRIVS failed", __func__); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + box->child_pid = child_pid; +} + +#endif /* SANDBOX_SECCOMP_FILTER */ diff --git a/aosp/external/openssh/sandbox-solaris.c b/aosp/external/openssh/sandbox-solaris.c new file mode 100644 index 0000000000000000000000000000000000000000..56ddb9a9942a0622410b14587a720572ff2a054f --- /dev/null +++ b/aosp/external/openssh/sandbox-solaris.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015 Joyent, Inc + * Author: Alex Wilson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_SOLARIS +#ifndef USE_SOLARIS_PRIVS +# error "--with-solaris-privs must be used with the Solaris sandbox" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PRIV_H +# include +#endif + +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +struct ssh_sandbox { + priv_set_t *pset; +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box = NULL; + + box = xcalloc(1, sizeof(*box)); + + /* Start with "basic" and drop everything we don't need. */ + box->pset = solaris_basic_privset(); + + if (box->pset == NULL) { + free(box); + return NULL; + } + + /* Drop everything except the ability to use already-opened files */ + if (priv_delset(box->pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS + priv_delset(box->pset, PRIV_NET_ACCESS) != 0 || +#endif +#ifdef PRIV_DAX_ACCESS + priv_delset(box->pset, PRIV_DAX_ACCESS) != 0 || +#endif +#ifdef PRIV_SYS_IB_INFO + priv_delset(box->pset, PRIV_SYS_IB_INFO) != 0 || +#endif + priv_delset(box->pset, PRIV_PROC_EXEC) != 0 || + priv_delset(box->pset, PRIV_PROC_FORK) != 0 || + priv_delset(box->pset, PRIV_PROC_INFO) != 0 || + priv_delset(box->pset, PRIV_PROC_SESSION) != 0) { + free(box); + return NULL; + } + + /* These may not be available on older Solaris-es */ +# if defined(PRIV_FILE_READ) && defined(PRIV_FILE_WRITE) + if (priv_delset(box->pset, PRIV_FILE_READ) != 0 || + priv_delset(box->pset, PRIV_FILE_WRITE) != 0) { + free(box); + return NULL; + } +# endif + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + if (setppriv(PRIV_SET, PRIV_PERMITTED, box->pset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, box->pset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, box->pset) != 0) + fatal("setppriv: %s", strerror(errno)); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + priv_freeset(box->pset); + box->pset = NULL; + free(box); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + /* Nothing to do here */ +} + +#endif /* SANDBOX_SOLARIS */ diff --git a/aosp/external/openssh/sandbox-systrace.c b/aosp/external/openssh/sandbox-systrace.c new file mode 100644 index 0000000000000000000000000000000000000000..e61d581aedbabba9fb9a99f24820bd6768474701 --- /dev/null +++ b/aosp/external/openssh/sandbox-systrace.c @@ -0,0 +1,218 @@ +/* $OpenBSD: sandbox-systrace.c,v 1.18 2015/10/02 01:39:26 deraadt Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef SANDBOX_SYSTRACE + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atomicio.h" +#include "log.h" +#include "ssh-sandbox.h" +#include "xmalloc.h" + +struct sandbox_policy { + int syscall; + int action; +}; + +/* Permitted syscalls in preauth. Unlisted syscalls get SYSTR_POLICY_KILL */ +static const struct sandbox_policy preauth_policy[] = { + { SYS_exit, SYSTR_POLICY_PERMIT }, +#ifdef SYS_kbind + { SYS_kbind, SYSTR_POLICY_PERMIT }, +#endif + + { SYS_getpid, SYSTR_POLICY_PERMIT }, + { SYS_getpgid, SYSTR_POLICY_PERMIT }, + { SYS_clock_gettime, SYSTR_POLICY_PERMIT }, + { SYS_gettimeofday, SYSTR_POLICY_PERMIT }, + { SYS_nanosleep, SYSTR_POLICY_PERMIT }, + { SYS_sigprocmask, SYSTR_POLICY_PERMIT }, + +#ifdef SYS_getentropy + /* OpenBSD 5.6 and newer use getentropy(2) to seed arc4random(3). */ + { SYS_getentropy, SYSTR_POLICY_PERMIT }, +#else + /* Previous releases used sysctl(3)'s kern.arnd variable. */ + { SYS___sysctl, SYSTR_POLICY_PERMIT }, +#endif +#ifdef SYS_sendsyslog + { SYS_sendsyslog, SYSTR_POLICY_PERMIT }, +#endif + + { SYS_madvise, SYSTR_POLICY_PERMIT }, + { SYS_mmap, SYSTR_POLICY_PERMIT }, + { SYS_mprotect, SYSTR_POLICY_PERMIT }, + { SYS_mquery, SYSTR_POLICY_PERMIT }, + { SYS_munmap, SYSTR_POLICY_PERMIT }, + + { SYS_poll, SYSTR_POLICY_PERMIT }, + { SYS_select, SYSTR_POLICY_PERMIT }, + { SYS_read, SYSTR_POLICY_PERMIT }, + { SYS_write, SYSTR_POLICY_PERMIT }, + { SYS_shutdown, SYSTR_POLICY_PERMIT }, + { SYS_close, SYSTR_POLICY_PERMIT }, + + { SYS_open, SYSTR_POLICY_NEVER }, + + { -1, -1 } +}; + +struct ssh_sandbox { + int systrace_fd; + pid_t child_pid; + void (*osigchld)(int); +}; + +struct ssh_sandbox * +ssh_sandbox_init(struct monitor *monitor) +{ + struct ssh_sandbox *box; + + debug3("%s: preparing systrace sandbox", __func__); + box = xcalloc(1, sizeof(*box)); + box->systrace_fd = -1; + box->child_pid = 0; + box->osigchld = ssh_signal(SIGCHLD, SIG_IGN); + + return box; +} + +void +ssh_sandbox_child(struct ssh_sandbox *box) +{ + debug3("%s: ready", __func__); + ssh_signal(SIGCHLD, box->osigchld); + if (kill(getpid(), SIGSTOP) != 0) + fatal("%s: kill(%d, SIGSTOP)", __func__, getpid()); + debug3("%s: started", __func__); +} + +static void +ssh_sandbox_parent(struct ssh_sandbox *box, pid_t child_pid, + const struct sandbox_policy *allowed_syscalls) +{ + int dev_systrace, i, j, found, status; + pid_t pid; + struct systrace_policy policy; + + /* Wait for the child to send itself a SIGSTOP */ + debug3("%s: wait for child %ld", __func__, (long)child_pid); + do { + pid = waitpid(child_pid, &status, WUNTRACED); + } while (pid == -1 && errno == EINTR); + ssh_signal(SIGCHLD, box->osigchld); + if (!WIFSTOPPED(status)) { + if (WIFSIGNALED(status)) + fatal("%s: child terminated with signal %d", + __func__, WTERMSIG(status)); + if (WIFEXITED(status)) + fatal("%s: child exited with status %d", + __func__, WEXITSTATUS(status)); + fatal("%s: child not stopped", __func__); + } + debug3("%s: child %ld stopped", __func__, (long)child_pid); + box->child_pid = child_pid; + + /* Set up systracing of child */ + if ((dev_systrace = open("/dev/systrace", O_RDONLY)) == -1) + fatal("%s: open(\"/dev/systrace\"): %s", __func__, + strerror(errno)); + if (ioctl(dev_systrace, STRIOCCLONE, &box->systrace_fd) == -1) + fatal("%s: ioctl(STRIOCCLONE, %d): %s", __func__, + dev_systrace, strerror(errno)); + close(dev_systrace); + debug3("%s: systrace attach, fd=%d", __func__, box->systrace_fd); + if (ioctl(box->systrace_fd, STRIOCATTACH, &child_pid) == -1) + fatal("%s: ioctl(%d, STRIOCATTACH, %d): %s", __func__, + box->systrace_fd, child_pid, strerror(errno)); + + /* Allocate and assign policy */ + memset(&policy, 0, sizeof(policy)); + policy.strp_op = SYSTR_POLICY_NEW; + policy.strp_maxents = SYS_MAXSYSCALL; + if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) + fatal("%s: ioctl(%d, STRIOCPOLICY (new)): %s", __func__, + box->systrace_fd, strerror(errno)); + + policy.strp_op = SYSTR_POLICY_ASSIGN; + policy.strp_pid = box->child_pid; + if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) + fatal("%s: ioctl(%d, STRIOCPOLICY (assign)): %s", + __func__, box->systrace_fd, strerror(errno)); + + /* Set per-syscall policy */ + for (i = 0; i < SYS_MAXSYSCALL; i++) { + found = 0; + for (j = 0; allowed_syscalls[j].syscall != -1; j++) { + if (allowed_syscalls[j].syscall == i) { + found = 1; + break; + } + } + policy.strp_op = SYSTR_POLICY_MODIFY; + policy.strp_code = i; + policy.strp_policy = found ? + allowed_syscalls[j].action : SYSTR_POLICY_KILL; + if (found) + debug3("%s: policy: enable syscall %d", __func__, i); + if (ioctl(box->systrace_fd, STRIOCPOLICY, &policy) == -1) + fatal("%s: ioctl(%d, STRIOCPOLICY (modify)): %s", + __func__, box->systrace_fd, strerror(errno)); + } + + /* Signal the child to start running */ + debug3("%s: start child %ld", __func__, (long)child_pid); + if (kill(box->child_pid, SIGCONT) != 0) + fatal("%s: kill(%d, SIGCONT)", __func__, box->child_pid); +} + +void +ssh_sandbox_parent_finish(struct ssh_sandbox *box) +{ + /* Closing this before the child exits will terminate it */ + close(box->systrace_fd); + + free(box); + debug3("%s: finished", __func__); +} + +void +ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) +{ + ssh_sandbox_parent(box, child_pid, preauth_policy); +} + +#endif /* SANDBOX_SYSTRACE */ diff --git a/aosp/external/openssh/sc25519.c b/aosp/external/openssh/sc25519.c new file mode 100644 index 0000000000000000000000000000000000000000..1568d9a58c9808303a9d78614fcbd8be34540dce --- /dev/null +++ b/aosp/external/openssh/sc25519.c @@ -0,0 +1,308 @@ +/* $OpenBSD: sc25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/sc25519.c + */ + +#include "includes.h" + +#include "sc25519.h" + +/*Arithmetic modulo the group order m = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 */ + +static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + +static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21, + 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F}; + +static crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ +{ + unsigned int x = a; + x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */ + x >>= 31; /* 0: no; 1: yes */ + return x; +} + +/* Reduce coefficients of r before calling reduce_add_sub */ +static void reduce_add_sub(sc25519 *r) +{ + crypto_uint32 pb = 0; + crypto_uint32 b; + crypto_uint32 mask; + int i; + unsigned char t[32]; + + for(i=0;i<32;i++) + { + pb += m[i]; + b = lt(r->v[i],pb); + t[i] = r->v[i]-pb+(b<<8); + pb = b; + } + mask = b - 1; + for(i=0;i<32;i++) + r->v[i] ^= mask & (r->v[i] ^ t[i]); +} + +/* Reduce coefficients of x before calling barrett_reduce */ +static void barrett_reduce(sc25519 *r, const crypto_uint32 x[64]) +{ + /* See HAC, Alg. 14.42 */ + int i,j; + crypto_uint32 q2[66]; + crypto_uint32 *q3 = q2 + 33; + crypto_uint32 r1[33]; + crypto_uint32 r2[33]; + crypto_uint32 carry; + crypto_uint32 pb = 0; + crypto_uint32 b; + + for (i = 0;i < 66;++i) q2[i] = 0; + for (i = 0;i < 33;++i) r2[i] = 0; + + for(i=0;i<33;i++) + for(j=0;j<33;j++) + if(i+j >= 31) q2[i+j] += mu[i]*x[j+31]; + carry = q2[31] >> 8; + q2[32] += carry; + carry = q2[32] >> 8; + q2[33] += carry; + + for(i=0;i<33;i++)r1[i] = x[i]; + for(i=0;i<32;i++) + for(j=0;j<33;j++) + if(i+j < 33) r2[i+j] += m[i]*q3[j]; + + for(i=0;i<32;i++) + { + carry = r2[i] >> 8; + r2[i+1] += carry; + r2[i] &= 0xff; + } + + for(i=0;i<32;i++) + { + pb += r2[i]; + b = lt(r1[i],pb); + r->v[i] = r1[i]-pb+(b<<8); + pb = b; + } + + /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3 + * If so: Handle it here! + */ + + reduce_add_sub(r); + reduce_add_sub(r); +} + +void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]) +{ + int i; + crypto_uint32 t[64]; + for(i=0;i<32;i++) t[i] = x[i]; + for(i=32;i<64;++i) t[i] = 0; + barrett_reduce(r, t); +} + +void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]) +{ + int i; + for(i=0;i<16;i++) r->v[i] = x[i]; +} + +void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]) +{ + int i; + crypto_uint32 t[64]; + for(i=0;i<64;i++) t[i] = x[i]; + barrett_reduce(r, t); +} + +void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x) +{ + int i; + for(i=0;i<16;i++) + r->v[i] = x->v[i]; + for(i=0;i<16;i++) + r->v[16+i] = 0; +} + +void sc25519_to32bytes(unsigned char r[32], const sc25519 *x) +{ + int i; + for(i=0;i<32;i++) r[i] = x->v[i]; +} + +int sc25519_iszero_vartime(const sc25519 *x) +{ + int i; + for(i=0;i<32;i++) + if(x->v[i] != 0) return 0; + return 1; +} + +int sc25519_isshort_vartime(const sc25519 *x) +{ + int i; + for(i=31;i>15;i--) + if(x->v[i] != 0) return 0; + return 1; +} + +int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y) +{ + int i; + for(i=31;i>=0;i--) + { + if(x->v[i] < y->v[i]) return 1; + if(x->v[i] > y->v[i]) return 0; + } + return 0; +} + +void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y) +{ + int i, carry; + for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i]; + for(i=0;i<31;i++) + { + carry = r->v[i] >> 8; + r->v[i+1] += carry; + r->v[i] &= 0xff; + } + reduce_add_sub(r); +} + +void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y) +{ + crypto_uint32 b = 0; + crypto_uint32 t; + int i; + for(i=0;i<32;i++) + { + t = x->v[i] - y->v[i] - b; + r->v[i] = t & 255; + b = (t >> 8) & 1; + } +} + +void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y) +{ + int i,j,carry; + crypto_uint32 t[64]; + for(i=0;i<64;i++)t[i] = 0; + + for(i=0;i<32;i++) + for(j=0;j<32;j++) + t[i+j] += x->v[i] * y->v[j]; + + /* Reduce coefficients */ + for(i=0;i<63;i++) + { + carry = t[i] >> 8; + t[i+1] += carry; + t[i] &= 0xff; + } + + barrett_reduce(r, t); +} + +void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y) +{ + sc25519 t; + sc25519_from_shortsc(&t, y); + sc25519_mul(r, x, &t); +} + +void sc25519_window3(signed char r[85], const sc25519 *s) +{ + char carry; + int i; + for(i=0;i<10;i++) + { + r[8*i+0] = s->v[3*i+0] & 7; + r[8*i+1] = (s->v[3*i+0] >> 3) & 7; + r[8*i+2] = (s->v[3*i+0] >> 6) & 7; + r[8*i+2] ^= (s->v[3*i+1] << 2) & 7; + r[8*i+3] = (s->v[3*i+1] >> 1) & 7; + r[8*i+4] = (s->v[3*i+1] >> 4) & 7; + r[8*i+5] = (s->v[3*i+1] >> 7) & 7; + r[8*i+5] ^= (s->v[3*i+2] << 1) & 7; + r[8*i+6] = (s->v[3*i+2] >> 2) & 7; + r[8*i+7] = (s->v[3*i+2] >> 5) & 7; + } + r[8*i+0] = s->v[3*i+0] & 7; + r[8*i+1] = (s->v[3*i+0] >> 3) & 7; + r[8*i+2] = (s->v[3*i+0] >> 6) & 7; + r[8*i+2] ^= (s->v[3*i+1] << 2) & 7; + r[8*i+3] = (s->v[3*i+1] >> 1) & 7; + r[8*i+4] = (s->v[3*i+1] >> 4) & 7; + + /* Making it signed */ + carry = 0; + for(i=0;i<84;i++) + { + r[i] += carry; + r[i+1] += r[i] >> 3; + r[i] &= 7; + carry = r[i] >> 2; + r[i] -= carry<<3; + } + r[84] += carry; +} + +void sc25519_window5(signed char r[51], const sc25519 *s) +{ + char carry; + int i; + for(i=0;i<6;i++) + { + r[8*i+0] = s->v[5*i+0] & 31; + r[8*i+1] = (s->v[5*i+0] >> 5) & 31; + r[8*i+1] ^= (s->v[5*i+1] << 3) & 31; + r[8*i+2] = (s->v[5*i+1] >> 2) & 31; + r[8*i+3] = (s->v[5*i+1] >> 7) & 31; + r[8*i+3] ^= (s->v[5*i+2] << 1) & 31; + r[8*i+4] = (s->v[5*i+2] >> 4) & 31; + r[8*i+4] ^= (s->v[5*i+3] << 4) & 31; + r[8*i+5] = (s->v[5*i+3] >> 1) & 31; + r[8*i+6] = (s->v[5*i+3] >> 6) & 31; + r[8*i+6] ^= (s->v[5*i+4] << 2) & 31; + r[8*i+7] = (s->v[5*i+4] >> 3) & 31; + } + r[8*i+0] = s->v[5*i+0] & 31; + r[8*i+1] = (s->v[5*i+0] >> 5) & 31; + r[8*i+1] ^= (s->v[5*i+1] << 3) & 31; + r[8*i+2] = (s->v[5*i+1] >> 2) & 31; + + /* Making it signed */ + carry = 0; + for(i=0;i<50;i++) + { + r[i] += carry; + r[i+1] += r[i] >> 5; + r[i] &= 31; + carry = r[i] >> 4; + r[i] -= carry<<5; + } + r[50] += carry; +} + +void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2) +{ + int i; + for(i=0;i<31;i++) + { + r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2); + r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2); + r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2); + r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2); + } + r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2); + r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2); + r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2); +} diff --git a/aosp/external/openssh/sc25519.h b/aosp/external/openssh/sc25519.h new file mode 100644 index 0000000000000000000000000000000000000000..a2c15d5ff99608e563ef4eacf64c41a5b49472f2 --- /dev/null +++ b/aosp/external/openssh/sc25519.h @@ -0,0 +1,80 @@ +/* $OpenBSD: sc25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/sc25519.h + */ + +#ifndef SC25519_H +#define SC25519_H + +#include "crypto_api.h" + +#define sc25519 crypto_sign_ed25519_ref_sc25519 +#define shortsc25519 crypto_sign_ed25519_ref_shortsc25519 +#define sc25519_from32bytes crypto_sign_ed25519_ref_sc25519_from32bytes +#define shortsc25519_from16bytes crypto_sign_ed25519_ref_shortsc25519_from16bytes +#define sc25519_from64bytes crypto_sign_ed25519_ref_sc25519_from64bytes +#define sc25519_from_shortsc crypto_sign_ed25519_ref_sc25519_from_shortsc +#define sc25519_to32bytes crypto_sign_ed25519_ref_sc25519_to32bytes +#define sc25519_iszero_vartime crypto_sign_ed25519_ref_sc25519_iszero_vartime +#define sc25519_isshort_vartime crypto_sign_ed25519_ref_sc25519_isshort_vartime +#define sc25519_lt_vartime crypto_sign_ed25519_ref_sc25519_lt_vartime +#define sc25519_add crypto_sign_ed25519_ref_sc25519_add +#define sc25519_sub_nored crypto_sign_ed25519_ref_sc25519_sub_nored +#define sc25519_mul crypto_sign_ed25519_ref_sc25519_mul +#define sc25519_mul_shortsc crypto_sign_ed25519_ref_sc25519_mul_shortsc +#define sc25519_window3 crypto_sign_ed25519_ref_sc25519_window3 +#define sc25519_window5 crypto_sign_ed25519_ref_sc25519_window5 +#define sc25519_2interleave2 crypto_sign_ed25519_ref_sc25519_2interleave2 + +typedef struct +{ + crypto_uint32 v[32]; +} +sc25519; + +typedef struct +{ + crypto_uint32 v[16]; +} +shortsc25519; + +void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]); + +void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]); + +void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]); + +void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x); + +void sc25519_to32bytes(unsigned char r[32], const sc25519 *x); + +int sc25519_iszero_vartime(const sc25519 *x); + +int sc25519_isshort_vartime(const sc25519 *x); + +int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y); + +void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y); + +void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y); + +void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y); + +void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y); + +/* Convert s into a representation of the form \sum_{i=0}^{84}r[i]2^3 + * with r[i] in {-4,...,3} + */ +void sc25519_window3(signed char r[85], const sc25519 *s); + +/* Convert s into a representation of the form \sum_{i=0}^{50}r[i]2^5 + * with r[i] in {-16,...,15} + */ +void sc25519_window5(signed char r[51], const sc25519 *s); + +void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2); + +#endif diff --git a/aosp/external/openssh/scp.1 b/aosp/external/openssh/scp.1 new file mode 100644 index 0000000000000000000000000000000000000000..3af6ece1e7a1ebfdd81dea31f21c43ed90edcdb7 --- /dev/null +++ b/aosp/external/openssh/scp.1 @@ -0,0 +1,311 @@ +.\" +.\" scp.1 +.\" +.\" Author: Tatu Ylonen +.\" +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" Created: Sun May 7 00:14:37 1995 ylo +.\" +.\" $OpenBSD: scp.1,v 1.108 2022/02/23 21:21:16 djm Exp $ +.\" +.Dd $Mdocdate: February 23 2022 $ +.Dt SCP 1 +.Os +.Sh NAME +.Nm scp +.Nd OpenSSH secure file copy +.Sh SYNOPSIS +.Nm scp +.Op Fl 346ABCOpqRrsTv +.Op Fl c Ar cipher +.Op Fl D Ar sftp_server_path +.Op Fl F Ar ssh_config +.Op Fl i Ar identity_file +.Op Fl J Ar destination +.Op Fl l Ar limit +.Op Fl o Ar ssh_option +.Op Fl P Ar port +.Op Fl S Ar program +.Ar source ... target +.Sh DESCRIPTION +.Nm +copies files between hosts on a network. +.Pp +It uses +.Xr ssh 1 +for data transfer, and uses the same authentication and provides the +same security as a login session. +.Pp +.Nm +will ask for passwords or passphrases if they are needed for +authentication. +.Pp +The +.Ar source +and +.Ar target +may be specified as a local pathname, a remote host with optional path +in the form +.Sm off +.Oo user @ Oc host : Op path , +.Sm on +or a URI in the form +.Sm off +.No scp:// Oo user @ Oc host Oo : port Oc Op / path . +.Sm on +Local file names can be made explicit using absolute or relative pathnames +to avoid +.Nm +treating file names containing +.Sq :\& +as host specifiers. +.Pp +When copying between two remote hosts, if the URI format is used, a +.Ar port +cannot be specified on the +.Ar target +if the +.Fl R +option is used. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 3 +Copies between two remote hosts are transferred through the local host. +Without this option the data is copied directly between the two remote +hosts. +Note that, when using the legacy SCP protocol (via the +.Fl O +flag), this option +selects batch mode for the second host as +.Nm +cannot ask for passwords or passphrases for both hosts. +This mode is the default. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl A +Allows forwarding of +.Xr ssh-agent 1 +to the remote system. +The default is not to forward an authentication agent. +.It Fl B +Selects batch mode (prevents asking for passwords or passphrases). +.It Fl C +Compression enable. +Passes the +.Fl C +flag to +.Xr ssh 1 +to enable compression. +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfer. +This option is directly passed to +.Xr ssh 1 . +.It Fl D Ar sftp_server_path +When using the SFTP protocol support via +.Fl M , +connect directly to a local SFTP server program rather than a +remote one via +.Xr ssh 1 . +This option may be useful in debugging the client and server. +.It Fl F Ar ssh_config +Specifies an alternative +per-user configuration file for +.Nm ssh . +This option is directly passed to +.Xr ssh 1 . +.It Fl i Ar identity_file +Selects the file from which the identity (private key) for public key +authentication is read. +This option is directly passed to +.Xr ssh 1 . +.It Fl J Ar destination +Connect to the target host by first making an +.Nm +connection to the jump host described by +.Ar destination +and then establishing a TCP forwarding to the ultimate destination from +there. +Multiple jump hops may be specified separated by comma characters. +This is a shortcut to specify a +.Cm ProxyJump +configuration directive. +This option is directly passed to +.Xr ssh 1 . +.It Fl l Ar limit +Limits the used bandwidth, specified in Kbit/s. +.It Fl O +Use the legacy SCP protocol for file transfers instead of the SFTP protocol. +Forcing the use of the SCP protocol may be necessary for servers that do +not implement SFTP, for backwards-compatibility for particular filename +wildcard patterns and for expanding paths with a +.Sq ~ +prefix for older SFTP servers. +.It Fl o Ar ssh_option +Can be used to pass options to +.Nm ssh +in the format used in +.Xr ssh_config 5 . +This is useful for specifying options +for which there is no separate +.Nm scp +command-line flag. +For full details of the options listed below, and their possible values, see +.Xr ssh_config 5 . +.Pp +.Bl -tag -width Ds -offset indent -compact +.It AddressFamily +.It BatchMode +.It BindAddress +.It BindInterface +.It CanonicalDomains +.It CanonicalizeFallbackLocal +.It CanonicalizeHostname +.It CanonicalizeMaxDots +.It CanonicalizePermittedCNAMEs +.It CASignatureAlgorithms +.It CertificateFile +.It CheckHostIP +.It Ciphers +.It Compression +.It ConnectionAttempts +.It ConnectTimeout +.It ControlMaster +.It ControlPath +.It ControlPersist +.It GlobalKnownHostsFile +.It GSSAPIAuthentication +.It GSSAPIDelegateCredentials +.It HashKnownHosts +.It Host +.It HostbasedAcceptedAlgorithms +.It HostbasedAuthentication +.It HostKeyAlgorithms +.It HostKeyAlias +.It Hostname +.It IdentitiesOnly +.It IdentityAgent +.It IdentityFile +.It IPQoS +.It KbdInteractiveAuthentication +.It KbdInteractiveDevices +.It KexAlgorithms +.It KnownHostsCommand +.It LogLevel +.It MACs +.It NoHostAuthenticationForLocalhost +.It NumberOfPasswordPrompts +.It PasswordAuthentication +.It PKCS11Provider +.It Port +.It PreferredAuthentications +.It ProxyCommand +.It ProxyJump +.It PubkeyAcceptedAlgorithms +.It PubkeyAuthentication +.It RekeyLimit +.It SendEnv +.It ServerAliveInterval +.It ServerAliveCountMax +.It SetEnv +.It StrictHostKeyChecking +.It TCPKeepAlive +.It UpdateHostKeys +.It User +.It UserKnownHostsFile +.It VerifyHostKeyDNS +.El +.It Fl P Ar port +Specifies the port to connect to on the remote host. +Note that this option is written with a capital +.Sq P , +because +.Fl p +is already reserved for preserving the times and mode bits of the file. +.It Fl p +Preserves modification times, access times, and file mode bits from the +source file. +.It Fl q +Quiet mode: disables the progress meter as well as warning and diagnostic +messages from +.Xr ssh 1 . +.It Fl R +Copies between two remote hosts are performed by connecting to the origin +host and executing +.Nm +there. +This requires that +.Nm +running on the origin host can authenticate to the destination host without +requiring a password. +.It Fl r +Recursively copy entire directories. +Note that +.Nm +follows symbolic links encountered in the tree traversal. +.It Fl S Ar program +Name of +.Ar program +to use for the encrypted connection. +The program must understand +.Xr ssh 1 +options. +.It Fl T +Disable strict filename checking. +By default when copying files from a remote host to a local directory +.Nm +checks that the received filenames match those requested on the command-line +to prevent the remote end from sending unexpected or unwanted files. +Because of differences in how various operating systems and shells interpret +filename wildcards, these checks may cause wanted files to be rejected. +This option disables these checks at the expense of fully trusting that +the server will not send unexpected filenames. +.It Fl v +Verbose mode. +Causes +.Nm +and +.Xr ssh 1 +to print debugging messages about their progress. +This is helpful in +debugging connection, authentication, and configuration problems. +.El +.Sh EXIT STATUS +.Ex -std scp +.Sh SEE ALSO +.Xr sftp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr ssh_config 5 , +.Xr sftp-server 8 , +.Xr sshd 8 +.Sh HISTORY +.Nm +is based on the rcp program in +.Bx +source code from the Regents of the University of California. +.Pp +Since OpenSSH 8.8, +.Nm +has use the SFTP protocol for transfers by default. +.Sh AUTHORS +.An Timo Rinne Aq Mt tri@iki.fi +.An Tatu Ylonen Aq Mt ylo@cs.hut.fi +.Sh CAVEATS +The legacy SCP protocol (selected by the +.Fl O +flag) requires execution of the remote user's shell to perform +.Xr glob 3 +pattern matching. +This requires careful quoting of any characters that have special meaning to +the remote shell, such as quote characters. diff --git a/aosp/external/openssh/scp.c b/aosp/external/openssh/scp.c new file mode 100644 index 0000000000000000000000000000000000000000..c36d66aa59416d72495fdaecf881c902cb281e5b --- /dev/null +++ b/aosp/external/openssh/scp.c @@ -0,0 +1,2175 @@ +/* $OpenBSD: scp.c,v 1.247 2022/03/20 08:52:17 djm Exp $ */ +/* + * scp - secure remote copy. This is basically patched BSD rcp which + * uses ssh to do the data transfer (instead of using rcmd). + * + * NOTE: This version should NOT be suid root. (This uses ssh to + * do the transfer and ssh has the necessary privileges.) + * + * 1995 Timo Rinne , Tatu Ylonen + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * Copyright (c) 1999 Aaron Campbell. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Parts from: + * + * Copyright (c) 1983, 1990, 1992, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_FNMATCH_H +#include +#endif +#ifdef USE_SYSTEM_GLOB +# include +#else +# include "openbsd-compat/glob.h" +#endif +#ifdef HAVE_LIBGEN_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include +#include +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +#include +#endif + +#include "xmalloc.h" +#include "ssh.h" +#include "atomicio.h" +#include "pathnames.h" +#include "log.h" +#include "misc.h" +#include "progressmeter.h" +#include "utf8.h" +#include "sftp.h" + +#include "sftp-common.h" +#include "sftp-client.h" + +extern char *__progname; + +#define COPY_BUFLEN 16384 + +int do_cmd(char *, char *, char *, int, int, char *, int *, int *, pid_t *); +int do_cmd2(char *, char *, int, char *, int, int); + +/* Struct for addargs */ +arglist args; +arglist remote_remote_args; + +/* Bandwidth limit */ +long long limit_kbps = 0; +struct bwlimit bwlimit; + +/* Name of current file being transferred. */ +char *curfile; + +/* This is set to non-zero to enable verbose mode. */ +int verbose_mode = 0; +LogLevel log_level = SYSLOG_LEVEL_INFO; + +/* This is set to zero if the progressmeter is not desired. */ +int showprogress = 1; + +/* + * This is set to non-zero if remote-remote copy should be piped + * through this process. + */ +int throughlocal = 1; + +/* Non-standard port to use for the ssh connection or -1. */ +int sshport = -1; + +/* This is the program to execute for the secured connection. ("ssh" or -S) */ +char *ssh_program = _PATH_SSH_PROGRAM; + +/* This is used to store the pid of ssh_program */ +pid_t do_cmd_pid = -1; +pid_t do_cmd_pid2 = -1; + +/* Needed for sftp */ +volatile sig_atomic_t interrupted = 0; + +int remote_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ + +static void +killchild(int signo) +{ + if (do_cmd_pid > 1) { + kill(do_cmd_pid, signo ? signo : SIGTERM); + waitpid(do_cmd_pid, NULL, 0); + } + if (do_cmd_pid2 > 1) { + kill(do_cmd_pid2, signo ? signo : SIGTERM); + waitpid(do_cmd_pid2, NULL, 0); + } + + if (signo) + _exit(1); + exit(1); +} + +static void +suspone(int pid, int signo) +{ + int status; + + if (pid > 1) { + kill(pid, signo); + while (waitpid(pid, &status, WUNTRACED) == -1 && + errno == EINTR) + ; + } +} + +static void +suspchild(int signo) +{ + suspone(do_cmd_pid, signo); + suspone(do_cmd_pid2, signo); + kill(getpid(), SIGSTOP); +} + +static int +do_local_cmd(arglist *a) +{ + u_int i; + int status; + pid_t pid; + + if (a->num == 0) + fatal("do_local_cmd: no arguments"); + + if (verbose_mode) { + fprintf(stderr, "Executing:"); + for (i = 0; i < a->num; i++) + fmprintf(stderr, " %s", a->list[i]); + fprintf(stderr, "\n"); + } + if ((pid = fork()) == -1) + fatal("do_local_cmd: fork: %s", strerror(errno)); + + if (pid == 0) { + execvp(a->list[0], a->list); + perror(a->list[0]); + exit(1); + } + + do_cmd_pid = pid; + ssh_signal(SIGTERM, killchild); + ssh_signal(SIGINT, killchild); + ssh_signal(SIGHUP, killchild); + + while (waitpid(pid, &status, 0) == -1) + if (errno != EINTR) + fatal("do_local_cmd: waitpid: %s", strerror(errno)); + + do_cmd_pid = -1; + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return (-1); + + return (0); +} + +/* + * This function executes the given command as the specified user on the + * given host. This returns < 0 if execution fails, and >= 0 otherwise. This + * assigns the input and output file descriptors on success. + */ + +int +do_cmd(char *program, char *host, char *remuser, int port, int subsystem, + char *cmd, int *fdin, int *fdout, pid_t *pid) +{ + int pin[2], pout[2], reserved[2]; + + if (verbose_mode) + fmprintf(stderr, + "Executing: program %s host %s, user %s, command %s\n", + program, host, + remuser ? remuser : "(unspecified)", cmd); + + if (port == -1) + port = sshport; + + /* + * Reserve two descriptors so that the real pipes won't get + * descriptors 0 and 1 because that will screw up dup2 below. + */ + if (pipe(reserved) == -1) + fatal("pipe: %s", strerror(errno)); + + /* Create a socket pair for communicating with ssh. */ + if (pipe(pin) == -1) + fatal("pipe: %s", strerror(errno)); + if (pipe(pout) == -1) + fatal("pipe: %s", strerror(errno)); + + /* Free the reserved descriptors. */ + close(reserved[0]); + close(reserved[1]); + + ssh_signal(SIGTSTP, suspchild); + ssh_signal(SIGTTIN, suspchild); + ssh_signal(SIGTTOU, suspchild); + + /* Fork a child to execute the command on the remote host using ssh. */ + *pid = fork(); + if (*pid == 0) { + /* Child. */ + close(pin[1]); + close(pout[0]); + dup2(pin[0], 0); + dup2(pout[1], 1); + close(pin[0]); + close(pout[1]); + + replacearg(&args, 0, "%s", program); + if (port != -1) { + addargs(&args, "-p"); + addargs(&args, "%d", port); + } + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } + if (subsystem) + addargs(&args, "-s"); + addargs(&args, "--"); + addargs(&args, "%s", host); + addargs(&args, "%s", cmd); + + execvp(program, args.list); + perror(program); + exit(1); + } else if (*pid == -1) { + fatal("fork: %s", strerror(errno)); + } + /* Parent. Close the other side, and return the local side. */ + close(pin[0]); + *fdout = pin[1]; + close(pout[1]); + *fdin = pout[0]; + ssh_signal(SIGTERM, killchild); + ssh_signal(SIGINT, killchild); + ssh_signal(SIGHUP, killchild); + return 0; +} + +/* + * This function executes a command similar to do_cmd(), but expects the + * input and output descriptors to be setup by a previous call to do_cmd(). + * This way the input and output of two commands can be connected. + */ +int +do_cmd2(char *host, char *remuser, int port, char *cmd, + int fdin, int fdout) +{ + int status; + pid_t pid; + + if (verbose_mode) + fmprintf(stderr, + "Executing: 2nd program %s host %s, user %s, command %s\n", + ssh_program, host, + remuser ? remuser : "(unspecified)", cmd); + + if (port == -1) + port = sshport; + + /* Fork a child to execute the command on the remote host using ssh. */ + pid = fork(); + if (pid == 0) { + dup2(fdin, 0); + dup2(fdout, 1); + + replacearg(&args, 0, "%s", ssh_program); + if (port != -1) { + addargs(&args, "-p"); + addargs(&args, "%d", port); + } + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } + addargs(&args, "-oBatchMode=yes"); + addargs(&args, "--"); + addargs(&args, "%s", host); + addargs(&args, "%s", cmd); + + execvp(ssh_program, args.list); + perror(ssh_program); + exit(1); + } else if (pid == -1) { + fatal("fork: %s", strerror(errno)); + } + while (waitpid(pid, &status, 0) == -1) + if (errno != EINTR) + fatal("do_cmd2: waitpid: %s", strerror(errno)); + return 0; +} + +typedef struct { + size_t cnt; + char *buf; +} BUF; + +BUF *allocbuf(BUF *, int, int); +void lostconn(int); +int okname(char *); +void run_err(const char *,...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int note_err(const char *,...) + __attribute__((__format__ (printf, 1, 2))); +void verifydir(char *); + +struct passwd *pwd; +uid_t userid; +int errs, remin, remout, remin2, remout2; +int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; + +#define CMDNEEDS 64 +char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ + +enum scp_mode_e { + MODE_SCP, + MODE_SFTP +}; + +int response(void); +void rsource(char *, struct stat *); +void sink(int, char *[], const char *); +void source(int, char *[]); +void tolocal(int, char *[], enum scp_mode_e, char *sftp_direct); +void toremote(int, char *[], enum scp_mode_e, char *sftp_direct); +void usage(void); + +void source_sftp(int, char *, char *, struct sftp_conn *); +void sink_sftp(int, char *, const char *, struct sftp_conn *); +void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *, + char *, char *); + +int +main(int argc, char **argv) +{ + int ch, fflag, tflag, status, n; + char **newargv, *argv0; + const char *errstr; + extern char *optarg; + extern int optind; + enum scp_mode_e mode = MODE_SFTP; + char *sftp_direct = NULL; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + seed_rng(); + + msetlocale(); + + /* Copy argv, because we modify it */ + argv0 = argv[0]; + newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv)); + for (n = 0; n < argc; n++) + newargv[n] = xstrdup(argv[n]); + argv = newargv; + + __progname = ssh_get_progname(argv[0]); + + log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); + + memset(&args, '\0', sizeof(args)); + memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); + args.list = remote_remote_args.list = NULL; + addargs(&args, "%s", ssh_program); + addargs(&args, "-x"); + addargs(&args, "-oPermitLocalCommand=no"); + addargs(&args, "-oClearAllForwardings=yes"); + addargs(&args, "-oRemoteCommand=none"); + addargs(&args, "-oRequestTTY=no"); + + fflag = Tflag = tflag = 0; + while ((ch = getopt(argc, argv, + "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:")) != -1) { + switch (ch) { + /* User-visible flags. */ + case '1': + fatal("SSH protocol v.1 is no longer supported"); + break; + case '2': + /* Ignored */ + break; + case 'A': + case '4': + case '6': + case 'C': + addargs(&args, "-%c", ch); + addargs(&remote_remote_args, "-%c", ch); + break; + case 'D': + sftp_direct = optarg; + break; + case '3': + throughlocal = 1; + break; + case 'R': + throughlocal = 0; + break; + case 'o': + case 'c': + case 'i': + case 'F': + case 'J': + addargs(&remote_remote_args, "-%c", ch); + addargs(&remote_remote_args, "%s", optarg); + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); + break; + case 'O': + mode = MODE_SCP; + break; + case 's': + mode = MODE_SFTP; + break; + case 'P': + sshport = a2port(optarg); + if (sshport <= 0) + fatal("bad port \"%s\"\n", optarg); + break; + case 'B': + addargs(&remote_remote_args, "-oBatchmode=yes"); + addargs(&args, "-oBatchmode=yes"); + break; + case 'l': + limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, + &errstr); + if (errstr != NULL) + usage(); + limit_kbps *= 1024; /* kbps */ + bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); + break; + case 'p': + pflag = 1; + break; + case 'r': + iamrecursive = 1; + break; + case 'S': + ssh_program = xstrdup(optarg); + break; + case 'v': + addargs(&args, "-v"); + addargs(&remote_remote_args, "-v"); + if (verbose_mode == 0) + log_level = SYSLOG_LEVEL_DEBUG1; + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + verbose_mode = 1; + break; + case 'q': + addargs(&args, "-q"); + addargs(&remote_remote_args, "-q"); + showprogress = 0; + break; + + /* Server options. */ + case 'd': + targetshouldbedirectory = 1; + break; + case 'f': /* "from" */ + iamremote = 1; + fflag = 1; + break; + case 't': /* "to" */ + iamremote = 1; + tflag = 1; +#ifdef HAVE_CYGWIN + setmode(0, O_BINARY); +#endif + break; + case 'T': + Tflag = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); + + /* Do this last because we want the user to be able to override it */ + addargs(&args, "-oForwardAgent=no"); + + if (iamremote) + mode = MODE_SCP; + + if ((pwd = getpwuid(userid = getuid())) == NULL) + fatal("unknown user %u", (u_int) userid); + + if (!isatty(STDOUT_FILENO)) + showprogress = 0; + + if (pflag) { + /* Cannot pledge: -p allows setuid/setgid files... */ + } else { + if (pledge("stdio rpath wpath cpath fattr tty proc exec", + NULL) == -1) { + perror("pledge"); + exit(1); + } + } + + remin = STDIN_FILENO; + remout = STDOUT_FILENO; + + if (fflag) { + /* Follow "protocol", send data. */ + (void) response(); + source(argc, argv); + exit(errs != 0); + } + if (tflag) { + /* Receive data. */ + sink(argc, argv, NULL); + exit(errs != 0); + } + if (argc < 2) + usage(); + if (argc > 2) + targetshouldbedirectory = 1; + + remin = remout = -1; + do_cmd_pid = -1; + /* Command to be executed on remote system using "ssh". */ + (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", + verbose_mode ? " -v" : "", + iamrecursive ? " -r" : "", pflag ? " -p" : "", + targetshouldbedirectory ? " -d" : ""); + + (void) ssh_signal(SIGPIPE, lostconn); + + if (colon(argv[argc - 1])) /* Dest is remote host. */ + toremote(argc, argv, mode, sftp_direct); + else { + if (targetshouldbedirectory) + verifydir(argv[argc - 1]); + tolocal(argc, argv, mode, sftp_direct); /* Dest is local host. */ + } + /* + * Finally check the exit status of the ssh process, if one was forked + * and no error has occurred yet + */ + if (do_cmd_pid != -1 && (mode == MODE_SFTP || errs == 0)) { + if (remin != -1) + (void) close(remin); + if (remout != -1) + (void) close(remout); + if (waitpid(do_cmd_pid, &status, 0) == -1) + errs = 1; + else { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errs = 1; + } + } + exit(errs != 0); +} + +/* Callback from atomicio6 to update progress meter and limit bandwidth */ +static int +scpio(void *_cnt, size_t s) +{ + off_t *cnt = (off_t *)_cnt; + + *cnt += s; + refresh_progress_meter(0); + if (limit_kbps > 0) + bandwidth_limit(&bwlimit, s); + return 0; +} + +static int +do_times(int fd, int verb, const struct stat *sb) +{ + /* strlen(2^64) == 20; strlen(10^6) == 7 */ + char buf[(20 + 7 + 2) * 2 + 2]; + + (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", + (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), + (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); + if (verb) { + fprintf(stderr, "File mtime %lld atime %lld\n", + (long long)sb->st_mtime, (long long)sb->st_atime); + fprintf(stderr, "Sending file timestamps: %s", buf); + } + (void) atomicio(vwrite, fd, buf, strlen(buf)); + return (response()); +} + +static int +parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, + char **pathp) +{ + int r; + + r = parse_uri("scp", uri, userp, hostp, portp, pathp); + if (r == 0 && *pathp == NULL) + *pathp = xstrdup("."); + return r; +} + +/* Appends a string to an array; returns 0 on success, -1 on alloc failure */ +static int +append(char *cp, char ***ap, size_t *np) +{ + char **tmp; + + if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL) + return -1; + tmp[(*np)] = cp; + (*np)++; + *ap = tmp; + return 0; +} + +/* + * Finds the start and end of the first brace pair in the pattern. + * returns 0 on success or -1 for invalid patterns. + */ +static int +find_brace(const char *pattern, int *startp, int *endp) +{ + int i; + int in_bracket, brace_level; + + *startp = *endp = -1; + in_bracket = brace_level = 0; + for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) { + switch (pattern[i]) { + case '\\': + /* skip next character */ + if (pattern[i + 1] != '\0') + i++; + break; + case '[': + in_bracket = 1; + break; + case ']': + in_bracket = 0; + break; + case '{': + if (in_bracket) + break; + if (pattern[i + 1] == '}') { + /* Protect a single {}, for find(1), like csh */ + i++; /* skip */ + break; + } + if (*startp == -1) + *startp = i; + brace_level++; + break; + case '}': + if (in_bracket) + break; + if (*startp < 0) { + /* Unbalanced brace */ + return -1; + } + if (--brace_level <= 0) + *endp = i; + break; + } + } + /* unbalanced brackets/braces */ + if (*endp < 0 && (*startp >= 0 || in_bracket)) + return -1; + return 0; +} + +/* + * Assembles and records a successfully-expanded pattern, returns -1 on + * alloc failure. + */ +static int +emit_expansion(const char *pattern, int brace_start, int brace_end, + int sel_start, int sel_end, char ***patternsp, size_t *npatternsp) +{ + char *cp; + int o = 0, tail_len = strlen(pattern + brace_end + 1); + + if ((cp = malloc(brace_start + (sel_end - sel_start) + + tail_len + 1)) == NULL) + return -1; + + /* Pattern before initial brace */ + if (brace_start > 0) { + memcpy(cp, pattern, brace_start); + o = brace_start; + } + /* Current braced selection */ + if (sel_end - sel_start > 0) { + memcpy(cp + o, pattern + sel_start, + sel_end - sel_start); + o += sel_end - sel_start; + } + /* Remainder of pattern after closing brace */ + if (tail_len > 0) { + memcpy(cp + o, pattern + brace_end + 1, tail_len); + o += tail_len; + } + cp[o] = '\0'; + if (append(cp, patternsp, npatternsp) != 0) { + free(cp); + return -1; + } + return 0; +} + +/* + * Expand the first encountered brace in pattern, appending the expanded + * patterns it yielded to the *patternsp array. + * + * Returns 0 on success or -1 on allocation failure. + * + * Signals whether expansion was performed via *expanded and whether + * pattern was invalid via *invalid. + */ +static int +brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp, + int *expanded, int *invalid) +{ + int i; + int in_bracket, brace_start, brace_end, brace_level; + int sel_start, sel_end; + + *invalid = *expanded = 0; + + if (find_brace(pattern, &brace_start, &brace_end) != 0) { + *invalid = 1; + return 0; + } else if (brace_start == -1) + return 0; + + in_bracket = brace_level = 0; + for (i = sel_start = brace_start + 1; i < brace_end; i++) { + switch (pattern[i]) { + case '{': + if (in_bracket) + break; + brace_level++; + break; + case '}': + if (in_bracket) + break; + brace_level--; + break; + case '[': + in_bracket = 1; + break; + case ']': + in_bracket = 0; + break; + case '\\': + if (i < brace_end - 1) + i++; /* skip */ + break; + } + if (pattern[i] == ',' || i == brace_end - 1) { + if (in_bracket || brace_level > 0) + continue; + /* End of a selection, emit an expanded pattern */ + + /* Adjust end index for last selection */ + sel_end = (i == brace_end - 1) ? brace_end : i; + if (emit_expansion(pattern, brace_start, brace_end, + sel_start, sel_end, patternsp, npatternsp) != 0) + return -1; + /* move on to the next selection */ + sel_start = i + 1; + continue; + } + } + if (in_bracket || brace_level > 0) { + *invalid = 1; + return 0; + } + /* success */ + *expanded = 1; + return 0; +} + +/* Expand braces from pattern. Returns 0 on success, -1 on failure */ +static int +brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) +{ + char *cp, *cp2, **active = NULL, **done = NULL; + size_t i, nactive = 0, ndone = 0; + int ret = -1, invalid = 0, expanded = 0; + + *patternsp = NULL; + *npatternsp = 0; + + /* Start the worklist with the original pattern */ + if ((cp = strdup(pattern)) == NULL) + return -1; + if (append(cp, &active, &nactive) != 0) { + free(cp); + return -1; + } + while (nactive > 0) { + cp = active[nactive - 1]; + nactive--; + if (brace_expand_one(cp, &active, &nactive, + &expanded, &invalid) == -1) { + free(cp); + goto fail; + } + if (invalid) + fatal_f("invalid brace pattern \"%s\"", cp); + if (expanded) { + /* + * Current entry expanded to new entries on the + * active list; discard the progenitor pattern. + */ + free(cp); + continue; + } + /* + * Pattern did not expand; append the finename component to + * the completed list + */ + if ((cp2 = strrchr(cp, '/')) != NULL) + *cp2++ = '\0'; + else + cp2 = cp; + if (append(xstrdup(cp2), &done, &ndone) != 0) { + free(cp); + goto fail; + } + free(cp); + } + /* success */ + *patternsp = done; + *npatternsp = ndone; + done = NULL; + ndone = 0; + ret = 0; + fail: + for (i = 0; i < nactive; i++) + free(active[i]); + free(active); + for (i = 0; i < ndone; i++) + free(done[i]); + free(done); + return ret; +} + +static struct sftp_conn * +do_sftp_connect(char *host, char *user, int port, char *sftp_direct, + int *reminp, int *remoutp, int *pidp) +{ + if (sftp_direct == NULL) { + if (do_cmd(ssh_program, host, user, port, 1, "sftp", + reminp, remoutp, pidp) < 0) + return NULL; + + } else { + freeargs(&args); + addargs(&args, "sftp-server"); + if (do_cmd(sftp_direct, host, NULL, -1, 0, "sftp", + reminp, remoutp, pidp) < 0) + return NULL; + } + return do_init(*reminp, *remoutp, 32768, 64, limit_kbps); +} + +void +toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) +{ + char *suser = NULL, *host = NULL, *src = NULL; + char *bp, *tuser, *thost, *targ; + int sport = -1, tport = -1; + struct sftp_conn *conn = NULL, *conn2 = NULL; + arglist alist; + int i, r, status; + u_int j; + + memset(&alist, '\0', sizeof(alist)); + alist.list = NULL; + + /* Parse target */ + r = parse_scp_uri(argv[argc - 1], &tuser, &thost, &tport, &targ); + if (r == -1) { + fmprintf(stderr, "%s: invalid uri\n", argv[argc - 1]); + ++errs; + goto out; + } + if (r != 0) { + if (parse_user_host_path(argv[argc - 1], &tuser, &thost, + &targ) == -1) { + fmprintf(stderr, "%s: invalid target\n", argv[argc - 1]); + ++errs; + goto out; + } + } + + /* Parse source files */ + for (i = 0; i < argc - 1; i++) { + free(suser); + free(host); + free(src); + r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); + if (r == -1) { + fmprintf(stderr, "%s: invalid uri\n", argv[i]); + ++errs; + continue; + } + if (r != 0) { + parse_user_host_path(argv[i], &suser, &host, &src); + } + if (suser != NULL && !okname(suser)) { + ++errs; + continue; + } + if (host && throughlocal) { /* extended remote to remote */ + if (mode == MODE_SFTP) { + if (remin == -1) { + /* Connect to dest now */ + conn = do_sftp_connect(thost, tuser, + tport, sftp_direct, + &remin, &remout, &do_cmd_pid); + if (conn == NULL) { + fatal("Unable to open " + "destination connection"); + } + debug3_f("origin in %d out %d pid %ld", + remin, remout, (long)do_cmd_pid); + } + /* + * XXX remember suser/host/sport and only + * reconnect if they change between arguments. + * would save reconnections for cases like + * scp -3 hosta:/foo hosta:/bar hostb: + */ + /* Connect to origin now */ + conn2 = do_sftp_connect(host, suser, + sport, sftp_direct, + &remin2, &remout2, &do_cmd_pid2); + if (conn2 == NULL) { + fatal("Unable to open " + "source connection"); + } + debug3_f("destination in %d out %d pid %ld", + remin2, remout2, (long)do_cmd_pid2); + throughlocal_sftp(conn2, conn, src, targ); + (void) close(remin2); + (void) close(remout2); + remin2 = remout2 = -1; + if (waitpid(do_cmd_pid2, &status, 0) == -1) + ++errs; + else if (!WIFEXITED(status) || + WEXITSTATUS(status) != 0) + ++errs; + do_cmd_pid2 = -1; + continue; + } else { + xasprintf(&bp, "%s -f %s%s", cmd, + *src == '-' ? "-- " : "", src); + if (do_cmd(ssh_program, host, suser, sport, 0, + bp, &remin, &remout, &do_cmd_pid) < 0) + exit(1); + free(bp); + xasprintf(&bp, "%s -t %s%s", cmd, + *targ == '-' ? "-- " : "", targ); + if (do_cmd2(thost, tuser, tport, bp, + remin, remout) < 0) + exit(1); + free(bp); + (void) close(remin); + (void) close(remout); + remin = remout = -1; + } + } else if (host) { /* standard remote to remote */ + /* + * Second remote user is passed to first remote side + * via scp command-line. Ensure it contains no obvious + * shell characters. + */ + if (tuser != NULL && !okname(tuser)) { + ++errs; + continue; + } + if (tport != -1 && tport != SSH_DEFAULT_PORT) { + /* This would require the remote support URIs */ + fatal("target port not supported with two " + "remote hosts and the -R option"); + } + + freeargs(&alist); + addargs(&alist, "%s", ssh_program); + addargs(&alist, "-x"); + addargs(&alist, "-oClearAllForwardings=yes"); + addargs(&alist, "-n"); + for (j = 0; j < remote_remote_args.num; j++) { + addargs(&alist, "%s", + remote_remote_args.list[j]); + } + + if (sport != -1) { + addargs(&alist, "-p"); + addargs(&alist, "%d", sport); + } + if (suser) { + addargs(&alist, "-l"); + addargs(&alist, "%s", suser); + } + addargs(&alist, "--"); + addargs(&alist, "%s", host); + addargs(&alist, "%s", cmd); + addargs(&alist, "%s", src); + addargs(&alist, "%s%s%s:%s", + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + if (do_local_cmd(&alist) != 0) + errs = 1; + } else { /* local to remote */ + if (mode == MODE_SFTP) { + if (remin == -1) { + /* Connect to remote now */ + conn = do_sftp_connect(thost, tuser, + tport, sftp_direct, + &remin, &remout, &do_cmd_pid); + if (conn == NULL) { + fatal("Unable to open sftp " + "connection"); + } + } + + /* The protocol */ + source_sftp(1, argv[i], targ, conn); + continue; + } + /* SCP */ + if (remin == -1) { + xasprintf(&bp, "%s -t %s%s", cmd, + *targ == '-' ? "-- " : "", targ); + if (do_cmd(ssh_program, thost, tuser, tport, 0, + bp, &remin, &remout, &do_cmd_pid) < 0) + exit(1); + if (response() < 0) + exit(1); + free(bp); + } + source(1, argv + i); + } + } +out: + if (mode == MODE_SFTP) + free(conn); + free(tuser); + free(thost); + free(targ); + free(suser); + free(host); + free(src); +} + +void +tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) +{ + char *bp, *host = NULL, *src = NULL, *suser = NULL; + arglist alist; + struct sftp_conn *conn = NULL; + int i, r, sport = -1; + + memset(&alist, '\0', sizeof(alist)); + alist.list = NULL; + + for (i = 0; i < argc - 1; i++) { + free(suser); + free(host); + free(src); + r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); + if (r == -1) { + fmprintf(stderr, "%s: invalid uri\n", argv[i]); + ++errs; + continue; + } + if (r != 0) + parse_user_host_path(argv[i], &suser, &host, &src); + if (suser != NULL && !okname(suser)) { + ++errs; + continue; + } + if (!host) { /* Local to local. */ + freeargs(&alist); + addargs(&alist, "%s", _PATH_CP); + if (iamrecursive) + addargs(&alist, "-r"); + if (pflag) + addargs(&alist, "-p"); + addargs(&alist, "--"); + addargs(&alist, "%s", argv[i]); + addargs(&alist, "%s", argv[argc-1]); + if (do_local_cmd(&alist)) + ++errs; + continue; + } + /* Remote to local. */ + if (mode == MODE_SFTP) { + conn = do_sftp_connect(host, suser, sport, + sftp_direct, &remin, &remout, &do_cmd_pid); + if (conn == NULL) { + error("sftp connection failed"); + ++errs; + continue; + } + + /* The protocol */ + sink_sftp(1, argv[argc - 1], src, conn); + + free(conn); + (void) close(remin); + (void) close(remout); + remin = remout = -1; + continue; + } + /* SCP */ + xasprintf(&bp, "%s -f %s%s", + cmd, *src == '-' ? "-- " : "", src); + if (do_cmd(ssh_program, host, suser, sport, 0, bp, + &remin, &remout, &do_cmd_pid) < 0) { + free(bp); + ++errs; + continue; + } + free(bp); + sink(1, argv + argc - 1, src); + (void) close(remin); + remin = remout = -1; + } + free(suser); + free(host); + free(src); +} + +/* Prepare remote path, handling ~ by assuming cwd is the homedir */ +static char * +prepare_remote_path(struct sftp_conn *conn, const char *path) +{ + size_t nslash; + + /* Handle ~ prefixed paths */ + if (*path == '\0' || strcmp(path, "~") == 0) + return xstrdup("."); + if (*path != '~') + return xstrdup(path); + if (strncmp(path, "~/", 2) == 0) { + if ((nslash = strspn(path + 2, "/")) == strlen(path + 2)) + return xstrdup("."); + return xstrdup(path + 2 + nslash); + } + if (can_expand_path(conn)) + return do_expand_path(conn, path); + /* No protocol extension */ + error("server expand-path extension is required " + "for ~user paths in SFTP mode"); + return NULL; +} + +void +source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn) +{ + char *target = NULL, *filename = NULL, *abs_dst = NULL; + int src_is_dir, target_is_dir; + Attrib a; + struct stat st; + + memset(&a, '\0', sizeof(a)); + if (stat(src, &st) != 0) + fatal("stat local \"%s\": %s", src, strerror(errno)); + src_is_dir = S_ISDIR(st.st_mode); + if ((filename = basename(src)) == NULL) + fatal("basename \"%s\": %s", src, strerror(errno)); + + /* + * No need to glob here - the local shell already took care of + * the expansions + */ + if ((target = prepare_remote_path(conn, targ)) == NULL) + cleanup_exit(255); + target_is_dir = remote_is_dir(conn, target); + if (targetshouldbedirectory && !target_is_dir) { + debug("target directory \"%s\" does not exist", target); + a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = st.st_mode | 0700; /* ensure writable */ + if (do_mkdir(conn, target, &a, 1) != 0) + cleanup_exit(255); /* error already logged */ + target_is_dir = 1; + } + if (target_is_dir) + abs_dst = path_append(target, filename); + else { + abs_dst = target; + target = NULL; + } + debug3_f("copying local %s to remote %s", src, abs_dst); + + if (src_is_dir && iamrecursive) { + if (upload_dir(conn, src, abs_dst, pflag, + SFTP_PROGRESS_ONLY, 0, 0, 1) != 0) { + error("failed to upload directory %s to %s", src, targ); + errs = 1; + } + } else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0) { + error("failed to upload file %s to %s", src, targ); + errs = 1; + } + + free(abs_dst); + free(target); +} + +void +source(int argc, char **argv) +{ + struct stat stb; + static BUF buffer; + BUF *bp; + off_t i, statbytes; + size_t amt, nr; + int fd = -1, haderr, indx; + char *last, *name, buf[PATH_MAX + 128], encname[PATH_MAX]; + int len; + + for (indx = 0; indx < argc; ++indx) { + name = argv[indx]; + statbytes = 0; + len = strlen(name); + while (len > 1 && name[len-1] == '/') + name[--len] = '\0'; + if ((fd = open(name, O_RDONLY|O_NONBLOCK)) == -1) + goto syserr; + if (strchr(name, '\n') != NULL) { + strnvis(encname, name, sizeof(encname), VIS_NL); + name = encname; + } + if (fstat(fd, &stb) == -1) { +syserr: run_err("%s: %s", name, strerror(errno)); + goto next; + } + if (stb.st_size < 0) { + run_err("%s: %s", name, "Negative file size"); + goto next; + } + unset_nonblock(fd); + switch (stb.st_mode & S_IFMT) { + case S_IFREG: + break; + case S_IFDIR: + if (iamrecursive) { + rsource(name, &stb); + goto next; + } + /* FALLTHROUGH */ + default: + run_err("%s: not a regular file", name); + goto next; + } + if ((last = strrchr(name, '/')) == NULL) + last = name; + else + ++last; + curfile = last; + if (pflag) { + if (do_times(remout, verbose_mode, &stb) < 0) + goto next; + } +#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) + snprintf(buf, sizeof buf, "C%04o %lld %s\n", + (u_int) (stb.st_mode & FILEMODEMASK), + (long long)stb.st_size, last); + if (verbose_mode) + fmprintf(stderr, "Sending file modes: %s", buf); + (void) atomicio(vwrite, remout, buf, strlen(buf)); + if (response() < 0) + goto next; + if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { +next: if (fd != -1) { + (void) close(fd); + fd = -1; + } + continue; + } + if (showprogress) + start_progress_meter(curfile, stb.st_size, &statbytes); + set_nonblock(remout); + for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { + amt = bp->cnt; + if (i + (off_t)amt > stb.st_size) + amt = stb.st_size - i; + if (!haderr) { + if ((nr = atomicio(read, fd, + bp->buf, amt)) != amt) { + haderr = errno; + memset(bp->buf + nr, 0, amt - nr); + } + } + /* Keep writing after error to retain sync */ + if (haderr) { + (void)atomicio(vwrite, remout, bp->buf, amt); + memset(bp->buf, 0, amt); + continue; + } + if (atomicio6(vwrite, remout, bp->buf, amt, scpio, + &statbytes) != amt) + haderr = errno; + } + unset_nonblock(remout); + + if (fd != -1) { + if (close(fd) == -1 && !haderr) + haderr = errno; + fd = -1; + } + if (!haderr) + (void) atomicio(vwrite, remout, "", 1); + else + run_err("%s: %s", name, strerror(haderr)); + (void) response(); + if (showprogress) + stop_progress_meter(); + } +} + +void +rsource(char *name, struct stat *statp) +{ + DIR *dirp; + struct dirent *dp; + char *last, *vect[1], path[PATH_MAX]; + + if (!(dirp = opendir(name))) { + run_err("%s: %s", name, strerror(errno)); + return; + } + last = strrchr(name, '/'); + if (last == NULL) + last = name; + else + last++; + if (pflag) { + if (do_times(remout, verbose_mode, statp) < 0) { + closedir(dirp); + return; + } + } + (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", + (u_int) (statp->st_mode & FILEMODEMASK), 0, last); + if (verbose_mode) + fmprintf(stderr, "Entering directory: %s", path); + (void) atomicio(vwrite, remout, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { + run_err("%s/%s: name too long", name, dp->d_name); + continue; + } + (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); + vect[0] = path; + source(1, vect); + } + (void) closedir(dirp); + (void) atomicio(vwrite, remout, "E\n", 2); + (void) response(); +} + +void +sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn) +{ + char *abs_src = NULL; + char *abs_dst = NULL; + glob_t g; + char *filename, *tmp = NULL; + int i, r, err = 0, dst_is_dir; + struct stat st; + + memset(&g, 0, sizeof(g)); + + /* + * Here, we need remote glob as SFTP can not depend on remote shell + * expansions + */ + if ((abs_src = prepare_remote_path(conn, src)) == NULL) { + err = -1; + goto out; + } + + debug3_f("copying remote %s to local %s", abs_src, dst); + if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { + if (r == GLOB_NOSPACE) + error("%s: too many glob matches", src); + else + error("%s: %s", src, strerror(ENOENT)); + err = -1; + goto out; + } + + if ((r = stat(dst, &st)) != 0) + debug2_f("stat local \"%s\": %s", dst, strerror(errno)); + dst_is_dir = r == 0 && S_ISDIR(st.st_mode); + + if (g.gl_matchc > 1 && !dst_is_dir) { + if (r == 0) { + error("Multiple files match pattern, but destination " + "\"%s\" is not a directory", dst); + err = -1; + goto out; + } + debug2_f("creating destination \"%s\"", dst); + if (mkdir(dst, 0777) != 0) { + error("local mkdir \"%s\": %s", dst, strerror(errno)); + err = -1; + goto out; + } + dst_is_dir = 1; + } + + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + err = -1; + goto out; + } + + if (dst_is_dir) + abs_dst = path_append(dst, filename); + else + abs_dst = xstrdup(dst); + + debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { + if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, + pflag, SFTP_PROGRESS_ONLY, 0, 0, 1) == -1) + err = -1; + } else { + if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + pflag, 0, 0) == -1) + err = -1; + } + free(abs_dst); + abs_dst = NULL; + free(tmp); + tmp = NULL; + } + +out: + free(abs_src); + free(tmp); + globfree(&g); + if (err == -1) + errs = 1; +} + + +#define TYPE_OVERFLOW(type, val) \ + ((sizeof(type) == 4 && (val) > INT32_MAX) || \ + (sizeof(type) == 8 && (val) > INT64_MAX) || \ + (sizeof(type) != 4 && sizeof(type) != 8)) + +void +sink(int argc, char **argv, const char *src) +{ + static BUF buffer; + struct stat stb; + BUF *bp; + off_t i; + size_t j, count; + int amt, exists, first, ofd; + mode_t mode, omode, mask; + off_t size, statbytes; + unsigned long long ull; + int setimes, targisdir, wrerr; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; + char **patterns = NULL; + size_t n, npatterns = 0; + struct timeval tv[2]; + +#define atime tv[0] +#define mtime tv[1] +#define SCREWUP(str) { why = str; goto screwup; } + + if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) + SCREWUP("Unexpected off_t/time_t size"); + + setimes = targisdir = 0; + mask = umask(0); + if (!pflag) + (void) umask(mask); + if (argc != 1) { + run_err("ambiguous target"); + exit(1); + } + targ = *argv; + if (targetshouldbedirectory) + verifydir(targ); + + (void) atomicio(vwrite, remout, "", 1); + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; + if (src != NULL && !iamrecursive && !Tflag) { + /* + * Prepare to try to restrict incoming filenames to match + * the requested destination file glob. + */ + if (brace_expand(src, &patterns, &npatterns) != 0) + fatal_f("could not expand pattern"); + } + for (first = 1;; first = 0) { + cp = buf; + if (atomicio(read, remin, cp, 1) != 1) + goto done; + if (*cp++ == '\n') + SCREWUP("unexpected "); + do { + if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) + SCREWUP("lost connection"); + *cp++ = ch; + } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); + *cp = 0; + if (verbose_mode) + fmprintf(stderr, "Sink: %s", buf); + + if (buf[0] == '\01' || buf[0] == '\02') { + if (iamremote == 0) { + (void) snmprintf(visbuf, sizeof(visbuf), + NULL, "%s", buf + 1); + (void) atomicio(vwrite, STDERR_FILENO, + visbuf, strlen(visbuf)); + } + if (buf[0] == '\02') + exit(1); + ++errs; + continue; + } + if (buf[0] == 'E') { + (void) atomicio(vwrite, remout, "", 1); + goto done; + } + if (ch == '\n') + *--cp = 0; + + cp = buf; + if (*cp == 'T') { + setimes++; + cp++; + if (!isdigit((unsigned char)*cp)) + SCREWUP("mtime.sec not present"); + ull = strtoull(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("mtime.sec not delimited"); + if (TYPE_OVERFLOW(time_t, ull)) + setimes = 0; /* out of range */ + mtime.tv_sec = ull; + mtime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || + mtime.tv_usec > 999999) + SCREWUP("mtime.usec not delimited"); + if (!isdigit((unsigned char)*cp)) + SCREWUP("atime.sec not present"); + ull = strtoull(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("atime.sec not delimited"); + if (TYPE_OVERFLOW(time_t, ull)) + setimes = 0; /* out of range */ + atime.tv_sec = ull; + atime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || + atime.tv_usec > 999999) + SCREWUP("atime.usec not delimited"); + (void) atomicio(vwrite, remout, "", 1); + continue; + } + if (*cp != 'C' && *cp != 'D') { + /* + * Check for the case "rcp remote:foo\* local:bar". + * In this case, the line "No match." can be returned + * by the shell before the rcp command on the remote is + * executed so the ^Aerror_message convention isn't + * followed. + */ + if (first) { + run_err("%s", cp); + exit(1); + } + SCREWUP("expected control record"); + } + mode = 0; + for (++cp; cp < buf + 5; cp++) { + if (*cp < '0' || *cp > '7') + SCREWUP("bad mode"); + mode = (mode << 3) | (*cp - '0'); + } + if (!pflag) + mode &= ~mask; + if (*cp++ != ' ') + SCREWUP("mode not delimited"); + + if (!isdigit((unsigned char)*cp)) + SCREWUP("size not present"); + ull = strtoull(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("size not delimited"); + if (TYPE_OVERFLOW(off_t, ull)) + SCREWUP("size out of range"); + size = (off_t)ull; + + if (*cp == '\0' || strchr(cp, '/') != NULL || + strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { + run_err("error: unexpected filename: %s", cp); + exit(1); + } + if (npatterns > 0) { + for (n = 0; n < npatterns; n++) { + if (fnmatch(patterns[n], cp, 0) == 0) + break; + } + if (n >= npatterns) + SCREWUP("filename does not match request"); + } + if (targisdir) { + static char *namebuf; + static size_t cursize; + size_t need; + + need = strlen(targ) + strlen(cp) + 250; + if (need > cursize) { + free(namebuf); + namebuf = xmalloc(need); + cursize = need; + } + (void) snprintf(namebuf, need, "%s%s%s", targ, + strcmp(targ, "/") ? "/" : "", cp); + np = namebuf; + } else + np = targ; + curfile = cp; + exists = stat(np, &stb) == 0; + if (buf[0] == 'D') { + int mod_flag = pflag; + if (!iamrecursive) + SCREWUP("received directory without -r"); + if (exists) { + if (!S_ISDIR(stb.st_mode)) { + errno = ENOTDIR; + goto bad; + } + if (pflag) + (void) chmod(np, mode); + } else { + /* Handle copying from a read-only directory */ + mod_flag = 1; + if (mkdir(np, mode | S_IRWXU) == -1) + goto bad; + } + vect[0] = xstrdup(np); + sink(1, vect, src); + if (setimes) { + setimes = 0; + (void) utimes(vect[0], tv); + } + if (mod_flag) + (void) chmod(vect[0], mode); + free(vect[0]); + continue; + } + omode = mode; + mode |= S_IWUSR; + if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) == -1) { +bad: run_err("%s: %s", np, strerror(errno)); + continue; + } + (void) atomicio(vwrite, remout, "", 1); + if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { + (void) close(ofd); + continue; + } + cp = bp->buf; + wrerr = 0; + + /* + * NB. do not use run_err() unless immediately followed by + * exit() below as it may send a spurious reply that might + * desyncronise us from the peer. Use note_err() instead. + */ + statbytes = 0; + if (showprogress) + start_progress_meter(curfile, size, &statbytes); + set_nonblock(remin); + for (count = i = 0; i < size; i += bp->cnt) { + amt = bp->cnt; + if (i + amt > size) + amt = size - i; + count += amt; + do { + j = atomicio6(read, remin, cp, amt, + scpio, &statbytes); + if (j == 0) { + run_err("%s", j != EPIPE ? + strerror(errno) : + "dropped connection"); + exit(1); + } + amt -= j; + cp += j; + } while (amt > 0); + + if (count == bp->cnt) { + /* Keep reading so we stay sync'd up. */ + if (!wrerr) { + if (atomicio(vwrite, ofd, bp->buf, + count) != count) { + note_err("%s: %s", np, + strerror(errno)); + wrerr = 1; + } + } + count = 0; + cp = bp->buf; + } + } + unset_nonblock(remin); + if (count != 0 && !wrerr && + atomicio(vwrite, ofd, bp->buf, count) != count) { + note_err("%s: %s", np, strerror(errno)); + wrerr = 1; + } + if (!wrerr && (!exists || S_ISREG(stb.st_mode)) && + ftruncate(ofd, size) != 0) + note_err("%s: truncate: %s", np, strerror(errno)); + if (pflag) { + if (exists || omode != mode) +#ifdef HAVE_FCHMOD + if (fchmod(ofd, omode)) { +#else /* HAVE_FCHMOD */ + if (chmod(np, omode)) { +#endif /* HAVE_FCHMOD */ + note_err("%s: set mode: %s", + np, strerror(errno)); + } + } else { + if (!exists && omode != mode) +#ifdef HAVE_FCHMOD + if (fchmod(ofd, omode & ~mask)) { +#else /* HAVE_FCHMOD */ + if (chmod(np, omode & ~mask)) { +#endif /* HAVE_FCHMOD */ + note_err("%s: set mode: %s", + np, strerror(errno)); + } + } + if (close(ofd) == -1) + note_err("%s: close: %s", np, strerror(errno)); + (void) response(); + if (showprogress) + stop_progress_meter(); + if (setimes && !wrerr) { + setimes = 0; + if (utimes(np, tv) == -1) { + note_err("%s: set times: %s", + np, strerror(errno)); + } + } + /* If no error was noted then signal success for this file */ + if (note_err(NULL) == 0) + (void) atomicio(vwrite, remout, "", 1); + } +done: + for (n = 0; n < npatterns; n++) + free(patterns[n]); + free(patterns); + return; +screwup: + for (n = 0; n < npatterns; n++) + free(patterns[n]); + free(patterns); + run_err("protocol error: %s", why); + exit(1); +} + +void +throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to, + char *src, char *targ) +{ + char *target = NULL, *filename = NULL, *abs_dst = NULL; + char *abs_src = NULL, *tmp = NULL; + glob_t g; + int i, r, targetisdir, err = 0; + + if ((filename = basename(src)) == NULL) + fatal("basename %s: %s", src, strerror(errno)); + + if ((abs_src = prepare_remote_path(from, src)) == NULL || + (target = prepare_remote_path(to, targ)) == NULL) + cleanup_exit(255); + memset(&g, 0, sizeof(g)); + + targetisdir = remote_is_dir(to, target); + if (!targetisdir && targetshouldbedirectory) { + error("%s: destination is not a directory", targ); + err = -1; + goto out; + } + + debug3_f("copying remote %s to remote %s", abs_src, target); + if ((r = remote_glob(from, abs_src, GLOB_MARK, NULL, &g)) != 0) { + if (r == GLOB_NOSPACE) + error("%s: too many glob matches", src); + else + error("%s: %s", src, strerror(ENOENT)); + err = -1; + goto out; + } + + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + err = -1; + goto out; + } + + if (targetisdir) + abs_dst = path_append(target, filename); + else + abs_dst = xstrdup(target); + + debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { + if (crossload_dir(from, to, g.gl_pathv[i], abs_dst, + NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) + err = -1; + } else { + if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, + pflag) == -1) + err = -1; + } + free(abs_dst); + abs_dst = NULL; + free(tmp); + tmp = NULL; + } + +out: + free(abs_src); + free(abs_dst); + free(target); + free(tmp); + globfree(&g); + if (err == -1) + errs = 1; +} + +int +response(void) +{ + char ch, *cp, resp, rbuf[2048], visbuf[2048]; + + if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) + lostconn(0); + + cp = rbuf; + switch (resp) { + case 0: /* ok */ + return (0); + default: + *cp++ = resp; + /* FALLTHROUGH */ + case 1: /* error, followed by error msg */ + case 2: /* fatal error, "" */ + do { + if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) + lostconn(0); + *cp++ = ch; + } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); + + if (!iamremote) { + cp[-1] = '\0'; + (void) snmprintf(visbuf, sizeof(visbuf), + NULL, "%s\n", rbuf); + (void) atomicio(vwrite, STDERR_FILENO, + visbuf, strlen(visbuf)); + } + ++errs; + if (resp == 1) + return (-1); + exit(1); + } + /* NOTREACHED */ +} + +void +usage(void) +{ + (void) fprintf(stderr, + "usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]\n" + " [-i identity_file] [-J destination] [-l limit]\n" + " [-o ssh_option] [-P port] [-S program] source ... target\n"); + exit(1); +} + +void +run_err(const char *fmt,...) +{ + static FILE *fp; + va_list ap; + + ++errs; + if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { + (void) fprintf(fp, "%c", 0x01); + (void) fprintf(fp, "scp: "); + va_start(ap, fmt); + (void) vfprintf(fp, fmt, ap); + va_end(ap); + (void) fprintf(fp, "\n"); + (void) fflush(fp); + } + + if (!iamremote) { + va_start(ap, fmt); + vfmprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } +} + +/* + * Notes a sink error for sending at the end of a file transfer. Returns 0 if + * no error has been noted or -1 otherwise. Use note_err(NULL) to flush + * any active error at the end of the transfer. + */ +int +note_err(const char *fmt, ...) +{ + static char *emsg; + va_list ap; + + /* Replay any previously-noted error */ + if (fmt == NULL) { + if (emsg == NULL) + return 0; + run_err("%s", emsg); + free(emsg); + emsg = NULL; + return -1; + } + + errs++; + /* Prefer first-noted error */ + if (emsg != NULL) + return -1; + + va_start(ap, fmt); + vasnmprintf(&emsg, INT_MAX, NULL, fmt, ap); + va_end(ap); + return -1; +} + +void +verifydir(char *cp) +{ + struct stat stb; + + if (!stat(cp, &stb)) { + if (S_ISDIR(stb.st_mode)) + return; + errno = ENOTDIR; + } + run_err("%s: %s", cp, strerror(errno)); + killchild(0); +} + +int +okname(char *cp0) +{ + int c; + char *cp; + + cp = cp0; + do { + c = (int)*cp; + if (c & 0200) + goto bad; + if (!isalpha(c) && !isdigit((unsigned char)c)) { + switch (c) { + case '\'': + case '"': + case '`': + case ' ': + case '#': + goto bad; + default: + break; + } + } + } while (*++cp); + return (1); + +bad: fmprintf(stderr, "%s: invalid user name\n", cp0); + return (0); +} + +BUF * +allocbuf(BUF *bp, int fd, int blksize) +{ + size_t size; +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + struct stat stb; + + if (fstat(fd, &stb) == -1) { + run_err("fstat: %s", strerror(errno)); + return (0); + } + size = ROUNDUP(stb.st_blksize, blksize); + if (size == 0) + size = blksize; +#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ + size = blksize; +#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ + if (bp->cnt >= size) + return (bp); + bp->buf = xrecallocarray(bp->buf, bp->cnt, size, 1); + bp->cnt = size; + return (bp); +} + +void +lostconn(int signo) +{ + if (!iamremote) + (void)write(STDERR_FILENO, "lost connection\n", 16); + if (signo) + _exit(1); + else + exit(1); +} + +void +cleanup_exit(int i) +{ + if (remin > 0) + close(remin); + if (remout > 0) + close(remout); + if (remin2 > 0) + close(remin2); + if (remout2 > 0) + close(remout2); + if (do_cmd_pid > 0) + waitpid(do_cmd_pid, NULL, 0); + if (do_cmd_pid2 > 0) + waitpid(do_cmd_pid2, NULL, 0); + exit(i); +} diff --git a/aosp/external/openssh/servconf.c b/aosp/external/openssh/servconf.c new file mode 100755 index 0000000000000000000000000000000000000000..f8e90d8b19902525ef0b4ab3be9f747ec8732d25 --- /dev/null +++ b/aosp/external/openssh/servconf.c @@ -0,0 +1,3046 @@ + +/* $OpenBSD: servconf.c,v 1.384 2022/03/18 04:04:11 djm Exp $ */ +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include +#ifdef __OpenBSD__ +#include +#endif + +#include +#include +#include +#ifdef HAVE_NET_ROUTE_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UTIL_H +#include +#endif +#ifdef USE_SYSTEM_GLOB +# include +#else +# include "openbsd-compat/glob.h" +#endif + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#include "log.h" +#include "sshbuf.h" +#include "misc.h" +#include "servconf.h" +#include "compat.h" +#include "pathnames.h" +#include "cipher.h" +#include "sshkey.h" +#include "kex.h" +#include "mac.h" +#include "match.h" +#include "channels.h" +#include "groupaccess.h" +#include "canohost.h" +#include "packet.h" +#include "ssherr.h" +#include "hostfile.h" +#include "auth.h" +#include "myproposal.h" +#include "digest.h" + +#if defined(ANDROID) +#include +#endif + +static void add_listen_addr(ServerOptions *, const char *, + const char *, int); +static void add_one_listen_addr(ServerOptions *, const char *, + const char *, int); +static void parse_server_config_depth(ServerOptions *options, + const char *filename, struct sshbuf *conf, struct include_list *includes, + struct connection_info *connectinfo, int flags, int *activep, int depth); + +/* Use of privilege separation or not */ +extern int use_privsep; +extern struct sshbuf *cfg; + +/* Initializes the server options to their default values. */ + +void +initialize_server_options(ServerOptions *options) +{ + memset(options, 0, sizeof(*options)); + + /* Portable-specific options */ + options->use_pam = -1; + + /* Standard Options */ + options->num_ports = 0; + options->ports_from_cmdline = 0; + options->queued_listen_addrs = NULL; + options->num_queued_listens = 0; + options->listen_addrs = NULL; + options->num_listen_addrs = 0; + options->address_family = -1; + options->routing_domain = NULL; + options->num_host_key_files = 0; + options->num_host_cert_files = 0; + options->host_key_agent = NULL; + options->pid_file = NULL; + options->login_grace_time = -1; + options->permit_root_login = PERMIT_NOT_SET; + options->ignore_rhosts = -1; + options->ignore_user_known_hosts = -1; + options->print_motd = -1; + options->print_lastlog = -1; + options->x11_forwarding = -1; + options->x11_display_offset = -1; + options->x11_use_localhost = -1; + options->permit_tty = -1; + options->permit_user_rc = -1; + options->xauth_location = NULL; + options->strict_modes = -1; + options->tcp_keep_alive = -1; + options->log_facility = SYSLOG_FACILITY_NOT_SET; + options->log_level = SYSLOG_LEVEL_NOT_SET; + options->num_log_verbose = 0; + options->log_verbose = NULL; + options->hostbased_authentication = -1; + options->hostbased_uses_name_from_packet_only = -1; + options->hostbased_accepted_algos = NULL; + options->hostkeyalgorithms = NULL; + options->pubkey_authentication = -1; + options->pubkey_auth_options = -1; + options->pubkey_accepted_algos = NULL; + options->kerberos_authentication = -1; + options->kerberos_or_local_passwd = -1; + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->permit_empty_passwd = -1; + options->permit_user_env = -1; + options->permit_user_env_allowlist = NULL; + options->compression = -1; + options->rekey_limit = -1; + options->rekey_interval = -1; + options->allow_tcp_forwarding = -1; + options->allow_streamlocal_forwarding = -1; + options->allow_agent_forwarding = -1; + options->num_allow_users = 0; + options->num_deny_users = 0; + options->num_allow_groups = 0; + options->num_deny_groups = 0; + options->ciphers = NULL; + options->macs = NULL; + options->kex_algorithms = NULL; + options->ca_sign_algorithms = NULL; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; + options->num_subsystems = 0; + options->max_startups_begin = -1; + options->max_startups_rate = -1; + options->max_startups = -1; + options->per_source_max_startups = -1; + options->per_source_masklen_ipv4 = -1; + options->per_source_masklen_ipv6 = -1; + options->max_authtries = -1; + options->max_sessions = -1; + options->banner = NULL; + options->use_dns = -1; + options->client_alive_interval = -1; + options->client_alive_count_max = -1; + options->num_authkeys_files = 0; + options->num_accept_env = 0; + options->num_setenv = 0; + options->permit_tun = -1; + options->permitted_opens = NULL; + options->permitted_listens = NULL; + options->adm_forced_command = NULL; + options->chroot_directory = NULL; + options->authorized_keys_command = NULL; + options->authorized_keys_command_user = NULL; + options->revoked_keys_file = NULL; + options->sk_provider = NULL; + options->trusted_user_ca_keys = NULL; + options->authorized_principals_file = NULL; + options->authorized_principals_command = NULL; + options->authorized_principals_command_user = NULL; + options->ip_qos_interactive = -1; + options->ip_qos_bulk = -1; + options->version_addendum = NULL; + options->fingerprint_hash = -1; + options->disable_forwarding = -1; + options->expose_userauth_info = -1; +} + +/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ +static int +option_clear_or_none(const char *o) +{ + return o == NULL || strcasecmp(o, "none") == 0; +} + +static void +assemble_algorithms(ServerOptions *o) +{ + char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig; + char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig; + int r; + + all_cipher = cipher_alg_list(',', 0); + all_mac = mac_alg_list(','); + all_kex = kex_alg_list(','); + all_key = sshkey_alg_list(0, 0, 1, ','); + all_sig = sshkey_alg_list(0, 1, 1, ','); + /* remove unsupported algos from default lists */ + def_cipher = match_filter_allowlist(KEX_SERVER_ENCRYPT, all_cipher); + def_mac = match_filter_allowlist(KEX_SERVER_MAC, all_mac); + def_kex = match_filter_allowlist(KEX_SERVER_KEX, all_kex); + def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); + def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig); +#define ASSEMBLE(what, defaults, all) \ + do { \ + if ((r = kex_assemble_names(&o->what, defaults, all)) != 0) \ + fatal_fr(r, "%s", #what); \ + } while (0) + ASSEMBLE(ciphers, def_cipher, all_cipher); + ASSEMBLE(macs, def_mac, all_mac); + ASSEMBLE(kex_algorithms, def_kex, all_kex); + ASSEMBLE(hostkeyalgorithms, def_key, all_key); + ASSEMBLE(hostbased_accepted_algos, def_key, all_key); + ASSEMBLE(pubkey_accepted_algos, def_key, all_key); + ASSEMBLE(ca_sign_algorithms, def_sig, all_sig); +#undef ASSEMBLE + free(all_cipher); + free(all_mac); + free(all_kex); + free(all_key); + free(all_sig); + free(def_cipher); + free(def_mac); + free(def_kex); + free(def_key); + free(def_sig); +} + +void +servconf_add_hostkey(const char *file, const int line, + ServerOptions *options, const char *path, int userprovided) +{ + char *apath = derelativise_path(path); + + opt_array_append2(file, line, "HostKey", + &options->host_key_files, &options->host_key_file_userprovided, + &options->num_host_key_files, apath, userprovided); + free(apath); +} + +void +servconf_add_hostcert(const char *file, const int line, + ServerOptions *options, const char *path) +{ + char *apath = derelativise_path(path); + + opt_array_append(file, line, "HostCertificate", + &options->host_cert_files, &options->num_host_cert_files, apath); + free(apath); +} + +void +fill_default_server_options(ServerOptions *options) +{ + u_int i; + + /* Portable-specific options */ + if (options->use_pam == -1) + options->use_pam = 0; + + /* Standard Options */ + if (options->num_host_key_files == 0) { + /* fill default hostkeys for protocols */ + servconf_add_hostkey("[default]", 0, options, + _PATH_HOST_RSA_KEY_FILE, 0); +#ifdef OPENSSL_HAS_ECC + servconf_add_hostkey("[default]", 0, options, + _PATH_HOST_ECDSA_KEY_FILE, 0); +#endif + servconf_add_hostkey("[default]", 0, options, + _PATH_HOST_ED25519_KEY_FILE, 0); +#ifdef WITH_XMSS + servconf_add_hostkey("[default]", 0, options, + _PATH_HOST_XMSS_KEY_FILE, 0); +#endif /* WITH_XMSS */ + } + /* No certificates by default */ + if (options->num_ports == 0) + options->ports[options->num_ports++] = SSH_DEFAULT_PORT; + if (options->address_family == -1) + options->address_family = AF_UNSPEC; + if (options->listen_addrs == NULL) + add_listen_addr(options, NULL, NULL, 0); + if (options->pid_file == NULL) + options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); + if (options->moduli_file == NULL) + options->moduli_file = xstrdup(_PATH_DH_MODULI); + if (options->login_grace_time == -1) + options->login_grace_time = 120; + if (options->permit_root_login == PERMIT_NOT_SET) + options->permit_root_login = PERMIT_NO_PASSWD; + if (options->ignore_rhosts == -1) + options->ignore_rhosts = 1; + if (options->ignore_user_known_hosts == -1) + options->ignore_user_known_hosts = 0; + if (options->print_motd == -1) + options->print_motd = 1; + if (options->print_lastlog == -1) + options->print_lastlog = 1; + if (options->x11_forwarding == -1) + options->x11_forwarding = 0; + if (options->x11_display_offset == -1) + options->x11_display_offset = 10; + if (options->x11_use_localhost == -1) + options->x11_use_localhost = 1; + if (options->xauth_location == NULL) + options->xauth_location = xstrdup(_PATH_XAUTH); + if (options->permit_tty == -1) + options->permit_tty = 1; + if (options->permit_user_rc == -1) + options->permit_user_rc = 1; + if (options->strict_modes == -1) + options->strict_modes = 1; + if (options->tcp_keep_alive == -1) + options->tcp_keep_alive = 1; + if (options->log_facility == SYSLOG_FACILITY_NOT_SET) + options->log_facility = SYSLOG_FACILITY_AUTH; + if (options->log_level == SYSLOG_LEVEL_NOT_SET) + options->log_level = SYSLOG_LEVEL_INFO; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; + if (options->hostbased_uses_name_from_packet_only == -1) + options->hostbased_uses_name_from_packet_only = 0; + if (options->pubkey_authentication == -1) + options->pubkey_authentication = 1; + if (options->pubkey_auth_options == -1) + options->pubkey_auth_options = 0; + if (options->kerberos_authentication == -1) + options->kerberos_authentication = 0; + if (options->kerberos_or_local_passwd == -1) + options->kerberos_or_local_passwd = 1; + if (options->kerberos_ticket_cleanup == -1) + options->kerberos_ticket_cleanup = 1; + if (options->kerberos_get_afs_token == -1) + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; + if (options->gss_cleanup_creds == -1) + options->gss_cleanup_creds = 1; + if (options->gss_strict_acceptor == -1) + options->gss_strict_acceptor = 1; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) + options->kbd_interactive_authentication = 1; + if (options->permit_empty_passwd == -1) + options->permit_empty_passwd = 0; + if (options->permit_user_env == -1) { + options->permit_user_env = 0; + options->permit_user_env_allowlist = NULL; + } + if (options->compression == -1) +#ifdef WITH_ZLIB + options->compression = COMP_DELAYED; +#else + options->compression = COMP_NONE; +#endif + + if (options->rekey_limit == -1) + options->rekey_limit = 0; + if (options->rekey_interval == -1) + options->rekey_interval = 0; + if (options->allow_tcp_forwarding == -1) + options->allow_tcp_forwarding = FORWARD_ALLOW; + if (options->allow_streamlocal_forwarding == -1) + options->allow_streamlocal_forwarding = FORWARD_ALLOW; + if (options->allow_agent_forwarding == -1) + options->allow_agent_forwarding = 1; + if (options->fwd_opts.gateway_ports == -1) + options->fwd_opts.gateway_ports = 0; + if (options->max_startups == -1) + options->max_startups = 100; + if (options->max_startups_rate == -1) + options->max_startups_rate = 30; /* 30% */ + if (options->max_startups_begin == -1) + options->max_startups_begin = 10; + if (options->per_source_max_startups == -1) + options->per_source_max_startups = INT_MAX; + if (options->per_source_masklen_ipv4 == -1) + options->per_source_masklen_ipv4 = 32; + if (options->per_source_masklen_ipv6 == -1) + options->per_source_masklen_ipv6 = 128; + if (options->max_authtries == -1) + options->max_authtries = DEFAULT_AUTH_FAIL_MAX; + if (options->max_sessions == -1) + options->max_sessions = DEFAULT_SESSIONS_MAX; + if (options->use_dns == -1) + options->use_dns = 0; + if (options->client_alive_interval == -1) + options->client_alive_interval = 0; + if (options->client_alive_count_max == -1) + options->client_alive_count_max = 3; + if (options->num_authkeys_files == 0) { + opt_array_append("[default]", 0, "AuthorizedKeysFiles", + &options->authorized_keys_files, + &options->num_authkeys_files, + _PATH_SSH_USER_PERMITTED_KEYS); + opt_array_append("[default]", 0, "AuthorizedKeysFiles", + &options->authorized_keys_files, + &options->num_authkeys_files, + _PATH_SSH_USER_PERMITTED_KEYS2); + } + if (options->permit_tun == -1) + options->permit_tun = SSH_TUNMODE_NO; + if (options->ip_qos_interactive == -1) + options->ip_qos_interactive = IPTOS_DSCP_AF21; + if (options->ip_qos_bulk == -1) + options->ip_qos_bulk = IPTOS_DSCP_CS1; + if (options->version_addendum == NULL) + options->version_addendum = xstrdup(""); + if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) + options->fwd_opts.streamlocal_bind_mask = 0177; + if (options->fwd_opts.streamlocal_bind_unlink == -1) + options->fwd_opts.streamlocal_bind_unlink = 0; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; + if (options->disable_forwarding == -1) + options->disable_forwarding = 0; + if (options->expose_userauth_info == -1) + options->expose_userauth_info = 0; + if (options->sk_provider == NULL) + options->sk_provider = xstrdup("internal"); + + assemble_algorithms(options); + + /* Turn privilege separation and sandboxing on by default */ + if (use_privsep == -1) + use_privsep = PRIVSEP_OFF; // /*penwel:opssh authord with password 2021-04-16*/ + +#define CLEAR_ON_NONE(v) \ + do { \ + if (option_clear_or_none(v)) { \ + free(v); \ + v = NULL; \ + } \ + } while(0) + CLEAR_ON_NONE(options->pid_file); + CLEAR_ON_NONE(options->xauth_location); + CLEAR_ON_NONE(options->banner); + CLEAR_ON_NONE(options->trusted_user_ca_keys); + CLEAR_ON_NONE(options->revoked_keys_file); + CLEAR_ON_NONE(options->sk_provider); + CLEAR_ON_NONE(options->authorized_principals_file); + CLEAR_ON_NONE(options->adm_forced_command); + CLEAR_ON_NONE(options->chroot_directory); + CLEAR_ON_NONE(options->routing_domain); + CLEAR_ON_NONE(options->host_key_agent); + for (i = 0; i < options->num_host_key_files; i++) + CLEAR_ON_NONE(options->host_key_files[i]); + for (i = 0; i < options->num_host_cert_files; i++) + CLEAR_ON_NONE(options->host_cert_files[i]); +#undef CLEAR_ON_NONE + + /* Similar handling for AuthenticationMethods=any */ + if (options->num_auth_methods == 1 && + strcmp(options->auth_methods[0], "any") == 0) { + free(options->auth_methods[0]); + options->auth_methods[0] = NULL; + options->num_auth_methods = 0; + } +} + +/* Keyword tokens. */ +typedef enum { + sBadOption, /* == unknown option */ + /* Portable-specific options */ + sUsePAM, + /* Standard Options */ + sPort, sHostKeyFile, sLoginGraceTime, + sPermitRootLogin, sLogFacility, sLogLevel, sLogVerbose, + sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, + sKerberosGetAFSToken, sPasswordAuthentication, + sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, + sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, + sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, + sPermitUserEnvironment, sAllowTcpForwarding, sCompression, + sRekeyLimit, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, + sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, sModuliFile, + sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedAlgorithms, + sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, + sBanner, sUseDNS, sHostbasedAuthentication, + sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedAlgorithms, + sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, sInclude, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, + sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, + sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum, + sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, + sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, + sStreamLocalBindMask, sStreamLocalBindUnlink, + sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, + sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider, + sDeprecated, sIgnore, sUnsupported +} ServerOpCodes; + +#define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */ +#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ +#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) +#define SSHCFG_NEVERMATCH 0x04 /* Match never matches; internal only */ +#define SSHCFG_MATCH_ONLY 0x08 /* Match only in conditional blocks; internal only */ + +/* Textual representation of the tokens. */ +static struct { + const char *name; + ServerOpCodes opcode; + u_int flags; +} keywords[] = { + /* Portable-specific options */ +#ifdef USE_PAM + { "usepam", sUsePAM, SSHCFG_GLOBAL }, +#else + { "usepam", sUnsupported, SSHCFG_GLOBAL }, +#endif + { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, + /* Standard Options */ + { "port", sPort, SSHCFG_GLOBAL }, + { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, + { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ + { "hostkeyagent", sHostKeyAgent, SSHCFG_GLOBAL }, + { "pidfile", sPidFile, SSHCFG_GLOBAL }, + { "modulifile", sModuliFile, SSHCFG_GLOBAL }, + { "serverkeybits", sDeprecated, SSHCFG_GLOBAL }, + { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, + { "keyregenerationinterval", sDeprecated, SSHCFG_GLOBAL }, + { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL }, + { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, + { "loglevel", sLogLevel, SSHCFG_ALL }, + { "logverbose", sLogVerbose, SSHCFG_ALL }, + { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, + { "rhostsrsaauthentication", sDeprecated, SSHCFG_ALL }, + { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, + { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL }, + { "hostbasedacceptedalgorithms", sHostbasedAcceptedAlgorithms, SSHCFG_ALL }, + { "hostbasedacceptedkeytypes", sHostbasedAcceptedAlgorithms, SSHCFG_ALL }, /* obsolete */ + { "hostkeyalgorithms", sHostKeyAlgorithms, SSHCFG_GLOBAL }, + { "rsaauthentication", sDeprecated, SSHCFG_ALL }, + { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, + { "pubkeyacceptedalgorithms", sPubkeyAcceptedAlgorithms, SSHCFG_ALL }, + { "pubkeyacceptedkeytypes", sPubkeyAcceptedAlgorithms, SSHCFG_ALL }, /* obsolete */ + { "pubkeyauthoptions", sPubkeyAuthOptions, SSHCFG_ALL }, + { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ +#ifdef KRB5 + { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, + { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, + { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, +#ifdef USE_AFS + { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, +#else + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, +#endif +#else + { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, + { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, +#endif + { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, + { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, +#ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, +#else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, +#endif + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */ + { "skeyauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */ + { "checkmail", sDeprecated, SSHCFG_GLOBAL }, + { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, + { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, + { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, +#ifdef DISABLE_LASTLOG + { "printlastlog", sUnsupported, SSHCFG_GLOBAL }, +#else + { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, +#endif + { "ignorerhosts", sIgnoreRhosts, SSHCFG_ALL }, + { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, + { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, + { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, + { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, + { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, + { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, + { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, + { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, + { "uselogin", sDeprecated, SSHCFG_GLOBAL }, + { "compression", sCompression, SSHCFG_GLOBAL }, + { "rekeylimit", sRekeyLimit, SSHCFG_ALL }, + { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, + { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ + { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, + { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL }, + { "allowusers", sAllowUsers, SSHCFG_ALL }, + { "denyusers", sDenyUsers, SSHCFG_ALL }, + { "allowgroups", sAllowGroups, SSHCFG_ALL }, + { "denygroups", sDenyGroups, SSHCFG_ALL }, + { "ciphers", sCiphers, SSHCFG_GLOBAL }, + { "macs", sMacs, SSHCFG_GLOBAL }, + { "protocol", sIgnore, SSHCFG_GLOBAL }, + { "gatewayports", sGatewayPorts, SSHCFG_ALL }, + { "subsystem", sSubsystem, SSHCFG_GLOBAL }, + { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, + { "persourcemaxstartups", sPerSourceMaxStartups, SSHCFG_GLOBAL }, + { "persourcenetblocksize", sPerSourceNetBlockSize, SSHCFG_GLOBAL }, + { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, + { "maxsessions", sMaxSessions, SSHCFG_ALL }, + { "banner", sBanner, SSHCFG_ALL }, + { "usedns", sUseDNS, SSHCFG_GLOBAL }, + { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, + { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, + { "clientaliveinterval", sClientAliveInterval, SSHCFG_ALL }, + { "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL }, + { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, + { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, + { "useprivilegeseparation", sDeprecated, SSHCFG_GLOBAL}, + { "acceptenv", sAcceptEnv, SSHCFG_ALL }, + { "setenv", sSetEnv, SSHCFG_ALL }, + { "permittunnel", sPermitTunnel, SSHCFG_ALL }, + { "permittty", sPermitTTY, SSHCFG_ALL }, + { "permituserrc", sPermitUserRC, SSHCFG_ALL }, + { "match", sMatch, SSHCFG_ALL }, + { "permitopen", sPermitOpen, SSHCFG_ALL }, + { "permitlisten", sPermitListen, SSHCFG_ALL }, + { "forcecommand", sForceCommand, SSHCFG_ALL }, + { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, + { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, + { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, + { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, + { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, + { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, + { "include", sInclude, SSHCFG_ALL }, + { "ipqos", sIPQoS, SSHCFG_ALL }, + { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, + { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, + { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL }, + { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL }, + { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, + { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, + { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, + { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, + { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, + { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, + { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, + { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, + { "rdomain", sRDomain, SSHCFG_ALL }, + { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL }, + { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL }, + { NULL, sBadOption, 0 } +}; + +static struct { + int val; + char *text; +} tunmode_desc[] = { + { SSH_TUNMODE_NO, "no" }, + { SSH_TUNMODE_POINTOPOINT, "point-to-point" }, + { SSH_TUNMODE_ETHERNET, "ethernet" }, + { SSH_TUNMODE_YES, "yes" }, + { -1, NULL } +}; + +/* Returns an opcode name from its number */ + +static const char * +lookup_opcode_name(ServerOpCodes code) +{ + u_int i; + + for (i = 0; keywords[i].name != NULL; i++) + if (keywords[i].opcode == code) + return(keywords[i].name); + return "UNKNOWN"; +} + + +/* + * Returns the number of the token pointed to by cp or sBadOption. + */ + +static ServerOpCodes +parse_token(const char *cp, const char *filename, + int linenum, u_int *flags) +{ + u_int i; + + for (i = 0; keywords[i].name; i++) + if (strcasecmp(cp, keywords[i].name) == 0) { + *flags = keywords[i].flags; + return keywords[i].opcode; + } + + error("%s: line %d: Bad configuration option: %s", + filename, linenum, cp); + return sBadOption; +} + +char * +derelativise_path(const char *path) +{ + char *expanded, *ret, cwd[PATH_MAX]; + + if (strcasecmp(path, "none") == 0) + return xstrdup("none"); + expanded = tilde_expand_filename(path, getuid()); + if (path_absolute(expanded)) + return expanded; + if (getcwd(cwd, sizeof(cwd)) == NULL) + fatal_f("getcwd: %s", strerror(errno)); + xasprintf(&ret, "%s/%s", cwd, expanded); + free(expanded); + return ret; +} + +static void +add_listen_addr(ServerOptions *options, const char *addr, + const char *rdomain, int port) +{ + u_int i; + + if (port > 0) + add_one_listen_addr(options, addr, rdomain, port); + else { + for (i = 0; i < options->num_ports; i++) { + add_one_listen_addr(options, addr, rdomain, + options->ports[i]); + } + } +} + +static void +add_one_listen_addr(ServerOptions *options, const char *addr, + const char *rdomain, int port) +{ + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr; + u_int i; + + /* Find listen_addrs entry for this rdomain */ + for (i = 0; i < options->num_listen_addrs; i++) { + if (rdomain == NULL && options->listen_addrs[i].rdomain == NULL) + break; + if (rdomain == NULL || options->listen_addrs[i].rdomain == NULL) + continue; + if (strcmp(rdomain, options->listen_addrs[i].rdomain) == 0) + break; + } + if (i >= options->num_listen_addrs) { + /* No entry for this rdomain; allocate one */ + if (i >= INT_MAX) + fatal_f("too many listen addresses"); + options->listen_addrs = xrecallocarray(options->listen_addrs, + options->num_listen_addrs, options->num_listen_addrs + 1, + sizeof(*options->listen_addrs)); + i = options->num_listen_addrs++; + if (rdomain != NULL) + options->listen_addrs[i].rdomain = xstrdup(rdomain); + } + /* options->listen_addrs[i] points to the addresses for this rdomain */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = options->address_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) + fatal("bad addr or host: %s (%s)", + addr ? addr : "", + ssh_gai_strerror(gaierr)); + for (ai = aitop; ai->ai_next; ai = ai->ai_next) + ; + ai->ai_next = options->listen_addrs[i].addrs; + options->listen_addrs[i].addrs = aitop; +} + +/* Returns nonzero if the routing domain name is valid */ +static int +valid_rdomain(const char *name) +{ +#if defined(HAVE_SYS_VALID_RDOMAIN) + return sys_valid_rdomain(name); +#elif defined(__OpenBSD__) + const char *errstr; + long long num; + struct rt_tableinfo info; + int mib[6]; + size_t miblen = sizeof(mib); + + if (name == NULL) + return 1; + + num = strtonum(name, 0, 255, &errstr); + if (errstr != NULL) + return 0; + + /* Check whether the table actually exists */ + memset(mib, 0, sizeof(mib)); + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[4] = NET_RT_TABLE; + mib[5] = (int)num; + if (sysctl(mib, 6, &info, &miblen, NULL, 0) == -1) + return 0; + + return 1; +#else /* defined(__OpenBSD__) */ + error("Routing domains are not supported on this platform"); + return 0; +#endif +} + +/* + * Queue a ListenAddress to be processed once we have all of the Ports + * and AddressFamily options. + */ +static void +queue_listen_addr(ServerOptions *options, const char *addr, + const char *rdomain, int port) +{ + struct queued_listenaddr *qla; + + options->queued_listen_addrs = xrecallocarray( + options->queued_listen_addrs, + options->num_queued_listens, options->num_queued_listens + 1, + sizeof(*options->queued_listen_addrs)); + qla = &options->queued_listen_addrs[options->num_queued_listens++]; + qla->addr = xstrdup(addr); + qla->port = port; + qla->rdomain = rdomain == NULL ? NULL : xstrdup(rdomain); +} + +/* + * Process queued (text) ListenAddress entries. + */ +static void +process_queued_listen_addrs(ServerOptions *options) +{ + u_int i; + struct queued_listenaddr *qla; + + if (options->num_ports == 0) + options->ports[options->num_ports++] = SSH_DEFAULT_PORT; + if (options->address_family == -1) + options->address_family = AF_UNSPEC; + + for (i = 0; i < options->num_queued_listens; i++) { + qla = &options->queued_listen_addrs[i]; + add_listen_addr(options, qla->addr, qla->rdomain, qla->port); + free(qla->addr); + free(qla->rdomain); + } + free(options->queued_listen_addrs); + options->queued_listen_addrs = NULL; + options->num_queued_listens = 0; +} + +/* + * Inform channels layer of permitopen options for a single forwarding + * direction (local/remote). + */ +static void +process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode, + char **opens, u_int num_opens) +{ + u_int i; + int port; + char *host, *arg, *oarg; + int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; + const char *what = lookup_opcode_name(opcode); + + channel_clear_permission(ssh, FORWARD_ADM, where); + if (num_opens == 0) + return; /* permit any */ + + /* handle keywords: "any" / "none" */ + if (num_opens == 1 && strcmp(opens[0], "any") == 0) + return; + if (num_opens == 1 && strcmp(opens[0], "none") == 0) { + channel_disable_admin(ssh, where); + return; + } + /* Otherwise treat it as a list of permitted host:port */ + for (i = 0; i < num_opens; i++) { + oarg = arg = xstrdup(opens[i]); + host = hpdelim(&arg); + if (host == NULL) + fatal_f("missing host in %s", what); + host = cleanhostname(host); + if (arg == NULL || ((port = permitopen_port(arg)) < 0)) + fatal_f("bad port number in %s", what); + /* Send it to channels layer */ + channel_add_permission(ssh, FORWARD_ADM, + where, host, port); + free(oarg); + } +} + +/* + * Inform channels layer of permitopen options from configuration. + */ +void +process_permitopen(struct ssh *ssh, ServerOptions *options) +{ + process_permitopen_list(ssh, sPermitOpen, + options->permitted_opens, options->num_permitted_opens); + process_permitopen_list(ssh, sPermitListen, + options->permitted_listens, + options->num_permitted_listens); +} + +struct connection_info * +get_connection_info(struct ssh *ssh, int populate, int use_dns) +{ + static struct connection_info ci; + + if (ssh == NULL || !populate) + return &ci; + ci.host = auth_get_canonical_hostname(ssh, use_dns); + ci.address = ssh_remote_ipaddr(ssh); + ci.laddress = ssh_local_ipaddr(ssh); + ci.lport = ssh_local_port(ssh); + ci.rdomain = ssh_packet_rdomain_in(ssh); + return &ci; +} + +/* + * The strategy for the Match blocks is that the config file is parsed twice. + * + * The first time is at startup. activep is initialized to 1 and the + * directives in the global context are processed and acted on. Hitting a + * Match directive unsets activep and the directives inside the block are + * checked for syntax only. + * + * The second time is after a connection has been established but before + * authentication. activep is initialized to 2 and global config directives + * are ignored since they have already been processed. If the criteria in a + * Match block is met, activep is set and the subsequent directives + * processed and actioned until EOF or another Match block unsets it. Any + * options set are copied into the main server config. + * + * Potential additions/improvements: + * - Add Match support for pre-kex directives, eg. Ciphers. + * + * - Add a Tag directive (idea from David Leonard) ala pf, eg: + * Match Address 192.168.0.* + * Tag trusted + * Match Group wheel + * Tag trusted + * Match Tag trusted + * AllowTcpForwarding yes + * GatewayPorts clientspecified + * [...] + * + * - Add a PermittedChannelRequests directive + * Match Group shell + * PermittedChannelRequests session,forwarded-tcpip + */ + +static int +match_cfg_line_group(const char *grps, int line, const char *user) +{ + int result = 0; + struct passwd *pw; + + if (user == NULL) + goto out; + + if ((pw = getpwnam(user)) == NULL) { + debug("Can't match group at line %d because user %.100s does " + "not exist", line, user); + } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { + debug("Can't Match group because user %.100s not in any group " + "at line %d", user, line); + } else if (ga_match_pattern_list(grps) != 1) { + debug("user %.100s does not match group list %.100s at line %d", + user, grps, line); + } else { + debug("user %.100s matched group list %.100s at line %d", user, + grps, line); + result = 1; + } +out: + ga_free(); + return result; +} + +static void +match_test_missing_fatal(const char *criteria, const char *attrib) +{ + fatal("'Match %s' in configuration but '%s' not in connection " + "test specification.", criteria, attrib); +} + +/* + * All of the attributes on a single Match line are ANDed together, so we need + * to check every attribute and set the result to zero if any attribute does + * not match. + */ +static int +match_cfg_line(char **condition, int line, struct connection_info *ci) +{ + int result = 1, attributes = 0, port; + char *arg, *attrib, *cp = *condition; + + if (ci == NULL) + debug3("checking syntax for 'Match %s'", cp); + else + debug3("checking match for '%s' user %s host %s addr %s " + "laddr %s lport %d", cp, ci->user ? ci->user : "(null)", + ci->host ? ci->host : "(null)", + ci->address ? ci->address : "(null)", + ci->laddress ? ci->laddress : "(null)", ci->lport); + + while ((attrib = strdelim(&cp)) && *attrib != '\0') { + /* Terminate on comment */ + if (*attrib == '#') { + cp = NULL; /* mark all arguments consumed */ + break; + } + arg = NULL; + attributes++; + /* Criterion "all" has no argument and must appear alone */ + if (strcasecmp(attrib, "all") == 0) { + if (attributes > 1 || ((arg = strdelim(&cp)) != NULL && + *arg != '\0' && *arg != '#')) { + error("'all' cannot be combined with other " + "Match attributes"); + return -1; + } + if (arg != NULL && *arg == '#') + cp = NULL; /* mark all arguments consumed */ + *condition = cp; + return 1; + } + /* All other criteria require an argument */ + if ((arg = strdelim(&cp)) == NULL || + *arg == '\0' || *arg == '#') { + error("Missing Match criteria for %s", attrib); + return -1; + } + if (strcasecmp(attrib, "user") == 0) { + if (ci == NULL || (ci->test && ci->user == NULL)) { + result = 0; + continue; + } + if (ci->user == NULL) + match_test_missing_fatal("User", "user"); + if (match_usergroup_pattern_list(ci->user, arg) != 1) + result = 0; + else + debug("user %.100s matched 'User %.100s' at " + "line %d", ci->user, arg, line); + } else if (strcasecmp(attrib, "group") == 0) { + if (ci == NULL || (ci->test && ci->user == NULL)) { + result = 0; + continue; + } + if (ci->user == NULL) + match_test_missing_fatal("Group", "user"); + switch (match_cfg_line_group(arg, line, ci->user)) { + case -1: + return -1; + case 0: + result = 0; + } + } else if (strcasecmp(attrib, "host") == 0) { + if (ci == NULL || (ci->test && ci->host == NULL)) { + result = 0; + continue; + } + if (ci->host == NULL) + match_test_missing_fatal("Host", "host"); + if (match_hostname(ci->host, arg) != 1) + result = 0; + else + debug("connection from %.100s matched 'Host " + "%.100s' at line %d", ci->host, arg, line); + } else if (strcasecmp(attrib, "address") == 0) { + if (ci == NULL || (ci->test && ci->address == NULL)) { + if (addr_match_list(NULL, arg) != 0) + fatal("Invalid Match address argument " + "'%s' at line %d", arg, line); + result = 0; + continue; + } + if (ci->address == NULL) + match_test_missing_fatal("Address", "addr"); + switch (addr_match_list(ci->address, arg)) { + case 1: + debug("connection from %.100s matched 'Address " + "%.100s' at line %d", ci->address, arg, line); + break; + case 0: + case -1: + result = 0; + break; + case -2: + return -1; + } + } else if (strcasecmp(attrib, "localaddress") == 0){ + if (ci == NULL || (ci->test && ci->laddress == NULL)) { + if (addr_match_list(NULL, arg) != 0) + fatal("Invalid Match localaddress " + "argument '%s' at line %d", arg, + line); + result = 0; + continue; + } + if (ci->laddress == NULL) + match_test_missing_fatal("LocalAddress", + "laddr"); + switch (addr_match_list(ci->laddress, arg)) { + case 1: + debug("connection from %.100s matched " + "'LocalAddress %.100s' at line %d", + ci->laddress, arg, line); + break; + case 0: + case -1: + result = 0; + break; + case -2: + return -1; + } + } else if (strcasecmp(attrib, "localport") == 0) { + if ((port = a2port(arg)) == -1) { + error("Invalid LocalPort '%s' on Match line", + arg); + return -1; + } + if (ci == NULL || (ci->test && ci->lport == -1)) { + result = 0; + continue; + } + if (ci->lport == 0) + match_test_missing_fatal("LocalPort", "lport"); + /* TODO support port lists */ + if (port == ci->lport) + debug("connection from %.100s matched " + "'LocalPort %d' at line %d", + ci->laddress, port, line); + else + result = 0; + } else if (strcasecmp(attrib, "rdomain") == 0) { + if (ci == NULL || (ci->test && ci->rdomain == NULL)) { + result = 0; + continue; + } + if (ci->rdomain == NULL) + match_test_missing_fatal("RDomain", "rdomain"); + if (match_pattern_list(ci->rdomain, arg, 0) != 1) + result = 0; + else + debug("user %.100s matched 'RDomain %.100s' at " + "line %d", ci->rdomain, arg, line); + } else { + error("Unsupported Match attribute %s", attrib); + return -1; + } + } + if (attributes == 0) { + error("One or more attributes required for Match"); + return -1; + } + if (ci != NULL) + debug3("match %sfound", result ? "" : "not "); + *condition = cp; + return result; +} + +#define WHITESPACE " \t\r\n" + +/* Multistate option parsing */ +struct multistate { + char *key; + int value; +}; +static const struct multistate multistate_flag[] = { + { "yes", 1 }, + { "no", 0 }, + { NULL, -1 } +}; +static const struct multistate multistate_ignore_rhosts[] = { + { "yes", IGNORE_RHOSTS_YES }, + { "no", IGNORE_RHOSTS_NO }, + { "shosts-only", IGNORE_RHOSTS_SHOSTS }, + { NULL, -1 } +}; +static const struct multistate multistate_addressfamily[] = { + { "inet", AF_INET }, + { "inet6", AF_INET6 }, + { "any", AF_UNSPEC }, + { NULL, -1 } +}; +static const struct multistate multistate_permitrootlogin[] = { + { "without-password", PERMIT_NO_PASSWD }, + { "prohibit-password", PERMIT_NO_PASSWD }, + { "forced-commands-only", PERMIT_FORCED_ONLY }, + { "yes", PERMIT_YES }, + { "no", PERMIT_NO }, + { NULL, -1 } +}; +static const struct multistate multistate_compression[] = { +#ifdef WITH_ZLIB + { "yes", COMP_DELAYED }, + { "delayed", COMP_DELAYED }, +#endif + { "no", COMP_NONE }, + { NULL, -1 } +}; +static const struct multistate multistate_gatewayports[] = { + { "clientspecified", 2 }, + { "yes", 1 }, + { "no", 0 }, + { NULL, -1 } +}; +static const struct multistate multistate_tcpfwd[] = { + { "yes", FORWARD_ALLOW }, + { "all", FORWARD_ALLOW }, + { "no", FORWARD_DENY }, + { "remote", FORWARD_REMOTE }, + { "local", FORWARD_LOCAL }, + { NULL, -1 } +}; + +static int +process_server_config_line_depth(ServerOptions *options, char *line, + const char *filename, int linenum, int *activep, + struct connection_info *connectinfo, int *inc_flags, int depth, + struct include_list *includes) +{ + char *str, ***chararrayptr, **charptr, *arg, *arg2, *p, *keyword; + int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found; + SyslogFacility *log_facility_ptr; + LogLevel *log_level_ptr; + ServerOpCodes opcode; + u_int i, *uintptr, uvalue, flags = 0; + size_t len; + long long val64; + const struct multistate *multistate_ptr; + const char *errstr; + struct include_item *item; + glob_t gbuf; + char **oav = NULL, **av; + int oac = 0, ac; + int ret = -1; + + /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ + if ((len = strlen(line)) == 0) + return 0; + for (len--; len > 0; len--) { + if (strchr(WHITESPACE "\f", line[len]) == NULL) + break; + line[len] = '\0'; + } + + str = line; + if ((keyword = strdelim(&str)) == NULL) + return 0; + /* Ignore leading whitespace */ + if (*keyword == '\0') + keyword = strdelim(&str); + if (!keyword || !*keyword || *keyword == '#') + return 0; + if (str == NULL || *str == '\0') { + error("%s line %d: no argument after keyword \"%s\"", + filename, linenum, keyword); + return -1; + } + intptr = NULL; + charptr = NULL; + opcode = parse_token(keyword, filename, linenum, &flags); + + if (argv_split(str, &oac, &oav, 1) != 0) { + error("%s line %d: invalid quotes", filename, linenum); + return -1; + } + ac = oac; + av = oav; + + if (activep == NULL) { /* We are processing a command line directive */ + cmdline = 1; + activep = &cmdline; + } + if (*activep && opcode != sMatch && opcode != sInclude) + debug3("%s:%d setting %s %s", filename, linenum, keyword, str); + if (*activep == 0 && !(flags & SSHCFG_MATCH)) { + if (connectinfo == NULL) { + fatal("%s line %d: Directive '%s' is not allowed " + "within a Match block", filename, linenum, keyword); + } else { /* this is a directive we have already processed */ + ret = 0; + goto out; + } + } + + switch (opcode) { + /* Portable-specific options */ + case sUsePAM: + intptr = &options->use_pam; + goto parse_flag; + + /* Standard Options */ + case sBadOption: + goto out; + case sPort: + /* ignore ports from configfile if cmdline specifies ports */ + if (options->ports_from_cmdline) { + argv_consume(&ac); + break; + } + if (options->num_ports >= MAX_PORTS) + fatal("%s line %d: too many ports.", + filename, linenum); + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing port number.", + filename, linenum); + options->ports[options->num_ports++] = a2port(arg); + if (options->ports[options->num_ports-1] <= 0) + fatal("%s line %d: Badly formatted port number.", + filename, linenum); + break; + + case sLoginGraceTime: + intptr = &options->login_grace_time; + parse_time: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing time value.", + filename, linenum); + if ((value = convtime(arg)) == -1) + fatal("%s line %d: invalid time value.", + filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case sListenAddress: + arg = argv_next(&ac, &av); + if (arg == NULL || *arg == '\0') + fatal("%s line %d: missing address", + filename, linenum); + /* check for bare IPv6 address: no "[]" and 2 or more ":" */ + if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL + && strchr(p+1, ':') != NULL) { + port = 0; + p = arg; + } else { + arg2 = NULL; + p = hpdelim(&arg); + if (p == NULL) + fatal("%s line %d: bad address:port usage", + filename, linenum); + p = cleanhostname(p); + if (arg == NULL) + port = 0; + else if ((port = a2port(arg)) <= 0) + fatal("%s line %d: bad port number", + filename, linenum); + } + /* Optional routing table */ + arg2 = NULL; + if ((arg = argv_next(&ac, &av)) != NULL) { + if (strcmp(arg, "rdomain") != 0 || + (arg2 = argv_next(&ac, &av)) == NULL) + fatal("%s line %d: bad ListenAddress syntax", + filename, linenum); + if (!valid_rdomain(arg2)) + fatal("%s line %d: bad routing domain", + filename, linenum); + } + queue_listen_addr(options, p, arg2, port); + + break; + + case sAddressFamily: + intptr = &options->address_family; + multistate_ptr = multistate_addressfamily; + parse_multistate: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing argument.", + filename, linenum); + value = -1; + for (i = 0; multistate_ptr[i].key != NULL; i++) { + if (strcasecmp(arg, multistate_ptr[i].key) == 0) { + value = multistate_ptr[i].value; + break; + } + } + if (value == -1) + fatal("%s line %d: unsupported option \"%s\".", + filename, linenum, arg); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case sHostKeyFile: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*activep) { + servconf_add_hostkey(filename, linenum, + options, arg, 1); + } + break; + + case sHostKeyAgent: + charptr = &options->host_key_agent; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing socket name.", + filename, linenum); + if (*activep && *charptr == NULL) + *charptr = !strcmp(arg, SSH_AUTHSOCKET_ENV_NAME) ? + xstrdup(arg) : derelativise_path(arg); + break; + + case sHostCertificate: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*activep) + servconf_add_hostcert(filename, linenum, options, arg); + break; + + case sPidFile: + charptr = &options->pid_file; + parse_filename: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", + filename, linenum); + if (*activep && *charptr == NULL) { + *charptr = derelativise_path(arg); + /* increase optional counter */ + if (intptr != NULL) + *intptr = *intptr + 1; + } + break; + + case sModuliFile: + charptr = &options->moduli_file; + goto parse_filename; + + case sPermitRootLogin: + intptr = &options->permit_root_login; + multistate_ptr = multistate_permitrootlogin; + goto parse_multistate; + + case sIgnoreRhosts: + intptr = &options->ignore_rhosts; + multistate_ptr = multistate_ignore_rhosts; + goto parse_multistate; + + case sIgnoreUserKnownHosts: + intptr = &options->ignore_user_known_hosts; + parse_flag: + multistate_ptr = multistate_flag; + goto parse_multistate; + + case sHostbasedAuthentication: + intptr = &options->hostbased_authentication; + goto parse_flag; + + case sHostbasedUsesNameFromPacketOnly: + intptr = &options->hostbased_uses_name_from_packet_only; + goto parse_flag; + + case sHostbasedAcceptedAlgorithms: + charptr = &options->hostbased_accepted_algos; + parse_pubkey_algos: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", + filename, linenum); + if (*arg != '-' && + !sshkey_names_valid2(*arg == '+' || *arg == '^' ? + arg + 1 : arg, 1)) + fatal("%s line %d: Bad key types '%s'.", + filename, linenum, arg ? arg : ""); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case sHostKeyAlgorithms: + charptr = &options->hostkeyalgorithms; + goto parse_pubkey_algos; + + case sCASignatureAlgorithms: + charptr = &options->ca_sign_algorithms; + goto parse_pubkey_algos; + + case sPubkeyAuthentication: + intptr = &options->pubkey_authentication; + goto parse_flag; + + case sPubkeyAcceptedAlgorithms: + charptr = &options->pubkey_accepted_algos; + goto parse_pubkey_algos; + + case sPubkeyAuthOptions: + intptr = &options->pubkey_auth_options; + value = 0; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (strcasecmp(arg, "none") == 0) + continue; + if (strcasecmp(arg, "touch-required") == 0) + value |= PUBKEYAUTH_TOUCH_REQUIRED; + else if (strcasecmp(arg, "verify-required") == 0) + value |= PUBKEYAUTH_VERIFY_REQUIRED; + else { + error("%s line %d: unsupported %s option %s", + filename, linenum, keyword, arg); + goto out; + } + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + case sKerberosAuthentication: + intptr = &options->kerberos_authentication; + goto parse_flag; + + case sKerberosOrLocalPasswd: + intptr = &options->kerberos_or_local_passwd; + goto parse_flag; + + case sKerberosTicketCleanup: + intptr = &options->kerberos_ticket_cleanup; + goto parse_flag; + + case sKerberosGetAFSToken: + intptr = &options->kerberos_get_afs_token; + goto parse_flag; + + case sGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; + + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; + goto parse_flag; + + case sGssStrictAcceptor: + intptr = &options->gss_strict_acceptor; + goto parse_flag; + + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; + + case sKbdInteractiveAuthentication: + intptr = &options->kbd_interactive_authentication; + goto parse_flag; + + case sPrintMotd: + intptr = &options->print_motd; + goto parse_flag; + + case sPrintLastLog: + intptr = &options->print_lastlog; + goto parse_flag; + + case sX11Forwarding: + intptr = &options->x11_forwarding; + goto parse_flag; + + case sX11DisplayOffset: + intptr = &options->x11_display_offset; + parse_int: + arg = argv_next(&ac, &av); + if ((errstr = atoi_err(arg, &value)) != NULL) + fatal("%s line %d: %s integer value %s.", + filename, linenum, keyword, errstr); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case sX11UseLocalhost: + intptr = &options->x11_use_localhost; + goto parse_flag; + + case sXAuthLocation: + charptr = &options->xauth_location; + goto parse_filename; + + case sPermitTTY: + intptr = &options->permit_tty; + goto parse_flag; + + case sPermitUserRC: + intptr = &options->permit_user_rc; + goto parse_flag; + + case sStrictModes: + intptr = &options->strict_modes; + goto parse_flag; + + case sTCPKeepAlive: + intptr = &options->tcp_keep_alive; + goto parse_flag; + + case sEmptyPasswd: + intptr = &options->permit_empty_passwd; + goto parse_flag; + + case sPermitUserEnvironment: + intptr = &options->permit_user_env; + charptr = &options->permit_user_env_allowlist; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + value = 0; + p = NULL; + if (strcmp(arg, "yes") == 0) + value = 1; + else if (strcmp(arg, "no") == 0) + value = 0; + else { + /* Pattern-list specified */ + value = 1; + p = xstrdup(arg); + } + if (*activep && *intptr == -1) { + *intptr = value; + *charptr = p; + p = NULL; + } + free(p); + break; + + case sCompression: + intptr = &options->compression; + multistate_ptr = multistate_compression; + goto parse_multistate; + + case sRekeyLimit: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (strcmp(arg, "default") == 0) { + val64 = 0; + } else { + if (scan_scaled(arg, &val64) == -1) + fatal("%.200s line %d: Bad %s number '%s': %s", + filename, linenum, keyword, + arg, strerror(errno)); + if (val64 != 0 && val64 < 16) + fatal("%.200s line %d: %s too small", + filename, linenum, keyword); + } + if (*activep && options->rekey_limit == -1) + options->rekey_limit = val64; + if (ac != 0) { /* optional rekey interval present */ + if (strcmp(av[0], "none") == 0) { + (void)argv_next(&ac, &av); /* discard */ + break; + } + intptr = &options->rekey_interval; + goto parse_time; + } + break; + + case sGatewayPorts: + intptr = &options->fwd_opts.gateway_ports; + multistate_ptr = multistate_gatewayports; + goto parse_multistate; + + case sUseDNS: + intptr = &options->use_dns; + goto parse_flag; + + case sLogFacility: + log_facility_ptr = &options->log_facility; + arg = argv_next(&ac, &av); + value = log_facility_number(arg); + if (value == SYSLOG_FACILITY_NOT_SET) + fatal("%.200s line %d: unsupported log facility '%s'", + filename, linenum, arg ? arg : ""); + if (*log_facility_ptr == -1) + *log_facility_ptr = (SyslogFacility) value; + break; + + case sLogLevel: + log_level_ptr = &options->log_level; + arg = argv_next(&ac, &av); + value = log_level_number(arg); + if (value == SYSLOG_LEVEL_NOT_SET) + fatal("%.200s line %d: unsupported log level '%s'", + filename, linenum, arg ? arg : ""); + if (*activep && *log_level_ptr == -1) + *log_level_ptr = (LogLevel) value; + break; + + case sLogVerbose: + found = options->num_log_verbose == 0; + i = 0; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + /* Allow "none" only in first position */ + if (strcasecmp(arg, "none") == 0) { + if (i > 0 || ac > 0) { + error("%s line %d: keyword %s \"none\" " + "argument must appear alone.", + filename, linenum, keyword); + goto out; + } + } + i++; + if (!found || !*activep) + continue; + opt_array_append(filename, linenum, keyword, + &options->log_verbose, &options->num_log_verbose, + arg); + } + break; + + case sAllowTcpForwarding: + intptr = &options->allow_tcp_forwarding; + multistate_ptr = multistate_tcpfwd; + goto parse_multistate; + + case sAllowStreamLocalForwarding: + intptr = &options->allow_streamlocal_forwarding; + multistate_ptr = multistate_tcpfwd; + goto parse_multistate; + + case sAllowAgentForwarding: + intptr = &options->allow_agent_forwarding; + goto parse_flag; + + case sDisableForwarding: + intptr = &options->disable_forwarding; + goto parse_flag; + + case sAllowUsers: + chararrayptr = &options->allow_users; + uintptr = &options->num_allow_users; + parse_allowdenyusers: + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0' || + match_user(NULL, NULL, NULL, arg) == -1) + fatal("%s line %d: invalid %s pattern: \"%s\"", + filename, linenum, keyword, arg); + if (!*activep) + continue; + opt_array_append(filename, linenum, keyword, + chararrayptr, uintptr, arg); + } + break; + + case sDenyUsers: + chararrayptr = &options->deny_users; + uintptr = &options->num_deny_users; + goto parse_allowdenyusers; + + case sAllowGroups: + chararrayptr = &options->allow_groups; + uintptr = &options->num_allow_groups; + parse_allowdenygroups: + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') + fatal("%s line %d: empty %s pattern", + filename, linenum, keyword); + if (!*activep) + continue; + opt_array_append(filename, linenum, keyword, + chararrayptr, uintptr, arg); + } + break; + + case sDenyGroups: + chararrayptr = &options->deny_groups; + uintptr = &options->num_deny_groups; + goto parse_allowdenygroups; + + case sCiphers: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (*arg != '-' && + !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) + fatal("%s line %d: Bad SSH2 cipher spec '%s'.", + filename, linenum, arg ? arg : ""); + if (options->ciphers == NULL) + options->ciphers = xstrdup(arg); + break; + + case sMacs: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (*arg != '-' && + !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) + fatal("%s line %d: Bad SSH2 mac spec '%s'.", + filename, linenum, arg ? arg : ""); + if (options->macs == NULL) + options->macs = xstrdup(arg); + break; + + case sKexAlgorithms: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (*arg != '-' && + !kex_names_valid(*arg == '+' || *arg == '^' ? + arg + 1 : arg)) + fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", + filename, linenum, arg ? arg : ""); + if (options->kex_algorithms == NULL) + options->kex_algorithms = xstrdup(arg); + break; + + case sSubsystem: + if (options->num_subsystems >= MAX_SUBSYSTEMS) { + fatal("%s line %d: too many subsystems defined.", + filename, linenum); + } + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (!*activep) { + arg = argv_next(&ac, &av); + break; + } + for (i = 0; i < options->num_subsystems; i++) + if (strcmp(arg, options->subsystem_name[i]) == 0) + fatal("%s line %d: Subsystem '%s' " + "already defined.", filename, linenum, arg); + options->subsystem_name[options->num_subsystems] = xstrdup(arg); + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing subsystem command.", + filename, linenum); + options->subsystem_command[options->num_subsystems] = xstrdup(arg); + + /* Collect arguments (separate to executable) */ + p = xstrdup(arg); + len = strlen(p) + 1; + while ((arg = argv_next(&ac, &av)) != NULL) { + len += 1 + strlen(arg); + p = xreallocarray(p, 1, len); + strlcat(p, " ", len); + strlcat(p, arg, len); + } + options->subsystem_args[options->num_subsystems] = p; + options->num_subsystems++; + break; + + case sMaxStartups: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if ((n = sscanf(arg, "%d:%d:%d", + &options->max_startups_begin, + &options->max_startups_rate, + &options->max_startups)) == 3) { + if (options->max_startups_begin > + options->max_startups || + options->max_startups_rate > 100 || + options->max_startups_rate < 1) + fatal("%s line %d: Invalid %s spec.", + filename, linenum, keyword); + } else if (n != 1) + fatal("%s line %d: Invalid %s spec.", + filename, linenum, keyword); + else + options->max_startups = options->max_startups_begin; + break; + + case sPerSourceNetBlockSize: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + switch (n = sscanf(arg, "%d:%d", &value, &value2)) { + case 2: + if (value2 < 0 || value2 > 128) + n = -1; + /* FALLTHROUGH */ + case 1: + if (value < 0 || value > 32) + n = -1; + } + if (n != 1 && n != 2) + fatal("%s line %d: Invalid %s spec.", + filename, linenum, keyword); + if (*activep) { + options->per_source_masklen_ipv4 = value; + options->per_source_masklen_ipv6 = value2; + } + break; + + case sPerSourceMaxStartups: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (strcmp(arg, "none") == 0) { /* no limit */ + value = INT_MAX; + } else { + if ((errstr = atoi_err(arg, &value)) != NULL) + fatal("%s line %d: %s integer value %s.", + filename, linenum, keyword, errstr); + } + if (*activep) + options->per_source_max_startups = value; + break; + + case sMaxAuthTries: + intptr = &options->max_authtries; + goto parse_int; + + case sMaxSessions: + intptr = &options->max_sessions; + goto parse_int; + + case sBanner: + charptr = &options->banner; + goto parse_filename; + + /* + * These options can contain %X options expanded at + * connect time, so that you can specify paths like: + * + * AuthorizedKeysFile /etc/ssh_keys/%u + */ + case sAuthorizedKeysFile: + uvalue = options->num_authkeys_files; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + arg2 = tilde_expand_filename(arg, getuid()); + if (*activep && uvalue == 0) { + opt_array_append(filename, linenum, keyword, + &options->authorized_keys_files, + &options->num_authkeys_files, arg2); + } + free(arg2); + } + break; + + case sAuthorizedPrincipalsFile: + charptr = &options->authorized_principals_file; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (*activep && *charptr == NULL) { + *charptr = tilde_expand_filename(arg, getuid()); + /* increase optional counter */ + if (intptr != NULL) + *intptr = *intptr + 1; + } + break; + + case sClientAliveInterval: + intptr = &options->client_alive_interval; + goto parse_time; + + case sClientAliveCountMax: + intptr = &options->client_alive_count_max; + goto parse_int; + + case sAcceptEnv: + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0' || strchr(arg, '=') != NULL) + fatal("%s line %d: Invalid environment name.", + filename, linenum); + if (!*activep) + continue; + opt_array_append(filename, linenum, keyword, + &options->accept_env, &options->num_accept_env, + arg); + } + break; + + case sSetEnv: + uvalue = options->num_setenv; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (*arg == '\0' || strchr(arg, '=') == NULL) + fatal("%s line %d: Invalid environment.", + filename, linenum); + if (!*activep || uvalue != 0) + continue; + opt_array_append(filename, linenum, keyword, + &options->setenv, &options->num_setenv, arg); + } + break; + + case sPermitTunnel: + intptr = &options->permit_tun; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + value = -1; + for (i = 0; tunmode_desc[i].val != -1; i++) + if (strcmp(tunmode_desc[i].text, arg) == 0) { + value = tunmode_desc[i].val; + break; + } + if (value == -1) + fatal("%s line %d: bad %s argument %s", + filename, linenum, keyword, arg); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case sInclude: + if (cmdline) { + fatal("Include directive not supported as a " + "command-line option"); + } + value = 0; + while ((arg2 = argv_next(&ac, &av)) != NULL) { + if (*arg2 == '\0') { + error("%s line %d: keyword %s empty argument", + filename, linenum, keyword); + goto out; + } + value++; + found = 0; + if (*arg2 != '/' && *arg2 != '~') { + xasprintf(&arg, "%s/%s", SSHDIR, arg2); + } else + arg = xstrdup(arg2); + + /* + * Don't let included files clobber the containing + * file's Match state. + */ + oactive = *activep; + + /* consult cache of include files */ + TAILQ_FOREACH(item, includes, entry) { + if (strcmp(item->selector, arg) != 0) + continue; + if (item->filename != NULL) { + parse_server_config_depth(options, + item->filename, item->contents, + includes, connectinfo, + (*inc_flags & SSHCFG_MATCH_ONLY + ? SSHCFG_MATCH_ONLY : (oactive + ? 0 : SSHCFG_NEVERMATCH)), + activep, depth + 1); + } + found = 1; + *activep = oactive; + } + if (found != 0) { + free(arg); + continue; + } + + /* requested glob was not in cache */ + debug2("%s line %d: new include %s", + filename, linenum, arg); + if ((r = glob(arg, 0, NULL, &gbuf)) != 0) { + if (r != GLOB_NOMATCH) { + fatal("%s line %d: include \"%s\" glob " + "failed", filename, linenum, arg); + } + /* + * If no entry matched then record a + * placeholder to skip later glob calls. + */ + debug2("%s line %d: no match for %s", + filename, linenum, arg); + item = xcalloc(1, sizeof(*item)); + item->selector = strdup(arg); + TAILQ_INSERT_TAIL(includes, + item, entry); + } + if (gbuf.gl_pathc > INT_MAX) + fatal_f("too many glob results"); + for (n = 0; n < (int)gbuf.gl_pathc; n++) { + debug2("%s line %d: including %s", + filename, linenum, gbuf.gl_pathv[n]); + item = xcalloc(1, sizeof(*item)); + item->selector = strdup(arg); + item->filename = strdup(gbuf.gl_pathv[n]); + if ((item->contents = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + load_server_config(item->filename, + item->contents); + parse_server_config_depth(options, + item->filename, item->contents, + includes, connectinfo, + (*inc_flags & SSHCFG_MATCH_ONLY + ? SSHCFG_MATCH_ONLY : (oactive + ? 0 : SSHCFG_NEVERMATCH)), + activep, depth + 1); + *activep = oactive; + TAILQ_INSERT_TAIL(includes, item, entry); + } + globfree(&gbuf); + free(arg); + } + if (value == 0) { + fatal("%s line %d: %s missing filename argument", + filename, linenum, keyword); + } + break; + + case sMatch: + if (cmdline) + fatal("Match directive not supported as a command-line " + "option"); + value = match_cfg_line(&str, linenum, + (*inc_flags & SSHCFG_NEVERMATCH ? NULL : connectinfo)); + if (value < 0) + fatal("%s line %d: Bad Match condition", filename, + linenum); + *activep = (*inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; + /* + * The MATCH_ONLY flag is applicable only until the first + * match block. + */ + *inc_flags &= ~SSHCFG_MATCH_ONLY; + /* + * If match_cfg_line() didn't consume all its arguments then + * arrange for the extra arguments check below to fail. + */ + if (str == NULL || *str == '\0') + argv_consume(&ac); + break; + + case sPermitListen: + case sPermitOpen: + if (opcode == sPermitListen) { + uintptr = &options->num_permitted_listens; + chararrayptr = &options->permitted_listens; + } else { + uintptr = &options->num_permitted_opens; + chararrayptr = &options->permitted_opens; + } + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + uvalue = *uintptr; /* modified later */ + if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { + if (*activep && uvalue == 0) { + *uintptr = 1; + *chararrayptr = xcalloc(1, + sizeof(**chararrayptr)); + (*chararrayptr)[0] = xstrdup(arg); + } + break; + } + for (; arg != NULL && *arg != '\0'; arg = argv_next(&ac, &av)) { + if (opcode == sPermitListen && + strchr(arg, ':') == NULL) { + /* + * Allow bare port number for PermitListen + * to indicate a wildcard listen host. + */ + xasprintf(&arg2, "*:%s", arg); + } else { + arg2 = xstrdup(arg); + p = hpdelim(&arg); + if (p == NULL) { + fatal("%s line %d: %s missing host", + filename, linenum, keyword); + } + p = cleanhostname(p); + } + if (arg == NULL || + ((port = permitopen_port(arg)) < 0)) { + fatal("%s line %d: %s bad port number", + filename, linenum, keyword); + } + if (*activep && uvalue == 0) { + opt_array_append(filename, linenum, keyword, + chararrayptr, uintptr, arg2); + } + free(arg2); + } + break; + + case sForceCommand: + if (str == NULL || *str == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + len = strspn(str, WHITESPACE); + if (*activep && options->adm_forced_command == NULL) + options->adm_forced_command = xstrdup(str + len); + argv_consume(&ac); + break; + + case sChrootDirectory: + charptr = &options->chroot_directory; + + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case sTrustedUserCAKeys: + charptr = &options->trusted_user_ca_keys; + goto parse_filename; + + case sRevokedKeys: + charptr = &options->revoked_keys_file; + goto parse_filename; + + case sSecurityKeyProvider: + charptr = &options->sk_provider; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (*activep && *charptr == NULL) { + *charptr = strcasecmp(arg, "internal") == 0 ? + xstrdup(arg) : derelativise_path(arg); + /* increase optional counter */ + if (intptr != NULL) + *intptr = *intptr + 1; + } + break; + + case sIPQoS: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if ((value = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad %s value: %s", + filename, linenum, keyword, arg); + arg = argv_next(&ac, &av); + if (arg == NULL) + value2 = value; + else if ((value2 = parse_ipqos(arg)) == -1) + fatal("%s line %d: Bad %s value: %s", + filename, linenum, keyword, arg); + if (*activep) { + options->ip_qos_interactive = value; + options->ip_qos_bulk = value2; + } + break; + + case sVersionAddendum: + if (str == NULL || *str == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + len = strspn(str, WHITESPACE); + if (strchr(str + len, '\r') != NULL) { + fatal("%.200s line %d: Invalid %s argument", + filename, linenum, keyword); + } + if ((arg = strchr(line, '#')) != NULL) { + *arg = '\0'; + rtrim(line); + } + if (*activep && options->version_addendum == NULL) { + if (strcasecmp(str + len, "none") == 0) + options->version_addendum = xstrdup(""); + else + options->version_addendum = xstrdup(str + len); + } + argv_consume(&ac); + break; + + case sAuthorizedKeysCommand: + charptr = &options->authorized_keys_command; + parse_command: + len = strspn(str, WHITESPACE); + if (str[len] != '/' && strcasecmp(str + len, "none") != 0) { + fatal("%.200s line %d: %s must be an absolute path", + filename, linenum, keyword); + } + if (*activep && options->authorized_keys_command == NULL) + *charptr = xstrdup(str + len); + argv_consume(&ac); + break; + + case sAuthorizedKeysCommandUser: + charptr = &options->authorized_keys_command_user; + parse_localuser: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') { + fatal("%s line %d: missing %s argument.", + filename, linenum, keyword); + } + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case sAuthorizedPrincipalsCommand: + charptr = &options->authorized_principals_command; + goto parse_command; + + case sAuthorizedPrincipalsCommandUser: + charptr = &options->authorized_principals_command_user; + goto parse_localuser; + + case sAuthenticationMethods: + found = options->num_auth_methods == 0; + value = 0; /* seen "any" pseudo-method */ + value2 = 0; /* successfully parsed any method */ + while ((arg = argv_next(&ac, &av)) != NULL) { + if (strcmp(arg, "any") == 0) { + if (options->num_auth_methods > 0) { + fatal("%s line %d: \"any\" must " + "appear alone in %s", + filename, linenum, keyword); + } + value = 1; + } else if (value) { + fatal("%s line %d: \"any\" must appear " + "alone in %s", filename, linenum, keyword); + } else if (auth2_methods_valid(arg, 0) != 0) { + fatal("%s line %d: invalid %s method list.", + filename, linenum, keyword); + } + value2 = 1; + if (!found || !*activep) + continue; + opt_array_append(filename, linenum, keyword, + &options->auth_methods, + &options->num_auth_methods, arg); + } + if (value2 == 0) { + fatal("%s line %d: no %s specified", + filename, linenum, keyword); + } + break; + + case sStreamLocalBindMask: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + /* Parse mode in octal format */ + value = strtol(arg, &p, 8); + if (arg == p || value < 0 || value > 0777) + fatal("%s line %d: Invalid %s.", + filename, linenum, keyword); + if (*activep) + options->fwd_opts.streamlocal_bind_mask = (mode_t)value; + break; + + case sStreamLocalBindUnlink: + intptr = &options->fwd_opts.streamlocal_bind_unlink; + goto parse_flag; + + case sFingerprintHash: + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if ((value = ssh_digest_alg_by_name(arg)) == -1) + fatal("%.200s line %d: Invalid %s algorithm \"%s\".", + filename, linenum, keyword, arg); + if (*activep) + options->fingerprint_hash = value; + break; + + case sExposeAuthInfo: + intptr = &options->expose_userauth_info; + goto parse_flag; + + case sRDomain: +#if !defined(__OpenBSD__) && !defined(HAVE_SYS_SET_PROCESS_RDOMAIN) + fatal("%s line %d: setting RDomain not supported on this " + "platform.", filename, linenum); +#endif + charptr = &options->routing_domain; + arg = argv_next(&ac, &av); + if (!arg || *arg == '\0') + fatal("%s line %d: %s missing argument.", + filename, linenum, keyword); + if (strcasecmp(arg, "none") != 0 && strcmp(arg, "%D") != 0 && + !valid_rdomain(arg)) + fatal("%s line %d: invalid routing domain", + filename, linenum); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case sDeprecated: + case sIgnore: + case sUnsupported: + do_log2(opcode == sIgnore ? + SYSLOG_LEVEL_DEBUG2 : SYSLOG_LEVEL_INFO, + "%s line %d: %s option %s", filename, linenum, + opcode == sUnsupported ? "Unsupported" : "Deprecated", + keyword); + argv_consume(&ac); + break; + + default: + fatal("%s line %d: Missing handler for opcode %s (%d)", + filename, linenum, keyword, opcode); + } + /* Check that there is no garbage at end of line. */ + if (ac > 0) { + error("%.200s line %d: keyword %s extra arguments " + "at end of line", filename, linenum, keyword); + goto out; + } + + /* success */ + ret = 0; + out: + argv_free(oav, oac); + return ret; +} + +int +process_server_config_line(ServerOptions *options, char *line, + const char *filename, int linenum, int *activep, + struct connection_info *connectinfo, struct include_list *includes) +{ + int inc_flags = 0; + + return process_server_config_line_depth(options, line, filename, + linenum, activep, connectinfo, &inc_flags, 0, includes); +} + + +/* Reads the server configuration file. */ + +void +load_server_config(const char *filename, struct sshbuf *conf) +{ + struct stat st; + char *line = NULL, *cp; + size_t linesize = 0; + FILE *f; + int r, lineno = 0; + + debug2_f("filename %s", filename); + if ((f = fopen(filename, "r")) == NULL) { + perror(filename); + exit(1); + } + sshbuf_reset(conf); + /* grow buffer, so realloc is avoided for large config files */ + if (fstat(fileno(f), &st) == 0 && st.st_size > 0 && + (r = sshbuf_allocate(conf, st.st_size)) != 0) + fatal_fr(r, "allocate"); + while (getline(&line, &linesize, f) != -1) { + lineno++; + /* + * Strip whitespace + * NB - preserve newlines, they are needed to reproduce + * line numbers later for error messages + */ + cp = line + strspn(line, " \t\r"); + if ((r = sshbuf_put(conf, cp, strlen(cp))) != 0) + fatal_fr(r, "sshbuf_put"); + } + free(line); + if ((r = sshbuf_put_u8(conf, 0)) != 0) + fatal_fr(r, "sshbuf_put_u8"); + fclose(f); + debug2_f("done config len = %zu", sshbuf_len(conf)); +} + +void +parse_server_match_config(ServerOptions *options, + struct include_list *includes, struct connection_info *connectinfo) +{ + ServerOptions mo; +#if defined(ANDROID) + //char value[PROPERTY_VALUE_MAX]; +#endif + + initialize_server_options(&mo); + parse_server_config(&mo, "reprocess config", cfg, includes, + connectinfo, 0); +#if defined(ANDROID) + /* Allow root login if ro.debuggable is set. */ + //property_get("ro.debuggable", value, ""); + //if (strcmp(value, "1") == 0) { + mo.permit_root_login = PERMIT_YES; + //} +#endif + copy_set_server_options(options, &mo, 0); +} + +int parse_server_match_testspec(struct connection_info *ci, char *spec) +{ + char *p; + + while ((p = strsep(&spec, ",")) && *p != '\0') { + if (strncmp(p, "addr=", 5) == 0) { + ci->address = xstrdup(p + 5); + } else if (strncmp(p, "host=", 5) == 0) { + ci->host = xstrdup(p + 5); + } else if (strncmp(p, "user=", 5) == 0) { + ci->user = xstrdup(p + 5); + } else if (strncmp(p, "laddr=", 6) == 0) { + ci->laddress = xstrdup(p + 6); + } else if (strncmp(p, "rdomain=", 8) == 0) { + ci->rdomain = xstrdup(p + 8); + } else if (strncmp(p, "lport=", 6) == 0) { + ci->lport = a2port(p + 6); + if (ci->lport == -1) { + fprintf(stderr, "Invalid port '%s' in test mode" + " specification %s\n", p+6, p); + return -1; + } + } else { + fprintf(stderr, "Invalid test mode specification %s\n", + p); + return -1; + } + } + return 0; +} + +/* + * Copy any supported values that are set. + * + * If the preauth flag is set, we do not bother copying the string or + * array values that are not used pre-authentication, because any that we + * do use must be explicitly sent in mm_getpwnamallow(). + */ +void +copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) +{ +#define M_CP_INTOPT(n) do {\ + if (src->n != -1) \ + dst->n = src->n; \ +} while (0) + + M_CP_INTOPT(password_authentication); + M_CP_INTOPT(gss_authentication); + M_CP_INTOPT(pubkey_authentication); + M_CP_INTOPT(pubkey_auth_options); + M_CP_INTOPT(kerberos_authentication); + M_CP_INTOPT(hostbased_authentication); + M_CP_INTOPT(hostbased_uses_name_from_packet_only); + M_CP_INTOPT(kbd_interactive_authentication); + M_CP_INTOPT(permit_root_login); + M_CP_INTOPT(permit_empty_passwd); + M_CP_INTOPT(ignore_rhosts); + + M_CP_INTOPT(allow_tcp_forwarding); + M_CP_INTOPT(allow_streamlocal_forwarding); + M_CP_INTOPT(allow_agent_forwarding); + M_CP_INTOPT(disable_forwarding); + M_CP_INTOPT(expose_userauth_info); + M_CP_INTOPT(permit_tun); + M_CP_INTOPT(fwd_opts.gateway_ports); + M_CP_INTOPT(fwd_opts.streamlocal_bind_unlink); + M_CP_INTOPT(x11_display_offset); + M_CP_INTOPT(x11_forwarding); + M_CP_INTOPT(x11_use_localhost); + M_CP_INTOPT(permit_tty); + M_CP_INTOPT(permit_user_rc); + M_CP_INTOPT(max_sessions); + M_CP_INTOPT(max_authtries); + M_CP_INTOPT(client_alive_count_max); + M_CP_INTOPT(client_alive_interval); + M_CP_INTOPT(ip_qos_interactive); + M_CP_INTOPT(ip_qos_bulk); + M_CP_INTOPT(rekey_limit); + M_CP_INTOPT(rekey_interval); + M_CP_INTOPT(log_level); + + /* + * The bind_mask is a mode_t that may be unsigned, so we can't use + * M_CP_INTOPT - it does a signed comparison that causes compiler + * warnings. + */ + if (src->fwd_opts.streamlocal_bind_mask != (mode_t)-1) { + dst->fwd_opts.streamlocal_bind_mask = + src->fwd_opts.streamlocal_bind_mask; + } + + /* M_CP_STROPT and M_CP_STRARRAYOPT should not appear before here */ +#define M_CP_STROPT(n) do {\ + if (src->n != NULL && dst->n != src->n) { \ + free(dst->n); \ + dst->n = src->n; \ + } \ +} while(0) +#define M_CP_STRARRAYOPT(s, num_s) do {\ + u_int i; \ + if (src->num_s != 0) { \ + for (i = 0; i < dst->num_s; i++) \ + free(dst->s[i]); \ + free(dst->s); \ + dst->s = xcalloc(src->num_s, sizeof(*dst->s)); \ + for (i = 0; i < src->num_s; i++) \ + dst->s[i] = xstrdup(src->s[i]); \ + dst->num_s = src->num_s; \ + } \ +} while(0) + + /* See comment in servconf.h */ + COPY_MATCH_STRING_OPTS(); + + /* Arguments that accept '+...' need to be expanded */ + assemble_algorithms(dst); + + /* + * The only things that should be below this point are string options + * which are only used after authentication. + */ + if (preauth) + return; + + /* These options may be "none" to clear a global setting */ + M_CP_STROPT(adm_forced_command); + if (option_clear_or_none(dst->adm_forced_command)) { + free(dst->adm_forced_command); + dst->adm_forced_command = NULL; + } + M_CP_STROPT(chroot_directory); + if (option_clear_or_none(dst->chroot_directory)) { + free(dst->chroot_directory); + dst->chroot_directory = NULL; + } +} + +#undef M_CP_INTOPT +#undef M_CP_STROPT +#undef M_CP_STRARRAYOPT + +#define SERVCONF_MAX_DEPTH 16 +static void +parse_server_config_depth(ServerOptions *options, const char *filename, + struct sshbuf *conf, struct include_list *includes, + struct connection_info *connectinfo, int flags, int *activep, int depth) +{ + int linenum, bad_options = 0; + char *cp, *obuf, *cbuf; + + if (depth < 0 || depth > SERVCONF_MAX_DEPTH) + fatal("Too many recursive configuration includes"); + + debug2_f("config %s len %zu%s", filename, sshbuf_len(conf), + (flags & SSHCFG_NEVERMATCH ? " [checking syntax only]" : "")); + + if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) + fatal_f("sshbuf_dup_string failed"); + linenum = 1; + while ((cp = strsep(&cbuf, "\n")) != NULL) { + if (process_server_config_line_depth(options, cp, + filename, linenum++, activep, connectinfo, &flags, + depth, includes) != 0) + bad_options++; + } + free(obuf); + if (bad_options > 0) + fatal("%s: terminating, %d bad configuration options", + filename, bad_options); +} + +void +parse_server_config(ServerOptions *options, const char *filename, + struct sshbuf *conf, struct include_list *includes, + struct connection_info *connectinfo, int reexec) +{ + int active = connectinfo ? 0 : 1; + parse_server_config_depth(options, filename, conf, includes, + connectinfo, (connectinfo ? SSHCFG_MATCH_ONLY : 0), &active, 0); + if (!reexec) + process_queued_listen_addrs(options); +} + +static const char * +fmt_multistate_int(int val, const struct multistate *m) +{ + u_int i; + + for (i = 0; m[i].key != NULL; i++) { + if (m[i].value == val) + return m[i].key; + } + return "UNKNOWN"; +} + +static const char * +fmt_intarg(ServerOpCodes code, int val) +{ + if (val == -1) + return "unset"; + switch (code) { + case sAddressFamily: + return fmt_multistate_int(val, multistate_addressfamily); + case sPermitRootLogin: + return fmt_multistate_int(val, multistate_permitrootlogin); + case sGatewayPorts: + return fmt_multistate_int(val, multistate_gatewayports); + case sCompression: + return fmt_multistate_int(val, multistate_compression); + case sAllowTcpForwarding: + return fmt_multistate_int(val, multistate_tcpfwd); + case sAllowStreamLocalForwarding: + return fmt_multistate_int(val, multistate_tcpfwd); + case sIgnoreRhosts: + return fmt_multistate_int(val, multistate_ignore_rhosts); + case sFingerprintHash: + return ssh_digest_alg_name(val); + default: + switch (val) { + case 0: + return "no"; + case 1: + return "yes"; + default: + return "UNKNOWN"; + } + } +} + +static void +dump_cfg_int(ServerOpCodes code, int val) +{ + printf("%s %d\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_oct(ServerOpCodes code, int val) +{ + printf("%s 0%o\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_fmtint(ServerOpCodes code, int val) +{ + printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); +} + +static void +dump_cfg_string(ServerOpCodes code, const char *val) +{ + printf("%s %s\n", lookup_opcode_name(code), + val == NULL ? "none" : val); +} + +static void +dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) +{ + u_int i; + + for (i = 0; i < count; i++) + printf("%s %s\n", lookup_opcode_name(code), vals[i]); +} + +static void +dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) +{ + u_int i; + + if (count <= 0 && code != sAuthenticationMethods) + return; + printf("%s", lookup_opcode_name(code)); + for (i = 0; i < count; i++) + printf(" %s", vals[i]); + if (code == sAuthenticationMethods && count == 0) + printf(" any"); + printf("\n"); +} + +static char * +format_listen_addrs(struct listenaddr *la) +{ + int r; + struct addrinfo *ai; + char addr[NI_MAXHOST], port[NI_MAXSERV]; + char *laddr1 = xstrdup(""), *laddr2 = NULL; + + /* + * ListenAddress must be after Port. add_one_listen_addr pushes + * addresses onto a stack, so to maintain ordering we need to + * print these in reverse order. + */ + for (ai = la->addrs; ai; ai = ai->ai_next) { + if ((r = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, + sizeof(addr), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + error("getnameinfo: %.100s", ssh_gai_strerror(r)); + continue; + } + laddr2 = laddr1; + if (ai->ai_family == AF_INET6) { + xasprintf(&laddr1, "listenaddress [%s]:%s%s%s\n%s", + addr, port, + la->rdomain == NULL ? "" : " rdomain ", + la->rdomain == NULL ? "" : la->rdomain, + laddr2); + } else { + xasprintf(&laddr1, "listenaddress %s:%s%s%s\n%s", + addr, port, + la->rdomain == NULL ? "" : " rdomain ", + la->rdomain == NULL ? "" : la->rdomain, + laddr2); + } + free(laddr2); + } + return laddr1; +} + +void +dump_config(ServerOptions *o) +{ + char *s; + u_int i; + + /* these are usually at the top of the config */ + for (i = 0; i < o->num_ports; i++) + printf("port %d\n", o->ports[i]); + dump_cfg_fmtint(sAddressFamily, o->address_family); + + for (i = 0; i < o->num_listen_addrs; i++) { + s = format_listen_addrs(&o->listen_addrs[i]); + printf("%s", s); + free(s); + } + + /* integer arguments */ +#ifdef USE_PAM + dump_cfg_fmtint(sUsePAM, o->use_pam); +#endif + dump_cfg_int(sLoginGraceTime, o->login_grace_time); + dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); + dump_cfg_int(sMaxAuthTries, o->max_authtries); + dump_cfg_int(sMaxSessions, o->max_sessions); + dump_cfg_int(sClientAliveInterval, o->client_alive_interval); + dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); + dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask); + + /* formatted integer arguments */ + dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); + dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); + dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); + dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); + dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, + o->hostbased_uses_name_from_packet_only); + dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication); +#ifdef KRB5 + dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication); + dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); + dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); +# ifdef USE_AFS + dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); +# endif +#endif +#ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); +#endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, + o->kbd_interactive_authentication); + dump_cfg_fmtint(sPrintMotd, o->print_motd); +#ifndef DISABLE_LASTLOG + dump_cfg_fmtint(sPrintLastLog, o->print_lastlog); +#endif + dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding); + dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); + dump_cfg_fmtint(sPermitTTY, o->permit_tty); + dump_cfg_fmtint(sPermitUserRC, o->permit_user_rc); + dump_cfg_fmtint(sStrictModes, o->strict_modes); + dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); + dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); + dump_cfg_fmtint(sCompression, o->compression); + dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); + dump_cfg_fmtint(sUseDNS, o->use_dns); + dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); + dump_cfg_fmtint(sAllowAgentForwarding, o->allow_agent_forwarding); + dump_cfg_fmtint(sDisableForwarding, o->disable_forwarding); + dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); + dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); + dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash); + dump_cfg_fmtint(sExposeAuthInfo, o->expose_userauth_info); + + /* string arguments */ + dump_cfg_string(sPidFile, o->pid_file); + dump_cfg_string(sModuliFile, o->moduli_file); + dump_cfg_string(sXAuthLocation, o->xauth_location); + dump_cfg_string(sCiphers, o->ciphers); + dump_cfg_string(sMacs, o->macs); + dump_cfg_string(sBanner, o->banner); + dump_cfg_string(sForceCommand, o->adm_forced_command); + dump_cfg_string(sChrootDirectory, o->chroot_directory); + dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); + dump_cfg_string(sRevokedKeys, o->revoked_keys_file); + dump_cfg_string(sSecurityKeyProvider, o->sk_provider); + dump_cfg_string(sAuthorizedPrincipalsFile, + o->authorized_principals_file); + dump_cfg_string(sVersionAddendum, *o->version_addendum == '\0' + ? "none" : o->version_addendum); + dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); + dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); + dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command); + dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user); + dump_cfg_string(sHostKeyAgent, o->host_key_agent); + dump_cfg_string(sKexAlgorithms, o->kex_algorithms); + dump_cfg_string(sCASignatureAlgorithms, o->ca_sign_algorithms); + dump_cfg_string(sHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos); + dump_cfg_string(sHostKeyAlgorithms, o->hostkeyalgorithms); + dump_cfg_string(sPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos); +#if defined(__OpenBSD__) || defined(HAVE_SYS_SET_PROCESS_RDOMAIN) + dump_cfg_string(sRDomain, o->routing_domain); +#endif + + /* string arguments requiring a lookup */ + dump_cfg_string(sLogLevel, log_level_name(o->log_level)); + dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); + + /* string array arguments */ + dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files, + o->authorized_keys_files); + dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, + o->host_key_files); + dump_cfg_strarray(sHostCertificate, o->num_host_cert_files, + o->host_cert_files); + dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); + dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); + dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); + dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); + dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); + dump_cfg_strarray(sSetEnv, o->num_setenv, o->setenv); + dump_cfg_strarray_oneline(sAuthenticationMethods, + o->num_auth_methods, o->auth_methods); + dump_cfg_strarray_oneline(sLogVerbose, + o->num_log_verbose, o->log_verbose); + + /* other arguments */ + for (i = 0; i < o->num_subsystems; i++) + printf("subsystem %s %s\n", o->subsystem_name[i], + o->subsystem_args[i]); + + printf("maxstartups %d:%d:%d\n", o->max_startups_begin, + o->max_startups_rate, o->max_startups); + printf("persourcemaxstartups "); + if (o->per_source_max_startups == INT_MAX) + printf("none\n"); + else + printf("%d\n", o->per_source_max_startups); + printf("persourcenetblocksize %d:%d\n", o->per_source_masklen_ipv4, + o->per_source_masklen_ipv6); + + s = NULL; + for (i = 0; tunmode_desc[i].val != -1; i++) { + if (tunmode_desc[i].val == o->permit_tun) { + s = tunmode_desc[i].text; + break; + } + } + dump_cfg_string(sPermitTunnel, s); + + printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); + printf("%s\n", iptos2str(o->ip_qos_bulk)); + + printf("rekeylimit %llu %d\n", (unsigned long long)o->rekey_limit, + o->rekey_interval); + + printf("permitopen"); + if (o->num_permitted_opens == 0) + printf(" any"); + else { + for (i = 0; i < o->num_permitted_opens; i++) + printf(" %s", o->permitted_opens[i]); + } + printf("\n"); + printf("permitlisten"); + if (o->num_permitted_listens == 0) + printf(" any"); + else { + for (i = 0; i < o->num_permitted_listens; i++) + printf(" %s", o->permitted_listens[i]); + } + printf("\n"); + + if (o->permit_user_env_allowlist == NULL) { + dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); + } else { + printf("permituserenvironment %s\n", + o->permit_user_env_allowlist); + } + + printf("pubkeyauthoptions"); + if (o->pubkey_auth_options == 0) + printf(" none"); + if (o->pubkey_auth_options & PUBKEYAUTH_TOUCH_REQUIRED) + printf(" touch-required"); + if (o->pubkey_auth_options & PUBKEYAUTH_VERIFY_REQUIRED) + printf(" verify-required"); + printf("\n"); +} diff --git a/aosp/external/openssh/servconf.h b/aosp/external/openssh/servconf.h new file mode 100644 index 0000000000000000000000000000000000000000..8a04463e0b7954c3a2265f91436e0636f79d7f00 --- /dev/null +++ b/aosp/external/openssh/servconf.h @@ -0,0 +1,313 @@ +/* $OpenBSD: servconf.h,v 1.156 2022/03/18 04:04:11 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Definitions for server configuration data and for the functions reading it. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef SERVCONF_H +#define SERVCONF_H + +#include + +#define MAX_PORTS 256 /* Max # ports. */ + +#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ + +/* permit_root_login */ +#define PERMIT_NOT_SET -1 +#define PERMIT_NO 0 +#define PERMIT_FORCED_ONLY 1 +#define PERMIT_NO_PASSWD 2 +#define PERMIT_YES 3 + +/* use_privsep */ +#define PRIVSEP_OFF 0 +#define PRIVSEP_ON 1 +#define PRIVSEP_NOSANDBOX 2 + +/* PermitOpen */ +#define PERMITOPEN_ANY 0 +#define PERMITOPEN_NONE -2 + +/* IgnoreRhosts */ +#define IGNORE_RHOSTS_NO 0 +#define IGNORE_RHOSTS_YES 1 +#define IGNORE_RHOSTS_SHOSTS 2 + +#define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ +#define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ + +/* Magic name for internal sftp-server */ +#define INTERNAL_SFTP_NAME "internal-sftp" + +/* PubkeyAuthOptions flags */ +#define PUBKEYAUTH_TOUCH_REQUIRED (1) +#define PUBKEYAUTH_VERIFY_REQUIRED (1<<1) + +struct ssh; +struct fwd_perm_list; + +/* + * Used to store addresses from ListenAddr directives. These may be + * incomplete, as they may specify addresses that need to be merged + * with any ports requested by ListenPort. + */ +struct queued_listenaddr { + char *addr; + int port; /* <=0 if unspecified */ + char *rdomain; +}; + +/* Resolved listen addresses, grouped by optional routing domain */ +struct listenaddr { + char *rdomain; + struct addrinfo *addrs; +}; + +typedef struct { + u_int num_ports; + u_int ports_from_cmdline; + int ports[MAX_PORTS]; /* Port number to listen on. */ + struct queued_listenaddr *queued_listen_addrs; + u_int num_queued_listens; + struct listenaddr *listen_addrs; + u_int num_listen_addrs; + int address_family; /* Address family used by the server. */ + + char *routing_domain; /* Bind session to routing domain */ + + char **host_key_files; /* Files containing host keys. */ + int *host_key_file_userprovided; /* Key was specified by user. */ + u_int num_host_key_files; /* Number of files for host keys. */ + char **host_cert_files; /* Files containing host certs. */ + u_int num_host_cert_files; /* Number of files for host certs. */ + + char *host_key_agent; /* ssh-agent socket for host keys. */ + char *pid_file; /* Where to put our pid */ + char *moduli_file; /* moduli file for DH-GEX */ + int login_grace_time; /* Disconnect if no auth in this time + * (sec). */ + int permit_root_login; /* PERMIT_*, see above */ + int ignore_rhosts; /* Ignore .rhosts and .shosts. */ + int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts + * for RhostsRsaAuth */ + int print_motd; /* If true, print /etc/motd. */ + int print_lastlog; /* If true, print lastlog */ + int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ + int x11_display_offset; /* What DISPLAY number to start + * searching at */ + int x11_use_localhost; /* If true, use localhost for fake X11 server. */ + char *xauth_location; /* Location of xauth program */ + int permit_tty; /* If false, deny pty allocation */ + int permit_user_rc; /* If false, deny ~/.ssh/rc execution */ + int strict_modes; /* If true, require string home dir modes. */ + int tcp_keep_alive; /* If true, set SO_KEEPALIVE. */ + int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ + int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ + char *ciphers; /* Supported SSH2 ciphers. */ + char *macs; /* Supported SSH2 macs. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ + struct ForwardOptions fwd_opts; /* forwarding options */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for system logging. */ + u_int num_log_verbose; /* Verbose log overrides */ + char **log_verbose; + int hostbased_authentication; /* If true, permit ssh2 hostbased auth */ + int hostbased_uses_name_from_packet_only; /* experimental */ + char *hostbased_accepted_algos; /* Algos allowed for hostbased */ + char *hostkeyalgorithms; /* SSH2 server key types */ + char *ca_sign_algorithms; /* Allowed CA signature algorithms */ + int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ + char *pubkey_accepted_algos; /* Signature algos allowed for pubkey */ + int pubkey_auth_options; /* -1 or mask of PUBKEYAUTH_* flags */ + int kerberos_authentication; /* If true, permit Kerberos + * authentication. */ + int kerberos_or_local_passwd; /* If true, permit kerberos + * and any other password + * authentication mechanism, + * such as SecurID or + * /etc/passwd */ + int kerberos_ticket_cleanup; /* If true, destroy ticket + * file on logout. */ + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ + int gss_authentication; /* If true, permit GSSAPI authentication */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ + int permit_empty_passwd; /* If false, do not permit empty + * passwords. */ + int permit_user_env; /* If true, read ~/.ssh/environment */ + char *permit_user_env_allowlist; /* pattern-list of allowed env names */ + int compression; /* If true, compression is allowed */ + int allow_tcp_forwarding; /* One of FORWARD_* */ + int allow_streamlocal_forwarding; /* One of FORWARD_* */ + int allow_agent_forwarding; + int disable_forwarding; + u_int num_allow_users; + char **allow_users; + u_int num_deny_users; + char **deny_users; + u_int num_allow_groups; + char **allow_groups; + u_int num_deny_groups; + char **deny_groups; + + u_int num_subsystems; + char *subsystem_name[MAX_SUBSYSTEMS]; + char *subsystem_command[MAX_SUBSYSTEMS]; + char *subsystem_args[MAX_SUBSYSTEMS]; + + u_int num_accept_env; + char **accept_env; + u_int num_setenv; + char **setenv; + + int max_startups_begin; + int max_startups_rate; + int max_startups; + int per_source_max_startups; + int per_source_masklen_ipv4; + int per_source_masklen_ipv6; + int max_authtries; + int max_sessions; + char *banner; /* SSH-2 banner message */ + int use_dns; + int client_alive_interval; /* + * poke the client this often to + * see if it's still there + */ + int client_alive_count_max; /* + * If the client is unresponsive + * for this many intervals above, + * disconnect the session + */ + + u_int num_authkeys_files; /* Files containing public keys */ + char **authorized_keys_files; + + char *adm_forced_command; + + int use_pam; /* Enable auth via PAM */ + + int permit_tun; + + char **permitted_opens; /* May also be one of PERMITOPEN_* */ + u_int num_permitted_opens; + char **permitted_listens; /* May also be one of PERMITOPEN_* */ + u_int num_permitted_listens; + + char *chroot_directory; + char *revoked_keys_file; + char *trusted_user_ca_keys; + char *authorized_keys_command; + char *authorized_keys_command_user; + char *authorized_principals_file; + char *authorized_principals_command; + char *authorized_principals_command_user; + + int64_t rekey_limit; + int rekey_interval; + + char *version_addendum; /* Appended to SSH banner */ + + u_int num_auth_methods; + char **auth_methods; + + int fingerprint_hash; + int expose_userauth_info; + u_int64_t timing_secret; + char *sk_provider; +} ServerOptions; + +/* Information about the incoming connection as used by Match */ +struct connection_info { + const char *user; + const char *host; /* possibly resolved hostname */ + const char *address; /* remote address */ + const char *laddress; /* local address */ + int lport; /* local port */ + const char *rdomain; /* routing domain if available */ + int test; /* test mode, allow some attributes to be + * unspecified */ +}; + +/* List of included files for re-exec from the parsed configuration */ +struct include_item { + char *selector; + char *filename; + struct sshbuf *contents; + TAILQ_ENTRY(include_item) entry; +}; +TAILQ_HEAD(include_list, include_item); + + +/* + * These are string config options that must be copied between the + * Match sub-config and the main config, and must be sent from the + * privsep child to the privsep master. We use a macro to ensure all + * the options are copied and the copies are done in the correct order. + * + * NB. an option must appear in servconf.c:copy_set_server_options() or + * COPY_MATCH_STRING_OPTS here but never both. + */ +#define COPY_MATCH_STRING_OPTS() do { \ + M_CP_STROPT(banner); \ + M_CP_STROPT(trusted_user_ca_keys); \ + M_CP_STROPT(revoked_keys_file); \ + M_CP_STROPT(authorized_keys_command); \ + M_CP_STROPT(authorized_keys_command_user); \ + M_CP_STROPT(authorized_principals_file); \ + M_CP_STROPT(authorized_principals_command); \ + M_CP_STROPT(authorized_principals_command_user); \ + M_CP_STROPT(hostbased_accepted_algos); \ + M_CP_STROPT(pubkey_accepted_algos); \ + M_CP_STROPT(ca_sign_algorithms); \ + M_CP_STROPT(routing_domain); \ + M_CP_STROPT(permit_user_env_allowlist); \ + M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ + M_CP_STRARRAYOPT(allow_users, num_allow_users); \ + M_CP_STRARRAYOPT(deny_users, num_deny_users); \ + M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \ + M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ + M_CP_STRARRAYOPT(accept_env, num_accept_env); \ + M_CP_STRARRAYOPT(setenv, num_setenv); \ + M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ + M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \ + M_CP_STRARRAYOPT(permitted_listens, num_permitted_listens); \ + M_CP_STRARRAYOPT(log_verbose, num_log_verbose); \ + } while (0) + +struct connection_info *get_connection_info(struct ssh *, int, int); +void initialize_server_options(ServerOptions *); +void fill_default_server_options(ServerOptions *); +int process_server_config_line(ServerOptions *, char *, const char *, int, + int *, struct connection_info *, struct include_list *includes); +void process_permitopen(struct ssh *ssh, ServerOptions *options); +void load_server_config(const char *, struct sshbuf *); +void parse_server_config(ServerOptions *, const char *, struct sshbuf *, + struct include_list *includes, struct connection_info *, int); +void parse_server_match_config(ServerOptions *, + struct include_list *includes, struct connection_info *); +int parse_server_match_testspec(struct connection_info *, char *); +int server_match_spec_complete(struct connection_info *); +void copy_set_server_options(ServerOptions *, ServerOptions *, int); +void dump_config(ServerOptions *); +char *derelativise_path(const char *); +void servconf_add_hostkey(const char *, const int, + ServerOptions *, const char *path, int); +void servconf_add_hostcert(const char *, const int, + ServerOptions *, const char *path); + +#endif /* SERVCONF_H */ diff --git a/aosp/external/openssh/serverloop.c b/aosp/external/openssh/serverloop.c new file mode 100644 index 0000000000000000000000000000000000000000..0541f028a650e5986e11d39cc686d6eb570b2823 --- /dev/null +++ b/aosp/external/openssh/serverloop.c @@ -0,0 +1,936 @@ +/* $OpenBSD: serverloop.c,v 1.231 2022/01/22 00:49:34 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Server main loop for handling the interactive session. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 support by Markus Friedl. + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include + +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "packet.h" +#include "sshbuf.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "canohost.h" +#include "sshpty.h" +#include "channels.h" +#include "compat.h" +#include "ssh2.h" +#include "sshkey.h" +#include "cipher.h" +#include "kex.h" +#include "hostfile.h" +#include "auth.h" +#include "session.h" +#include "dispatch.h" +#include "auth-options.h" +#include "serverloop.h" +#include "ssherr.h" + +extern ServerOptions options; + +/* XXX */ +extern Authctxt *the_authctxt; +extern struct sshauthopt *auth_opts; +extern int use_privsep; + +static int no_more_sessions = 0; /* Disallow further sessions. */ + +static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ + +/* Cleanup on signals (!use_privsep case only) */ +static volatile sig_atomic_t received_sigterm = 0; + +/* prototypes */ +static void server_init_dispatch(struct ssh *); + +/* requested tunnel forwarding interface(s), shared with session.c */ +char *tun_fwd_ifnames = NULL; + +/* returns 1 if bind to specified port by specified user is permitted */ +static int +bind_permitted(int port, uid_t uid) +{ + if (use_privsep) + return 1; /* allow system to decide */ + if (port < IPPORT_RESERVED && uid != 0) + return 0; + return 1; +} + +/*ARGSUSED*/ +static void +sigchld_handler(int sig) +{ + child_terminated = 1; +} + +/*ARGSUSED*/ +static void +sigterm_handler(int sig) +{ + received_sigterm = sig; +} + +static void +client_alive_check(struct ssh *ssh) +{ + char remote_id[512]; + int r, channel_id; + + /* timeout, check to see how many we have had */ + if (options.client_alive_count_max > 0 && + ssh_packet_inc_alive_timeouts(ssh) > + options.client_alive_count_max) { + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + logit("Timeout, client not responding from %s", remote_id); + cleanup_exit(255); + } + + /* + * send a bogus global/channel request with "wantreply", + * we should get back a failure + */ + if ((channel_id = channel_find_open(ssh)) == -1) { + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) + != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0) /* boolean: want reply */ + fatal_fr(r, "compose"); + } else { + channel_request_start(ssh, channel_id, + "keepalive@openssh.com", 1); + } + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send"); +} + +/* + * Sleep in ppoll() until we can do something. + * Optionally, a maximum time can be specified for the duration of + * the wait (0 = infinite). + */ +static void +wait_until_can_do_something(struct ssh *ssh, + int connection_in, int connection_out, struct pollfd **pfdp, + u_int *npfd_allocp, u_int *npfd_activep, u_int64_t max_time_ms, + sigset_t *sigsetp, int *conn_in_readyp, int *conn_out_readyp) +{ + struct timespec ts, *tsp; + int ret; + time_t minwait_secs = 0; + int client_alive_scheduled = 0; + u_int p; + /* time we last heard from the client OR sent a keepalive */ + static time_t last_client_time; + + *conn_in_readyp = *conn_out_readyp = 0; + + /* Prepare channel poll. First two pollfd entries are reserved */ + channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, + 2, &minwait_secs); + if (*npfd_activep < 2) + fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ + + /* XXX need proper deadline system for rekey/client alive */ + if (minwait_secs != 0) + max_time_ms = MINIMUM(max_time_ms, (u_int)minwait_secs * 1000); + + /* + * if using client_alive, set the max timeout accordingly, + * and indicate that this particular timeout was for client + * alive by setting the client_alive_scheduled flag. + * + * this could be randomized somewhat to make traffic + * analysis more difficult, but we're not doing it yet. + */ + if (options.client_alive_interval) { + uint64_t keepalive_ms = + (uint64_t)options.client_alive_interval * 1000; + + if (max_time_ms == 0 || max_time_ms > keepalive_ms) { + max_time_ms = keepalive_ms; + client_alive_scheduled = 1; + } + if (last_client_time == 0) + last_client_time = monotime(); + } + +#if 0 + /* wrong: bad condition XXX */ + if (channel_not_very_much_buffered_data()) +#endif + /* Monitor client connection on reserved pollfd entries */ + (*pfdp)[0].fd = connection_in; + (*pfdp)[0].events = POLLIN; + (*pfdp)[1].fd = connection_out; + (*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; + + /* + * If child has terminated and there is enough buffer space to read + * from it, then read as much as is available and exit. + */ + if (child_terminated && ssh_packet_not_very_much_data_to_write(ssh)) + if (max_time_ms == 0 || client_alive_scheduled) + max_time_ms = 100; + + if (max_time_ms == 0) + tsp = NULL; + else { + ts.tv_sec = max_time_ms / 1000; + ts.tv_nsec = 1000000 * (max_time_ms % 1000); + tsp = &ts; + } + + /* Wait for something to happen, or the timeout to expire. */ + ret = ppoll(*pfdp, *npfd_activep, tsp, sigsetp); + + if (ret == -1) { + for (p = 0; p < *npfd_activep; p++) + (*pfdp)[p].revents = 0; + if (errno != EINTR) + fatal_f("ppoll: %.100s", strerror(errno)); + return; + } + + *conn_in_readyp = (*pfdp)[0].revents != 0; + *conn_out_readyp = (*pfdp)[1].revents != 0; + + if (client_alive_scheduled) { + time_t now = monotime(); + + /* + * If the ppoll timed out, or returned for some other reason + * but we haven't heard from the client in time, send keepalive. + */ + if (ret == 0 || (last_client_time != 0 && last_client_time + + options.client_alive_interval <= now)) { + client_alive_check(ssh); + last_client_time = now; + } else if (*conn_in_readyp) + last_client_time = now; + } +} + +/* + * Processes input from the client and the program. Input data is stored + * in buffers and processed later. + */ +static int +process_input(struct ssh *ssh, int connection_in) +{ + int r; + + if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) + return 0; /* success */ + if (r == SSH_ERR_SYSTEM_ERROR) { + if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) + return 0; + if (errno == EPIPE) { + verbose("Connection closed by %.100s port %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + return -1; + } + verbose("Read error from remote host %s port %d: %s", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + strerror(errno)); + cleanup_exit(255); + } + return -1; +} + +/* + * Sends data from internal buffers to client program stdin. + */ +static void +process_output(struct ssh *ssh, int connection_out) +{ + int r; + + /* Send any buffered packet data to the client. */ + if ((r = ssh_packet_write_poll(ssh)) != 0) { + sshpkt_fatal(ssh, r, "%s: ssh_packet_write_poll", + __func__); + } +} + +static void +process_buffered_input_packets(struct ssh *ssh) +{ + ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, NULL); +} + +static void +collect_children(struct ssh *ssh) +{ + pid_t pid; + int status; + + if (child_terminated) { + debug("Received SIGCHLD."); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || + (pid == -1 && errno == EINTR)) + if (pid > 0) + session_close_by_pid(ssh, pid, status); + child_terminated = 0; + } +} + +void +server_loop2(struct ssh *ssh, Authctxt *authctxt) +{ + struct pollfd *pfd = NULL; + u_int npfd_alloc = 0, npfd_active = 0; + int r, conn_in_ready, conn_out_ready; + u_int connection_in, connection_out; + u_int64_t rekey_timeout_ms = 0; + sigset_t bsigset, osigset; + + debug("Entering interactive session for SSH2."); + + if (sigemptyset(&bsigset) == -1 || sigaddset(&bsigset, SIGCHLD) == -1) + error_f("bsigset setup: %s", strerror(errno)); + ssh_signal(SIGCHLD, sigchld_handler); + child_terminated = 0; + connection_in = ssh_packet_get_connection_in(ssh); + connection_out = ssh_packet_get_connection_out(ssh); + + if (!use_privsep) { + ssh_signal(SIGTERM, sigterm_handler); + ssh_signal(SIGINT, sigterm_handler); + ssh_signal(SIGQUIT, sigterm_handler); + } + + server_init_dispatch(ssh); + + for (;;) { + process_buffered_input_packets(ssh); + + if (!ssh_packet_is_rekeying(ssh) && + ssh_packet_not_very_much_data_to_write(ssh)) + channel_output_poll(ssh); + if (options.rekey_interval > 0 && + !ssh_packet_is_rekeying(ssh)) { + rekey_timeout_ms = ssh_packet_get_rekey_timeout(ssh) * + 1000; + } else { + rekey_timeout_ms = 0; + } + + /* + * Block SIGCHLD while we check for dead children, then pass + * the old signal mask through to ppoll() so that it'll wake + * up immediately if a child exits after we've called waitpid(). + */ + if (sigprocmask(SIG_BLOCK, &bsigset, &osigset) == -1) + error_f("bsigset sigprocmask: %s", strerror(errno)); + collect_children(ssh); + wait_until_can_do_something(ssh, connection_in, connection_out, + &pfd, &npfd_alloc, &npfd_active, rekey_timeout_ms, &osigset, + &conn_in_ready, &conn_out_ready); + if (sigprocmask(SIG_UNBLOCK, &bsigset, &osigset) == -1) + error_f("osigset sigprocmask: %s", strerror(errno)); + + if (received_sigterm) { + logit("Exiting on signal %d", (int)received_sigterm); + /* Clean up sessions, utmp, etc. */ + cleanup_exit(255); + } + + if (!ssh_packet_is_rekeying(ssh)) + channel_after_poll(ssh, pfd, npfd_active); + if (conn_in_ready && + process_input(ssh, connection_in) < 0) + break; + /* A timeout may have triggered rekeying */ + if ((r = ssh_packet_check_rekey(ssh)) != 0) + fatal_fr(r, "cannot start rekeying"); + if (conn_out_ready) + process_output(ssh, connection_out); + } + collect_children(ssh); + free(pfd); + + /* free all channels, no more reads and writes */ + channel_free_all(ssh); + + /* free remaining sessions, e.g. remove wtmp entries */ + session_destroy_all(ssh, NULL); +} + +static int +server_input_keep_alive(int type, u_int32_t seq, struct ssh *ssh) +{ + debug("Got %d/%u for keepalive", type, seq); + /* + * reset timeout, since we got a sane answer from the client. + * even if this was generated by something other than + * the bogus CHANNEL_REQUEST we send for keepalives. + */ + ssh_packet_set_alive_timeouts(ssh, 0); + return 0; +} + +static Channel * +server_request_direct_tcpip(struct ssh *ssh, int *reason, const char **errmsg) +{ + Channel *c = NULL; + char *target = NULL, *originator = NULL; + u_int target_port = 0, originator_port = 0; + int r; + + if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &target_port)) != 0 || + (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + if (target_port > 0xFFFF) { + error_f("invalid target port"); + *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; + goto out; + } + if (originator_port > 0xFFFF) { + error_f("invalid originator port"); + *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; + goto out; + } + + debug_f("originator %s port %u, target %s port %u", + originator, originator_port, target, target_port); + + /* XXX fine grained permissions */ + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && + auth_opts->permit_port_forwarding_flag && + !options.disable_forwarding) { + c = channel_connect_to_port(ssh, target, target_port, + "direct-tcpip", "direct-tcpip", reason, errmsg); + } else { + logit("refused local port forward: " + "originator %s port %d, target %s port %d", + originator, originator_port, target, target_port); + if (reason != NULL) + *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; + } + + out: + free(originator); + free(target); + return c; +} + +static Channel * +server_request_direct_streamlocal(struct ssh *ssh) +{ + Channel *c = NULL; + char *target = NULL, *originator = NULL; + u_int originator_port = 0; + struct passwd *pw = the_authctxt->pw; + int r; + + if (pw == NULL || !the_authctxt->valid) + fatal_f("no/invalid user"); + + if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + if (originator_port > 0xFFFF) { + error_f("invalid originator port"); + goto out; + } + + debug_f("originator %s port %d, target %s", + originator, originator_port, target); + + /* XXX fine grained permissions */ + if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && + auth_opts->permit_port_forwarding_flag && + !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) { + c = channel_connect_to_path(ssh, target, + "direct-streamlocal@openssh.com", "direct-streamlocal"); + } else { + logit("refused streamlocal port forward: " + "originator %s port %d, target %s", + originator, originator_port, target); + } + +out: + free(originator); + free(target); + return c; +} + +static Channel * +server_request_tun(struct ssh *ssh) +{ + Channel *c = NULL; + u_int mode, tun; + int r, sock; + char *tmp, *ifname = NULL; + + if ((r = sshpkt_get_u32(ssh, &mode)) != 0) + sshpkt_fatal(ssh, r, "%s: parse mode", __func__); + switch (mode) { + case SSH_TUNMODE_POINTOPOINT: + case SSH_TUNMODE_ETHERNET: + break; + default: + ssh_packet_send_debug(ssh, "Unsupported tunnel device mode."); + return NULL; + } + if ((options.permit_tun & mode) == 0) { + ssh_packet_send_debug(ssh, "Server has rejected tunnel device " + "forwarding"); + return NULL; + } + + if ((r = sshpkt_get_u32(ssh, &tun)) != 0) + sshpkt_fatal(ssh, r, "%s: parse device", __func__); + if (tun > INT_MAX) { + debug_f("invalid tun"); + goto done; + } + if (auth_opts->force_tun_device != -1) { + if (tun != SSH_TUNID_ANY && + auth_opts->force_tun_device != (int)tun) + goto done; + tun = auth_opts->force_tun_device; + } + sock = tun_open(tun, mode, &ifname); + if (sock < 0) + goto done; + debug("Tunnel forwarding using interface %s", ifname); + + c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + c->datagram = 1; +#if defined(SSH_TUN_FILTER) + if (mode == SSH_TUNMODE_POINTOPOINT) + channel_register_filter(ssh, c->self, sys_tun_infilter, + sys_tun_outfilter, NULL, NULL); +#endif + + /* + * Update the list of names exposed to the session + * XXX remove these if the tunnels are closed (won't matter + * much if they are already in the environment though) + */ + tmp = tun_fwd_ifnames; + xasprintf(&tun_fwd_ifnames, "%s%s%s", + tun_fwd_ifnames == NULL ? "" : tun_fwd_ifnames, + tun_fwd_ifnames == NULL ? "" : ",", + ifname); + free(tmp); + free(ifname); + + done: + if (c == NULL) + ssh_packet_send_debug(ssh, "Failed to open the tunnel device."); + return c; +} + +static Channel * +server_request_session(struct ssh *ssh) +{ + Channel *c; + int r; + + debug("input_session_request"); + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + if (no_more_sessions) { + ssh_packet_disconnect(ssh, "Possible attack: attempt to open a " + "session after additional sessions disabled"); + } + + /* + * A server session has no fd to read or write until a + * CHANNEL_REQUEST for a shell is made, so we set the type to + * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all + * CHANNEL_REQUEST messages is registered. + */ + c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL, + -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, + 0, "server-session", 1); + if (session_open(the_authctxt, c->self) != 1) { + debug("session open failed, free channel %d", c->self); + channel_free(ssh, c); + return NULL; + } + channel_register_cleanup(ssh, c->self, session_close_by_channel, 0); + return c; +} + +static int +server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c = NULL; + char *ctype = NULL; + const char *errmsg = NULL; + int r, reason = SSH2_OPEN_CONNECT_FAILED; + u_int rchan = 0, rmaxpack = 0, rwindow = 0; + + if ((r = sshpkt_get_cstring(ssh, &ctype, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &rchan)) != 0 || + (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || + (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + debug_f("ctype %s rchan %u win %u max %u", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "session") == 0) { + c = server_request_session(ssh); + } else if (strcmp(ctype, "direct-tcpip") == 0) { + c = server_request_direct_tcpip(ssh, &reason, &errmsg); + } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { + c = server_request_direct_streamlocal(ssh); + } else if (strcmp(ctype, "tun@openssh.com") == 0) { + c = server_request_tun(ssh); + } + if (c != NULL) { + debug_f("confirm %s", ctype); + c->remote_id = rchan; + c->have_remote_id = 1; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + if (c->type != SSH_CHANNEL_CONNECTING) { + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + sshpkt_fatal(ssh, r, + "%s: send open confirm", __func__); + } + } + } else { + debug_f("failure %s", ctype); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, rchan)) != 0 || + (r = sshpkt_put_u32(ssh, reason)) != 0 || + (r = sshpkt_put_cstring(ssh, errmsg ? errmsg : "open failed")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) { + sshpkt_fatal(ssh, r, + "%s: send open failure", __func__); + } + } + free(ctype); + return 0; +} + +static int +server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp) +{ + struct sshbuf *resp = NULL; + struct sshbuf *sigbuf = NULL; + struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; + int r, ndx, success = 0; + const u_char *blob; + const char *sigalg, *kex_rsa_sigalg = NULL; + u_char *sig = 0; + size_t blen, slen; + + if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if (sshkey_type_plain(sshkey_type_from_name( + ssh->kex->hostkey_alg)) == KEY_RSA) + kex_rsa_sigalg = ssh->kex->hostkey_alg; + while (ssh_packet_remaining(ssh) > 0) { + sshkey_free(key); + key = NULL; + if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 || + (r = sshkey_from_blob(blob, blen, &key)) != 0) { + error_fr(r, "parse key"); + goto out; + } + /* + * Better check that this is actually one of our hostkeys + * before attempting to sign anything with it. + */ + if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) { + error_f("unknown host %s key", sshkey_type(key)); + goto out; + } + /* + * XXX refactor: make kex->sign just use an index rather + * than passing in public and private keys + */ + if ((key_prv = get_hostkey_by_index(ndx)) == NULL && + (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) { + error_f("can't retrieve hostkey %d", ndx); + goto out; + } + sshbuf_reset(sigbuf); + free(sig); + sig = NULL; + /* + * For RSA keys, prefer to use the signature type negotiated + * during KEX to the default (SHA1). + */ + sigalg = NULL; + if (sshkey_type_plain(key->type) == KEY_RSA) { + if (kex_rsa_sigalg != NULL) + sigalg = kex_rsa_sigalg; + else if (ssh->kex->flags & KEX_RSA_SHA2_512_SUPPORTED) + sigalg = "rsa-sha2-512"; + else if (ssh->kex->flags & KEX_RSA_SHA2_256_SUPPORTED) + sigalg = "rsa-sha2-256"; + } + debug3_f("sign %s key (index %d) using sigalg %s", + sshkey_type(key), ndx, sigalg == NULL ? "default" : sigalg); + if ((r = sshbuf_put_cstring(sigbuf, + "hostkeys-prove-00@openssh.com")) != 0 || + (r = sshbuf_put_stringb(sigbuf, + ssh->kex->session_id)) != 0 || + (r = sshkey_puts(key, sigbuf)) != 0 || + (r = ssh->kex->sign(ssh, key_prv, key_pub, &sig, &slen, + sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), sigalg)) != 0 || + (r = sshbuf_put_string(resp, sig, slen)) != 0) { + error_fr(r, "assemble signature"); + goto out; + } + } + /* Success */ + *respp = resp; + resp = NULL; /* don't free it */ + success = 1; + out: + free(sig); + sshbuf_free(resp); + sshbuf_free(sigbuf); + sshkey_free(key); + return success; +} + +static int +server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) +{ + char *rtype = NULL; + u_char want_reply = 0; + int r, success = 0, allocated_listen_port = 0; + u_int port = 0; + struct sshbuf *resp = NULL; + struct passwd *pw = the_authctxt->pw; + struct Forward fwd; + + memset(&fwd, 0, sizeof(fwd)); + if (pw == NULL || !the_authctxt->valid) + fatal_f("no/invalid user"); + + if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &want_reply)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + debug_f("rtype %s want_reply %d", rtype, want_reply); + + /* -R style forwarding */ + if (strcmp(rtype, "tcpip-forward") == 0) { + if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &port)) != 0) + sshpkt_fatal(ssh, r, "%s: parse tcpip-forward", __func__); + debug_f("tcpip-forward listen %s port %u", + fwd.listen_host, port); + if (port <= INT_MAX) + fwd.listen_port = (int)port; + /* check permissions */ + if (port > INT_MAX || + (options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || + !auth_opts->permit_port_forwarding_flag || + options.disable_forwarding || + (!want_reply && fwd.listen_port == 0) || + (fwd.listen_port != 0 && + !bind_permitted(fwd.listen_port, pw->pw_uid))) { + success = 0; + ssh_packet_send_debug(ssh, "Server has disabled port forwarding."); + } else { + /* Start listening on the port */ + success = channel_setup_remote_fwd_listener(ssh, &fwd, + &allocated_listen_port, &options.fwd_opts); + } + if ((resp = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if (allocated_listen_port != 0 && + (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) + fatal_fr(r, "sshbuf_put_u32"); + } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { + if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &port)) != 0) + sshpkt_fatal(ssh, r, "%s: parse cancel-tcpip-forward", __func__); + + debug_f("cancel-tcpip-forward addr %s port %d", + fwd.listen_host, port); + if (port <= INT_MAX) { + fwd.listen_port = (int)port; + success = channel_cancel_rport_listener(ssh, &fwd); + } + } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { + if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0) + sshpkt_fatal(ssh, r, "%s: parse streamlocal-forward@openssh.com", __func__); + debug_f("streamlocal-forward listen path %s", + fwd.listen_path); + + /* check permissions */ + if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 + || !auth_opts->permit_port_forwarding_flag || + options.disable_forwarding || + (pw->pw_uid != 0 && !use_privsep)) { + success = 0; + ssh_packet_send_debug(ssh, "Server has disabled " + "streamlocal forwarding."); + } else { + /* Start listening on the socket */ + success = channel_setup_remote_fwd_listener(ssh, + &fwd, NULL, &options.fwd_opts); + } + } else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) { + if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0) + sshpkt_fatal(ssh, r, "%s: parse cancel-streamlocal-forward@openssh.com", __func__); + debug_f("cancel-streamlocal-forward path %s", + fwd.listen_path); + + success = channel_cancel_rport_listener(ssh, &fwd); + } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { + no_more_sessions = 1; + success = 1; + } else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) { + success = server_input_hostkeys_prove(ssh, &resp); + } + /* XXX sshpkt_get_end() */ + if (want_reply) { + if ((r = sshpkt_start(ssh, success ? + SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE)) != 0 || + (success && resp != NULL && (r = sshpkt_putb(ssh, resp)) != 0) || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send reply", __func__); + } + free(fwd.listen_host); + free(fwd.listen_path); + free(rtype); + sshbuf_free(resp); + return 0; +} + +static int +server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) +{ + Channel *c; + int r, success = 0; + char *rtype = NULL; + u_char want_reply = 0; + u_int id = 0; + + if ((r = sshpkt_get_u32(ssh, &id)) != 0 || + (r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &want_reply)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + debug("server_input_channel_req: channel %u request %s reply %d", + id, rtype, want_reply); + + if (id >= INT_MAX || (c = channel_lookup(ssh, (int)id)) == NULL) { + ssh_packet_disconnect(ssh, "%s: unknown channel %d", + __func__, id); + } + if (!strcmp(rtype, "eow@openssh.com")) { + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + chan_rcvd_eow(ssh, c); + } else if ((c->type == SSH_CHANNEL_LARVAL || + c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) + success = session_input_channel_req(ssh, c, rtype); + if (want_reply && !(c->flags & CHAN_CLOSE_SENT)) { + if (!c->have_remote_id) + fatal_f("channel %d: no remote_id", c->self); + if ((r = sshpkt_start(ssh, success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send reply", __func__); + } + free(rtype); + return 0; +} + +static void +server_init_dispatch(struct ssh *ssh) +{ + debug("server_init_dispatch"); + ssh_dispatch_init(ssh, &dispatch_protocol_error); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); + ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); + /* client_alive */ + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive); + ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive); + ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive); + /* rekeying */ + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); +} diff --git a/aosp/external/openssh/serverloop.h b/aosp/external/openssh/serverloop.h new file mode 100644 index 0000000000000000000000000000000000000000..fd2cf63f74e7321461220556c4e09354d5593bf3 --- /dev/null +++ b/aosp/external/openssh/serverloop.h @@ -0,0 +1,28 @@ +/* $OpenBSD: serverloop.h,v 1.8 2017/09/12 06:32:07 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Performs the interactive session. This handles data transmission between + * the client and the program. Note that the notion of stdin, stdout, and + * stderr in this function is sort of reversed: this function writes to stdin + * (of the child program), and reads from stdout and stderr (of the child + * program). + */ +#ifndef SERVERLOOP_H +#define SERVERLOOP_H + +struct ssh; + +void server_loop2(struct ssh *, Authctxt *); + +#endif diff --git a/aosp/external/openssh/session.c b/aosp/external/openssh/session.c new file mode 100644 index 0000000000000000000000000000000000000000..7a2814f33c08ab4d7329b413e2adfb4aad0fb527 --- /dev/null +++ b/aosp/external/openssh/session.c @@ -0,0 +1,2716 @@ +/* $OpenBSD: session.c,v 1.330 2022/02/08 08:59:12 dtucker Exp $ */ +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 support by Markus Friedl. + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "sshpty.h" +#include "packet.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "match.h" +#include "uidswap.h" +#include "compat.h" +#include "channels.h" +#include "sshkey.h" +#include "cipher.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "hostfile.h" +#include "auth.h" +#include "auth-options.h" +#include "authfd.h" +#include "pathnames.h" +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "sshlogin.h" +#include "serverloop.h" +#include "canohost.h" +#include "session.h" +#include "kex.h" +#include "monitor_wrap.h" +#include "sftp.h" +#include "atomicio.h" + +#if defined(KRB5) && defined(USE_AFS) +#include +#endif + +#ifdef WITH_SELINUX +#include +#endif + +#define IS_INTERNAL_SFTP(c) \ + (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \ + (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \ + c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \ + c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t')) + +/* func */ + +Session *session_new(void); +void session_set_fds(struct ssh *, Session *, int, int, int, int, int); +void session_pty_cleanup(Session *); +void session_proctitle(Session *); +int session_setup_x11fwd(struct ssh *, Session *); +int do_exec_pty(struct ssh *, Session *, const char *); +int do_exec_no_pty(struct ssh *, Session *, const char *); +int do_exec(struct ssh *, Session *, const char *); +void do_login(struct ssh *, Session *, const char *); +void do_child(struct ssh *, Session *, const char *); +void do_motd(void); +int check_quietlogin(Session *, const char *); + +static void do_authenticated2(struct ssh *, Authctxt *); + +static int session_pty_req(struct ssh *, Session *); + +/* import */ +extern ServerOptions options; +extern char *__progname; +extern int debug_flag; +extern u_int utmp_len; +extern int startup_pipe; +extern void destroy_sensitive_data(void); +extern struct sshbuf *loginmsg; +extern struct sshauthopt *auth_opts; +extern char *tun_fwd_ifnames; /* serverloop.c */ + +/* original command from peer. */ +const char *original_command = NULL; + +/* data */ +static int sessions_first_unused = -1; +static int sessions_nalloc = 0; +static Session *sessions = NULL; + +#define SUBSYSTEM_NONE 0 +#define SUBSYSTEM_EXT 1 +#define SUBSYSTEM_INT_SFTP 2 +#define SUBSYSTEM_INT_SFTP_ERROR 3 + +#ifdef HAVE_LOGIN_CAP +login_cap_t *lc; +#endif + +static int is_child = 0; +static int in_chroot = 0; + +/* File containing userauth info, if ExposeAuthInfo set */ +static char *auth_info_file = NULL; + +/* Name and directory of socket for authentication agent forwarding. */ +static char *auth_sock_name = NULL; +static char *auth_sock_dir = NULL; + +/* removes the agent forwarding socket */ + +static void +auth_sock_cleanup_proc(struct passwd *pw) +{ + if (auth_sock_name != NULL) { + temporarily_use_uid(pw); + unlink(auth_sock_name); + rmdir(auth_sock_dir); + auth_sock_name = NULL; + restore_uid(); + } +} + +static int +auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw) +{ + Channel *nc; + int sock = -1; + + if (auth_sock_name != NULL) { + error("authentication forwarding requested twice."); + return 0; + } + + /* Temporarily drop privileged uid for mkdir/bind. */ + temporarily_use_uid(pw); + + /* Allocate a buffer for the socket name, and format the name. */ + auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); + + /* Create private directory for socket */ + if (mkdtemp(auth_sock_dir) == NULL) { + ssh_packet_send_debug(ssh, "Agent forwarding disabled: " + "mkdtemp() failed: %.100s", strerror(errno)); + restore_uid(); + free(auth_sock_dir); + auth_sock_dir = NULL; + goto authsock_err; + } + + xasprintf(&auth_sock_name, "%s/agent.%ld", + auth_sock_dir, (long) getpid()); + + /* Start a Unix listener on auth_sock_name. */ + sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0); + + /* Restore the privileged uid. */ + restore_uid(); + + /* Check for socket/bind/listen failure. */ + if (sock < 0) + goto authsock_err; + + /* Allocate a channel for the authentication agent socket. */ + nc = channel_new(ssh, "auth socket", + SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, + 0, "auth socket", 1); + nc->path = xstrdup(auth_sock_name); + return 1; + + authsock_err: + free(auth_sock_name); + if (auth_sock_dir != NULL) { + temporarily_use_uid(pw); + rmdir(auth_sock_dir); + restore_uid(); + free(auth_sock_dir); + } + if (sock != -1) + close(sock); + auth_sock_name = NULL; + auth_sock_dir = NULL; + return 0; +} + +static void +display_loginmsg(void) +{ + int r; + + if (sshbuf_len(loginmsg) == 0) + return; + if ((r = sshbuf_put_u8(loginmsg, 0)) != 0) + fatal_fr(r, "sshbuf_put_u8"); + printf("%s", (char *)sshbuf_ptr(loginmsg)); + sshbuf_reset(loginmsg); +} + +static void +prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) +{ + int fd = -1, success = 0; + + if (!options.expose_userauth_info || info == NULL) + return; + + temporarily_use_uid(pw); + auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX"); + if ((fd = mkstemp(auth_info_file)) == -1) { + error_f("mkstemp: %s", strerror(errno)); + goto out; + } + if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info), + sshbuf_len(info)) != sshbuf_len(info)) { + error_f("write: %s", strerror(errno)); + goto out; + } + if (close(fd) != 0) { + error_f("close: %s", strerror(errno)); + goto out; + } + success = 1; + out: + if (!success) { + if (fd != -1) + close(fd); + free(auth_info_file); + auth_info_file = NULL; + } + restore_uid(); +} + +static void +set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) +{ + char *tmp, *cp, *host; + int port; + size_t i; + + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) { + channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL); + for (i = 0; i < auth_opts->npermitopen; i++) { + tmp = cp = xstrdup(auth_opts->permitopen[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim2(&cp, NULL)) == NULL) + fatal_f("internal error: hpdelim"); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal_f("internal error: permitopen port"); + channel_add_permission(ssh, + FORWARD_USER, FORWARD_LOCAL, host, port); + free(tmp); + } + } + if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) { + channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE); + for (i = 0; i < auth_opts->npermitlisten; i++) { + tmp = cp = xstrdup(auth_opts->permitlisten[i]); + /* This shouldn't fail as it has already been checked */ + if ((host = hpdelim(&cp)) == NULL) + fatal_f("internal error: hpdelim"); + host = cleanhostname(host); + if (cp == NULL || (port = permitopen_port(cp)) < 0) + fatal_f("internal error: permitlisten port"); + channel_add_permission(ssh, + FORWARD_USER, FORWARD_REMOTE, host, port); + free(tmp); + } + } +} + +void +do_authenticated(struct ssh *ssh, Authctxt *authctxt) +{ + setproctitle("%s", authctxt->pw->pw_name); + + auth_log_authopts("active", auth_opts, 0); + + /* setup the channel layer */ + /* XXX - streamlocal? */ + set_fwdpermit_from_authopts(ssh, auth_opts); + + if (!auth_opts->permit_port_forwarding_flag || + options.disable_forwarding) { + channel_disable_admin(ssh, FORWARD_LOCAL); + channel_disable_admin(ssh, FORWARD_REMOTE); + } else { + if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) + channel_disable_admin(ssh, FORWARD_LOCAL); + else + channel_permit_all(ssh, FORWARD_LOCAL); + if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0) + channel_disable_admin(ssh, FORWARD_REMOTE); + else + channel_permit_all(ssh, FORWARD_REMOTE); + } + auth_debug_send(ssh); + + prepare_auth_info_file(authctxt->pw, authctxt->session_info); + + do_authenticated2(ssh, authctxt); + + do_cleanup(ssh, authctxt); +} + +/* Check untrusted xauth strings for metacharacters */ +static int +xauth_valid_string(const char *s) +{ + size_t i; + + for (i = 0; s[i] != '\0'; i++) { + if (!isalnum((u_char)s[i]) && + s[i] != '.' && s[i] != ':' && s[i] != '/' && + s[i] != '-' && s[i] != '_') + return 0; + } + return 1; +} + +#define USE_PIPES 1 +/* + * This is called to fork and execute a command when we have no tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors and such. + */ +int +do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) +{ + pid_t pid; +#ifdef USE_PIPES + int pin[2], pout[2], perr[2]; + + if (s == NULL) + fatal("do_exec_no_pty: no session"); + + /* Allocate pipes for communicating with the program. */ + if (pipe(pin) == -1) { + error_f("pipe in: %.100s", strerror(errno)); + return -1; + } + if (pipe(pout) == -1) { + error_f("pipe out: %.100s", strerror(errno)); + close(pin[0]); + close(pin[1]); + return -1; + } + if (pipe(perr) == -1) { + error_f("pipe err: %.100s", strerror(errno)); + close(pin[0]); + close(pin[1]); + close(pout[0]); + close(pout[1]); + return -1; + } +#else + int inout[2], err[2]; + + if (s == NULL) + fatal("do_exec_no_pty: no session"); + + /* Uses socket pairs to communicate with the program. */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) { + error_f("socketpair #1: %.100s", strerror(errno)); + return -1; + } + if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) { + error_f("socketpair #2: %.100s", strerror(errno)); + close(inout[0]); + close(inout[1]); + return -1; + } +#endif + + session_proctitle(s); + + /* Fork the child. */ + switch ((pid = fork())) { + case -1: + error_f("fork: %.100s", strerror(errno)); +#ifdef USE_PIPES + close(pin[0]); + close(pin[1]); + close(pout[0]); + close(pout[1]); + close(perr[0]); + close(perr[1]); +#else + close(inout[0]); + close(inout[1]); + close(err[0]); + close(err[1]); +#endif + return -1; + case 0: + is_child = 1; + + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. + */ + if (setsid() == -1) + error("setsid failed: %.100s", strerror(errno)); + +#ifdef USE_PIPES + /* + * Redirect stdin. We close the parent side of the socket + * pair, and make the child side the standard input. + */ + close(pin[1]); + if (dup2(pin[0], 0) == -1) + perror("dup2 stdin"); + close(pin[0]); + + /* Redirect stdout. */ + close(pout[0]); + if (dup2(pout[1], 1) == -1) + perror("dup2 stdout"); + close(pout[1]); + + /* Redirect stderr. */ + close(perr[0]); + if (dup2(perr[1], 2) == -1) + perror("dup2 stderr"); + close(perr[1]); +#else + /* + * Redirect stdin, stdout, and stderr. Stdin and stdout will + * use the same socket, as some programs (particularly rdist) + * seem to depend on it. + */ + close(inout[1]); + close(err[1]); + if (dup2(inout[0], 0) == -1) /* stdin */ + perror("dup2 stdin"); + if (dup2(inout[0], 1) == -1) /* stdout (same as stdin) */ + perror("dup2 stdout"); + close(inout[0]); + if (dup2(err[0], 2) == -1) /* stderr */ + perror("dup2 stderr"); + close(err[0]); +#endif + + /* Do processing for the child (exec command etc). */ + do_child(ssh, s, command); + /* NOTREACHED */ + default: + break; + } + +#ifdef HAVE_CYGWIN + cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); +#endif + + s->pid = pid; + /* Set interactive/non-interactive mode. */ + ssh_packet_set_interactive(ssh, s->display != NULL, + options.ip_qos_interactive, options.ip_qos_bulk); + + /* + * Clear loginmsg, since it's the child's responsibility to display + * it to the user, otherwise multiple sessions may accumulate + * multiple copies of the login messages. + */ + sshbuf_reset(loginmsg); + +#ifdef USE_PIPES + /* We are the parent. Close the child sides of the pipes. */ + close(pin[0]); + close(pout[1]); + close(perr[1]); + + session_set_fds(ssh, s, pin[1], pout[0], perr[0], + s->is_subsystem, 0); +#else + /* We are the parent. Close the child sides of the socket pairs. */ + close(inout[0]); + close(err[0]); + + /* + * Enter the interactive session. Note: server_loop must be able to + * handle the case that fdin and fdout are the same. + */ + session_set_fds(ssh, s, inout[1], inout[1], err[1], + s->is_subsystem, 0); +#endif + return 0; +} + +/* + * This is called to fork and execute a command when we have a tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors, controlling tty, updating wtmp, utmp, + * lastlog, and other such operations. + */ +int +do_exec_pty(struct ssh *ssh, Session *s, const char *command) +{ + int fdout, ptyfd, ttyfd, ptymaster; + pid_t pid; + + if (s == NULL) + fatal("do_exec_pty: no session"); + ptyfd = s->ptyfd; + ttyfd = s->ttyfd; + + /* + * Create another descriptor of the pty master side for use as the + * standard input. We could use the original descriptor, but this + * simplifies code in server_loop. The descriptor is bidirectional. + * Do this before forking (and cleanup in the child) so as to + * detect and gracefully fail out-of-fd conditions. + */ + if ((fdout = dup(ptyfd)) == -1) { + error_f("dup #1: %s", strerror(errno)); + close(ttyfd); + close(ptyfd); + return -1; + } + /* we keep a reference to the pty master */ + if ((ptymaster = dup(ptyfd)) == -1) { + error_f("dup #2: %s", strerror(errno)); + close(ttyfd); + close(ptyfd); + close(fdout); + return -1; + } + + /* Fork the child. */ + switch ((pid = fork())) { + case -1: + error_f("fork: %.100s", strerror(errno)); + close(fdout); + close(ptymaster); + close(ttyfd); + close(ptyfd); + return -1; + case 0: + is_child = 1; + + close(fdout); + close(ptymaster); + + /* Close the master side of the pseudo tty. */ + close(ptyfd); + + /* Make the pseudo tty our controlling tty. */ + pty_make_controlling_tty(&ttyfd, s->tty); + + /* Redirect stdin/stdout/stderr from the pseudo tty. */ + if (dup2(ttyfd, 0) == -1) + error("dup2 stdin: %s", strerror(errno)); + if (dup2(ttyfd, 1) == -1) + error("dup2 stdout: %s", strerror(errno)); + if (dup2(ttyfd, 2) == -1) + error("dup2 stderr: %s", strerror(errno)); + + /* Close the extra descriptor for the pseudo tty. */ + close(ttyfd); + + /* record login, etc. similar to login(1) */ +#ifndef HAVE_OSF_SIA + do_login(ssh, s, command); +#endif + /* + * Do common processing for the child, such as execing + * the command. + */ + do_child(ssh, s, command); + /* NOTREACHED */ + default: + break; + } + +#ifdef HAVE_CYGWIN + cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); +#endif + + s->pid = pid; + + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); + + /* Enter interactive session. */ + s->ptymaster = ptymaster; + ssh_packet_set_interactive(ssh, 1, + options.ip_qos_interactive, options.ip_qos_bulk); + session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1); + return 0; +} + +/* + * This is called to fork and execute a command. If another command is + * to be forced, execute that instead. + */ +int +do_exec(struct ssh *ssh, Session *s, const char *command) +{ + int ret; + const char *forced = NULL, *tty = NULL; + char session_type[1024]; + + if (options.adm_forced_command) { + original_command = command; + command = options.adm_forced_command; + forced = "(config)"; + } else if (auth_opts->force_command != NULL) { + original_command = command; + command = auth_opts->force_command; + forced = "(key-option)"; + } + s->forced = 0; + if (forced != NULL) { + s->forced = 1; + if (IS_INTERNAL_SFTP(command)) { + s->is_subsystem = s->is_subsystem ? + SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; + } else if (s->is_subsystem) + s->is_subsystem = SUBSYSTEM_EXT; + snprintf(session_type, sizeof(session_type), + "forced-command %s '%.900s'", forced, command); + } else if (s->is_subsystem) { + snprintf(session_type, sizeof(session_type), + "subsystem '%.900s'", s->subsys); + } else if (command == NULL) { + snprintf(session_type, sizeof(session_type), "shell"); + } else { + /* NB. we don't log unforced commands to preserve privacy */ + snprintf(session_type, sizeof(session_type), "command"); + } + + if (s->ttyfd != -1) { + tty = s->tty; + if (strncmp(tty, "/dev/", 5) == 0) + tty += 5; + } + + verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", + session_type, + tty == NULL ? "" : " on ", + tty == NULL ? "" : tty, + s->pw->pw_name, + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), + s->self); + +#ifdef SSH_AUDIT_EVENTS + if (command != NULL) + PRIVSEP(audit_run_command(command)); + else if (s->ttyfd == -1) { + char *shell = s->pw->pw_shell; + + if (shell[0] == '\0') /* empty shell means /bin/sh */ + shell =_PATH_BSHELL; + PRIVSEP(audit_run_command(shell)); + } +#endif + if (s->ttyfd != -1) + ret = do_exec_pty(ssh, s, command); + else + ret = do_exec_no_pty(ssh, s, command); + + original_command = NULL; + + /* + * Clear loginmsg: it's the child's responsibility to display + * it to the user, otherwise multiple sessions may accumulate + * multiple copies of the login messages. + */ + sshbuf_reset(loginmsg); + + return ret; +} + +/* administrative, login(1)-like work */ +void +do_login(struct ssh *ssh, Session *s, const char *command) +{ + socklen_t fromlen; + struct sockaddr_storage from; + struct passwd * pw = s->pw; + pid_t pid = getpid(); + + /* + * Get IP address of client. If the connection is not a socket, let + * the address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + fromlen = sizeof(from); + if (ssh_packet_connection_is_on_socket(ssh)) { + if (getpeername(ssh_packet_get_connection_in(ssh), + (struct sockaddr *)&from, &fromlen) == -1) { + debug("getpeername: %.100s", strerror(errno)); + cleanup_exit(255); + } + } + + /* Record that there was a login on that tty from the remote host. */ + if (!use_privsep) + record_login(pid, s->tty, pw->pw_name, pw->pw_uid, + session_get_remote_name_or_ip(ssh, utmp_len, + options.use_dns), + (struct sockaddr *)&from, fromlen); + +#ifdef USE_PAM + /* + * If password change is needed, do it now. + * This needs to occur before the ~/.hushlogin check. + */ + if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) { + display_loginmsg(); + do_pam_chauthtok(); + s->authctxt->force_pwchange = 0; + /* XXX - signal [net] parent to enable forwardings */ + } +#endif + + if (check_quietlogin(s, command)) + return; + + display_loginmsg(); + + do_motd(); +} + +/* + * Display the message of the day. + */ +void +do_motd(void) +{ + FILE *f; + char buf[256]; + + if (options.print_motd) { +#ifdef HAVE_LOGIN_CAP + f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", + "/etc/motd"), "r"); +#else + f = fopen("/etc/motd", "r"); +#endif + if (f) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stdout); + fclose(f); + } + } +} + + +/* + * Check for quiet login, either .hushlogin or command given. + */ +int +check_quietlogin(Session *s, const char *command) +{ + char buf[256]; + struct passwd *pw = s->pw; + struct stat st; + + /* Return 1 if .hushlogin exists or a command given. */ + if (command != NULL) + return 1; + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) + return 1; +#else + if (stat(buf, &st) >= 0) + return 1; +#endif + return 0; +} + +/* + * Reads environment variables from the given file and adds/overrides them + * into the environment. If the file does not exist, this does nothing. + * Otherwise, it must consist of empty lines, comments (line starts with '#') + * and assignments of the form name=value. No other forms are allowed. + * If allowlist is not NULL, then it is interpreted as a pattern list and + * only variable names that match it will be accepted. + */ +static void +read_environment_file(char ***env, u_int *envsize, + const char *filename, const char *allowlist) +{ + FILE *f; + char *line = NULL, *cp, *value; + size_t linesize = 0; + u_int lineno = 0; + + f = fopen(filename, "r"); + if (!f) + return; + + while (getline(&line, &linesize, f) != -1) { + if (++lineno > 1000) + fatal("Too many lines in environment file %s", filename); + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') + continue; + + cp[strcspn(cp, "\n")] = '\0'; + + value = strchr(cp, '='); + if (value == NULL) { + fprintf(stderr, "Bad line %u in %.100s\n", lineno, + filename); + continue; + } + /* + * Replace the equals sign by nul, and advance value to + * the value string. + */ + *value = '\0'; + value++; + if (allowlist != NULL && + match_pattern_list(cp, allowlist, 0) != 1) + continue; + child_set_env(env, envsize, cp, value); + } + free(line); + fclose(f); +} + +#ifdef HAVE_ETC_DEFAULT_LOGIN +/* + * Return named variable from specified environment, or NULL if not present. + */ +static char * +child_get_env(char **env, const char *name) +{ + int i; + size_t len; + + len = strlen(name); + for (i=0; env[i] != NULL; i++) + if (strncmp(name, env[i], len) == 0 && env[i][len] == '=') + return(env[i] + len + 1); + return NULL; +} + +/* + * Read /etc/default/login. + * We pick up the PATH (or SUPATH for root) and UMASK. + */ +static void +read_etc_default_login(char ***env, u_int *envsize, uid_t uid) +{ + char **tmpenv = NULL, *var; + u_int i, tmpenvsize = 0; + u_long mask; + + /* + * We don't want to copy the whole file to the child's environment, + * so we use a temporary environment and copy the variables we're + * interested in. + */ + read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login", + options.permit_user_env_allowlist); + + if (tmpenv == NULL) + return; + + if (uid == 0) + var = child_get_env(tmpenv, "SUPATH"); + else + var = child_get_env(tmpenv, "PATH"); + if (var != NULL) + child_set_env(env, envsize, "PATH", var); + + if ((var = child_get_env(tmpenv, "UMASK")) != NULL) + if (sscanf(var, "%5lo", &mask) == 1) + umask((mode_t)mask); + + for (i = 0; tmpenv[i] != NULL; i++) + free(tmpenv[i]); + free(tmpenv); +} +#endif /* HAVE_ETC_DEFAULT_LOGIN */ + +#if defined(USE_PAM) || defined(HAVE_CYGWIN) +static void +copy_environment_denylist(char **source, char ***env, u_int *envsize, + const char *denylist) +{ + char *var_name, *var_val; + int i; + + if (source == NULL) + return; + + for(i = 0; source[i] != NULL; i++) { + var_name = xstrdup(source[i]); + if ((var_val = strstr(var_name, "=")) == NULL) { + free(var_name); + continue; + } + *var_val++ = '\0'; + + if (denylist == NULL || + match_pattern_list(var_name, denylist, 0) != 1) { + debug3("Copy environment: %s=%s", var_name, var_val); + child_set_env(env, envsize, var_name, var_val); + } + + free(var_name); + } +} +#endif /* defined(USE_PAM) || defined(HAVE_CYGWIN) */ + +#ifdef HAVE_CYGWIN +static void +copy_environment(char **source, char ***env, u_int *envsize) +{ + copy_environment_denylist(source, env, envsize, NULL); +} +#endif + +static char ** +do_setup_env(struct ssh *ssh, Session *s, const char *shell) +{ + char buf[256]; + size_t n; + u_int i, envsize; + char *ocp, *cp, *value, **env, *laddr; + struct passwd *pw = s->pw; +#if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) + char *path = NULL; +#endif + + /* Initialize the environment. */ + envsize = 100; + env = xcalloc(envsize, sizeof(char *)); + env[0] = NULL; + +#ifdef HAVE_CYGWIN + /* + * The Windows environment contains some setting which are + * important for a running system. They must not be dropped. + */ + { + char **p; + + p = fetch_windows_environment(); + copy_environment(p, &env, &envsize); + free_windows_environment(p); + } +#endif + +#ifdef GSSAPI + /* Allow any GSSAPI methods that we've used to alter + * the child's environment as they see fit + */ + ssh_gssapi_do_child(&env, &envsize); +#endif + + /* Set basic environment. */ + for (i = 0; i < s->num_env; i++) + child_set_env(&env, &envsize, s->env[i].name, s->env[i].val); + + child_set_env(&env, &envsize, "USER", pw->pw_name); + child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); +#ifdef _AIX + child_set_env(&env, &envsize, "LOGIN", pw->pw_name); +#endif + child_set_env(&env, &envsize, "HOME", pw->pw_dir); +#ifdef HAVE_LOGIN_CAP + if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) + child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); + else + child_set_env(&env, &envsize, "PATH", getenv("PATH")); +#else /* HAVE_LOGIN_CAP */ +# ifndef HAVE_CYGWIN + /* + * There's no standard path on Windows. The path contains + * important components pointing to the system directories, + * needed for loading shared libraries. So the path better + * remains intact here. + */ +# ifdef HAVE_ETC_DEFAULT_LOGIN + read_etc_default_login(&env, &envsize, pw->pw_uid); + path = child_get_env(env, "PATH"); +# endif /* HAVE_ETC_DEFAULT_LOGIN */ + if (path == NULL || *path == '\0') { + child_set_env(&env, &envsize, "PATH", + s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); + } +# endif /* HAVE_CYGWIN */ +#endif /* HAVE_LOGIN_CAP */ + +#if !defined(ANDROID) + if (!options.use_pam) { + snprintf(buf, sizeof buf, "%.200s/%.50s", + _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); + } +#endif + + /* Normal systems set SHELL by default. */ + child_set_env(&env, &envsize, "SHELL", shell); + + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); + if (s->term) + child_set_env(&env, &envsize, "TERM", s->term); + if (s->display) + child_set_env(&env, &envsize, "DISPLAY", s->display); + + /* + * Since we clear KRB5CCNAME at startup, if it's set now then it + * must have been set by a native authentication method (eg AIX or + * SIA), so copy it to the child. + */ + { + char *cp; + + if ((cp = getenv("KRB5CCNAME")) != NULL) + child_set_env(&env, &envsize, "KRB5CCNAME", cp); + } + +#ifdef _AIX + { + char *cp; + + if ((cp = getenv("AUTHSTATE")) != NULL) + child_set_env(&env, &envsize, "AUTHSTATE", cp); + read_environment_file(&env, &envsize, "/etc/environment", + options.permit_user_env_allowlist); + } +#endif +#ifdef KRB5 + if (s->authctxt->krb5_ccname) + child_set_env(&env, &envsize, "KRB5CCNAME", + s->authctxt->krb5_ccname); +#endif + if (auth_sock_name != NULL) + child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, + auth_sock_name); + + + /* Set custom environment options from pubkey authentication. */ + if (options.permit_user_env) { + for (n = 0 ; n < auth_opts->nenv; n++) { + ocp = xstrdup(auth_opts->env[n]); + cp = strchr(ocp, '='); + if (cp != NULL) { + *cp = '\0'; + /* Apply PermitUserEnvironment allowlist */ + if (options.permit_user_env_allowlist == NULL || + match_pattern_list(ocp, + options.permit_user_env_allowlist, 0) == 1) + child_set_env(&env, &envsize, + ocp, cp + 1); + } + free(ocp); + } + } + + /* read $HOME/.ssh/environment. */ + if (options.permit_user_env) { + snprintf(buf, sizeof buf, "%.200s/%s/environment", + pw->pw_dir, _PATH_SSH_USER_DIR); + read_environment_file(&env, &envsize, buf, + options.permit_user_env_allowlist); + } + +#ifdef USE_PAM + /* + * Pull in any environment variables that may have + * been set by PAM. + */ + if (options.use_pam) { + char **p; + + /* + * Don't allow PAM-internal env vars to leak + * back into the session environment. + */ +#define PAM_ENV_DENYLIST "SSH_AUTH_INFO*,SSH_CONNECTION*" + p = fetch_pam_child_environment(); + copy_environment_denylist(p, &env, &envsize, + PAM_ENV_DENYLIST); + free_pam_environment(p); + + p = fetch_pam_environment(); + copy_environment_denylist(p, &env, &envsize, + PAM_ENV_DENYLIST); + free_pam_environment(p); + } +#endif /* USE_PAM */ + + /* Environment specified by admin */ + for (i = 0; i < options.num_setenv; i++) { + cp = xstrdup(options.setenv[i]); + if ((value = strchr(cp, '=')) == NULL) { + /* shouldn't happen; vars are checked in servconf.c */ + fatal("Invalid config SetEnv: %s", options.setenv[i]); + } + *value++ = '\0'; + child_set_env(&env, &envsize, cp, value); + } + + /* SSH_CLIENT deprecated */ + snprintf(buf, sizeof buf, "%.50s %d %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + ssh_local_port(ssh)); + child_set_env(&env, &envsize, "SSH_CLIENT", buf); + + laddr = get_local_ipaddr(ssh_packet_get_connection_in(ssh)); + snprintf(buf, sizeof buf, "%.50s %d %.50s %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + laddr, ssh_local_port(ssh)); + free(laddr); + child_set_env(&env, &envsize, "SSH_CONNECTION", buf); + + if (tun_fwd_ifnames != NULL) + child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames); + if (auth_info_file != NULL) + child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file); + if (s->ttyfd != -1) + child_set_env(&env, &envsize, "SSH_TTY", s->tty); + if (original_command) + child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", + original_command); + + if (debug_flag) { + /* dump the environment */ + fprintf(stderr, "Environment:\n"); + for (i = 0; env[i]; i++) + fprintf(stderr, " %.200s\n", env[i]); + } + return env; +} + +/* + * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found + * first in this order). + */ +static void +do_rc_files(struct ssh *ssh, Session *s, const char *shell) +{ + FILE *f = NULL; + char *cmd = NULL, *user_rc = NULL; + int do_xauth; + struct stat st; + + do_xauth = + s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; + xasprintf(&user_rc, "%s/%s", s->pw->pw_dir, _PATH_SSH_USER_RC); + + /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ + if (!s->is_subsystem && options.adm_forced_command == NULL && + auth_opts->permit_user_rc && options.permit_user_rc && + stat(user_rc, &st) >= 0) { + if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, + user_rc) == -1) + fatal_f("xasprintf: %s", strerror(errno)); + if (debug_flag) + fprintf(stderr, "Running %s\n", cmd); + f = popen(cmd, "w"); + if (f) { + if (do_xauth) + fprintf(f, "%s %s\n", s->auth_proto, + s->auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", + user_rc); + } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, + _PATH_SSH_SYSTEM_RC); + f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); + if (f) { + if (do_xauth) + fprintf(f, "%s %s\n", s->auth_proto, + s->auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", + _PATH_SSH_SYSTEM_RC); + } else if (do_xauth && options.xauth_location != NULL) { + /* Add authority data to .Xauthority if appropriate. */ + if (debug_flag) { + fprintf(stderr, + "Running %.500s remove %.100s\n", + options.xauth_location, s->auth_display); + fprintf(stderr, + "%.500s add %.100s %.100s %.100s\n", + options.xauth_location, s->auth_display, + s->auth_proto, s->auth_data); + } + if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1) + fatal_f("xasprintf: %s", strerror(errno)); + f = popen(cmd, "w"); + if (f) { + fprintf(f, "remove %s\n", + s->auth_display); + fprintf(f, "add %s %s %s\n", + s->auth_display, s->auth_proto, + s->auth_data); + pclose(f); + } else { + fprintf(stderr, "Could not run %s\n", + cmd); + } + } + free(cmd); + free(user_rc); +} + +static void +do_nologin(struct passwd *pw) +{ + FILE *f = NULL; + char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; + struct stat sb; + +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0) + return; + nl = login_getcapstr(lc, "nologin", def_nl, def_nl); +#else + if (pw->pw_uid == 0) + return; + nl = def_nl; +#endif + if (stat(nl, &sb) == -1) + return; + + /* /etc/nologin exists. Print its contents if we can and exit. */ + logit("User %.100s not allowed because %s exists", pw->pw_name, nl); + if ((f = fopen(nl, "r")) != NULL) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + } + exit(254); +} + +/* + * Chroot into a directory after checking it for safety: all path components + * must be root-owned directories with strict permissions. + */ +static void +safely_chroot(const char *path, uid_t uid) +{ + const char *cp; + char component[PATH_MAX]; + struct stat st; + + if (!path_absolute(path)) + fatal("chroot path does not begin at root"); + if (strlen(path) >= sizeof(component)) + fatal("chroot path too long"); + + /* + * Descend the path, checking that each component is a + * root-owned directory with strict permissions. + */ + for (cp = path; cp != NULL;) { + if ((cp = strchr(cp, '/')) == NULL) + strlcpy(component, path, sizeof(component)); + else { + cp++; + memcpy(component, path, cp - path); + component[cp - path] = '\0'; + } + + debug3_f("checking '%s'", component); + + if (stat(component, &st) != 0) + fatal_f("stat(\"%s\"): %s", + component, strerror(errno)); + if (st.st_uid != 0 || (st.st_mode & 022) != 0) + fatal("bad ownership or modes for chroot " + "directory %s\"%s\"", + cp == NULL ? "" : "component ", component); + if (!S_ISDIR(st.st_mode)) + fatal("chroot path %s\"%s\" is not a directory", + cp == NULL ? "" : "component ", component); + + } + + if (chdir(path) == -1) + fatal("Unable to chdir to chroot path \"%s\": " + "%s", path, strerror(errno)); + if (chroot(path) == -1) + fatal("chroot(\"%s\"): %s", path, strerror(errno)); + if (chdir("/") == -1) + fatal_f("chdir(/) after chroot: %s", strerror(errno)); + verbose("Changed root directory to \"%s\"", path); +} + +/* Set login name, uid, gid, and groups. */ +void +do_setusercontext(struct passwd *pw) +{ + char uidstr[32], *chroot_path, *tmp; + + platform_setusercontext(pw); + + if (platform_privileged_uidswap()) { +#ifdef HAVE_LOGIN_CAP + if (setusercontext(lc, pw, pw->pw_uid, + (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { + perror("unable to set user context"); + exit(1); + } +#else + if (setlogin(pw->pw_name) < 0) + error("setlogin failed: %s", strerror(errno)); + if (setgid(pw->pw_gid) < 0) { + perror("setgid"); + exit(1); + } + /* Initialize the group list. */ + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + perror("initgroups"); + exit(1); + } + endgrent(); +#endif + + platform_setusercontext_post_groups(pw); + + if (!in_chroot && options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) { + tmp = tilde_expand_filename(options.chroot_directory, + pw->pw_uid); + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)pw->pw_uid); + chroot_path = percent_expand(tmp, "h", pw->pw_dir, + "u", pw->pw_name, "U", uidstr, (char *)NULL); + safely_chroot(chroot_path, pw->pw_uid); + free(tmp); + free(chroot_path); + /* Make sure we don't attempt to chroot again */ + free(options.chroot_directory); + options.chroot_directory = NULL; + in_chroot = 1; + } + +#ifdef HAVE_LOGIN_CAP + if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { + perror("unable to set user context (setuser)"); + exit(1); + } + /* + * FreeBSD's setusercontext() will not apply the user's + * own umask setting unless running with the user's UID. + */ + (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK); +#else +# ifdef USE_LIBIAF + /* + * In a chroot environment, the set_id() will always fail; + * typically because of the lack of necessary authentication + * services and runtime such as ./usr/lib/libiaf.so, + * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the + * internal sftp chroot case. We'll lose auditing and ACLs but + * permanently_set_uid will take care of the rest. + */ + if (!in_chroot && set_id(pw->pw_name) != 0) + fatal("set_id(%s) Failed", pw->pw_name); +# endif /* USE_LIBIAF */ + /* Permanently switch to the desired uid. */ + permanently_set_uid(pw); +#endif + } else if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) { + fatal("server lacks privileges to chroot to ChrootDirectory"); + } + + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); +} + +static void +do_pwchange(Session *s) +{ + fflush(NULL); + fprintf(stderr, "WARNING: Your password has expired.\n"); + if (s->ttyfd != -1) { + fprintf(stderr, + "You must change your password now and login again!\n"); +#ifdef WITH_SELINUX + setexeccon(NULL); +#endif +#ifdef PASSWD_NEEDS_USERNAME + execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name, + (char *)NULL); +#else + execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); +#endif + perror("passwd"); + } else { + fprintf(stderr, + "Password change required but no TTY available.\n"); + } + exit(1); +} + +static void +child_close_fds(struct ssh *ssh) +{ + extern int auth_sock; + + if (auth_sock != -1) { + close(auth_sock); + auth_sock = -1; + } + + if (ssh_packet_get_connection_in(ssh) == + ssh_packet_get_connection_out(ssh)) + close(ssh_packet_get_connection_in(ssh)); + else { + close(ssh_packet_get_connection_in(ssh)); + close(ssh_packet_get_connection_out(ssh)); + } + /* + * Close all descriptors related to channels. They will still remain + * open in the parent. + */ + /* XXX better use close-on-exec? -markus */ + channel_close_all(ssh); + + /* + * Close any extra file descriptors. Note that there may still be + * descriptors left by system functions. They will be closed later. + */ + endpwent(); + + /* Stop directing logs to a high-numbered fd before we close it */ + log_redirect_stderr_to(NULL); + + /* + * Close any extra open file descriptors so that we don't have them + * hanging around in clients. Note that we want to do this after + * initgroups, because at least on Solaris 2.3 it leaves file + * descriptors open. + */ + closefrom(STDERR_FILENO + 1); +} + +/* + * Performs common processing for the child, such as setting up the + * environment, closing extra file descriptors, setting the user and group + * ids, and executing the command or shell. + */ +#define ARGV_MAX 10 +void +do_child(struct ssh *ssh, Session *s, const char *command) +{ + extern char **environ; + char **env, *argv[ARGV_MAX], remote_id[512]; + const char *shell, *shell0; + struct passwd *pw = s->pw; + int r = 0; + + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + + /* remove hostkey from the child's memory */ + destroy_sensitive_data(); + ssh_packet_clear_keys(ssh); + + /* Force a password change */ + if (s->authctxt->force_pwchange) { + do_setusercontext(pw); + child_close_fds(ssh); + do_pwchange(s); + exit(1); + } + + /* + * Login(1) does this as well, and it needs uid 0 for the "-h" + * switch, so we let login(1) to this for us. + */ +#ifdef HAVE_OSF_SIA + session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); + if (!check_quietlogin(s, command)) + do_motd(); +#else /* HAVE_OSF_SIA */ + /* When PAM is enabled we rely on it to do the nologin check */ + if (!options.use_pam) + do_nologin(pw); + do_setusercontext(pw); + /* + * PAM session modules in do_setusercontext may have + * generated messages, so if this in an interactive + * login then display them too. + */ + if (!check_quietlogin(s, command)) + display_loginmsg(); +#endif /* HAVE_OSF_SIA */ + +#ifdef USE_PAM + if (options.use_pam && !is_pam_session_open()) { + debug3("PAM session not opened, exiting"); + display_loginmsg(); + exit(254); + } +#endif + + /* + * Get the shell from the password data. An empty shell field is + * legal, and means /bin/sh. + */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; + + /* + * Make sure $SHELL points to the shell from the password file, + * even if shell is overridden from login.conf + */ + env = do_setup_env(ssh, s, shell); + +#ifdef HAVE_LOGIN_CAP + shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); +#endif + + /* + * Close the connection descriptors; note that this is the child, and + * the server will still have the socket open, and it is important + * that we do not shutdown it. Note that the descriptors cannot be + * closed before building the environment, as we call + * ssh_remote_ipaddr there. + */ + child_close_fds(ssh); + + /* + * Must take new environment into use so that .ssh/rc, + * /etc/ssh/sshrc and xauth are run in the proper environment. + */ + environ = env; + +#if defined(KRB5) && defined(USE_AFS) + /* + * At this point, we check to see if AFS is active and if we have + * a valid Kerberos 5 TGT. If so, it seems like a good idea to see + * if we can (and need to) extend the ticket into an AFS token. If + * we don't do this, we run into potential problems if the user's + * home directory is in AFS and it's not world-readable. + */ + + if (options.kerberos_get_afs_token && k_hasafs() && + (s->authctxt->krb5_ctx != NULL)) { + char cell[64]; + + debug("Getting AFS token"); + + k_setpag(); + + if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) + krb5_afslog(s->authctxt->krb5_ctx, + s->authctxt->krb5_fwd_ccache, cell, NULL); + + krb5_afslog_home(s->authctxt->krb5_ctx, + s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); + } +#endif + + /* Change current directory to the user's home directory. */ + if (chdir(pw->pw_dir) == -1) { + /* Suppress missing homedir warning for chroot case */ +#ifdef HAVE_LOGIN_CAP + r = login_getcapbool(lc, "requirehome", 0); +#endif + if (r || !in_chroot) { + fprintf(stderr, "Could not chdir to home " + "directory %s: %s\n", pw->pw_dir, + strerror(errno)); + } + if (r) + exit(1); + } + + closefrom(STDERR_FILENO + 1); + + do_rc_files(ssh, s, shell); + + /* restore SIGPIPE for child */ + ssh_signal(SIGPIPE, SIG_DFL); + + if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { + error("Connection from %s: refusing non-sftp session", + remote_id); + printf("This service allows sftp connections only.\n"); + fflush(NULL); + exit(1); + } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { + extern int optind, optreset; + int i; + char *p, *args; + + setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME); + args = xstrdup(command ? command : "sftp-server"); + for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) + if (i < ARGV_MAX - 1) + argv[i++] = p; + argv[i] = NULL; + optind = optreset = 1; + __progname = argv[0]; +#ifdef WITH_SELINUX + ssh_selinux_change_context("sftpd_t"); +#endif + exit(sftp_server_main(i, argv, s->pw)); + } + + fflush(NULL); + + /* Get the last component of the shell name. */ + if ((shell0 = strrchr(shell, '/')) != NULL) + shell0++; + else + shell0 = shell; + + /* + * If we have no command, execute the shell. In this case, the shell + * name to be passed in argv[0] is preceded by '-' to indicate that + * this is a login shell. + */ + if (!command) { + char argv0[256]; + + /* Start the shell. Set initial character to '-'. */ + argv0[0] = '-'; + + if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) + >= sizeof(argv0) - 1) { + errno = EINVAL; + perror(shell); + exit(1); + } + + /* Execute the shell. */ + argv[0] = argv0; + argv[1] = NULL; + execve(shell, argv, env); + + /* Executing the shell failed. */ + perror(shell); + exit(1); + } + /* + * Execute the command using the user's shell. This uses the -c + * option to execute the command. + */ + argv[0] = (char *) shell0; + argv[1] = "-c"; + argv[2] = (char *) command; + argv[3] = NULL; + execve(shell, argv, env); + perror(shell); + exit(1); +} + +void +session_unused(int id) +{ + debug3_f("session id %d unused", id); + if (id >= options.max_sessions || + id >= sessions_nalloc) { + fatal_f("insane session id %d (max %d nalloc %d)", + id, options.max_sessions, sessions_nalloc); + } + memset(&sessions[id], 0, sizeof(*sessions)); + sessions[id].self = id; + sessions[id].used = 0; + sessions[id].chanid = -1; + sessions[id].ptyfd = -1; + sessions[id].ttyfd = -1; + sessions[id].ptymaster = -1; + sessions[id].x11_chanids = NULL; + sessions[id].next_unused = sessions_first_unused; + sessions_first_unused = id; +} + +Session * +session_new(void) +{ + Session *s, *tmp; + + if (sessions_first_unused == -1) { + if (sessions_nalloc >= options.max_sessions) + return NULL; + debug2_f("allocate (allocated %d max %d)", + sessions_nalloc, options.max_sessions); + tmp = xrecallocarray(sessions, sessions_nalloc, + sessions_nalloc + 1, sizeof(*sessions)); + if (tmp == NULL) { + error_f("cannot allocate %d sessions", + sessions_nalloc + 1); + return NULL; + } + sessions = tmp; + session_unused(sessions_nalloc++); + } + + if (sessions_first_unused >= sessions_nalloc || + sessions_first_unused < 0) { + fatal_f("insane first_unused %d max %d nalloc %d", + sessions_first_unused, options.max_sessions, + sessions_nalloc); + } + + s = &sessions[sessions_first_unused]; + if (s->used) + fatal_f("session %d already used", sessions_first_unused); + sessions_first_unused = s->next_unused; + s->used = 1; + s->next_unused = -1; + debug("session_new: session %d", s->self); + + return s; +} + +static void +session_dump(void) +{ + int i; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + + debug("dump: used %d next_unused %d session %d " + "channel %d pid %ld", + s->used, + s->next_unused, + s->self, + s->chanid, + (long)s->pid); + } +} + +int +session_open(Authctxt *authctxt, int chanid) +{ + Session *s = session_new(); + debug("session_open: channel %d", chanid); + if (s == NULL) { + error("no more sessions"); + return 0; + } + s->authctxt = authctxt; + s->pw = authctxt->pw; + if (s->pw == NULL || !authctxt->valid) + fatal("no user for session %d", s->self); + debug("session_open: session %d: link with channel %d", s->self, chanid); + s->chanid = chanid; + return 1; +} + +Session * +session_by_tty(char *tty) +{ + int i; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { + debug("session_by_tty: session %d tty %s", i, tty); + return s; + } + } + debug("session_by_tty: unknown tty %.100s", tty); + session_dump(); + return NULL; +} + +static Session * +session_by_channel(int id) +{ + int i; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used && s->chanid == id) { + debug("session_by_channel: session %d channel %d", + i, id); + return s; + } + } + debug("session_by_channel: unknown channel %d", id); + session_dump(); + return NULL; +} + +static Session * +session_by_x11_channel(int id) +{ + int i, j; + + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + + if (s->x11_chanids == NULL || !s->used) + continue; + for (j = 0; s->x11_chanids[j] != -1; j++) { + if (s->x11_chanids[j] == id) { + debug("session_by_x11_channel: session %d " + "channel %d", s->self, id); + return s; + } + } + } + debug("session_by_x11_channel: unknown channel %d", id); + session_dump(); + return NULL; +} + +static Session * +session_by_pid(pid_t pid) +{ + int i; + debug("session_by_pid: pid %ld", (long)pid); + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used && s->pid == pid) + return s; + } + error("session_by_pid: unknown pid %ld", (long)pid); + session_dump(); + return NULL; +} + +static int +session_window_change_req(struct ssh *ssh, Session *s) +{ + int r; + + if ((r = sshpkt_get_u32(ssh, &s->col)) != 0 || + (r = sshpkt_get_u32(ssh, &s->row)) != 0 || + (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 || + (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + return 1; +} + +static int +session_pty_req(struct ssh *ssh, Session *s) +{ + int r; + + if (!auth_opts->permit_pty_flag || !options.permit_tty) { + debug("Allocating a pty not permitted for this connection."); + return 0; + } + if (s->ttyfd != -1) { + ssh_packet_disconnect(ssh, "Protocol error: you already have a pty."); + return 0; + } + + if ((r = sshpkt_get_cstring(ssh, &s->term, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &s->col)) != 0 || + (r = sshpkt_get_u32(ssh, &s->row)) != 0 || + (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 || + (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + if (strcmp(s->term, "") == 0) { + free(s->term); + s->term = NULL; + } + + /* Allocate a pty and open it. */ + debug("Allocating pty."); + if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, + sizeof(s->tty)))) { + free(s->term); + s->term = NULL; + s->ptyfd = -1; + s->ttyfd = -1; + error("session_pty_req: session %d alloc failed", s->self); + return 0; + } + debug("session_pty_req: session %d alloc %s", s->self, s->tty); + + ssh_tty_parse_modes(ssh, s->ttyfd); + + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + if (!use_privsep) + pty_setowner(s->pw, s->tty); + + /* Set window size from the packet. */ + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + session_proctitle(s); + return 1; +} + +static int +session_subsystem_req(struct ssh *ssh, Session *s) +{ + struct stat st; + int r, success = 0; + char *prog, *cmd; + u_int i; + + if ((r = sshpkt_get_cstring(ssh, &s->subsys, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + debug2("subsystem request for %.100s by user %s", s->subsys, + s->pw->pw_name); + + for (i = 0; i < options.num_subsystems; i++) { + if (strcmp(s->subsys, options.subsystem_name[i]) == 0) { + prog = options.subsystem_command[i]; + cmd = options.subsystem_args[i]; + if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { + s->is_subsystem = SUBSYSTEM_INT_SFTP; + debug("subsystem: %s", prog); + } else { + if (stat(prog, &st) == -1) + debug("subsystem: cannot stat %s: %s", + prog, strerror(errno)); + s->is_subsystem = SUBSYSTEM_EXT; + debug("subsystem: exec() %s", cmd); + } + success = do_exec(ssh, s, cmd) == 0; + break; + } + } + + if (!success) + logit("subsystem request for %.100s by user %s failed, " + "subsystem not found", s->subsys, s->pw->pw_name); + + return success; +} + +static int +session_x11_req(struct ssh *ssh, Session *s) +{ + int r, success; + u_char single_connection = 0; + + if (s->auth_proto != NULL || s->auth_data != NULL) { + error("session_x11_req: session %d: " + "x11 forwarding already active", s->self); + return 0; + } + if ((r = sshpkt_get_u8(ssh, &single_connection)) != 0 || + (r = sshpkt_get_cstring(ssh, &s->auth_proto, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &s->auth_data, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &s->screen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + s->single_connection = single_connection; + + if (xauth_valid_string(s->auth_proto) && + xauth_valid_string(s->auth_data)) + success = session_setup_x11fwd(ssh, s); + else { + success = 0; + error("Invalid X11 forwarding data"); + } + if (!success) { + free(s->auth_proto); + free(s->auth_data); + s->auth_proto = NULL; + s->auth_data = NULL; + } + return success; +} + +static int +session_shell_req(struct ssh *ssh, Session *s) +{ + int r; + + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + return do_exec(ssh, s, NULL) == 0; +} + +static int +session_exec_req(struct ssh *ssh, Session *s) +{ + u_int success; + int r; + char *command = NULL; + + if ((r = sshpkt_get_cstring(ssh, &command, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + success = do_exec(ssh, s, command) == 0; + free(command); + return success; +} + +static int +session_break_req(struct ssh *ssh, Session *s) +{ + int r; + + if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* ignore */ + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) == -1) + return 0; + return 1; +} + +static int +session_env_req(struct ssh *ssh, Session *s) +{ + char *name, *val; + u_int i; + int r; + + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &val, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + + /* Don't set too many environment variables */ + if (s->num_env > 128) { + debug2("Ignoring env request %s: too many env vars", name); + goto fail; + } + + for (i = 0; i < options.num_accept_env; i++) { + if (match_pattern(name, options.accept_env[i])) { + debug2("Setting env %d: %s=%s", s->num_env, name, val); + s->env = xrecallocarray(s->env, s->num_env, + s->num_env + 1, sizeof(*s->env)); + s->env[s->num_env].name = name; + s->env[s->num_env].val = val; + s->num_env++; + return (1); + } + } + debug2("Ignoring env request %s: disallowed name", name); + + fail: + free(name); + free(val); + return (0); +} + +/* + * Conversion of signals from ssh channel request names. + * Subset of signals from RFC 4254 section 6.10C, with SIGINFO as + * local extension. + */ +static int +name2sig(char *name) +{ +#define SSH_SIG(x) if (strcmp(name, #x) == 0) return SIG ## x + SSH_SIG(HUP); + SSH_SIG(INT); + SSH_SIG(KILL); + SSH_SIG(QUIT); + SSH_SIG(TERM); + SSH_SIG(USR1); + SSH_SIG(USR2); +#undef SSH_SIG +#ifdef SIGINFO + if (strcmp(name, "INFO@openssh.com") == 0) + return SIGINFO; +#endif + return -1; +} + +static int +session_signal_req(struct ssh *ssh, Session *s) +{ + char *signame = NULL; + int r, sig, success = 0; + + if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + error_fr(r, "parse"); + goto out; + } + if ((sig = name2sig(signame)) == -1) { + error_f("unsupported signal \"%s\"", signame); + goto out; + } + if (s->pid <= 0) { + error_f("no pid for session %d", s->self); + goto out; + } + if (s->forced || s->is_subsystem) { + error_f("refusing to send signal %s to %s session", + signame, s->forced ? "forced-command" : "subsystem"); + goto out; + } + if (!use_privsep || mm_is_monitor()) { + error_f("session signalling requires privilege separation"); + goto out; + } + + debug_f("signal %s, killpg(%ld, %d)", signame, (long)s->pid, sig); + temporarily_use_uid(s->pw); + r = killpg(s->pid, sig); + restore_uid(); + if (r != 0) { + error_f("killpg(%ld, %d): %s", (long)s->pid, + sig, strerror(errno)); + goto out; + } + + /* success */ + success = 1; + out: + free(signame); + return success; +} + +static int +session_auth_agent_req(struct ssh *ssh, Session *s) +{ + static int called = 0; + int r; + + if ((r = sshpkt_get_end(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: parse packet", __func__); + if (!auth_opts->permit_agent_forwarding_flag || + !options.allow_agent_forwarding) { + debug_f("agent forwarding disabled"); + return 0; + } + if (called) { + return 0; + } else { + called = 1; + return auth_input_request_forwarding(ssh, s->pw); + } +} + +int +session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype) +{ + int success = 0; + Session *s; + + if ((s = session_by_channel(c->self)) == NULL) { + logit_f("no session %d req %.100s", c->self, rtype); + return 0; + } + debug_f("session %d req %s", s->self, rtype); + + /* + * a session is in LARVAL state until a shell, a command + * or a subsystem is executed + */ + if (c->type == SSH_CHANNEL_LARVAL) { + if (strcmp(rtype, "shell") == 0) { + success = session_shell_req(ssh, s); + } else if (strcmp(rtype, "exec") == 0) { + success = session_exec_req(ssh, s); + } else if (strcmp(rtype, "pty-req") == 0) { + success = session_pty_req(ssh, s); + } else if (strcmp(rtype, "x11-req") == 0) { + success = session_x11_req(ssh, s); + } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { + success = session_auth_agent_req(ssh, s); + } else if (strcmp(rtype, "subsystem") == 0) { + success = session_subsystem_req(ssh, s); + } else if (strcmp(rtype, "env") == 0) { + success = session_env_req(ssh, s); + } + } + if (strcmp(rtype, "window-change") == 0) { + success = session_window_change_req(ssh, s); + } else if (strcmp(rtype, "break") == 0) { + success = session_break_req(ssh, s); + } else if (strcmp(rtype, "signal") == 0) { + success = session_signal_req(ssh, s); + } + + return success; +} + +void +session_set_fds(struct ssh *ssh, Session *s, + int fdin, int fdout, int fderr, int ignore_fderr, int is_tty) +{ + /* + * now that have a child and a pipe to the child, + * we can activate our channel and register the fd's + */ + if (s->chanid == -1) + fatal("no channel for session %d", s->self); + channel_set_fds(ssh, s->chanid, + fdout, fdin, fderr, + ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, + 1, is_tty, CHAN_SES_WINDOW_DEFAULT); +} + +/* + * Function to perform pty cleanup. Also called if we get aborted abnormally + * (e.g., due to a dropped connection). + */ +void +session_pty_cleanup2(Session *s) +{ + if (s == NULL) { + error_f("no session"); + return; + } + if (s->ttyfd == -1) + return; + + debug_f("session %d release %s", s->self, s->tty); + + /* Record that the user has logged out. */ + if (s->pid != 0) + record_logout(s->pid, s->tty, s->pw->pw_name); + + /* Release the pseudo-tty. */ + if (getuid() == 0) + pty_release(s->tty); + + /* + * Close the server side of the socket pairs. We must do this after + * the pty cleanup, so that another process doesn't get this pty + * while we're still cleaning up. + */ + if (s->ptymaster != -1 && close(s->ptymaster) == -1) + error("close(s->ptymaster/%d): %s", + s->ptymaster, strerror(errno)); + + /* unlink pty from session */ + s->ttyfd = -1; +} + +void +session_pty_cleanup(Session *s) +{ + PRIVSEP(session_pty_cleanup2(s)); +} + +static char * +sig2name(int sig) +{ +#define SSH_SIG(x) if (sig == SIG ## x) return #x + SSH_SIG(ABRT); + SSH_SIG(ALRM); + SSH_SIG(FPE); + SSH_SIG(HUP); + SSH_SIG(ILL); + SSH_SIG(INT); + SSH_SIG(KILL); + SSH_SIG(PIPE); + SSH_SIG(QUIT); + SSH_SIG(SEGV); + SSH_SIG(TERM); + SSH_SIG(USR1); + SSH_SIG(USR2); +#undef SSH_SIG + return "SIG@openssh.com"; +} + +static void +session_close_x11(struct ssh *ssh, int id) +{ + Channel *c; + + if ((c = channel_by_id(ssh, id)) == NULL) { + debug_f("x11 channel %d missing", id); + } else { + /* Detach X11 listener */ + debug_f("detach x11 channel %d", id); + channel_cancel_cleanup(ssh, id); + if (c->ostate != CHAN_OUTPUT_CLOSED) + chan_mark_dead(ssh, c); + } +} + +static void +session_close_single_x11(struct ssh *ssh, int id, void *arg) +{ + Session *s; + u_int i; + + debug3_f("channel %d", id); + channel_cancel_cleanup(ssh, id); + if ((s = session_by_x11_channel(id)) == NULL) + fatal_f("no x11 channel %d", id); + for (i = 0; s->x11_chanids[i] != -1; i++) { + debug_f("session %d: closing channel %d", + s->self, s->x11_chanids[i]); + /* + * The channel "id" is already closing, but make sure we + * close all of its siblings. + */ + if (s->x11_chanids[i] != id) + session_close_x11(ssh, s->x11_chanids[i]); + } + free(s->x11_chanids); + s->x11_chanids = NULL; + free(s->display); + s->display = NULL; + free(s->auth_proto); + s->auth_proto = NULL; + free(s->auth_data); + s->auth_data = NULL; + free(s->auth_display); + s->auth_display = NULL; +} + +static void +session_exit_message(struct ssh *ssh, Session *s, int status) +{ + Channel *c; + int r; + + if ((c = channel_lookup(ssh, s->chanid)) == NULL) + fatal_f("session %d: no channel %d", s->self, s->chanid); + debug_f("session %d channel %d pid %ld", + s->self, s->chanid, (long)s->pid); + + if (WIFEXITED(status)) { + channel_request_start(ssh, s->chanid, "exit-status", 0); + if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: exit reply", __func__); + } else if (WIFSIGNALED(status)) { + channel_request_start(ssh, s->chanid, "exit-signal", 0); +#ifndef WCOREDUMP +# define WCOREDUMP(x) (0) +#endif + if ((r = sshpkt_put_cstring(ssh, sig2name(WTERMSIG(status)))) != 0 || + (r = sshpkt_put_u8(ssh, WCOREDUMP(status)? 1 : 0)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: exit reply", __func__); + } else { + /* Some weird exit cause. Just exit. */ + ssh_packet_disconnect(ssh, "wait returned status %04x.", status); + } + + /* disconnect channel */ + debug_f("release channel %d", s->chanid); + + /* + * Adjust cleanup callback attachment to send close messages when + * the channel gets EOF. The session will be then be closed + * by session_close_by_channel when the child sessions close their fds. + */ + channel_register_cleanup(ssh, c->self, session_close_by_channel, 1); + + /* + * emulate a write failure with 'chan_write_failed', nobody will be + * interested in data we write. + * Note that we must not call 'chan_read_failed', since there could + * be some more data waiting in the pipe. + */ + if (c->ostate != CHAN_OUTPUT_CLOSED) + chan_write_failed(ssh, c); +} + +void +session_close(struct ssh *ssh, Session *s) +{ + u_int i; + + verbose("Close session: user %s from %.200s port %d id %d", + s->pw->pw_name, + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), + s->self); + + if (s->ttyfd != -1) + session_pty_cleanup(s); + free(s->term); + free(s->display); + free(s->x11_chanids); + free(s->auth_display); + free(s->auth_data); + free(s->auth_proto); + free(s->subsys); + if (s->env != NULL) { + for (i = 0; i < s->num_env; i++) { + free(s->env[i].name); + free(s->env[i].val); + } + free(s->env); + } + session_proctitle(s); + session_unused(s->self); +} + +void +session_close_by_pid(struct ssh *ssh, pid_t pid, int status) +{ + Session *s = session_by_pid(pid); + if (s == NULL) { + debug_f("no session for pid %ld", (long)pid); + return; + } + if (s->chanid != -1) + session_exit_message(ssh, s, status); + if (s->ttyfd != -1) + session_pty_cleanup(s); + s->pid = 0; +} + +/* + * this is called when a channel dies before + * the session 'child' itself dies + */ +void +session_close_by_channel(struct ssh *ssh, int id, void *arg) +{ + Session *s = session_by_channel(id); + u_int i; + + if (s == NULL) { + debug_f("no session for id %d", id); + return; + } + debug_f("channel %d child %ld", id, (long)s->pid); + if (s->pid != 0) { + debug_f("channel %d: has child, ttyfd %d", id, s->ttyfd); + /* + * delay detach of session, but release pty, since + * the fd's to the child are already closed + */ + if (s->ttyfd != -1) + session_pty_cleanup(s); + return; + } + /* detach by removing callback */ + channel_cancel_cleanup(ssh, s->chanid); + + /* Close any X11 listeners associated with this session */ + if (s->x11_chanids != NULL) { + for (i = 0; s->x11_chanids[i] != -1; i++) { + session_close_x11(ssh, s->x11_chanids[i]); + s->x11_chanids[i] = -1; + } + } + + s->chanid = -1; + session_close(ssh, s); +} + +void +session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *)) +{ + int i; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used) { + if (closefunc != NULL) + closefunc(s); + else + session_close(ssh, s); + } + } +} + +static char * +session_tty_list(void) +{ + static char buf[1024]; + int i; + char *cp; + + buf[0] = '\0'; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used && s->ttyfd != -1) { + + if (strncmp(s->tty, "/dev/", 5) != 0) { + cp = strrchr(s->tty, '/'); + cp = (cp == NULL) ? s->tty : cp + 1; + } else + cp = s->tty + 5; + + if (buf[0] != '\0') + strlcat(buf, ",", sizeof buf); + strlcat(buf, cp, sizeof buf); + } + } + if (buf[0] == '\0') + strlcpy(buf, "notty", sizeof buf); + return buf; +} + +void +session_proctitle(Session *s) +{ + if (s->pw == NULL) + error("no user for session %d", s->self); + else + setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); +} + +int +session_setup_x11fwd(struct ssh *ssh, Session *s) +{ + struct stat st; + char display[512], auth_display[512]; + char hostname[NI_MAXHOST]; + u_int i; + + if (!auth_opts->permit_x11_forwarding_flag) { + ssh_packet_send_debug(ssh, "X11 forwarding disabled by key options."); + return 0; + } + if (!options.x11_forwarding) { + debug("X11 forwarding disabled in server configuration file."); + return 0; + } + if (options.xauth_location == NULL || + (stat(options.xauth_location, &st) == -1)) { + ssh_packet_send_debug(ssh, "No xauth program; cannot forward X11."); + return 0; + } + if (s->display != NULL) { + debug("X11 display already set."); + return 0; + } + if (x11_create_display_inet(ssh, options.x11_display_offset, + options.x11_use_localhost, s->single_connection, + &s->display_number, &s->x11_chanids) == -1) { + debug("x11_create_display_inet failed."); + return 0; + } + for (i = 0; s->x11_chanids[i] != -1; i++) { + channel_register_cleanup(ssh, s->x11_chanids[i], + session_close_single_x11, 0); + } + + /* Set up a suitable value for the DISPLAY variable. */ + if (gethostname(hostname, sizeof(hostname)) == -1) + fatal("gethostname: %.100s", strerror(errno)); + /* + * auth_display must be used as the displayname when the + * authorization entry is added with xauth(1). This will be + * different than the DISPLAY string for localhost displays. + */ + if (options.x11_use_localhost) { + snprintf(display, sizeof display, "localhost:%u.%u", + s->display_number, s->screen); + snprintf(auth_display, sizeof auth_display, "unix:%u.%u", + s->display_number, s->screen); + s->display = xstrdup(display); + s->auth_display = xstrdup(auth_display); + } else { +#ifdef IPADDR_IN_DISPLAY + struct hostent *he; + struct in_addr my_addr; + + he = gethostbyname(hostname); + if (he == NULL) { + error("Can't get IP address for X11 DISPLAY."); + ssh_packet_send_debug(ssh, "Can't get IP address for X11 DISPLAY."); + return 0; + } + memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); + snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), + s->display_number, s->screen); +#else + snprintf(display, sizeof display, "%.400s:%u.%u", hostname, + s->display_number, s->screen); +#endif + s->display = xstrdup(display); + s->auth_display = xstrdup(display); + } + + return 1; +} + +static void +do_authenticated2(struct ssh *ssh, Authctxt *authctxt) +{ + server_loop2(ssh, authctxt); +} + +void +do_cleanup(struct ssh *ssh, Authctxt *authctxt) +{ + static int called = 0; + + debug("do_cleanup"); + + /* no cleanup if we're in the child for login shell */ + if (is_child) + return; + + /* avoid double cleanup */ + if (called) + return; + called = 1; + + if (authctxt == NULL) + return; + +#ifdef USE_PAM + if (options.use_pam) { + sshpam_cleanup(); + sshpam_thread_cleanup(); + } +#endif + + if (!authctxt->authenticated) + return; + +#ifdef KRB5 + if (options.kerberos_ticket_cleanup && + authctxt->krb5_ctx) + krb5_cleanup_proc(authctxt); +#endif + +#ifdef GSSAPI + if (options.gss_cleanup_creds) + ssh_gssapi_cleanup_creds(); +#endif + + /* remove agent socket */ + auth_sock_cleanup_proc(authctxt->pw); + + /* remove userauth info */ + if (auth_info_file != NULL) { + temporarily_use_uid(authctxt->pw); + unlink(auth_info_file); + restore_uid(); + free(auth_info_file); + auth_info_file = NULL; + } + + /* + * Cleanup ptys/utmp only if privsep is disabled, + * or if running in monitor. + */ + if (!use_privsep || mm_is_monitor()) + session_destroy_all(ssh, session_pty_cleanup2); +} + +/* Return a name for the remote host that fits inside utmp_size */ + +const char * +session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns) +{ + const char *remote = ""; + + if (utmp_size > 0) + remote = auth_get_canonical_hostname(ssh, use_dns); + if (utmp_size == 0 || strlen(remote) > utmp_size) + remote = ssh_remote_ipaddr(ssh); + return remote; +} + diff --git a/aosp/external/openssh/session.h b/aosp/external/openssh/session.h new file mode 100644 index 0000000000000000000000000000000000000000..ce59dabd906de32cf2c423d62bfd3e3d9f2a8ba8 --- /dev/null +++ b/aosp/external/openssh/session.h @@ -0,0 +1,84 @@ +/* $OpenBSD: session.h,v 1.36 2018/10/02 12:40:07 djm Exp $ */ + +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SESSION_H +#define SESSION_H + +#define TTYSZ 64 +typedef struct Session Session; +struct Session { + int used; + int self; + int next_unused; + struct passwd *pw; + Authctxt *authctxt; + pid_t pid; + int forced; + + /* tty */ + char *term; + int ptyfd, ttyfd, ptymaster; + u_int row, col, xpixel, ypixel; + char tty[TTYSZ]; + + /* X11 */ + u_int display_number; + char *display; + u_int screen; + char *auth_display; + char *auth_proto; + char *auth_data; + int single_connection; + + int chanid; + int *x11_chanids; + int is_subsystem; + char *subsys; + u_int num_env; + struct { + char *name; + char *val; + } *env; +}; + +void do_authenticated(struct ssh *, Authctxt *); +void do_cleanup(struct ssh *, Authctxt *); + +int session_open(Authctxt *, int); +void session_unused(int); +int session_input_channel_req(struct ssh *, Channel *, const char *); +void session_close_by_pid(struct ssh *ssh, pid_t, int); +void session_close_by_channel(struct ssh *, int, void *); +void session_destroy_all(struct ssh *, void (*)(Session *)); +void session_pty_cleanup2(Session *); + +Session *session_new(void); +Session *session_by_tty(char *); +void session_close(struct ssh *, Session *); +void do_setusercontext(struct passwd *); + +const char *session_get_remote_name_or_ip(struct ssh *, u_int, int); + +#endif diff --git a/aosp/external/openssh/sftp-client.c b/aosp/external/openssh/sftp-client.c new file mode 100644 index 0000000000000000000000000000000000000000..1b8ce6d78826227f51bfbcef273e5440668088fa --- /dev/null +++ b/aosp/external/openssh/sftp-client.c @@ -0,0 +1,2795 @@ +/* $OpenBSD: sftp-client.c,v 1.162 2022/03/31 03:07:03 djm Exp $ */ +/* + * Copyright (c) 2001-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* XXX: memleaks */ +/* XXX: signed vs unsigned */ +/* XXX: remove all logging, only return status codes */ +/* XXX: copy between two remote sites */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#include "openbsd-compat/sys-queue.h" +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#include +#include +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "log.h" +#include "atomicio.h" +#include "progressmeter.h" +#include "misc.h" +#include "utf8.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" + +extern volatile sig_atomic_t interrupted; +extern int showprogress; + +/* Default size of buffer for up/download */ +#define DEFAULT_COPY_BUFLEN 32768 + +/* Default number of concurrent outstanding requests */ +#define DEFAULT_NUM_REQUESTS 64 + +/* Minimum amount of data to read at a time */ +#define MIN_READ_SIZE 512 + +/* Maximum depth to descend in directory trees */ +#define MAX_DIR_DEPTH 64 + +/* Directory separator characters */ +#ifdef HAVE_CYGWIN +# define SFTP_DIRECTORY_CHARS "/\\" +#else /* HAVE_CYGWIN */ +# define SFTP_DIRECTORY_CHARS "/" +#endif /* HAVE_CYGWIN */ + +struct sftp_conn { + int fd_in; + int fd_out; + u_int download_buflen; + u_int upload_buflen; + u_int num_requests; + u_int version; + u_int msg_id; +#define SFTP_EXT_POSIX_RENAME 0x00000001 +#define SFTP_EXT_STATVFS 0x00000002 +#define SFTP_EXT_FSTATVFS 0x00000004 +#define SFTP_EXT_HARDLINK 0x00000008 +#define SFTP_EXT_FSYNC 0x00000010 +#define SFTP_EXT_LSETSTAT 0x00000020 +#define SFTP_EXT_LIMITS 0x00000040 +#define SFTP_EXT_PATH_EXPAND 0x00000080 +#define SFTP_EXT_COPY_DATA 0x00000100 + u_int exts; + u_int64_t limit_kbps; + struct bwlimit bwlimit_in, bwlimit_out; +}; + +/* Tracks in-progress requests during file transfers */ +struct request { + u_int id; + size_t len; + u_int64_t offset; + TAILQ_ENTRY(request) tq; +}; +TAILQ_HEAD(requests, request); + +static u_char * +get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, + const char *errfmt, ...) __attribute__((format(printf, 4, 5))); + +static struct request * +request_enqueue(struct requests *requests, u_int id, size_t len, + uint64_t offset) +{ + struct request *req; + + req = xcalloc(1, sizeof(*req)); + req->id = id; + req->len = len; + req->offset = offset; + TAILQ_INSERT_TAIL(requests, req, tq); + return req; +} + +static struct request * +request_find(struct requests *requests, u_int id) +{ + struct request *req; + + for (req = TAILQ_FIRST(requests); + req != NULL && req->id != id; + req = TAILQ_NEXT(req, tq)) + ; + return req; +} + +/* ARGSUSED */ +static int +sftpio(void *_bwlimit, size_t amount) +{ + struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; + + refresh_progress_meter(0); + if (bwlimit != NULL) + bandwidth_limit(bwlimit, amount); + return 0; +} + +static void +send_msg(struct sftp_conn *conn, struct sshbuf *m) +{ + u_char mlen[4]; + struct iovec iov[2]; + + if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH) + fatal("Outbound message too long %zu", sshbuf_len(m)); + + /* Send length first */ + put_u32(mlen, sshbuf_len(m)); + iov[0].iov_base = mlen; + iov[0].iov_len = sizeof(mlen); + iov[1].iov_base = (u_char *)sshbuf_ptr(m); + iov[1].iov_len = sshbuf_len(m); + + if (atomiciov6(writev, conn->fd_out, iov, 2, sftpio, + conn->limit_kbps > 0 ? &conn->bwlimit_out : NULL) != + sshbuf_len(m) + sizeof(mlen)) + fatal("Couldn't send packet: %s", strerror(errno)); + + sshbuf_reset(m); +} + +static void +get_msg_extended(struct sftp_conn *conn, struct sshbuf *m, int initial) +{ + u_int msg_len; + u_char *p; + int r; + + sshbuf_reset(m); + if ((r = sshbuf_reserve(m, 4, &p)) != 0) + fatal_fr(r, "reserve"); + if (atomicio6(read, conn->fd_in, p, 4, sftpio, + conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != 4) { + if (errno == EPIPE || errno == ECONNRESET) + fatal("Connection closed"); + else + fatal("Couldn't read packet: %s", strerror(errno)); + } + + if ((r = sshbuf_get_u32(m, &msg_len)) != 0) + fatal_fr(r, "sshbuf_get_u32"); + if (msg_len > SFTP_MAX_MSG_LENGTH) { + do_log2(initial ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_FATAL, + "Received message too long %u", msg_len); + fatal("Ensure the remote shell produces no output " + "for non-interactive sessions."); + } + + if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) + fatal_fr(r, "reserve"); + if (atomicio6(read, conn->fd_in, p, msg_len, sftpio, + conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) + != msg_len) { + if (errno == EPIPE) + fatal("Connection closed"); + else + fatal("Read packet: %s", strerror(errno)); + } +} + +static void +get_msg(struct sftp_conn *conn, struct sshbuf *m) +{ + get_msg_extended(conn, m, 0); +} + +static void +send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s, + u_int len) +{ + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, code)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_string(msg, s, len)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); + sshbuf_free(msg); +} + +static void +send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code, + const void *s, u_int len, Attrib *a) +{ + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, code)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_string(msg, s, len)) != 0 || + (r = encode_attrib(msg, a)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message fd %d T:%u I:%u F:0x%04x M:%05o", + conn->fd_out, code, id, a->flags, a->perm); + sshbuf_free(msg); +} + +static u_int +get_status(struct sftp_conn *conn, u_int expected_id) +{ + struct sshbuf *msg; + u_char type; + u_int id, status; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + get_msg(conn, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "compose"); + + if (id != expected_id) + fatal("ID mismatch (%u != %u)", id, expected_id); + if (type != SSH2_FXP_STATUS) + fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", + SSH2_FXP_STATUS, type); + + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse"); + sshbuf_free(msg); + + debug3("SSH2_FXP_STATUS %u", status); + + return status; +} + +static u_char * +get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, + const char *errfmt, ...) +{ + struct sshbuf *msg; + u_int id, status; + u_char type; + u_char *handle; + char errmsg[256]; + va_list args; + int r; + + va_start(args, errfmt); + if (errfmt != NULL) + vsnprintf(errmsg, sizeof(errmsg), errfmt, args); + va_end(args); + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + get_msg(conn, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + + if (id != expected_id) + fatal("%s: ID mismatch (%u != %u)", + errfmt == NULL ? __func__ : errmsg, id, expected_id); + if (type == SSH2_FXP_STATUS) { + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + if (errfmt != NULL) + error("%s: %s", errmsg, fx2txt(status)); + sshbuf_free(msg); + return(NULL); + } else if (type != SSH2_FXP_HANDLE) + fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", + errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); + + if ((r = sshbuf_get_string(msg, &handle, len)) != 0) + fatal_fr(r, "parse handle"); + sshbuf_free(msg); + + return handle; +} + +/* XXX returning &static is error-prone. Refactor to fill *Attrib argument */ +static Attrib * +get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet) +{ + struct sshbuf *msg; + u_int id; + u_char type; + int r; + static Attrib a; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + get_msg(conn, msg); + + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + + if (id != expected_id) + fatal("ID mismatch (%u != %u)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + u_int status; + + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + if (quiet) + debug("stat remote: %s", fx2txt(status)); + else + error("stat remote: %s", fx2txt(status)); + sshbuf_free(msg); + return(NULL); + } else if (type != SSH2_FXP_ATTRS) { + fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", + SSH2_FXP_ATTRS, type); + } + if ((r = decode_attrib(msg, &a)) != 0) { + error_fr(r, "decode_attrib"); + sshbuf_free(msg); + return NULL; + } + debug3("Received stat reply T:%u I:%u F:0x%04x M:%05o", + type, id, a.flags, a.perm); + sshbuf_free(msg); + + return &a; +} + +static int +get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st, + u_int expected_id, int quiet) +{ + struct sshbuf *msg; + u_char type; + u_int id; + u_int64_t flag; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + get_msg(conn, msg); + + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + + debug3("Received statvfs reply T:%u I:%u", type, id); + if (id != expected_id) + fatal("ID mismatch (%u != %u)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + u_int status; + + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + if (quiet) + debug("remote statvfs: %s", fx2txt(status)); + else + error("remote statvfs: %s", fx2txt(status)); + sshbuf_free(msg); + return -1; + } else if (type != SSH2_FXP_EXTENDED_REPLY) { + fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", + SSH2_FXP_EXTENDED_REPLY, type); + } + + memset(st, 0, sizeof(*st)); + if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_files)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 || + (r = sshbuf_get_u64(msg, &flag)) != 0 || + (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0) + fatal_fr(r, "parse statvfs"); + + st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; + st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; + + sshbuf_free(msg); + + return 0; +} + +struct sftp_conn * +do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, + u_int64_t limit_kbps) +{ + u_char type; + struct sshbuf *msg; + struct sftp_conn *ret; + int r; + + ret = xcalloc(1, sizeof(*ret)); + ret->msg_id = 1; + ret->fd_in = fd_in; + ret->fd_out = fd_out; + ret->download_buflen = ret->upload_buflen = + transfer_buflen ? transfer_buflen : DEFAULT_COPY_BUFLEN; + ret->num_requests = + num_requests ? num_requests : DEFAULT_NUM_REQUESTS; + ret->exts = 0; + ret->limit_kbps = 0; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 || + (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) + fatal_fr(r, "parse"); + + send_msg(ret, msg); + + get_msg_extended(ret, msg, 1); + + /* Expecting a VERSION reply */ + if ((r = sshbuf_get_u8(msg, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != SSH2_FXP_VERSION) { + error("Invalid packet back from SSH2_FXP_INIT (type %u)", + type); + sshbuf_free(msg); + free(ret); + return(NULL); + } + if ((r = sshbuf_get_u32(msg, &ret->version)) != 0) + fatal_fr(r, "parse version"); + + debug2("Remote version: %u", ret->version); + + /* Check for extensions */ + while (sshbuf_len(msg) > 0) { + char *name; + u_char *value; + size_t vlen; + int known = 0; + + if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 || + (r = sshbuf_get_string(msg, &value, &vlen)) != 0) + fatal_fr(r, "parse extension"); + if (strcmp(name, "posix-rename@openssh.com") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_POSIX_RENAME; + known = 1; + } else if (strcmp(name, "statvfs@openssh.com") == 0 && + strcmp((char *)value, "2") == 0) { + ret->exts |= SFTP_EXT_STATVFS; + known = 1; + } else if (strcmp(name, "fstatvfs@openssh.com") == 0 && + strcmp((char *)value, "2") == 0) { + ret->exts |= SFTP_EXT_FSTATVFS; + known = 1; + } else if (strcmp(name, "hardlink@openssh.com") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_HARDLINK; + known = 1; + } else if (strcmp(name, "fsync@openssh.com") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_FSYNC; + known = 1; + } else if (strcmp(name, "lsetstat@openssh.com") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_LSETSTAT; + known = 1; + } else if (strcmp(name, "limits@openssh.com") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_LIMITS; + known = 1; + } else if (strcmp(name, "expand-path@openssh.com") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_PATH_EXPAND; + known = 1; + } else if (strcmp(name, "copy-data") == 0 && + strcmp((char *)value, "1") == 0) { + ret->exts |= SFTP_EXT_COPY_DATA; + known = 1; + } + if (known) { + debug2("Server supports extension \"%s\" revision %s", + name, value); + } else { + debug2("Unrecognised server extension \"%s\"", name); + } + free(name); + free(value); + } + + sshbuf_free(msg); + + /* Query the server for its limits */ + if (ret->exts & SFTP_EXT_LIMITS) { + struct sftp_limits limits; + if (do_limits(ret, &limits) != 0) + fatal_f("limits failed"); + + /* If the caller did not specify, find a good value */ + if (transfer_buflen == 0) { + ret->download_buflen = limits.read_length; + ret->upload_buflen = limits.write_length; + debug("Using server download size %u", ret->download_buflen); + debug("Using server upload size %u", ret->upload_buflen); + } + + /* Use the server limit to scale down our value only */ + if (num_requests == 0 && limits.open_handles) { + ret->num_requests = + MINIMUM(DEFAULT_NUM_REQUESTS, limits.open_handles); + debug("Server handle limit %llu; using %u", + (unsigned long long)limits.open_handles, + ret->num_requests); + } + } + + /* Some filexfer v.0 servers don't support large packets */ + if (ret->version == 0) { + ret->download_buflen = MINIMUM(ret->download_buflen, 20480); + ret->upload_buflen = MINIMUM(ret->upload_buflen, 20480); + } + + ret->limit_kbps = limit_kbps; + if (ret->limit_kbps > 0) { + bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps, + ret->download_buflen); + bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps, + ret->upload_buflen); + } + + return ret; +} + +u_int +sftp_proto_version(struct sftp_conn *conn) +{ + return conn->version; +} + +int +do_limits(struct sftp_conn *conn, struct sftp_limits *limits) +{ + u_int id, msg_id; + u_char type; + struct sshbuf *msg; + int r; + + if ((conn->exts & SFTP_EXT_LIMITS) == 0) { + error("Server does not support limits@openssh.com extension"); + return -1; + } + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "limits@openssh.com")) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message limits@openssh.com I:%u", id); + + get_msg(conn, msg); + + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &msg_id)) != 0) + fatal_fr(r, "parse"); + + debug3("Received limits reply T:%u I:%u", type, msg_id); + if (id != msg_id) + fatal("ID mismatch (%u != %u)", msg_id, id); + if (type != SSH2_FXP_EXTENDED_REPLY) { + debug_f("expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", + SSH2_FXP_EXTENDED_REPLY, type); + /* Disable the limits extension */ + conn->exts &= ~SFTP_EXT_LIMITS; + sshbuf_free(msg); + return 0; + } + + memset(limits, 0, sizeof(*limits)); + if ((r = sshbuf_get_u64(msg, &limits->packet_length)) != 0 || + (r = sshbuf_get_u64(msg, &limits->read_length)) != 0 || + (r = sshbuf_get_u64(msg, &limits->write_length)) != 0 || + (r = sshbuf_get_u64(msg, &limits->open_handles)) != 0) + fatal_fr(r, "parse limits"); + + sshbuf_free(msg); + + return 0; +} + +int +do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) +{ + u_int id, status; + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_string(msg, handle, handle_len)) != 0) + fatal_fr(r, "parse"); + send_msg(conn, msg); + debug3("Sent message SSH2_FXP_CLOSE I:%u", id); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("close remote: %s", fx2txt(status)); + + sshbuf_free(msg); + + return status == SSH2_FX_OK ? 0 : -1; +} + + +static int +do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, + SFTP_DIRENT ***dir) +{ + struct sshbuf *msg; + u_int count, id, i, expected_id, ents = 0; + size_t handle_len; + u_char type, *handle; + int status = SSH2_FX_FAILURE; + int r; + + if (dir) + *dir = NULL; + + id = conn->msg_id++; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, path)) != 0) + fatal_fr(r, "compose OPENDIR"); + send_msg(conn, msg); + + handle = get_handle(conn, id, &handle_len, + "remote readdir(\"%s\")", path); + if (handle == NULL) { + sshbuf_free(msg); + return -1; + } + + if (dir) { + ents = 0; + *dir = xcalloc(1, sizeof(**dir)); + (*dir)[0] = NULL; + } + + for (; !interrupted;) { + id = expected_id = conn->msg_id++; + + debug3("Sending SSH2_FXP_READDIR I:%u", id); + + sshbuf_reset(msg); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_string(msg, handle, handle_len)) != 0) + fatal_fr(r, "compose READDIR"); + send_msg(conn, msg); + + sshbuf_reset(msg); + + get_msg(conn, msg); + + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + + debug3("Received reply T:%u I:%u", type, id); + + if (id != expected_id) + fatal("ID mismatch (%u != %u)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int rstatus; + + if ((r = sshbuf_get_u32(msg, &rstatus)) != 0) + fatal_fr(r, "parse status"); + debug3("Received SSH2_FXP_STATUS %d", rstatus); + if (rstatus == SSH2_FX_EOF) + break; + error("Couldn't read directory: %s", fx2txt(rstatus)); + goto out; + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", + SSH2_FXP_NAME, type); + + if ((r = sshbuf_get_u32(msg, &count)) != 0) + fatal_fr(r, "parse count"); + if (count > SSHBUF_SIZE_MAX) + fatal_f("nonsensical number of entries"); + if (count == 0) + break; + debug3("Received %d SSH2_FXP_NAME responses", count); + for (i = 0; i < count; i++) { + char *filename, *longname; + Attrib a; + + if ((r = sshbuf_get_cstring(msg, &filename, + NULL)) != 0 || + (r = sshbuf_get_cstring(msg, &longname, + NULL)) != 0) + fatal_fr(r, "parse filenames"); + if ((r = decode_attrib(msg, &a)) != 0) { + error_fr(r, "couldn't decode attrib"); + free(filename); + free(longname); + goto out; + } + + if (print_flag) + mprintf("%s\n", longname); + + /* + * Directory entries should never contain '/' + * These can be used to attack recursive ops + * (e.g. send '../../../../etc/passwd') + */ + if (strpbrk(filename, SFTP_DIRECTORY_CHARS) != NULL) { + error("Server sent suspect path \"%s\" " + "during readdir of \"%s\"", filename, path); + } else if (dir) { + *dir = xreallocarray(*dir, ents + 2, sizeof(**dir)); + (*dir)[ents] = xcalloc(1, sizeof(***dir)); + (*dir)[ents]->filename = xstrdup(filename); + (*dir)[ents]->longname = xstrdup(longname); + memcpy(&(*dir)[ents]->a, &a, sizeof(a)); + (*dir)[++ents] = NULL; + } + free(filename); + free(longname); + } + } + status = 0; + + out: + sshbuf_free(msg); + do_close(conn, handle, handle_len); + free(handle); + + if (status != 0 && dir != NULL) { + /* Don't return results on error */ + free_sftp_dirents(*dir); + *dir = NULL; + } else if (interrupted && dir != NULL && *dir != NULL) { + /* Don't return partial matches on interrupt */ + free_sftp_dirents(*dir); + *dir = xcalloc(1, sizeof(**dir)); + **dir = NULL; + } + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) +{ + return(do_lsreaddir(conn, path, 0, dir)); +} + +void free_sftp_dirents(SFTP_DIRENT **s) +{ + int i; + + if (s == NULL) + return; + for (i = 0; s[i]; i++) { + free(s[i]->filename); + free(s[i]->longname); + free(s[i]); + } + free(s); +} + +int +do_rm(struct sftp_conn *conn, const char *path) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); + + id = conn->msg_id++; + send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path)); + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote delete %s: %s", path, fx2txt(status)); + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_MKDIR \"%s\"", path); + + id = conn->msg_id++; + send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path, + strlen(path), a); + + status = get_status(conn, id); + if (status != SSH2_FX_OK && print_flag) + error("remote mkdir \"%s\": %s", path, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_rmdir(struct sftp_conn *conn, const char *path) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_RMDIR \"%s\"", path); + + id = conn->msg_id++; + send_string_request(conn, id, SSH2_FXP_RMDIR, path, + strlen(path)); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote rmdir \"%s\": %s", path, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +Attrib * +do_stat(struct sftp_conn *conn, const char *path, int quiet) +{ + u_int id; + + debug2("Sending SSH2_FXP_STAT \"%s\"", path); + + id = conn->msg_id++; + + send_string_request(conn, id, + conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, + path, strlen(path)); + + return(get_decode_stat(conn, id, quiet)); +} + +Attrib * +do_lstat(struct sftp_conn *conn, const char *path, int quiet) +{ + u_int id; + + if (conn->version == 0) { + if (quiet) + debug("Server version does not support lstat operation"); + else + logit("Server version does not support lstat operation"); + return(do_stat(conn, path, quiet)); + } + + id = conn->msg_id++; + send_string_request(conn, id, SSH2_FXP_LSTAT, path, + strlen(path)); + + return(get_decode_stat(conn, id, quiet)); +} + +#ifdef notyet +Attrib * +do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, + int quiet) +{ + u_int id; + + debug2("Sending SSH2_FXP_FSTAT \"%s\""); + + id = conn->msg_id++; + send_string_request(conn, id, SSH2_FXP_FSTAT, handle, + handle_len); + + return(get_decode_stat(conn, id, quiet)); +} +#endif + +int +do_setstat(struct sftp_conn *conn, const char *path, Attrib *a) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_SETSTAT \"%s\"", path); + + id = conn->msg_id++; + send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path, + strlen(path), a); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote setstat \"%s\": %s", path, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, + Attrib *a) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_FSETSTAT"); + + id = conn->msg_id++; + send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle, + handle_len, a); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote fsetstat: %s", fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +/* Implements both the realpath and expand-path operations */ +static char * +do_realpath_expand(struct sftp_conn *conn, const char *path, int expand) +{ + struct sshbuf *msg; + u_int expected_id, count, id; + char *filename, *longname; + Attrib a; + u_char type; + int r; + const char *what = "SSH2_FXP_REALPATH"; + + if (expand) + what = "expand-path@openssh.com"; + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + expected_id = id = conn->msg_id++; + if (expand) { + debug2("Sending SSH2_FXP_EXTENDED(expand-path@openssh.com) " + "\"%s\"", path); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, + "expand-path@openssh.com")) != 0 || + (r = sshbuf_put_cstring(msg, path)) != 0) + fatal_fr(r, "compose %s", what); + send_msg(conn, msg); + } else { + debug2("Sending SSH2_FXP_REALPATH \"%s\"", path); + send_string_request(conn, id, SSH2_FXP_REALPATH, + path, strlen(path)); + } + get_msg(conn, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + + if (id != expected_id) + fatal("ID mismatch (%u != %u)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status; + char *errmsg; + + if ((r = sshbuf_get_u32(msg, &status)) != 0 || + (r = sshbuf_get_cstring(msg, &errmsg, NULL)) != 0) + fatal_fr(r, "parse status"); + error("%s %s: %s", expand ? "expand" : "realpath", + path, *errmsg == '\0' ? fx2txt(status) : errmsg); + free(errmsg); + sshbuf_free(msg); + return NULL; + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", + SSH2_FXP_NAME, type); + + if ((r = sshbuf_get_u32(msg, &count)) != 0) + fatal_fr(r, "parse count"); + if (count != 1) + fatal("Got multiple names (%d) from %s", count, what); + + if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || + (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || + (r = decode_attrib(msg, &a)) != 0) + fatal_fr(r, "parse filename/attrib"); + + debug3("%s %s -> %s", what, path, filename); + + free(longname); + + sshbuf_free(msg); + + return(filename); +} + +char * +do_realpath(struct sftp_conn *conn, const char *path) +{ + return do_realpath_expand(conn, path, 0); +} + +int +can_expand_path(struct sftp_conn *conn) +{ + return (conn->exts & SFTP_EXT_PATH_EXPAND) != 0; +} + +char * +do_expand_path(struct sftp_conn *conn, const char *path) +{ + if (!can_expand_path(conn)) { + debug3_f("no server support, fallback to realpath"); + return do_realpath_expand(conn, path, 0); + } + return do_realpath_expand(conn, path, 1); +} + +int +do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath) +{ + Attrib junk, *a; + struct sshbuf *msg; + u_char *old_handle, *new_handle; + u_int mode, status, id; + size_t old_handle_len, new_handle_len; + int r; + + /* Return if the extension is not supported */ + if ((conn->exts & SFTP_EXT_COPY_DATA) == 0) { + error("Server does not support copy-data extension"); + return -1; + } + + /* Make sure the file exists, and we can copy its perms */ + if ((a = do_stat(conn, oldpath, 0)) == NULL) + return -1; + + /* Do not preserve set[ug]id here, as we do not preserve ownership */ + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + mode = a->perm & 0777; + + if (!S_ISREG(a->perm)) { + error("Cannot copy non-regular file: %s", oldpath); + return -1; + } + } else { + /* NB: The user's umask will apply to this */ + mode = 0666; + } + + /* Set up the new perms for the new file */ + attrib_clear(a); + a->perm = mode; + a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + + if ((msg = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + + attrib_clear(&junk); /* Send empty attributes */ + + /* Open the old file for reading */ + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, oldpath)) != 0 || + (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 || + (r = encode_attrib(msg, &junk)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + send_msg(conn, msg); + debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, oldpath); + + sshbuf_reset(msg); + + old_handle = get_handle(conn, id, &old_handle_len, + "remote open(\"%s\")", oldpath); + if (old_handle == NULL) { + sshbuf_free(msg); + return -1; + } + + /* Open the new file for writing */ + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, newpath)) != 0 || + (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT| + SSH2_FXF_TRUNC)) != 0 || + (r = encode_attrib(msg, a)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + send_msg(conn, msg); + debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, newpath); + + sshbuf_reset(msg); + + new_handle = get_handle(conn, id, &new_handle_len, + "remote open(\"%s\")", newpath); + if (new_handle == NULL) { + sshbuf_free(msg); + free(old_handle); + return -1; + } + + /* Copy the file data */ + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "copy-data")) != 0 || + (r = sshbuf_put_string(msg, old_handle, old_handle_len)) != 0 || + (r = sshbuf_put_u64(msg, 0)) != 0 || + (r = sshbuf_put_u64(msg, 0)) != 0 || + (r = sshbuf_put_string(msg, new_handle, new_handle_len)) != 0 || + (r = sshbuf_put_u64(msg, 0)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + send_msg(conn, msg); + debug3("Sent message copy-data \"%s\" 0 0 -> \"%s\" 0", + oldpath, newpath); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("Couldn't copy file \"%s\" to \"%s\": %s", oldpath, + newpath, fx2txt(status)); + + /* Clean up everything */ + sshbuf_free(msg); + do_close(conn, old_handle, old_handle_len); + do_close(conn, new_handle, new_handle_len); + free(old_handle); + free(new_handle); + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, + int force_legacy) +{ + struct sshbuf *msg; + u_int status, id; + int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + /* Send rename request */ + id = conn->msg_id++; + if (use_ext) { + debug2("Sending SSH2_FXP_EXTENDED(posix-rename@openssh.com) " + "\"%s\" to \"%s\"", oldpath, newpath); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, + "posix-rename@openssh.com")) != 0) + fatal_fr(r, "compose posix-rename"); + } else { + debug2("Sending SSH2_FXP_RENAME \"%s\" to \"%s\"", + oldpath, newpath); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0) + fatal_fr(r, "compose rename"); + } + if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 || + (r = sshbuf_put_cstring(msg, newpath)) != 0) + fatal_fr(r, "compose paths"); + send_msg(conn, msg); + debug3("Sent message %s \"%s\" -> \"%s\"", + use_ext ? "posix-rename@openssh.com" : + "SSH2_FXP_RENAME", oldpath, newpath); + sshbuf_free(msg); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote rename \"%s\" to \"%s\": %s", oldpath, + newpath, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) +{ + struct sshbuf *msg; + u_int status, id; + int r; + + if ((conn->exts & SFTP_EXT_HARDLINK) == 0) { + error("Server does not support hardlink@openssh.com extension"); + return -1; + } + debug2("Sending SSH2_FXP_EXTENDED(hardlink@openssh.com) " + "\"%s\" to \"%s\"", oldpath, newpath); + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + /* Send link request */ + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || + (r = sshbuf_put_cstring(msg, oldpath)) != 0 || + (r = sshbuf_put_cstring(msg, newpath)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"", + oldpath, newpath); + sshbuf_free(msg); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote link \"%s\" to \"%s\": %s", oldpath, + newpath, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) +{ + struct sshbuf *msg; + u_int status, id; + int r; + + if (conn->version < 3) { + error("This server does not support the symlink operation"); + return(SSH2_FX_OP_UNSUPPORTED); + } + debug2("Sending SSH2_FXP_SYMLINK \"%s\" to \"%s\"", oldpath, newpath); + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + /* Send symlink request */ + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, oldpath)) != 0 || + (r = sshbuf_put_cstring(msg, newpath)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, + newpath); + sshbuf_free(msg); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote symlink file \"%s\" to \"%s\": %s", oldpath, + newpath, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +int +do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) +{ + struct sshbuf *msg; + u_int status, id; + int r; + + /* Silently return if the extension is not supported */ + if ((conn->exts & SFTP_EXT_FSYNC) == 0) + return -1; + debug2("Sending SSH2_FXP_EXTENDED(fsync@openssh.com)"); + + /* Send fsync request */ + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || + (r = sshbuf_put_string(msg, handle, handle_len)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message fsync@openssh.com I:%u", id); + sshbuf_free(msg); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote fsync: %s", fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +#ifdef notyet +char * +do_readlink(struct sftp_conn *conn, const char *path) +{ + struct sshbuf *msg; + u_int expected_id, count, id; + char *filename, *longname; + Attrib a; + u_char type; + int r; + + debug2("Sending SSH2_FXP_READLINK \"%s\"", path); + + expected_id = id = conn->msg_id++; + send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path)); + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + get_msg(conn, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + + if (id != expected_id) + fatal("ID mismatch (%u != %u)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status; + + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + error("Couldn't readlink: %s", fx2txt(status)); + sshbuf_free(msg); + return(NULL); + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", + SSH2_FXP_NAME, type); + + if ((r = sshbuf_get_u32(msg, &count)) != 0) + fatal_fr(r, "parse count"); + if (count != 1) + fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); + + if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || + (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || + (r = decode_attrib(msg, &a)) != 0) + fatal_fr(r, "parse filenames/attrib"); + + debug3("SSH_FXP_READLINK %s -> %s", path, filename); + + free(longname); + + sshbuf_free(msg); + + return filename; +} +#endif + +int +do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, + int quiet) +{ + struct sshbuf *msg; + u_int id; + int r; + + if ((conn->exts & SFTP_EXT_STATVFS) == 0) { + error("Server does not support statvfs@openssh.com extension"); + return -1; + } + + debug2("Sending SSH2_FXP_EXTENDED(statvfs@openssh.com) \"%s\"", path); + + id = conn->msg_id++; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || + (r = sshbuf_put_cstring(msg, path)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + sshbuf_free(msg); + + return get_decode_statvfs(conn, st, id, quiet); +} + +#ifdef notyet +int +do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, + struct sftp_statvfs *st, int quiet) +{ + struct sshbuf *msg; + u_int id; + + if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { + error("Server does not support fstatvfs@openssh.com extension"); + return -1; + } + + debug2("Sending SSH2_FXP_EXTENDED(fstatvfs@openssh.com)"); + + id = conn->msg_id++; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || + (r = sshbuf_put_string(msg, handle, handle_len)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + sshbuf_free(msg); + + return get_decode_statvfs(conn, st, id, quiet); +} +#endif + +int +do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a) +{ + struct sshbuf *msg; + u_int status, id; + int r; + + if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) { + error("Server does not support lsetstat@openssh.com extension"); + return -1; + } + + debug2("Sending SSH2_FXP_EXTENDED(lsetstat@openssh.com) \"%s\"", path); + + id = conn->msg_id++; + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 || + (r = sshbuf_put_cstring(msg, path)) != 0 || + (r = encode_attrib(msg, a)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + sshbuf_free(msg); + + status = get_status(conn, id); + if (status != SSH2_FX_OK) + error("remote lsetstat \"%s\": %s", path, fx2txt(status)); + + return status == SSH2_FX_OK ? 0 : -1; +} + +static void +send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, + u_int len, const u_char *handle, u_int handle_len) +{ + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || + (r = sshbuf_put_u64(msg, offset)) != 0 || + (r = sshbuf_put_u32(msg, len)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + sshbuf_free(msg); +} + +static int +send_open(struct sftp_conn *conn, const char *path, const char *tag, + u_int openmode, Attrib *a, u_char **handlep, size_t *handle_lenp) +{ + Attrib junk; + u_char *handle; + size_t handle_len; + struct sshbuf *msg; + int r; + u_int id; + + debug2("Sending SSH2_FXP_OPEN \"%s\"", path); + + *handlep = NULL; + *handle_lenp = 0; + + if (a == NULL) { + attrib_clear(&junk); /* Send empty attributes */ + a = &junk; + } + /* Send open request */ + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + id = conn->msg_id++; + if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_cstring(msg, path)) != 0 || + (r = sshbuf_put_u32(msg, openmode)) != 0 || + (r = encode_attrib(msg, a)) != 0) + fatal_fr(r, "compose %s open", tag); + send_msg(conn, msg); + sshbuf_free(msg); + debug3("Sent %s message SSH2_FXP_OPEN I:%u P:%s M:0x%04x", + tag, id, path, openmode); + if ((handle = get_handle(conn, id, &handle_len, + "%s open \"%s\"", tag, path)) == NULL) + return -1; + /* success */ + *handlep = handle; + *handle_lenp = handle_len; + return 0; +} + +static const char * +progress_meter_path(const char *path) +{ + const char *progresspath; + + if ((progresspath = strrchr(path, '/')) == NULL) + return path; + progresspath++; + if (*progresspath == '\0') + return path; + return progresspath; +} + +int +do_download(struct sftp_conn *conn, const char *remote_path, + const char *local_path, Attrib *a, int preserve_flag, int resume_flag, + int fsync_flag) +{ + struct sshbuf *msg; + u_char *handle; + int local_fd = -1, write_error; + int read_error, write_errno, lmodified = 0, reordered = 0, r; + u_int64_t offset = 0, size, highwater; + u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK; + off_t progress_counter; + size_t handle_len; + struct stat st; + struct requests requests; + struct request *req; + u_char type; + + debug2_f("download remote \"%s\" to local \"%s\"", + remote_path, local_path); + + TAILQ_INIT(&requests); + + if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) + return -1; + + /* Do not preserve set[ug]id here, as we do not preserve ownership */ + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + mode = a->perm & 0777; + else + mode = 0666; + + if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + (!S_ISREG(a->perm))) { + error("download %s: not a regular file", remote_path); + return(-1); + } + + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + size = a->size; + else + size = 0; + + buflen = conn->download_buflen; + + /* Send open request */ + if (send_open(conn, remote_path, "remote", SSH2_FXF_READ, NULL, + &handle, &handle_len) != 0) + return -1; + + local_fd = open(local_path, + O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR); + if (local_fd == -1) { + error("open local \"%s\": %s", local_path, strerror(errno)); + goto fail; + } + offset = highwater = 0; + if (resume_flag) { + if (fstat(local_fd, &st) == -1) { + error("stat local \"%s\": %s", + local_path, strerror(errno)); + goto fail; + } + if (st.st_size < 0) { + error("\"%s\" has negative size", local_path); + goto fail; + } + if ((u_int64_t)st.st_size > size) { + error("Unable to resume download of \"%s\": " + "local file is larger than remote", local_path); + fail: + do_close(conn, handle, handle_len); + free(handle); + if (local_fd != -1) + close(local_fd); + return -1; + } + offset = highwater = st.st_size; + } + + /* Read from remote and write to local */ + write_error = read_error = write_errno = num_req = 0; + max_req = 1; + progress_counter = offset; + + if (showprogress && size != 0) { + start_progress_meter(progress_meter_path(remote_path), + size, &progress_counter); + } + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + while (num_req > 0 || max_req > 0) { + u_char *data; + size_t len; + + /* + * Simulate EOF on interrupt: stop sending new requests and + * allow outstanding requests to drain gracefully + */ + if (interrupted) { + if (num_req == 0) /* If we haven't started yet... */ + break; + max_req = 0; + } + + /* Send some more requests */ + while (num_req < max_req) { + debug3("Request range %llu -> %llu (%d/%d)", + (unsigned long long)offset, + (unsigned long long)offset + buflen - 1, + num_req, max_req); + req = request_enqueue(&requests, conn->msg_id++, + buflen, offset); + offset += buflen; + num_req++; + send_read_request(conn, req->id, req->offset, + req->len, handle, handle_len); + } + + sshbuf_reset(msg); + get_msg(conn, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + debug3("Received reply T:%u I:%u R:%d", type, id, max_req); + + /* Find the request in our queue */ + if ((req = request_find(&requests, id)) == NULL) + fatal("Unexpected reply %u", id); + + switch (type) { + case SSH2_FXP_STATUS: + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + if (status != SSH2_FX_EOF) + read_error = 1; + max_req = 0; + TAILQ_REMOVE(&requests, req, tq); + free(req); + num_req--; + break; + case SSH2_FXP_DATA: + if ((r = sshbuf_get_string(msg, &data, &len)) != 0) + fatal_fr(r, "parse data"); + debug3("Received data %llu -> %llu", + (unsigned long long)req->offset, + (unsigned long long)req->offset + len - 1); + if (len > req->len) + fatal("Received more data than asked for " + "%zu > %zu", len, req->len); + lmodified = 1; + if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || + atomicio(vwrite, local_fd, data, len) != len) && + !write_error) { + write_errno = errno; + write_error = 1; + max_req = 0; + } + else if (!reordered && req->offset <= highwater) + highwater = req->offset + len; + else if (!reordered && req->offset > highwater) + reordered = 1; + progress_counter += len; + free(data); + + if (len == req->len) { + TAILQ_REMOVE(&requests, req, tq); + free(req); + num_req--; + } else { + /* Resend the request for the missing data */ + debug3("Short data block, re-requesting " + "%llu -> %llu (%2d)", + (unsigned long long)req->offset + len, + (unsigned long long)req->offset + + req->len - 1, num_req); + req->id = conn->msg_id++; + req->len -= len; + req->offset += len; + send_read_request(conn, req->id, + req->offset, req->len, handle, handle_len); + /* Reduce the request size */ + if (len < buflen) + buflen = MAXIMUM(MIN_READ_SIZE, len); + } + if (max_req > 0) { /* max_req = 0 iff EOF received */ + if (size > 0 && offset > size) { + /* Only one request at a time + * after the expected EOF */ + debug3("Finish at %llu (%2d)", + (unsigned long long)offset, + num_req); + max_req = 1; + } else if (max_req < conn->num_requests) { + ++max_req; + } + } + break; + default: + fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", + SSH2_FXP_DATA, type); + } + } + + if (showprogress && size) + stop_progress_meter(); + + /* Sanity check */ + if (TAILQ_FIRST(&requests) != NULL) + fatal("Transfer complete, but requests still in queue"); + /* Truncate at highest contiguous point to avoid holes on interrupt */ + if (read_error || write_error || interrupted) { + if (reordered && resume_flag) { + error("Unable to resume download of \"%s\": " + "server reordered requests", local_path); + } + debug("truncating at %llu", (unsigned long long)highwater); + if (ftruncate(local_fd, highwater) == -1) + error("local ftruncate \"%s\": %s", local_path, + strerror(errno)); + } + if (read_error) { + error("read remote \"%s\" : %s", remote_path, fx2txt(status)); + status = -1; + do_close(conn, handle, handle_len); + } else if (write_error) { + error("write local \"%s\": %s", local_path, + strerror(write_errno)); + status = SSH2_FX_FAILURE; + do_close(conn, handle, handle_len); + } else { + if (do_close(conn, handle, handle_len) != 0 || interrupted) + status = SSH2_FX_FAILURE; + else + status = SSH2_FX_OK; + /* Override umask and utimes if asked */ +#ifdef HAVE_FCHMOD + if (preserve_flag && fchmod(local_fd, mode) == -1) +#else + if (preserve_flag && chmod(local_path, mode) == -1) +#endif /* HAVE_FCHMOD */ + error("local chmod \"%s\": %s", local_path, + strerror(errno)); + if (preserve_flag && + (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { + struct timeval tv[2]; + tv[0].tv_sec = a->atime; + tv[1].tv_sec = a->mtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + if (utimes(local_path, tv) == -1) + error("local set times \"%s\": %s", + local_path, strerror(errno)); + } + if (resume_flag && !lmodified) + logit("File \"%s\" was not modified", local_path); + else if (fsync_flag) { + debug("syncing \"%s\"", local_path); + if (fsync(local_fd) == -1) + error("local sync \"%s\": %s", + local_path, strerror(errno)); + } + } + close(local_fd); + sshbuf_free(msg); + free(handle); + + return status == SSH2_FX_OK ? 0 : -1; +} + +static int +download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, + int depth, Attrib *dirattrib, int preserve_flag, int print_flag, + int resume_flag, int fsync_flag, int follow_link_flag) +{ + int i, ret = 0; + SFTP_DIRENT **dir_entries; + char *filename, *new_src = NULL, *new_dst = NULL; + mode_t mode = 0777, tmpmode = mode; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + debug2_f("download dir remote \"%s\" to local \"%s\"", src, dst); + + if (dirattrib == NULL && + (dirattrib = do_stat(conn, src, 1)) == NULL) { + error("stat remote \"%s\" directory failed", src); + return -1; + } + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (print_flag && print_flag != SFTP_PROGRESS_ONLY) + mprintf("Retrieving %s\n", src); + + if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + mode = dirattrib->perm & 01777; + tmpmode = mode | (S_IWUSR|S_IXUSR); + } else { + debug("download remote \"%s\": server " + "did not send permissions", dst); + } + + if (mkdir(dst, tmpmode) == -1 && errno != EEXIST) { + error("mkdir %s: %s", dst, strerror(errno)); + return -1; + } + + if (do_readdir(conn, src, &dir_entries) == -1) { + error("remote readdir \"%s\" failed", src); + return -1; + } + + for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { + free(new_dst); + free(new_src); + + filename = dir_entries[i]->filename; + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (S_ISDIR(dir_entries[i]->a.perm)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + if (download_dir_internal(conn, new_src, new_dst, + depth + 1, &(dir_entries[i]->a), preserve_flag, + print_flag, resume_flag, + fsync_flag, follow_link_flag) == -1) + ret = -1; + } else if (S_ISREG(dir_entries[i]->a.perm) || + (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { + /* + * If this is a symlink then don't send the link's + * Attrib. do_download() will do a FXP_STAT operation + * and get the link target's attributes. + */ + if (do_download(conn, new_src, new_dst, + S_ISLNK(dir_entries[i]->a.perm) ? NULL : + &(dir_entries[i]->a), + preserve_flag, resume_flag, fsync_flag) == -1) { + error("Download of file %s to %s failed", + new_src, new_dst); + ret = -1; + } + } else + logit("download \"%s\": not a regular file", new_src); + + } + free(new_dst); + free(new_src); + + if (preserve_flag) { + if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + struct timeval tv[2]; + tv[0].tv_sec = dirattrib->atime; + tv[1].tv_sec = dirattrib->mtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + if (utimes(dst, tv) == -1) + error("local set times on \"%s\": %s", + dst, strerror(errno)); + } else + debug("Server did not send times for directory " + "\"%s\"", dst); + } + + if (mode != tmpmode && chmod(dst, mode) == -1) + error("local chmod directory \"%s\": %s", dst, + strerror(errno)); + + free_sftp_dirents(dir_entries); + + return ret; +} + +int +download_dir(struct sftp_conn *conn, const char *src, const char *dst, + Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, + int fsync_flag, int follow_link_flag) +{ + char *src_canon; + int ret; + + if ((src_canon = do_realpath(conn, src)) == NULL) { + error("download \"%s\": path canonicalization failed", src); + return -1; + } + + ret = download_dir_internal(conn, src_canon, dst, 0, + dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag, + follow_link_flag); + free(src_canon); + return ret; +} + +int +do_upload(struct sftp_conn *conn, const char *local_path, + const char *remote_path, int preserve_flag, int resume, int fsync_flag) +{ + int r, local_fd; + u_int status = SSH2_FX_OK; + u_int id; + u_char type; + off_t offset, progress_counter; + u_char *handle, *data; + struct sshbuf *msg; + struct stat sb; + Attrib a, *c = NULL; + u_int32_t startid; + u_int32_t ackid; + struct request *ack = NULL; + struct requests acks; + size_t handle_len; + + debug2_f("upload local \"%s\" to remote \"%s\"", + local_path, remote_path); + + TAILQ_INIT(&acks); + + if ((local_fd = open(local_path, O_RDONLY)) == -1) { + error("open local \"%s\": %s", local_path, strerror(errno)); + return(-1); + } + if (fstat(local_fd, &sb) == -1) { + error("fstat local \"%s\": %s", local_path, strerror(errno)); + close(local_fd); + return(-1); + } + if (!S_ISREG(sb.st_mode)) { + error("local \"%s\" is not a regular file", local_path); + close(local_fd); + return(-1); + } + stat_to_attrib(&sb, &a); + + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 0777; + if (!preserve_flag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + if (resume) { + /* Get remote file size if it exists */ + if ((c = do_stat(conn, remote_path, 0)) == NULL) { + close(local_fd); + return -1; + } + + if ((off_t)c->size >= sb.st_size) { + error("resume \"%s\": destination file " + "same size or larger", local_path); + close(local_fd); + return -1; + } + + if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { + close(local_fd); + return -1; + } + } + + /* Send open request */ + if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT| + (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC), + &a, &handle, &handle_len) != 0) { + close(local_fd); + return -1; + } + + id = conn->msg_id; + startid = ackid = id + 1; + data = xmalloc(conn->upload_buflen); + + /* Read from local and write to remote */ + offset = progress_counter = (resume ? c->size : 0); + if (showprogress) { + start_progress_meter(progress_meter_path(local_path), + sb.st_size, &progress_counter); + } + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + for (;;) { + int len; + + /* + * Can't use atomicio here because it returns 0 on EOF, + * thus losing the last block of the file. + * Simulate an EOF on interrupt, allowing ACKs from the + * server to drain. + */ + if (interrupted || status != SSH2_FX_OK) + len = 0; + else do + len = read(local_fd, data, conn->upload_buflen); + while ((len == -1) && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + + if (len == -1) { + fatal("read local \"%s\": %s", + local_path, strerror(errno)); + } else if (len != 0) { + ack = request_enqueue(&acks, ++id, len, offset); + sshbuf_reset(msg); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || + (r = sshbuf_put_u32(msg, ack->id)) != 0 || + (r = sshbuf_put_string(msg, handle, + handle_len)) != 0 || + (r = sshbuf_put_u64(msg, offset)) != 0 || + (r = sshbuf_put_string(msg, data, len)) != 0) + fatal_fr(r, "compose"); + send_msg(conn, msg); + debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", + id, (unsigned long long)offset, len); + } else if (TAILQ_FIRST(&acks) == NULL) + break; + + if (ack == NULL) + fatal("Unexpected ACK %u", id); + + if (id == startid || len == 0 || + id - ackid >= conn->num_requests) { + u_int rid; + + sshbuf_reset(msg); + get_msg(conn, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &rid)) != 0) + fatal_fr(r, "parse"); + + if (type != SSH2_FXP_STATUS) + fatal("Expected SSH2_FXP_STATUS(%d) packet, " + "got %d", SSH2_FXP_STATUS, type); + + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + debug3("SSH2_FXP_STATUS %u", status); + + /* Find the request in our queue */ + if ((ack = request_find(&acks, rid)) == NULL) + fatal("Can't find request for ID %u", rid); + TAILQ_REMOVE(&acks, ack, tq); + debug3("In write loop, ack for %u %zu bytes at %lld", + ack->id, ack->len, (unsigned long long)ack->offset); + ++ackid; + progress_counter += ack->len; + free(ack); + } + offset += len; + if (offset < 0) + fatal_f("offset < 0"); + } + sshbuf_free(msg); + + if (showprogress) + stop_progress_meter(); + free(data); + + if (status != SSH2_FX_OK) { + error("write remote \"%s\": %s", remote_path, fx2txt(status)); + status = SSH2_FX_FAILURE; + } + + if (close(local_fd) == -1) { + error("close local \"%s\": %s", local_path, strerror(errno)); + status = SSH2_FX_FAILURE; + } + + /* Override umask and utimes if asked */ + if (preserve_flag) + do_fsetstat(conn, handle, handle_len, &a); + + if (fsync_flag) + (void)do_fsync(conn, handle, handle_len); + + if (do_close(conn, handle, handle_len) != 0) + status = SSH2_FX_FAILURE; + + free(handle); + + return status == SSH2_FX_OK ? 0 : -1; +} + +static int +upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, + int depth, int preserve_flag, int print_flag, int resume, int fsync_flag, + int follow_link_flag) +{ + int ret = 0; + DIR *dirp; + struct dirent *dp; + char *filename, *new_src = NULL, *new_dst = NULL; + struct stat sb; + Attrib a, *dirattrib; + u_int32_t saved_perm; + + debug2_f("upload local dir \"%s\" to remote \"%s\"", src, dst); + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (stat(src, &sb) == -1) { + error("stat local \"%s\": %s", src, strerror(errno)); + return -1; + } + if (!S_ISDIR(sb.st_mode)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (print_flag && print_flag != SFTP_PROGRESS_ONLY) + mprintf("Entering %s\n", src); + + stat_to_attrib(&sb, &a); + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 01777; + if (!preserve_flag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + /* + * sftp lacks a portable status value to match errno EEXIST, + * so if we get a failure back then we must check whether + * the path already existed and is a directory. Ensure we can + * write to the directory we create for the duration of the transfer. + */ + saved_perm = a.perm; + a.perm |= (S_IWUSR|S_IXUSR); + if (do_mkdir(conn, dst, &a, 0) != 0) { + if ((dirattrib = do_stat(conn, dst, 0)) == NULL) + return -1; + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" exists but is not a directory", dst); + return -1; + } + } + a.perm = saved_perm; + + if ((dirp = opendir(src)) == NULL) { + error("local opendir \"%s\": %s", src, strerror(errno)); + return -1; + } + + while (((dp = readdir(dirp)) != NULL) && !interrupted) { + if (dp->d_ino == 0) + continue; + free(new_dst); + free(new_src); + filename = dp->d_name; + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (lstat(new_src, &sb) == -1) { + logit("local lstat \"%s\": %s", filename, + strerror(errno)); + ret = -1; + } else if (S_ISDIR(sb.st_mode)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + + if (upload_dir_internal(conn, new_src, new_dst, + depth + 1, preserve_flag, print_flag, resume, + fsync_flag, follow_link_flag) == -1) + ret = -1; + } else if (S_ISREG(sb.st_mode) || + (follow_link_flag && S_ISLNK(sb.st_mode))) { + if (do_upload(conn, new_src, new_dst, + preserve_flag, resume, fsync_flag) == -1) { + error("upload \"%s\" to \"%s\" failed", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file", filename); + } + free(new_dst); + free(new_src); + + do_setstat(conn, dst, &a); + + (void) closedir(dirp); + return ret; +} + +int +upload_dir(struct sftp_conn *conn, const char *src, const char *dst, + int preserve_flag, int print_flag, int resume, int fsync_flag, + int follow_link_flag) +{ + char *dst_canon; + int ret; + + if ((dst_canon = do_realpath(conn, dst)) == NULL) { + error("upload \"%s\": path canonicalization failed", dst); + return -1; + } + + ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, + print_flag, resume, fsync_flag, follow_link_flag); + + free(dst_canon); + return ret; +} + +static void +handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous, + u_int *nreqsp, u_int *write_errorp) +{ + struct sshbuf *msg; + u_char type; + u_int id, status; + int r; + struct pollfd pfd; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + /* Try to eat replies from the upload side */ + while (*nreqsp > 0) { + debug3_f("%u outstanding replies", *nreqsp); + if (!synchronous) { + /* Bail out if no data is ready to be read */ + pfd.fd = to->fd_in; + pfd.events = POLLIN; + if ((r = poll(&pfd, 1, 0)) == -1) { + if (errno == EINTR) + break; + fatal_f("poll: %s", strerror(errno)); + } else if (r == 0) + break; /* fd not ready */ + } + sshbuf_reset(msg); + get_msg(to, msg); + + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "dest parse"); + debug3("Received dest reply T:%u I:%u R:%u", type, id, *nreqsp); + if (type != SSH2_FXP_STATUS) { + fatal_f("Expected SSH2_FXP_STATUS(%d) packet, got %d", + SSH2_FXP_STATUS, type); + } + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse dest status"); + debug3("dest SSH2_FXP_STATUS %u", status); + if (status != SSH2_FX_OK) { + /* record first error */ + if (*write_errorp == 0) + *write_errorp = status; + } + /* + * XXX this doesn't do full reply matching like do_upload and + * so cannot gracefully truncate terminated uploads at a + * high-water mark. ATM the only caller of this function (scp) + * doesn't support transfer resumption, so this doesn't matter + * a whole lot. + * + * To be safe, do_crossload truncates the destination file to + * zero length on upload failure, since we can't trust the + * server not to have reordered replies that could have + * inserted holes where none existed in the source file. + * + * XXX we could get a more accutate progress bar if we updated + * the counter based on the reply from the destination... + */ + (*nreqsp)--; + } + debug3_f("done: %u outstanding replies", *nreqsp); + sshbuf_free(msg); +} + +int +do_crossload(struct sftp_conn *from, struct sftp_conn *to, + const char *from_path, const char *to_path, + Attrib *a, int preserve_flag) +{ + struct sshbuf *msg; + int write_error, read_error, r; + u_int64_t offset = 0, size; + u_int id, buflen, num_req, max_req, status = SSH2_FX_OK; + u_int num_upload_req; + off_t progress_counter; + u_char *from_handle, *to_handle; + size_t from_handle_len, to_handle_len; + struct requests requests; + struct request *req; + u_char type; + + debug2_f("crossload src \"%s\" to dst \"%s\"", from_path, to_path); + + TAILQ_INIT(&requests); + + if (a == NULL && (a = do_stat(from, from_path, 0)) == NULL) + return -1; + + if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + (!S_ISREG(a->perm))) { + error("download \"%s\": not a regular file", from_path); + return(-1); + } + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + size = a->size; + else + size = 0; + + buflen = from->download_buflen; + if (buflen > to->upload_buflen) + buflen = to->upload_buflen; + + /* Send open request to read side */ + if (send_open(from, from_path, "origin", SSH2_FXF_READ, NULL, + &from_handle, &from_handle_len) != 0) + return -1; + + /* Send open request to write side */ + a->flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a->flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a->perm &= 0777; + if (!preserve_flag) + a->flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + if (send_open(to, to_path, "dest", + SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, + &to_handle, &to_handle_len) != 0) { + do_close(from, from_handle, from_handle_len); + return -1; + } + + /* Read from remote "from" and write to remote "to" */ + offset = 0; + write_error = read_error = num_req = num_upload_req = 0; + max_req = 1; + progress_counter = 0; + + if (showprogress && size != 0) { + start_progress_meter(progress_meter_path(from_path), + size, &progress_counter); + } + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + while (num_req > 0 || max_req > 0) { + u_char *data; + size_t len; + + /* + * Simulate EOF on interrupt: stop sending new requests and + * allow outstanding requests to drain gracefully + */ + if (interrupted) { + if (num_req == 0) /* If we haven't started yet... */ + break; + max_req = 0; + } + + /* Send some more requests */ + while (num_req < max_req) { + debug3("Request range %llu -> %llu (%d/%d)", + (unsigned long long)offset, + (unsigned long long)offset + buflen - 1, + num_req, max_req); + req = request_enqueue(&requests, from->msg_id++, + buflen, offset); + offset += buflen; + num_req++; + send_read_request(from, req->id, req->offset, + req->len, from_handle, from_handle_len); + } + + /* Try to eat replies from the upload side (nonblocking) */ + handle_dest_replies(to, to_path, 0, + &num_upload_req, &write_error); + + sshbuf_reset(msg); + get_msg(from, msg); + if ((r = sshbuf_get_u8(msg, &type)) != 0 || + (r = sshbuf_get_u32(msg, &id)) != 0) + fatal_fr(r, "parse"); + debug3("Received origin reply T:%u I:%u R:%d", + type, id, max_req); + + /* Find the request in our queue */ + if ((req = request_find(&requests, id)) == NULL) + fatal("Unexpected reply %u", id); + + switch (type) { + case SSH2_FXP_STATUS: + if ((r = sshbuf_get_u32(msg, &status)) != 0) + fatal_fr(r, "parse status"); + if (status != SSH2_FX_EOF) + read_error = 1; + max_req = 0; + TAILQ_REMOVE(&requests, req, tq); + free(req); + num_req--; + break; + case SSH2_FXP_DATA: + if ((r = sshbuf_get_string(msg, &data, &len)) != 0) + fatal_fr(r, "parse data"); + debug3("Received data %llu -> %llu", + (unsigned long long)req->offset, + (unsigned long long)req->offset + len - 1); + if (len > req->len) + fatal("Received more data than asked for " + "%zu > %zu", len, req->len); + + /* Write this chunk out to the destination */ + sshbuf_reset(msg); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || + (r = sshbuf_put_u32(msg, to->msg_id++)) != 0 || + (r = sshbuf_put_string(msg, to_handle, + to_handle_len)) != 0 || + (r = sshbuf_put_u64(msg, req->offset)) != 0 || + (r = sshbuf_put_string(msg, data, len)) != 0) + fatal_fr(r, "compose write"); + send_msg(to, msg); + debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%zu", + id, (unsigned long long)offset, len); + num_upload_req++; + progress_counter += len; + free(data); + + if (len == req->len) { + TAILQ_REMOVE(&requests, req, tq); + free(req); + num_req--; + } else { + /* Resend the request for the missing data */ + debug3("Short data block, re-requesting " + "%llu -> %llu (%2d)", + (unsigned long long)req->offset + len, + (unsigned long long)req->offset + + req->len - 1, num_req); + req->id = from->msg_id++; + req->len -= len; + req->offset += len; + send_read_request(from, req->id, + req->offset, req->len, + from_handle, from_handle_len); + /* Reduce the request size */ + if (len < buflen) + buflen = MAXIMUM(MIN_READ_SIZE, len); + } + if (max_req > 0) { /* max_req = 0 iff EOF received */ + if (size > 0 && offset > size) { + /* Only one request at a time + * after the expected EOF */ + debug3("Finish at %llu (%2d)", + (unsigned long long)offset, + num_req); + max_req = 1; + } else if (max_req < from->num_requests) { + ++max_req; + } + } + break; + default: + fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", + SSH2_FXP_DATA, type); + } + } + + if (showprogress && size) + stop_progress_meter(); + + /* Drain replies from the server (blocking) */ + debug3_f("waiting for %u replies from destination", num_upload_req); + handle_dest_replies(to, to_path, 1, &num_upload_req, &write_error); + + /* Sanity check */ + if (TAILQ_FIRST(&requests) != NULL) + fatal("Transfer complete, but requests still in queue"); + /* Truncate at 0 length on interrupt or error to avoid holes at dest */ + if (read_error || write_error || interrupted) { + debug("truncating \"%s\" at 0", to_path); + do_close(to, to_handle, to_handle_len); + free(to_handle); + if (send_open(to, to_path, "dest", + SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, + &to_handle, &to_handle_len) != 0) { + error("dest truncate \"%s\" failed", to_path); + to_handle = NULL; + } + } + if (read_error) { + error("read origin \"%s\": %s", from_path, fx2txt(status)); + status = -1; + do_close(from, from_handle, from_handle_len); + if (to_handle != NULL) + do_close(to, to_handle, to_handle_len); + } else if (write_error) { + error("write dest \"%s\": %s", to_path, fx2txt(write_error)); + status = SSH2_FX_FAILURE; + do_close(from, from_handle, from_handle_len); + if (to_handle != NULL) + do_close(to, to_handle, to_handle_len); + } else { + if (do_close(from, from_handle, from_handle_len) != 0 || + interrupted) + status = -1; + else + status = SSH2_FX_OK; + if (to_handle != NULL) { + /* Need to resend utimes after write */ + if (preserve_flag) + do_fsetstat(to, to_handle, to_handle_len, a); + do_close(to, to_handle, to_handle_len); + } + } + sshbuf_free(msg); + free(from_handle); + free(to_handle); + + return status == SSH2_FX_OK ? 0 : -1; +} + +static int +crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, + const char *from_path, const char *to_path, + int depth, Attrib *dirattrib, int preserve_flag, int print_flag, + int follow_link_flag) +{ + int i, ret = 0; + SFTP_DIRENT **dir_entries; + char *filename, *new_from_path = NULL, *new_to_path = NULL; + mode_t mode = 0777; + Attrib curdir; + + debug2_f("crossload dir src \"%s\" to dst \"%s\"", from_path, to_path); + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (dirattrib == NULL && + (dirattrib = do_stat(from, from_path, 1)) == NULL) { + error("stat remote \"%s\" failed", from_path); + return -1; + } + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" is not a directory", from_path); + return -1; + } + if (print_flag && print_flag != SFTP_PROGRESS_ONLY) + mprintf("Retrieving %s\n", from_path); + + curdir = *dirattrib; /* dirattrib will be clobbered */ + curdir.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + curdir.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + if ((curdir.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) == 0) { + debug("Origin did not send permissions for " + "directory \"%s\"", to_path); + curdir.perm = S_IWUSR|S_IXUSR; + curdir.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + } + /* We need to be able to write to the directory while we transfer it */ + mode = curdir.perm & 01777; + curdir.perm = mode | (S_IWUSR|S_IXUSR); + + /* + * sftp lacks a portable status value to match errno EEXIST, + * so if we get a failure back then we must check whether + * the path already existed and is a directory. Ensure we can + * write to the directory we create for the duration of the transfer. + */ + if (do_mkdir(to, to_path, &curdir, 0) != 0) { + if ((dirattrib = do_stat(to, to_path, 0)) == NULL) + return -1; + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" exists but is not a directory", to_path); + return -1; + } + } + curdir.perm = mode; + + if (do_readdir(from, from_path, &dir_entries) == -1) { + error("origin readdir \"%s\" failed", from_path); + return -1; + } + + for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { + free(new_from_path); + free(new_to_path); + + filename = dir_entries[i]->filename; + new_from_path = path_append(from_path, filename); + new_to_path = path_append(to_path, filename); + + if (S_ISDIR(dir_entries[i]->a.perm)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + if (crossload_dir_internal(from, to, + new_from_path, new_to_path, + depth + 1, &(dir_entries[i]->a), preserve_flag, + print_flag, follow_link_flag) == -1) + ret = -1; + } else if (S_ISREG(dir_entries[i]->a.perm) || + (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { + /* + * If this is a symlink then don't send the link's + * Attrib. do_download() will do a FXP_STAT operation + * and get the link target's attributes. + */ + if (do_crossload(from, to, new_from_path, new_to_path, + S_ISLNK(dir_entries[i]->a.perm) ? NULL : + &(dir_entries[i]->a), preserve_flag) == -1) { + error("crossload \"%s\" to \"%s\" failed", + new_from_path, new_to_path); + ret = -1; + } + } else { + logit("origin \"%s\": not a regular file", + new_from_path); + } + } + free(new_to_path); + free(new_from_path); + + do_setstat(to, to_path, &curdir); + + free_sftp_dirents(dir_entries); + + return ret; +} + +int +crossload_dir(struct sftp_conn *from, struct sftp_conn *to, + const char *from_path, const char *to_path, + Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag) +{ + char *from_path_canon; + int ret; + + if ((from_path_canon = do_realpath(from, from_path)) == NULL) { + error("crossload \"%s\": path canonicalization failed", + from_path); + return -1; + } + + ret = crossload_dir_internal(from, to, from_path_canon, to_path, 0, + dirattrib, preserve_flag, print_flag, follow_link_flag); + free(from_path_canon); + return ret; +} + +char * +path_append(const char *p1, const char *p2) +{ + char *ret; + size_t len = strlen(p1) + strlen(p2) + 2; + + ret = xmalloc(len); + strlcpy(ret, p1, len); + if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') + strlcat(ret, "/", len); + strlcat(ret, p2, len); + + return(ret); +} + +char * +make_absolute(char *p, const char *pwd) +{ + char *abs_str; + + /* Derelativise */ + if (p && !path_absolute(p)) { + abs_str = path_append(pwd, p); + free(p); + return(abs_str); + } else + return(p); +} + +int +remote_is_dir(struct sftp_conn *conn, const char *path) +{ + Attrib *a; + + /* XXX: report errors? */ + if ((a = do_stat(conn, path, 1)) == NULL) + return(0); + if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) + return(0); + return(S_ISDIR(a->perm)); +} + + +int +local_is_dir(const char *path) +{ + struct stat sb; + + /* XXX: report errors? */ + if (stat(path, &sb) == -1) + return(0); + + return(S_ISDIR(sb.st_mode)); +} + +/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ +int +globpath_is_dir(const char *pathname) +{ + size_t l = strlen(pathname); + + return l > 0 && pathname[l - 1] == '/'; +} + diff --git a/aosp/external/openssh/sftp-client.h b/aosp/external/openssh/sftp-client.h new file mode 100644 index 0000000000000000000000000000000000000000..282a4c70037d25827141ebe191c1229b3613e37b --- /dev/null +++ b/aosp/external/openssh/sftp-client.h @@ -0,0 +1,201 @@ +/* $OpenBSD: sftp-client.h,v 1.36 2022/03/31 03:07:03 djm Exp $ */ + +/* + * Copyright (c) 2001-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Client side of SSH2 filexfer protocol */ + +#ifndef _SFTP_CLIENT_H +#define _SFTP_CLIENT_H + +#ifdef USE_SYSTEM_GLOB +# include +#else +# include "openbsd-compat/glob.h" +#endif + +typedef struct SFTP_DIRENT SFTP_DIRENT; + +struct SFTP_DIRENT { + char *filename; + char *longname; + Attrib a; +}; + +/* + * Used for statvfs responses on the wire from the server, because the + * server's native format may be larger than the client's. + */ +struct sftp_statvfs { + u_int64_t f_bsize; + u_int64_t f_frsize; + u_int64_t f_blocks; + u_int64_t f_bfree; + u_int64_t f_bavail; + u_int64_t f_files; + u_int64_t f_ffree; + u_int64_t f_favail; + u_int64_t f_fsid; + u_int64_t f_flag; + u_int64_t f_namemax; +}; + +/* Used for limits response on the wire from the server */ +struct sftp_limits { + u_int64_t packet_length; + u_int64_t read_length; + u_int64_t write_length; + u_int64_t open_handles; +}; + +/* print flag values */ +#define SFTP_QUIET 0 /* be quiet during transfers */ +#define SFTP_PRINT 1 /* list files and show progress bar */ +#define SFTP_PROGRESS_ONLY 2 /* progress bar only */ + +/* + * Initialise a SSH filexfer connection. Returns NULL on error or + * a pointer to a initialized sftp_conn struct on success. + */ +struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t); + +u_int sftp_proto_version(struct sftp_conn *); + +/* Query server limits */ +int do_limits(struct sftp_conn *, struct sftp_limits *); + +/* Close file referred to by 'handle' */ +int do_close(struct sftp_conn *, const u_char *, u_int); + +/* Read contents of 'path' to NULL-terminated array 'dir' */ +int do_readdir(struct sftp_conn *, const char *, SFTP_DIRENT ***); + +/* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from do_readdir) */ +void free_sftp_dirents(SFTP_DIRENT **); + +/* Delete file 'path' */ +int do_rm(struct sftp_conn *, const char *); + +/* Create directory 'path' */ +int do_mkdir(struct sftp_conn *, const char *, Attrib *, int); + +/* Remove directory 'path' */ +int do_rmdir(struct sftp_conn *, const char *); + +/* Get file attributes of 'path' (follows symlinks) */ +Attrib *do_stat(struct sftp_conn *, const char *, int); + +/* Get file attributes of 'path' (does not follow symlinks) */ +Attrib *do_lstat(struct sftp_conn *, const char *, int); + +/* Set file attributes of 'path' */ +int do_setstat(struct sftp_conn *, const char *, Attrib *); + +/* Set file attributes of open file 'handle' */ +int do_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *); + +/* Set file attributes of 'path', not following symlinks */ +int do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a); + +/* Canonicalise 'path' - caller must free result */ +char *do_realpath(struct sftp_conn *, const char *); + +/* Canonicalisation with tilde expansion (requires server extension) */ +char *do_expand_path(struct sftp_conn *, const char *); + +/* Returns non-zero if server can tilde-expand paths */ +int can_expand_path(struct sftp_conn *); + +/* Get statistics for filesystem hosting file at "path" */ +int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int); + +/* Rename 'oldpath' to 'newpath' */ +int do_rename(struct sftp_conn *, const char *, const char *, int); + +/* Copy 'oldpath' to 'newpath' */ +int do_copy(struct sftp_conn *, const char *, const char *); + +/* Link 'oldpath' to 'newpath' */ +int do_hardlink(struct sftp_conn *, const char *, const char *); + +/* Rename 'oldpath' to 'newpath' */ +int do_symlink(struct sftp_conn *, const char *, const char *); + +/* Call fsync() on open file 'handle' */ +int do_fsync(struct sftp_conn *conn, u_char *, u_int); + +/* + * Download 'remote_path' to 'local_path'. Preserve permissions and times + * if 'pflag' is set + */ +int do_download(struct sftp_conn *, const char *, const char *, + Attrib *, int, int, int); + +/* + * Recursively download 'remote_directory' to 'local_directory'. Preserve + * times if 'pflag' is set + */ +int download_dir(struct sftp_conn *, const char *, const char *, + Attrib *, int, int, int, int, int); + +/* + * Upload 'local_path' to 'remote_path'. Preserve permissions and times + * if 'pflag' is set + */ +int do_upload(struct sftp_conn *, const char *, const char *, int, int, int); + +/* + * Recursively upload 'local_directory' to 'remote_directory'. Preserve + * times if 'pflag' is set + */ +int upload_dir(struct sftp_conn *, const char *, const char *, int, int, int, + int, int); + +/* + * Download a 'from_path' from the 'from' connection and upload it to + * to 'to' connection at 'to_path'. + */ +int +do_crossload(struct sftp_conn *from, struct sftp_conn *to, + const char *from_path, const char *to_path, + Attrib *a, int preserve_flag); + +/* + * Recursively download a directory from 'from_path' from the 'from' + * connection and upload it to 'to' connection at 'to_path'. + */ +int crossload_dir(struct sftp_conn *from, struct sftp_conn *to, + const char *from_path, const char *to_path, + Attrib *dirattrib, int preserve_flag, int print_flag, + int follow_link_flag); + +/* Concatenate paths, taking care of slashes. Caller must free result. */ +char *path_append(const char *, const char *); + +/* Make absolute path if relative path and CWD is given. Does not modify + * original if the path is already absolute. */ +char *make_absolute(char *, const char *); + +/* Check if remote path is directory */ +int remote_is_dir(struct sftp_conn *conn, const char *path); + +/* Check if local path is directory */ +int local_is_dir(const char *path); + +/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ +int globpath_is_dir(const char *pathname); + +#endif diff --git a/aosp/external/openssh/sftp-common.c b/aosp/external/openssh/sftp-common.c new file mode 100644 index 0000000000000000000000000000000000000000..3ad57673d41ef2b2c44e86bf88f1214943a7a98a --- /dev/null +++ b/aosp/external/openssh/sftp-common.c @@ -0,0 +1,259 @@ +/* $OpenBSD: sftp-common.c,v 1.32 2020/10/18 11:32:02 djm Exp $ */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UTIL_H +#include +#endif + +#include "xmalloc.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "log.h" +#include "misc.h" + +#include "sftp.h" +#include "sftp-common.h" + +/* Clear contents of attributes structure */ +void +attrib_clear(Attrib *a) +{ + a->flags = 0; + a->size = 0; + a->uid = 0; + a->gid = 0; + a->perm = 0; + a->atime = 0; + a->mtime = 0; +} + +/* Convert from struct stat to filexfer attribs */ +void +stat_to_attrib(const struct stat *st, Attrib *a) +{ + attrib_clear(a); + a->flags = 0; + a->flags |= SSH2_FILEXFER_ATTR_SIZE; + a->size = st->st_size; + a->flags |= SSH2_FILEXFER_ATTR_UIDGID; + a->uid = st->st_uid; + a->gid = st->st_gid; + a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a->perm = st->st_mode; + a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME; + a->atime = st->st_atime; + a->mtime = st->st_mtime; +} + +/* Convert from filexfer attribs to struct stat */ +void +attrib_to_stat(const Attrib *a, struct stat *st) +{ + memset(st, 0, sizeof(*st)); + + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + st->st_size = a->size; + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + st->st_uid = a->uid; + st->st_gid = a->gid; + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + st->st_mode = a->perm; + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + st->st_atime = a->atime; + st->st_mtime = a->mtime; + } +} + +/* Decode attributes in buffer */ +int +decode_attrib(struct sshbuf *b, Attrib *a) +{ + int r; + + attrib_clear(a); + if ((r = sshbuf_get_u32(b, &a->flags)) != 0) + return r; + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { + if ((r = sshbuf_get_u64(b, &a->size)) != 0) + return r; + } + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + if ((r = sshbuf_get_u32(b, &a->uid)) != 0 || + (r = sshbuf_get_u32(b, &a->gid)) != 0) + return r; + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + if ((r = sshbuf_get_u32(b, &a->perm)) != 0) + return r; + } + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + if ((r = sshbuf_get_u32(b, &a->atime)) != 0 || + (r = sshbuf_get_u32(b, &a->mtime)) != 0) + return r; + } + /* vendor-specific extensions */ + if (a->flags & SSH2_FILEXFER_ATTR_EXTENDED) { + char *type; + u_char *data; + size_t dlen; + u_int i, count; + + if ((r = sshbuf_get_u32(b, &count)) != 0) + return r; + for (i = 0; i < count; i++) { + if ((r = sshbuf_get_cstring(b, &type, NULL)) != 0 || + (r = sshbuf_get_string(b, &data, &dlen)) != 0) + return r; + debug3("Got file attribute \"%.100s\" len %zu", + type, dlen); + free(type); + free(data); + } + } + return 0; +} + +/* Encode attributes to buffer */ +int +encode_attrib(struct sshbuf *b, const Attrib *a) +{ + int r; + + if ((r = sshbuf_put_u32(b, a->flags)) != 0) + return r; + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { + if ((r = sshbuf_put_u64(b, a->size)) != 0) + return r; + } + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + if ((r = sshbuf_put_u32(b, a->uid)) != 0 || + (r = sshbuf_put_u32(b, a->gid)) != 0) + return r; + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + if ((r = sshbuf_put_u32(b, a->perm)) != 0) + return r; + } + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + if ((r = sshbuf_put_u32(b, a->atime)) != 0 || + (r = sshbuf_put_u32(b, a->mtime)) != 0) + return r; + } + return 0; +} + +/* Convert from SSH2_FX_ status to text error message */ +const char * +fx2txt(int status) +{ + switch (status) { + case SSH2_FX_OK: + return("No error"); + case SSH2_FX_EOF: + return("End of file"); + case SSH2_FX_NO_SUCH_FILE: + return("No such file or directory"); + case SSH2_FX_PERMISSION_DENIED: + return("Permission denied"); + case SSH2_FX_FAILURE: + return("Failure"); + case SSH2_FX_BAD_MESSAGE: + return("Bad message"); + case SSH2_FX_NO_CONNECTION: + return("No connection"); + case SSH2_FX_CONNECTION_LOST: + return("Connection lost"); + case SSH2_FX_OP_UNSUPPORTED: + return("Operation unsupported"); + default: + return("Unknown status"); + } + /* NOTREACHED */ +} + +/* + * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh + */ +char * +ls_file(const char *name, const struct stat *st, int remote, int si_units) +{ + int ulen, glen, sz = 0; + struct tm *ltime = localtime(&st->st_mtime); + const char *user, *group; + char buf[1024], lc[8], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + char sbuf[FMT_SCALED_STRSIZE]; + time_t now; + + strmode(st->st_mode, mode); + if (remote) { + snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); + user = ubuf; + snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); + group = gbuf; + strlcpy(lc, "?", sizeof(lc)); + } else { + user = user_from_uid(st->st_uid, 0); + group = group_from_gid(st->st_gid, 0); + snprintf(lc, sizeof(lc), "%u", (u_int)st->st_nlink); + } + if (ltime != NULL) { + now = time(NULL); + if (now - (365*24*60*60)/2 < st->st_mtime && + now >= st->st_mtime) + sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); + else + sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); + } + if (sz == 0) + tbuf[0] = '\0'; + ulen = MAXIMUM(strlen(user), 8); + glen = MAXIMUM(strlen(group), 8); + if (si_units) { + fmt_scaled((long long)st->st_size, sbuf); + snprintf(buf, sizeof buf, "%s %3s %-*s %-*s %8s %s %s", + mode, lc, ulen, user, glen, group, + sbuf, tbuf, name); + } else { + snprintf(buf, sizeof buf, "%s %3s %-*s %-*s %8llu %s %s", + mode, lc, ulen, user, glen, group, + (unsigned long long)st->st_size, tbuf, name); + } + return xstrdup(buf); +} diff --git a/aosp/external/openssh/sftp-common.h b/aosp/external/openssh/sftp-common.h new file mode 100644 index 0000000000000000000000000000000000000000..2e778a9ca0ba29dff2c0b9f3eaf8413e298f5ec6 --- /dev/null +++ b/aosp/external/openssh/sftp-common.h @@ -0,0 +1,52 @@ +/* $OpenBSD: sftp-common.h,v 1.12 2015/01/14 13:54:13 djm Exp $ */ + +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Maximum packet that we are willing to send/accept */ +#define SFTP_MAX_MSG_LENGTH (256 * 1024) + +struct sshbuf; +typedef struct Attrib Attrib; + +/* File attributes */ +struct Attrib { + u_int32_t flags; + u_int64_t size; + u_int32_t uid; + u_int32_t gid; + u_int32_t perm; + u_int32_t atime; + u_int32_t mtime; +}; + +void attrib_clear(Attrib *); +void stat_to_attrib(const struct stat *, Attrib *); +void attrib_to_stat(const Attrib *, struct stat *); +int decode_attrib(struct sshbuf *, Attrib *); +int encode_attrib(struct sshbuf *, const Attrib *); +char *ls_file(const char *, const struct stat *, int, int); + +const char *fx2txt(int); diff --git a/aosp/external/openssh/sftp-glob.c b/aosp/external/openssh/sftp-glob.c new file mode 100644 index 0000000000000000000000000000000000000000..764e99552d42615d88a0d597e48eb7f6b83240ef --- /dev/null +++ b/aosp/external/openssh/sftp-glob.c @@ -0,0 +1,150 @@ +/* $OpenBSD: sftp-glob.c,v 1.30 2022/02/25 09:46:24 dtucker Exp $ */ +/* + * Copyright (c) 2001-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" + +int remote_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); + +struct SFTP_OPENDIR { + SFTP_DIRENT **dir; + int offset; +}; + +static struct { + struct sftp_conn *conn; +} cur; + +static void * +fudge_opendir(const char *path) +{ + struct SFTP_OPENDIR *r; + + r = xcalloc(1, sizeof(*r)); + + if (do_readdir(cur.conn, path, &r->dir)) { + free(r); + return(NULL); + } + + r->offset = 0; + + return((void *)r); +} + +static struct dirent * +fudge_readdir(struct SFTP_OPENDIR *od) +{ + /* Solaris needs sizeof(dirent) + path length (see below) */ + static char buf[sizeof(struct dirent) + MAXPATHLEN]; + struct dirent *ret = (struct dirent *)buf; +#ifdef __GNU_LIBRARY__ + static int inum = 1; +#endif /* __GNU_LIBRARY__ */ + + if (od->dir[od->offset] == NULL) + return(NULL); + + memset(buf, 0, sizeof(buf)); + + /* + * Solaris defines dirent->d_name as a one byte array and expects + * you to hack around it. + */ +#ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME + strlcpy(ret->d_name, od->dir[od->offset++]->filename, MAXPATHLEN); +#else + strlcpy(ret->d_name, od->dir[od->offset++]->filename, + sizeof(ret->d_name)); +#endif +#ifdef __GNU_LIBRARY__ + /* + * Idiot glibc uses extensions to struct dirent for readdir with + * ALTDIRFUNCs. Not that this is documented anywhere but the + * source... Fake an inode number to appease it. + */ + ret->d_ino = inum++; + if (!inum) + inum = 1; +#endif /* __GNU_LIBRARY__ */ + + return(ret); +} + +static void +fudge_closedir(struct SFTP_OPENDIR *od) +{ + free_sftp_dirents(od->dir); + free(od); +} + +static int +fudge_lstat(const char *path, struct stat *st) +{ + Attrib *a; + + if (!(a = do_lstat(cur.conn, path, 1))) + return(-1); + + attrib_to_stat(a, st); + + return(0); +} + +static int +fudge_stat(const char *path, struct stat *st) +{ + Attrib *a; + + if (!(a = do_stat(cur.conn, path, 1))) + return(-1); + + attrib_to_stat(a, st); + + return(0); +} + +int +remote_glob(struct sftp_conn *conn, const char *pattern, int flags, + int (*errfunc)(const char *, int), glob_t *pglob) +{ + pglob->gl_opendir = fudge_opendir; + pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir; + pglob->gl_closedir = (void (*)(void *))fudge_closedir; + pglob->gl_lstat = fudge_lstat; + pglob->gl_stat = fudge_stat; + + memset(&cur, 0, sizeof(cur)); + cur.conn = conn; + + return(glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob)); +} diff --git a/aosp/external/openssh/sftp-realpath.c b/aosp/external/openssh/sftp-realpath.c new file mode 100644 index 0000000000000000000000000000000000000000..2ec779d8f901043081c79d4a02d22f86f13a2ada --- /dev/null +++ b/aosp/external/openssh/sftp-realpath.c @@ -0,0 +1,225 @@ +/* $OpenBSD: sftp-realpath.c,v 1.2 2021/09/02 21:03:54 deraadt Exp $ */ +/* + * Copyright (c) 2003 Constantin S. Svintsoff + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef SYMLOOP_MAX +# define SYMLOOP_MAX 32 +#endif + +/* XXX rewrite sftp-server to use POSIX realpath and remove this hack */ + +char *sftp_realpath(const char *path, char *resolved); + +/* + * char *realpath(const char *path, char resolved[PATH_MAX]); + * + * Find the real name of path, by removing all ".", ".." and symlink + * components. Returns (resolved) on success, or (NULL) on failure, + * in which case the path which caused trouble is left in (resolved). + */ +char * +sftp_realpath(const char *path, char *resolved) +{ + struct stat sb; + char *p, *q, *s; + size_t left_len, resolved_len; + unsigned symlinks; + int serrno, slen, mem_allocated; + char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; + + if (path[0] == '\0') { + errno = ENOENT; + return (NULL); + } + + serrno = errno; + + if (resolved == NULL) { + resolved = malloc(PATH_MAX); + if (resolved == NULL) + return (NULL); + mem_allocated = 1; + } else + mem_allocated = 0; + + symlinks = 0; + if (path[0] == '/') { + resolved[0] = '/'; + resolved[1] = '\0'; + if (path[1] == '\0') + return (resolved); + resolved_len = 1; + left_len = strlcpy(left, path + 1, sizeof(left)); + } else { + if (getcwd(resolved, PATH_MAX) == NULL) { + if (mem_allocated) + free(resolved); + else + strlcpy(resolved, ".", PATH_MAX); + return (NULL); + } + resolved_len = strlen(resolved); + left_len = strlcpy(left, path, sizeof(left)); + } + if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + goto err; + } + + /* + * Iterate over path components in `left'. + */ + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + s = p ? p : left + left_len; + if (s - left >= (ptrdiff_t)sizeof(next_token)) { + errno = ENAMETOOLONG; + goto err; + } + memcpy(next_token, left, s - left); + next_token[s - left] = '\0'; + left_len -= s - left; + if (p != NULL) + memmove(left, s + 1, left_len + 1); + if (resolved[resolved_len - 1] != '/') { + if (resolved_len + 1 >= PATH_MAX) { + errno = ENAMETOOLONG; + goto err; + } + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; + } + if (next_token[0] == '\0') + continue; + else if (strcmp(next_token, ".") == 0) + continue; + else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ + if (resolved_len > 1) { + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + continue; + } + + /* + * Append the next path component and lstat() it. If + * lstat() fails we still can return successfully if + * there are no more path components left. + */ + resolved_len = strlcat(resolved, next_token, PATH_MAX); + if (resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + goto err; + } + if (lstat(resolved, &sb) != 0) { + if (errno == ENOENT && p == NULL) { + errno = serrno; + return (resolved); + } + goto err; + } + if (S_ISLNK(sb.st_mode)) { + if (symlinks++ > SYMLOOP_MAX) { + errno = ELOOP; + goto err; + } + slen = readlink(resolved, symlink, sizeof(symlink) - 1); + if (slen < 0) + goto err; + symlink[slen] = '\0'; + if (symlink[0] == '/') { + resolved[1] = 0; + resolved_len = 1; + } else if (resolved_len > 1) { + /* Strip the last path component. */ + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + + /* + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. + */ + if (p != NULL) { + if (symlink[slen - 1] != '/') { + if (slen + 1 >= + (ptrdiff_t)sizeof(symlink)) { + errno = ENAMETOOLONG; + goto err; + } + symlink[slen] = '/'; + symlink[slen + 1] = 0; + } + left_len = strlcat(symlink, left, sizeof(symlink)); + if (left_len >= sizeof(symlink)) { + errno = ENAMETOOLONG; + goto err; + } + } + left_len = strlcpy(left, symlink, sizeof(left)); + } + } + + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; + return (resolved); + +err: + if (mem_allocated) + free(resolved); + return (NULL); +} diff --git a/aosp/external/openssh/sftp-server-main.c b/aosp/external/openssh/sftp-server-main.c new file mode 100644 index 0000000000000000000000000000000000000000..06566d36ed8400d9a13470d055afabbd5abda1a7 --- /dev/null +++ b/aosp/external/openssh/sftp-server-main.c @@ -0,0 +1,54 @@ +/* $OpenBSD: sftp-server-main.c,v 1.6 2019/06/06 05:13:13 otto Exp $ */ +/* + * Copyright (c) 2008 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include + +#include "log.h" +#include "sftp.h" +#include "misc.h" +#include "xmalloc.h" + +void +cleanup_exit(int i) +{ + sftp_server_cleanup_exit(i); +} + +int +main(int argc, char **argv) +{ + struct passwd *user_pw; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + seed_rng(); + + if ((user_pw = getpwuid(getuid())) == NULL) { + fprintf(stderr, "No user found for uid %lu\n", + (u_long)getuid()); + return 1; + } + + return (sftp_server_main(argc, argv, user_pw)); +} diff --git a/aosp/external/openssh/sftp-server.8 b/aosp/external/openssh/sftp-server.8 new file mode 100644 index 0000000000000000000000000000000000000000..5311bf9299fa96072793981cbe5cc031fe446772 --- /dev/null +++ b/aosp/external/openssh/sftp-server.8 @@ -0,0 +1,170 @@ +.\" $OpenBSD: sftp-server.8,v 1.31 2021/07/27 14:14:25 jmc Exp $ +.\" +.\" Copyright (c) 2000 Markus Friedl. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: July 27 2021 $ +.Dt SFTP-SERVER 8 +.Os +.Sh NAME +.Nm sftp-server +.Nd OpenSSH SFTP server subsystem +.Sh SYNOPSIS +.Nm sftp-server +.Bk -words +.Op Fl ehR +.Op Fl d Ar start_directory +.Op Fl f Ar log_facility +.Op Fl l Ar log_level +.Op Fl P Ar denied_requests +.Op Fl p Ar allowed_requests +.Op Fl u Ar umask +.Ek +.Nm +.Fl Q Ar protocol_feature +.Sh DESCRIPTION +.Nm +is a program that speaks the server side of SFTP protocol +to stdout and expects client requests from stdin. +.Nm +is not intended to be called directly, but from +.Xr sshd 8 +using the +.Cm Subsystem +option. +.Pp +Command-line flags to +.Nm +should be specified in the +.Cm Subsystem +declaration. +See +.Xr sshd_config 5 +for more information. +.Pp +Valid options are: +.Bl -tag -width Ds +.It Fl d Ar start_directory +Specifies an alternate starting directory for users. +The pathname may contain the following tokens that are expanded at runtime: +%% is replaced by a literal '%', +%d is replaced by the home directory of the user being authenticated, +and %u is replaced by the username of that user. +The default is to use the user's home directory. +This option is useful in conjunction with the +.Xr sshd_config 5 +.Cm ChrootDirectory +option. +.It Fl e +Causes +.Nm +to print logging information to stderr instead of syslog for debugging. +.It Fl f Ar log_facility +Specifies the facility code that is used when logging messages from +.Nm . +The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, +LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. +The default is AUTH. +.It Fl h +Displays +.Nm +usage information. +.It Fl l Ar log_level +Specifies which messages will be logged by +.Nm . +The possible values are: +QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. +INFO and VERBOSE log transactions that +.Nm +performs on behalf of the client. +DEBUG and DEBUG1 are equivalent. +DEBUG2 and DEBUG3 each specify higher levels of debugging output. +The default is ERROR. +.It Fl P Ar denied_requests +Specifies a comma-separated list of SFTP protocol requests that are banned by +the server. +.Nm +will reply to any denied request with a failure. +The +.Fl Q +flag can be used to determine the supported request types. +If both denied and allowed lists are specified, then the denied list is +applied before the allowed list. +.It Fl p Ar allowed_requests +Specifies a comma-separated list of SFTP protocol requests that are permitted +by the server. +All request types that are not on the allowed list will be logged and replied +to with a failure message. +.Pp +Care must be taken when using this feature to ensure that requests made +implicitly by SFTP clients are permitted. +.It Fl Q Ar protocol_feature +Queries protocol features supported by +.Nm . +At present the only feature that may be queried is +.Dq requests , +which may be used to deny or allow specific requests (flags +.Fl P +and +.Fl p +respectively). +.It Fl R +Places this instance of +.Nm +into a read-only mode. +Attempts to open files for writing, as well as other operations that change +the state of the filesystem, will be denied. +.It Fl u Ar umask +Sets an explicit +.Xr umask 2 +to be applied to newly-created files and directories, instead of the +user's default mask. +.El +.Pp +On some systems, +.Nm +must be able to access +.Pa /dev/log +for logging to work, and use of +.Nm +in a chroot configuration therefore requires that +.Xr syslogd 8 +establish a logging socket inside the chroot directory. +.Sh SEE ALSO +.Xr sftp 1 , +.Xr ssh 1 , +.Xr sshd_config 5 , +.Xr sshd 8 +.Rs +.%A T. Ylonen +.%A S. Lehtinen +.%T "SSH File Transfer Protocol" +.%N draft-ietf-secsh-filexfer-02.txt +.%D October 2001 +.%O work in progress material +.Re +.Sh HISTORY +.Nm +first appeared in +.Ox 2.8 . +.Sh AUTHORS +.An Markus Friedl Aq Mt markus@openbsd.org diff --git a/aosp/external/openssh/sftp-server.c b/aosp/external/openssh/sftp-server.c new file mode 100644 index 0000000000000000000000000000000000000000..3dd19d4c81db19d262e5456025fa99dfb869e467 --- /dev/null +++ b/aosp/external/openssh/sftp-server.c @@ -0,0 +1,2018 @@ +/* $OpenBSD: sftp-server.c,v 1.140 2022/03/31 03:05:49 djm Exp $ */ +/* + * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "atomicio.h" +#include "xmalloc.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "log.h" +#include "misc.h" +#include "match.h" +#include "uidswap.h" + +#include "sftp.h" +#include "sftp-common.h" + +char *sftp_realpath(const char *, char *); /* sftp-realpath.c */ + +/* Maximum data read that we are willing to accept */ +#define SFTP_MAX_READ_LENGTH (SFTP_MAX_MSG_LENGTH - 1024) + +/* Our verbosity */ +static LogLevel log_level = SYSLOG_LEVEL_ERROR; + +/* Our client */ +static struct passwd *pw = NULL; +static char *client_addr = NULL; + +/* input and output queue */ +struct sshbuf *iqueue; +struct sshbuf *oqueue; + +/* Version of client */ +static u_int version; + +/* SSH2_FXP_INIT received */ +static int init_done; + +/* Disable writes */ +static int readonly; + +/* Requests that are allowed/denied */ +static char *request_allowlist, *request_denylist; + +/* portable attributes, etc. */ +typedef struct Stat Stat; + +struct Stat { + char *name; + char *long_name; + Attrib attrib; +}; + +/* Packet handlers */ +static void process_open(u_int32_t id); +static void process_close(u_int32_t id); +static void process_read(u_int32_t id); +static void process_write(u_int32_t id); +static void process_stat(u_int32_t id); +static void process_lstat(u_int32_t id); +static void process_fstat(u_int32_t id); +static void process_setstat(u_int32_t id); +static void process_fsetstat(u_int32_t id); +static void process_opendir(u_int32_t id); +static void process_readdir(u_int32_t id); +static void process_remove(u_int32_t id); +static void process_mkdir(u_int32_t id); +static void process_rmdir(u_int32_t id); +static void process_realpath(u_int32_t id); +static void process_rename(u_int32_t id); +static void process_readlink(u_int32_t id); +static void process_symlink(u_int32_t id); +static void process_extended_posix_rename(u_int32_t id); +static void process_extended_statvfs(u_int32_t id); +static void process_extended_fstatvfs(u_int32_t id); +static void process_extended_hardlink(u_int32_t id); +static void process_extended_fsync(u_int32_t id); +static void process_extended_lsetstat(u_int32_t id); +static void process_extended_limits(u_int32_t id); +static void process_extended_expand(u_int32_t id); +static void process_extended_copy_data(u_int32_t id); +static void process_extended(u_int32_t id); + +struct sftp_handler { + const char *name; /* user-visible name for fine-grained perms */ + const char *ext_name; /* extended request name */ + u_int type; /* packet type, for non extended packets */ + void (*handler)(u_int32_t); + int does_write; /* if nonzero, banned for readonly mode */ +}; + +static const struct sftp_handler handlers[] = { + /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ + { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, + { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, + { "read", NULL, SSH2_FXP_READ, process_read, 0 }, + { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, + { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, + { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, + { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, + { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, + { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, + { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, + { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, + { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, + { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, + { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, + { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, + { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, + { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, + { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, + { NULL, NULL, 0, NULL, 0 } +}; + +/* SSH2_FXP_EXTENDED submessages */ +static const struct sftp_handler extended_handlers[] = { + { "posix-rename", "posix-rename@openssh.com", 0, + process_extended_posix_rename, 1 }, + { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, + { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, + { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, + { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, + { "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 }, + { "limits", "limits@openssh.com", 0, process_extended_limits, 0 }, + { "expand-path", "expand-path@openssh.com", 0, + process_extended_expand, 0 }, + { "copy-data", "copy-data", 0, process_extended_copy_data, 1 }, + { NULL, NULL, 0, NULL, 0 } +}; + +static const struct sftp_handler * +extended_handler_byname(const char *name) +{ + int i; + + for (i = 0; extended_handlers[i].handler != NULL; i++) { + if (strcmp(name, extended_handlers[i].ext_name) == 0) + return &extended_handlers[i]; + } + return NULL; +} + +static int +request_permitted(const struct sftp_handler *h) +{ + char *result; + + if (readonly && h->does_write) { + verbose("Refusing %s request in read-only mode", h->name); + return 0; + } + if (request_denylist != NULL && + ((result = match_list(h->name, request_denylist, NULL))) != NULL) { + free(result); + verbose("Refusing denylisted %s request", h->name); + return 0; + } + if (request_allowlist != NULL && + ((result = match_list(h->name, request_allowlist, NULL))) != NULL) { + free(result); + debug2("Permitting allowlisted %s request", h->name); + return 1; + } + if (request_allowlist != NULL) { + verbose("Refusing non-allowlisted %s request", h->name); + return 0; + } + return 1; +} + +static int +errno_to_portable(int unixerrno) +{ + int ret = 0; + + switch (unixerrno) { + case 0: + ret = SSH2_FX_OK; + break; + case ENOENT: + case ENOTDIR: + case EBADF: + case ELOOP: + ret = SSH2_FX_NO_SUCH_FILE; + break; + case EPERM: + case EACCES: + case EFAULT: + ret = SSH2_FX_PERMISSION_DENIED; + break; + case ENAMETOOLONG: + case EINVAL: + ret = SSH2_FX_BAD_MESSAGE; + break; + case ENOSYS: + ret = SSH2_FX_OP_UNSUPPORTED; + break; + default: + ret = SSH2_FX_FAILURE; + break; + } + return ret; +} + +static int +flags_from_portable(int pflags) +{ + int flags = 0; + + if ((pflags & SSH2_FXF_READ) && + (pflags & SSH2_FXF_WRITE)) { + flags = O_RDWR; + } else if (pflags & SSH2_FXF_READ) { + flags = O_RDONLY; + } else if (pflags & SSH2_FXF_WRITE) { + flags = O_WRONLY; + } + if (pflags & SSH2_FXF_APPEND) + flags |= O_APPEND; + if (pflags & SSH2_FXF_CREAT) + flags |= O_CREAT; + if (pflags & SSH2_FXF_TRUNC) + flags |= O_TRUNC; + if (pflags & SSH2_FXF_EXCL) + flags |= O_EXCL; + return flags; +} + +static const char * +string_from_portable(int pflags) +{ + static char ret[128]; + + *ret = '\0'; + +#define PAPPEND(str) { \ + if (*ret != '\0') \ + strlcat(ret, ",", sizeof(ret)); \ + strlcat(ret, str, sizeof(ret)); \ + } + + if (pflags & SSH2_FXF_READ) + PAPPEND("READ") + if (pflags & SSH2_FXF_WRITE) + PAPPEND("WRITE") + if (pflags & SSH2_FXF_APPEND) + PAPPEND("APPEND") + if (pflags & SSH2_FXF_CREAT) + PAPPEND("CREATE") + if (pflags & SSH2_FXF_TRUNC) + PAPPEND("TRUNCATE") + if (pflags & SSH2_FXF_EXCL) + PAPPEND("EXCL") + + return ret; +} + +/* handle handles */ + +typedef struct Handle Handle; +struct Handle { + int use; + DIR *dirp; + int fd; + int flags; + char *name; + u_int64_t bytes_read, bytes_write; + int next_unused; +}; + +enum { + HANDLE_UNUSED, + HANDLE_DIR, + HANDLE_FILE +}; + +static Handle *handles = NULL; +static u_int num_handles = 0; +static int first_unused_handle = -1; + +static void handle_unused(int i) +{ + handles[i].use = HANDLE_UNUSED; + handles[i].next_unused = first_unused_handle; + first_unused_handle = i; +} + +static int +handle_new(int use, const char *name, int fd, int flags, DIR *dirp) +{ + int i; + + if (first_unused_handle == -1) { + if (num_handles + 1 <= num_handles) + return -1; + num_handles++; + handles = xreallocarray(handles, num_handles, sizeof(Handle)); + handle_unused(num_handles - 1); + } + + i = first_unused_handle; + first_unused_handle = handles[i].next_unused; + + handles[i].use = use; + handles[i].dirp = dirp; + handles[i].fd = fd; + handles[i].flags = flags; + handles[i].name = xstrdup(name); + handles[i].bytes_read = handles[i].bytes_write = 0; + + return i; +} + +static int +handle_is_ok(int i, int type) +{ + return i >= 0 && (u_int)i < num_handles && handles[i].use == type; +} + +static int +handle_to_string(int handle, u_char **stringp, int *hlenp) +{ + if (stringp == NULL || hlenp == NULL) + return -1; + *stringp = xmalloc(sizeof(int32_t)); + put_u32(*stringp, handle); + *hlenp = sizeof(int32_t); + return 0; +} + +static int +handle_from_string(const u_char *handle, u_int hlen) +{ + int val; + + if (hlen != sizeof(int32_t)) + return -1; + val = get_u32(handle); + if (handle_is_ok(val, HANDLE_FILE) || + handle_is_ok(val, HANDLE_DIR)) + return val; + return -1; +} + +static char * +handle_to_name(int handle) +{ + if (handle_is_ok(handle, HANDLE_DIR)|| + handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].name; + return NULL; +} + +static DIR * +handle_to_dir(int handle) +{ + if (handle_is_ok(handle, HANDLE_DIR)) + return handles[handle].dirp; + return NULL; +} + +static int +handle_to_fd(int handle) +{ + if (handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].fd; + return -1; +} + +static int +handle_to_flags(int handle) +{ + if (handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].flags; + return 0; +} + +static void +handle_update_read(int handle, ssize_t bytes) +{ + if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) + handles[handle].bytes_read += bytes; +} + +static void +handle_update_write(int handle, ssize_t bytes) +{ + if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) + handles[handle].bytes_write += bytes; +} + +static u_int64_t +handle_bytes_read(int handle) +{ + if (handle_is_ok(handle, HANDLE_FILE)) + return (handles[handle].bytes_read); + return 0; +} + +static u_int64_t +handle_bytes_write(int handle) +{ + if (handle_is_ok(handle, HANDLE_FILE)) + return (handles[handle].bytes_write); + return 0; +} + +static int +handle_close(int handle) +{ + int ret = -1; + + if (handle_is_ok(handle, HANDLE_FILE)) { + ret = close(handles[handle].fd); + free(handles[handle].name); + handle_unused(handle); + } else if (handle_is_ok(handle, HANDLE_DIR)) { + ret = closedir(handles[handle].dirp); + free(handles[handle].name); + handle_unused(handle); + } else { + errno = ENOENT; + } + return ret; +} + +static void +handle_log_close(int handle, char *emsg) +{ + if (handle_is_ok(handle, HANDLE_FILE)) { + logit("%s%sclose \"%s\" bytes read %llu written %llu", + emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", + handle_to_name(handle), + (unsigned long long)handle_bytes_read(handle), + (unsigned long long)handle_bytes_write(handle)); + } else { + logit("%s%sclosedir \"%s\"", + emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", + handle_to_name(handle)); + } +} + +static void +handle_log_exit(void) +{ + u_int i; + + for (i = 0; i < num_handles; i++) + if (handles[i].use != HANDLE_UNUSED) + handle_log_close(i, "forced"); +} + +static int +get_handle(struct sshbuf *queue, int *hp) +{ + u_char *handle; + int r; + size_t hlen; + + *hp = -1; + if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0) + return r; + if (hlen < 256) + *hp = handle_from_string(handle, hlen); + free(handle); + return 0; +} + +/* send replies */ + +static void +send_msg(struct sshbuf *m) +{ + int r; + + if ((r = sshbuf_put_stringb(oqueue, m)) != 0) + fatal_fr(r, "enqueue"); + sshbuf_reset(m); +} + +static const char * +status_to_message(u_int32_t status) +{ + static const char * const status_messages[] = { + "Success", /* SSH_FX_OK */ + "End of file", /* SSH_FX_EOF */ + "No such file", /* SSH_FX_NO_SUCH_FILE */ + "Permission denied", /* SSH_FX_PERMISSION_DENIED */ + "Failure", /* SSH_FX_FAILURE */ + "Bad message", /* SSH_FX_BAD_MESSAGE */ + "No connection", /* SSH_FX_NO_CONNECTION */ + "Connection lost", /* SSH_FX_CONNECTION_LOST */ + "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ + "Unknown error" /* Others */ + }; + return (status_messages[MINIMUM(status,SSH2_FX_MAX)]); +} + +static void +send_status_errmsg(u_int32_t id, u_int32_t status, const char *errmsg) +{ + struct sshbuf *msg; + int r; + + debug3("request %u: sent status %u", id, status); + if (log_level > SYSLOG_LEVEL_VERBOSE || + (status != SSH2_FX_OK && status != SSH2_FX_EOF)) + logit("sent status %s", status_to_message(status)); + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_u32(msg, status)) != 0) + fatal_fr(r, "compose"); + if (version >= 3) { + if ((r = sshbuf_put_cstring(msg, errmsg == NULL ? + status_to_message(status) : errmsg)) != 0 || + (r = sshbuf_put_cstring(msg, "")) != 0) + fatal_fr(r, "compose message"); + } + send_msg(msg); + sshbuf_free(msg); +} + +static void +send_status(u_int32_t id, u_int32_t status) +{ + send_status_errmsg(id, status, NULL); +} + +static void +send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) +{ + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, type)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_string(msg, data, dlen)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_free(msg); +} + +static void +send_data(u_int32_t id, const u_char *data, int dlen) +{ + debug("request %u: sent data len %d", id, dlen); + send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); +} + +static void +send_handle(u_int32_t id, int handle) +{ + u_char *string; + int hlen; + + handle_to_string(handle, &string, &hlen); + debug("request %u: sent handle handle %d", id, handle); + send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); + free(string); +} + +static void +send_names(u_int32_t id, int count, const Stat *stats) +{ + struct sshbuf *msg; + int i, r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_u32(msg, count)) != 0) + fatal_fr(r, "compose"); + debug("request %u: sent names count %d", id, count); + for (i = 0; i < count; i++) { + if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 || + (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 || + (r = encode_attrib(msg, &stats[i].attrib)) != 0) + fatal_fr(r, "compose filenames/attrib"); + } + send_msg(msg); + sshbuf_free(msg); +} + +static void +send_attrib(u_int32_t id, const Attrib *a) +{ + struct sshbuf *msg; + int r; + + debug("request %u: sent attrib have 0x%x", id, a->flags); + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = encode_attrib(msg, a)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_free(msg); +} + +static void +send_statvfs(u_int32_t id, struct statvfs *st) +{ + struct sshbuf *msg; + u_int64_t flag; + int r; + + flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; + flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 || + (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 || + (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 || + (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 || + (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 || + (r = sshbuf_put_u64(msg, st->f_files)) != 0 || + (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 || + (r = sshbuf_put_u64(msg, st->f_favail)) != 0 || + (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 || + (r = sshbuf_put_u64(msg, flag)) != 0 || + (r = sshbuf_put_u64(msg, st->f_namemax)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_free(msg); +} + +/* + * Prepare SSH2_FXP_VERSION extension advertisement for a single extension. + * The extension is checked for permission prior to advertisement. + */ +static int +compose_extension(struct sshbuf *msg, const char *name, const char *ver) +{ + int r; + const struct sftp_handler *exthnd; + + if ((exthnd = extended_handler_byname(name)) == NULL) + fatal_f("internal error: no handler for %s", name); + if (!request_permitted(exthnd)) { + debug2_f("refusing to advertise disallowed extension %s", name); + return 0; + } + if ((r = sshbuf_put_cstring(msg, name)) != 0 || + (r = sshbuf_put_cstring(msg, ver)) != 0) + fatal_fr(r, "compose %s", name); + return 0; +} + +/* parse incoming */ + +static void +process_init(void) +{ + struct sshbuf *msg; + int r; + + if ((r = sshbuf_get_u32(iqueue, &version)) != 0) + fatal_fr(r, "parse"); + verbose("received client version %u", version); + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 || + (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) + fatal_fr(r, "compose"); + + /* extension advertisements */ + compose_extension(msg, "posix-rename@openssh.com", "1"); + compose_extension(msg, "statvfs@openssh.com", "2"); + compose_extension(msg, "fstatvfs@openssh.com", "2"); + compose_extension(msg, "hardlink@openssh.com", "1"); + compose_extension(msg, "fsync@openssh.com", "1"); + compose_extension(msg, "lsetstat@openssh.com", "1"); + compose_extension(msg, "limits@openssh.com", "1"); + compose_extension(msg, "expand-path@openssh.com", "1"); + compose_extension(msg, "copy-data", "1"); + + send_msg(msg); + sshbuf_free(msg); +} + +static void +process_open(u_int32_t id) +{ + u_int32_t pflags; + Attrib a; + char *name; + int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ + (r = decode_attrib(iqueue, &a)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: open flags %d", id, pflags); + flags = flags_from_portable(pflags); + mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; + logit("open \"%s\" flags %s mode 0%o", + name, string_from_portable(pflags), mode); + if (readonly && + ((flags & O_ACCMODE) != O_RDONLY || + (flags & (O_CREAT|O_TRUNC)) != 0)) { + verbose("Refusing open request in read-only mode"); + status = SSH2_FX_PERMISSION_DENIED; + } else { + fd = open(name, flags, mode); + if (fd == -1) { + status = errno_to_portable(errno); + } else { + handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); + if (handle < 0) { + close(fd); + } else { + send_handle(id, handle); + status = SSH2_FX_OK; + } + } + } + if (status != SSH2_FX_OK) + send_status(id, status); + free(name); +} + +static void +process_close(u_int32_t id) +{ + int r, handle, ret, status = SSH2_FX_FAILURE; + + if ((r = get_handle(iqueue, &handle)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: close handle %u", id, handle); + handle_log_close(handle, NULL); + ret = handle_close(handle); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); +} + +static void +process_read(u_int32_t id) +{ + static u_char *buf; + static size_t buflen; + u_int32_t len; + int r, handle, fd, ret, status = SSH2_FX_FAILURE; + u_int64_t off; + + if ((r = get_handle(iqueue, &handle)) != 0 || + (r = sshbuf_get_u64(iqueue, &off)) != 0 || + (r = sshbuf_get_u32(iqueue, &len)) != 0) + fatal_fr(r, "parse"); + + debug("request %u: read \"%s\" (handle %d) off %llu len %u", + id, handle_to_name(handle), handle, (unsigned long long)off, len); + if ((fd = handle_to_fd(handle)) == -1) + goto out; + if (len > SFTP_MAX_READ_LENGTH) { + debug2("read change len %u to %u", len, SFTP_MAX_READ_LENGTH); + len = SFTP_MAX_READ_LENGTH; + } + if (len > buflen) { + debug3_f("allocate %zu => %u", buflen, len); + if ((buf = realloc(NULL, len)) == NULL) + fatal_f("realloc failed"); + buflen = len; + } + if (lseek(fd, off, SEEK_SET) == -1) { + status = errno_to_portable(errno); + error_f("seek \"%.100s\": %s", handle_to_name(handle), + strerror(errno)); + goto out; + } + if (len == 0) { + /* weird, but not strictly disallowed */ + ret = 0; + } else if ((ret = read(fd, buf, len)) == -1) { + status = errno_to_portable(errno); + error_f("read \"%.100s\": %s", handle_to_name(handle), + strerror(errno)); + goto out; + } else if (ret == 0) { + status = SSH2_FX_EOF; + goto out; + } + send_data(id, buf, ret); + handle_update_read(handle, ret); + /* success */ + status = SSH2_FX_OK; + out: + if (status != SSH2_FX_OK) + send_status(id, status); +} + +static void +process_write(u_int32_t id) +{ + u_int64_t off; + size_t len; + int r, handle, fd, ret, status; + u_char *data; + + if ((r = get_handle(iqueue, &handle)) != 0 || + (r = sshbuf_get_u64(iqueue, &off)) != 0 || + (r = sshbuf_get_string(iqueue, &data, &len)) != 0) + fatal_fr(r, "parse"); + + debug("request %u: write \"%s\" (handle %d) off %llu len %zu", + id, handle_to_name(handle), handle, (unsigned long long)off, len); + fd = handle_to_fd(handle); + + if (fd < 0) + status = SSH2_FX_FAILURE; + else { + if (!(handle_to_flags(handle) & O_APPEND) && + lseek(fd, off, SEEK_SET) == -1) { + status = errno_to_portable(errno); + error_f("seek \"%.100s\": %s", handle_to_name(handle), + strerror(errno)); + } else { +/* XXX ATOMICIO ? */ + ret = write(fd, data, len); + if (ret == -1) { + status = errno_to_portable(errno); + error_f("write \"%.100s\": %s", + handle_to_name(handle), strerror(errno)); + } else if ((size_t)ret == len) { + status = SSH2_FX_OK; + handle_update_write(handle, ret); + } else { + debug2_f("nothing at all written"); + status = SSH2_FX_FAILURE; + } + } + } + send_status(id, status); + free(data); +} + +static void +process_do_stat(u_int32_t id, int do_lstat) +{ + Attrib a; + struct stat st; + char *name; + int r, status = SSH2_FX_FAILURE; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: %sstat", id, do_lstat ? "l" : ""); + verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); + r = do_lstat ? lstat(name, &st) : stat(name, &st); + if (r == -1) { + status = errno_to_portable(errno); + } else { + stat_to_attrib(&st, &a); + send_attrib(id, &a); + status = SSH2_FX_OK; + } + if (status != SSH2_FX_OK) + send_status(id, status); + free(name); +} + +static void +process_stat(u_int32_t id) +{ + process_do_stat(id, 0); +} + +static void +process_lstat(u_int32_t id) +{ + process_do_stat(id, 1); +} + +static void +process_fstat(u_int32_t id) +{ + Attrib a; + struct stat st; + int fd, r, handle, status = SSH2_FX_FAILURE; + + if ((r = get_handle(iqueue, &handle)) != 0) + fatal_fr(r, "parse"); + debug("request %u: fstat \"%s\" (handle %u)", + id, handle_to_name(handle), handle); + fd = handle_to_fd(handle); + if (fd >= 0) { + r = fstat(fd, &st); + if (r == -1) { + status = errno_to_portable(errno); + } else { + stat_to_attrib(&st, &a); + send_attrib(id, &a); + status = SSH2_FX_OK; + } + } + if (status != SSH2_FX_OK) + send_status(id, status); +} + +static struct timeval * +attrib_to_tv(const Attrib *a) +{ + static struct timeval tv[2]; + + tv[0].tv_sec = a->atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = a->mtime; + tv[1].tv_usec = 0; + return tv; +} + +static struct timespec * +attrib_to_ts(const Attrib *a) +{ + static struct timespec ts[2]; + + ts[0].tv_sec = a->atime; + ts[0].tv_nsec = 0; + ts[1].tv_sec = a->mtime; + ts[1].tv_nsec = 0; + return ts; +} + +static void +process_setstat(u_int32_t id) +{ + Attrib a; + char *name; + int r, status = SSH2_FX_OK; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = decode_attrib(iqueue, &a)) != 0) + fatal_fr(r, "parse"); + + debug("request %u: setstat name \"%s\"", id, name); + if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { + logit("set \"%s\" size %llu", + name, (unsigned long long)a.size); + r = truncate(name, a.size); + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + logit("set \"%s\" mode %04o", name, a.perm); + r = chmod(name, a.perm & 07777); + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + char buf[64]; + time_t t = a.mtime; + + strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", + localtime(&t)); + logit("set \"%s\" modtime %s", name, buf); + r = utimes(name, attrib_to_tv(&a)); + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { + logit("set \"%s\" owner %lu group %lu", name, + (u_long)a.uid, (u_long)a.gid); + r = chown(name, a.uid, a.gid); + if (r == -1) + status = errno_to_portable(errno); + } + send_status(id, status); + free(name); +} + +static void +process_fsetstat(u_int32_t id) +{ + Attrib a; + int handle, fd, r; + int status = SSH2_FX_OK; + + if ((r = get_handle(iqueue, &handle)) != 0 || + (r = decode_attrib(iqueue, &a)) != 0) + fatal_fr(r, "parse"); + + debug("request %u: fsetstat handle %d", id, handle); + fd = handle_to_fd(handle); + if (fd < 0) + status = SSH2_FX_FAILURE; + else { + char *name = handle_to_name(handle); + + if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { + logit("set \"%s\" size %llu", + name, (unsigned long long)a.size); + r = ftruncate(fd, a.size); + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + logit("set \"%s\" mode %04o", name, a.perm); +#ifdef HAVE_FCHMOD + r = fchmod(fd, a.perm & 07777); +#else + r = chmod(name, a.perm & 07777); +#endif + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + char buf[64]; + time_t t = a.mtime; + + strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", + localtime(&t)); + logit("set \"%s\" modtime %s", name, buf); +#ifdef HAVE_FUTIMES + r = futimes(fd, attrib_to_tv(&a)); +#else + r = utimes(name, attrib_to_tv(&a)); +#endif + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { + logit("set \"%s\" owner %lu group %lu", name, + (u_long)a.uid, (u_long)a.gid); +#ifdef HAVE_FCHOWN + r = fchown(fd, a.uid, a.gid); +#else + r = chown(name, a.uid, a.gid); +#endif + if (r == -1) + status = errno_to_portable(errno); + } + } + send_status(id, status); +} + +static void +process_opendir(u_int32_t id) +{ + DIR *dirp = NULL; + char *path; + int r, handle, status = SSH2_FX_FAILURE; + + if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: opendir", id); + logit("opendir \"%s\"", path); + dirp = opendir(path); + if (dirp == NULL) { + status = errno_to_portable(errno); + } else { + handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); + if (handle < 0) { + closedir(dirp); + } else { + send_handle(id, handle); + status = SSH2_FX_OK; + } + + } + if (status != SSH2_FX_OK) + send_status(id, status); + free(path); +} + +static void +process_readdir(u_int32_t id) +{ + DIR *dirp; + struct dirent *dp; + char *path; + int r, handle; + + if ((r = get_handle(iqueue, &handle)) != 0) + fatal_fr(r, "parse"); + + debug("request %u: readdir \"%s\" (handle %d)", id, + handle_to_name(handle), handle); + dirp = handle_to_dir(handle); + path = handle_to_name(handle); + if (dirp == NULL || path == NULL) { + send_status(id, SSH2_FX_FAILURE); + } else { + struct stat st; + char pathname[PATH_MAX]; + Stat *stats; + int nstats = 10, count = 0, i; + + stats = xcalloc(nstats, sizeof(Stat)); + while ((dp = readdir(dirp)) != NULL) { + if (count >= nstats) { + nstats *= 2; + stats = xreallocarray(stats, nstats, sizeof(Stat)); + } +/* XXX OVERFLOW ? */ + snprintf(pathname, sizeof pathname, "%s%s%s", path, + strcmp(path, "/") ? "/" : "", dp->d_name); + if (lstat(pathname, &st) == -1) + continue; + stat_to_attrib(&st, &(stats[count].attrib)); + stats[count].name = xstrdup(dp->d_name); + stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); + count++; + /* send up to 100 entries in one message */ + /* XXX check packet size instead */ + if (count == 100) + break; + } + if (count > 0) { + send_names(id, count, stats); + for (i = 0; i < count; i++) { + free(stats[i].name); + free(stats[i].long_name); + } + } else { + send_status(id, SSH2_FX_EOF); + } + free(stats); + } +} + +static void +process_remove(u_int32_t id) +{ + char *name; + int r, status = SSH2_FX_FAILURE; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: remove", id); + logit("remove name \"%s\"", name); + r = unlink(name); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); + free(name); +} + +static void +process_mkdir(u_int32_t id) +{ + Attrib a; + char *name; + int r, mode, status = SSH2_FX_FAILURE; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = decode_attrib(iqueue, &a)) != 0) + fatal_fr(r, "parse"); + + mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? + a.perm & 07777 : 0777; + debug3("request %u: mkdir", id); + logit("mkdir name \"%s\" mode 0%o", name, mode); + r = mkdir(name, mode); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); + free(name); +} + +static void +process_rmdir(u_int32_t id) +{ + char *name; + int r, status; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: rmdir", id); + logit("rmdir name \"%s\"", name); + r = rmdir(name); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); + free(name); +} + +static void +process_realpath(u_int32_t id) +{ + char resolvedname[PATH_MAX]; + char *path; + int r; + + if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) + fatal_fr(r, "parse"); + + if (path[0] == '\0') { + free(path); + path = xstrdup("."); + } + debug3("request %u: realpath", id); + verbose("realpath \"%s\"", path); + if (sftp_realpath(path, resolvedname) == NULL) { + send_status(id, errno_to_portable(errno)); + } else { + Stat s; + attrib_clear(&s.attrib); + s.name = s.long_name = resolvedname; + send_names(id, 1, &s); + } + free(path); +} + +static void +process_rename(u_int32_t id) +{ + char *oldpath, *newpath; + int r, status; + struct stat sb; + + if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || + (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: rename", id); + logit("rename old \"%s\" new \"%s\"", oldpath, newpath); + status = SSH2_FX_FAILURE; + if (lstat(oldpath, &sb) == -1) + status = errno_to_portable(errno); + else if (S_ISREG(sb.st_mode)) { + /* Race-free rename of regular files */ + if (link(oldpath, newpath) == -1) { + if (errno == EOPNOTSUPP || errno == ENOSYS +#ifdef EXDEV + || errno == EXDEV +#endif +#ifdef LINK_OPNOTSUPP_ERRNO + || errno == LINK_OPNOTSUPP_ERRNO +#endif + ) { + struct stat st; + + /* + * fs doesn't support links, so fall back to + * stat+rename. This is racy. + */ + if (stat(newpath, &st) == -1) { + if (rename(oldpath, newpath) == -1) + status = + errno_to_portable(errno); + else + status = SSH2_FX_OK; + } + } else { + status = errno_to_portable(errno); + } + } else if (unlink(oldpath) == -1) { + status = errno_to_portable(errno); + /* clean spare link */ + unlink(newpath); + } else + status = SSH2_FX_OK; + } else if (stat(newpath, &sb) == -1) { + if (rename(oldpath, newpath) == -1) + status = errno_to_portable(errno); + else + status = SSH2_FX_OK; + } + send_status(id, status); + free(oldpath); + free(newpath); +} + +static void +process_readlink(u_int32_t id) +{ + int r, len; + char buf[PATH_MAX]; + char *path; + + if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: readlink", id); + verbose("readlink \"%s\"", path); + if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) + send_status(id, errno_to_portable(errno)); + else { + Stat s; + + buf[len] = '\0'; + attrib_clear(&s.attrib); + s.name = s.long_name = buf; + send_names(id, 1, &s); + } + free(path); +} + +static void +process_symlink(u_int32_t id) +{ + char *oldpath, *newpath; + int r, status; + + if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || + (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: symlink", id); + logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); + /* this will fail if 'newpath' exists */ + r = symlink(oldpath, newpath); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); + free(oldpath); + free(newpath); +} + +static void +process_extended_posix_rename(u_int32_t id) +{ + char *oldpath, *newpath; + int r, status; + + if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || + (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: posix-rename", id); + logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); + r = rename(oldpath, newpath); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); + free(oldpath); + free(newpath); +} + +static void +process_extended_statvfs(u_int32_t id) +{ + char *path; + struct statvfs st; + int r; + + if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) + fatal_fr(r, "parse"); + debug3("request %u: statvfs", id); + logit("statvfs \"%s\"", path); + + if (statvfs(path, &st) != 0) + send_status(id, errno_to_portable(errno)); + else + send_statvfs(id, &st); + free(path); +} + +static void +process_extended_fstatvfs(u_int32_t id) +{ + int r, handle, fd; + struct statvfs st; + + if ((r = get_handle(iqueue, &handle)) != 0) + fatal_fr(r, "parse"); + debug("request %u: fstatvfs \"%s\" (handle %u)", + id, handle_to_name(handle), handle); + if ((fd = handle_to_fd(handle)) < 0) { + send_status(id, SSH2_FX_FAILURE); + return; + } + if (fstatvfs(fd, &st) != 0) + send_status(id, errno_to_portable(errno)); + else + send_statvfs(id, &st); +} + +static void +process_extended_hardlink(u_int32_t id) +{ + char *oldpath, *newpath; + int r, status; + + if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || + (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) + fatal_fr(r, "parse"); + + debug3("request %u: hardlink", id); + logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); + r = link(oldpath, newpath); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + send_status(id, status); + free(oldpath); + free(newpath); +} + +static void +process_extended_fsync(u_int32_t id) +{ + int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; + + if ((r = get_handle(iqueue, &handle)) != 0) + fatal_fr(r, "parse"); + debug3("request %u: fsync (handle %u)", id, handle); + verbose("fsync \"%s\"", handle_to_name(handle)); + if ((fd = handle_to_fd(handle)) < 0) + status = SSH2_FX_NO_SUCH_FILE; + else if (handle_is_ok(handle, HANDLE_FILE)) { + r = fsync(fd); + status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } + send_status(id, status); +} + +static void +process_extended_lsetstat(u_int32_t id) +{ + Attrib a; + char *name; + int r, status = SSH2_FX_OK; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = decode_attrib(iqueue, &a)) != 0) + fatal_fr(r, "parse"); + + debug("request %u: lsetstat name \"%s\"", id, name); + if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { + /* nonsensical for links */ + status = SSH2_FX_BAD_MESSAGE; + goto out; + } + if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + logit("set \"%s\" mode %04o", name, a.perm); + r = fchmodat(AT_FDCWD, name, + a.perm & 07777, AT_SYMLINK_NOFOLLOW); + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + char buf[64]; + time_t t = a.mtime; + + strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", + localtime(&t)); + logit("set \"%s\" modtime %s", name, buf); + r = utimensat(AT_FDCWD, name, + attrib_to_ts(&a), AT_SYMLINK_NOFOLLOW); + if (r == -1) + status = errno_to_portable(errno); + } + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { + logit("set \"%s\" owner %lu group %lu", name, + (u_long)a.uid, (u_long)a.gid); + r = fchownat(AT_FDCWD, name, a.uid, a.gid, + AT_SYMLINK_NOFOLLOW); + if (r == -1) + status = errno_to_portable(errno); + } + out: + send_status(id, status); + free(name); +} + +static void +process_extended_limits(u_int32_t id) +{ + struct sshbuf *msg; + int r; + uint64_t nfiles = 0; +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rlim; +#endif + + debug("request %u: limits", id); + +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + if (getrlimit(RLIMIT_NOFILE, &rlim) != -1 && rlim.rlim_cur > 5) + nfiles = rlim.rlim_cur - 5; /* stdio(3) + syslog + spare */ +#endif + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + /* max-packet-length */ + (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH)) != 0 || + /* max-read-length */ + (r = sshbuf_put_u64(msg, SFTP_MAX_READ_LENGTH)) != 0 || + /* max-write-length */ + (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH - 1024)) != 0 || + /* max-open-handles */ + (r = sshbuf_put_u64(msg, nfiles)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_free(msg); +} + +static void +process_extended_expand(u_int32_t id) +{ + char cwd[PATH_MAX], resolvedname[PATH_MAX]; + char *path, *npath; + int r; + Stat s; + + if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) + fatal_fr(r, "parse"); + if (getcwd(cwd, sizeof(cwd)) == NULL) { + send_status(id, errno_to_portable(errno)); + goto out; + } + + debug3("request %u: expand, original \"%s\"", id, path); + if (path[0] == '\0') { + /* empty path */ + free(path); + path = xstrdup("."); + } else if (*path == '~') { + /* ~ expand path */ + /* Special-case for "~" and "~/" to respect homedir flag */ + if (strcmp(path, "~") == 0) { + free(path); + path = xstrdup(cwd); + } else if (strncmp(path, "~/", 2) == 0) { + npath = xstrdup(path + 2); + free(path); + xasprintf(&path, "%s/%s", cwd, npath); + free(npath); + } else { + /* ~user expansions */ + if (tilde_expand(path, pw->pw_uid, &npath) != 0) { + send_status_errmsg(id, + errno_to_portable(ENOENT), "no such user"); + goto out; + } + free(path); + path = npath; + } + } else if (*path != '/') { + /* relative path */ + xasprintf(&npath, "%s/%s", cwd, path); + free(path); + path = npath; + } + verbose("expand \"%s\"", path); + if (sftp_realpath(path, resolvedname) == NULL) { + send_status(id, errno_to_portable(errno)); + goto out; + } + attrib_clear(&s.attrib); + s.name = s.long_name = resolvedname; + send_names(id, 1, &s); + out: + free(path); +} + +static void +process_extended_copy_data(u_int32_t id) +{ + u_char buf[64*1024]; + int read_handle, read_fd, write_handle, write_fd; + u_int64_t len, read_off, read_len, write_off; + int r, copy_until_eof, status = SSH2_FX_OP_UNSUPPORTED; + size_t ret; + + if ((r = get_handle(iqueue, &read_handle)) != 0 || + (r = sshbuf_get_u64(iqueue, &read_off)) != 0 || + (r = sshbuf_get_u64(iqueue, &read_len)) != 0 || + (r = get_handle(iqueue, &write_handle)) != 0 || + (r = sshbuf_get_u64(iqueue, &write_off)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + debug("request %u: copy-data from \"%s\" (handle %d) off %llu len %llu " + "to \"%s\" (handle %d) off %llu", + id, handle_to_name(read_handle), read_handle, + (unsigned long long)read_off, (unsigned long long)read_len, + handle_to_name(write_handle), write_handle, + (unsigned long long)write_off); + + /* For read length of 0, we read until EOF. */ + if (read_len == 0) { + read_len = (u_int64_t)-1 - read_off; + copy_until_eof = 1; + } else + copy_until_eof = 0; + + read_fd = handle_to_fd(read_handle); + write_fd = handle_to_fd(write_handle); + + /* Disallow reading & writing to the same handle or same path or dirs */ + if (read_handle == write_handle || read_fd < 0 || write_fd < 0 || + !strcmp(handle_to_name(read_handle), handle_to_name(write_handle))) { + status = SSH2_FX_FAILURE; + goto out; + } + + if (lseek(read_fd, read_off, SEEK_SET) < 0) { + status = errno_to_portable(errno); + error("%s: read_seek failed", __func__); + goto out; + } + + if ((handle_to_flags(write_handle) & O_APPEND) == 0 && + lseek(write_fd, write_off, SEEK_SET) < 0) { + status = errno_to_portable(errno); + error("%s: write_seek failed", __func__); + goto out; + } + + /* Process the request in chunks. */ + while (read_len > 0 || copy_until_eof) { + len = MINIMUM(sizeof(buf), read_len); + read_len -= len; + + ret = atomicio(read, read_fd, buf, len); + if (ret == 0 && errno == EPIPE) { + status = copy_until_eof ? SSH2_FX_OK : SSH2_FX_EOF; + break; + } else if (ret == 0) { + status = errno_to_portable(errno); + error("%s: read failed: %s", __func__, strerror(errno)); + break; + } + len = ret; + handle_update_read(read_handle, len); + + ret = atomicio(vwrite, write_fd, buf, len); + if (ret != len) { + status = errno_to_portable(errno); + error("%s: write failed: %llu != %llu: %s", __func__, + (unsigned long long)ret, (unsigned long long)len, + strerror(errno)); + break; + } + handle_update_write(write_handle, len); + } + + if (read_len == 0) + status = SSH2_FX_OK; + + out: + send_status(id, status); +} + +static void +process_extended(u_int32_t id) +{ + char *request; + int r; + const struct sftp_handler *exthand; + + if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0) + fatal_fr(r, "parse"); + if ((exthand = extended_handler_byname(request)) == NULL) { + error("Unknown extended request \"%.100s\"", request); + send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ + } else { + if (!request_permitted(exthand)) + send_status(id, SSH2_FX_PERMISSION_DENIED); + else + exthand->handler(id); + } + free(request); +} + +/* stolen from ssh-agent */ + +static void +process(void) +{ + u_int msg_len; + u_int buf_len; + u_int consumed; + u_char type; + const u_char *cp; + int i, r; + u_int32_t id; + + buf_len = sshbuf_len(iqueue); + if (buf_len < 5) + return; /* Incomplete message. */ + cp = sshbuf_ptr(iqueue); + msg_len = get_u32(cp); + if (msg_len > SFTP_MAX_MSG_LENGTH) { + error("bad message from %s local user %s", + client_addr, pw->pw_name); + sftp_server_cleanup_exit(11); + } + if (buf_len < msg_len + 4) + return; + if ((r = sshbuf_consume(iqueue, 4)) != 0) + fatal_fr(r, "consume"); + buf_len -= 4; + if ((r = sshbuf_get_u8(iqueue, &type)) != 0) + fatal_fr(r, "parse type"); + + switch (type) { + case SSH2_FXP_INIT: + process_init(); + init_done = 1; + break; + case SSH2_FXP_EXTENDED: + if (!init_done) + fatal("Received extended request before init"); + if ((r = sshbuf_get_u32(iqueue, &id)) != 0) + fatal_fr(r, "parse extended ID"); + process_extended(id); + break; + default: + if (!init_done) + fatal("Received %u request before init", type); + if ((r = sshbuf_get_u32(iqueue, &id)) != 0) + fatal_fr(r, "parse ID"); + for (i = 0; handlers[i].handler != NULL; i++) { + if (type == handlers[i].type) { + if (!request_permitted(&handlers[i])) { + send_status(id, + SSH2_FX_PERMISSION_DENIED); + } else { + handlers[i].handler(id); + } + break; + } + } + if (handlers[i].handler == NULL) + error("Unknown message %u", type); + } + /* discard the remaining bytes from the current packet */ + if (buf_len < sshbuf_len(iqueue)) { + error("iqueue grew unexpectedly"); + sftp_server_cleanup_exit(255); + } + consumed = buf_len - sshbuf_len(iqueue); + if (msg_len < consumed) { + error("msg_len %u < consumed %u", msg_len, consumed); + sftp_server_cleanup_exit(255); + } + if (msg_len > consumed && + (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) + fatal_fr(r, "consume"); +} + +/* Cleanup handler that logs active handles upon normal exit */ +void +sftp_server_cleanup_exit(int i) +{ + if (pw != NULL && client_addr != NULL) { + handle_log_exit(); + logit("session closed for local user %s from [%s]", + pw->pw_name, client_addr); + } + _exit(i); +} + +static void +sftp_server_usage(void) +{ + extern char *__progname; + + fprintf(stderr, + "usage: %s [-ehR] [-d start_directory] [-f log_facility] " + "[-l log_level]\n\t[-P denied_requests] " + "[-p allowed_requests] [-u umask]\n" + " %s -Q protocol_feature\n", + __progname, __progname); + exit(1); +} + +int +sftp_server_main(int argc, char **argv, struct passwd *user_pw) +{ + int i, r, in, out, ch, skipargs = 0, log_stderr = 0; + ssize_t len, olen; + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + char *cp, *homedir = NULL, uidstr[32], buf[4*4096]; + long mask; + + extern char *optarg; + extern char *__progname; + + __progname = ssh_get_progname(argv[0]); + log_init(__progname, log_level, log_facility, log_stderr); + + pw = pwcopy(user_pw); + + while (!skipargs && (ch = getopt(argc, argv, + "d:f:l:P:p:Q:u:cehR")) != -1) { + switch (ch) { + case 'Q': + if (strcasecmp(optarg, "requests") != 0) { + fprintf(stderr, "Invalid query type\n"); + exit(1); + } + for (i = 0; handlers[i].handler != NULL; i++) + printf("%s\n", handlers[i].name); + for (i = 0; extended_handlers[i].handler != NULL; i++) + printf("%s\n", extended_handlers[i].name); + exit(0); + break; + case 'R': + readonly = 1; + break; + case 'c': + /* + * Ignore all arguments if we are invoked as a + * shell using "sftp-server -c command" + */ + skipargs = 1; + break; + case 'e': + log_stderr = 1; + break; + case 'l': + log_level = log_level_number(optarg); + if (log_level == SYSLOG_LEVEL_NOT_SET) + error("Invalid log level \"%s\"", optarg); + break; + case 'f': + log_facility = log_facility_number(optarg); + if (log_facility == SYSLOG_FACILITY_NOT_SET) + error("Invalid log facility \"%s\"", optarg); + break; + case 'd': + cp = tilde_expand_filename(optarg, user_pw->pw_uid); + snprintf(uidstr, sizeof(uidstr), "%llu", + (unsigned long long)pw->pw_uid); + homedir = percent_expand(cp, "d", user_pw->pw_dir, + "u", user_pw->pw_name, "U", uidstr, (char *)NULL); + free(cp); + break; + case 'p': + if (request_allowlist != NULL) + fatal("Permitted requests already set"); + request_allowlist = xstrdup(optarg); + break; + case 'P': + if (request_denylist != NULL) + fatal("Refused requests already set"); + request_denylist = xstrdup(optarg); + break; + case 'u': + errno = 0; + mask = strtol(optarg, &cp, 8); + if (mask < 0 || mask > 0777 || *cp != '\0' || + cp == optarg || (mask == 0 && errno != 0)) + fatal("Invalid umask \"%s\"", optarg); + (void)umask((mode_t)mask); + break; + case 'h': + default: + sftp_server_usage(); + } + } + + log_init(__progname, log_level, log_facility, log_stderr); + + /* + * On platforms where we can, avoid making /proc/self/{mem,maps} + * available to the user so that sftp access doesn't automatically + * imply arbitrary code execution access that will break + * restricted configurations. + */ + platform_disable_tracing(1); /* strict */ + + /* Drop any fine-grained privileges we don't need */ + platform_pledge_sftp_server(); + + if ((cp = getenv("SSH_CONNECTION")) != NULL) { + client_addr = xstrdup(cp); + if ((cp = strchr(client_addr, ' ')) == NULL) { + error("Malformed SSH_CONNECTION variable: \"%s\"", + getenv("SSH_CONNECTION")); + sftp_server_cleanup_exit(255); + } + *cp = '\0'; + } else + client_addr = xstrdup("UNKNOWN"); + + logit("session opened for local user %s from [%s]", + pw->pw_name, client_addr); + + in = STDIN_FILENO; + out = STDOUT_FILENO; + +#ifdef HAVE_CYGWIN + setmode(in, O_BINARY); + setmode(out, O_BINARY); +#endif + + if ((iqueue = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((oqueue = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + if (homedir != NULL) { + if (chdir(homedir) != 0) { + error("chdir to \"%s\" failed: %s", homedir, + strerror(errno)); + } + } + + for (;;) { + struct pollfd pfd[2]; + + memset(pfd, 0, sizeof pfd); + pfd[0].fd = pfd[1].fd = -1; + + /* + * Ensure that we can read a full buffer and handle + * the worst-case length packet it can generate, + * otherwise apply backpressure by stopping reads. + */ + if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && + (r = sshbuf_check_reserve(oqueue, + SFTP_MAX_MSG_LENGTH)) == 0) { + pfd[0].fd = in; + pfd[0].events = POLLIN; + } + else if (r != SSH_ERR_NO_BUFFER_SPACE) + fatal_fr(r, "reserve"); + + olen = sshbuf_len(oqueue); + if (olen > 0) { + pfd[1].fd = out; + pfd[1].events = POLLOUT; + } + + if (poll(pfd, 2, -1) == -1) { + if (errno == EINTR) + continue; + error("poll: %s", strerror(errno)); + sftp_server_cleanup_exit(2); + } + + /* copy stdin to iqueue */ + if (pfd[0].revents & (POLLIN|POLLHUP)) { + len = read(in, buf, sizeof buf); + if (len == 0) { + debug("read eof"); + sftp_server_cleanup_exit(0); + } else if (len == -1) { + if (errno != EAGAIN && errno != EINTR) { + error("read: %s", strerror(errno)); + sftp_server_cleanup_exit(1); + } + } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) + fatal_fr(r, "sshbuf_put"); + } + /* send oqueue to stdout */ + if (pfd[1].revents & (POLLOUT|POLLHUP)) { + len = write(out, sshbuf_ptr(oqueue), olen); + if (len == 0 || (len == -1 && errno == EPIPE)) { + debug("write eof"); + sftp_server_cleanup_exit(0); + } else if (len == -1) { + sftp_server_cleanup_exit(1); + if (errno != EAGAIN && errno != EINTR) { + error("write: %s", strerror(errno)); + sftp_server_cleanup_exit(1); + } + } else if ((r = sshbuf_consume(oqueue, len)) != 0) + fatal_fr(r, "consume"); + } + + /* + * Process requests from client if we can fit the results + * into the output buffer, otherwise stop processing input + * and let the output queue drain. + */ + r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH); + if (r == 0) + process(); + else if (r != SSH_ERR_NO_BUFFER_SPACE) + fatal_fr(r, "reserve"); + } +} diff --git a/aosp/external/openssh/sftp.1 b/aosp/external/openssh/sftp.1 new file mode 100644 index 0000000000000000000000000000000000000000..39e7d6ed68a83b0ae723b6d3485f1417930599dc --- /dev/null +++ b/aosp/external/openssh/sftp.1 @@ -0,0 +1,711 @@ +.\" $OpenBSD: sftp.1,v 1.140 2022/03/31 17:27:27 naddy Exp $ +.\" +.\" Copyright (c) 2001 Damien Miller. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SFTP 1 +.Os +.Sh NAME +.Nm sftp +.Nd OpenSSH secure file transfer +.Sh SYNOPSIS +.Nm sftp +.Op Fl 46AaCfNpqrv +.Op Fl B Ar buffer_size +.Op Fl b Ar batchfile +.Op Fl c Ar cipher +.Op Fl D Ar sftp_server_path +.Op Fl F Ar ssh_config +.Op Fl i Ar identity_file +.Op Fl J Ar destination +.Op Fl l Ar limit +.Op Fl o Ar ssh_option +.Op Fl P Ar port +.Op Fl R Ar num_requests +.Op Fl S Ar program +.Op Fl s Ar subsystem | sftp_server +.Ar destination +.Sh DESCRIPTION +.Nm +is a file transfer program, similar to +.Xr ftp 1 , +which performs all operations over an encrypted +.Xr ssh 1 +transport. +It may also use many features of ssh, such as public key authentication and +compression. +.Pp +The +.Ar destination +may be specified either as +.Sm off +.Oo user @ Oc host Op : path +.Sm on +or as a URI in the form +.Sm off +.No sftp:// Oo user @ Oc host Oo : port Oc Op / path . +.Sm on +.Pp +If the +.Ar destination +includes a +.Ar path +and it is not a directory, +.Nm +will retrieve files automatically if a non-interactive +authentication method is used; otherwise it will do so after +successful interactive authentication. +.Pp +If no +.Ar path +is specified, or if the +.Ar path +is a directory, +.Nm +will log in to the specified +.Ar host +and enter interactive command mode, changing to the remote directory +if one was specified. +An optional trailing slash can be used to force the +.Ar path +to be interpreted as a directory. +.Pp +Since the destination formats use colon characters to delimit host +names from path names or port numbers, IPv6 addresses must be +enclosed in square brackets to avoid ambiguity. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl A +Allows forwarding of +.Xr ssh-agent 1 +to the remote system. +The default is not to forward an authentication agent. +.It Fl a +Attempt to continue interrupted transfers rather than overwriting +existing partial or complete copies of files. +If the partial contents differ from those being transferred, +then the resultant file is likely to be corrupt. +.It Fl B Ar buffer_size +Specify the size of the buffer that +.Nm +uses when transferring files. +Larger buffers require fewer round trips at the cost of higher +memory consumption. +The default is 32768 bytes. +.It Fl b Ar batchfile +Batch mode reads a series of commands from an input +.Ar batchfile +instead of +.Em stdin . +Since it lacks user interaction, it should be used in conjunction with +non-interactive authentication to obviate the need to enter a password +at connection time (see +.Xr sshd 8 +and +.Xr ssh-keygen 1 +for details). +.Pp +A +.Ar batchfile +of +.Sq \- +may be used to indicate standard input. +.Nm +will abort if any of the following +commands fail: +.Ic get , put , reget , reput , rename , ln , +.Ic rm , mkdir , chdir , ls , +.Ic lchdir , copy , cp , chmod , chown , +.Ic chgrp , lpwd , df , symlink , +and +.Ic lmkdir . +.Pp +Termination on error can be suppressed on a command by command basis by +prefixing the command with a +.Sq \- +character (for example, +.Ic -rm /tmp/blah* ) . +Echo of the command may be suppressed by prefixing the command with a +.Sq @ +character. +These two prefixes may be combined in any order, for example +.Ic -@ls /bsd . +.It Fl C +Enables compression (via ssh's +.Fl C +flag). +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfers. +This option is directly passed to +.Xr ssh 1 . +.It Fl D Ar sftp_server_path +Connect directly to a local sftp server +(rather than via +.Xr ssh 1 ) . +This option may be useful in debugging the client and server. +.It Fl F Ar ssh_config +Specifies an alternative +per-user configuration file for +.Xr ssh 1 . +This option is directly passed to +.Xr ssh 1 . +.It Fl f +Requests that files be flushed to disk immediately after transfer. +When uploading files, this feature is only enabled if the server +implements the "fsync@openssh.com" extension. +.It Fl i Ar identity_file +Selects the file from which the identity (private key) for public key +authentication is read. +This option is directly passed to +.Xr ssh 1 . +.It Fl J Ar destination +Connect to the target host by first making an +.Nm +connection to the jump host described by +.Ar destination +and then establishing a TCP forwarding to the ultimate destination from +there. +Multiple jump hops may be specified separated by comma characters. +This is a shortcut to specify a +.Cm ProxyJump +configuration directive. +This option is directly passed to +.Xr ssh 1 . +.It Fl l Ar limit +Limits the used bandwidth, specified in Kbit/s. +.It Fl N +Disables quiet mode, e.g. to override the implicit quiet mode set by the +.Fl b +flag. +.It Fl o Ar ssh_option +Can be used to pass options to +.Nm ssh +in the format used in +.Xr ssh_config 5 . +This is useful for specifying options +for which there is no separate +.Nm sftp +command-line flag. +For example, to specify an alternate port use: +.Ic sftp -oPort=24 . +For full details of the options listed below, and their possible values, see +.Xr ssh_config 5 . +.Pp +.Bl -tag -width Ds -offset indent -compact +.It AddressFamily +.It BatchMode +.It BindAddress +.It BindInterface +.It CanonicalDomains +.It CanonicalizeFallbackLocal +.It CanonicalizeHostname +.It CanonicalizeMaxDots +.It CanonicalizePermittedCNAMEs +.It CASignatureAlgorithms +.It CertificateFile +.It CheckHostIP +.It Ciphers +.It Compression +.It ConnectionAttempts +.It ConnectTimeout +.It ControlMaster +.It ControlPath +.It ControlPersist +.It GlobalKnownHostsFile +.It GSSAPIAuthentication +.It GSSAPIDelegateCredentials +.It HashKnownHosts +.It Host +.It HostbasedAcceptedAlgorithms +.It HostbasedAuthentication +.It HostKeyAlgorithms +.It HostKeyAlias +.It Hostname +.It IdentitiesOnly +.It IdentityAgent +.It IdentityFile +.It IPQoS +.It KbdInteractiveAuthentication +.It KbdInteractiveDevices +.It KexAlgorithms +.It KnownHostsCommand +.It LogLevel +.It MACs +.It NoHostAuthenticationForLocalhost +.It NumberOfPasswordPrompts +.It PasswordAuthentication +.It PKCS11Provider +.It Port +.It PreferredAuthentications +.It ProxyCommand +.It ProxyJump +.It PubkeyAcceptedAlgorithms +.It PubkeyAuthentication +.It RekeyLimit +.It SendEnv +.It ServerAliveInterval +.It ServerAliveCountMax +.It SetEnv +.It StrictHostKeyChecking +.It TCPKeepAlive +.It UpdateHostKeys +.It User +.It UserKnownHostsFile +.It VerifyHostKeyDNS +.El +.It Fl P Ar port +Specifies the port to connect to on the remote host. +.It Fl p +Preserves modification times, access times, and modes from the +original files transferred. +.It Fl q +Quiet mode: disables the progress meter as well as warning and +diagnostic messages from +.Xr ssh 1 . +.It Fl R Ar num_requests +Specify how many requests may be outstanding at any one time. +Increasing this may slightly improve file transfer speed +but will increase memory usage. +The default is 64 outstanding requests. +.It Fl r +Recursively copy entire directories when uploading and downloading. +Note that +.Nm +does not follow symbolic links encountered in the tree traversal. +.It Fl S Ar program +Name of the +.Ar program +to use for the encrypted connection. +The program must understand +.Xr ssh 1 +options. +.It Fl s Ar subsystem | sftp_server +Specifies the SSH2 subsystem or the path for an sftp server +on the remote host. +A path is useful when the remote +.Xr sshd 8 +does not have an sftp subsystem configured. +.It Fl v +Raise logging level. +This option is also passed to ssh. +.El +.Sh INTERACTIVE COMMANDS +Once in interactive mode, +.Nm +understands a set of commands similar to those of +.Xr ftp 1 . +Commands are case insensitive. +Pathnames that contain spaces must be enclosed in quotes. +Any special characters contained within pathnames that are recognized by +.Xr glob 3 +must be escaped with backslashes +.Pq Sq \e . +.Bl -tag -width Ds +.It Ic bye +Quit +.Nm sftp . +.It Ic cd Op Ar path +Change remote directory to +.Ar path . +If +.Ar path +is not specified, then change directory to the one the session started in. +.It Xo Ic chgrp +.Op Fl h +.Ar grp +.Ar path +.Xc +Change group of file +.Ar path +to +.Ar grp . +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Ar grp +must be a numeric GID. +.Pp +If the +.Fl h +flag is specified, then symlinks will not be followed. +Note that this is only supported by servers that implement +the "lsetstat@openssh.com" extension. +.It Xo Ic chmod +.Op Fl h +.Ar mode +.Ar path +.Xc +Change permissions of file +.Ar path +to +.Ar mode . +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Pp +If the +.Fl h +flag is specified, then symlinks will not be followed. +Note that this is only supported by servers that implement +the "lsetstat@openssh.com" extension. +.It Xo Ic chown +.Op Fl h +.Ar own +.Ar path +.Xc +Change owner of file +.Ar path +to +.Ar own . +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Ar own +must be a numeric UID. +.Pp +If the +.Fl h +flag is specified, then symlinks will not be followed. +Note that this is only supported by servers that implement +the "lsetstat@openssh.com" extension. +.It Ic copy Ar oldpath Ar newpath +Copy remote file from +.Ar oldpath +to +.Ar newpath . +.Pp +Note that this is only supported by servers that implement the "copy-data" +extension. +.It Ic cp Ar oldpath Ar newpath +Alias to +.Ic copy +command. +.It Xo Ic df +.Op Fl hi +.Op Ar path +.Xc +Display usage information for the filesystem holding the current directory +(or +.Ar path +if specified). +If the +.Fl h +flag is specified, the capacity information will be displayed using +"human-readable" suffixes. +The +.Fl i +flag requests display of inode information in addition to capacity information. +This command is only supported on servers that implement the +.Dq statvfs@openssh.com +extension. +.It Ic exit +Quit +.Nm sftp . +.It Xo Ic get +.Op Fl afpR +.Ar remote-path +.Op Ar local-path +.Xc +Retrieve the +.Ar remote-path +and store it on the local machine. +If the local +path name is not specified, it is given the same name it has on the +remote machine. +.Ar remote-path +may contain +.Xr glob 7 +characters and may match multiple files. +If it does and +.Ar local-path +is specified, then +.Ar local-path +must specify a directory. +.Pp +If the +.Fl a +flag is specified, then attempt to resume partial transfers of existing files. +Note that resumption assumes that any partial copy of the local file matches +the remote copy. +If the remote file contents differ from the partial local copy then the +resultant file is likely to be corrupt. +.Pp +If the +.Fl f +flag is specified, then +.Xr fsync 2 +will be called after the file transfer has completed to flush the file +to disk. +.Pp +If the +.Fl p +.\" undocumented redundant alias +.\" or +.\" .Fl P +flag is specified, then full file permissions and access times are +copied too. +.Pp +If the +.Fl R +.\" undocumented redundant alias +.\" or +.\" .Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. +.It Ic help +Display help text. +.It Ic lcd Op Ar path +Change local directory to +.Ar path . +If +.Ar path +is not specified, then change directory to the local user's home directory. +.It Ic lls Op Ar ls-options Op Ar path +Display local directory listing of either +.Ar path +or current directory if +.Ar path +is not specified. +.Ar ls-options +may contain any flags supported by the local system's +.Xr ls 1 +command. +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.It Ic lmkdir Ar path +Create local directory specified by +.Ar path . +.It Xo Ic ln +.Op Fl s +.Ar oldpath +.Ar newpath +.Xc +Create a link from +.Ar oldpath +to +.Ar newpath . +If the +.Fl s +flag is specified the created link is a symbolic link, otherwise it is +a hard link. +.It Ic lpwd +Print local working directory. +.It Xo Ic ls +.Op Fl 1afhlnrSt +.Op Ar path +.Xc +Display a remote directory listing of either +.Ar path +or the current directory if +.Ar path +is not specified. +.Ar path +may contain +.Xr glob 7 +characters and may match multiple files. +.Pp +The following flags are recognized and alter the behaviour of +.Ic ls +accordingly: +.Bl -tag -width Ds +.It Fl 1 +Produce single columnar output. +.It Fl a +List files beginning with a dot +.Pq Sq \&. . +.It Fl f +Do not sort the listing. +The default sort order is lexicographical. +.It Fl h +When used with a long format option, use unit suffixes: Byte, Kilobyte, +Megabyte, Gigabyte, Terabyte, Petabyte, and Exabyte in order to reduce +the number of digits to four or fewer using powers of 2 for sizes (K=1024, +M=1048576, etc.). +.It Fl l +Display additional details including permissions +and ownership information. +.It Fl n +Produce a long listing with user and group information presented +numerically. +.It Fl r +Reverse the sort order of the listing. +.It Fl S +Sort the listing by file size. +.It Fl t +Sort the listing by last modification time. +.El +.It Ic lumask Ar umask +Set local umask to +.Ar umask . +.It Ic mkdir Ar path +Create remote directory specified by +.Ar path . +.It Ic progress +Toggle display of progress meter. +.It Xo Ic put +.Op Fl afpR +.Ar local-path +.Op Ar remote-path +.Xc +Upload +.Ar local-path +and store it on the remote machine. +If the remote path name is not specified, it is given the same name it has +on the local machine. +.Ar local-path +may contain +.Xr glob 7 +characters and may match multiple files. +If it does and +.Ar remote-path +is specified, then +.Ar remote-path +must specify a directory. +.Pp +If the +.Fl a +flag is specified, then attempt to resume partial +transfers of existing files. +Note that resumption assumes that any partial copy of the remote file +matches the local copy. +If the local file contents differ from the remote local copy then +the resultant file is likely to be corrupt. +.Pp +If the +.Fl f +flag is specified, then a request will be sent to the server to call +.Xr fsync 2 +after the file has been transferred. +Note that this is only supported by servers that implement +the "fsync@openssh.com" extension. +.Pp +If the +.Fl p +.\" undocumented redundant alias +.\" or +.\" .Fl P +flag is specified, then full file permissions and access times are +copied too. +.Pp +If the +.Fl R +.\" undocumented redundant alias +.\" or +.\" .Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. +.It Ic pwd +Display remote working directory. +.It Ic quit +Quit +.Nm sftp . +.It Xo Ic reget +.Op Fl fpR +.Ar remote-path +.Op Ar local-path +.Xc +Resume download of +.Ar remote-path . +Equivalent to +.Ic get +with the +.Fl a +flag set. +.It Xo Ic reput +.Op Fl fpR +.Ar local-path +.Op Ar remote-path +.Xc +Resume upload of +.Ar local-path . +Equivalent to +.Ic put +with the +.Fl a +flag set. +.It Ic rename Ar oldpath newpath +Rename remote file from +.Ar oldpath +to +.Ar newpath . +.It Ic rm Ar path +Delete remote file specified by +.Ar path . +.It Ic rmdir Ar path +Remove remote directory specified by +.Ar path . +.It Ic symlink Ar oldpath newpath +Create a symbolic link from +.Ar oldpath +to +.Ar newpath . +.It Ic version +Display the +.Nm +protocol version. +.It Ic \&! Ns Ar command +Execute +.Ar command +in local shell. +.It Ic \&! +Escape to local shell. +.It Ic \&? +Synonym for help. +.El +.Sh SEE ALSO +.Xr ftp 1 , +.Xr ls 1 , +.Xr scp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr ssh_config 5 , +.Xr glob 7 , +.Xr sftp-server 8 , +.Xr sshd 8 +.Rs +.%A T. Ylonen +.%A S. Lehtinen +.%T "SSH File Transfer Protocol" +.%N draft-ietf-secsh-filexfer-00.txt +.%D January 2001 +.%O work in progress material +.Re diff --git a/aosp/external/openssh/sftp.c b/aosp/external/openssh/sftp.c new file mode 100644 index 0000000000000000000000000000000000000000..4efc025a504cd7b624a612c30d718eb214babc23 --- /dev/null +++ b/aosp/external/openssh/sftp.c @@ -0,0 +1,2598 @@ +/* $OpenBSD: sftp.c,v 1.214 2022/03/31 03:07:03 djm Exp $ */ +/* + * Copyright (c) 2001-2004 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + +#include +#include + +#ifdef HAVE_PATHS_H +# include +#endif +#ifdef HAVE_LIBGEN_H +#include +#endif +#ifdef HAVE_LOCALE_H +# include +#endif +#ifdef USE_LIBEDIT +#include +#else +typedef void EditLine; +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UTIL_H +# include +#endif + +#include "xmalloc.h" +#include "log.h" +#include "pathnames.h" +#include "misc.h" +#include "utf8.h" + +#include "sftp.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "sftp-common.h" +#include "sftp-client.h" + +/* File to read commands from */ +FILE* infile; + +/* Are we in batchfile mode? */ +int batchmode = 0; + +/* PID of ssh transport process */ +static volatile pid_t sshpid = -1; + +/* Suppress diagnostic messages */ +int quiet = 0; + +/* This is set to 0 if the progressmeter is not desired. */ +int showprogress = 1; + +/* When this option is set, we always recursively download/upload directories */ +int global_rflag = 0; + +/* When this option is set, we resume download or upload if possible */ +int global_aflag = 0; + +/* When this option is set, the file transfers will always preserve times */ +int global_pflag = 0; + +/* When this option is set, transfers will have fsync() called on each file */ +int global_fflag = 0; + +/* SIGINT received during command processing */ +volatile sig_atomic_t interrupted = 0; + +/* I wish qsort() took a separate ctx for the comparison function...*/ +int sort_flag; +glob_t *sort_glob; + +/* Context used for commandline completion */ +struct complete_ctx { + struct sftp_conn *conn; + char **remote_pathp; +}; + +int remote_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ + +extern char *__progname; + +/* Separators for interactive commands */ +#define WHITESPACE " \t\r\n" + +/* ls flags */ +#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ +#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ +#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ +#define LS_NAME_SORT 0x0008 /* Sort by name (default) */ +#define LS_TIME_SORT 0x0010 /* Sort by mtime */ +#define LS_SIZE_SORT 0x0020 /* Sort by file size */ +#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ +#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ +#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ + +#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) +#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) + +/* Commands for interactive mode */ +enum sftp_command { + I_CHDIR = 1, + I_CHGRP, + I_CHMOD, + I_CHOWN, + I_COPY, + I_DF, + I_GET, + I_HELP, + I_LCHDIR, + I_LINK, + I_LLS, + I_LMKDIR, + I_LPWD, + I_LS, + I_LUMASK, + I_MKDIR, + I_PUT, + I_PWD, + I_QUIT, + I_REGET, + I_RENAME, + I_REPUT, + I_RM, + I_RMDIR, + I_SHELL, + I_SYMLINK, + I_VERSION, + I_PROGRESS, +}; + +struct CMD { + const char *c; + const int n; + const int t; +}; + +/* Type of completion */ +#define NOARGS 0 +#define REMOTE 1 +#define LOCAL 2 + +static const struct CMD cmds[] = { + { "bye", I_QUIT, NOARGS }, + { "cd", I_CHDIR, REMOTE }, + { "chdir", I_CHDIR, REMOTE }, + { "chgrp", I_CHGRP, REMOTE }, + { "chmod", I_CHMOD, REMOTE }, + { "chown", I_CHOWN, REMOTE }, + { "copy", I_COPY, REMOTE }, + { "cp", I_COPY, REMOTE }, + { "df", I_DF, REMOTE }, + { "dir", I_LS, REMOTE }, + { "exit", I_QUIT, NOARGS }, + { "get", I_GET, REMOTE }, + { "help", I_HELP, NOARGS }, + { "lcd", I_LCHDIR, LOCAL }, + { "lchdir", I_LCHDIR, LOCAL }, + { "lls", I_LLS, LOCAL }, + { "lmkdir", I_LMKDIR, LOCAL }, + { "ln", I_LINK, REMOTE }, + { "lpwd", I_LPWD, LOCAL }, + { "ls", I_LS, REMOTE }, + { "lumask", I_LUMASK, NOARGS }, + { "mkdir", I_MKDIR, REMOTE }, + { "mget", I_GET, REMOTE }, + { "mput", I_PUT, LOCAL }, + { "progress", I_PROGRESS, NOARGS }, + { "put", I_PUT, LOCAL }, + { "pwd", I_PWD, REMOTE }, + { "quit", I_QUIT, NOARGS }, + { "reget", I_REGET, REMOTE }, + { "rename", I_RENAME, REMOTE }, + { "reput", I_REPUT, LOCAL }, + { "rm", I_RM, REMOTE }, + { "rmdir", I_RMDIR, REMOTE }, + { "symlink", I_SYMLINK, REMOTE }, + { "version", I_VERSION, NOARGS }, + { "!", I_SHELL, NOARGS }, + { "?", I_HELP, NOARGS }, + { NULL, -1, -1 } +}; + +/* ARGSUSED */ +static void +killchild(int signo) +{ + pid_t pid; + + pid = sshpid; + if (pid > 1) { + kill(pid, SIGTERM); + waitpid(pid, NULL, 0); + } + + _exit(1); +} + +/* ARGSUSED */ +static void +suspchild(int signo) +{ + if (sshpid > 1) { + kill(sshpid, signo); + while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR) + continue; + } + kill(getpid(), SIGSTOP); +} + +/* ARGSUSED */ +static void +cmd_interrupt(int signo) +{ + const char msg[] = "\rInterrupt \n"; + int olderrno = errno; + + (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + interrupted = 1; + errno = olderrno; +} + +/* ARGSUSED */ +static void +read_interrupt(int signo) +{ + interrupted = 1; +} + +/*ARGSUSED*/ +static void +sigchld_handler(int sig) +{ + int save_errno = errno; + pid_t pid; + const char msg[] = "\rConnection closed. \n"; + + /* Report if ssh transport process dies. */ + while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR) + continue; + if (pid == sshpid) { + (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + sshpid = -1; + } + + errno = save_errno; +} + +static void +help(void) +{ + printf("Available commands:\n" + "bye Quit sftp\n" + "cd path Change remote directory to 'path'\n" + "chgrp [-h] grp path Change group of file 'path' to 'grp'\n" + "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n" + "chown [-h] own path Change owner of file 'path' to 'own'\n" + "copy oldpath newpath Copy remote file\n" + "cp oldpath newpath Copy remote file\n" + "df [-hi] [path] Display statistics for current directory or\n" + " filesystem containing 'path'\n" + "exit Quit sftp\n" + "get [-afpR] remote [local] Download file\n" + "help Display this help text\n" + "lcd path Change local directory to 'path'\n" + "lls [ls-options [path]] Display local directory listing\n" + "lmkdir path Create local directory\n" + "ln [-s] oldpath newpath Link remote file (-s for symlink)\n" + "lpwd Print local working directory\n" + "ls [-1afhlnrSt] [path] Display remote directory listing\n" + "lumask umask Set local umask to 'umask'\n" + "mkdir path Create remote directory\n" + "progress Toggle display of progress meter\n" + "put [-afpR] local [remote] Upload file\n" + "pwd Display remote working directory\n" + "quit Quit sftp\n" + "reget [-fpR] remote [local] Resume download file\n" + "rename oldpath newpath Rename remote file\n" + "reput [-fpR] local [remote] Resume upload file\n" + "rm path Delete remote file\n" + "rmdir path Remove remote directory\n" + "symlink oldpath newpath Symlink remote file\n" + "version Show SFTP version\n" + "!command Execute 'command' in local shell\n" + "! Escape to local shell\n" + "? Synonym for help\n"); +} + +static void +local_do_shell(const char *args) +{ + int status; + char *shell; + pid_t pid; + + if (!*args) + args = NULL; + + if ((shell = getenv("SHELL")) == NULL || *shell == '\0') + shell = _PATH_BSHELL; + + if ((pid = fork()) == -1) + fatal("Couldn't fork: %s", strerror(errno)); + + if (pid == 0) { + /* XXX: child has pipe fds to ssh subproc open - issue? */ + if (args) { + debug3("Executing %s -c \"%s\"", shell, args); + execl(shell, shell, "-c", args, (char *)NULL); + } else { + debug3("Executing %s", shell); + execl(shell, shell, (char *)NULL); + } + fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, + strerror(errno)); + _exit(1); + } + while (waitpid(pid, &status, 0) == -1) + if (errno != EINTR) + fatal("Couldn't wait for child: %s", strerror(errno)); + if (!WIFEXITED(status)) + error("Shell exited abnormally"); + else if (WEXITSTATUS(status)) + error("Shell exited with status %d", WEXITSTATUS(status)); +} + +static void +local_do_ls(const char *args) +{ + if (!args || !*args) + local_do_shell(_PATH_LS); + else { + int len = strlen(_PATH_LS " ") + strlen(args) + 1; + char *buf = xmalloc(len); + + /* XXX: quoting - rip quoting code from ftp? */ + snprintf(buf, len, _PATH_LS " %s", args); + local_do_shell(buf); + free(buf); + } +} + +/* Strip one path (usually the pwd) from the start of another */ +static char * +path_strip(const char *path, const char *strip) +{ + size_t len; + + if (strip == NULL) + return (xstrdup(path)); + + len = strlen(strip); + if (strncmp(path, strip, len) == 0) { + if (strip[len - 1] != '/' && path[len] == '/') + len++; + return (xstrdup(path + len)); + } + + return (xstrdup(path)); +} + +static int +parse_getput_flags(const char *cmd, char **argv, int argc, + int *aflag, int *fflag, int *pflag, int *rflag) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + *aflag = *fflag = *rflag = *pflag = 0; + while ((ch = getopt(argc, argv, "afPpRr")) != -1) { + switch (ch) { + case 'a': + *aflag = 1; + break; + case 'f': + *fflag = 1; + break; + case 'p': + case 'P': + *pflag = 1; + break; + case 'r': + case 'R': + *rflag = 1; + break; + default: + error("%s: Invalid flag -%c", cmd, optopt); + return -1; + } + } + + return optind; +} + +static int +parse_link_flags(const char *cmd, char **argv, int argc, int *sflag) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + *sflag = 0; + while ((ch = getopt(argc, argv, "s")) != -1) { + switch (ch) { + case 's': + *sflag = 1; + break; + default: + error("%s: Invalid flag -%c", cmd, optopt); + return -1; + } + } + + return optind; +} + +static int +parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + *lflag = 0; + while ((ch = getopt(argc, argv, "l")) != -1) { + switch (ch) { + case 'l': + *lflag = 1; + break; + default: + error("%s: Invalid flag -%c", cmd, optopt); + return -1; + } + } + + return optind; +} + +static int +parse_ls_flags(char **argv, int argc, int *lflag) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + *lflag = LS_NAME_SORT; + while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { + switch (ch) { + case '1': + *lflag &= ~VIEW_FLAGS; + *lflag |= LS_SHORT_VIEW; + break; + case 'S': + *lflag &= ~SORT_FLAGS; + *lflag |= LS_SIZE_SORT; + break; + case 'a': + *lflag |= LS_SHOW_ALL; + break; + case 'f': + *lflag &= ~SORT_FLAGS; + break; + case 'h': + *lflag |= LS_SI_UNITS; + break; + case 'l': + *lflag &= ~LS_SHORT_VIEW; + *lflag |= LS_LONG_VIEW; + break; + case 'n': + *lflag &= ~LS_SHORT_VIEW; + *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; + break; + case 'r': + *lflag |= LS_REVERSE_SORT; + break; + case 't': + *lflag &= ~SORT_FLAGS; + *lflag |= LS_TIME_SORT; + break; + default: + error("ls: Invalid flag -%c", optopt); + return -1; + } + } + + return optind; +} + +static int +parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + *hflag = *iflag = 0; + while ((ch = getopt(argc, argv, "hi")) != -1) { + switch (ch) { + case 'h': + *hflag = 1; + break; + case 'i': + *iflag = 1; + break; + default: + error("%s: Invalid flag -%c", cmd, optopt); + return -1; + } + } + + return optind; +} + +static int +parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + *hflag = 0; + while ((ch = getopt(argc, argv, "h")) != -1) { + switch (ch) { + case 'h': + *hflag = 1; + break; + default: + error("%s: Invalid flag -%c", cmd, optopt); + return -1; + } + } + + return optind; +} + +static int +parse_no_flags(const char *cmd, char **argv, int argc) +{ + extern int opterr, optind, optopt, optreset; + int ch; + + optind = optreset = 1; + opterr = 0; + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + default: + error("%s: Invalid flag -%c", cmd, optopt); + return -1; + } + } + + return optind; +} + +static int +process_get(struct sftp_conn *conn, const char *src, const char *dst, + const char *pwd, int pflag, int rflag, int resume, int fflag) +{ + char *abs_src = NULL; + char *abs_dst = NULL; + glob_t g; + char *filename, *tmp=NULL; + int i, r, err = 0; + + abs_src = xstrdup(src); + abs_src = make_absolute(abs_src, pwd); + memset(&g, 0, sizeof(g)); + + debug3("Looking up %s", abs_src); + if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { + if (r == GLOB_NOSPACE) { + error("Too many matches for \"%s\".", abs_src); + } else { + error("File \"%s\" not found.", abs_src); + } + err = -1; + goto out; + } + + /* + * If multiple matches then dst must be a directory or + * unspecified. + */ + if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) { + error("Multiple source paths, but destination " + "\"%s\" is not a directory", dst); + err = -1; + goto out; + } + + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + free(tmp); + err = -1; + goto out; + } + + if (g.gl_matchc == 1 && dst) { + if (local_is_dir(dst)) { + abs_dst = path_append(dst, filename); + } else { + abs_dst = xstrdup(dst); + } + } else if (dst) { + abs_dst = path_append(dst, filename); + } else { + abs_dst = xstrdup(filename); + } + free(tmp); + + resume |= global_aflag; + if (!quiet && resume) + mprintf("Resuming %s to %s\n", + g.gl_pathv[i], abs_dst); + else if (!quiet && !resume) + mprintf("Fetching %s to %s\n", + g.gl_pathv[i], abs_dst); + /* XXX follow link flag */ + if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag, 1, resume, + fflag || global_fflag, 0) == -1) + err = -1; + } else { + if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag, resume, + fflag || global_fflag) == -1) + err = -1; + } + free(abs_dst); + abs_dst = NULL; + } + +out: + free(abs_src); + globfree(&g); + return(err); +} + +static int +process_put(struct sftp_conn *conn, const char *src, const char *dst, + const char *pwd, int pflag, int rflag, int resume, int fflag) +{ + char *tmp_dst = NULL; + char *abs_dst = NULL; + char *tmp = NULL, *filename = NULL; + glob_t g; + int err = 0; + int i, dst_is_dir = 1; + struct stat sb; + + if (dst) { + tmp_dst = xstrdup(dst); + tmp_dst = make_absolute(tmp_dst, pwd); + } + + memset(&g, 0, sizeof(g)); + debug3("Looking up %s", src); + if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { + error("File \"%s\" not found.", src); + err = -1; + goto out; + } + + /* If we aren't fetching to pwd then stash this status for later */ + if (tmp_dst != NULL) + dst_is_dir = remote_is_dir(conn, tmp_dst); + + /* If multiple matches, dst may be directory or unspecified */ + if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { + error("Multiple paths match, but destination " + "\"%s\" is not a directory", tmp_dst); + err = -1; + goto out; + } + + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + if (stat(g.gl_pathv[i], &sb) == -1) { + err = -1; + error("stat %s: %s", g.gl_pathv[i], strerror(errno)); + continue; + } + + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + free(tmp); + err = -1; + goto out; + } + + if (g.gl_matchc == 1 && tmp_dst) { + /* If directory specified, append filename */ + if (dst_is_dir) + abs_dst = path_append(tmp_dst, filename); + else + abs_dst = xstrdup(tmp_dst); + } else if (tmp_dst) { + abs_dst = path_append(tmp_dst, filename); + } else { + abs_dst = make_absolute(xstrdup(filename), pwd); + } + free(tmp); + + resume |= global_aflag; + if (!quiet && resume) + mprintf("Resuming upload of %s to %s\n", + g.gl_pathv[i], abs_dst); + else if (!quiet && !resume) + mprintf("Uploading %s to %s\n", + g.gl_pathv[i], abs_dst); + /* XXX follow_link_flag */ + if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (upload_dir(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag, 1, resume, + fflag || global_fflag, 0) == -1) + err = -1; + } else { + if (do_upload(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag, resume, + fflag || global_fflag) == -1) + err = -1; + } + } + +out: + free(abs_dst); + free(tmp_dst); + globfree(&g); + return(err); +} + +static int +sdirent_comp(const void *aa, const void *bb) +{ + SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; + SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; + int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; + +#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) + if (sort_flag & LS_NAME_SORT) + return (rmul * strcmp(a->filename, b->filename)); + else if (sort_flag & LS_TIME_SORT) + return (rmul * NCMP(a->a.mtime, b->a.mtime)); + else if (sort_flag & LS_SIZE_SORT) + return (rmul * NCMP(a->a.size, b->a.size)); + + fatal("Unknown ls sort type"); +} + +/* sftp ls.1 replacement for directories */ +static int +do_ls_dir(struct sftp_conn *conn, const char *path, + const char *strip_path, int lflag) +{ + int n; + u_int c = 1, colspace = 0, columns = 1; + SFTP_DIRENT **d; + + if ((n = do_readdir(conn, path, &d)) != 0) + return (n); + + if (!(lflag & LS_SHORT_VIEW)) { + u_int m = 0, width = 80; + struct winsize ws; + char *tmp; + + /* Count entries for sort and find longest filename */ + for (n = 0; d[n] != NULL; n++) { + if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) + m = MAXIMUM(m, strlen(d[n]->filename)); + } + + /* Add any subpath that also needs to be counted */ + tmp = path_strip(path, strip_path); + m += strlen(tmp); + free(tmp); + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + + columns = width / (m + 2); + columns = MAXIMUM(columns, 1); + colspace = width / columns; + colspace = MINIMUM(colspace, width); + } + + if (lflag & SORT_FLAGS) { + for (n = 0; d[n] != NULL; n++) + ; /* count entries */ + sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); + qsort(d, n, sizeof(*d), sdirent_comp); + } + + for (n = 0; d[n] != NULL && !interrupted; n++) { + char *tmp, *fname; + + if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) + continue; + + tmp = path_append(path, d[n]->filename); + fname = path_strip(tmp, strip_path); + free(tmp); + + if (lflag & LS_LONG_VIEW) { + if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { + char *lname; + struct stat sb; + + memset(&sb, 0, sizeof(sb)); + attrib_to_stat(&d[n]->a, &sb); + lname = ls_file(fname, &sb, 1, + (lflag & LS_SI_UNITS)); + mprintf("%s\n", lname); + free(lname); + } else + mprintf("%s\n", d[n]->longname); + } else { + mprintf("%-*s", colspace, fname); + if (c >= columns) { + printf("\n"); + c = 1; + } else + c++; + } + + free(fname); + } + + if (!(lflag & LS_LONG_VIEW) && (c != 1)) + printf("\n"); + + free_sftp_dirents(d); + return (0); +} + +static int +sglob_comp(const void *aa, const void *bb) +{ + u_int a = *(const u_int *)aa; + u_int b = *(const u_int *)bb; + const char *ap = sort_glob->gl_pathv[a]; + const char *bp = sort_glob->gl_pathv[b]; + const struct stat *as = sort_glob->gl_statv[a]; + const struct stat *bs = sort_glob->gl_statv[b]; + int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; + +#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) + if (sort_flag & LS_NAME_SORT) + return (rmul * strcmp(ap, bp)); + else if (sort_flag & LS_TIME_SORT) { +#if defined(HAVE_STRUCT_STAT_ST_MTIM) + if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==)) + return 0; + return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ? + rmul : -rmul; +#elif defined(HAVE_STRUCT_STAT_ST_MTIME) + return (rmul * NCMP(as->st_mtime, bs->st_mtime)); +#else + return rmul * 1; +#endif + } else if (sort_flag & LS_SIZE_SORT) + return (rmul * NCMP(as->st_size, bs->st_size)); + + fatal("Unknown ls sort type"); +} + +/* sftp ls.1 replacement which handles path globs */ +static int +do_globbed_ls(struct sftp_conn *conn, const char *path, + const char *strip_path, int lflag) +{ + char *fname, *lname; + glob_t g; + int err, r; + struct winsize ws; + u_int i, j, nentries, *indices = NULL, c = 1; + u_int colspace = 0, columns = 1, m = 0, width = 80; + + memset(&g, 0, sizeof(g)); + + if ((r = remote_glob(conn, path, + GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, + NULL, &g)) != 0 || + (g.gl_pathc && !g.gl_matchc)) { + if (g.gl_pathc) + globfree(&g); + if (r == GLOB_NOSPACE) { + error("Can't ls: Too many matches for \"%s\"", path); + } else { + error("Can't ls: \"%s\" not found", path); + } + return -1; + } + + if (interrupted) + goto out; + + /* + * If the glob returns a single match and it is a directory, + * then just list its contents. + */ + if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && + S_ISDIR(g.gl_statv[0]->st_mode)) { + err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); + globfree(&g); + return err; + } + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + + if (!(lflag & LS_SHORT_VIEW)) { + /* Count entries for sort and find longest filename */ + for (i = 0; g.gl_pathv[i]; i++) + m = MAXIMUM(m, strlen(g.gl_pathv[i])); + + columns = width / (m + 2); + columns = MAXIMUM(columns, 1); + colspace = width / columns; + } + + /* + * Sorting: rather than mess with the contents of glob_t, prepare + * an array of indices into it and sort that. For the usual + * unsorted case, the indices are just the identity 1=1, 2=2, etc. + */ + for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++) + ; /* count entries */ + indices = calloc(nentries, sizeof(*indices)); + for (i = 0; i < nentries; i++) + indices[i] = i; + + if (lflag & SORT_FLAGS) { + sort_glob = &g; + sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); + qsort(indices, nentries, sizeof(*indices), sglob_comp); + sort_glob = NULL; + } + + for (j = 0; j < nentries && !interrupted; j++) { + i = indices[j]; + fname = path_strip(g.gl_pathv[i], strip_path); + if (lflag & LS_LONG_VIEW) { + if (g.gl_statv[i] == NULL) { + error("no stat information for %s", fname); + continue; + } + lname = ls_file(fname, g.gl_statv[i], 1, + (lflag & LS_SI_UNITS)); + mprintf("%s\n", lname); + free(lname); + } else { + mprintf("%-*s", colspace, fname); + if (c >= columns) { + printf("\n"); + c = 1; + } else + c++; + } + free(fname); + } + + if (!(lflag & LS_LONG_VIEW) && (c != 1)) + printf("\n"); + + out: + if (g.gl_pathc) + globfree(&g); + free(indices); + + return 0; +} + +static int +do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag) +{ + struct sftp_statvfs st; + char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE]; + char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE]; + char s_icapacity[16], s_dcapacity[16]; + + if (do_statvfs(conn, path, &st, 1) == -1) + return -1; + if (st.f_files == 0) + strlcpy(s_icapacity, "ERR", sizeof(s_icapacity)); + else { + snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%", + (unsigned long long)(100 * (st.f_files - st.f_ffree) / + st.f_files)); + } + if (st.f_blocks == 0) + strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity)); + else { + snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%", + (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / + st.f_blocks)); + } + if (iflag) { + printf(" Inodes Used Avail " + "(root) %%Capacity\n"); + printf("%11llu %11llu %11llu %11llu %s\n", + (unsigned long long)st.f_files, + (unsigned long long)(st.f_files - st.f_ffree), + (unsigned long long)st.f_favail, + (unsigned long long)st.f_ffree, s_icapacity); + } else if (hflag) { + strlcpy(s_used, "error", sizeof(s_used)); + strlcpy(s_avail, "error", sizeof(s_avail)); + strlcpy(s_root, "error", sizeof(s_root)); + strlcpy(s_total, "error", sizeof(s_total)); + fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); + fmt_scaled(st.f_bavail * st.f_frsize, s_avail); + fmt_scaled(st.f_bfree * st.f_frsize, s_root); + fmt_scaled(st.f_blocks * st.f_frsize, s_total); + printf(" Size Used Avail (root) %%Capacity\n"); + printf("%7sB %7sB %7sB %7sB %s\n", + s_total, s_used, s_avail, s_root, s_dcapacity); + } else { + printf(" Size Used Avail " + "(root) %%Capacity\n"); + printf("%12llu %12llu %12llu %12llu %s\n", + (unsigned long long)(st.f_frsize * st.f_blocks / 1024), + (unsigned long long)(st.f_frsize * + (st.f_blocks - st.f_bfree) / 1024), + (unsigned long long)(st.f_frsize * st.f_bavail / 1024), + (unsigned long long)(st.f_frsize * st.f_bfree / 1024), + s_dcapacity); + } + return 0; +} + +/* + * Undo escaping of glob sequences in place. Used to undo extra escaping + * applied in makeargv() when the string is destined for a function that + * does not glob it. + */ +static void +undo_glob_escape(char *s) +{ + size_t i, j; + + for (i = j = 0;;) { + if (s[i] == '\0') { + s[j] = '\0'; + return; + } + if (s[i] != '\\') { + s[j++] = s[i++]; + continue; + } + /* s[i] == '\\' */ + ++i; + switch (s[i]) { + case '?': + case '[': + case '*': + case '\\': + s[j++] = s[i++]; + break; + case '\0': + s[j++] = '\\'; + s[j] = '\0'; + return; + default: + s[j++] = '\\'; + s[j++] = s[i++]; + break; + } + } +} + +/* + * Split a string into an argument vector using sh(1)-style quoting, + * comment and escaping rules, but with some tweaks to handle glob(3) + * wildcards. + * The "sloppy" flag allows for recovery from missing terminating quote, for + * use in parsing incomplete commandlines during tab autocompletion. + * + * Returns NULL on error or a NULL-terminated array of arguments. + * + * If "lastquote" is not NULL, the quoting character used for the last + * argument is placed in *lastquote ("\0", "'" or "\""). + * + * If "terminated" is not NULL, *terminated will be set to 1 when the + * last argument's quote has been properly terminated or 0 otherwise. + * This parameter is only of use if "sloppy" is set. + */ +#define MAXARGS 128 +#define MAXARGLEN 8192 +static char ** +makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, + u_int *terminated) +{ + int argc, quot; + size_t i, j; + static char argvs[MAXARGLEN]; + static char *argv[MAXARGS + 1]; + enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; + + *argcp = argc = 0; + if (strlen(arg) > sizeof(argvs) - 1) { + args_too_longs: + error("string too long"); + return NULL; + } + if (terminated != NULL) + *terminated = 1; + if (lastquote != NULL) + *lastquote = '\0'; + state = MA_START; + i = j = 0; + for (;;) { + if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){ + error("Too many arguments."); + return NULL; + } + if (isspace((unsigned char)arg[i])) { + if (state == MA_UNQUOTED) { + /* Terminate current argument */ + argvs[j++] = '\0'; + argc++; + state = MA_START; + } else if (state != MA_START) + argvs[j++] = arg[i]; + } else if (arg[i] == '"' || arg[i] == '\'') { + q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; + if (state == MA_START) { + argv[argc] = argvs + j; + state = q; + if (lastquote != NULL) + *lastquote = arg[i]; + } else if (state == MA_UNQUOTED) + state = q; + else if (state == q) + state = MA_UNQUOTED; + else + argvs[j++] = arg[i]; + } else if (arg[i] == '\\') { + if (state == MA_SQUOTE || state == MA_DQUOTE) { + quot = state == MA_SQUOTE ? '\'' : '"'; + /* Unescape quote we are in */ + /* XXX support \n and friends? */ + if (arg[i + 1] == quot) { + i++; + argvs[j++] = arg[i]; + } else if (arg[i + 1] == '?' || + arg[i + 1] == '[' || arg[i + 1] == '*') { + /* + * Special case for sftp: append + * double-escaped glob sequence - + * glob will undo one level of + * escaping. NB. string can grow here. + */ + if (j >= sizeof(argvs) - 5) + goto args_too_longs; + argvs[j++] = '\\'; + argvs[j++] = arg[i++]; + argvs[j++] = '\\'; + argvs[j++] = arg[i]; + } else { + argvs[j++] = arg[i++]; + argvs[j++] = arg[i]; + } + } else { + if (state == MA_START) { + argv[argc] = argvs + j; + state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; + } + if (arg[i + 1] == '?' || arg[i + 1] == '[' || + arg[i + 1] == '*' || arg[i + 1] == '\\') { + /* + * Special case for sftp: append + * escaped glob sequence - + * glob will undo one level of + * escaping. + */ + argvs[j++] = arg[i++]; + argvs[j++] = arg[i]; + } else { + /* Unescape everything */ + /* XXX support \n and friends? */ + i++; + argvs[j++] = arg[i]; + } + } + } else if (arg[i] == '#') { + if (state == MA_SQUOTE || state == MA_DQUOTE) + argvs[j++] = arg[i]; + else + goto string_done; + } else if (arg[i] == '\0') { + if (state == MA_SQUOTE || state == MA_DQUOTE) { + if (sloppy) { + state = MA_UNQUOTED; + if (terminated != NULL) + *terminated = 0; + goto string_done; + } + error("Unterminated quoted argument"); + return NULL; + } + string_done: + if (state == MA_UNQUOTED) { + argvs[j++] = '\0'; + argc++; + } + break; + } else { + if (state == MA_START) { + argv[argc] = argvs + j; + state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; + } + if ((state == MA_SQUOTE || state == MA_DQUOTE) && + (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { + /* + * Special case for sftp: escape quoted + * glob(3) wildcards. NB. string can grow + * here. + */ + if (j >= sizeof(argvs) - 3) + goto args_too_longs; + argvs[j++] = '\\'; + argvs[j++] = arg[i]; + } else + argvs[j++] = arg[i]; + } + i++; + } + *argcp = argc; + return argv; +} + +static int +parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag, + int *fflag, int *hflag, int *iflag, int *lflag, int *pflag, + int *rflag, int *sflag, + unsigned long *n_arg, char **path1, char **path2) +{ + const char *cmd, *cp = *cpp; + char *cp2, **argv; + int base = 0; + long long ll; + int path1_mandatory = 0, i, cmdnum, optidx, argc; + + /* Skip leading whitespace */ + cp = cp + strspn(cp, WHITESPACE); + + /* + * Check for leading '-' (disable error processing) and '@' (suppress + * command echo) + */ + *ignore_errors = 0; + *disable_echo = 0; + for (;*cp != '\0'; cp++) { + if (*cp == '-') { + *ignore_errors = 1; + } else if (*cp == '@') { + *disable_echo = 1; + } else { + /* all other characters terminate prefix processing */ + break; + } + } + cp = cp + strspn(cp, WHITESPACE); + + /* Ignore blank lines and lines which begin with comment '#' char */ + if (*cp == '\0' || *cp == '#') + return (0); + + if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) + return -1; + + /* Figure out which command we have */ + for (i = 0; cmds[i].c != NULL; i++) { + if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0) + break; + } + cmdnum = cmds[i].n; + cmd = cmds[i].c; + + /* Special case */ + if (*cp == '!') { + cp++; + cmdnum = I_SHELL; + } else if (cmdnum == -1) { + error("Invalid command."); + return -1; + } + + /* Get arguments and parse flags */ + *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0; + *rflag = *sflag = 0; + *path1 = *path2 = NULL; + optidx = 1; + switch (cmdnum) { + case I_GET: + case I_REGET: + case I_REPUT: + case I_PUT: + if ((optidx = parse_getput_flags(cmd, argv, argc, + aflag, fflag, pflag, rflag)) == -1) + return -1; + /* Get first pathname (mandatory) */ + if (argc - optidx < 1) { + error("You must specify at least one path after a " + "%s command.", cmd); + return -1; + } + *path1 = xstrdup(argv[optidx]); + /* Get second pathname (optional) */ + if (argc - optidx > 1) { + *path2 = xstrdup(argv[optidx + 1]); + /* Destination is not globbed */ + undo_glob_escape(*path2); + } + break; + case I_LINK: + if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) + return -1; + goto parse_two_paths; + case I_COPY: + if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) + return -1; + goto parse_two_paths; + case I_RENAME: + if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1) + return -1; + goto parse_two_paths; + case I_SYMLINK: + if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) + return -1; + parse_two_paths: + if (argc - optidx < 2) { + error("You must specify two paths after a %s " + "command.", cmd); + return -1; + } + *path1 = xstrdup(argv[optidx]); + *path2 = xstrdup(argv[optidx + 1]); + /* Paths are not globbed */ + undo_glob_escape(*path1); + undo_glob_escape(*path2); + break; + case I_RM: + case I_MKDIR: + case I_RMDIR: + case I_LMKDIR: + path1_mandatory = 1; + /* FALLTHROUGH */ + case I_CHDIR: + case I_LCHDIR: + if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) + return -1; + /* Get pathname (mandatory) */ + if (argc - optidx < 1) { + if (!path1_mandatory) + break; /* return a NULL path1 */ + error("You must specify a path after a %s command.", + cmd); + return -1; + } + *path1 = xstrdup(argv[optidx]); + /* Only "rm" globs */ + if (cmdnum != I_RM) + undo_glob_escape(*path1); + break; + case I_DF: + if ((optidx = parse_df_flags(cmd, argv, argc, hflag, + iflag)) == -1) + return -1; + /* Default to current directory if no path specified */ + if (argc - optidx < 1) + *path1 = NULL; + else { + *path1 = xstrdup(argv[optidx]); + undo_glob_escape(*path1); + } + break; + case I_LS: + if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) + return(-1); + /* Path is optional */ + if (argc - optidx > 0) + *path1 = xstrdup(argv[optidx]); + break; + case I_LLS: + /* Skip ls command and following whitespace */ + cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); + case I_SHELL: + /* Uses the rest of the line */ + break; + case I_LUMASK: + case I_CHMOD: + base = 8; + /* FALLTHROUGH */ + case I_CHOWN: + case I_CHGRP: + if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1) + return -1; + /* Get numeric arg (mandatory) */ + if (argc - optidx < 1) + goto need_num_arg; + errno = 0; + ll = strtoll(argv[optidx], &cp2, base); + if (cp2 == argv[optidx] || *cp2 != '\0' || + ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) || + ll < 0 || ll > UINT32_MAX) { + need_num_arg: + error("You must supply a numeric argument " + "to the %s command.", cmd); + return -1; + } + *n_arg = ll; + if (cmdnum == I_LUMASK) + break; + /* Get pathname (mandatory) */ + if (argc - optidx < 2) { + error("You must specify a path after a %s command.", + cmd); + return -1; + } + *path1 = xstrdup(argv[optidx + 1]); + break; + case I_QUIT: + case I_PWD: + case I_LPWD: + case I_HELP: + case I_VERSION: + case I_PROGRESS: + if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) + return -1; + break; + default: + fatal("Command not implemented"); + } + + *cpp = cp; + return(cmdnum); +} + +static int +parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, + const char *startdir, int err_abort, int echo_command) +{ + const char *ocmd = cmd; + char *path1, *path2, *tmp; + int ignore_errors = 0, disable_echo = 1; + int aflag = 0, fflag = 0, hflag = 0, iflag = 0; + int lflag = 0, pflag = 0, rflag = 0, sflag = 0; + int cmdnum, i; + unsigned long n_arg = 0; + Attrib a, *aa; + char path_buf[PATH_MAX]; + int err = 0; + glob_t g; + + path1 = path2 = NULL; + cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag, + &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, + &path1, &path2); + if (ignore_errors != 0) + err_abort = 0; + + if (echo_command && !disable_echo) + mprintf("sftp> %s\n", ocmd); + + memset(&g, 0, sizeof(g)); + + /* Perform command */ + switch (cmdnum) { + case 0: + /* Blank line */ + break; + case -1: + /* Unrecognized command */ + err = -1; + break; + case I_REGET: + aflag = 1; + /* FALLTHROUGH */ + case I_GET: + err = process_get(conn, path1, path2, *pwd, pflag, + rflag, aflag, fflag); + break; + case I_REPUT: + aflag = 1; + /* FALLTHROUGH */ + case I_PUT: + err = process_put(conn, path1, path2, *pwd, pflag, + rflag, aflag, fflag); + break; + case I_COPY: + path1 = make_absolute(path1, *pwd); + path2 = make_absolute(path2, *pwd); + err = do_copy(conn, path1, path2); + break; + case I_RENAME: + path1 = make_absolute(path1, *pwd); + path2 = make_absolute(path2, *pwd); + err = do_rename(conn, path1, path2, lflag); + break; + case I_SYMLINK: + sflag = 1; + /* FALLTHROUGH */ + case I_LINK: + if (!sflag) + path1 = make_absolute(path1, *pwd); + path2 = make_absolute(path2, *pwd); + err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); + break; + case I_RM: + path1 = make_absolute(path1, *pwd); + remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + if (!quiet) + mprintf("Removing %s\n", g.gl_pathv[i]); + err = do_rm(conn, g.gl_pathv[i]); + if (err != 0 && err_abort) + break; + } + break; + case I_MKDIR: + path1 = make_absolute(path1, *pwd); + attrib_clear(&a); + a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = 0777; + err = do_mkdir(conn, path1, &a, 1); + break; + case I_RMDIR: + path1 = make_absolute(path1, *pwd); + err = do_rmdir(conn, path1); + break; + case I_CHDIR: + if (path1 == NULL || *path1 == '\0') + path1 = xstrdup(startdir); + path1 = make_absolute(path1, *pwd); + if ((tmp = do_realpath(conn, path1)) == NULL) { + err = 1; + break; + } + if ((aa = do_stat(conn, tmp, 0)) == NULL) { + free(tmp); + err = 1; + break; + } + if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { + error("Can't change directory: Can't check target"); + free(tmp); + err = 1; + break; + } + if (!S_ISDIR(aa->perm)) { + error("Can't change directory: \"%s\" is not " + "a directory", tmp); + free(tmp); + err = 1; + break; + } + free(*pwd); + *pwd = tmp; + break; + case I_LS: + if (!path1) { + do_ls_dir(conn, *pwd, *pwd, lflag); + break; + } + + /* Strip pwd off beginning of non-absolute paths */ + tmp = NULL; + if (!path_absolute(path1)) + tmp = *pwd; + + path1 = make_absolute(path1, *pwd); + err = do_globbed_ls(conn, path1, tmp, lflag); + break; + case I_DF: + /* Default to current directory if no path specified */ + if (path1 == NULL) + path1 = xstrdup(*pwd); + path1 = make_absolute(path1, *pwd); + err = do_df(conn, path1, hflag, iflag); + break; + case I_LCHDIR: + if (path1 == NULL || *path1 == '\0') + path1 = xstrdup("~"); + tmp = tilde_expand_filename(path1, getuid()); + free(path1); + path1 = tmp; + if (chdir(path1) == -1) { + error("Couldn't change local directory to " + "\"%s\": %s", path1, strerror(errno)); + err = 1; + } + break; + case I_LMKDIR: + if (mkdir(path1, 0777) == -1) { + error("Couldn't create local directory " + "\"%s\": %s", path1, strerror(errno)); + err = 1; + } + break; + case I_LLS: + local_do_ls(cmd); + break; + case I_SHELL: + local_do_shell(cmd); + break; + case I_LUMASK: + umask(n_arg); + printf("Local umask: %03lo\n", n_arg); + break; + case I_CHMOD: + path1 = make_absolute(path1, *pwd); + attrib_clear(&a); + a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = n_arg; + remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + if (!quiet) + mprintf("Changing mode on %s\n", + g.gl_pathv[i]); + err = (hflag ? do_lsetstat : do_setstat)(conn, + g.gl_pathv[i], &a); + if (err != 0 && err_abort) + break; + } + break; + case I_CHOWN: + case I_CHGRP: + path1 = make_absolute(path1, *pwd); + remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + if (!(aa = (hflag ? do_lstat : do_stat)(conn, + g.gl_pathv[i], 0))) { + if (err_abort) { + err = -1; + break; + } else + continue; + } + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + if (err_abort) { + err = -1; + break; + } else + continue; + } + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + if (cmdnum == I_CHOWN) { + if (!quiet) + mprintf("Changing owner on %s\n", + g.gl_pathv[i]); + aa->uid = n_arg; + } else { + if (!quiet) + mprintf("Changing group on %s\n", + g.gl_pathv[i]); + aa->gid = n_arg; + } + err = (hflag ? do_lsetstat : do_setstat)(conn, + g.gl_pathv[i], aa); + if (err != 0 && err_abort) + break; + } + break; + case I_PWD: + mprintf("Remote working directory: %s\n", *pwd); + break; + case I_LPWD: + if (!getcwd(path_buf, sizeof(path_buf))) { + error("Couldn't get local cwd: %s", strerror(errno)); + err = -1; + break; + } + mprintf("Local working directory: %s\n", path_buf); + break; + case I_QUIT: + /* Processed below */ + break; + case I_HELP: + help(); + break; + case I_VERSION: + printf("SFTP protocol version %u\n", sftp_proto_version(conn)); + break; + case I_PROGRESS: + showprogress = !showprogress; + if (showprogress) + printf("Progress meter enabled\n"); + else + printf("Progress meter disabled\n"); + break; + default: + fatal("%d is not implemented", cmdnum); + } + + if (g.gl_pathc) + globfree(&g); + free(path1); + free(path2); + + /* If an unignored error occurs in batch mode we should abort. */ + if (err_abort && err != 0) + return (-1); + else if (cmdnum == I_QUIT) + return (1); + + return (0); +} + +#ifdef USE_LIBEDIT +static char * +prompt(EditLine *el) +{ + return ("sftp> "); +} + +/* Display entries in 'list' after skipping the first 'len' chars */ +static void +complete_display(char **list, u_int len) +{ + u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; + struct winsize ws; + char *tmp; + + /* Count entries for sort and find longest */ + for (y = 0; list[y]; y++) + m = MAXIMUM(m, strlen(list[y])); + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + + m = m > len ? m - len : 0; + columns = width / (m + 2); + columns = MAXIMUM(columns, 1); + colspace = width / columns; + colspace = MINIMUM(colspace, width); + + printf("\n"); + m = 1; + for (y = 0; list[y]; y++) { + llen = strlen(list[y]); + tmp = llen > len ? list[y] + len : ""; + mprintf("%-*s", colspace, tmp); + if (m >= columns) { + printf("\n"); + m = 1; + } else + m++; + } + printf("\n"); +} + +/* + * Given a "list" of words that begin with a common prefix of "word", + * attempt to find an autocompletion to extends "word" by the next + * characters common to all entries in "list". + */ +static char * +complete_ambiguous(const char *word, char **list, size_t count) +{ + if (word == NULL) + return NULL; + + if (count > 0) { + u_int y, matchlen = strlen(list[0]); + + /* Find length of common stem */ + for (y = 1; list[y]; y++) { + u_int x; + + for (x = 0; x < matchlen; x++) + if (list[0][x] != list[y][x]) + break; + + matchlen = x; + } + + if (matchlen > strlen(word)) { + char *tmp = xstrdup(list[0]); + + tmp[matchlen] = '\0'; + return tmp; + } + } + + return xstrdup(word); +} + +/* Autocomplete a sftp command */ +static int +complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, + int terminated) +{ + u_int y, count = 0, cmdlen, tmplen; + char *tmp, **list, argterm[3]; + const LineInfo *lf; + + list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); + + /* No command specified: display all available commands */ + if (cmd == NULL) { + for (y = 0; cmds[y].c; y++) + list[count++] = xstrdup(cmds[y].c); + + list[count] = NULL; + complete_display(list, 0); + + for (y = 0; list[y] != NULL; y++) + free(list[y]); + free(list); + return count; + } + + /* Prepare subset of commands that start with "cmd" */ + cmdlen = strlen(cmd); + for (y = 0; cmds[y].c; y++) { + if (!strncasecmp(cmd, cmds[y].c, cmdlen)) + list[count++] = xstrdup(cmds[y].c); + } + list[count] = NULL; + + if (count == 0) { + free(list); + return 0; + } + + /* Complete ambiguous command */ + tmp = complete_ambiguous(cmd, list, count); + if (count > 1) + complete_display(list, 0); + + for (y = 0; list[y]; y++) + free(list[y]); + free(list); + + if (tmp != NULL) { + tmplen = strlen(tmp); + cmdlen = strlen(cmd); + /* If cmd may be extended then do so */ + if (tmplen > cmdlen) + if (el_insertstr(el, tmp + cmdlen) == -1) + fatal("el_insertstr failed."); + lf = el_line(el); + /* Terminate argument cleanly */ + if (count == 1) { + y = 0; + if (!terminated) + argterm[y++] = quote; + if (lastarg || *(lf->cursor) != ' ') + argterm[y++] = ' '; + argterm[y] = '\0'; + if (y > 0 && el_insertstr(el, argterm) == -1) + fatal("el_insertstr failed."); + } + free(tmp); + } + + return count; +} + +/* + * Determine whether a particular sftp command's arguments (if any) + * represent local or remote files. + */ +static int +complete_is_remote(char *cmd) { + int i; + + if (cmd == NULL) + return -1; + + for (i = 0; cmds[i].c; i++) { + if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) + return cmds[i].t; + } + + return -1; +} + +/* Autocomplete a filename "file" */ +static int +complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, + char *file, int remote, int lastarg, char quote, int terminated) +{ + glob_t g; + char *tmp, *tmp2, ins[8]; + u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; + int clen; + const LineInfo *lf; + + /* Glob from "file" location */ + if (file == NULL) + tmp = xstrdup("*"); + else + xasprintf(&tmp, "%s*", file); + + /* Check if the path is absolute. */ + isabs = path_absolute(tmp); + + memset(&g, 0, sizeof(g)); + if (remote != LOCAL) { + tmp = make_absolute(tmp, remote_path); + remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + } else + glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + + /* Determine length of pwd so we can trim completion display */ + for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { + /* Terminate counting on first unescaped glob metacharacter */ + if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { + if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') + hadglob = 1; + break; + } + if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') + tmplen++; + if (tmp[tmplen] == '/') + pwdlen = tmplen + 1; /* track last seen '/' */ + } + free(tmp); + tmp = NULL; + + if (g.gl_matchc == 0) + goto out; + + if (g.gl_matchc > 1) + complete_display(g.gl_pathv, pwdlen); + + /* Don't try to extend globs */ + if (file == NULL || hadglob) + goto out; + + tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); + tmp = path_strip(tmp2, isabs ? NULL : remote_path); + free(tmp2); + + if (tmp == NULL) + goto out; + + tmplen = strlen(tmp); + filelen = strlen(file); + + /* Count the number of escaped characters in the input string. */ + cesc = isesc = 0; + for (i = 0; i < filelen; i++) { + if (!isesc && file[i] == '\\' && i + 1 < filelen){ + isesc = 1; + cesc++; + } else + isesc = 0; + } + + if (tmplen > (filelen - cesc)) { + tmp2 = tmp + filelen - cesc; + len = strlen(tmp2); + /* quote argument on way out */ + for (i = 0; i < len; i += clen) { + if ((clen = mblen(tmp2 + i, len - i)) < 0 || + (size_t)clen > sizeof(ins) - 2) + fatal("invalid multibyte character"); + ins[0] = '\\'; + memcpy(ins + 1, tmp2 + i, clen); + ins[clen + 1] = '\0'; + switch (tmp2[i]) { + case '\'': + case '"': + case '\\': + case '\t': + case '[': + case ' ': + case '#': + case '*': + if (quote == '\0' || tmp2[i] == quote) { + if (el_insertstr(el, ins) == -1) + fatal("el_insertstr " + "failed."); + break; + } + /* FALLTHROUGH */ + default: + if (el_insertstr(el, ins + 1) == -1) + fatal("el_insertstr failed."); + break; + } + } + } + + lf = el_line(el); + if (g.gl_matchc == 1) { + i = 0; + if (!terminated && quote != '\0') + ins[i++] = quote; + if (*(lf->cursor - 1) != '/' && + (lastarg || *(lf->cursor) != ' ')) + ins[i++] = ' '; + ins[i] = '\0'; + if (i > 0 && el_insertstr(el, ins) == -1) + fatal("el_insertstr failed."); + } + free(tmp); + + out: + globfree(&g); + return g.gl_matchc; +} + +/* tab-completion hook function, called via libedit */ +static unsigned char +complete(EditLine *el, int ch) +{ + char **argv, *line, quote; + int argc, carg; + u_int cursor, len, terminated, ret = CC_ERROR; + const LineInfo *lf; + struct complete_ctx *complete_ctx; + + lf = el_line(el); + if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) + fatal_f("el_get failed"); + + /* Figure out which argument the cursor points to */ + cursor = lf->cursor - lf->buffer; + line = xmalloc(cursor + 1); + memcpy(line, lf->buffer, cursor); + line[cursor] = '\0'; + argv = makeargv(line, &carg, 1, "e, &terminated); + free(line); + + /* Get all the arguments on the line */ + len = lf->lastchar - lf->buffer; + line = xmalloc(len + 1); + memcpy(line, lf->buffer, len); + line[len] = '\0'; + argv = makeargv(line, &argc, 1, NULL, NULL); + + /* Ensure cursor is at EOL or a argument boundary */ + if (line[cursor] != ' ' && line[cursor] != '\0' && + line[cursor] != '\n') { + free(line); + return ret; + } + + if (carg == 0) { + /* Show all available commands */ + complete_cmd_parse(el, NULL, argc == carg, '\0', 1); + ret = CC_REDISPLAY; + } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { + /* Handle the command parsing */ + if (complete_cmd_parse(el, argv[0], argc == carg, + quote, terminated) != 0) + ret = CC_REDISPLAY; + } else if (carg >= 1) { + /* Handle file parsing */ + int remote = complete_is_remote(argv[0]); + char *filematch = NULL; + + if (carg > 1 && line[cursor-1] != ' ') + filematch = argv[carg - 1]; + + if (remote != 0 && + complete_match(el, complete_ctx->conn, + *complete_ctx->remote_pathp, filematch, + remote, carg == argc, quote, terminated) != 0) + ret = CC_REDISPLAY; + } + + free(line); + return ret; +} +#endif /* USE_LIBEDIT */ + +static int +interactive_loop(struct sftp_conn *conn, char *file1, char *file2) +{ + char *remote_path; + char *dir = NULL, *startdir = NULL; + char cmd[2048]; + int err, interactive; + EditLine *el = NULL; +#ifdef USE_LIBEDIT + History *hl = NULL; + HistEvent hev; + extern char *__progname; + struct complete_ctx complete_ctx; + + if (!batchmode && isatty(STDIN_FILENO)) { + if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) + fatal("Couldn't initialise editline"); + if ((hl = history_init()) == NULL) + fatal("Couldn't initialise editline history"); + history(hl, &hev, H_SETSIZE, 100); + el_set(el, EL_HIST, history, hl); + + el_set(el, EL_PROMPT, prompt); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_TERMINAL, NULL); + el_set(el, EL_SIGNAL, 1); + el_source(el, NULL); + + /* Tab Completion */ + el_set(el, EL_ADDFN, "ftp-complete", + "Context sensitive argument completion", complete); + complete_ctx.conn = conn; + complete_ctx.remote_pathp = &remote_path; + el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); + el_set(el, EL_BIND, "^I", "ftp-complete", NULL); + /* enable ctrl-left-arrow and ctrl-right-arrow */ + el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL); + el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL); + el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); + el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); + /* make ^w match ksh behaviour */ + el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL); + } +#endif /* USE_LIBEDIT */ + + remote_path = do_realpath(conn, "."); + if (remote_path == NULL) + fatal("Need cwd"); + startdir = xstrdup(remote_path); + + if (file1 != NULL) { + dir = xstrdup(file1); + dir = make_absolute(dir, remote_path); + + if (remote_is_dir(conn, dir) && file2 == NULL) { + if (!quiet) + mprintf("Changing to: %s\n", dir); + snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); + if (parse_dispatch_command(conn, cmd, + &remote_path, startdir, 1, 0) != 0) { + free(dir); + free(startdir); + free(remote_path); + free(conn); + return (-1); + } + } else { + /* XXX this is wrong wrt quoting */ + snprintf(cmd, sizeof cmd, "get%s %s%s%s", + global_aflag ? " -a" : "", dir, + file2 == NULL ? "" : " ", + file2 == NULL ? "" : file2); + err = parse_dispatch_command(conn, cmd, + &remote_path, startdir, 1, 0); + free(dir); + free(startdir); + free(remote_path); + free(conn); + return (err); + } + free(dir); + } + + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(infile, NULL, _IOLBF, 0); + + interactive = !batchmode && isatty(STDIN_FILENO); + err = 0; + for (;;) { + struct sigaction sa; + + interrupted = 0; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = interactive ? read_interrupt : killchild; + if (sigaction(SIGINT, &sa, NULL) == -1) { + debug3("sigaction(%s): %s", strsignal(SIGINT), + strerror(errno)); + break; + } + if (el == NULL) { + if (interactive) + printf("sftp> "); + if (fgets(cmd, sizeof(cmd), infile) == NULL) { + if (interactive) + printf("\n"); + if (interrupted) + continue; + break; + } + } else { +#ifdef USE_LIBEDIT + const char *line; + int count = 0; + + if ((line = el_gets(el, &count)) == NULL || + count <= 0) { + printf("\n"); + if (interrupted) + continue; + break; + } + history(hl, &hev, H_ENTER, line); + if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { + fprintf(stderr, "Error: input line too long\n"); + continue; + } +#endif /* USE_LIBEDIT */ + } + + cmd[strcspn(cmd, "\n")] = '\0'; + + /* Handle user interrupts gracefully during commands */ + interrupted = 0; + ssh_signal(SIGINT, cmd_interrupt); + + err = parse_dispatch_command(conn, cmd, &remote_path, + startdir, batchmode, !interactive && el == NULL); + if (err != 0) + break; + } + ssh_signal(SIGCHLD, SIG_DFL); + free(remote_path); + free(startdir); + free(conn); + +#ifdef USE_LIBEDIT + if (el != NULL) + el_end(el); +#endif /* USE_LIBEDIT */ + + /* err == 1 signifies normal "quit" exit */ + return (err >= 0 ? 0 : -1); +} + +static void +connect_to_server(char *path, char **args, int *in, int *out) +{ + int c_in, c_out; +#ifdef USE_PIPES + int pin[2], pout[2]; + + if ((pipe(pin) == -1) || (pipe(pout) == -1)) + fatal("pipe: %s", strerror(errno)); + *in = pin[0]; + *out = pout[1]; + c_in = pout[0]; + c_out = pin[1]; +#else /* USE_PIPES */ + int inout[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) + fatal("socketpair: %s", strerror(errno)); + *in = *out = inout[0]; + c_in = c_out = inout[1]; +#endif /* USE_PIPES */ + + if ((sshpid = fork()) == -1) + fatal("fork: %s", strerror(errno)); + else if (sshpid == 0) { + if ((dup2(c_in, STDIN_FILENO) == -1) || + (dup2(c_out, STDOUT_FILENO) == -1)) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + _exit(1); + } + close(*in); + close(*out); + close(c_in); + close(c_out); + + /* + * The underlying ssh is in the same process group, so we must + * ignore SIGINT if we want to gracefully abort commands, + * otherwise the signal will make it to the ssh process and + * kill it too. Contrawise, since sftp sends SIGTERMs to the + * underlying ssh, it must *not* ignore that signal. + */ + ssh_signal(SIGINT, SIG_IGN); + ssh_signal(SIGTERM, SIG_DFL); + execvp(path, args); + fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); + _exit(1); + } + + ssh_signal(SIGTERM, killchild); + ssh_signal(SIGINT, killchild); + ssh_signal(SIGHUP, killchild); + ssh_signal(SIGTSTP, suspchild); + ssh_signal(SIGTTIN, suspchild); + ssh_signal(SIGTTOU, suspchild); + ssh_signal(SIGCHLD, sigchld_handler); + close(c_in); + close(c_out); +} + +static void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, + "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" + " [-D sftp_server_path] [-F ssh_config] [-i identity_file]\n" + " [-J destination] [-l limit] [-o ssh_option] [-P port]\n" + " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n" + " destination\n", + __progname); + exit(1); +} + +int +main(int argc, char **argv) +{ + int in, out, ch, err, tmp, port = -1, noisy = 0; + char *host = NULL, *user, *cp, *file2 = NULL; + int debug_level = 0; + char *file1 = NULL, *sftp_server = NULL; + char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; + const char *errstr; + LogLevel ll = SYSLOG_LEVEL_INFO; + arglist args; + extern int optind; + extern char *optarg; + struct sftp_conn *conn; + size_t copy_buffer_len = 0; + size_t num_requests = 0; + long long limit_kbps = 0; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + msetlocale(); + + seed_rng(); + + __progname = ssh_get_progname(argv[0]); + memset(&args, '\0', sizeof(args)); + args.list = NULL; + addargs(&args, "%s", ssh_program); + addargs(&args, "-oForwardX11 no"); + addargs(&args, "-oPermitLocalCommand no"); + addargs(&args, "-oClearAllForwardings yes"); + + ll = SYSLOG_LEVEL_INFO; + infile = stdin; + + while ((ch = getopt(argc, argv, + "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) { + switch (ch) { + /* Passed through to ssh(1) */ + case 'A': + case '4': + case '6': + case 'C': + addargs(&args, "-%c", ch); + break; + /* Passed through to ssh(1) with argument */ + case 'F': + case 'J': + case 'c': + case 'i': + case 'o': + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); + break; + case 'q': + ll = SYSLOG_LEVEL_ERROR; + quiet = 1; + showprogress = 0; + addargs(&args, "-%c", ch); + break; + case 'P': + port = a2port(optarg); + if (port <= 0) + fatal("Bad port \"%s\"\n", optarg); + break; + case 'v': + if (debug_level < 3) { + addargs(&args, "-v"); + ll = SYSLOG_LEVEL_DEBUG1 + debug_level; + } + debug_level++; + break; + case '1': + fatal("SSH protocol v.1 is no longer supported"); + break; + case '2': + /* accept silently */ + break; + case 'a': + global_aflag = 1; + break; + case 'B': + copy_buffer_len = strtol(optarg, &cp, 10); + if (copy_buffer_len == 0 || *cp != '\0') + fatal("Invalid buffer size \"%s\"", optarg); + break; + case 'b': + if (batchmode) + fatal("Batch file already specified."); + + /* Allow "-" as stdin */ + if (strcmp(optarg, "-") != 0 && + (infile = fopen(optarg, "r")) == NULL) + fatal("%s (%s).", strerror(errno), optarg); + showprogress = 0; + quiet = batchmode = 1; + addargs(&args, "-obatchmode yes"); + break; + case 'f': + global_fflag = 1; + break; + case 'N': + noisy = 1; /* Used to clear quiet mode after getopt */ + break; + case 'p': + global_pflag = 1; + break; + case 'D': + sftp_direct = optarg; + break; + case 'l': + limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, + &errstr); + if (errstr != NULL) + usage(); + limit_kbps *= 1024; /* kbps */ + break; + case 'r': + global_rflag = 1; + break; + case 'R': + num_requests = strtol(optarg, &cp, 10); + if (num_requests == 0 || *cp != '\0') + fatal("Invalid number of requests \"%s\"", + optarg); + break; + case 's': + sftp_server = optarg; + break; + case 'S': + ssh_program = optarg; + replacearg(&args, 0, "%s", ssh_program); + break; + case 'h': + default: + usage(); + } + } + + /* Do this last because we want the user to be able to override it */ + addargs(&args, "-oForwardAgent no"); + + if (!isatty(STDERR_FILENO)) + showprogress = 0; + + if (noisy) + quiet = 0; + + log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); + + if (sftp_direct == NULL) { + if (optind == argc || argc > (optind + 2)) + usage(); + argv += optind; + + switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) { + case -1: + usage(); + break; + case 0: + if (tmp != -1) + port = tmp; + break; + default: + /* Try with user, host and path. */ + if (parse_user_host_path(*argv, &user, &host, + &file1) == 0) + break; + /* Try with user and host. */ + if (parse_user_host_port(*argv, &user, &host, NULL) + == 0) + break; + /* Treat as a plain hostname. */ + host = xstrdup(*argv); + host = cleanhostname(host); + break; + } + file2 = *(argv + 1); + + if (!*host) { + fprintf(stderr, "Missing hostname\n"); + usage(); + } + + if (port != -1) + addargs(&args, "-oPort %d", port); + if (user != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", user); + } + + /* no subsystem if the server-spec contains a '/' */ + if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) + addargs(&args, "-s"); + + addargs(&args, "--"); + addargs(&args, "%s", host); + addargs(&args, "%s", (sftp_server != NULL ? + sftp_server : "sftp")); + + connect_to_server(ssh_program, args.list, &in, &out); + } else { + args.list = NULL; + addargs(&args, "sftp-server"); + + connect_to_server(sftp_direct, args.list, &in, &out); + } + freeargs(&args); + + conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); + if (conn == NULL) + fatal("Couldn't initialise connection to server"); + + if (!quiet) { + if (sftp_direct == NULL) + fprintf(stderr, "Connected to %s.\n", host); + else + fprintf(stderr, "Attached to %s.\n", sftp_direct); + } + + err = interactive_loop(conn, file1, file2); + +#if !defined(USE_PIPES) + shutdown(in, SHUT_RDWR); + shutdown(out, SHUT_RDWR); +#endif + + close(in); + close(out); + if (batchmode) + fclose(infile); + + while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1) + if (errno != EINTR) + fatal("Couldn't wait for ssh process: %s", + strerror(errno)); + + exit(err == 0 ? 0 : 1); +} diff --git a/aosp/external/openssh/sftp.h b/aosp/external/openssh/sftp.h new file mode 100644 index 0000000000000000000000000000000000000000..2bde8bb7ff0aa36f4362b5e28ecda49a332abcea --- /dev/null +++ b/aosp/external/openssh/sftp.h @@ -0,0 +1,101 @@ +/* $OpenBSD: sftp.h,v 1.9 2008/06/13 00:12:02 dtucker Exp $ */ + +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * draft-ietf-secsh-filexfer-01.txt + */ + +/* version */ +#define SSH2_FILEXFER_VERSION 3 + +/* client to server */ +#define SSH2_FXP_INIT 1 +#define SSH2_FXP_OPEN 3 +#define SSH2_FXP_CLOSE 4 +#define SSH2_FXP_READ 5 +#define SSH2_FXP_WRITE 6 +#define SSH2_FXP_LSTAT 7 +#define SSH2_FXP_STAT_VERSION_0 7 +#define SSH2_FXP_FSTAT 8 +#define SSH2_FXP_SETSTAT 9 +#define SSH2_FXP_FSETSTAT 10 +#define SSH2_FXP_OPENDIR 11 +#define SSH2_FXP_READDIR 12 +#define SSH2_FXP_REMOVE 13 +#define SSH2_FXP_MKDIR 14 +#define SSH2_FXP_RMDIR 15 +#define SSH2_FXP_REALPATH 16 +#define SSH2_FXP_STAT 17 +#define SSH2_FXP_RENAME 18 +#define SSH2_FXP_READLINK 19 +#define SSH2_FXP_SYMLINK 20 + +/* server to client */ +#define SSH2_FXP_VERSION 2 +#define SSH2_FXP_STATUS 101 +#define SSH2_FXP_HANDLE 102 +#define SSH2_FXP_DATA 103 +#define SSH2_FXP_NAME 104 +#define SSH2_FXP_ATTRS 105 + +#define SSH2_FXP_EXTENDED 200 +#define SSH2_FXP_EXTENDED_REPLY 201 + +/* attributes */ +#define SSH2_FILEXFER_ATTR_SIZE 0x00000001 +#define SSH2_FILEXFER_ATTR_UIDGID 0x00000002 +#define SSH2_FILEXFER_ATTR_PERMISSIONS 0x00000004 +#define SSH2_FILEXFER_ATTR_ACMODTIME 0x00000008 +#define SSH2_FILEXFER_ATTR_EXTENDED 0x80000000 + +/* portable open modes */ +#define SSH2_FXF_READ 0x00000001 +#define SSH2_FXF_WRITE 0x00000002 +#define SSH2_FXF_APPEND 0x00000004 +#define SSH2_FXF_CREAT 0x00000008 +#define SSH2_FXF_TRUNC 0x00000010 +#define SSH2_FXF_EXCL 0x00000020 + +/* statvfs@openssh.com f_flag flags */ +#define SSH2_FXE_STATVFS_ST_RDONLY 0x00000001 +#define SSH2_FXE_STATVFS_ST_NOSUID 0x00000002 + +/* status messages */ +#define SSH2_FX_OK 0 +#define SSH2_FX_EOF 1 +#define SSH2_FX_NO_SUCH_FILE 2 +#define SSH2_FX_PERMISSION_DENIED 3 +#define SSH2_FX_FAILURE 4 +#define SSH2_FX_BAD_MESSAGE 5 +#define SSH2_FX_NO_CONNECTION 6 +#define SSH2_FX_CONNECTION_LOST 7 +#define SSH2_FX_OP_UNSUPPORTED 8 +#define SSH2_FX_MAX 8 + +struct passwd; + +int sftp_server_main(int, char **, struct passwd *); +void sftp_server_cleanup_exit(int) __attribute__((noreturn)); diff --git a/aosp/external/openssh/sk-api.h b/aosp/external/openssh/sk-api.h new file mode 100644 index 0000000000000000000000000000000000000000..34e110b4e320aac9c13ebc925fb353bf4f2e580b --- /dev/null +++ b/aosp/external/openssh/sk-api.h @@ -0,0 +1,101 @@ +/* $OpenBSD: sk-api.h,v 1.14 2021/11/02 22:56:40 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SK_API_H +#define _SK_API_H 1 + +#include +#ifdef HAVE_STDINT_H +#include +#endif + +/* Flags */ +#define SSH_SK_USER_PRESENCE_REQD 0x01 +#define SSH_SK_USER_VERIFICATION_REQD 0x04 +#define SSH_SK_RESIDENT_KEY 0x20 + +/* Algs */ +#define SSH_SK_ECDSA 0x00 +#define SSH_SK_ED25519 0x01 + +/* Error codes */ +#define SSH_SK_ERR_GENERAL -1 +#define SSH_SK_ERR_UNSUPPORTED -2 +#define SSH_SK_ERR_PIN_REQUIRED -3 +#define SSH_SK_ERR_DEVICE_NOT_FOUND -4 + +struct sk_enroll_response { + uint8_t flags; + uint8_t *public_key; + size_t public_key_len; + uint8_t *key_handle; + size_t key_handle_len; + uint8_t *signature; + size_t signature_len; + uint8_t *attestation_cert; + size_t attestation_cert_len; + uint8_t *authdata; + size_t authdata_len; +}; + +struct sk_sign_response { + uint8_t flags; + uint32_t counter; + uint8_t *sig_r; + size_t sig_r_len; + uint8_t *sig_s; + size_t sig_s_len; +}; + +struct sk_resident_key { + uint32_t alg; + size_t slot; + char *application; + struct sk_enroll_response key; + uint8_t flags; + uint8_t *user_id; + size_t user_id_len; +}; + +struct sk_option { + char *name; + char *value; + uint8_t required; +}; + +#define SSH_SK_VERSION_MAJOR 0x00090000 /* current API version */ +#define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 + +/* Return the version of the middleware API */ +uint32_t sk_api_version(void); + +/* Enroll a U2F key (private key generation) */ +int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, const char *pin, + struct sk_option **options, struct sk_enroll_response **enroll_response); + +/* Sign a challenge */ +int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len, + const char *application, const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response); + +/* Enumerate all resident keys */ +int sk_load_resident_keys(const char *pin, struct sk_option **options, + struct sk_resident_key ***rks, size_t *nrks); + +#endif /* _SK_API_H */ diff --git a/aosp/external/openssh/sk-usbhid.c b/aosp/external/openssh/sk-usbhid.c new file mode 100644 index 0000000000000000000000000000000000000000..2d36ac337ffa3e9f14b81ad0f1f80299c11772a9 --- /dev/null +++ b/aosp/external/openssh/sk-usbhid.c @@ -0,0 +1,1400 @@ +/* $OpenBSD: sk-usbhid.c,v 1.38 2022/02/07 01:25:12 djm Exp $ */ +/* + * Copyright (c) 2019 Markus Friedl + * Copyright (c) 2020 Pedro Martelletto + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef ENABLE_SK_INTERNAL + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SHA2_H +#include +#endif + +/* + * Almost every use of OpenSSL in this file is for ECDSA-NISTP256. + * This is strictly a larger hammer than necessary, but it reduces changes + * with upstream. + */ +#ifndef OPENSSL_HAS_ECC +# undef WITH_OPENSSL +#endif + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#include +#include +#endif /* WITH_OPENSSL */ + +#include +#include + +/* backwards compat for libfido2 */ +#ifndef HAVE_FIDO_CRED_PROT +#define fido_cred_prot(x) (0) +#endif +#ifndef HAVE_FIDO_CRED_SET_PROT +#define fido_cred_set_prot(x, y) (FIDO_ERR_UNSUPPORTED_OPTION) +#endif +#ifndef HAVE_FIDO_DEV_SUPPORTS_CRED_PROT +#define fido_dev_supports_cred_prot(x) (0) +#endif +#ifndef HAVE_FIDO_DEV_GET_TOUCH_BEGIN +#define fido_dev_get_touch_begin(x) (FIDO_ERR_UNSUPPORTED_OPTION) +#endif +#ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS +#define fido_dev_get_touch_status(x, y, z) (FIDO_ERR_UNSUPPORTED_OPTION) +#endif +#ifndef FIDO_CRED_PROT_UV_REQUIRED +#define FIDO_CRED_PROT_UV_REQUIRED 0 +#endif +#ifndef FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID +#define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0 +#endif + +#ifndef SK_STANDALONE +# include "log.h" +# include "xmalloc.h" +# include "misc.h" +/* + * If building as part of OpenSSH, then rename exported functions. + * This must be done before including sk-api.h. + */ +# define sk_api_version ssh_sk_api_version +# define sk_enroll ssh_sk_enroll +# define sk_sign ssh_sk_sign +# define sk_load_resident_keys ssh_sk_load_resident_keys +#endif /* !SK_STANDALONE */ + +#include "sk-api.h" + +/* #define SK_DEBUG 1 */ + +#ifdef SK_DEBUG +#define SSH_FIDO_INIT_ARG FIDO_DEBUG +#else +#define SSH_FIDO_INIT_ARG 0 +#endif + +#define MAX_FIDO_DEVICES 8 +#define FIDO_POLL_MS 50 +#define SELECT_MS 15000 +#define POLL_SLEEP_NS 200000000 + +/* Compatibility with OpenSSH 1.0.x */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +#define ECDSA_SIG_get0(sig, pr, ps) \ + do { \ + (*pr) = sig->r; \ + (*ps) = sig->s; \ + } while (0) +#endif +#ifndef FIDO_ERR_OPERATION_DENIED +#define FIDO_ERR_OPERATION_DENIED 0x27 +#endif + +struct sk_usbhid { + fido_dev_t *dev; + char *path; +}; + +/* Return the version of the middleware API */ +uint32_t sk_api_version(void); + +/* Enroll a U2F key (private key generation) */ +int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, const char *pin, + struct sk_option **options, struct sk_enroll_response **enroll_response); + +/* Sign a challenge */ +int sk_sign(uint32_t alg, const uint8_t *data, size_t data_len, + const char *application, const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response); + +/* Load resident keys */ +int sk_load_resident_keys(const char *pin, struct sk_option **options, + struct sk_resident_key ***rks, size_t *nrks); + +static void skdebug(const char *func, const char *fmt, ...) + __attribute__((__format__ (printf, 2, 3))); + +static void +skdebug(const char *func, const char *fmt, ...) +{ +#if !defined(SK_STANDALONE) + char *msg; + va_list ap; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + debug("%s: %s", func, msg); + free(msg); +#elif defined(SK_DEBUG) + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s: ", func); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); +#else + (void)func; /* XXX */ + (void)fmt; /* XXX */ +#endif +} + +uint32_t +sk_api_version(void) +{ + return SSH_SK_VERSION_MAJOR; +} + +static struct sk_usbhid * +sk_open(const char *path) +{ + struct sk_usbhid *sk; + int r; + + if (path == NULL) { + skdebug(__func__, "path == NULL"); + return NULL; + } + if ((sk = calloc(1, sizeof(*sk))) == NULL) { + skdebug(__func__, "calloc sk failed"); + return NULL; + } + if ((sk->path = strdup(path)) == NULL) { + skdebug(__func__, "strdup path failed"); + free(sk); + return NULL; + } + if ((sk->dev = fido_dev_new()) == NULL) { + skdebug(__func__, "fido_dev_new failed"); + free(sk->path); + free(sk); + return NULL; + } + if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) { + skdebug(__func__, "fido_dev_open %s failed: %s", sk->path, + fido_strerr(r)); + fido_dev_free(&sk->dev); + free(sk->path); + free(sk); + return NULL; + } + return sk; +} + +static void +sk_close(struct sk_usbhid *sk) +{ + if (sk == NULL) + return; + fido_dev_cancel(sk->dev); /* cancel any pending operation */ + fido_dev_close(sk->dev); + fido_dev_free(&sk->dev); + free(sk->path); + free(sk); +} + +static struct sk_usbhid ** +sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen) +{ + const fido_dev_info_t *di; + struct sk_usbhid **skv; + size_t i; + + *nopen = 0; + if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) { + skdebug(__func__, "calloc skv failed"); + return NULL; + } + for (i = 0; i < ndevs; i++) { + if ((di = fido_dev_info_ptr(devlist, i)) == NULL) + skdebug(__func__, "fido_dev_info_ptr failed"); + else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL) + skdebug(__func__, "sk_open failed"); + else + (*nopen)++; + } + if (*nopen == 0) { + for (i = 0; i < ndevs; i++) + sk_close(skv[i]); + free(skv); + skv = NULL; + } + + return skv; +} + +static void +sk_closev(struct sk_usbhid **skv, size_t nsk) +{ + size_t i; + + for (i = 0; i < nsk; i++) + sk_close(skv[i]); + free(skv); +} + +static int +sk_touch_begin(struct sk_usbhid **skv, size_t nsk) +{ + size_t i, ok = 0; + int r; + + for (i = 0; i < nsk; i++) + if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK) + skdebug(__func__, "fido_dev_get_touch_begin %s failed:" + " %s", skv[i]->path, fido_strerr(r)); + else + ok++; + + return ok ? 0 : -1; +} + +static int +sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx) +{ + struct timespec ts_pause; + size_t npoll, i; + int r; + + ts_pause.tv_sec = 0; + ts_pause.tv_nsec = POLL_SLEEP_NS; + nanosleep(&ts_pause, NULL); + npoll = nsk; + for (i = 0; i < nsk; i++) { + if (skv[i] == NULL) + continue; /* device discarded */ + skdebug(__func__, "polling %s", skv[i]->path); + if ((r = fido_dev_get_touch_status(skv[i]->dev, touch, + FIDO_POLL_MS)) != FIDO_OK) { + skdebug(__func__, "fido_dev_get_touch_status %s: %s", + skv[i]->path, fido_strerr(r)); + sk_close(skv[i]); /* discard device */ + skv[i] = NULL; + if (--npoll == 0) { + skdebug(__func__, "no device left to poll"); + return -1; + } + } else if (*touch) { + *idx = i; + return 0; + } + } + *touch = 0; + return 0; +} + +#if !defined(HAVE_FIDO_ASSERT_SET_CLIENTDATA) || \ + !defined(HAVE_FIDO_CRED_SET_CLIENTDATA) +/* Calculate SHA256(m) */ +static int +sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen) +{ +#ifdef WITH_OPENSSL + u_int mdlen; +#else + SHA2_CTX ctx; +#endif + + if (dlen != 32) + return -1; +#ifdef WITH_OPENSSL + mdlen = dlen; + if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL)) + return -1; +#else + SHA256Init(&ctx); + SHA256Update(&ctx, (const uint8_t *)m, mlen); + SHA256Final(d, &ctx); +#endif + return 0; +} +#endif /* !HAVE_FIDO_ASSERT_SET_CLIENTDATA || !HAVE_FIDO_CRED_SET_CLIENTDATA */ + +#ifndef HAVE_FIDO_CRED_SET_CLIENTDATA +static int +fido_cred_set_clientdata(fido_cred_t *cred, const u_char *ptr, size_t len) +{ + uint8_t d[32]; + int r; + + if (sha256_mem(ptr, len, d, sizeof(d)) != 0) { + skdebug(__func__, "hash challenge failed"); + return FIDO_ERR_INTERNAL; + } + r = fido_cred_set_clientdata_hash(cred, d, sizeof(d)); + explicit_bzero(d, sizeof(d)); + if (r != FIDO_OK) { + skdebug(__func__, "fido_cred_set_clientdata_hash failed: %s", + fido_strerr(r)); + } + return r; +} +#endif /* HAVE_FIDO_CRED_SET_CLIENTDATA */ + +#ifndef HAVE_FIDO_ASSERT_SET_CLIENTDATA +static int +fido_assert_set_clientdata(fido_assert_t *assert, const u_char *ptr, size_t len) +{ + uint8_t d[32]; + int r; + + if (sha256_mem(ptr, len, d, sizeof(d)) != 0) { + skdebug(__func__, "hash challenge failed"); + return FIDO_ERR_INTERNAL; + } + r = fido_assert_set_clientdata_hash(assert, d, sizeof(d)); + explicit_bzero(d, sizeof(d)); + if (r != FIDO_OK) { + skdebug(__func__, "fido_assert_set_clientdata_hash failed: %s", + fido_strerr(r)); + } + return r; +} +#endif /* HAVE_FIDO_ASSERT_SET_CLIENTDATA */ + +/* Check if the specified key handle exists on a given sk. */ +static int +sk_try(const struct sk_usbhid *sk, const char *application, + const uint8_t *key_handle, size_t key_handle_len) +{ + fido_assert_t *assert = NULL; + int r = FIDO_ERR_INTERNAL; + uint8_t message[32]; + + memset(message, '\0', sizeof(message)); + if ((assert = fido_assert_new()) == NULL) { + skdebug(__func__, "fido_assert_new failed"); + goto out; + } + /* generate an invalid signature on FIDO2 tokens */ + if ((r = fido_assert_set_clientdata(assert, message, + sizeof(message))) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_clientdata_hash: %s", + fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_allow_cred(assert, key_handle, + key_handle_len)) != FIDO_OK) { + skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { + skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); + goto out; + } + r = fido_dev_get_assert(sk->dev, assert, NULL); + skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); + if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { + /* U2F tokens may return this */ + r = FIDO_OK; + } + out: + fido_assert_free(&assert); + + return r != FIDO_OK ? -1 : 0; +} + +static int +check_sk_options(fido_dev_t *dev, const char *opt, int *ret) +{ + fido_cbor_info_t *info; + char * const *name; + const bool *value; + size_t len, i; + int r; + + *ret = -1; + + if (!fido_dev_is_fido2(dev)) { + skdebug(__func__, "device is not fido2"); + return 0; + } + if ((info = fido_cbor_info_new()) == NULL) { + skdebug(__func__, "fido_cbor_info_new failed"); + return -1; + } + if ((r = fido_dev_get_cbor_info(dev, info)) != FIDO_OK) { + skdebug(__func__, "fido_dev_get_cbor_info: %s", fido_strerr(r)); + fido_cbor_info_free(&info); + return -1; + } + name = fido_cbor_info_options_name_ptr(info); + value = fido_cbor_info_options_value_ptr(info); + len = fido_cbor_info_options_len(info); + for (i = 0; i < len; i++) { + if (!strcmp(name[i], opt)) { + *ret = value[i]; + break; + } + } + fido_cbor_info_free(&info); + if (*ret == -1) + skdebug(__func__, "option %s is unknown", opt); + else + skdebug(__func__, "option %s is %s", opt, *ret ? "on" : "off"); + + return 0; +} + +static struct sk_usbhid * +sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs, + const char *application, const uint8_t *key_handle, size_t key_handle_len) +{ + struct sk_usbhid **skv, *sk; + size_t skvcnt, i; + int internal_uv; + + if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { + skdebug(__func__, "sk_openv failed"); + return NULL; + } + if (skvcnt == 1 && check_sk_options(skv[0]->dev, "uv", + &internal_uv) == 0 && internal_uv != -1) { + sk = skv[0]; + skv[0] = NULL; + goto out; + } + sk = NULL; + for (i = 0; i < skvcnt; i++) { + if (sk_try(skv[i], application, key_handle, + key_handle_len) == 0) { + sk = skv[i]; + skv[i] = NULL; + skdebug(__func__, "found key in %s", sk->path); + break; + } + } + out: + sk_closev(skv, skvcnt); + return sk; +} + +static struct sk_usbhid * +sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs) +{ + struct sk_usbhid **skv, *sk; + struct timeval tv_start, tv_now, tv_delta; + size_t skvcnt, idx; + int touch, ms_remain; + + if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) { + skdebug(__func__, "sk_openv failed"); + return NULL; + } + sk = NULL; + if (skvcnt < 2) { + if (skvcnt == 1) { + /* single candidate */ + sk = skv[0]; + skv[0] = NULL; + } + goto out; + } +#ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS + skdebug(__func__, "libfido2 version does not support a feature needed for multiple tokens. Please upgrade to >=1.5.0"); + goto out; +#endif + + if (sk_touch_begin(skv, skvcnt) == -1) { + skdebug(__func__, "sk_touch_begin failed"); + goto out; + } + monotime_tv(&tv_start); + do { + if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) { + skdebug(__func__, "sk_touch_poll failed"); + goto out; + } + if (touch) { + sk = skv[idx]; + skv[idx] = NULL; + goto out; + } + monotime_tv(&tv_now); + timersub(&tv_now, &tv_start, &tv_delta); + ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 - + tv_delta.tv_usec / 1000; + } while (ms_remain >= FIDO_POLL_MS); + skdebug(__func__, "timeout"); +out: + sk_closev(skv, skvcnt); + return sk; +} + +static struct sk_usbhid * +sk_probe(const char *application, const uint8_t *key_handle, + size_t key_handle_len) +{ + struct sk_usbhid *sk; + fido_dev_info_t *devlist; + size_t ndevs; + int r; + + if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { + skdebug(__func__, "fido_dev_info_new failed"); + return NULL; + } + if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, + &ndevs)) != FIDO_OK) { + skdebug(__func__, "fido_dev_info_manifest failed: %s", + fido_strerr(r)); + fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); + return NULL; + } + skdebug(__func__, "%zu device(s) detected", ndevs); + if (ndevs == 0) { + sk = NULL; + } else if (application != NULL && key_handle != NULL) { + skdebug(__func__, "selecting sk by cred"); + sk = sk_select_by_cred(devlist, ndevs, application, key_handle, + key_handle_len); + } else { + skdebug(__func__, "selecting sk by touch"); + sk = sk_select_by_touch(devlist, ndevs); + } + fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); + return sk; +} + +#ifdef WITH_OPENSSL +/* + * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, + * but the API expects a SEC1 octet string. + */ +static int +pack_public_key_ecdsa(const fido_cred_t *cred, + struct sk_enroll_response *response) +{ + const uint8_t *ptr; + BIGNUM *x = NULL, *y = NULL; + EC_POINT *q = NULL; + EC_GROUP *g = NULL; + int ret = -1; + + response->public_key = NULL; + response->public_key_len = 0; + + if ((x = BN_new()) == NULL || + (y = BN_new()) == NULL || + (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || + (q = EC_POINT_new(g)) == NULL) { + skdebug(__func__, "libcrypto setup failed"); + goto out; + } + if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { + skdebug(__func__, "fido_cred_pubkey_ptr failed"); + goto out; + } + if (fido_cred_pubkey_len(cred) != 64) { + skdebug(__func__, "bad fido_cred_pubkey_len %zu", + fido_cred_pubkey_len(cred)); + goto out; + } + + if (BN_bin2bn(ptr, 32, x) == NULL || + BN_bin2bn(ptr + 32, 32, y) == NULL) { + skdebug(__func__, "BN_bin2bn failed"); + goto out; + } + if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { + skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); + goto out; + } + response->public_key_len = EC_POINT_point2oct(g, q, + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + if (response->public_key_len == 0 || response->public_key_len > 2048) { + skdebug(__func__, "bad pubkey length %zu", + response->public_key_len); + goto out; + } + if ((response->public_key = malloc(response->public_key_len)) == NULL) { + skdebug(__func__, "malloc pubkey failed"); + goto out; + } + if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, + response->public_key, response->public_key_len, NULL) == 0) { + skdebug(__func__, "EC_POINT_point2oct failed"); + goto out; + } + /* success */ + ret = 0; + out: + if (ret != 0 && response->public_key != NULL) { + memset(response->public_key, 0, response->public_key_len); + free(response->public_key); + response->public_key = NULL; + } + EC_POINT_free(q); + EC_GROUP_free(g); + BN_clear_free(x); + BN_clear_free(y); + return ret; +} +#endif /* WITH_OPENSSL */ + +static int +pack_public_key_ed25519(const fido_cred_t *cred, + struct sk_enroll_response *response) +{ + const uint8_t *ptr; + size_t len; + int ret = -1; + + response->public_key = NULL; + response->public_key_len = 0; + + if ((len = fido_cred_pubkey_len(cred)) != 32) { + skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); + goto out; + } + if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { + skdebug(__func__, "fido_cred_pubkey_ptr failed"); + goto out; + } + response->public_key_len = len; + if ((response->public_key = malloc(response->public_key_len)) == NULL) { + skdebug(__func__, "malloc pubkey failed"); + goto out; + } + memcpy(response->public_key, ptr, len); + ret = 0; + out: + if (ret != 0) + free(response->public_key); + return ret; +} + +static int +pack_public_key(uint32_t alg, const fido_cred_t *cred, + struct sk_enroll_response *response) +{ + switch(alg) { +#ifdef WITH_OPENSSL + case SSH_SK_ECDSA: + return pack_public_key_ecdsa(cred, response); +#endif /* WITH_OPENSSL */ + case SSH_SK_ED25519: + return pack_public_key_ed25519(cred, response); + default: + return -1; + } +} + +static int +fidoerr_to_skerr(int fidoerr) +{ + switch (fidoerr) { + case FIDO_ERR_UNSUPPORTED_OPTION: + case FIDO_ERR_UNSUPPORTED_ALGORITHM: + return SSH_SK_ERR_UNSUPPORTED; + case FIDO_ERR_PIN_REQUIRED: + case FIDO_ERR_PIN_INVALID: + case FIDO_ERR_OPERATION_DENIED: + return SSH_SK_ERR_PIN_REQUIRED; + default: + return -1; + } +} + +static int +check_enroll_options(struct sk_option **options, char **devicep, + uint8_t *user_id, size_t user_id_len) +{ + size_t i; + + if (options == NULL) + return 0; + for (i = 0; options[i] != NULL; i++) { + if (strcmp(options[i]->name, "device") == 0) { + if ((*devicep = strdup(options[i]->value)) == NULL) { + skdebug(__func__, "strdup device failed"); + return -1; + } + skdebug(__func__, "requested device %s", *devicep); + } else if (strcmp(options[i]->name, "user") == 0) { + if (strlcpy(user_id, options[i]->value, user_id_len) >= + user_id_len) { + skdebug(__func__, "user too long"); + return -1; + } + skdebug(__func__, "requested user %s", + (char *)user_id); + } else { + skdebug(__func__, "requested unsupported option %s", + options[i]->name); + if (options[i]->required) { + skdebug(__func__, "unknown required option"); + return -1; + } + } + } + return 0; +} + +int +sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, const char *pin, + struct sk_option **options, struct sk_enroll_response **enroll_response) +{ + fido_cred_t *cred = NULL; + const uint8_t *ptr; + uint8_t user_id[32]; + struct sk_usbhid *sk = NULL; + struct sk_enroll_response *response = NULL; + size_t len; + int credprot; + int internal_uv; + int cose_alg; + int ret = SSH_SK_ERR_GENERAL; + int r; + char *device = NULL; + + fido_init(SSH_FIDO_INIT_ARG); + + if (enroll_response == NULL) { + skdebug(__func__, "enroll_response == NULL"); + goto out; + } + *enroll_response = NULL; + memset(user_id, 0, sizeof(user_id)); + if (check_enroll_options(options, &device, user_id, + sizeof(user_id)) != 0) + goto out; /* error already logged */ + + switch(alg) { +#ifdef WITH_OPENSSL + case SSH_SK_ECDSA: + cose_alg = COSE_ES256; + break; +#endif /* WITH_OPENSSL */ + case SSH_SK_ED25519: + cose_alg = COSE_EDDSA; + break; + default: + skdebug(__func__, "unsupported key type %d", alg); + goto out; + } + if (device != NULL) + sk = sk_open(device); + else + sk = sk_probe(NULL, NULL, 0); + if (sk == NULL) { + ret = SSH_SK_ERR_DEVICE_NOT_FOUND; + skdebug(__func__, "failed to find sk"); + goto out; + } + skdebug(__func__, "using device %s", sk->path); + if ((cred = fido_cred_new()) == NULL) { + skdebug(__func__, "fido_cred_new failed"); + goto out; + } + if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_clientdata(cred, + challenge, challenge_len)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_clientdata: %s", + fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ? + FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), + "openssh", "openssh", NULL)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); + goto out; + } + if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) { +#if !defined(HAVE_FIDO_DEV_SUPPORTS_CRED_PROT) || \ + !defined(HAVE_FIDO_CRED_SET_PROT) + skdebug(__func__, "libfido2 version does not support a feature required for this operation. Please upgrade to >=1.5.0"); + ret = SSH_SK_ERR_UNSUPPORTED; + goto out; + credprot = 0; (void)credprot; /* avoid warning */ +#endif + if (!fido_dev_supports_cred_prot(sk->dev)) { + skdebug(__func__, "%s does not support credprot, " + "refusing to create unprotected " + "resident/verify-required key", sk->path); + ret = SSH_SK_ERR_UNSUPPORTED; + goto out; + } + if ((flags & SSH_SK_USER_VERIFICATION_REQD)) + credprot = FIDO_CRED_PROT_UV_REQUIRED; + else + credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID; + + if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) { + skdebug(__func__, "fido_cred_set_prot: %s", + fido_strerr(r)); + ret = fidoerr_to_skerr(r); + goto out; + } + } + if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) { + skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); + ret = fidoerr_to_skerr(r); + goto out; + } + if (fido_cred_x5c_ptr(cred) != NULL) { + if ((r = fido_cred_verify(cred)) != FIDO_OK) { + skdebug(__func__, "fido_cred_verify: %s", + fido_strerr(r)); + goto out; + } + } else { + skdebug(__func__, "self-attested credential"); + if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { + skdebug(__func__, "fido_cred_verify_self: %s", + fido_strerr(r)); + goto out; + } + } + if ((response = calloc(1, sizeof(*response))) == NULL) { + skdebug(__func__, "calloc response failed"); + goto out; + } + response->flags = flags; + if ((flags & SSH_SK_USER_VERIFICATION_REQD)) { + if (check_sk_options(sk->dev, "uv", &internal_uv) == 0 && + internal_uv != -1) { + /* user verification handled by token */ + response->flags &= ~SSH_SK_USER_VERIFICATION_REQD; + } + } + if (pack_public_key(alg, cred, response) != 0) { + skdebug(__func__, "pack_public_key failed"); + goto out; + } + if ((ptr = fido_cred_id_ptr(cred)) != NULL) { + len = fido_cred_id_len(cred); + if ((response->key_handle = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc key handle failed"); + goto out; + } + memcpy(response->key_handle, ptr, len); + response->key_handle_len = len; + } + if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { + len = fido_cred_sig_len(cred); + if ((response->signature = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + memcpy(response->signature, ptr, len); + response->signature_len = len; + } + if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { + len = fido_cred_x5c_len(cred); + skdebug(__func__, "attestation cert len=%zu", len); + if ((response->attestation_cert = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc attestation cert failed"); + goto out; + } + memcpy(response->attestation_cert, ptr, len); + response->attestation_cert_len = len; + } + if ((ptr = fido_cred_authdata_ptr(cred)) != NULL) { + len = fido_cred_authdata_len(cred); + skdebug(__func__, "authdata len=%zu", len); + if ((response->authdata = calloc(1, len)) == NULL) { + skdebug(__func__, "calloc authdata failed"); + goto out; + } + memcpy(response->authdata, ptr, len); + response->authdata_len = len; + } + *enroll_response = response; + response = NULL; + ret = 0; + out: + free(device); + if (response != NULL) { + free(response->public_key); + free(response->key_handle); + free(response->signature); + free(response->attestation_cert); + free(response->authdata); + free(response); + } + sk_close(sk); + fido_cred_free(&cred); + return ret; +} + +#ifdef WITH_OPENSSL +static int +pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) +{ + ECDSA_SIG *sig = NULL; + const BIGNUM *sig_r, *sig_s; + const unsigned char *cp; + size_t sig_len; + int ret = -1; + + cp = fido_assert_sig_ptr(assert, 0); + sig_len = fido_assert_sig_len(assert, 0); + if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { + skdebug(__func__, "d2i_ECDSA_SIG failed"); + goto out; + } + ECDSA_SIG_get0(sig, &sig_r, &sig_s); + response->sig_r_len = BN_num_bytes(sig_r); + response->sig_s_len = BN_num_bytes(sig_s); + if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || + (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + BN_bn2bin(sig_r, response->sig_r); + BN_bn2bin(sig_s, response->sig_s); + ret = 0; + out: + ECDSA_SIG_free(sig); + if (ret != 0) { + free(response->sig_r); + free(response->sig_s); + response->sig_r = NULL; + response->sig_s = NULL; + } + return ret; +} +#endif /* WITH_OPENSSL */ + +static int +pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) +{ + const unsigned char *ptr; + size_t len; + int ret = -1; + + ptr = fido_assert_sig_ptr(assert, 0); + len = fido_assert_sig_len(assert, 0); + if (len != 64) { + skdebug(__func__, "bad length %zu", len); + goto out; + } + response->sig_r_len = len; + if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { + skdebug(__func__, "calloc signature failed"); + goto out; + } + memcpy(response->sig_r, ptr, len); + ret = 0; + out: + if (ret != 0) { + free(response->sig_r); + response->sig_r = NULL; + } + return ret; +} + +static int +pack_sig(uint32_t alg, fido_assert_t *assert, + struct sk_sign_response *response) +{ + switch(alg) { +#ifdef WITH_OPENSSL + case SSH_SK_ECDSA: + return pack_sig_ecdsa(assert, response); +#endif /* WITH_OPENSSL */ + case SSH_SK_ED25519: + return pack_sig_ed25519(assert, response); + default: + return -1; + } +} + +/* Checks sk_options for sk_sign() and sk_load_resident_keys() */ +static int +check_sign_load_resident_options(struct sk_option **options, char **devicep) +{ + size_t i; + + if (options == NULL) + return 0; + for (i = 0; options[i] != NULL; i++) { + if (strcmp(options[i]->name, "device") == 0) { + if ((*devicep = strdup(options[i]->value)) == NULL) { + skdebug(__func__, "strdup device failed"); + return -1; + } + skdebug(__func__, "requested device %s", *devicep); + } else { + skdebug(__func__, "requested unsupported option %s", + options[i]->name); + if (options[i]->required) { + skdebug(__func__, "unknown required option"); + return -1; + } + } + } + return 0; +} + +int +sk_sign(uint32_t alg, const uint8_t *data, size_t datalen, + const char *application, + const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, const char *pin, struct sk_option **options, + struct sk_sign_response **sign_response) +{ + fido_assert_t *assert = NULL; + char *device = NULL; + struct sk_usbhid *sk = NULL; + struct sk_sign_response *response = NULL; + int ret = SSH_SK_ERR_GENERAL, internal_uv; + int r; + + fido_init(SSH_FIDO_INIT_ARG); + + if (sign_response == NULL) { + skdebug(__func__, "sign_response == NULL"); + goto out; + } + *sign_response = NULL; + if (check_sign_load_resident_options(options, &device) != 0) + goto out; /* error already logged */ + if (device != NULL) + sk = sk_open(device); + else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD)) + sk = sk_probe(NULL, NULL, 0); + else + sk = sk_probe(application, key_handle, key_handle_len); + if (sk == NULL) { + ret = SSH_SK_ERR_DEVICE_NOT_FOUND; + skdebug(__func__, "failed to find sk"); + goto out; + } + if ((assert = fido_assert_new()) == NULL) { + skdebug(__func__, "fido_assert_new failed"); + goto out; + } + if ((r = fido_assert_set_clientdata(assert, + data, datalen)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_clientdata: %s", + fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_allow_cred(assert, key_handle, + key_handle_len)) != FIDO_OK) { + skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); + goto out; + } + if ((r = fido_assert_set_up(assert, + (flags & SSH_SK_USER_PRESENCE_REQD) ? + FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); + goto out; + } + if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD)) { + if (check_sk_options(sk->dev, "uv", &internal_uv) < 0 || + internal_uv != 1) { + skdebug(__func__, "check_sk_options uv"); + ret = SSH_SK_ERR_PIN_REQUIRED; + goto out; + } + if ((r = fido_assert_set_uv(assert, + FIDO_OPT_TRUE)) != FIDO_OK) { + skdebug(__func__, "fido_assert_set_uv: %s", + fido_strerr(r)); + ret = fidoerr_to_skerr(r); + goto out; + } + } + if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) { + skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); + ret = fidoerr_to_skerr(r); + goto out; + } + if ((response = calloc(1, sizeof(*response))) == NULL) { + skdebug(__func__, "calloc response failed"); + goto out; + } + response->flags = fido_assert_flags(assert, 0); + response->counter = fido_assert_sigcount(assert, 0); + if (pack_sig(alg, assert, response) != 0) { + skdebug(__func__, "pack_sig failed"); + goto out; + } + *sign_response = response; + response = NULL; + ret = 0; + out: + free(device); + if (response != NULL) { + free(response->sig_r); + free(response->sig_s); + free(response); + } + sk_close(sk); + fido_assert_free(&assert); + return ret; +} + +static int +read_rks(struct sk_usbhid *sk, const char *pin, + struct sk_resident_key ***rksp, size_t *nrksp) +{ + int ret = SSH_SK_ERR_GENERAL, r = -1, internal_uv; + fido_credman_metadata_t *metadata = NULL; + fido_credman_rp_t *rp = NULL; + fido_credman_rk_t *rk = NULL; + size_t i, j, nrp, nrk, user_id_len; + const fido_cred_t *cred; + const char *rp_id, *rp_name, *user_name; + struct sk_resident_key *srk = NULL, **tmp; + const u_char *user_id; + + if (pin == NULL) { + skdebug(__func__, "no PIN specified"); + ret = SSH_SK_ERR_PIN_REQUIRED; + goto out; + } + if ((metadata = fido_credman_metadata_new()) == NULL) { + skdebug(__func__, "alloc failed"); + goto out; + } + if (check_sk_options(sk->dev, "uv", &internal_uv) != 0) { + skdebug(__func__, "check_sk_options failed"); + goto out; + } + + if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) { + if (r == FIDO_ERR_INVALID_COMMAND) { + skdebug(__func__, "device %s does not support " + "resident keys", sk->path); + ret = 0; + goto out; + } + skdebug(__func__, "get metadata for %s failed: %s", + sk->path, fido_strerr(r)); + ret = fidoerr_to_skerr(r); + goto out; + } + skdebug(__func__, "existing %llu, remaining %llu", + (unsigned long long)fido_credman_rk_existing(metadata), + (unsigned long long)fido_credman_rk_remaining(metadata)); + if ((rp = fido_credman_rp_new()) == NULL) { + skdebug(__func__, "alloc rp failed"); + goto out; + } + if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) { + skdebug(__func__, "get RPs for %s failed: %s", + sk->path, fido_strerr(r)); + goto out; + } + nrp = fido_credman_rp_count(rp); + skdebug(__func__, "Device %s has resident keys for %zu RPs", + sk->path, nrp); + + /* Iterate over RP IDs that have resident keys */ + for (i = 0; i < nrp; i++) { + rp_id = fido_credman_rp_id(rp, i); + rp_name = fido_credman_rp_name(rp, i); + skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu", + i, rp_name == NULL ? "(none)" : rp_name, + rp_id == NULL ? "(none)" : rp_id, + fido_credman_rp_id_hash_len(rp, i)); + + /* Skip non-SSH RP IDs */ + if (rp_id == NULL || + strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0) + continue; + + fido_credman_rk_free(&rk); + if ((rk = fido_credman_rk_new()) == NULL) { + skdebug(__func__, "alloc rk failed"); + goto out; + } + if ((r = fido_credman_get_dev_rk(sk->dev, + fido_credman_rp_id(rp, i), rk, pin)) != 0) { + skdebug(__func__, "get RKs for %s slot %zu failed: %s", + sk->path, i, fido_strerr(r)); + goto out; + } + nrk = fido_credman_rk_count(rk); + skdebug(__func__, "RP \"%s\" has %zu resident keys", + fido_credman_rp_id(rp, i), nrk); + + /* Iterate over resident keys for this RP ID */ + for (j = 0; j < nrk; j++) { + if ((cred = fido_credman_rk(rk, j)) == NULL) { + skdebug(__func__, "no RK in slot %zu", j); + continue; + } + if ((user_name = fido_cred_user_name(cred)) == NULL) + user_name = ""; + user_id = fido_cred_user_id_ptr(cred); + user_id_len = fido_cred_user_id_len(cred); + skdebug(__func__, "Device %s RP \"%s\" user \"%s\" " + "uidlen %zu slot %zu: type %d flags 0x%02x " + "prot 0x%02x", sk->path, rp_id, user_name, + user_id_len, j, fido_cred_type(cred), + fido_cred_flags(cred), fido_cred_prot(cred)); + + /* build response entry */ + if ((srk = calloc(1, sizeof(*srk))) == NULL || + (srk->key.key_handle = calloc(1, + fido_cred_id_len(cred))) == NULL || + (srk->application = strdup(rp_id)) == NULL || + (user_id_len > 0 && + (srk->user_id = calloc(1, user_id_len)) == NULL)) { + skdebug(__func__, "alloc sk_resident_key"); + goto out; + } + + srk->key.key_handle_len = fido_cred_id_len(cred); + memcpy(srk->key.key_handle, fido_cred_id_ptr(cred), + srk->key.key_handle_len); + srk->user_id_len = user_id_len; + if (srk->user_id_len != 0) + memcpy(srk->user_id, user_id, srk->user_id_len); + + switch (fido_cred_type(cred)) { + case COSE_ES256: + srk->alg = SSH_SK_ECDSA; + break; + case COSE_EDDSA: + srk->alg = SSH_SK_ED25519; + break; + default: + skdebug(__func__, "unsupported key type %d", + fido_cred_type(cred)); + goto out; /* XXX free rk and continue */ + } + + if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED + && internal_uv == -1) + srk->flags |= SSH_SK_USER_VERIFICATION_REQD; + + if ((r = pack_public_key(srk->alg, cred, + &srk->key)) != 0) { + skdebug(__func__, "pack public key failed"); + goto out; + } + /* append */ + if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1, + sizeof(**rksp))) == NULL) { + skdebug(__func__, "alloc rksp"); + goto out; + } + *rksp = tmp; + (*rksp)[(*nrksp)++] = srk; + srk = NULL; + } + } + /* Success */ + ret = 0; + out: + if (srk != NULL) { + free(srk->application); + freezero(srk->key.public_key, srk->key.public_key_len); + freezero(srk->key.key_handle, srk->key.key_handle_len); + freezero(srk->user_id, srk->user_id_len); + freezero(srk, sizeof(*srk)); + } + fido_credman_rp_free(&rp); + fido_credman_rk_free(&rk); + fido_credman_metadata_free(&metadata); + return ret; +} + +int +sk_load_resident_keys(const char *pin, struct sk_option **options, + struct sk_resident_key ***rksp, size_t *nrksp) +{ + int ret = SSH_SK_ERR_GENERAL, r = -1; + size_t i, nrks = 0; + struct sk_resident_key **rks = NULL; + struct sk_usbhid *sk = NULL; + char *device = NULL; + + *rksp = NULL; + *nrksp = 0; + + fido_init(SSH_FIDO_INIT_ARG); + + if (check_sign_load_resident_options(options, &device) != 0) + goto out; /* error already logged */ + if (device != NULL) + sk = sk_open(device); + else + sk = sk_probe(NULL, NULL, 0); + if (sk == NULL) { + ret = SSH_SK_ERR_DEVICE_NOT_FOUND; + skdebug(__func__, "failed to find sk"); + goto out; + } + skdebug(__func__, "trying %s", sk->path); + if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) { + skdebug(__func__, "read_rks failed for %s", sk->path); + ret = r; + goto out; + } + /* success, unless we have no keys but a specific error */ + if (nrks > 0 || ret == SSH_SK_ERR_GENERAL) + ret = 0; + *rksp = rks; + *nrksp = nrks; + rks = NULL; + nrks = 0; + out: + sk_close(sk); + for (i = 0; i < nrks; i++) { + free(rks[i]->application); + freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); + freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); + freezero(rks[i]->user_id, rks[i]->user_id_len); + freezero(rks[i], sizeof(*rks[i])); + } + free(rks); + return ret; +} + +#endif /* ENABLE_SK_INTERNAL */ diff --git a/aosp/external/openssh/smult_curve25519_ref.c b/aosp/external/openssh/smult_curve25519_ref.c new file mode 100644 index 0000000000000000000000000000000000000000..2e69934d4dea9c542b6fac9c3b0c0f2bf8a1336c --- /dev/null +++ b/aosp/external/openssh/smult_curve25519_ref.c @@ -0,0 +1,265 @@ +/* $OpenBSD: smult_curve25519_ref.c,v 1.2 2013/11/02 22:02:14 markus Exp $ */ +/* +version 20081011 +Matthew Dempsky +Public domain. +Derived from public domain code by D. J. Bernstein. +*/ + +int crypto_scalarmult_curve25519(unsigned char *, const unsigned char *, const unsigned char *); + +static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; } + u += a[31] + b[31]; out[31] = u; +} + +static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 218; + for (j = 0;j < 31;++j) { + u += a[j] + 65280 - b[j]; + out[j] = u & 255; + u >>= 8; + } + u += a[31] - b[31]; + out[31] = u; +} + +static void squeeze(unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u; +} + +static const unsigned int minusp[32] = { + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 +} ; + +static void freeze(unsigned int a[32]) +{ + unsigned int aorig[32]; + unsigned int j; + unsigned int negative; + + for (j = 0;j < 32;++j) aorig[j] = a[j]; + add(a,a,minusp); + negative = -((a[31] >> 7) & 1); + for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]); +} + +static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j <= i;++j) u += a[j] * b[i - j]; + for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j]; + out[i] = u; + } + squeeze(out); +} + +static void mult121665(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + + u = 0; + for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; } + u += 121665 * a[31]; out[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; } + u += out[j]; out[j] = u; +} + +static void square(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j < i - j;++j) u += a[j] * a[i - j]; + for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j]; + u *= 2; + if ((i & 1) == 0) { + u += a[i / 2] * a[i / 2]; + u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; + } + out[i] = u; + } + squeeze(out); +} + +static void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b) +{ + unsigned int j; + unsigned int t; + unsigned int bminus1; + + bminus1 = b - 1; + for (j = 0;j < 64;++j) { + t = bminus1 & (r[j] ^ s[j]); + p[j] = s[j] ^ t; + q[j] = r[j] ^ t; + } +} + +static void mainloop(unsigned int work[64],const unsigned char e[32]) +{ + unsigned int xzm1[64]; + unsigned int xzm[64]; + unsigned int xzmb[64]; + unsigned int xzm1b[64]; + unsigned int xznb[64]; + unsigned int xzn1b[64]; + unsigned int a0[64]; + unsigned int a1[64]; + unsigned int b0[64]; + unsigned int b1[64]; + unsigned int c1[64]; + unsigned int r[32]; + unsigned int s[32]; + unsigned int t[32]; + unsigned int u[32]; + unsigned int j; + unsigned int b; + int pos; + + for (j = 0;j < 32;++j) xzm1[j] = work[j]; + xzm1[32] = 1; + for (j = 33;j < 64;++j) xzm1[j] = 0; + + xzm[0] = 1; + for (j = 1;j < 64;++j) xzm[j] = 0; + + for (pos = 254;pos >= 0;--pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + select(xzmb,xzm1b,xzm,xzm1,b); + add(a0,xzmb,xzmb + 32); + sub(a0 + 32,xzmb,xzmb + 32); + add(a1,xzm1b,xzm1b + 32); + sub(a1 + 32,xzm1b,xzm1b + 32); + square(b0,a0); + square(b0 + 32,a0 + 32); + mult(b1,a1,a0 + 32); + mult(b1 + 32,a1 + 32,a0); + add(c1,b1,b1 + 32); + sub(c1 + 32,b1,b1 + 32); + square(r,c1 + 32); + sub(s,b0,b0 + 32); + mult121665(t,s); + add(u,t,b0); + mult(xznb,b0,b0 + 32); + mult(xznb + 32,s,u); + square(xzn1b,c1); + mult(xzn1b + 32,r,work); + select(xzm,xzm1,xznb,xzn1b,b); + } + + for (j = 0;j < 64;++j) work[j] = xzm[j]; +} + +static void recip(unsigned int out[32],const unsigned int z[32]) +{ + unsigned int z2[32]; + unsigned int z9[32]; + unsigned int z11[32]; + unsigned int z2_5_0[32]; + unsigned int z2_10_0[32]; + unsigned int z2_20_0[32]; + unsigned int z2_50_0[32]; + unsigned int z2_100_0[32]; + unsigned int t0[32]; + unsigned int t1[32]; + int i; + + /* 2 */ square(z2,z); + /* 4 */ square(t1,z2); + /* 8 */ square(t0,t1); + /* 9 */ mult(z9,t0,z); + /* 11 */ mult(z11,z9,z2); + /* 22 */ square(t0,z11); + /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ square(t0,z2_5_0); + /* 2^7 - 2^2 */ square(t1,t0); + /* 2^8 - 2^3 */ square(t0,t1); + /* 2^9 - 2^4 */ square(t1,t0); + /* 2^10 - 2^5 */ square(t0,t1); + /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ square(t0,z2_10_0); + /* 2^12 - 2^2 */ square(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ square(t0,z2_20_0); + /* 2^22 - 2^2 */ square(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ square(t1,t0); + /* 2^42 - 2^2 */ square(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ square(t0,z2_50_0); + /* 2^52 - 2^2 */ square(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ square(t1,z2_100_0); + /* 2^102 - 2^2 */ square(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ square(t0,t1); + /* 2^202 - 2^2 */ square(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ square(t1,t0); + /* 2^252 - 2^2 */ square(t0,t1); + /* 2^253 - 2^3 */ square(t1,t0); + /* 2^254 - 2^4 */ square(t0,t1); + /* 2^255 - 2^5 */ square(t1,t0); + /* 2^255 - 21 */ mult(out,t1,z11); +} + +int crypto_scalarmult_curve25519(unsigned char *q, + const unsigned char *n, + const unsigned char *p) +{ + unsigned int work[96]; + unsigned char e[32]; + unsigned int i; + for (i = 0;i < 32;++i) e[i] = n[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + for (i = 0;i < 32;++i) work[i] = p[i]; + mainloop(work,e); + recip(work + 32,work + 32); + mult(work + 64,work,work + 32); + freeze(work + 64); + for (i = 0;i < 32;++i) q[i] = work[64 + i]; + return 0; +} diff --git a/aosp/external/openssh/sntrup761.c b/aosp/external/openssh/sntrup761.c new file mode 100644 index 0000000000000000000000000000000000000000..c63e600fb5b40fc7cefec8313c57af058cf85fa3 --- /dev/null +++ b/aosp/external/openssh/sntrup761.c @@ -0,0 +1,1273 @@ +/* $OpenBSD: sntrup761.c,v 1.5 2021/01/08 02:33:13 dtucker Exp $ */ + +/* + * Public Domain, Authors: + * - Daniel J. Bernstein + * - Chitchanok Chuengsatiansup + * - Tanja Lange + * - Christine van Vredendaal + */ + +#include "includes.h" + +#ifdef USE_SNTRUP761X25519 + +#include +#include "crypto_api.h" + +#define int8 crypto_int8 +#define uint8 crypto_uint8 +#define int16 crypto_int16 +#define uint16 crypto_uint16 +#define int32 crypto_int32 +#define uint32 crypto_uint32 +#define int64 crypto_int64 +#define uint64 crypto_uint64 + +/* from supercop-20201130/crypto_sort/int32/portable4/int32_minmax.inc */ +#define int32_MINMAX(a,b) \ +do { \ + int64_t ab = (int64_t)b ^ (int64_t)a; \ + int64_t c = (int64_t)b - (int64_t)a; \ + c ^= ab & (c ^ b); \ + c >>= 31; \ + c &= ab; \ + a ^= c; \ + b ^= c; \ +} while(0) + +/* from supercop-20201130/crypto_sort/int32/portable4/sort.c */ + + +static void crypto_sort_int32(void *array,long long n) +{ + long long top,p,q,r,i,j; + int32 *x = array; + + if (n < 2) return; + top = 1; + while (top < n - top) top += top; + + for (p = top;p >= 1;p >>= 1) { + i = 0; + while (i + 2 * p <= n) { + for (j = i;j < i + p;++j) + int32_MINMAX(x[j],x[j+p]); + i += 2 * p; + } + for (j = i;j < n - p;++j) + int32_MINMAX(x[j],x[j+p]); + + i = 0; + j = 0; + for (q = top;q > p;q >>= 1) { + if (j != i) for (;;) { + if (j == n - q) goto done; + int32 a = x[j + p]; + for (r = q;r > p;r >>= 1) + int32_MINMAX(a,x[j + r]); + x[j + p] = a; + ++j; + if (j == i + p) { + i += 2 * p; + break; + } + } + while (i + p <= n - q) { + for (j = i;j < i + p;++j) { + int32 a = x[j + p]; + for (r = q;r > p;r >>= 1) + int32_MINMAX(a,x[j+r]); + x[j + p] = a; + } + i += 2 * p; + } + /* now i + p > n - q */ + j = i; + while (j < n - q) { + int32 a = x[j + p]; + for (r = q;r > p;r >>= 1) + int32_MINMAX(a,x[j+r]); + x[j + p] = a; + ++j; + } + + done: ; + } + } +} + +/* from supercop-20201130/crypto_sort/uint32/useint32/sort.c */ + +/* can save time by vectorizing xor loops */ +/* can save time by integrating xor loops with int32_sort */ + +static void crypto_sort_uint32(void *array,long long n) +{ + crypto_uint32 *x = array; + long long j; + for (j = 0;j < n;++j) x[j] ^= 0x80000000; + crypto_sort_int32(array,n); + for (j = 0;j < n;++j) x[j] ^= 0x80000000; +} + +/* from supercop-20201130/crypto_kem/sntrup761/ref/uint32.c */ + +/* +CPU division instruction typically takes time depending on x. +This software is designed to take time independent of x. +Time still varies depending on m; user must ensure that m is constant. +Time also varies on CPUs where multiplication is variable-time. +There could be more CPU issues. +There could also be compiler issues. +*/ + +static void uint32_divmod_uint14(uint32 *q,uint16 *r,uint32 x,uint16 m) +{ + uint32 v = 0x80000000; + uint32 qpart; + uint32 mask; + + v /= m; + + /* caller guarantees m > 0 */ + /* caller guarantees m < 16384 */ + /* vm <= 2^31 <= vm+m-1 */ + /* xvm <= 2^31 x <= xvm+x(m-1) */ + + *q = 0; + + qpart = (x*(uint64)v)>>31; + /* 2^31 qpart <= xv <= 2^31 qpart + 2^31-1 */ + /* 2^31 qpart m <= xvm <= 2^31 qpart m + (2^31-1)m */ + /* 2^31 qpart m <= 2^31 x <= 2^31 qpart m + (2^31-1)m + x(m-1) */ + /* 0 <= 2^31 newx <= (2^31-1)m + x(m-1) */ + /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */ + /* 0 <= newx <= (1-1/2^31)(2^14-1) + (2^32-1)((2^14-1)-1)/2^31 */ + + x -= qpart*m; *q += qpart; + /* x <= 49146 */ + + qpart = (x*(uint64)v)>>31; + /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */ + /* 0 <= newx <= m + 49146(2^14-1)/2^31 */ + /* 0 <= newx <= m + 0.4 */ + /* 0 <= newx <= m */ + + x -= qpart*m; *q += qpart; + /* x <= m */ + + x -= m; *q += 1; + mask = -(x>>31); + x += mask&(uint32)m; *q += mask; + /* x < m */ + + *r = x; +} + + +static uint16 uint32_mod_uint14(uint32 x,uint16 m) +{ + uint32 q; + uint16 r; + uint32_divmod_uint14(&q,&r,x,m); + return r; +} + +/* from supercop-20201130/crypto_kem/sntrup761/ref/int32.c */ + +static void int32_divmod_uint14(int32 *q,uint16 *r,int32 x,uint16 m) +{ + uint32 uq,uq2; + uint16 ur,ur2; + uint32 mask; + + uint32_divmod_uint14(&uq,&ur,0x80000000+(uint32)x,m); + uint32_divmod_uint14(&uq2,&ur2,0x80000000,m); + ur -= ur2; uq -= uq2; + mask = -(uint32)(ur>>15); + ur += mask&m; uq += mask; + *r = ur; *q = uq; +} + + +static uint16 int32_mod_uint14(int32 x,uint16 m) +{ + int32 q; + uint16 r; + int32_divmod_uint14(&q,&r,x,m); + return r; +} + +/* from supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h */ +/* pick one of these three: */ +#define SIZE761 +#undef SIZE653 +#undef SIZE857 + +/* pick one of these two: */ +#define SNTRUP /* Streamlined NTRU Prime */ +#undef LPR /* NTRU LPRime */ + +/* from supercop-20201130/crypto_kem/sntrup761/ref/params.h */ +#ifndef params_H +#define params_H + +/* menu of parameter choices: */ + + +/* what the menu means: */ + +#if defined(SIZE761) +#define p 761 +#define q 4591 +#define Rounded_bytes 1007 +#ifndef LPR +#define Rq_bytes 1158 +#define w 286 +#else +#define w 250 +#define tau0 2156 +#define tau1 114 +#define tau2 2007 +#define tau3 287 +#endif + +#elif defined(SIZE653) +#define p 653 +#define q 4621 +#define Rounded_bytes 865 +#ifndef LPR +#define Rq_bytes 994 +#define w 288 +#else +#define w 252 +#define tau0 2175 +#define tau1 113 +#define tau2 2031 +#define tau3 290 +#endif + +#elif defined(SIZE857) +#define p 857 +#define q 5167 +#define Rounded_bytes 1152 +#ifndef LPR +#define Rq_bytes 1322 +#define w 322 +#else +#define w 281 +#define tau0 2433 +#define tau1 101 +#define tau2 2265 +#define tau3 324 +#endif + +#else +#error "no parameter set defined" +#endif + +#ifdef LPR +#define I 256 +#endif + +#endif + +/* from supercop-20201130/crypto_kem/sntrup761/ref/Decode.h */ +#ifndef Decode_H +#define Decode_H + + +/* Decode(R,s,M,len) */ +/* assumes 0 < M[i] < 16384 */ +/* produces 0 <= R[i] < M[i] */ + +#endif + +/* from supercop-20201130/crypto_kem/sntrup761/ref/Decode.c */ + +static void Decode(uint16 *out,const unsigned char *S,const uint16 *M,long long len) +{ + if (len == 1) { + if (M[0] == 1) + *out = 0; + else if (M[0] <= 256) + *out = uint32_mod_uint14(S[0],M[0]); + else + *out = uint32_mod_uint14(S[0]+(((uint16)S[1])<<8),M[0]); + } + if (len > 1) { + uint16 R2[(len+1)/2]; + uint16 M2[(len+1)/2]; + uint16 bottomr[len/2]; + uint32 bottomt[len/2]; + long long i; + for (i = 0;i < len-1;i += 2) { + uint32 m = M[i]*(uint32) M[i+1]; + if (m > 256*16383) { + bottomt[i/2] = 256*256; + bottomr[i/2] = S[0]+256*S[1]; + S += 2; + M2[i/2] = (((m+255)>>8)+255)>>8; + } else if (m >= 16384) { + bottomt[i/2] = 256; + bottomr[i/2] = S[0]; + S += 1; + M2[i/2] = (m+255)>>8; + } else { + bottomt[i/2] = 1; + bottomr[i/2] = 0; + M2[i/2] = m; + } + } + if (i < len) + M2[i/2] = M[i]; + Decode(R2,S,M2,(len+1)/2); + for (i = 0;i < len-1;i += 2) { + uint32 r = bottomr[i/2]; + uint32 r1; + uint16 r0; + r += bottomt[i/2]*R2[i/2]; + uint32_divmod_uint14(&r1,&r0,r,M[i]); + r1 = uint32_mod_uint14(r1,M[i+1]); /* only needed for invalid inputs */ + *out++ = r0; + *out++ = r1; + } + if (i < len) + *out++ = R2[i/2]; + } +} + +/* from supercop-20201130/crypto_kem/sntrup761/ref/Encode.h */ +#ifndef Encode_H +#define Encode_H + + +/* Encode(s,R,M,len) */ +/* assumes 0 <= R[i] < M[i] < 16384 */ + +#endif + +/* from supercop-20201130/crypto_kem/sntrup761/ref/Encode.c */ + +/* 0 <= R[i] < M[i] < 16384 */ +static void Encode(unsigned char *out,const uint16 *R,const uint16 *M,long long len) +{ + if (len == 1) { + uint16 r = R[0]; + uint16 m = M[0]; + while (m > 1) { + *out++ = r; + r >>= 8; + m = (m+255)>>8; + } + } + if (len > 1) { + uint16 R2[(len+1)/2]; + uint16 M2[(len+1)/2]; + long long i; + for (i = 0;i < len-1;i += 2) { + uint32 m0 = M[i]; + uint32 r = R[i]+R[i+1]*m0; + uint32 m = M[i+1]*m0; + while (m >= 16384) { + *out++ = r; + r >>= 8; + m = (m+255)>>8; + } + R2[i/2] = r; + M2[i/2] = m; + } + if (i < len) { + R2[i/2] = R[i]; + M2[i/2] = M[i]; + } + Encode(out,R2,M2,(len+1)/2); + } +} + +/* from supercop-20201130/crypto_kem/sntrup761/ref/kem.c */ + +#ifdef LPR +#endif + + +/* ----- masks */ + +#ifndef LPR + +/* return -1 if x!=0; else return 0 */ +static int int16_nonzero_mask(int16 x) +{ + uint16 u = x; /* 0, else 1...65535 */ + uint32 v = u; /* 0, else 1...65535 */ + v = -v; /* 0, else 2^32-65535...2^32-1 */ + v >>= 31; /* 0, else 1 */ + return -v; /* 0, else -1 */ +} + +#endif + +/* return -1 if x<0; otherwise return 0 */ +static int int16_negative_mask(int16 x) +{ + uint16 u = x; + u >>= 15; + return -(int) u; + /* alternative with gcc -fwrapv: */ + /* x>>15 compiles to CPU's arithmetic right shift */ +} + +/* ----- arithmetic mod 3 */ + +typedef int8 small; + +/* F3 is always represented as -1,0,1 */ +/* so ZZ_fromF3 is a no-op */ + +/* x must not be close to top int16 */ +static small F3_freeze(int16 x) +{ + return int32_mod_uint14(x+1,3)-1; +} + +/* ----- arithmetic mod q */ + +#define q12 ((q-1)/2) +typedef int16 Fq; +/* always represented as -q12...q12 */ +/* so ZZ_fromFq is a no-op */ + +/* x must not be close to top int32 */ +static Fq Fq_freeze(int32 x) +{ + return int32_mod_uint14(x+q12,q)-q12; +} + +#ifndef LPR + +static Fq Fq_recip(Fq a1) +{ + int i = 1; + Fq ai = a1; + + while (i < q-2) { + ai = Fq_freeze(a1*(int32)ai); + i += 1; + } + return ai; +} + +#endif + +/* ----- Top and Right */ + +#ifdef LPR +#define tau 16 + +static int8 Top(Fq C) +{ + return (tau1*(int32)(C+tau0)+16384)>>15; +} + +static Fq Right(int8 T) +{ + return Fq_freeze(tau3*(int32)T-tau2); +} +#endif + +/* ----- small polynomials */ + +#ifndef LPR + +/* 0 if Weightw_is(r), else -1 */ +static int Weightw_mask(small *r) +{ + int weight = 0; + int i; + + for (i = 0;i < p;++i) weight += r[i]&1; + return int16_nonzero_mask(weight-w); +} + +/* R3_fromR(R_fromRq(r)) */ +static void R3_fromRq(small *out,const Fq *r) +{ + int i; + for (i = 0;i < p;++i) out[i] = F3_freeze(r[i]); +} + +/* h = f*g in the ring R3 */ +static void R3_mult(small *h,const small *f,const small *g) +{ + small fg[p+p-1]; + small result; + int i,j; + + for (i = 0;i < p;++i) { + result = 0; + for (j = 0;j <= i;++j) result = F3_freeze(result+f[j]*g[i-j]); + fg[i] = result; + } + for (i = p;i < p+p-1;++i) { + result = 0; + for (j = i-p+1;j < p;++j) result = F3_freeze(result+f[j]*g[i-j]); + fg[i] = result; + } + + for (i = p+p-2;i >= p;--i) { + fg[i-p] = F3_freeze(fg[i-p]+fg[i]); + fg[i-p+1] = F3_freeze(fg[i-p+1]+fg[i]); + } + + for (i = 0;i < p;++i) h[i] = fg[i]; +} + +/* returns 0 if recip succeeded; else -1 */ +static int R3_recip(small *out,const small *in) +{ + small f[p+1],g[p+1],v[p+1],r[p+1]; + int i,loop,delta; + int sign,swap,t; + + for (i = 0;i < p+1;++i) v[i] = 0; + for (i = 0;i < p+1;++i) r[i] = 0; + r[0] = 1; + for (i = 0;i < p;++i) f[i] = 0; + f[0] = 1; f[p-1] = f[p] = -1; + for (i = 0;i < p;++i) g[p-1-i] = in[i]; + g[p] = 0; + + delta = 1; + + for (loop = 0;loop < 2*p-1;++loop) { + for (i = p;i > 0;--i) v[i] = v[i-1]; + v[0] = 0; + + sign = -g[0]*f[0]; + swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]); + delta ^= swap&(delta^-delta); + delta += 1; + + for (i = 0;i < p+1;++i) { + t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t; + t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t; + } + + for (i = 0;i < p+1;++i) g[i] = F3_freeze(g[i]+sign*f[i]); + for (i = 0;i < p+1;++i) r[i] = F3_freeze(r[i]+sign*v[i]); + + for (i = 0;i < p;++i) g[i] = g[i+1]; + g[p] = 0; + } + + sign = f[0]; + for (i = 0;i < p;++i) out[i] = sign*v[p-1-i]; + + return int16_nonzero_mask(delta); +} + +#endif + +/* ----- polynomials mod q */ + +/* h = f*g in the ring Rq */ +static void Rq_mult_small(Fq *h,const Fq *f,const small *g) +{ + Fq fg[p+p-1]; + Fq result; + int i,j; + + for (i = 0;i < p;++i) { + result = 0; + for (j = 0;j <= i;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]); + fg[i] = result; + } + for (i = p;i < p+p-1;++i) { + result = 0; + for (j = i-p+1;j < p;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]); + fg[i] = result; + } + + for (i = p+p-2;i >= p;--i) { + fg[i-p] = Fq_freeze(fg[i-p]+fg[i]); + fg[i-p+1] = Fq_freeze(fg[i-p+1]+fg[i]); + } + + for (i = 0;i < p;++i) h[i] = fg[i]; +} + +#ifndef LPR + +/* h = 3f in Rq */ +static void Rq_mult3(Fq *h,const Fq *f) +{ + int i; + + for (i = 0;i < p;++i) h[i] = Fq_freeze(3*f[i]); +} + +/* out = 1/(3*in) in Rq */ +/* returns 0 if recip succeeded; else -1 */ +static int Rq_recip3(Fq *out,const small *in) +{ + Fq f[p+1],g[p+1],v[p+1],r[p+1]; + int i,loop,delta; + int swap,t; + int32 f0,g0; + Fq scale; + + for (i = 0;i < p+1;++i) v[i] = 0; + for (i = 0;i < p+1;++i) r[i] = 0; + r[0] = Fq_recip(3); + for (i = 0;i < p;++i) f[i] = 0; + f[0] = 1; f[p-1] = f[p] = -1; + for (i = 0;i < p;++i) g[p-1-i] = in[i]; + g[p] = 0; + + delta = 1; + + for (loop = 0;loop < 2*p-1;++loop) { + for (i = p;i > 0;--i) v[i] = v[i-1]; + v[0] = 0; + + swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]); + delta ^= swap&(delta^-delta); + delta += 1; + + for (i = 0;i < p+1;++i) { + t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t; + t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t; + } + + f0 = f[0]; + g0 = g[0]; + for (i = 0;i < p+1;++i) g[i] = Fq_freeze(f0*g[i]-g0*f[i]); + for (i = 0;i < p+1;++i) r[i] = Fq_freeze(f0*r[i]-g0*v[i]); + + for (i = 0;i < p;++i) g[i] = g[i+1]; + g[p] = 0; + } + + scale = Fq_recip(f[0]); + for (i = 0;i < p;++i) out[i] = Fq_freeze(scale*(int32)v[p-1-i]); + + return int16_nonzero_mask(delta); +} + +#endif + +/* ----- rounded polynomials mod q */ + +static void Round(Fq *out,const Fq *a) +{ + int i; + for (i = 0;i < p;++i) out[i] = a[i]-F3_freeze(a[i]); +} + +/* ----- sorting to generate short polynomial */ + +static void Short_fromlist(small *out,const uint32 *in) +{ + uint32 L[p]; + int i; + + for (i = 0;i < w;++i) L[i] = in[i]&(uint32)-2; + for (i = w;i < p;++i) L[i] = (in[i]&(uint32)-3)|1; + crypto_sort_uint32(L,p); + for (i = 0;i < p;++i) out[i] = (L[i]&3)-1; +} + +/* ----- underlying hash function */ + +#define Hash_bytes 32 + +/* e.g., b = 0 means out = Hash0(in) */ +static void Hash_prefix(unsigned char *out,int b,const unsigned char *in,int inlen) +{ + unsigned char x[inlen+1]; + unsigned char h[64]; + int i; + + x[0] = b; + for (i = 0;i < inlen;++i) x[i+1] = in[i]; + crypto_hash_sha512(h,x,inlen+1); + for (i = 0;i < 32;++i) out[i] = h[i]; +} + +/* ----- higher-level randomness */ + +static uint32 urandom32(void) +{ + unsigned char c[4]; + uint32 out[4]; + + randombytes(c,4); + out[0] = (uint32)c[0]; + out[1] = ((uint32)c[1])<<8; + out[2] = ((uint32)c[2])<<16; + out[3] = ((uint32)c[3])<<24; + return out[0]+out[1]+out[2]+out[3]; +} + +static void Short_random(small *out) +{ + uint32 L[p]; + int i; + + for (i = 0;i < p;++i) L[i] = urandom32(); + Short_fromlist(out,L); +} + +#ifndef LPR + +static void Small_random(small *out) +{ + int i; + + for (i = 0;i < p;++i) out[i] = (((urandom32()&0x3fffffff)*3)>>30)-1; +} + +#endif + +/* ----- Streamlined NTRU Prime Core */ + +#ifndef LPR + +/* h,(f,ginv) = KeyGen() */ +static void KeyGen(Fq *h,small *f,small *ginv) +{ + small g[p]; + Fq finv[p]; + + for (;;) { + Small_random(g); + if (R3_recip(ginv,g) == 0) break; + } + Short_random(f); + Rq_recip3(finv,f); /* always works */ + Rq_mult_small(h,finv,g); +} + +/* c = Encrypt(r,h) */ +static void Encrypt(Fq *c,const small *r,const Fq *h) +{ + Fq hr[p]; + + Rq_mult_small(hr,h,r); + Round(c,hr); +} + +/* r = Decrypt(c,(f,ginv)) */ +static void Decrypt(small *r,const Fq *c,const small *f,const small *ginv) +{ + Fq cf[p]; + Fq cf3[p]; + small e[p]; + small ev[p]; + int mask; + int i; + + Rq_mult_small(cf,c,f); + Rq_mult3(cf3,cf); + R3_fromRq(e,cf3); + R3_mult(ev,e,ginv); + + mask = Weightw_mask(ev); /* 0 if weight w, else -1 */ + for (i = 0;i < w;++i) r[i] = ((ev[i]^1)&~mask)^1; + for (i = w;i < p;++i) r[i] = ev[i]&~mask; +} + +#endif + +/* ----- NTRU LPRime Core */ + +#ifdef LPR + +/* (G,A),a = KeyGen(G); leaves G unchanged */ +static void KeyGen(Fq *A,small *a,const Fq *G) +{ + Fq aG[p]; + + Short_random(a); + Rq_mult_small(aG,G,a); + Round(A,aG); +} + +/* B,T = Encrypt(r,(G,A),b) */ +static void Encrypt(Fq *B,int8 *T,const int8 *r,const Fq *G,const Fq *A,const small *b) +{ + Fq bG[p]; + Fq bA[p]; + int i; + + Rq_mult_small(bG,G,b); + Round(B,bG); + Rq_mult_small(bA,A,b); + for (i = 0;i < I;++i) T[i] = Top(Fq_freeze(bA[i]+r[i]*q12)); +} + +/* r = Decrypt((B,T),a) */ +static void Decrypt(int8 *r,const Fq *B,const int8 *T,const small *a) +{ + Fq aB[p]; + int i; + + Rq_mult_small(aB,B,a); + for (i = 0;i < I;++i) + r[i] = -int16_negative_mask(Fq_freeze(Right(T[i])-aB[i]+4*w+1)); +} + +#endif + +/* ----- encoding I-bit inputs */ + +#ifdef LPR + +#define Inputs_bytes (I/8) +typedef int8 Inputs[I]; /* passed by reference */ + +static void Inputs_encode(unsigned char *s,const Inputs r) +{ + int i; + for (i = 0;i < Inputs_bytes;++i) s[i] = 0; + for (i = 0;i < I;++i) s[i>>3] |= r[i]<<(i&7); +} + +#endif + +/* ----- Expand */ + +#ifdef LPR + +static const unsigned char aes_nonce[16] = {0}; + +static void Expand(uint32 *L,const unsigned char *k) +{ + int i; + crypto_stream_aes256ctr((unsigned char *) L,4*p,aes_nonce,k); + for (i = 0;i < p;++i) { + uint32 L0 = ((unsigned char *) L)[4*i]; + uint32 L1 = ((unsigned char *) L)[4*i+1]; + uint32 L2 = ((unsigned char *) L)[4*i+2]; + uint32 L3 = ((unsigned char *) L)[4*i+3]; + L[i] = L0+(L1<<8)+(L2<<16)+(L3<<24); + } +} + +#endif + +/* ----- Seeds */ + +#ifdef LPR + +#define Seeds_bytes 32 + +static void Seeds_random(unsigned char *s) +{ + randombytes(s,Seeds_bytes); +} + +#endif + +/* ----- Generator, HashShort */ + +#ifdef LPR + +/* G = Generator(k) */ +static void Generator(Fq *G,const unsigned char *k) +{ + uint32 L[p]; + int i; + + Expand(L,k); + for (i = 0;i < p;++i) G[i] = uint32_mod_uint14(L[i],q)-q12; +} + +/* out = HashShort(r) */ +static void HashShort(small *out,const Inputs r) +{ + unsigned char s[Inputs_bytes]; + unsigned char h[Hash_bytes]; + uint32 L[p]; + + Inputs_encode(s,r); + Hash_prefix(h,5,s,sizeof s); + Expand(L,h); + Short_fromlist(out,L); +} + +#endif + +/* ----- NTRU LPRime Expand */ + +#ifdef LPR + +/* (S,A),a = XKeyGen() */ +static void XKeyGen(unsigned char *S,Fq *A,small *a) +{ + Fq G[p]; + + Seeds_random(S); + Generator(G,S); + KeyGen(A,a,G); +} + +/* B,T = XEncrypt(r,(S,A)) */ +static void XEncrypt(Fq *B,int8 *T,const int8 *r,const unsigned char *S,const Fq *A) +{ + Fq G[p]; + small b[p]; + + Generator(G,S); + HashShort(b,r); + Encrypt(B,T,r,G,A,b); +} + +#define XDecrypt Decrypt + +#endif + +/* ----- encoding small polynomials (including short polynomials) */ + +#define Small_bytes ((p+3)/4) + +/* these are the only functions that rely on p mod 4 = 1 */ + +static void Small_encode(unsigned char *s,const small *f) +{ + small x; + int i; + + for (i = 0;i < p/4;++i) { + x = *f++ + 1; + x += (*f++ + 1)<<2; + x += (*f++ + 1)<<4; + x += (*f++ + 1)<<6; + *s++ = x; + } + x = *f++ + 1; + *s++ = x; +} + +static void Small_decode(small *f,const unsigned char *s) +{ + unsigned char x; + int i; + + for (i = 0;i < p/4;++i) { + x = *s++; + *f++ = ((small)(x&3))-1; x >>= 2; + *f++ = ((small)(x&3))-1; x >>= 2; + *f++ = ((small)(x&3))-1; x >>= 2; + *f++ = ((small)(x&3))-1; + } + x = *s++; + *f++ = ((small)(x&3))-1; +} + +/* ----- encoding general polynomials */ + +#ifndef LPR + +static void Rq_encode(unsigned char *s,const Fq *r) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) R[i] = r[i]+q12; + for (i = 0;i < p;++i) M[i] = q; + Encode(s,R,M,p); +} + +static void Rq_decode(Fq *r,const unsigned char *s) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) M[i] = q; + Decode(R,s,M,p); + for (i = 0;i < p;++i) r[i] = ((Fq)R[i])-q12; +} + +#endif + +/* ----- encoding rounded polynomials */ + +static void Rounded_encode(unsigned char *s,const Fq *r) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) R[i] = ((r[i]+q12)*10923)>>15; + for (i = 0;i < p;++i) M[i] = (q+2)/3; + Encode(s,R,M,p); +} + +static void Rounded_decode(Fq *r,const unsigned char *s) +{ + uint16 R[p],M[p]; + int i; + + for (i = 0;i < p;++i) M[i] = (q+2)/3; + Decode(R,s,M,p); + for (i = 0;i < p;++i) r[i] = R[i]*3-q12; +} + +/* ----- encoding top polynomials */ + +#ifdef LPR + +#define Top_bytes (I/2) + +static void Top_encode(unsigned char *s,const int8 *T) +{ + int i; + for (i = 0;i < Top_bytes;++i) + s[i] = T[2*i]+(T[2*i+1]<<4); +} + +static void Top_decode(int8 *T,const unsigned char *s) +{ + int i; + for (i = 0;i < Top_bytes;++i) { + T[2*i] = s[i]&15; + T[2*i+1] = s[i]>>4; + } +} + +#endif + +/* ----- Streamlined NTRU Prime Core plus encoding */ + +#ifndef LPR + +typedef small Inputs[p]; /* passed by reference */ +#define Inputs_random Short_random +#define Inputs_encode Small_encode +#define Inputs_bytes Small_bytes + +#define Ciphertexts_bytes Rounded_bytes +#define SecretKeys_bytes (2*Small_bytes) +#define PublicKeys_bytes Rq_bytes + +/* pk,sk = ZKeyGen() */ +static void ZKeyGen(unsigned char *pk,unsigned char *sk) +{ + Fq h[p]; + small f[p],v[p]; + + KeyGen(h,f,v); + Rq_encode(pk,h); + Small_encode(sk,f); sk += Small_bytes; + Small_encode(sk,v); +} + +/* C = ZEncrypt(r,pk) */ +static void ZEncrypt(unsigned char *C,const Inputs r,const unsigned char *pk) +{ + Fq h[p]; + Fq c[p]; + Rq_decode(h,pk); + Encrypt(c,r,h); + Rounded_encode(C,c); +} + +/* r = ZDecrypt(C,sk) */ +static void ZDecrypt(Inputs r,const unsigned char *C,const unsigned char *sk) +{ + small f[p],v[p]; + Fq c[p]; + + Small_decode(f,sk); sk += Small_bytes; + Small_decode(v,sk); + Rounded_decode(c,C); + Decrypt(r,c,f,v); +} + +#endif + +/* ----- NTRU LPRime Expand plus encoding */ + +#ifdef LPR + +#define Ciphertexts_bytes (Rounded_bytes+Top_bytes) +#define SecretKeys_bytes Small_bytes +#define PublicKeys_bytes (Seeds_bytes+Rounded_bytes) + +static void Inputs_random(Inputs r) +{ + unsigned char s[Inputs_bytes]; + int i; + + randombytes(s,sizeof s); + for (i = 0;i < I;++i) r[i] = 1&(s[i>>3]>>(i&7)); +} + +/* pk,sk = ZKeyGen() */ +static void ZKeyGen(unsigned char *pk,unsigned char *sk) +{ + Fq A[p]; + small a[p]; + + XKeyGen(pk,A,a); pk += Seeds_bytes; + Rounded_encode(pk,A); + Small_encode(sk,a); +} + +/* c = ZEncrypt(r,pk) */ +static void ZEncrypt(unsigned char *c,const Inputs r,const unsigned char *pk) +{ + Fq A[p]; + Fq B[p]; + int8 T[I]; + + Rounded_decode(A,pk+Seeds_bytes); + XEncrypt(B,T,r,pk,A); + Rounded_encode(c,B); c += Rounded_bytes; + Top_encode(c,T); +} + +/* r = ZDecrypt(C,sk) */ +static void ZDecrypt(Inputs r,const unsigned char *c,const unsigned char *sk) +{ + small a[p]; + Fq B[p]; + int8 T[I]; + + Small_decode(a,sk); + Rounded_decode(B,c); + Top_decode(T,c+Rounded_bytes); + XDecrypt(r,B,T,a); +} + +#endif + +/* ----- confirmation hash */ + +#define Confirm_bytes 32 + +/* h = HashConfirm(r,pk,cache); cache is Hash4(pk) */ +static void HashConfirm(unsigned char *h,const unsigned char *r,const unsigned char *pk,const unsigned char *cache) +{ +#ifndef LPR + unsigned char x[Hash_bytes*2]; + int i; + + Hash_prefix(x,3,r,Inputs_bytes); + for (i = 0;i < Hash_bytes;++i) x[Hash_bytes+i] = cache[i]; +#else + unsigned char x[Inputs_bytes+Hash_bytes]; + int i; + + for (i = 0;i < Inputs_bytes;++i) x[i] = r[i]; + for (i = 0;i < Hash_bytes;++i) x[Inputs_bytes+i] = cache[i]; +#endif + Hash_prefix(h,2,x,sizeof x); +} + +/* ----- session-key hash */ + +/* k = HashSession(b,y,z) */ +static void HashSession(unsigned char *k,int b,const unsigned char *y,const unsigned char *z) +{ +#ifndef LPR + unsigned char x[Hash_bytes+Ciphertexts_bytes+Confirm_bytes]; + int i; + + Hash_prefix(x,3,y,Inputs_bytes); + for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Hash_bytes+i] = z[i]; +#else + unsigned char x[Inputs_bytes+Ciphertexts_bytes+Confirm_bytes]; + int i; + + for (i = 0;i < Inputs_bytes;++i) x[i] = y[i]; + for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Inputs_bytes+i] = z[i]; +#endif + Hash_prefix(k,b,x,sizeof x); +} + +/* ----- Streamlined NTRU Prime and NTRU LPRime */ + +/* pk,sk = KEM_KeyGen() */ +static void KEM_KeyGen(unsigned char *pk,unsigned char *sk) +{ + int i; + + ZKeyGen(pk,sk); sk += SecretKeys_bytes; + for (i = 0;i < PublicKeys_bytes;++i) *sk++ = pk[i]; + randombytes(sk,Inputs_bytes); sk += Inputs_bytes; + Hash_prefix(sk,4,pk,PublicKeys_bytes); +} + +/* c,r_enc = Hide(r,pk,cache); cache is Hash4(pk) */ +static void Hide(unsigned char *c,unsigned char *r_enc,const Inputs r,const unsigned char *pk,const unsigned char *cache) +{ + Inputs_encode(r_enc,r); + ZEncrypt(c,r,pk); c += Ciphertexts_bytes; + HashConfirm(c,r_enc,pk,cache); +} + +/* c,k = Encap(pk) */ +static void Encap(unsigned char *c,unsigned char *k,const unsigned char *pk) +{ + Inputs r; + unsigned char r_enc[Inputs_bytes]; + unsigned char cache[Hash_bytes]; + + Hash_prefix(cache,4,pk,PublicKeys_bytes); + Inputs_random(r); + Hide(c,r_enc,r,pk,cache); + HashSession(k,1,r_enc,c); +} + +/* 0 if matching ciphertext+confirm, else -1 */ +static int Ciphertexts_diff_mask(const unsigned char *c,const unsigned char *c2) +{ + uint16 differentbits = 0; + int len = Ciphertexts_bytes+Confirm_bytes; + + while (len-- > 0) differentbits |= (*c++)^(*c2++); + return (1&((differentbits-1)>>8))-1; +} + +/* k = Decap(c,sk) */ +static void Decap(unsigned char *k,const unsigned char *c,const unsigned char *sk) +{ + const unsigned char *pk = sk + SecretKeys_bytes; + const unsigned char *rho = pk + PublicKeys_bytes; + const unsigned char *cache = rho + Inputs_bytes; + Inputs r; + unsigned char r_enc[Inputs_bytes]; + unsigned char cnew[Ciphertexts_bytes+Confirm_bytes]; + int mask; + int i; + + ZDecrypt(r,c,sk); + Hide(cnew,r_enc,r,pk,cache); + mask = Ciphertexts_diff_mask(c,cnew); + for (i = 0;i < Inputs_bytes;++i) r_enc[i] ^= mask&(r_enc[i]^rho[i]); + HashSession(k,1+mask,r_enc,c); +} + +/* ----- crypto_kem API */ + + +int crypto_kem_sntrup761_keypair(unsigned char *pk,unsigned char *sk) +{ + KEM_KeyGen(pk,sk); + return 0; +} + +int crypto_kem_sntrup761_enc(unsigned char *c,unsigned char *k,const unsigned char *pk) +{ + Encap(c,k,pk); + return 0; +} + +int crypto_kem_sntrup761_dec(unsigned char *k,const unsigned char *c,const unsigned char *sk) +{ + Decap(k,c,sk); + return 0; +} +#endif /* USE_SNTRUP761X25519 */ diff --git a/aosp/external/openssh/sntrup761.sh b/aosp/external/openssh/sntrup761.sh new file mode 100644 index 0000000000000000000000000000000000000000..5cd5f92c31d79fcc73697cb89d070a149dc0e18c --- /dev/null +++ b/aosp/external/openssh/sntrup761.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# $OpenBSD: sntrup761.sh,v 1.5 2021/01/08 02:33:13 dtucker Exp $ +# Placed in the Public Domain. +# +AUTHOR="supercop-20201130/crypto_kem/sntrup761/ref/implementors" +FILES=" + supercop-20201130/crypto_sort/int32/portable4/int32_minmax.inc + supercop-20201130/crypto_sort/int32/portable4/sort.c + supercop-20201130/crypto_sort/uint32/useint32/sort.c + supercop-20201130/crypto_kem/sntrup761/ref/uint32.c + supercop-20201130/crypto_kem/sntrup761/ref/int32.c + supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h + supercop-20201130/crypto_kem/sntrup761/ref/params.h + supercop-20201130/crypto_kem/sntrup761/ref/Decode.h + supercop-20201130/crypto_kem/sntrup761/ref/Decode.c + supercop-20201130/crypto_kem/sntrup761/ref/Encode.h + supercop-20201130/crypto_kem/sntrup761/ref/Encode.c + supercop-20201130/crypto_kem/sntrup761/ref/kem.c +" +### + +set -e +cd $1 +echo -n '/* $' +echo 'OpenBSD: $ */' +echo +echo '/*' +echo ' * Public Domain, Authors:' +sed -e '/Alphabetical order:/d' -e 's/^/ * - /' < $AUTHOR +echo ' */' +echo +echo '#include ' +echo '#include "crypto_api.h"' +echo +# Map the types used in this code to the ones in crypto_api.h. We use #define +# instead of typedef since some systems have existing intXX types and do not +# permit multiple typedefs even if they do not conflict. +for t in int8 uint8 int16 uint16 int32 uint32 int64 uint64; do + echo "#define $t crypto_${t}" +done +echo +for i in $FILES; do + echo "/* from $i */" + # Changes to all files: + # - remove all includes, we inline everything required. + # - make functions not required elsewhere static. + # - rename the functions we do use. + # - remove unneccesary defines and externs. + sed -e "/#include/d" \ + -e "s/crypto_kem_/crypto_kem_sntrup761_/g" \ + -e "s/^void /static void /g" \ + -e "s/^int16 /static int16 /g" \ + -e "s/^uint16 /static uint16 /g" \ + -e "/^extern /d" \ + -e '/CRYPTO_NAMESPACE/d' \ + -e "/^#define int32 crypto_int32/d" \ + $i | \ + case "$i" in + # Use int64_t for intermediate values in int32_MINMAX to prevent signed + # 32-bit integer overflow when called by crypto_sort_uint32. + */int32_minmax.inc) + sed -e "s/int32 ab = b ^ a/int64_t ab = (int64_t)b ^ (int64_t)a/" \ + -e "s/int32 c = b - a/int64_t c = (int64_t)b - (int64_t)a/" + ;; + */int32/portable4/sort.c) + sed -e "s/void crypto_sort/void crypto_sort_int32/g" + ;; + */uint32/useint32/sort.c) + sed -e "s/void crypto_sort/void crypto_sort_uint32/g" + ;; + # Remove unused function to prevent warning. + */crypto_kem/sntrup761/ref/int32.c) + sed -e '/ int32_div_uint14/,/^}$/d' + ;; + # Remove unused function to prevent warning. + */crypto_kem/sntrup761/ref/uint32.c) + sed -e '/ uint32_div_uint14/,/^}$/d' + ;; + # Default: pass through. + *) + cat + ;; + esac + echo +done diff --git a/aosp/external/openssh/srclimit.c b/aosp/external/openssh/srclimit.c new file mode 100644 index 0000000000000000000000000000000000000000..5014ed79f2965abc071c48e1b0f1f084e4766df7 --- /dev/null +++ b/aosp/external/openssh/srclimit.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2020 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include + +#include "addr.h" +#include "canohost.h" +#include "log.h" +#include "misc.h" +#include "srclimit.h" +#include "xmalloc.h" + +static int max_children, max_persource, ipv4_masklen, ipv6_masklen; + +/* Per connection state, used to enforce unauthenticated connection limit. */ +static struct child_info { + int id; + struct xaddr addr; +} *child; + +void +srclimit_init(int max, int persource, int ipv4len, int ipv6len) +{ + int i; + + max_children = max; + ipv4_masklen = ipv4len; + ipv6_masklen = ipv6len; + max_persource = persource; + if (max_persource == INT_MAX) /* no limit */ + return; + debug("%s: max connections %d, per source %d, masks %d,%d", __func__, + max, persource, ipv4len, ipv6len); + if (max <= 0) + fatal("%s: invalid number of sockets: %d", __func__, max); + child = xcalloc(max_children, sizeof(*child)); + for (i = 0; i < max_children; i++) + child[i].id = -1; +} + +/* returns 1 if connection allowed, 0 if not allowed. */ +int +srclimit_check_allow(int sock, int id) +{ + struct xaddr xa, xb, xmask; + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + struct sockaddr *sa = (struct sockaddr *)&addr; + int i, bits, first_unused, count = 0; + char xas[NI_MAXHOST]; + + if (max_persource == INT_MAX) /* no limit */ + return 1; + + debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource); + if (getpeername(sock, sa, &addrlen) != 0) + return 1; /* not remote socket? */ + if (addr_sa_to_xaddr(sa, addrlen, &xa) != 0) + return 1; /* unknown address family? */ + + /* Mask address off address to desired size. */ + bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen; + if (addr_netmask(xa.af, bits, &xmask) != 0 || + addr_and(&xb, &xa, &xmask) != 0) { + debug3("%s: invalid mask %d bits", __func__, bits); + return 1; + } + + first_unused = max_children; + /* Count matching entries and find first unused one. */ + for (i = 0; i < max_children; i++) { + if (child[i].id == -1) { + if (i < first_unused) + first_unused = i; + } else if (addr_cmp(&child[i].addr, &xb) == 0) { + count++; + } + } + if (addr_ntop(&xa, xas, sizeof(xas)) != 0) { + debug3("%s: addr ntop failed", __func__); + return 1; + } + debug3("%s: new unauthenticated connection from %s/%d, at %d of %d", + __func__, xas, bits, count, max_persource); + + if (first_unused == max_children) { /* no free slot found */ + debug3("%s: no free slot", __func__); + return 0; + } + if (first_unused < 0 || first_unused >= max_children) + fatal("%s: internal error: first_unused out of range", + __func__); + + if (count >= max_persource) + return 0; + + /* Connection allowed, store masked address. */ + child[first_unused].id = id; + memcpy(&child[first_unused].addr, &xb, sizeof(xb)); + return 1; +} + +void +srclimit_done(int id) +{ + int i; + + if (max_persource == INT_MAX) /* no limit */ + return; + + debug("%s: id %d", __func__, id); + /* Clear corresponding state entry. */ + for (i = 0; i < max_children; i++) { + if (child[i].id == id) { + child[i].id = -1; + return; + } + } +} diff --git a/aosp/external/openssh/srclimit.h b/aosp/external/openssh/srclimit.h new file mode 100644 index 0000000000000000000000000000000000000000..6e04f32b3ff760feee81ee90114c518d4c90ee45 --- /dev/null +++ b/aosp/external/openssh/srclimit.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020 Darren Tucker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +void srclimit_init(int, int, int, int); +int srclimit_check_allow(int, int); +void srclimit_done(int); diff --git a/aosp/external/openssh/ssh-add.1 b/aosp/external/openssh/ssh-add.1 new file mode 100644 index 0000000000000000000000000000000000000000..4601f5981cd3490b2c0b37e4d4ecfe8c91e37f07 --- /dev/null +++ b/aosp/external/openssh/ssh-add.1 @@ -0,0 +1,342 @@ +.\" $OpenBSD: ssh-add.1,v 1.84 2022/02/04 02:49:17 dtucker Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: February 4 2022 $ +.Dt SSH-ADD 1 +.Os +.Sh NAME +.Nm ssh-add +.Nd adds private key identities to the OpenSSH authentication agent +.Sh SYNOPSIS +.Nm ssh-add +.Op Fl cDdKkLlqvXx +.Op Fl E Ar fingerprint_hash +.Op Fl H Ar hostkey_file +.Op Fl h Ar destination_constraint +.Op Fl S Ar provider +.Op Fl t Ar life +.Op Ar +.Nm ssh-add +.Fl s Ar pkcs11 +.Nm ssh-add +.Fl e Ar pkcs11 +.Nm ssh-add +.Fl T +.Ar pubkey ... +.Sh DESCRIPTION +.Nm +adds private key identities to the authentication agent, +.Xr ssh-agent 1 . +When run without arguments, it adds the files +.Pa ~/.ssh/id_rsa , +.Pa ~/.ssh/id_ecdsa , +.Pa ~/.ssh/id_ecdsa_sk , +.Pa ~/.ssh/id_ed25519 , +.Pa ~/.ssh/id_ed25519_sk , +and +.Pa ~/.ssh/id_dsa . +After loading a private key, +.Nm +will try to load corresponding certificate information from the +filename obtained by appending +.Pa -cert.pub +to the name of the private key file. +Alternative file names can be given on the command line. +.Pp +If any file requires a passphrase, +.Nm +asks for the passphrase from the user. +The passphrase is read from the user's tty. +.Nm +retries the last passphrase if multiple identity files are given. +.Pp +The authentication agent must be running and the +.Ev SSH_AUTH_SOCK +environment variable must contain the name of its socket for +.Nm +to work. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c +Indicates that added identities should be subject to confirmation before +being used for authentication. +Confirmation is performed by +.Xr ssh-askpass 1 . +Successful confirmation is signaled by a zero exit status from +.Xr ssh-askpass 1 , +rather than text entered into the requester. +.It Fl D +Deletes all identities from the agent. +.It Fl d +Instead of adding identities, removes identities from the agent. +If +.Nm +has been run without arguments, the keys for the default identities and +their corresponding certificates will be removed. +Otherwise, the argument list will be interpreted as a list of paths to +public key files to specify keys and certificates to be removed from the agent. +If no public key is found at a given path, +.Nm +will append +.Pa .pub +and retry. +If the argument list consists of +.Dq - +then +.Nm +will read public keys to be removed from standard input. +.It Fl E Ar fingerprint_hash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . +.It Fl e Ar pkcs11 +Remove keys provided by the PKCS#11 shared library +.Ar pkcs11 . +.It Fl H Ar hostkey_file +Specifies a known hosts file to look up hostkeys when using +destination-constrained keys via the +.Fl h +flag. +This option may be specified multiple times to allow multiple files to be +searched. +If no files are specified, +.Nm +will use the default +.Xr ssh_config 5 +known hosts files: +.Pa ~/.ssh/known_hosts , +.Pa ~/.ssh/known_hosts2 , +.Pa /etc/ssh/ssh_known_hosts , +and +.Pa /etc/ssh/ssh_known_hosts2 . +.It Fl h Ar destination_constraint +When adding keys, constrain them to be usable only through specific hosts or to +specific destinations. +.Pp +Destination constraints of the form +.Sq [user@]dest-hostname +permit use of the key only from the origin host (the one running +.Xr ssh-agent 1 ) +to the listed destination host, with optional user name. +.Pp +Constraints of the form +.Sq src-hostname>[user@]dst-hostname +allow a key available on a forwarded +.Xr ssh-agent 1 +to be used through a particular host (as specified by +.Sq src-hostname ) +to authenticate to a further host, +specified by +.Sq dst-hostname . +.Pp +Multiple destination constraints may be added when loading keys. +When attempting authentication with a key that has destination constraints, +the whole connection path, including +.Xr ssh-agent 1 +forwarding, is tested against those constraints and each +hop must be permitted for the attempt to succeed. +For example, if key is forwarded to a remote host, +.Sq host-b , +and is attempting authentication to another host, +.Sq host-c , +then the operation will be successful only if +.Sq host-b +was permitted from the origin host and the subsequent +.Sq host-b>host-c +hop is also permitted by destination constraints. +.Pp +Hosts are identified by their host keys, and are looked up from known hosts +files by +.Nm . +Wildcards patterns may be used for hostnames and certificate host +keys are supported. +By default, keys added by +.Nm +are not destination constrained. +.Pp +Destination constraints were added in OpenSSH release 8.9. +Support in both the remote SSH client and server is required when using +destination-constrained keys over a forwarded +.Xr ssh-agent 1 +channel. +.Pp +It is also important to note that destination constraints can only be +enforced by +.Xr ssh-agent 1 +when a key is used, or when it is forwarded by a +.Sy cooperating +.Xr ssh 1 . +Specifically, it does not prevent an attacker with access to a remote +.Ev SSH_AUTH_SOCK +from forwarding it again and using it on a different host (but only to +a permitted destination). +.It Fl K +Load resident keys from a FIDO authenticator. +.It Fl k +When loading keys into or deleting keys from the agent, process plain private +keys only and skip certificates. +.It Fl L +Lists public key parameters of all identities currently represented +by the agent. +.It Fl l +Lists fingerprints of all identities currently represented by the agent. +.It Fl q +Be quiet after a successful operation. +.It Fl S Ar provider +Specifies a path to a library that will be used when adding +FIDO authenticator-hosted keys, overriding the default of using the +internal USB HID support. +.It Fl s Ar pkcs11 +Add keys provided by the PKCS#11 shared library +.Ar pkcs11 . +.It Fl T Ar pubkey ... +Tests whether the private keys that correspond to the specified +.Ar pubkey +files are usable by performing sign and verify operations on each. +.It Fl t Ar life +Set a maximum lifetime when adding identities to an agent. +The lifetime may be specified in seconds or in a time format +specified in +.Xr sshd_config 5 . +.It Fl v +Verbose mode. +Causes +.Nm +to print debugging messages about its progress. +This is helpful in debugging problems. +Multiple +.Fl v +options increase the verbosity. +The maximum is 3. +.It Fl X +Unlock the agent. +.It Fl x +Lock the agent with a password. +.El +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev "DISPLAY", "SSH_ASKPASS" and "SSH_ASKPASS_REQUIRE" +If +.Nm +needs a passphrase, it will read the passphrase from the current +terminal if it was run from a terminal. +If +.Nm +does not have a terminal associated with it but +.Ev DISPLAY +and +.Ev SSH_ASKPASS +are set, it will execute the program specified by +.Ev SSH_ASKPASS +(by default +.Dq ssh-askpass ) +and open an X11 window to read the passphrase. +This is particularly useful when calling +.Nm +from a +.Pa .xsession +or related script. +.Pp +.Ev SSH_ASKPASS_REQUIRE +allows further control over the use of an askpass program. +If this variable is set to +.Dq never +then +.Nm +will never attempt to use one. +If it is set to +.Dq prefer , +then +.Nm +will prefer to use the askpass program instead of the TTY when requesting +passwords. +Finally, if the variable is set to +.Dq force , +then the askpass program will be used for all passphrase input regardless +of whether +.Ev DISPLAY +is set. +.It Ev SSH_AUTH_SOCK +Identifies the path of a +.Ux Ns -domain +socket used to communicate with the agent. +.It Ev SSH_SK_PROVIDER +Specifies a path to a library that will be used when loading any +FIDO authenticator-hosted keys, overriding the default of using +the built-in USB HID support. +.El +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa ~/.ssh/id_dsa +.It Pa ~/.ssh/id_ecdsa +.It Pa ~/.ssh/id_ecdsa_sk +.It Pa ~/.ssh/id_ed25519 +.It Pa ~/.ssh/id_ed25519_sk +.It Pa ~/.ssh/id_rsa +Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, +authenticator-hosted Ed25519 or RSA authentication identity of the user. +.El +.Pp +Identity files should not be readable by anyone but the user. +Note that +.Nm +ignores identity files if they are accessible by others. +.Sh EXIT STATUS +Exit status is 0 on success, 1 if the specified command fails, +and 2 if +.Nm +is unable to contact the authentication agent. +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-agent 1 , +.Xr ssh-askpass 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. diff --git a/aosp/external/openssh/ssh-add.c b/aosp/external/openssh/ssh-add.c new file mode 100644 index 0000000000000000000000000000000000000000..7555477482766e4464ab92bcbf19a879fe28d4f1 --- /dev/null +++ b/aosp/external/openssh/ssh-add.c @@ -0,0 +1,1014 @@ +/* $OpenBSD: ssh-add.c,v 1.165 2022/02/04 02:49:17 dtucker Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Adds an identity to the authentication server, or removes an identity. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation, + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#ifdef WITH_OPENSSL +# include +# include "openbsd-compat/openssl-compat.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh.h" +#include "log.h" +#include "sshkey.h" +#include "sshbuf.h" +#include "authfd.h" +#include "authfile.h" +#include "pathnames.h" +#include "misc.h" +#include "ssherr.h" +#include "digest.h" +#include "ssh-sk.h" +#include "sk-api.h" +#include "hostfile.h" + +/* argv0 */ +extern char *__progname; + +/* Default files to add */ +static char *default_files[] = { +#ifdef WITH_OPENSSL + _PATH_SSH_CLIENT_ID_RSA, +#ifdef OPENSSL_HAS_ECC + _PATH_SSH_CLIENT_ID_ECDSA, + _PATH_SSH_CLIENT_ID_ECDSA_SK, +#endif +#endif /* WITH_OPENSSL */ + _PATH_SSH_CLIENT_ID_ED25519, + _PATH_SSH_CLIENT_ID_ED25519_SK, + _PATH_SSH_CLIENT_ID_XMSS, + _PATH_SSH_CLIENT_ID_DSA, + NULL +}; + +static int fingerprint_hash = SSH_FP_HASH_DEFAULT; + +/* Default lifetime (0 == forever) */ +static int lifetime = 0; + +/* User has to confirm key use */ +static int confirm = 0; + +/* Maximum number of signatures (XMSS) */ +static u_int maxsign = 0; +static u_int minleft = 0; + +/* we keep a cache of one passphrase */ +static char *pass = NULL; +static void +clear_pass(void) +{ + if (pass) { + freezero(pass, strlen(pass)); + pass = NULL; + } +} + +static int +delete_one(int agent_fd, const struct sshkey *key, const char *comment, + const char *path, int qflag) +{ + int r; + + if ((r = ssh_remove_identity(agent_fd, key)) != 0) { + fprintf(stderr, "Could not remove identity \"%s\": %s\n", + path, ssh_err(r)); + return r; + } + if (!qflag) { + fprintf(stderr, "Identity removed: %s %s (%s)\n", path, + sshkey_type(key), comment); + } + return 0; +} + +static int +delete_stdin(int agent_fd, int qflag) +{ + char *line = NULL, *cp; + size_t linesize = 0; + struct sshkey *key = NULL; + int lnum = 0, r, ret = -1; + + while (getline(&line, &linesize, stdin) != -1) { + lnum++; + sshkey_free(key); + key = NULL; + line[strcspn(line, "\n")] = '\0'; + cp = line + strspn(line, " \t"); + if (*cp == '#' || *cp == '\0') + continue; + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal_f("sshkey_new"); + if ((r = sshkey_read(key, &cp)) != 0) { + error_r(r, "(stdin):%d: invalid key", lnum); + continue; + } + if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 0) + ret = 0; + } + sshkey_free(key); + free(line); + return ret; +} + +static int +delete_file(int agent_fd, const char *filename, int key_only, int qflag) +{ + struct sshkey *public, *cert = NULL; + char *certpath = NULL, *comment = NULL; + int r, ret = -1; + + if (strcmp(filename, "-") == 0) + return delete_stdin(agent_fd, qflag); + + if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { + printf("Bad key file %s: %s\n", filename, ssh_err(r)); + return -1; + } + if (delete_one(agent_fd, public, comment, filename, qflag) == 0) + ret = 0; + + if (key_only) + goto out; + + /* Now try to delete the corresponding certificate too */ + free(comment); + comment = NULL; + xasprintf(&certpath, "%s-cert.pub", filename); + if ((r = sshkey_load_public(certpath, &cert, &comment)) != 0) { + if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) + error_r(r, "Failed to load certificate \"%s\"", certpath); + goto out; + } + + if (!sshkey_equal_public(cert, public)) + fatal("Certificate %s does not match private key %s", + certpath, filename); + + if (delete_one(agent_fd, cert, comment, certpath, qflag) == 0) + ret = 0; + + out: + sshkey_free(cert); + sshkey_free(public); + free(certpath); + free(comment); + + return ret; +} + +/* Send a request to remove all identities. */ +static int +delete_all(int agent_fd, int qflag) +{ + int ret = -1; + + /* + * Since the agent might be forwarded, old or non-OpenSSH, when asked + * to remove all keys, attempt to remove both protocol v.1 and v.2 + * keys. + */ + if (ssh_remove_all_identities(agent_fd, 2) == 0) + ret = 0; + /* ignore error-code for ssh1 */ + ssh_remove_all_identities(agent_fd, 1); + + if (ret != 0) + fprintf(stderr, "Failed to remove all identities.\n"); + else if (!qflag) + fprintf(stderr, "All identities removed.\n"); + + return ret; +} + +static int +add_file(int agent_fd, const char *filename, int key_only, int qflag, + const char *skprovider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) +{ + struct sshkey *private, *cert; + char *comment = NULL; + char msg[1024], *certpath = NULL; + int r, fd, ret = -1; + size_t i; + u_int32_t left; + struct sshbuf *keyblob; + struct ssh_identitylist *idlist; + + if (strcmp(filename, "-") == 0) { + fd = STDIN_FILENO; + filename = "(stdin)"; + } else if ((fd = open(filename, O_RDONLY)) == -1) { + perror(filename); + return -1; + } + + /* + * Since we'll try to load a keyfile multiple times, permission errors + * will occur multiple times, so check perms first and bail if wrong. + */ + if (fd != STDIN_FILENO) { + if (sshkey_perm_ok(fd, filename) != 0) { + close(fd); + return -1; + } + } + if ((r = sshbuf_load_fd(fd, &keyblob)) != 0) { + fprintf(stderr, "Error loading key \"%s\": %s\n", + filename, ssh_err(r)); + sshbuf_free(keyblob); + close(fd); + return -1; + } + close(fd); + + /* At first, try empty passphrase */ + if ((r = sshkey_parse_private_fileblob(keyblob, "", &private, + &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { + fprintf(stderr, "Error loading key \"%s\": %s\n", + filename, ssh_err(r)); + goto fail_load; + } + /* try last */ + if (private == NULL && pass != NULL) { + if ((r = sshkey_parse_private_fileblob(keyblob, pass, &private, + &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE) { + fprintf(stderr, "Error loading key \"%s\": %s\n", + filename, ssh_err(r)); + goto fail_load; + } + } + if (private == NULL) { + /* clear passphrase since it did not work */ + clear_pass(); + snprintf(msg, sizeof msg, "Enter passphrase for %s%s: ", + filename, confirm ? " (will confirm each use)" : ""); + for (;;) { + pass = read_passphrase(msg, RP_ALLOW_STDIN); + if (strcmp(pass, "") == 0) + goto fail_load; + if ((r = sshkey_parse_private_fileblob(keyblob, pass, + &private, &comment)) == 0) + break; + else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) { + fprintf(stderr, + "Error loading key \"%s\": %s\n", + filename, ssh_err(r)); + fail_load: + clear_pass(); + sshbuf_free(keyblob); + return -1; + } + clear_pass(); + snprintf(msg, sizeof msg, + "Bad passphrase, try again for %s%s: ", filename, + confirm ? " (will confirm each use)" : ""); + } + } + if (comment == NULL || *comment == '\0') + comment = xstrdup(filename); + sshbuf_free(keyblob); + + /* For XMSS */ + if ((r = sshkey_set_filename(private, filename)) != 0) { + fprintf(stderr, "Could not add filename to private key: %s (%s)\n", + filename, comment); + goto out; + } + if (maxsign && minleft && + (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) { + for (i = 0; i < idlist->nkeys; i++) { + if (!sshkey_equal_public(idlist->keys[i], private)) + continue; + left = sshkey_signatures_left(idlist->keys[i]); + if (left < minleft) { + fprintf(stderr, + "Only %d signatures left.\n", left); + break; + } + fprintf(stderr, "Skipping update: "); + if (left == minleft) { + fprintf(stderr, + "required signatures left (%d).\n", left); + } else { + fprintf(stderr, + "more signatures left (%d) than" + " required (%d).\n", left, minleft); + } + ssh_free_identitylist(idlist); + goto out; + } + ssh_free_identitylist(idlist); + } + + if (sshkey_is_sk(private)) { + if (skprovider == NULL) { + fprintf(stderr, "Cannot load FIDO key %s " + "without provider\n", filename); + goto out; + } + } else { + /* Don't send provider constraint for other keys */ + skprovider = NULL; + } + + if ((r = ssh_add_identity_constrained(agent_fd, private, comment, + lifetime, confirm, maxsign, skprovider, + dest_constraints, ndest_constraints)) == 0) { + ret = 0; + if (!qflag) { + fprintf(stderr, "Identity added: %s (%s)\n", + filename, comment); + if (lifetime != 0) { + fprintf(stderr, + "Lifetime set to %d seconds\n", lifetime); + } + if (confirm != 0) { + fprintf(stderr, "The user must confirm " + "each use of the key\n"); + } + } + } else { + fprintf(stderr, "Could not add identity \"%s\": %s\n", + filename, ssh_err(r)); + } + + /* Skip trying to load the cert if requested */ + if (key_only) + goto out; + + /* Now try to add the certificate flavour too */ + xasprintf(&certpath, "%s-cert.pub", filename); + if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { + if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) + error_r(r, "Failed to load certificate \"%s\"", certpath); + goto out; + } + + if (!sshkey_equal_public(cert, private)) { + error("Certificate %s does not match private key %s", + certpath, filename); + sshkey_free(cert); + goto out; + } + + /* Graft with private bits */ + if ((r = sshkey_to_certified(private)) != 0) { + error_fr(r, "sshkey_to_certified"); + sshkey_free(cert); + goto out; + } + if ((r = sshkey_cert_copy(cert, private)) != 0) { + error_fr(r, "sshkey_cert_copy"); + sshkey_free(cert); + goto out; + } + sshkey_free(cert); + + if ((r = ssh_add_identity_constrained(agent_fd, private, comment, + lifetime, confirm, maxsign, skprovider, + dest_constraints, ndest_constraints)) != 0) { + error_r(r, "Certificate %s (%s) add failed", certpath, + private->cert->key_id); + goto out; + } + /* success */ + if (!qflag) { + fprintf(stderr, "Certificate added: %s (%s)\n", certpath, + private->cert->key_id); + if (lifetime != 0) { + fprintf(stderr, "Lifetime set to %d seconds\n", + lifetime); + } + if (confirm != 0) { + fprintf(stderr, "The user must confirm each use " + "of the key\n"); + } + } + + out: + free(certpath); + free(comment); + sshkey_free(private); + + return ret; +} + +static int +update_card(int agent_fd, int add, const char *id, int qflag, + struct dest_constraint **dest_constraints, size_t ndest_constraints) +{ + char *pin = NULL; + int r, ret = -1; + + if (add) { + if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", + RP_ALLOW_STDIN)) == NULL) + return -1; + } + + if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, + lifetime, confirm, dest_constraints, ndest_constraints)) == 0) { + ret = 0; + if (!qflag) { + fprintf(stderr, "Card %s: %s\n", + add ? "added" : "removed", id); + } + } else { + fprintf(stderr, "Could not %s card \"%s\": %s\n", + add ? "add" : "remove", id, ssh_err(r)); + ret = -1; + } + free(pin); + return ret; +} + +static int +test_key(int agent_fd, const char *filename) +{ + struct sshkey *key = NULL; + u_char *sig = NULL; + size_t slen = 0; + int r, ret = -1; + char data[1024]; + + if ((r = sshkey_load_public(filename, &key, NULL)) != 0) { + error_r(r, "Couldn't read public key %s", filename); + return -1; + } + arc4random_buf(data, sizeof(data)); + if ((r = ssh_agent_sign(agent_fd, key, &sig, &slen, data, sizeof(data), + NULL, 0)) != 0) { + error_r(r, "Agent signature failed for %s", filename); + goto done; + } + if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), + NULL, 0, NULL)) != 0) { + error_r(r, "Signature verification failed for %s", filename); + goto done; + } + /* success */ + ret = 0; + done: + free(sig); + sshkey_free(key); + return ret; +} + +static int +list_identities(int agent_fd, int do_fp) +{ + char *fp; + int r; + struct ssh_identitylist *idlist; + u_int32_t left; + size_t i; + + if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { + if (r != SSH_ERR_AGENT_NO_IDENTITIES) + fprintf(stderr, "error fetching identities: %s\n", + ssh_err(r)); + else + printf("The agent has no identities.\n"); + return -1; + } + for (i = 0; i < idlist->nkeys; i++) { + if (do_fp) { + fp = sshkey_fingerprint(idlist->keys[i], + fingerprint_hash, SSH_FP_DEFAULT); + printf("%u %s %s (%s)\n", sshkey_size(idlist->keys[i]), + fp == NULL ? "(null)" : fp, idlist->comments[i], + sshkey_type(idlist->keys[i])); + free(fp); + } else { + if ((r = sshkey_write(idlist->keys[i], stdout)) != 0) { + fprintf(stderr, "sshkey_write: %s\n", + ssh_err(r)); + continue; + } + fprintf(stdout, " %s", idlist->comments[i]); + left = sshkey_signatures_left(idlist->keys[i]); + if (left > 0) + fprintf(stdout, + " [signatures left %d]", left); + fprintf(stdout, "\n"); + } + } + ssh_free_identitylist(idlist); + return 0; +} + +static int +lock_agent(int agent_fd, int lock) +{ + char prompt[100], *p1, *p2; + int r, passok = 1, ret = -1; + + strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); + p1 = read_passphrase(prompt, RP_ALLOW_STDIN); + if (lock) { + strlcpy(prompt, "Again: ", sizeof prompt); + p2 = read_passphrase(prompt, RP_ALLOW_STDIN); + if (strcmp(p1, p2) != 0) { + fprintf(stderr, "Passwords do not match.\n"); + passok = 0; + } + freezero(p2, strlen(p2)); + } + if (passok) { + if ((r = ssh_lock_agent(agent_fd, lock, p1)) == 0) { + fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); + ret = 0; + } else { + fprintf(stderr, "Failed to %slock agent: %s\n", + lock ? "" : "un", ssh_err(r)); + } + } + freezero(p1, strlen(p1)); + return (ret); +} + +static int +load_resident_keys(int agent_fd, const char *skprovider, int qflag, + struct dest_constraint **dest_constraints, size_t ndest_constraints) +{ + struct sshsk_resident_key **srks; + size_t nsrks, i; + struct sshkey *key; + int r, ok = 0; + char *fp; + + pass = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); + if ((r = sshsk_load_resident(skprovider, NULL, pass, 0, + &srks, &nsrks)) != 0) { + error_r(r, "Unable to load resident keys"); + return r; + } + for (i = 0; i < nsrks; i++) { + key = srks[i]->key; + if ((fp = sshkey_fingerprint(key, + fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + if ((r = ssh_add_identity_constrained(agent_fd, key, "", + lifetime, confirm, maxsign, skprovider, + dest_constraints, ndest_constraints)) != 0) { + error("Unable to add key %s %s", + sshkey_type(key), fp); + free(fp); + ok = r; + continue; + } + if (ok == 0) + ok = 1; + if (!qflag) { + fprintf(stderr, "Resident identity added: %s %s\n", + sshkey_type(key), fp); + if (lifetime != 0) { + fprintf(stderr, + "Lifetime set to %d seconds\n", lifetime); + } + if (confirm != 0) { + fprintf(stderr, "The user must confirm " + "each use of the key\n"); + } + } + free(fp); + } + sshsk_free_resident_keys(srks, nsrks); + if (nsrks == 0) + return SSH_ERR_KEY_NOT_FOUND; + return ok == 1 ? 0 : ok; +} + +static int +do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, + const char *skprovider, struct dest_constraint **dest_constraints, + size_t ndest_constraints) +{ + if (deleting) { + if (delete_file(agent_fd, file, key_only, qflag) == -1) + return -1; + } else { + if (add_file(agent_fd, file, key_only, qflag, skprovider, + dest_constraints, ndest_constraints) == -1) + return -1; + } + return 0; +} + +/* Append string 's' to a NULL-terminated array of strings */ +static void +stringlist_append(char ***listp, const char *s) +{ + size_t i = 0; + + if (*listp == NULL) + *listp = xcalloc(2, sizeof(**listp)); + else { + for (i = 0; (*listp)[i] != NULL; i++) + ; /* count */ + *listp = xrecallocarray(*listp, i + 1, i + 2, sizeof(**listp)); + } + (*listp)[i] = xstrdup(s); +} + +static void +parse_dest_constraint_hop(const char *s, struct dest_constraint_hop *dch, + char **hostkey_files) +{ + char *user = NULL, *host, *os, *path; + size_t i; + struct hostkeys *hostkeys; + const struct hostkey_entry *hke; + int r, want_ca; + + memset(dch, '\0', sizeof(*dch)); + os = xstrdup(s); + if ((host = strchr(os, '@')) == NULL) + host = os; + else { + *host++ = '\0'; + user = os; + } + cleanhostname(host); + /* Trivial case: username@ (all hosts) */ + if (*host == '\0') { + if (user == NULL) { + fatal("Invalid key destination constraint \"%s\": " + "does not specify user or host", s); + } + dch->user = xstrdup(user); + /* other fields left blank */ + free(os); + return; + } + if (hostkey_files == NULL) + fatal_f("no hostkey files"); + /* Otherwise we need to look up the keys for this hostname */ + hostkeys = init_hostkeys(); + for (i = 0; hostkey_files[i]; i++) { + path = tilde_expand_filename(hostkey_files[i], getuid()); + debug2_f("looking up host keys for \"%s\" in %s", host, path); + load_hostkeys(hostkeys, host, path, 0); + free(path); + } + dch->user = user == NULL ? NULL : xstrdup(user); + dch->hostname = xstrdup(host); + for (i = 0; i < hostkeys->num_entries; i++) { + hke = hostkeys->entries + i; + want_ca = hke->marker == MRK_CA; + if (hke->marker != MRK_NONE && !want_ca) + continue; + debug3_f("%s%s%s: adding %s %skey from %s:%lu as key %u", + user == NULL ? "": user, user == NULL ? "" : "@", + host, sshkey_type(hke->key), want_ca ? "CA " : "", + hke->file, hke->line, dch->nkeys); + dch->keys = xrecallocarray(dch->keys, dch->nkeys, + dch->nkeys + 1, sizeof(*dch->keys)); + dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys, + dch->nkeys + 1, sizeof(*dch->key_is_ca)); + if ((r = sshkey_from_private(hke->key, + &(dch->keys[dch->nkeys]))) != 0) + fatal_fr(r, "sshkey_from_private"); + dch->key_is_ca[dch->nkeys] = want_ca; + dch->nkeys++; + } + if (dch->nkeys == 0) + fatal("No host keys found for destination \"%s\"", host); + free_hostkeys(hostkeys); + free(os); + return; +} + +static void +parse_dest_constraint(const char *s, struct dest_constraint ***dcp, + size_t *ndcp, char **hostkey_files) +{ + struct dest_constraint *dc; + char *os, *cp; + + dc = xcalloc(1, sizeof(*dc)); + os = xstrdup(s); + if ((cp = strchr(os, '>')) == NULL) { + /* initial hop; no 'from' hop specified */ + parse_dest_constraint_hop(os, &dc->to, hostkey_files); + } else { + /* two hops specified */ + *(cp++) = '\0'; + parse_dest_constraint_hop(os, &dc->from, hostkey_files); + parse_dest_constraint_hop(cp, &dc->to, hostkey_files); + if (dc->from.user != NULL) { + fatal("Invalid key constraint %s: cannot specify " + "user on 'from' host", os); + } + } + /* XXX eliminate or error on duplicates */ + debug2_f("constraint %zu: %s%s%s (%u keys) > %s%s%s (%u keys)", *ndcp, + dc->from.user ? dc->from.user : "", dc->from.user ? "@" : "", + dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys, + dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "", + dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys); + *dcp = xrecallocarray(*dcp, *ndcp, *ndcp + 1, sizeof(**dcp)); + (*dcp)[(*ndcp)++] = dc; + free(os); +} + + +static void +usage(void) +{ + fprintf(stderr, +"usage: ssh-add [-cDdKkLlqvXx] [-E fingerprint_hash] [-H hostkey_file]\n" +" [-h destination_constraint] [-S provider] [-t life]\n" +#ifdef WITH_XMSS +" [-M maxsign] [-m minleft]\n" +#endif +" [file ...]\n" +" ssh-add -s pkcs11\n" +" ssh-add -e pkcs11\n" +" ssh-add -T pubkey ...\n" + ); +} + +int +main(int argc, char **argv) +{ + extern char *optarg; + extern int optind; + int agent_fd; + char *pkcs11provider = NULL, *skprovider = NULL; + char **dest_constraint_strings = NULL, **hostkey_files = NULL; + int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; + int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_INFO; + struct dest_constraint **dest_constraints = NULL; + size_t ndest_constraints = 0; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + __progname = ssh_get_progname(argv[0]); + seed_rng(); + + log_init(__progname, log_level, log_facility, 1); + + setvbuf(stdout, NULL, _IOLBF, 0); + + /* First, get a connection to the authentication agent. */ + switch (r = ssh_get_authentication_socket(&agent_fd)) { + case 0: + break; + case SSH_ERR_AGENT_NOT_PRESENT: + fprintf(stderr, "Could not open a connection to your " + "authentication agent.\n"); + exit(2); + default: + fprintf(stderr, "Error connecting to agent: %s\n", ssh_err(r)); + exit(2); + } + + skprovider = getenv("SSH_SK_PROVIDER"); + + while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) { + switch (ch) { + case 'v': + if (log_level == SYSLOG_LEVEL_INFO) + log_level = SYSLOG_LEVEL_DEBUG1; + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + break; + case 'E': + fingerprint_hash = ssh_digest_alg_by_name(optarg); + if (fingerprint_hash == -1) + fatal("Invalid hash algorithm \"%s\"", optarg); + break; + case 'H': + stringlist_append(&hostkey_files, optarg); + break; + case 'h': + stringlist_append(&dest_constraint_strings, optarg); + break; + case 'k': + key_only = 1; + break; + case 'K': + do_download = 1; + break; + case 'l': + case 'L': + if (lflag != 0) + fatal("-%c flag already specified", lflag); + lflag = ch; + break; + case 'x': + case 'X': + if (xflag != 0) + fatal("-%c flag already specified", xflag); + xflag = ch; + break; + case 'c': + confirm = 1; + break; + case 'm': + minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL); + if (minleft == 0) { + usage(); + ret = 1; + goto done; + } + break; + case 'M': + maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL); + if (maxsign == 0) { + usage(); + ret = 1; + goto done; + } + break; + case 'd': + deleting = 1; + break; + case 'D': + Dflag = 1; + break; + case 's': + pkcs11provider = optarg; + break; + case 'S': + skprovider = optarg; + break; + case 'e': + deleting = 1; + pkcs11provider = optarg; + break; + case 't': + if ((lifetime = convtime(optarg)) == -1 || + lifetime < 0 || (u_long)lifetime > UINT32_MAX) { + fprintf(stderr, "Invalid lifetime\n"); + ret = 1; + goto done; + } + break; + case 'q': + qflag = 1; + break; + case 'T': + Tflag = 1; + break; + default: + usage(); + ret = 1; + goto done; + } + } + log_init(__progname, log_level, log_facility, 1); + + if ((xflag != 0) + (lflag != 0) + (Dflag != 0) > 1) + fatal("Invalid combination of actions"); + else if (xflag) { + if (lock_agent(agent_fd, xflag == 'x' ? 1 : 0) == -1) + ret = 1; + goto done; + } else if (lflag) { + if (list_identities(agent_fd, lflag == 'l' ? 1 : 0) == -1) + ret = 1; + goto done; + } else if (Dflag) { + if (delete_all(agent_fd, qflag) == -1) + ret = 1; + goto done; + } + +#ifdef ENABLE_SK_INTERNAL + if (skprovider == NULL) + skprovider = "internal"; +#endif + + if (hostkey_files == NULL) { + /* use defaults from readconf.c */ + stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE); + stringlist_append(&hostkey_files, _PATH_SSH_USER_HOSTFILE2); + stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE); + stringlist_append(&hostkey_files, _PATH_SSH_SYSTEM_HOSTFILE2); + } + if (dest_constraint_strings != NULL) { + for (i = 0; dest_constraint_strings[i] != NULL; i++) { + parse_dest_constraint(dest_constraint_strings[i], + &dest_constraints, &ndest_constraints, hostkey_files); + } + } + + argc -= optind; + argv += optind; + if (Tflag) { + if (argc <= 0) + fatal("no keys to test"); + for (r = i = 0; i < argc; i++) + r |= test_key(agent_fd, argv[i]); + ret = r == 0 ? 0 : 1; + goto done; + } + if (pkcs11provider != NULL) { + if (update_card(agent_fd, !deleting, pkcs11provider, + qflag, dest_constraints, ndest_constraints) == -1) + ret = 1; + goto done; + } + if (do_download) { + if (skprovider == NULL) + fatal("Cannot download keys without provider"); + if (load_resident_keys(agent_fd, skprovider, qflag, + dest_constraints, ndest_constraints) != 0) + ret = 1; + goto done; + } + if (argc == 0) { + char buf[PATH_MAX]; + struct passwd *pw; + struct stat st; + int count = 0; + + if ((pw = getpwuid(getuid())) == NULL) { + fprintf(stderr, "No user found with uid %u\n", + (u_int)getuid()); + ret = 1; + goto done; + } + + for (i = 0; default_files[i]; i++) { + snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, + default_files[i]); + if (stat(buf, &st) == -1) + continue; + if (do_file(agent_fd, deleting, key_only, buf, + qflag, skprovider, + dest_constraints, ndest_constraints) == -1) + ret = 1; + else + count++; + } + if (count == 0) + ret = 1; + } else { + for (i = 0; i < argc; i++) { + if (do_file(agent_fd, deleting, key_only, + argv[i], qflag, skprovider, + dest_constraints, ndest_constraints) == -1) + ret = 1; + } + } +done: + clear_pass(); + ssh_close_authentication_socket(agent_fd); + return ret; +} diff --git a/aosp/external/openssh/ssh-agent.1 b/aosp/external/openssh/ssh-agent.1 new file mode 100644 index 0000000000000000000000000000000000000000..ea43cd156be36b809a7c3860dcc0acff62ff7a15 --- /dev/null +++ b/aosp/external/openssh/ssh-agent.1 @@ -0,0 +1,233 @@ +.\" $OpenBSD: ssh-agent.1,v 1.73 2022/03/31 17:27:27 naddy Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SSH-AGENT 1 +.Os +.Sh NAME +.Nm ssh-agent +.Nd OpenSSH authentication agent +.Sh SYNOPSIS +.Nm ssh-agent +.Op Fl c | s +.Op Fl \&Dd +.Op Fl a Ar bind_address +.Op Fl E Ar fingerprint_hash +.Op Fl P Ar allowed_providers +.Op Fl t Ar life +.Nm ssh-agent +.Op Fl a Ar bind_address +.Op Fl E Ar fingerprint_hash +.Op Fl P Ar allowed_providers +.Op Fl t Ar life +.Ar command Op Ar arg ... +.Nm ssh-agent +.Op Fl c | s +.Fl k +.Sh DESCRIPTION +.Nm +is a program to hold private keys used for public key authentication. +Through use of environment variables the agent can be located +and automatically used for authentication when logging in to other +machines using +.Xr ssh 1 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a Ar bind_address +Bind the agent to the +.Ux Ns -domain +socket +.Ar bind_address . +The default is +.Pa $TMPDIR/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt . +.It Fl c +Generate C-shell commands on +.Dv stdout . +This is the default if +.Ev SHELL +looks like it's a csh style of shell. +.It Fl D +Foreground mode. +When this option is specified, +.Nm +will not fork. +.It Fl d +Debug mode. +When this option is specified, +.Nm +will not fork and will write debug information to standard error. +.It Fl E Ar fingerprint_hash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . +.It Fl k +Kill the current agent (given by the +.Ev SSH_AGENT_PID +environment variable). +.It Fl P Ar allowed_providers +Specify a pattern-list of acceptable paths for PKCS#11 provider and FIDO +authenticator middleware shared libraries that may be used with the +.Fl S +or +.Fl s +options to +.Xr ssh-add 1 . +Libraries that do not match the pattern list will be refused. +See PATTERNS in +.Xr ssh_config 5 +for a description of pattern-list syntax. +The default list is +.Dq /usr/lib/*,/usr/local/lib/* . +.It Fl s +Generate Bourne shell commands on +.Dv stdout . +This is the default if +.Ev SHELL +does not look like it's a csh style of shell. +.It Fl t Ar life +Set a default value for the maximum lifetime of identities added to the agent. +The lifetime may be specified in seconds or in a time format specified in +.Xr sshd_config 5 . +A lifetime specified for an identity with +.Xr ssh-add 1 +overrides this value. +Without this option the default maximum lifetime is forever. +.It Ar command Op Ar arg ... +If a command (and optional arguments) is given, +this is executed as a subprocess of the agent. +The agent exits automatically when the command given on the command +line terminates. +.El +.Pp +There are two main ways to get an agent set up. +The first is at the start of an X session, +where all other windows or programs are started as children of the +.Nm +program. +The agent starts a command under which its environment +variables are exported, for example +.Cm ssh-agent xterm & . +When the command terminates, so does the agent. +.Pp +The second method is used for a login session. +When +.Nm +is started, +it prints the shell commands required to set its environment variables, +which in turn can be evaluated in the calling shell, for example +.Cm eval `ssh-agent -s` . +.Pp +In both cases, +.Xr ssh 1 +looks at these environment variables +and uses them to establish a connection to the agent. +.Pp +The agent initially does not have any private keys. +Keys are added using +.Xr ssh-add 1 +or by +.Xr ssh 1 +when +.Cm AddKeysToAgent +is set in +.Xr ssh_config 5 . +Multiple identities may be stored in +.Nm +concurrently and +.Xr ssh 1 +will automatically use them if present. +.Xr ssh-add 1 +is also used to remove keys from +.Nm +and to query the keys that are held in one. +.Pp +Connections to +.Nm +may be forwarded from further remote hosts using the +.Fl A +option to +.Xr ssh 1 +(but see the caveats documented therein), +avoiding the need for authentication data to be stored on other machines. +Authentication passphrases and private keys never go over the network: +the connection to the agent is forwarded over SSH remote connections +and the result is returned to the requester, +allowing the user access to their identities anywhere in the network +in a secure fashion. +.Sh ENVIRONMENT +.Bl -tag -width "SSH_AGENT_PID" +.It Ev SSH_AGENT_PID +When +.Nm +starts, it stores the name of the agent's process ID (PID) in this variable. +.It Ev SSH_AUTH_SOCK +When +.Nm +starts, it creates a +.Ux Ns -domain +socket and stores its pathname in this variable. +It is accessible only to the current user, +but is easily abused by root or another instance of the same user. +.El +.Sh FILES +.Bl -tag -width Ds +.It Pa $TMPDIR/ssh-XXXXXXXXXX/agent. +.Ux Ns -domain +sockets used to contain the connection to the authentication agent. +These sockets should only be readable by the owner. +The sockets should get automatically removed when the agent exits. +.El +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr ssh_config 5 , +.Xr sshd 8 +.Sh AUTHORS +.An -nosplit +OpenSSH is a derivative of the original and free ssh 1.2.12 release by +.An Tatu Ylonen . +.An Aaron Campbell , Bob Beck , Markus Friedl , Niels Provos , Theo de Raadt +and +.An Dug Song +removed many bugs, re-added newer features and created OpenSSH. +.An Markus Friedl +contributed the support for SSH protocol versions 1.5 and 2.0. diff --git a/aosp/external/openssh/ssh-agent.c b/aosp/external/openssh/ssh-agent.c new file mode 100644 index 0000000000000000000000000000000000000000..03ae2b022eedf1334e55f229dec1f4743eababe1 --- /dev/null +++ b/aosp/external/openssh/ssh-agent.c @@ -0,0 +1,2281 @@ +/* $OpenBSD: ssh-agent.c,v 1.287 2022/01/14 03:43:48 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * The authentication agent program. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_UN_H +# include +#endif +#include "openbsd-compat/sys-queue.h" + +#ifdef WITH_OPENSSL +#include +#include "openbsd-compat/openssl-compat.h" +#endif + +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#ifdef HAVE_POLL_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UTIL_H +# include +#endif + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "authfd.h" +#include "compat.h" +#include "log.h" +#include "misc.h" +#include "digest.h" +#include "ssherr.h" +#include "match.h" +#include "msg.h" +#include "ssherr.h" +#include "pathnames.h" +#include "ssh-pkcs11.h" +#include "sk-api.h" +#include "myproposal.h" + +#ifndef DEFAULT_ALLOWED_PROVIDERS +# define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*" +#endif + +/* Maximum accepted message length */ +#define AGENT_MAX_LEN (256*1024) +/* Maximum bytes to read from client socket */ +#define AGENT_RBUF_LEN (4096) +/* Maximum number of recorded session IDs/hostkeys per connection */ +#define AGENT_MAX_SESSION_IDS 16 +/* Maximum size of session ID */ +#define AGENT_MAX_SID_LEN 128 +/* Maximum number of destination constraints to accept on a key */ +#define AGENT_MAX_DEST_CONSTRAINTS 1024 + +/* XXX store hostkey_sid in a refcounted tree */ + +typedef enum { + AUTH_UNUSED = 0, + AUTH_SOCKET = 1, + AUTH_CONNECTION = 2, +} sock_type; + +struct hostkey_sid { + struct sshkey *key; + struct sshbuf *sid; + int forwarded; +}; + +typedef struct socket_entry { + int fd; + sock_type type; + struct sshbuf *input; + struct sshbuf *output; + struct sshbuf *request; + size_t nsession_ids; + struct hostkey_sid *session_ids; +} SocketEntry; + +u_int sockets_alloc = 0; +SocketEntry *sockets = NULL; + +typedef struct identity { + TAILQ_ENTRY(identity) next; + struct sshkey *key; + char *comment; + char *provider; + time_t death; + u_int confirm; + char *sk_provider; + struct dest_constraint *dest_constraints; + size_t ndest_constraints; +} Identity; + +struct idtable { + int nentries; + TAILQ_HEAD(idqueue, identity) idlist; +}; + +/* private key table */ +struct idtable *idtab; + +int max_fd = 0; + +/* pid of shell == parent of agent */ +pid_t parent_pid = -1; +time_t parent_alive_interval = 0; + +/* pid of process for which cleanup_socket is applicable */ +pid_t cleanup_pid = 0; + +/* pathname and directory for AUTH_SOCKET */ +char socket_name[PATH_MAX]; +char socket_dir[PATH_MAX]; + +/* Pattern-list of allowed PKCS#11/Security key paths */ +static char *allowed_providers; + +/* locking */ +#define LOCK_SIZE 32 +#define LOCK_SALT_SIZE 16 +#define LOCK_ROUNDS 1 +int locked = 0; +u_char lock_pwhash[LOCK_SIZE]; +u_char lock_salt[LOCK_SALT_SIZE]; + +extern char *__progname; + +/* Default lifetime in seconds (0 == forever) */ +static int lifetime = 0; + +static int fingerprint_hash = SSH_FP_HASH_DEFAULT; + +/* Refuse signing of non-SSH messages for web-origin FIDO keys */ +static int restrict_websafe = 1; + +static void +close_socket(SocketEntry *e) +{ + size_t i; + + close(e->fd); + sshbuf_free(e->input); + sshbuf_free(e->output); + sshbuf_free(e->request); + for (i = 0; i < e->nsession_ids; i++) { + sshkey_free(e->session_ids[i].key); + sshbuf_free(e->session_ids[i].sid); + } + free(e->session_ids); + memset(e, '\0', sizeof(*e)); + e->fd = -1; + e->type = AUTH_UNUSED; +} + +static void +idtab_init(void) +{ + idtab = xcalloc(1, sizeof(*idtab)); + TAILQ_INIT(&idtab->idlist); + idtab->nentries = 0; +} + +static void +free_dest_constraint_hop(struct dest_constraint_hop *dch) +{ + u_int i; + + if (dch == NULL) + return; + free(dch->user); + free(dch->hostname); + for (i = 0; i < dch->nkeys; i++) + sshkey_free(dch->keys[i]); + free(dch->keys); + free(dch->key_is_ca); +} + +static void +free_dest_constraints(struct dest_constraint *dcs, size_t ndcs) +{ + size_t i; + + for (i = 0; i < ndcs; i++) { + free_dest_constraint_hop(&dcs[i].from); + free_dest_constraint_hop(&dcs[i].to); + } + free(dcs); +} + +static void +free_identity(Identity *id) +{ + sshkey_free(id->key); + free(id->provider); + free(id->comment); + free(id->sk_provider); + free_dest_constraints(id->dest_constraints, id->ndest_constraints); + free(id); +} + +/* + * Match 'key' against the key/CA list in a destination constraint hop + * Returns 0 on success or -1 otherwise. + */ +static int +match_key_hop(const char *tag, const struct sshkey *key, + const struct dest_constraint_hop *dch) +{ + const char *reason = NULL; + const char *hostname = dch->hostname ? dch->hostname : "(ORIGIN)"; + u_int i; + char *fp; + + if (key == NULL) + return -1; + /* XXX logspam */ + if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + debug3_f("%s: entering hostname %s, requested key %s %s, %u keys avail", + tag, hostname, sshkey_type(key), fp, dch->nkeys); + free(fp); + for (i = 0; i < dch->nkeys; i++) { + if (dch->keys[i] == NULL) + return -1; + /* XXX logspam */ + if ((fp = sshkey_fingerprint(dch->keys[i], SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + debug3_f("%s: key %u: %s%s %s", tag, i, + dch->key_is_ca[i] ? "CA " : "", + sshkey_type(dch->keys[i]), fp); + free(fp); + if (!sshkey_is_cert(key)) { + /* plain key */ + if (dch->key_is_ca[i] || + !sshkey_equal(key, dch->keys[i])) + continue; + return 0; + } + /* certificate */ + if (!dch->key_is_ca[i]) + continue; + if (key->cert == NULL || key->cert->signature_key == NULL) + return -1; /* shouldn't happen */ + if (!sshkey_equal(key->cert->signature_key, dch->keys[i])) + continue; + if (sshkey_cert_check_host(key, hostname, 1, + SSH_ALLOWED_CA_SIGALGS, &reason) != 0) { + debug_f("cert %s / hostname %s rejected: %s", + key->cert->key_id, hostname, reason); + continue; + } + return 0; + } + return -1; +} + +/* Check destination constraints on an identity against the hostkey/user */ +static int +permitted_by_dest_constraints(const struct sshkey *fromkey, + const struct sshkey *tokey, Identity *id, const char *user, + const char **hostnamep) +{ + size_t i; + struct dest_constraint *d; + + if (hostnamep != NULL) + *hostnamep = NULL; + for (i = 0; i < id->ndest_constraints; i++) { + d = id->dest_constraints + i; + /* XXX remove logspam */ + debug2_f("constraint %zu %s%s%s (%u keys) > %s%s%s (%u keys)", + i, d->from.user ? d->from.user : "", + d->from.user ? "@" : "", + d->from.hostname ? d->from.hostname : "(ORIGIN)", + d->from.nkeys, + d->to.user ? d->to.user : "", d->to.user ? "@" : "", + d->to.hostname ? d->to.hostname : "(ANY)", d->to.nkeys); + + /* Match 'from' key */ + if (fromkey == NULL) { + /* We are matching the first hop */ + if (d->from.hostname != NULL || d->from.nkeys != 0) + continue; + } else if (match_key_hop("from", fromkey, &d->from) != 0) + continue; + + /* Match 'to' key */ + if (tokey != NULL && match_key_hop("to", tokey, &d->to) != 0) + continue; + + /* Match user if specified */ + if (d->to.user != NULL && user != NULL && + !match_pattern(user, d->to.user)) + continue; + + /* successfully matched this constraint */ + if (hostnamep != NULL) + *hostnamep = d->to.hostname; + debug2_f("allowed for hostname %s", + d->to.hostname == NULL ? "*" : d->to.hostname); + return 0; + } + /* no match */ + debug2_f("%s identity \"%s\" not permitted for this destination", + sshkey_type(id->key), id->comment); + return -1; +} + +/* + * Check whether hostkeys on a SocketEntry and the optionally specified user + * are permitted by the destination constraints on the Identity. + * Returns 0 on success or -1 otherwise. + */ +static int +identity_permitted(Identity *id, SocketEntry *e, char *user, + const char **forward_hostnamep, const char **last_hostnamep) +{ + size_t i; + const char **hp; + struct hostkey_sid *hks; + const struct sshkey *fromkey = NULL; + const char *test_user; + char *fp1, *fp2; + + /* XXX remove logspam */ + debug3_f("entering: key %s comment \"%s\", %zu socket bindings, " + "%zu constraints", sshkey_type(id->key), id->comment, + e->nsession_ids, id->ndest_constraints); + if (id->ndest_constraints == 0) + return 0; /* unconstrained */ + if (e->nsession_ids == 0) + return 0; /* local use */ + /* + * Walk through the hops recorded by session_id and try to find a + * constraint that satisfies each. + */ + for (i = 0; i < e->nsession_ids; i++) { + hks = e->session_ids + i; + if (hks->key == NULL) + fatal_f("internal error: no bound key"); + /* XXX remove logspam */ + fp1 = fp2 = NULL; + if (fromkey != NULL && + (fp1 = sshkey_fingerprint(fromkey, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + if ((fp2 = sshkey_fingerprint(hks->key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + debug3_f("socketentry fd=%d, entry %zu %s, " + "from hostkey %s %s to user %s hostkey %s %s", + e->fd, i, hks->forwarded ? "FORWARD" : "AUTH", + fromkey ? sshkey_type(fromkey) : "(ORIGIN)", + fromkey ? fp1 : "", user ? user : "(ANY)", + sshkey_type(hks->key), fp2); + free(fp1); + free(fp2); + /* + * Record the hostnames for the initial forwarding and + * the final destination. + */ + hp = NULL; + if (i == e->nsession_ids - 1) + hp = last_hostnamep; + else if (i == 0) + hp = forward_hostnamep; + /* Special handling for final recorded binding */ + test_user = NULL; + if (i == e->nsession_ids - 1) { + /* Can only check user at final hop */ + test_user = user; + /* + * user is only presented for signature requests. + * If this is the case, make sure last binding is not + * for a forwarding. + */ + if (hks->forwarded && user != NULL) { + error_f("tried to sign on forwarding hop"); + return -1; + } + } else if (!hks->forwarded) { + error_f("tried to forward though signing bind"); + return -1; + } + if (permitted_by_dest_constraints(fromkey, hks->key, id, + test_user, hp) != 0) + return -1; + fromkey = hks->key; + } + /* + * Another special case: if the last bound session ID was for a + * forwarding, and this function is not being called to check a sign + * request (i.e. no 'user' supplied), then only permit the key if + * there is a permission that would allow it to be used at another + * destination. This hides keys that are allowed to be used to + * authenticate *to* a host but not permitted for *use* beyond it. + */ + hks = &e->session_ids[e->nsession_ids - 1]; + if (hks->forwarded && user == NULL && + permitted_by_dest_constraints(hks->key, NULL, id, + NULL, NULL) != 0) { + debug3_f("key permitted at host but not after"); + return -1; + } + + /* success */ + return 0; +} + +/* return matching private key for given public key */ +static Identity * +lookup_identity(struct sshkey *key) +{ + Identity *id; + + TAILQ_FOREACH(id, &idtab->idlist, next) { + if (sshkey_equal(key, id->key)) + return (id); + } + return (NULL); +} + +/* Check confirmation of keysign request */ +static int +confirm_key(Identity *id, const char *extra) +{ + char *p; + int ret = -1; + + p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT); + if (p != NULL && + ask_permission("Allow use of key %s?\nKey fingerprint %s.%s%s", + id->comment, p, + extra == NULL ? "" : "\n", extra == NULL ? "" : extra)) + ret = 0; + free(p); + + return (ret); +} + +static void +send_status(SocketEntry *e, int success) +{ + int r; + + if ((r = sshbuf_put_u32(e->output, 1)) != 0 || + (r = sshbuf_put_u8(e->output, success ? + SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) + fatal_fr(r, "compose"); +} + +/* send list of supported public keys to 'client' */ +static void +process_request_identities(SocketEntry *e) +{ + Identity *id; + struct sshbuf *msg, *keys; + int r; + u_int nentries = 0; + + debug2_f("entering"); + + if ((msg = sshbuf_new()) == NULL || (keys = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + TAILQ_FOREACH(id, &idtab->idlist, next) { + /* identity not visible, don't include in response */ + if (identity_permitted(id, e, NULL, NULL, NULL) != 0) + continue; + if ((r = sshkey_puts_opts(id->key, keys, + SSHKEY_SERIALIZE_INFO)) != 0 || + (r = sshbuf_put_cstring(keys, id->comment)) != 0) { + error_fr(r, "compose key/comment"); + continue; + } + nentries++; + } + debug2_f("replying with %u allowed of %u available keys", + nentries, idtab->nentries); + if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || + (r = sshbuf_put_u32(msg, nentries)) != 0 || + (r = sshbuf_putb(msg, keys)) != 0) + fatal_fr(r, "compose"); + if ((r = sshbuf_put_stringb(e->output, msg)) != 0) + fatal_fr(r, "enqueue"); + sshbuf_free(msg); + sshbuf_free(keys); +} + + +static char * +agent_decode_alg(struct sshkey *key, u_int flags) +{ + if (key->type == KEY_RSA) { + if (flags & SSH_AGENT_RSA_SHA2_256) + return "rsa-sha2-256"; + else if (flags & SSH_AGENT_RSA_SHA2_512) + return "rsa-sha2-512"; + } else if (key->type == KEY_RSA_CERT) { + if (flags & SSH_AGENT_RSA_SHA2_256) + return "rsa-sha2-256-cert-v01@openssh.com"; + else if (flags & SSH_AGENT_RSA_SHA2_512) + return "rsa-sha2-512-cert-v01@openssh.com"; + } + return NULL; +} + +/* + * Attempt to parse the contents of a buffer as a SSH publickey userauth + * request, checking its contents for consistency and matching the embedded + * key against the one that is being used for signing. + * Note: does not modify msg buffer. + * Optionally extract the username, session ID and/or hostkey from the request. + */ +static int +parse_userauth_request(struct sshbuf *msg, const struct sshkey *expected_key, + char **userp, struct sshbuf **sess_idp, struct sshkey **hostkeyp) +{ + struct sshbuf *b = NULL, *sess_id = NULL; + char *user = NULL, *service = NULL, *method = NULL, *pkalg = NULL; + int r; + u_char t, sig_follows; + struct sshkey *mkey = NULL, *hostkey = NULL; + + if (userp != NULL) + *userp = NULL; + if (sess_idp != NULL) + *sess_idp = NULL; + if (hostkeyp != NULL) + *hostkeyp = NULL; + if ((b = sshbuf_fromb(msg)) == NULL) + fatal_f("sshbuf_fromb"); + + /* SSH userauth request */ + if ((r = sshbuf_froms(b, &sess_id)) != 0) + goto out; + if (sshbuf_len(sess_id) == 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_u8(b, &t)) != 0 || /* SSH2_MSG_USERAUTH_REQUEST */ + (r = sshbuf_get_cstring(b, &user, NULL)) != 0 || /* server user */ + (r = sshbuf_get_cstring(b, &service, NULL)) != 0 || /* service */ + (r = sshbuf_get_cstring(b, &method, NULL)) != 0 || /* method */ + (r = sshbuf_get_u8(b, &sig_follows)) != 0 || /* sig-follows */ + (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0 || /* alg */ + (r = sshkey_froms(b, &mkey)) != 0) /* key */ + goto out; + if (t != SSH2_MSG_USERAUTH_REQUEST || + sig_follows != 1 || + strcmp(service, "ssh-connection") != 0 || + !sshkey_equal(expected_key, mkey) || + sshkey_type_from_name(pkalg) != expected_key->type) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (strcmp(method, "publickey-hostbound-v00@openssh.com") == 0) { + if ((r = sshkey_froms(b, &hostkey)) != 0) + goto out; + } else if (strcmp(method, "publickey") != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(b) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + r = 0; + debug3_f("well formed userauth"); + if (userp != NULL) { + *userp = user; + user = NULL; + } + if (sess_idp != NULL) { + *sess_idp = sess_id; + sess_id = NULL; + } + if (hostkeyp != NULL) { + *hostkeyp = hostkey; + hostkey = NULL; + } + out: + sshbuf_free(b); + sshbuf_free(sess_id); + free(user); + free(service); + free(method); + free(pkalg); + sshkey_free(mkey); + sshkey_free(hostkey); + return r; +} + +/* + * Attempt to parse the contents of a buffer as a SSHSIG signature request. + * Note: does not modify buffer. + */ +static int +parse_sshsig_request(struct sshbuf *msg) +{ + int r; + struct sshbuf *b; + + if ((b = sshbuf_fromb(msg)) == NULL) + fatal_f("sshbuf_fromb"); + + if ((r = sshbuf_cmp(b, 0, "SSHSIG", 6)) != 0 || + (r = sshbuf_consume(b, 6)) != 0 || + (r = sshbuf_get_cstring(b, NULL, NULL)) != 0 || /* namespace */ + (r = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || /* reserved */ + (r = sshbuf_get_cstring(b, NULL, NULL)) != 0 || /* hashalg */ + (r = sshbuf_get_string_direct(b, NULL, NULL)) != 0) /* H(msg) */ + goto out; + if (sshbuf_len(b) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +/* + * This function inspects a message to be signed by a FIDO key that has a + * web-like application string (i.e. one that does not begin with "ssh:". + * It checks that the message is one of those expected for SSH operations + * (pubkey userauth, sshsig, CA key signing) to exclude signing challenges + * for the web. + */ +static int +check_websafe_message_contents(struct sshkey *key, struct sshbuf *data) +{ + if (parse_userauth_request(data, key, NULL, NULL, NULL) == 0) { + debug_f("signed data matches public key userauth request"); + return 1; + } + if (parse_sshsig_request(data) == 0) { + debug_f("signed data matches SSHSIG signature request"); + return 1; + } + + /* XXX check CA signature operation */ + + error("web-origin key attempting to sign non-SSH message"); + return 0; +} + +static int +buf_equal(const struct sshbuf *a, const struct sshbuf *b) +{ + if (sshbuf_ptr(a) == NULL || sshbuf_ptr(b) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (sshbuf_len(a) != sshbuf_len(b)) + return SSH_ERR_INVALID_FORMAT; + if (timingsafe_bcmp(sshbuf_ptr(a), sshbuf_ptr(b), sshbuf_len(a)) != 0) + return SSH_ERR_INVALID_FORMAT; + return 0; +} + +/* ssh2 only */ +static void +process_sign_request2(SocketEntry *e) +{ + u_char *signature = NULL; + size_t slen = 0; + u_int compat = 0, flags; + int r, ok = -1, retried = 0; + char *fp = NULL, *pin = NULL, *prompt = NULL; + char *user = NULL, *sig_dest = NULL; + const char *fwd_host = NULL, *dest_host = NULL; + struct sshbuf *msg = NULL, *data = NULL, *sid = NULL; + struct sshkey *key = NULL, *hostkey = NULL; + struct identity *id; + struct notifier_ctx *notifier = NULL; + + debug_f("entering"); + + if ((msg = sshbuf_new()) == NULL || (data = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshkey_froms(e->request, &key)) != 0 || + (r = sshbuf_get_stringb(e->request, data)) != 0 || + (r = sshbuf_get_u32(e->request, &flags)) != 0) { + error_fr(r, "parse"); + goto send; + } + + if ((id = lookup_identity(key)) == NULL) { + verbose_f("%s key not found", sshkey_type(key)); + goto send; + } + if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + + if (id->ndest_constraints != 0) { + if (e->nsession_ids == 0) { + logit_f("refusing use of destination-constrained key " + "to sign on unbound connection"); + goto send; + } + if (parse_userauth_request(data, key, &user, &sid, + &hostkey) != 0) { + logit_f("refusing use of destination-constrained key " + "to sign an unidentified signature"); + goto send; + } + /* XXX logspam */ + debug_f("user=%s", user); + if (identity_permitted(id, e, user, &fwd_host, &dest_host) != 0) + goto send; + /* XXX display fwd_host/dest_host in askpass UI */ + /* + * Ensure that the session ID is the most recent one + * registered on the socket - it should have been bound by + * ssh immediately before userauth. + */ + if (buf_equal(sid, + e->session_ids[e->nsession_ids - 1].sid) != 0) { + error_f("unexpected session ID (%zu listed) on " + "signature request for target user %s with " + "key %s %s", e->nsession_ids, user, + sshkey_type(id->key), fp); + goto send; + } + /* + * Ensure that the hostkey embedded in the signature matches + * the one most recently bound to the socket. An exception is + * made for the initial forwarding hop. + */ + if (e->nsession_ids > 1 && hostkey == NULL) { + error_f("refusing use of destination-constrained key: " + "no hostkey recorded in signature for forwarded " + "connection"); + goto send; + } + if (hostkey != NULL && !sshkey_equal(hostkey, + e->session_ids[e->nsession_ids - 1].key)) { + error_f("refusing use of destination-constrained key: " + "mismatch between hostkey in request and most " + "recently bound session"); + goto send; + } + xasprintf(&sig_dest, "public key authentication request for " + "user \"%s\" to listed host", user); + } + if (id->confirm && confirm_key(id, sig_dest) != 0) { + verbose_f("user refused key"); + goto send; + } + if (sshkey_is_sk(id->key)) { + if (strncmp(id->key->sk_application, "ssh:", 4) != 0 && + !check_websafe_message_contents(key, data)) { + /* error already logged */ + goto send; + } + if ((id->key->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { + /* XXX include sig_dest */ + xasprintf(&prompt, "Enter PIN%sfor %s key %s: ", + (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD) ? + " and confirm user presence " : " ", + sshkey_type(id->key), fp); + pin = read_passphrase(prompt, RP_USE_ASKPASS); + free(prompt); + prompt = NULL; + } else if ((id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { + notifier = notify_start(0, + "Confirm user presence for key %s %s%s%s", + sshkey_type(id->key), fp, + sig_dest == NULL ? "" : "\n", + sig_dest == NULL ? "" : sig_dest); + } + } + retry_pin: + if ((r = sshkey_sign(id->key, &signature, &slen, + sshbuf_ptr(data), sshbuf_len(data), agent_decode_alg(key, flags), + id->sk_provider, pin, compat)) != 0) { + debug_fr(r, "sshkey_sign"); + if (pin == NULL && !retried && sshkey_is_sk(id->key) && + r == SSH_ERR_KEY_WRONG_PASSPHRASE) { + if (notifier) { + notify_complete(notifier, NULL); + notifier = NULL; + } + /* XXX include sig_dest */ + xasprintf(&prompt, "Enter PIN%sfor %s key %s: ", + (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD) ? + " and confirm user presence " : " ", + sshkey_type(id->key), fp); + pin = read_passphrase(prompt, RP_USE_ASKPASS); + retried = 1; + goto retry_pin; + } + error_fr(r, "sshkey_sign"); + goto send; + } + /* Success */ + ok = 0; + send: + notify_complete(notifier, "User presence confirmed"); + + if (ok == 0) { + if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || + (r = sshbuf_put_string(msg, signature, slen)) != 0) + fatal_fr(r, "compose"); + } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) + fatal_fr(r, "compose failure"); + + if ((r = sshbuf_put_stringb(e->output, msg)) != 0) + fatal_fr(r, "enqueue"); + + sshbuf_free(sid); + sshbuf_free(data); + sshbuf_free(msg); + sshkey_free(key); + sshkey_free(hostkey); + free(fp); + free(signature); + free(sig_dest); + free(user); + free(prompt); + if (pin != NULL) + freezero(pin, strlen(pin)); +} + +/* shared */ +static void +process_remove_identity(SocketEntry *e) +{ + int r, success = 0; + struct sshkey *key = NULL; + Identity *id; + + debug2_f("entering"); + if ((r = sshkey_froms(e->request, &key)) != 0) { + error_fr(r, "parse key"); + goto done; + } + if ((id = lookup_identity(key)) == NULL) { + debug_f("key not found"); + goto done; + } + /* identity not visible, cannot be removed */ + if (identity_permitted(id, e, NULL, NULL, NULL) != 0) + goto done; /* error already logged */ + /* We have this key, free it. */ + if (idtab->nentries < 1) + fatal_f("internal error: nentries %d", idtab->nentries); + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + idtab->nentries--; + success = 1; + done: + sshkey_free(key); + send_status(e, success); +} + +static void +process_remove_all_identities(SocketEntry *e) +{ + Identity *id; + + debug2_f("entering"); + /* Loop over all identities and clear the keys. */ + for (id = TAILQ_FIRST(&idtab->idlist); id; + id = TAILQ_FIRST(&idtab->idlist)) { + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + } + + /* Mark that there are no identities. */ + idtab->nentries = 0; + + /* Send success. */ + send_status(e, 1); +} + +/* removes expired keys and returns number of seconds until the next expiry */ +static time_t +reaper(void) +{ + time_t deadline = 0, now = monotime(); + Identity *id, *nxt; + + for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + if (id->death == 0) + continue; + if (now >= id->death) { + debug("expiring key '%s'", id->comment); + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + idtab->nentries--; + } else + deadline = (deadline == 0) ? id->death : + MINIMUM(deadline, id->death); + } + if (deadline == 0 || deadline <= now) + return 0; + else + return (deadline - now); +} + +static int +parse_dest_constraint_hop(struct sshbuf *b, struct dest_constraint_hop *dch) +{ + u_char key_is_ca; + size_t elen = 0; + int r; + struct sshkey *k = NULL; + char *fp; + + memset(dch, '\0', sizeof(*dch)); + if ((r = sshbuf_get_cstring(b, &dch->user, NULL)) != 0 || + (r = sshbuf_get_cstring(b, &dch->hostname, NULL)) != 0 || + (r = sshbuf_get_string_direct(b, NULL, &elen)) != 0) { + error_fr(r, "parse"); + goto out; + } + if (elen != 0) { + error_f("unsupported extensions (len %zu)", elen); + r = SSH_ERR_FEATURE_UNSUPPORTED; + goto out; + } + if (*dch->hostname == '\0') { + free(dch->hostname); + dch->hostname = NULL; + } + if (*dch->user == '\0') { + free(dch->user); + dch->user = NULL; + } + while (sshbuf_len(b) != 0) { + dch->keys = xrecallocarray(dch->keys, dch->nkeys, + dch->nkeys + 1, sizeof(*dch->keys)); + dch->key_is_ca = xrecallocarray(dch->key_is_ca, dch->nkeys, + dch->nkeys + 1, sizeof(*dch->key_is_ca)); + if ((r = sshkey_froms(b, &k)) != 0 || + (r = sshbuf_get_u8(b, &key_is_ca)) != 0) + goto out; + if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + debug3_f("%s%s%s: adding %skey %s %s", + dch->user == NULL ? "" : dch->user, + dch->user == NULL ? "" : "@", + dch->hostname, key_is_ca ? "CA " : "", sshkey_type(k), fp); + free(fp); + dch->keys[dch->nkeys] = k; + dch->key_is_ca[dch->nkeys] = key_is_ca != 0; + dch->nkeys++; + k = NULL; /* transferred */ + } + /* success */ + r = 0; + out: + sshkey_free(k); + return r; +} + +static int +parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc) +{ + struct sshbuf *b = NULL, *frombuf = NULL, *tobuf = NULL; + int r; + size_t elen = 0; + + debug3_f("entering"); + + memset(dc, '\0', sizeof(*dc)); + if ((r = sshbuf_froms(m, &b)) != 0 || + (r = sshbuf_froms(b, &frombuf)) != 0 || + (r = sshbuf_froms(b, &tobuf)) != 0 || + (r = sshbuf_get_string_direct(b, NULL, &elen)) != 0) { + error_fr(r, "parse"); + goto out; + } + if ((r = parse_dest_constraint_hop(frombuf, &dc->from) != 0) || + (r = parse_dest_constraint_hop(tobuf, &dc->to) != 0)) + goto out; /* already logged */ + if (elen != 0) { + error_f("unsupported extensions (len %zu)", elen); + r = SSH_ERR_FEATURE_UNSUPPORTED; + goto out; + } + debug2_f("parsed %s (%u keys) > %s%s%s (%u keys)", + dc->from.hostname ? dc->from.hostname : "(ORIGIN)", dc->from.nkeys, + dc->to.user ? dc->to.user : "", dc->to.user ? "@" : "", + dc->to.hostname ? dc->to.hostname : "(ANY)", dc->to.nkeys); + /* check consistency */ + if ((dc->from.hostname == NULL) != (dc->from.nkeys == 0) || + dc->from.user != NULL) { + error_f("inconsistent \"from\" specification"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (dc->to.hostname == NULL || dc->to.nkeys == 0) { + error_f("incomplete \"to\" specification"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + r = 0; + out: + sshbuf_free(b); + sshbuf_free(frombuf); + sshbuf_free(tobuf); + return r; +} + +static int +parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp, + struct dest_constraint **dcsp, size_t *ndcsp) +{ + char *ext_name = NULL; + int r; + struct sshbuf *b = NULL; + + if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) { + error_fr(r, "parse constraint extension"); + goto out; + } + debug_f("constraint ext %s", ext_name); + if (strcmp(ext_name, "sk-provider@openssh.com") == 0) { + if (sk_providerp == NULL) { + error_f("%s not valid here", ext_name); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (*sk_providerp != NULL) { + error_f("%s already set", ext_name); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_cstring(m, sk_providerp, NULL)) != 0) { + error_fr(r, "parse %s", ext_name); + goto out; + } + } else if (strcmp(ext_name, + "restrict-destination-v00@openssh.com") == 0) { + if (*dcsp != NULL) { + error_f("%s already set", ext_name); + goto out; + } + if ((r = sshbuf_froms(m, &b)) != 0) { + error_fr(r, "parse %s outer", ext_name); + goto out; + } + while (sshbuf_len(b) != 0) { + if (*ndcsp >= AGENT_MAX_DEST_CONSTRAINTS) { + error_f("too many %s constraints", ext_name); + goto out; + } + *dcsp = xrecallocarray(*dcsp, *ndcsp, *ndcsp + 1, + sizeof(**dcsp)); + if ((r = parse_dest_constraint(b, + *dcsp + (*ndcsp)++)) != 0) + goto out; /* error already logged */ + } + } else { + error_f("unsupported constraint \"%s\"", ext_name); + r = SSH_ERR_FEATURE_UNSUPPORTED; + goto out; + } + /* success */ + r = 0; + out: + free(ext_name); + sshbuf_free(b); + return r; +} + +static int +parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp, + u_int *secondsp, int *confirmp, char **sk_providerp, + struct dest_constraint **dcsp, size_t *ndcsp) +{ + u_char ctype; + int r; + u_int seconds, maxsign = 0; + + while (sshbuf_len(m)) { + if ((r = sshbuf_get_u8(m, &ctype)) != 0) { + error_fr(r, "parse constraint type"); + goto out; + } + switch (ctype) { + case SSH_AGENT_CONSTRAIN_LIFETIME: + if (*deathp != 0) { + error_f("lifetime already set"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_u32(m, &seconds)) != 0) { + error_fr(r, "parse lifetime constraint"); + goto out; + } + *deathp = monotime() + seconds; + *secondsp = seconds; + break; + case SSH_AGENT_CONSTRAIN_CONFIRM: + if (*confirmp != 0) { + error_f("confirm already set"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + *confirmp = 1; + break; + case SSH_AGENT_CONSTRAIN_MAXSIGN: + if (k == NULL) { + error_f("maxsign not valid here"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (maxsign != 0) { + error_f("maxsign already set"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_u32(m, &maxsign)) != 0) { + error_fr(r, "parse maxsign constraint"); + goto out; + } + if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) { + error_fr(r, "enable maxsign"); + goto out; + } + break; + case SSH_AGENT_CONSTRAIN_EXTENSION: + if ((r = parse_key_constraint_extension(m, + sk_providerp, dcsp, ndcsp)) != 0) + goto out; /* error already logged */ + break; + default: + error_f("Unknown constraint %d", ctype); + r = SSH_ERR_FEATURE_UNSUPPORTED; + goto out; + } + } + /* success */ + r = 0; + out: + return r; +} + +static void +process_add_identity(SocketEntry *e) +{ + Identity *id; + int success = 0, confirm = 0; + char *fp, *comment = NULL, *sk_provider = NULL; + char canonical_provider[PATH_MAX]; + time_t death = 0; + u_int seconds = 0; + struct dest_constraint *dest_constraints = NULL; + size_t ndest_constraints = 0; + struct sshkey *k = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + debug2_f("entering"); + if ((r = sshkey_private_deserialize(e->request, &k)) != 0 || + k == NULL || + (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) { + error_fr(r, "parse"); + goto out; + } + if (parse_key_constraints(e->request, k, &death, &seconds, &confirm, + &sk_provider, &dest_constraints, &ndest_constraints) != 0) { + error_f("failed to parse constraints"); + sshbuf_reset(e->request); + goto out; + } + + if (sk_provider != NULL) { + if (!sshkey_is_sk(k)) { + error("Cannot add provider: %s is not an " + "authenticator-hosted key", sshkey_type(k)); + goto out; + } + if (strcasecmp(sk_provider, "internal") == 0) { + debug_f("internal provider"); + } else { + if (realpath(sk_provider, canonical_provider) == NULL) { + verbose("failed provider \"%.100s\": " + "realpath: %s", sk_provider, + strerror(errno)); + goto out; + } + free(sk_provider); + sk_provider = xstrdup(canonical_provider); + if (match_pattern_list(sk_provider, + allowed_providers, 0) != 1) { + error("Refusing add key: " + "provider %s not allowed", sk_provider); + goto out; + } + } + } + if ((r = sshkey_shield_private(k)) != 0) { + error_fr(r, "shield private"); + goto out; + } + if (lifetime && !death) + death = monotime() + lifetime; + if ((id = lookup_identity(k)) == NULL) { + id = xcalloc(1, sizeof(Identity)); + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); + /* Increment the number of identities. */ + idtab->nentries++; + } else { + /* identity not visible, do not update */ + if (identity_permitted(id, e, NULL, NULL, NULL) != 0) + goto out; /* error already logged */ + /* key state might have been updated */ + sshkey_free(id->key); + free(id->comment); + free(id->sk_provider); + free_dest_constraints(id->dest_constraints, + id->ndest_constraints); + } + /* success */ + id->key = k; + id->comment = comment; + id->death = death; + id->confirm = confirm; + id->sk_provider = sk_provider; + id->dest_constraints = dest_constraints; + id->ndest_constraints = ndest_constraints; + + if ((fp = sshkey_fingerprint(k, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + debug_f("add %s %s \"%.100s\" (life: %u) (confirm: %u) " + "(provider: %s) (destination constraints: %zu)", + sshkey_ssh_name(k), fp, comment, seconds, confirm, + sk_provider == NULL ? "none" : sk_provider, ndest_constraints); + free(fp); + /* transferred */ + k = NULL; + comment = NULL; + sk_provider = NULL; + dest_constraints = NULL; + ndest_constraints = 0; + success = 1; + out: + free(sk_provider); + free(comment); + sshkey_free(k); + free_dest_constraints(dest_constraints, ndest_constraints); + send_status(e, success); +} + +/* XXX todo: encrypt sensitive data with passphrase */ +static void +process_lock_agent(SocketEntry *e, int lock) +{ + int r, success = 0, delay; + char *passwd; + u_char passwdhash[LOCK_SIZE]; + static u_int fail_count = 0; + size_t pwlen; + + debug2_f("entering"); + /* + * This is deliberately fatal: the user has requested that we lock, + * but we can't parse their request properly. The only safe thing to + * do is abort. + */ + if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0) + fatal_fr(r, "parse"); + if (pwlen == 0) { + debug("empty password not supported"); + } else if (locked && !lock) { + if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), + passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0) + fatal("bcrypt_pbkdf"); + if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE) == 0) { + debug("agent unlocked"); + locked = 0; + fail_count = 0; + explicit_bzero(lock_pwhash, sizeof(lock_pwhash)); + success = 1; + } else { + /* delay in 0.1s increments up to 10s */ + if (fail_count < 100) + fail_count++; + delay = 100000 * fail_count; + debug("unlock failed, delaying %0.1lf seconds", + (double)delay/1000000); + usleep(delay); + } + explicit_bzero(passwdhash, sizeof(passwdhash)); + } else if (!locked && lock) { + debug("agent locked"); + locked = 1; + arc4random_buf(lock_salt, sizeof(lock_salt)); + if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), + lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS) < 0) + fatal("bcrypt_pbkdf"); + success = 1; + } + freezero(passwd, pwlen); + send_status(e, success); +} + +static void +no_identities(SocketEntry *e) +{ + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || + (r = sshbuf_put_u32(msg, 0)) != 0 || + (r = sshbuf_put_stringb(e->output, msg)) != 0) + fatal_fr(r, "compose"); + sshbuf_free(msg); +} + +#ifdef ENABLE_PKCS11 +static void +process_add_smartcard_key(SocketEntry *e) +{ + char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; + char **comments = NULL; + int r, i, count = 0, success = 0, confirm = 0; + u_int seconds = 0; + time_t death = 0; + struct sshkey **keys = NULL, *k; + Identity *id; + struct dest_constraint *dest_constraints = NULL; + size_t ndest_constraints = 0; + + debug2_f("entering"); + if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) { + error_fr(r, "parse"); + goto send; + } + if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm, + NULL, &dest_constraints, &ndest_constraints) != 0) { + error_f("failed to parse constraints"); + goto send; + } + if (realpath(provider, canonical_provider) == NULL) { + verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", + provider, strerror(errno)); + goto send; + } + if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) { + verbose("refusing PKCS#11 add of \"%.100s\": " + "provider not allowed", canonical_provider); + goto send; + } + debug_f("add %.100s", canonical_provider); + if (lifetime && !death) + death = monotime() + lifetime; + + count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments); + for (i = 0; i < count; i++) { + k = keys[i]; + if (lookup_identity(k) == NULL) { + id = xcalloc(1, sizeof(Identity)); + id->key = k; + keys[i] = NULL; /* transferred */ + id->provider = xstrdup(canonical_provider); + if (*comments[i] != '\0') { + id->comment = comments[i]; + comments[i] = NULL; /* transferred */ + } else { + id->comment = xstrdup(canonical_provider); + } + id->death = death; + id->confirm = confirm; + id->dest_constraints = dest_constraints; + id->ndest_constraints = ndest_constraints; + dest_constraints = NULL; /* transferred */ + ndest_constraints = 0; + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); + idtab->nentries++; + success = 1; + } + /* XXX update constraints for existing keys */ + sshkey_free(keys[i]); + free(comments[i]); + } +send: + free(pin); + free(provider); + free(keys); + free(comments); + free_dest_constraints(dest_constraints, ndest_constraints); + send_status(e, success); +} + +static void +process_remove_smartcard_key(SocketEntry *e) +{ + char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; + int r, success = 0; + Identity *id, *nxt; + + debug2_f("entering"); + if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) { + error_fr(r, "parse"); + goto send; + } + free(pin); + + if (realpath(provider, canonical_provider) == NULL) { + verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", + provider, strerror(errno)); + goto send; + } + + debug_f("remove %.100s", canonical_provider); + for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + /* Skip file--based keys */ + if (id->provider == NULL) + continue; + if (!strcmp(canonical_provider, id->provider)) { + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + idtab->nentries--; + } + } + if (pkcs11_del_provider(canonical_provider) == 0) + success = 1; + else + error_f("pkcs11_del_provider failed"); +send: + free(provider); + send_status(e, success); +} +#endif /* ENABLE_PKCS11 */ + +static int +process_ext_session_bind(SocketEntry *e) +{ + int r, sid_match, key_match; + struct sshkey *key = NULL; + struct sshbuf *sid = NULL, *sig = NULL; + char *fp = NULL; + size_t i; + u_char fwd = 0; + + debug2_f("entering"); + if ((r = sshkey_froms(e->request, &key)) != 0 || + (r = sshbuf_froms(e->request, &sid)) != 0 || + (r = sshbuf_froms(e->request, &sig)) != 0 || + (r = sshbuf_get_u8(e->request, &fwd)) != 0) { + error_fr(r, "parse"); + goto out; + } + if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + /* check signature with hostkey on session ID */ + if ((r = sshkey_verify(key, sshbuf_ptr(sig), sshbuf_len(sig), + sshbuf_ptr(sid), sshbuf_len(sid), NULL, 0, NULL)) != 0) { + error_fr(r, "sshkey_verify for %s %s", sshkey_type(key), fp); + goto out; + } + /* check whether sid/key already recorded */ + for (i = 0; i < e->nsession_ids; i++) { + if (!e->session_ids[i].forwarded) { + error_f("attempt to bind session ID to socket " + "previously bound for authentication attempt"); + r = -1; + goto out; + } + sid_match = buf_equal(sid, e->session_ids[i].sid) == 0; + key_match = sshkey_equal(key, e->session_ids[i].key); + if (sid_match && key_match) { + debug_f("session ID already recorded for %s %s", + sshkey_type(key), fp); + r = 0; + goto out; + } else if (sid_match) { + error_f("session ID recorded against different key " + "for %s %s", sshkey_type(key), fp); + r = -1; + goto out; + } + /* + * new sid with previously-seen key can happen, e.g. multiple + * connections to the same host. + */ + } + /* record new key/sid */ + if (e->nsession_ids >= AGENT_MAX_SESSION_IDS) { + error_f("too many session IDs recorded"); + goto out; + } + e->session_ids = xrecallocarray(e->session_ids, e->nsession_ids, + e->nsession_ids + 1, sizeof(*e->session_ids)); + i = e->nsession_ids++; + debug_f("recorded %s %s (slot %zu of %d)", sshkey_type(key), fp, i, + AGENT_MAX_SESSION_IDS); + e->session_ids[i].key = key; + e->session_ids[i].forwarded = fwd != 0; + key = NULL; /* transferred */ + /* can't transfer sid; it's refcounted and scoped to request's life */ + if ((e->session_ids[i].sid = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_putb(e->session_ids[i].sid, sid)) != 0) + fatal_fr(r, "sshbuf_putb session ID"); + /* success */ + r = 0; + out: + sshkey_free(key); + sshbuf_free(sid); + sshbuf_free(sig); + return r == 0 ? 1 : 0; +} + +static void +process_extension(SocketEntry *e) +{ + int r, success = 0; + char *name; + + debug2_f("entering"); + if ((r = sshbuf_get_cstring(e->request, &name, NULL)) != 0) { + error_fr(r, "parse"); + goto send; + } + if (strcmp(name, "session-bind@openssh.com") == 0) + success = process_ext_session_bind(e); + else + debug_f("unsupported extension \"%s\"", name); + free(name); +send: + send_status(e, success); +} +/* + * dispatch incoming message. + * returns 1 on success, 0 for incomplete messages or -1 on error. + */ +static int +process_message(u_int socknum) +{ + u_int msg_len; + u_char type; + const u_char *cp; + int r; + SocketEntry *e; + + if (socknum >= sockets_alloc) + fatal_f("sock %u >= allocated %u", socknum, sockets_alloc); + e = &sockets[socknum]; + + if (sshbuf_len(e->input) < 5) + return 0; /* Incomplete message header. */ + cp = sshbuf_ptr(e->input); + msg_len = PEEK_U32(cp); + if (msg_len > AGENT_MAX_LEN) { + debug_f("socket %u (fd=%d) message too long %u > %u", + socknum, e->fd, msg_len, AGENT_MAX_LEN); + return -1; + } + if (sshbuf_len(e->input) < msg_len + 4) + return 0; /* Incomplete message body. */ + + /* move the current input to e->request */ + sshbuf_reset(e->request); + if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || + (r = sshbuf_get_u8(e->request, &type)) != 0) { + if (r == SSH_ERR_MESSAGE_INCOMPLETE || + r == SSH_ERR_STRING_TOO_LARGE) { + error_fr(r, "parse"); + return -1; + } + fatal_fr(r, "parse"); + } + + debug_f("socket %u (fd=%d) type %d", socknum, e->fd, type); + + /* check whether agent is locked */ + if (locked && type != SSH_AGENTC_UNLOCK) { + sshbuf_reset(e->request); + switch (type) { + case SSH2_AGENTC_REQUEST_IDENTITIES: + /* send empty lists */ + no_identities(e); + break; + default: + /* send a fail message for all other request types */ + send_status(e, 0); + } + return 1; + } + + switch (type) { + case SSH_AGENTC_LOCK: + case SSH_AGENTC_UNLOCK: + process_lock_agent(e, type == SSH_AGENTC_LOCK); + break; + case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: + process_remove_all_identities(e); /* safe for !WITH_SSH1 */ + break; + /* ssh2 */ + case SSH2_AGENTC_SIGN_REQUEST: + process_sign_request2(e); + break; + case SSH2_AGENTC_REQUEST_IDENTITIES: + process_request_identities(e); + break; + case SSH2_AGENTC_ADD_IDENTITY: + case SSH2_AGENTC_ADD_ID_CONSTRAINED: + process_add_identity(e); + break; + case SSH2_AGENTC_REMOVE_IDENTITY: + process_remove_identity(e); + break; + case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: + process_remove_all_identities(e); + break; +#ifdef ENABLE_PKCS11 + case SSH_AGENTC_ADD_SMARTCARD_KEY: + case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: + process_add_smartcard_key(e); + break; + case SSH_AGENTC_REMOVE_SMARTCARD_KEY: + process_remove_smartcard_key(e); + break; +#endif /* ENABLE_PKCS11 */ + case SSH_AGENTC_EXTENSION: + process_extension(e); + break; + default: + /* Unknown message. Respond with failure. */ + error("Unknown message %d", type); + sshbuf_reset(e->request); + send_status(e, 0); + break; + } + return 1; +} + +static void +new_socket(sock_type type, int fd) +{ + u_int i, old_alloc, new_alloc; + + debug_f("type = %s", type == AUTH_CONNECTION ? "CONNECTION" : + (type == AUTH_SOCKET ? "SOCKET" : "UNKNOWN")); + set_nonblock(fd); + + if (fd > max_fd) + max_fd = fd; + + for (i = 0; i < sockets_alloc; i++) + if (sockets[i].type == AUTH_UNUSED) { + sockets[i].fd = fd; + if ((sockets[i].input = sshbuf_new()) == NULL || + (sockets[i].output = sshbuf_new()) == NULL || + (sockets[i].request = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + sockets[i].type = type; + return; + } + old_alloc = sockets_alloc; + new_alloc = sockets_alloc + 10; + sockets = xrecallocarray(sockets, old_alloc, new_alloc, + sizeof(sockets[0])); + for (i = old_alloc; i < new_alloc; i++) + sockets[i].type = AUTH_UNUSED; + sockets_alloc = new_alloc; + sockets[old_alloc].fd = fd; + if ((sockets[old_alloc].input = sshbuf_new()) == NULL || + (sockets[old_alloc].output = sshbuf_new()) == NULL || + (sockets[old_alloc].request = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + sockets[old_alloc].type = type; +} + +static int +handle_socket_read(u_int socknum) +{ + struct sockaddr_un sunaddr; + socklen_t slen; + uid_t euid; + gid_t egid; + int fd; + + slen = sizeof(sunaddr); + fd = accept(sockets[socknum].fd, (struct sockaddr *)&sunaddr, &slen); + if (fd == -1) { + error("accept from AUTH_SOCKET: %s", strerror(errno)); + return -1; + } + if (getpeereid(fd, &euid, &egid) == -1) { + error("getpeereid %d failed: %s", fd, strerror(errno)); + close(fd); + return -1; + } + if ((euid != 0) && (getuid() != euid)) { + error("uid mismatch: peer euid %u != uid %u", + (u_int) euid, (u_int) getuid()); + close(fd); + return -1; + } + new_socket(AUTH_CONNECTION, fd); + return 0; +} + +static int +handle_conn_read(u_int socknum) +{ + char buf[AGENT_RBUF_LEN]; + ssize_t len; + int r; + + if ((len = read(sockets[socknum].fd, buf, sizeof(buf))) <= 0) { + if (len == -1) { + if (errno == EAGAIN || errno == EINTR) + return 0; + error_f("read error on socket %u (fd %d): %s", + socknum, sockets[socknum].fd, strerror(errno)); + } + return -1; + } + if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0) + fatal_fr(r, "compose"); + explicit_bzero(buf, sizeof(buf)); + for (;;) { + if ((r = process_message(socknum)) == -1) + return -1; + else if (r == 0) + break; + } + return 0; +} + +static int +handle_conn_write(u_int socknum) +{ + ssize_t len; + int r; + + if (sshbuf_len(sockets[socknum].output) == 0) + return 0; /* shouldn't happen */ + if ((len = write(sockets[socknum].fd, + sshbuf_ptr(sockets[socknum].output), + sshbuf_len(sockets[socknum].output))) <= 0) { + if (len == -1) { + if (errno == EAGAIN || errno == EINTR) + return 0; + error_f("read error on socket %u (fd %d): %s", + socknum, sockets[socknum].fd, strerror(errno)); + } + return -1; + } + if ((r = sshbuf_consume(sockets[socknum].output, len)) != 0) + fatal_fr(r, "consume"); + return 0; +} + +static void +after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds) +{ + size_t i; + u_int socknum, activefds = npfd; + + for (i = 0; i < npfd; i++) { + if (pfd[i].revents == 0) + continue; + /* Find sockets entry */ + for (socknum = 0; socknum < sockets_alloc; socknum++) { + if (sockets[socknum].type != AUTH_SOCKET && + sockets[socknum].type != AUTH_CONNECTION) + continue; + if (pfd[i].fd == sockets[socknum].fd) + break; + } + if (socknum >= sockets_alloc) { + error_f("no socket for fd %d", pfd[i].fd); + continue; + } + /* Process events */ + switch (sockets[socknum].type) { + case AUTH_SOCKET: + if ((pfd[i].revents & (POLLIN|POLLERR)) == 0) + break; + if (npfd > maxfds) { + debug3("out of fds (active %u >= limit %u); " + "skipping accept", activefds, maxfds); + break; + } + if (handle_socket_read(socknum) == 0) + activefds++; + break; + case AUTH_CONNECTION: + if ((pfd[i].revents & (POLLIN|POLLHUP|POLLERR)) != 0 && + handle_conn_read(socknum) != 0) + goto close_sock; + if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 && + handle_conn_write(socknum) != 0) { + close_sock: + if (activefds == 0) + fatal("activefds == 0 at close_sock"); + close_socket(&sockets[socknum]); + activefds--; + break; + } + break; + default: + break; + } + } +} + +static int +prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds) +{ + struct pollfd *pfd = *pfdp; + size_t i, j, npfd = 0; + time_t deadline; + int r; + + /* Count active sockets */ + for (i = 0; i < sockets_alloc; i++) { + switch (sockets[i].type) { + case AUTH_SOCKET: + case AUTH_CONNECTION: + npfd++; + break; + case AUTH_UNUSED: + break; + default: + fatal("Unknown socket type %d", sockets[i].type); + break; + } + } + if (npfd != *npfdp && + (pfd = recallocarray(pfd, *npfdp, npfd, sizeof(*pfd))) == NULL) + fatal_f("recallocarray failed"); + *pfdp = pfd; + *npfdp = npfd; + + for (i = j = 0; i < sockets_alloc; i++) { + switch (sockets[i].type) { + case AUTH_SOCKET: + if (npfd > maxfds) { + debug3("out of fds (active %zu >= limit %u); " + "skipping arming listener", npfd, maxfds); + break; + } + pfd[j].fd = sockets[i].fd; + pfd[j].revents = 0; + pfd[j].events = POLLIN; + j++; + break; + case AUTH_CONNECTION: + pfd[j].fd = sockets[i].fd; + pfd[j].revents = 0; + /* + * Only prepare to read if we can handle a full-size + * input read buffer and enqueue a max size reply.. + */ + if ((r = sshbuf_check_reserve(sockets[i].input, + AGENT_RBUF_LEN)) == 0 && + (r = sshbuf_check_reserve(sockets[i].output, + AGENT_MAX_LEN)) == 0) + pfd[j].events = POLLIN; + else if (r != SSH_ERR_NO_BUFFER_SPACE) + fatal_fr(r, "reserve"); + if (sshbuf_len(sockets[i].output) > 0) + pfd[j].events |= POLLOUT; + j++; + break; + default: + break; + } + } + deadline = reaper(); + if (parent_alive_interval != 0) + deadline = (deadline == 0) ? parent_alive_interval : + MINIMUM(deadline, parent_alive_interval); + if (deadline == 0) { + *timeoutp = -1; /* INFTIM */ + } else { + if (deadline > INT_MAX / 1000) + *timeoutp = INT_MAX / 1000; + else + *timeoutp = deadline * 1000; + } + return (1); +} + +static void +cleanup_socket(void) +{ + if (cleanup_pid != 0 && getpid() != cleanup_pid) + return; + debug_f("cleanup"); + if (socket_name[0]) + unlink(socket_name); + if (socket_dir[0]) + rmdir(socket_dir); +} + +void +cleanup_exit(int i) +{ + cleanup_socket(); + _exit(i); +} + +/*ARGSUSED*/ +static void +cleanup_handler(int sig) +{ + cleanup_socket(); +#ifdef ENABLE_PKCS11 + pkcs11_terminate(); +#endif + _exit(2); +} + +static void +check_parent_exists(void) +{ + /* + * If our parent has exited then getppid() will return (pid_t)1, + * so testing for that should be safe. + */ + if (parent_pid != -1 && getppid() != parent_pid) { + /* printf("Parent has died - Authentication agent exiting.\n"); */ + cleanup_socket(); + _exit(2); + } +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" + " [-P allowed_providers] [-t life]\n" + " ssh-agent [-a bind_address] [-E fingerprint_hash] [-P allowed_providers]\n" + " [-t life] command [arg ...]\n" + " ssh-agent [-c | -s] -k\n"); + exit(1); +} + +int +main(int ac, char **av) +{ + int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; + int sock, ch, result, saved_errno; + char *shell, *format, *pidstr, *agentsocket = NULL; +#ifdef HAVE_SETRLIMIT + struct rlimit rlim; +#endif + extern int optind; + extern char *optarg; + pid_t pid; + char pidstrbuf[1 + 3 * sizeof pid]; + size_t len; + mode_t prev_mask; + int timeout = -1; /* INFTIM */ + struct pollfd *pfd = NULL; + size_t npfd = 0; + u_int maxfds; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + /* drop */ + setegid(getgid()); + setgid(getgid()); + + platform_disable_tracing(0); /* strict=no */ + +#ifdef RLIMIT_NOFILE + if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) + fatal("%s: getrlimit: %s", __progname, strerror(errno)); +#endif + + __progname = ssh_get_progname(av[0]); + seed_rng(); + + while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) { + switch (ch) { + case 'E': + fingerprint_hash = ssh_digest_alg_by_name(optarg); + if (fingerprint_hash == -1) + fatal("Invalid hash algorithm \"%s\"", optarg); + break; + case 'c': + if (s_flag) + usage(); + c_flag++; + break; + case 'k': + k_flag++; + break; + case 'O': + if (strcmp(optarg, "no-restrict-websafe") == 0) + restrict_websafe = 0; + else + fatal("Unknown -O option"); + break; + case 'P': + if (allowed_providers != NULL) + fatal("-P option already specified"); + allowed_providers = xstrdup(optarg); + break; + case 's': + if (c_flag) + usage(); + s_flag++; + break; + case 'd': + if (d_flag || D_flag) + usage(); + d_flag++; + break; + case 'D': + if (d_flag || D_flag) + usage(); + D_flag++; + break; + case 'a': + agentsocket = optarg; + break; + case 't': + if ((lifetime = convtime(optarg)) == -1) { + fprintf(stderr, "Invalid lifetime\n"); + usage(); + } + break; + default: + usage(); + } + } + ac -= optind; + av += optind; + + if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) + usage(); + + if (allowed_providers == NULL) + allowed_providers = xstrdup(DEFAULT_ALLOWED_PROVIDERS); + + if (ac == 0 && !c_flag && !s_flag) { + shell = getenv("SHELL"); + if (shell != NULL && (len = strlen(shell)) > 2 && + strncmp(shell + len - 3, "csh", 3) == 0) + c_flag = 1; + } + if (k_flag) { + const char *errstr = NULL; + + pidstr = getenv(SSH_AGENTPID_ENV_NAME); + if (pidstr == NULL) { + fprintf(stderr, "%s not set, cannot kill agent\n", + SSH_AGENTPID_ENV_NAME); + exit(1); + } + pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); + if (errstr) { + fprintf(stderr, + "%s=\"%s\", which is not a good PID: %s\n", + SSH_AGENTPID_ENV_NAME, pidstr, errstr); + exit(1); + } + if (kill(pid, SIGTERM) == -1) { + perror("kill"); + exit(1); + } + format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %ld killed;\n", (long)pid); + exit(0); + } + + /* + * Minimum file descriptors: + * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) + + * a few spare for libc / stack protectors / sanitisers, etc. + */ +#define SSH_AGENT_MIN_FDS (3+1+1+1+4) + if (rlim.rlim_cur < SSH_AGENT_MIN_FDS) + fatal("%s: file descriptor rlimit %lld too low (minimum %u)", + __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS); + maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS; + + parent_pid = getpid(); + + if (agentsocket == NULL) { + /* Create private directory for agent socket */ + mktemp_proto(socket_dir, sizeof(socket_dir)); + if (mkdtemp(socket_dir) == NULL) { + perror("mkdtemp: private socket dir"); + exit(1); + } + snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, + (long)parent_pid); + } else { + /* Try to use specified agent socket */ + socket_dir[0] = '\0'; + strlcpy(socket_name, agentsocket, sizeof socket_name); + } + + /* + * Create socket early so it will exist before command gets run from + * the parent. + */ + prev_mask = umask(0177); + sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); + if (sock < 0) { + /* XXX - unix_listener() calls error() not perror() */ + *socket_name = '\0'; /* Don't unlink any existing file */ + cleanup_exit(1); + } + umask(prev_mask); + + /* + * Fork, and have the parent execute the command, if any, or present + * the socket data. The child continues as the authentication agent. + */ + if (D_flag || d_flag) { + log_init(__progname, + d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, + SYSLOG_FACILITY_AUTH, 1); + format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, + SSH_AUTHSOCKET_ENV_NAME); + printf("echo Agent pid %ld;\n", (long)parent_pid); + fflush(stdout); + goto skip; + } + pid = fork(); + if (pid == -1) { + perror("fork"); + cleanup_exit(1); + } + if (pid != 0) { /* Parent - execute the given command. */ + close(sock); + snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); + if (ac == 0) { + format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, + SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, + SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %ld;\n", (long)pid); + exit(0); + } + if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || + setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { + perror("setenv"); + exit(1); + } + execvp(av[0], av); + perror(av[0]); + exit(1); + } + /* child */ + log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); + + if (setsid() == -1) { + error("setsid: %s", strerror(errno)); + cleanup_exit(1); + } + + (void)chdir("/"); + if (stdfd_devnull(1, 1, 1) == -1) + error_f("stdfd_devnull failed"); + +#ifdef HAVE_SETRLIMIT + /* deny core dumps, since memory contains unencrypted private keys */ + rlim.rlim_cur = rlim.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlim) == -1) { + error("setrlimit RLIMIT_CORE: %s", strerror(errno)); + cleanup_exit(1); + } +#endif + +skip: + + cleanup_pid = getpid(); + +#ifdef ENABLE_PKCS11 + pkcs11_init(0); +#endif + new_socket(AUTH_SOCKET, sock); + if (ac > 0) + parent_alive_interval = 10; + idtab_init(); + ssh_signal(SIGPIPE, SIG_IGN); + ssh_signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); + ssh_signal(SIGHUP, cleanup_handler); + ssh_signal(SIGTERM, cleanup_handler); + + if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) + fatal("%s: pledge: %s", __progname, strerror(errno)); + platform_pledge_agent(); + + while (1) { + prepare_poll(&pfd, &npfd, &timeout, maxfds); + result = poll(pfd, npfd, timeout); + saved_errno = errno; + if (parent_alive_interval != 0) + check_parent_exists(); + (void) reaper(); /* remove expired keys */ + if (result == -1) { + if (saved_errno == EINTR) + continue; + fatal("poll: %s", strerror(saved_errno)); + } else if (result > 0) + after_poll(pfd, npfd, maxfds); + } + /* NOTREACHED */ +} diff --git a/aosp/external/openssh/ssh-dss.c b/aosp/external/openssh/ssh-dss.c new file mode 100644 index 0000000000000000000000000000000000000000..fddc29cc9173238d7a1e2c2e76632254f2557ec5 --- /dev/null +++ b/aosp/external/openssh/ssh-dss.c @@ -0,0 +1,207 @@ +/* $OpenBSD: ssh-dss.c,v 1.39 2020/02/26 13:40:09 jsg Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include +#include + +#include +#include + +#include "sshbuf.h" +#include "compat.h" +#include "ssherr.h" +#include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" + +#include "openbsd-compat/openssl-compat.h" + +#define INTBLOB_LEN 20 +#define SIGBLOB_LEN (2*INTBLOB_LEN) + +int +ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) +{ + DSA_SIG *sig = NULL; + const BIGNUM *sig_r, *sig_s; + u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; + size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + struct sshbuf *b = NULL; + int ret = SSH_ERR_INVALID_ARGUMENT; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen == 0) + return SSH_ERR_INTERNAL_ERROR; + + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + DSA_SIG_get0(sig, &sig_r, &sig_s); + rlen = BN_num_bytes(sig_r); + slen = BN_num_bytes(sig_s); + if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { + ret = SSH_ERR_INTERNAL_ERROR; + goto out; + } + explicit_bzero(sigblob, SIGBLOB_LEN); + BN_bn2bin(sig_r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); + BN_bn2bin(sig_s, sigblob + SIGBLOB_LEN - slen); + + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || + (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) + goto out; + + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } + if (lenp != NULL) + *lenp = len; + ret = 0; + out: + explicit_bzero(digest, sizeof(digest)); + DSA_SIG_free(sig); + sshbuf_free(b); + return ret; +} + +int +ssh_dss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) +{ + DSA_SIG *sig = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; + u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL; + size_t len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + char *ktype = NULL; + + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + if (dlen == 0) + return SSH_ERR_INTERNAL_ERROR; + + /* fetch signature */ + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (strcmp("ssh-dss", ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + + if (len != SIGBLOB_LEN) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* parse signature */ + if ((sig = DSA_SIG_new()) == NULL || + (sig_r = BN_new()) == NULL || + (sig_s = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig_r) == NULL) || + (BN_bin2bn(sigblob + INTBLOB_LEN, INTBLOB_LEN, sig_s) == NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (!DSA_SIG_set0(sig, sig_r, sig_s)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + sig_r = sig_s = NULL; /* transferred */ + + /* sha1 the data */ + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + switch (DSA_do_verify(digest, dlen, sig, key->dsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + out: + explicit_bzero(digest, sizeof(digest)); + DSA_SIG_free(sig); + BN_clear_free(sig_r); + BN_clear_free(sig_s); + sshbuf_free(b); + free(ktype); + if (sigblob != NULL) + freezero(sigblob, len); + return ret; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/ssh-ecdsa-sk.c b/aosp/external/openssh/ssh-ecdsa-sk.c new file mode 100644 index 0000000000000000000000000000000000000000..c6927ecb27c891fcd65af44d19122e725cf8e3ef --- /dev/null +++ b/aosp/external/openssh/ssh-ecdsa-sk.c @@ -0,0 +1,324 @@ +/* $OpenBSD: ssh-ecdsa-sk.c,v 1.8 2020/06/22 23:44:27 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2010 Damien Miller. All rights reserved. + * Copyright (c) 2019 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* #define DEBUG_SK 1 */ + +#include "includes.h" + +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include +#endif + +#include +#include /* needed for DEBUG_SK only */ + +#include "openbsd-compat/openssl-compat.h" + +#include "sshbuf.h" +#include "ssherr.h" +#include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" + +#ifndef OPENSSL_HAS_ECC +/* ARGSUSED */ +int +ssh_ecdsa_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat, + struct sshkey_sig_details **detailsp) +{ + return SSH_ERR_FEATURE_UNSUPPORTED; +} +#else /* OPENSSL_HAS_ECC */ + +/* + * Check FIDO/W3C webauthn signatures clientData field against the expected + * format and prepare a hash of it for use in signature verification. + * + * webauthn signatures do not sign the hash of the message directly, but + * instead sign a JSON-like "clientData" wrapper structure that contains the + * message hash along with a other information. + * + * Fortunately this structure has a fixed format so it is possible to verify + * that the hash of the signed message is present within the clientData + * structure without needing to implement any JSON parsing. + */ +static int +webauthn_check_prepare_hash(const u_char *data, size_t datalen, + const char *origin, const struct sshbuf *wrapper, + uint8_t flags, const struct sshbuf *extensions, + u_char *msghash, size_t msghashlen) +{ + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *chall = NULL, *m = NULL; + + if ((m = sshbuf_new()) == NULL || + (chall = sshbuf_from(data, datalen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* + * Ensure origin contains no quote character and that the flags are + * consistent with what we received + */ + if (strchr(origin, '\"') != NULL || + (flags & 0x40) != 0 /* AD */ || + ((flags & 0x80) == 0 /* ED */) != (sshbuf_len(extensions) == 0)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* + * Prepare the preamble to clientData that we expect, poking the + * challenge and origin into their canonical positions in the + * structure. The crossOrigin flag and any additional extension + * fields present are ignored. + */ +#define WEBAUTHN_0 "{\"type\":\"webauthn.get\",\"challenge\":\"" +#define WEBAUTHN_1 "\",\"origin\":\"" +#define WEBAUTHN_2 "\"" + if ((r = sshbuf_put(m, WEBAUTHN_0, sizeof(WEBAUTHN_0) - 1)) != 0 || + (r = sshbuf_dtourlb64(chall, m, 0)) != 0 || + (r = sshbuf_put(m, WEBAUTHN_1, sizeof(WEBAUTHN_1) - 1)) != 0 || + (r = sshbuf_put(m, origin, strlen(origin))) != 0 || + (r = sshbuf_put(m, WEBAUTHN_2, sizeof(WEBAUTHN_2) - 1)) != 0) + goto out; +#ifdef DEBUG_SK + fprintf(stderr, "%s: received origin: %s\n", __func__, origin); + fprintf(stderr, "%s: received clientData:\n", __func__); + sshbuf_dump(wrapper, stderr); + fprintf(stderr, "%s: expected clientData premable:\n", __func__); + sshbuf_dump(m, stderr); +#endif + /* Check that the supplied clientData has the preamble we expect */ + if ((r = sshbuf_cmp(wrapper, 0, sshbuf_ptr(m), sshbuf_len(m))) != 0) + goto out; + + /* Prepare hash of clientData */ + if ((r = ssh_digest_buffer(SSH_DIGEST_SHA256, wrapper, + msghash, msghashlen)) != 0) + goto out; + + /* success */ + r = 0; + out: + sshbuf_free(chall); + sshbuf_free(m); + return r; +} + +/* ARGSUSED */ +int +ssh_ecdsa_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat, + struct sshkey_sig_details **detailsp) +{ + ECDSA_SIG *sig = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; + u_char sig_flags; + u_char msghash[32], apphash[32], sighash[32]; + u_int sig_counter; + int is_webauthn = 0, ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL; + struct sshbuf *webauthn_wrapper = NULL, *webauthn_exts = NULL; + char *ktype = NULL, *webauthn_origin = NULL; + struct sshkey_sig_details *details = NULL; +#ifdef DEBUG_SK + char *tmp = NULL; +#endif + + if (detailsp != NULL) + *detailsp = NULL; + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA_SK || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + + if (key->ecdsa_nid != NID_X9_62_prime256v1) + return SSH_ERR_INTERNAL_ERROR; + + /* fetch signature */ + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((details = calloc(1, sizeof(*details))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (strcmp(ktype, "webauthn-sk-ecdsa-sha2-nistp256@openssh.com") == 0) + is_webauthn = 1; + else if (strcmp(ktype, "sk-ecdsa-sha2-nistp256@openssh.com") != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_froms(b, &sigbuf) != 0 || + sshbuf_get_u8(b, &sig_flags) != 0 || + sshbuf_get_u32(b, &sig_counter) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (is_webauthn) { + if (sshbuf_get_cstring(b, &webauthn_origin, NULL) != 0 || + sshbuf_froms(b, &webauthn_wrapper) != 0 || + sshbuf_froms(b, &webauthn_exts) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + + /* parse signature */ + if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 || + sshbuf_get_bignum2(sigbuf, &sig_s) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + +#ifdef DEBUG_SK + fprintf(stderr, "%s: data: (len %zu)\n", __func__, datalen); + /* sshbuf_dump_data(data, datalen, stderr); */ + fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r))); + free(tmp); + fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s))); + free(tmp); + fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", + __func__, sig_flags, sig_counter); + if (is_webauthn) { + fprintf(stderr, "%s: webauthn origin: %s\n", __func__, + webauthn_origin); + fprintf(stderr, "%s: webauthn_wrapper:\n", __func__); + sshbuf_dump(webauthn_wrapper, stderr); + } +#endif + if ((sig = ECDSA_SIG_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + sig_r = sig_s = NULL; /* transferred */ + + /* Reconstruct data that was supposedly signed */ + if ((original_signed = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (is_webauthn) { + if ((ret = webauthn_check_prepare_hash(data, datalen, + webauthn_origin, webauthn_wrapper, sig_flags, webauthn_exts, + msghash, sizeof(msghash))) != 0) + goto out; + } else if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen, + msghash, sizeof(msghash))) != 0) + goto out; + /* Application value is hashed before signature */ + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, + strlen(key->sk_application), apphash, sizeof(apphash))) != 0) + goto out; +#ifdef DEBUG_SK + fprintf(stderr, "%s: hashed application:\n", __func__); + sshbuf_dump_data(apphash, sizeof(apphash), stderr); + fprintf(stderr, "%s: hashed message:\n", __func__); + sshbuf_dump_data(msghash, sizeof(msghash), stderr); +#endif + if ((ret = sshbuf_put(original_signed, + apphash, sizeof(apphash))) != 0 || + (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 || + (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 || + (ret = sshbuf_putb(original_signed, webauthn_exts)) != 0 || + (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0) + goto out; + /* Signature is over H(original_signed) */ + if ((ret = ssh_digest_buffer(SSH_DIGEST_SHA256, original_signed, + sighash, sizeof(sighash))) != 0) + goto out; + details->sk_counter = sig_counter; + details->sk_flags = sig_flags; +#ifdef DEBUG_SK + fprintf(stderr, "%s: signed buf:\n", __func__); + sshbuf_dump(original_signed, stderr); + fprintf(stderr, "%s: signed hash:\n", __func__); + sshbuf_dump_data(sighash, sizeof(sighash), stderr); +#endif + + /* Verify it */ + switch (ECDSA_do_verify(sighash, sizeof(sighash), sig, key->ecdsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + /* success */ + if (detailsp != NULL) { + *detailsp = details; + details = NULL; + } + out: + explicit_bzero(&sig_flags, sizeof(sig_flags)); + explicit_bzero(&sig_counter, sizeof(sig_counter)); + explicit_bzero(msghash, sizeof(msghash)); + explicit_bzero(sighash, sizeof(msghash)); + explicit_bzero(apphash, sizeof(apphash)); + sshkey_sig_details_free(details); + sshbuf_free(webauthn_wrapper); + sshbuf_free(webauthn_exts); + free(webauthn_origin); + sshbuf_free(original_signed); + sshbuf_free(sigbuf); + sshbuf_free(b); + ECDSA_SIG_free(sig); + BN_clear_free(sig_r); + BN_clear_free(sig_s); + free(ktype); + return ret; +} + +#endif /* OPENSSL_HAS_ECC */ diff --git a/aosp/external/openssh/ssh-ecdsa.c b/aosp/external/openssh/ssh-ecdsa.c new file mode 100644 index 0000000000000000000000000000000000000000..599c7199db1986a44f02642909cd3307461c3b5a --- /dev/null +++ b/aosp/external/openssh/ssh-ecdsa.c @@ -0,0 +1,200 @@ +/* $OpenBSD: ssh-ecdsa.c,v 1.16 2019/01/21 09:54:11 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2010 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) + +#include + +#include +#include +#include +#include + +#include + +#include "sshbuf.h" +#include "ssherr.h" +#include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" + +#include "openbsd-compat/openssl-compat.h" + +/* ARGSUSED */ +int +ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) +{ + ECDSA_SIG *sig = NULL; + const BIGNUM *sig_r, *sig_s; + int hash_alg; + u_char digest[SSH_DIGEST_MAX_LENGTH]; + size_t len, dlen; + struct sshbuf *b = NULL, *bb = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA) + return SSH_ERR_INVALID_ARGUMENT; + + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || + (dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + ECDSA_SIG_get0(sig, &sig_r, &sig_s); + if ((ret = sshbuf_put_bignum2(bb, sig_r)) != 0 || + (ret = sshbuf_put_bignum2(bb, sig_s)) != 0) + goto out; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || + (ret = sshbuf_put_stringb(b, bb)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } + if (lenp != NULL) + *lenp = len; + ret = 0; + out: + explicit_bzero(digest, sizeof(digest)); + sshbuf_free(b); + sshbuf_free(bb); + ECDSA_SIG_free(sig); + return ret; +} + +/* ARGSUSED */ +int +ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) +{ + ECDSA_SIG *sig = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; + int hash_alg; + u_char digest[SSH_DIGEST_MAX_LENGTH]; + size_t dlen; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL; + char *ktype = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || + (dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; + + /* fetch signature */ + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_froms(b, &sigbuf) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + + /* parse signature */ + if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 || + sshbuf_get_bignum2(sigbuf, &sig_s) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((sig = ECDSA_SIG_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + sig_r = sig_s = NULL; /* transferred */ + + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + out: + explicit_bzero(digest, sizeof(digest)); + sshbuf_free(sigbuf); + sshbuf_free(b); + ECDSA_SIG_free(sig); + BN_clear_free(sig_r); + BN_clear_free(sig_s); + free(ktype); + return ret; +} + +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ diff --git a/aosp/external/openssh/ssh-ed25519-sk.c b/aosp/external/openssh/ssh-ed25519-sk.c new file mode 100644 index 0000000000000000000000000000000000000000..4393ca669e17c6cda8eb4919a9694abfea1a227c --- /dev/null +++ b/aosp/external/openssh/ssh-ed25519-sk.c @@ -0,0 +1,163 @@ +/* $OpenBSD: ssh-ed25519-sk.c,v 1.6 2020/10/18 11:32:02 djm Exp $ */ +/* + * Copyright (c) 2019 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* #define DEBUG_SK 1 */ + +#include "includes.h" + +#define SSHKEY_INTERNAL +#include +#include + +#include "crypto_api.h" + +#include +#include + +#include "log.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "ssherr.h" +#include "ssh.h" +#include "digest.h" + +int +ssh_ed25519_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat, + struct sshkey_sig_details **detailsp) +{ + struct sshbuf *b = NULL; + struct sshbuf *encoded = NULL; + char *ktype = NULL; + const u_char *sigblob; + const u_char *sm; + u_char *m = NULL; + u_char apphash[32]; + u_char msghash[32]; + u_char sig_flags; + u_int sig_counter; + size_t len; + unsigned long long smlen = 0, mlen = 0; + int r = SSH_ERR_INTERNAL_ERROR; + int ret; + struct sshkey_sig_details *details = NULL; + + if (detailsp != NULL) + *detailsp = NULL; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_ED25519_SK || + key->ed25519_pk == NULL || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_get_string_direct(b, &sigblob, &len) != 0 || + sshbuf_get_u8(b, &sig_flags) != 0 || + sshbuf_get_u32(b, &sig_counter) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: data:\n", __func__); + /* sshbuf_dump_data(data, datalen, stderr); */ + fprintf(stderr, "%s: sigblob:\n", __func__); + sshbuf_dump_data(sigblob, len, stderr); + fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", + __func__, sig_flags, sig_counter); +#endif + if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + r = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + if (len > crypto_sign_ed25519_BYTES) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, + strlen(key->sk_application), apphash, sizeof(apphash)) != 0 || + ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen, + msghash, sizeof(msghash)) != 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: hashed application:\n", __func__); + sshbuf_dump_data(apphash, sizeof(apphash), stderr); + fprintf(stderr, "%s: hashed message:\n", __func__); + sshbuf_dump_data(msghash, sizeof(msghash), stderr); +#endif + if ((details = calloc(1, sizeof(*details))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + details->sk_counter = sig_counter; + details->sk_flags = sig_flags; + if ((encoded = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_put(encoded, sigblob, len) != 0 || + sshbuf_put(encoded, apphash, sizeof(apphash)) != 0 || + sshbuf_put_u8(encoded, sig_flags) != 0 || + sshbuf_put_u32(encoded, sig_counter) != 0 || + sshbuf_put(encoded, msghash, sizeof(msghash)) != 0) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: signed buf:\n", __func__); + sshbuf_dump(encoded, stderr); +#endif + sm = sshbuf_ptr(encoded); + smlen = sshbuf_len(encoded); + mlen = smlen; + if ((m = malloc(smlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, + key->ed25519_pk)) != 0) { + debug2_f("crypto_sign_ed25519_open failed: %d", ret); + } + if (ret != 0 || mlen != smlen - len) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + /* XXX compare 'm' and 'sm + len' ? */ + /* success */ + r = 0; + if (detailsp != NULL) { + *detailsp = details; + details = NULL; + } + out: + if (m != NULL) + freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ + sshkey_sig_details_free(details); + sshbuf_free(b); + sshbuf_free(encoded); + free(ktype); + return r; +} diff --git a/aosp/external/openssh/ssh-ed25519.c b/aosp/external/openssh/ssh-ed25519.c new file mode 100644 index 0000000000000000000000000000000000000000..23419f3c884a8a9367bf4bab4999eecde6ba086f --- /dev/null +++ b/aosp/external/openssh/ssh-ed25519.c @@ -0,0 +1,160 @@ +/* $OpenBSD: ssh-ed25519.c,v 1.9 2020/10/18 11:32:02 djm Exp $ */ +/* + * Copyright (c) 2013 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#include "crypto_api.h" + +#include +#include + +#include "log.h" +#include "sshbuf.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" +#include "ssherr.h" +#include "ssh.h" + +int +ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) +{ + u_char *sig = NULL; + size_t slen = 0, len; + unsigned long long smlen; + int r, ret; + struct sshbuf *b = NULL; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_ED25519 || + key->ed25519_sk == NULL || + datalen >= INT_MAX - crypto_sign_ed25519_BYTES) + return SSH_ERR_INVALID_ARGUMENT; + smlen = slen = datalen + crypto_sign_ed25519_BYTES; + if ((sig = malloc(slen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + + if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, + key->ed25519_sk)) != 0 || smlen <= datalen) { + r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ + goto out; + } + /* encode signature */ + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || + (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } + if (lenp != NULL) + *lenp = len; + /* success */ + r = 0; + out: + sshbuf_free(b); + if (sig != NULL) + freezero(sig, slen); + + return r; +} + +int +ssh_ed25519_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) +{ + struct sshbuf *b = NULL; + char *ktype = NULL; + const u_char *sigblob; + u_char *sm = NULL, *m = NULL; + size_t len; + unsigned long long smlen = 0, mlen = 0; + int r, ret; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_ED25519 || + key->ed25519_pk == NULL || + datalen >= INT_MAX - crypto_sign_ed25519_BYTES || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || + (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) + goto out; + if (strcmp("ssh-ed25519", ktype) != 0) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + r = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + if (len > crypto_sign_ed25519_BYTES) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (datalen >= SIZE_MAX - len) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + smlen = len + datalen; + mlen = smlen; + if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(sm, sigblob, len); + memcpy(sm+len, data, datalen); + if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, + key->ed25519_pk)) != 0) { + debug2_f("crypto_sign_ed25519_open failed: %d", ret); + } + if (ret != 0 || mlen != datalen) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + /* XXX compare 'm' and 'data' ? */ + /* success */ + r = 0; + out: + if (sm != NULL) + freezero(sm, smlen); + if (m != NULL) + freezero(m, smlen); /* NB mlen may be invalid if r != 0 */ + sshbuf_free(b); + free(ktype); + return r; +} diff --git a/aosp/external/openssh/ssh-gss.h b/aosp/external/openssh/ssh-gss.h new file mode 100644 index 0000000000000000000000000000000000000000..a8af117d2ef22feea2a32c207fcb32e02a673f90 --- /dev/null +++ b/aosp/external/openssh/ssh-gss.h @@ -0,0 +1,139 @@ +/* $OpenBSD: ssh-gss.h,v 1.15 2021/01/27 10:05:28 djm Exp $ */ +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SSH_GSS_H +#define _SSH_GSS_H + +#ifdef GSSAPI + +#ifdef HAVE_GSSAPI_H +#include +#elif defined(HAVE_GSSAPI_GSSAPI_H) +#include +#endif + +#ifdef KRB5 +# ifndef HEIMDAL +# ifdef HAVE_GSSAPI_GENERIC_H +# include +# elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H) +# include +# endif + +/* Old MIT Kerberos doesn't seem to define GSS_NT_HOSTBASED_SERVICE */ + +# if !HAVE_DECL_GSS_C_NT_HOSTBASED_SERVICE +# define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +# endif /* !HAVE_DECL_GSS_C_NT_... */ + +# endif /* !HEIMDAL */ +#endif /* KRB5 */ + +/* draft-ietf-secsh-gsskeyex-06 */ +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 + +#define SSH_GSS_OIDTYPE 0x06 + +typedef struct { + char *filename; + char *envvar; + char *envval; + void *data; +} ssh_gssapi_ccache; + +typedef struct { + gss_buffer_desc displayname; + gss_buffer_desc exportedname; + gss_cred_id_t creds; + struct ssh_gssapi_mech_struct *mech; + ssh_gssapi_ccache store; +} ssh_gssapi_client; + +typedef struct ssh_gssapi_mech_struct { + char *enc_name; + char *name; + gss_OID_desc oid; + int (*dochild) (ssh_gssapi_client *); + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); + void (*storecreds) (ssh_gssapi_client *); +} ssh_gssapi_mech; + +typedef struct { + OM_uint32 major; /* both */ + OM_uint32 minor; /* both */ + gss_ctx_id_t context; /* both */ + gss_name_t name; /* both */ + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ + gss_cred_id_t client_creds; /* server */ +} Gssctxt; + +extern ssh_gssapi_mech *supported_mechs[]; + +int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); +void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); +void ssh_gssapi_set_oid(Gssctxt *, gss_OID); +void ssh_gssapi_supported_oids(gss_OID_set *); +ssh_gssapi_mech *ssh_gssapi_get_ctype(Gssctxt *); +void ssh_gssapi_prepare_supported_oids(void); +OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *); + +struct sshbuf; +int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *); + +OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *); +OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +OM_uint32 ssh_gssapi_getclient(Gssctxt *, ssh_gssapi_client *); +void ssh_gssapi_error(Gssctxt *); +char *ssh_gssapi_last_error(Gssctxt *, OM_uint32 *, OM_uint32 *); +void ssh_gssapi_build_ctx(Gssctxt **); +void ssh_gssapi_delete_ctx(Gssctxt **); +OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); +void ssh_gssapi_buildmic(struct sshbuf *, const char *, + const char *, const char *, const struct sshbuf *); +int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); + +/* In the server */ +OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +int ssh_gssapi_userok(char *name); +OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); +void ssh_gssapi_do_child(char ***, u_int *); +void ssh_gssapi_cleanup_creds(void); +void ssh_gssapi_storecreds(void); +const char *ssh_gssapi_displayname(void); + +#endif /* GSSAPI */ + +#endif /* _SSH_GSS_H */ diff --git a/aosp/external/openssh/ssh-keygen.1 b/aosp/external/openssh/ssh-keygen.1 new file mode 100644 index 0000000000000000000000000000000000000000..59b7f23a1fa5bf31b309776f89f7f74392146adb --- /dev/null +++ b/aosp/external/openssh/ssh-keygen.1 @@ -0,0 +1,1264 @@ +.\" $OpenBSD: ssh-keygen.1,v 1.220 2022/02/06 00:29:03 jsg Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: February 6 2022 $ +.Dt SSH-KEYGEN 1 +.Os +.Sh NAME +.Nm ssh-keygen +.Nd OpenSSH authentication key utility +.Sh SYNOPSIS +.Nm ssh-keygen +.Op Fl q +.Op Fl a Ar rounds +.Op Fl b Ar bits +.Op Fl C Ar comment +.Op Fl f Ar output_keyfile +.Op Fl m Ar format +.Op Fl N Ar new_passphrase +.Op Fl O Ar option +.Op Fl t Cm dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa +.Op Fl w Ar provider +.Op Fl Z Ar cipher +.Nm ssh-keygen +.Fl p +.Op Fl a Ar rounds +.Op Fl f Ar keyfile +.Op Fl m Ar format +.Op Fl N Ar new_passphrase +.Op Fl P Ar old_passphrase +.Op Fl Z Ar cipher +.Nm ssh-keygen +.Fl i +.Op Fl f Ar input_keyfile +.Op Fl m Ar key_format +.Nm ssh-keygen +.Fl e +.Op Fl f Ar input_keyfile +.Op Fl m Ar key_format +.Nm ssh-keygen +.Fl y +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl c +.Op Fl a Ar rounds +.Op Fl C Ar comment +.Op Fl f Ar keyfile +.Op Fl P Ar passphrase +.Nm ssh-keygen +.Fl l +.Op Fl v +.Op Fl E Ar fingerprint_hash +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl B +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl D Ar pkcs11 +.Nm ssh-keygen +.Fl F Ar hostname +.Op Fl lv +.Op Fl f Ar known_hosts_file +.Nm ssh-keygen +.Fl H +.Op Fl f Ar known_hosts_file +.Nm ssh-keygen +.Fl K +.Op Fl a Ar rounds +.Op Fl w Ar provider +.Nm ssh-keygen +.Fl R Ar hostname +.Op Fl f Ar known_hosts_file +.Nm ssh-keygen +.Fl r Ar hostname +.Op Fl g +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl M Cm generate +.Op Fl O Ar option +.Ar output_file +.Nm ssh-keygen +.Fl M Cm screen +.Op Fl f Ar input_file +.Op Fl O Ar option +.Ar output_file +.Nm ssh-keygen +.Fl I Ar certificate_identity +.Fl s Ar ca_key +.Op Fl hU +.Op Fl D Ar pkcs11_provider +.Op Fl n Ar principals +.Op Fl O Ar option +.Op Fl V Ar validity_interval +.Op Fl z Ar serial_number +.Ar +.Nm ssh-keygen +.Fl L +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl A +.Op Fl a Ar rounds +.Op Fl f Ar prefix_path +.Nm ssh-keygen +.Fl k +.Fl f Ar krl_file +.Op Fl u +.Op Fl s Ar ca_public +.Op Fl z Ar version_number +.Ar +.Nm ssh-keygen +.Fl Q +.Op Fl l +.Fl f Ar krl_file +.Ar +.Nm ssh-keygen +.Fl Y Cm find-principals +.Op Fl O Ar option +.Fl s Ar signature_file +.Fl f Ar allowed_signers_file +.Nm ssh-keygen +.Fl Y Cm match-principals +.Fl I Ar signer_identity +.Fl f Ar allowed_signers_file +.Nm ssh-keygen +.Fl Y Cm check-novalidate +.Op Fl O Ar option +.Fl n Ar namespace +.Fl s Ar signature_file +.Nm ssh-keygen +.Fl Y Cm sign +.Op Fl O Ar option +.Fl f Ar key_file +.Fl n Ar namespace +.Ar +.Nm ssh-keygen +.Fl Y Cm verify +.Op Fl O Ar option +.Fl f Ar allowed_signers_file +.Fl I Ar signer_identity +.Fl n Ar namespace +.Fl s Ar signature_file +.Op Fl r Ar revocation_file +.Sh DESCRIPTION +.Nm +generates, manages and converts authentication keys for +.Xr ssh 1 . +.Nm +can create keys for use by SSH protocol version 2. +.Pp +The type of key to be generated is specified with the +.Fl t +option. +If invoked without any arguments, +.Nm +will generate an RSA key. +.Pp +.Nm +is also used to generate groups for use in Diffie-Hellman group +exchange (DH-GEX). +See the +.Sx MODULI GENERATION +section for details. +.Pp +Finally, +.Nm +can be used to generate and update Key Revocation Lists, and to test whether +given keys have been revoked by one. +See the +.Sx KEY REVOCATION LISTS +section for details. +.Pp +Normally each user wishing to use SSH +with public key authentication runs this once to create the authentication +key in +.Pa ~/.ssh/id_dsa , +.Pa ~/.ssh/id_ecdsa , +.Pa ~/.ssh/id_ecdsa_sk , +.Pa ~/.ssh/id_ed25519 , +.Pa ~/.ssh/id_ed25519_sk +or +.Pa ~/.ssh/id_rsa . +Additionally, the system administrator may use this to generate host keys, +as seen in +.Pa /etc/rc . +.Pp +Normally this program generates the key and asks for a file in which +to store the private key. +The public key is stored in a file with the same name but +.Dq .pub +appended. +The program also asks for a passphrase. +The passphrase may be empty to indicate no passphrase +(host keys must have an empty passphrase), or it may be a string of +arbitrary length. +A passphrase is similar to a password, except it can be a phrase with a +series of words, punctuation, numbers, whitespace, or any string of +characters you want. +Good passphrases are 10-30 characters long, are +not simple sentences or otherwise easily guessable (English +prose has only 1-2 bits of entropy per character, and provides very bad +passphrases), and contain a mix of upper and lowercase letters, +numbers, and non-alphanumeric characters. +The passphrase can be changed later by using the +.Fl p +option. +.Pp +There is no way to recover a lost passphrase. +If the passphrase is lost or forgotten, a new key must be generated +and the corresponding public key copied to other machines. +.Pp +.Nm +will by default write keys in an OpenSSH-specific format. +This format is preferred as it offers better protection for +keys at rest as well as allowing storage of key comments within +the private key file itself. +The key comment may be useful to help identify the key. +The comment is initialized to +.Dq user@host +when the key is created, but can be changed using the +.Fl c +option. +.Pp +It is still possible for +.Nm +to write the previously-used PEM format private keys using the +.Fl m +flag. +This may be used when generating new keys, and existing new-format +keys may be converted using this option in conjunction with the +.Fl p +(change passphrase) flag. +.Pp +After a key is generated, +.Nm +will ask where the keys +should be placed to be activated. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl A +For each of the key types (rsa, dsa, ecdsa and ed25519) +for which host keys +do not exist, generate the host keys with the default key file path, +an empty passphrase, default bits for the key type, and default comment. +If +.Fl f +has also been specified, its argument is used as a prefix to the +default path for the resulting host key files. +This is used by +.Pa /etc/rc +to generate new host keys. +.It Fl a Ar rounds +When saving a private key, this option specifies the number of KDF +(key derivation function, currently +.Xr bcrypt_pbkdf 3 ) +rounds used. +Higher numbers result in slower passphrase verification and increased +resistance to brute-force password cracking (should the keys be stolen). +The default is 16 rounds. +.It Fl B +Show the bubblebabble digest of specified private or public key file. +.It Fl b Ar bits +Specifies the number of bits in the key to create. +For RSA keys, the minimum size is 1024 bits and the default is 3072 bits. +Generally, 3072 bits is considered sufficient. +DSA keys must be exactly 1024 bits as specified by FIPS 186-2. +For ECDSA keys, the +.Fl b +flag determines the key length by selecting from one of three elliptic +curve sizes: 256, 384 or 521 bits. +Attempting to use bit lengths other than these three values for ECDSA keys +will fail. +ECDSA-SK, Ed25519 and Ed25519-SK keys have a fixed length and the +.Fl b +flag will be ignored. +.It Fl C Ar comment +Provides a new comment. +.It Fl c +Requests changing the comment in the private and public key files. +The program will prompt for the file containing the private keys, for +the passphrase if the key has one, and for the new comment. +.It Fl D Ar pkcs11 +Download the public keys provided by the PKCS#11 shared library +.Ar pkcs11 . +When used in combination with +.Fl s , +this option indicates that a CA key resides in a PKCS#11 token (see the +.Sx CERTIFICATES +section for details). +.It Fl E Ar fingerprint_hash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Dq md5 +and +.Dq sha256 . +The default is +.Dq sha256 . +.It Fl e +This option will read a private or public OpenSSH key file and +print to stdout a public key in one of the formats specified by the +.Fl m +option. +The default export format is +.Dq RFC4716 . +This option allows exporting OpenSSH keys for use by other programs, including +several commercial SSH implementations. +.It Fl F Ar hostname | [hostname]:port +Search for the specified +.Ar hostname +(with optional port number) +in a +.Pa known_hosts +file, listing any occurrences found. +This option is useful to find hashed host names or addresses and may also be +used in conjunction with the +.Fl H +option to print found keys in a hashed format. +.It Fl f Ar filename +Specifies the filename of the key file. +.It Fl g +Use generic DNS format when printing fingerprint resource records using the +.Fl r +command. +.It Fl H +Hash a +.Pa known_hosts +file. +This replaces all hostnames and addresses with hashed representations +within the specified file; the original content is moved to a file with +a .old suffix. +These hashes may be used normally by +.Nm ssh +and +.Nm sshd , +but they do not reveal identifying information should the file's contents +be disclosed. +This option will not modify existing hashed hostnames and is therefore safe +to use on files that mix hashed and non-hashed names. +.It Fl h +When signing a key, create a host certificate instead of a user +certificate. +See the +.Sx CERTIFICATES +section for details. +.It Fl I Ar certificate_identity +Specify the key identity when signing a public key. +See the +.Sx CERTIFICATES +section for details. +.It Fl i +This option will read an unencrypted private (or public) key file +in the format specified by the +.Fl m +option and print an OpenSSH compatible private +(or public) key to stdout. +This option allows importing keys from other software, including several +commercial SSH implementations. +The default import format is +.Dq RFC4716 . +.It Fl K +Download resident keys from a FIDO authenticator. +Public and private key files will be written to the current directory for +each downloaded key. +If multiple FIDO authenticators are attached, keys will be downloaded from +the first touched authenticator. +.It Fl k +Generate a KRL file. +In this mode, +.Nm +will generate a KRL file at the location specified via the +.Fl f +flag that revokes every key or certificate presented on the command line. +Keys/certificates to be revoked may be specified by public key file or +using the format described in the +.Sx KEY REVOCATION LISTS +section. +.It Fl L +Prints the contents of one or more certificates. +.It Fl l +Show fingerprint of specified public key file. +For RSA and DSA keys +.Nm +tries to find the matching public key file and prints its fingerprint. +If combined with +.Fl v , +a visual ASCII art representation of the key is supplied with the +fingerprint. +.It Fl M Cm generate +Generate candidate Diffie-Hellman Group Exchange (DH-GEX) parameters for +eventual use by the +.Sq diffie-hellman-group-exchange-* +key exchange methods. +The numbers generated by this operation must be further screened before +use. +See the +.Sx MODULI GENERATION +section for more information. +.It Fl M Cm screen +Screen candidate parameters for Diffie-Hellman Group Exchange. +This will accept a list of candidate numbers and test that they are +safe (Sophie Germain) primes with acceptable group generators. +The results of this operation may be added to the +.Pa /etc/moduli +file. +See the +.Sx MODULI GENERATION +section for more information. +.It Fl m Ar key_format +Specify a key format for key generation, the +.Fl i +(import), +.Fl e +(export) conversion options, and the +.Fl p +change passphrase operation. +The latter may be used to convert between OpenSSH private key and PEM +private key formats. +The supported key formats are: +.Dq RFC4716 +(RFC 4716/SSH2 public or private key), +.Dq PKCS8 +(PKCS8 public or private key) +or +.Dq PEM +(PEM public key). +By default OpenSSH will write newly-generated private keys in its own +format, but when converting public keys for export the default format is +.Dq RFC4716 . +Setting a format of +.Dq PEM +when generating or updating a supported private key type will cause the +key to be stored in the legacy PEM private key format. +.It Fl N Ar new_passphrase +Provides the new passphrase. +.It Fl n Ar principals +Specify one or more principals (user or host names) to be included in +a certificate when signing a key. +Multiple principals may be specified, separated by commas. +See the +.Sx CERTIFICATES +section for details. +.It Fl O Ar option +Specify a key/value option. +These are specific to the operation that +.Nm +has been requested to perform. +.Pp +When signing certificates, one of the options listed in the +.Sx CERTIFICATES +section may be specified here. +.Pp +When performing moduli generation or screening, one of the options +listed in the +.Sx MODULI GENERATION +section may be specified. +.Pp +When generating a key that will be hosted on a FIDO authenticator, +this flag may be used to specify key-specific options. +Those supported at present are: +.Bl -tag -width Ds +.It Cm application +Override the default FIDO application/origin string of +.Dq ssh: . +This may be useful when generating host or domain-specific resident keys. +The specified application string must begin with +.Dq ssh: . +.It Cm challenge Ns = Ns Ar path +Specifies a path to a challenge string that will be passed to the +FIDO token during key generation. +The challenge string may be used as part of an out-of-band +protocol for key enrollment +(a random challenge is used by default). +.It Cm device +Explicitly specify a +.Xr fido 4 +device to use, rather than letting the token middleware select one. +.It Cm no-touch-required +Indicate that the generated private key should not require touch +events (user presence) when making signatures. +Note that +.Xr sshd 8 +will refuse such signatures by default, unless overridden via +an authorized_keys option. +.It Cm resident +Indicate that the key should be stored on the FIDO authenticator itself. +Resident keys may be supported on FIDO2 tokens and typically require that +a PIN be set on the token prior to generation. +Resident keys may be loaded off the token using +.Xr ssh-add 1 . +.It Cm user +A username to be associated with a resident key, +overriding the empty default username. +Specifying a username may be useful when generating multiple resident keys +for the same application name. +.It Cm verify-required +Indicate that this private key should require user verification for +each signature. +Not all FIDO tokens support this option. +Currently PIN authentication is the only supported verification method, +but other methods may be supported in the future. +.It Cm write-attestation Ns = Ns Ar path +May be used at key generation time to record the attestation data +returned from FIDO tokens during key generation. +This information is potentially sensitive. +By default, this information is discarded. +.El +.Pp +When performing signature-related options using the +.Fl Y +flag, the following options are accepted: +.Bl -tag -width Ds +.It Cm hashalg Ns = Ns Ar algorithm +Selects the hash algorithm to use for hashing the message to be signed. +Valid algorithms are +.Dq sha256 +and +.Dq sha512. +The default is +.Dq sha512. +.It Cm print-pubkey +Print the full public key to standard output after signature verification. +.It Cm verify-time Ns = Ns Ar timestamp +Specifies a time to use when validating signatures instead of the current +time. +The time may be specified as a date in YYYYMMDD format or a time +in YYYYMMDDHHMM[SS] format. +.El +.Pp +The +.Fl O +option may be specified multiple times. +.It Fl P Ar passphrase +Provides the (old) passphrase. +.It Fl p +Requests changing the passphrase of a private key file instead of +creating a new private key. +The program will prompt for the file +containing the private key, for the old passphrase, and twice for the +new passphrase. +.It Fl Q +Test whether keys have been revoked in a KRL. +If the +.Fl l +option is also specified then the contents of the KRL will be printed. +.It Fl q +Silence +.Nm ssh-keygen . +.It Fl R Ar hostname | [hostname]:port +Removes all keys belonging to the specified +.Ar hostname +(with optional port number) +from a +.Pa known_hosts +file. +This option is useful to delete hashed hosts (see the +.Fl H +option above). +.It Fl r Ar hostname +Print the SSHFP fingerprint resource record named +.Ar hostname +for the specified public key file. +.It Fl s Ar ca_key +Certify (sign) a public key using the specified CA key. +See the +.Sx CERTIFICATES +section for details. +.Pp +When generating a KRL, +.Fl s +specifies a path to a CA public key file used to revoke certificates directly +by key ID or serial number. +See the +.Sx KEY REVOCATION LISTS +section for details. +.It Fl t Cm dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa +Specifies the type of key to create. +The possible values are +.Dq dsa , +.Dq ecdsa , +.Dq ecdsa-sk , +.Dq ed25519 , +.Dq ed25519-sk , +or +.Dq rsa . +.Pp +This flag may also be used to specify the desired signature type when +signing certificates using an RSA CA key. +The available RSA signature variants are +.Dq ssh-rsa +(SHA1 signatures, not recommended), +.Dq rsa-sha2-256 , +and +.Dq rsa-sha2-512 +(the default). +.It Fl U +When used in combination with +.Fl s , +this option indicates that a CA key resides in a +.Xr ssh-agent 1 . +See the +.Sx CERTIFICATES +section for more information. +.It Fl u +Update a KRL. +When specified with +.Fl k , +keys listed via the command line are added to the existing KRL rather than +a new KRL being created. +.It Fl V Ar validity_interval +Specify a validity interval when signing a certificate. +A validity interval may consist of a single time, indicating that the +certificate is valid beginning now and expiring at that time, or may consist +of two times separated by a colon to indicate an explicit time interval. +.Pp +The start time may be specified as the string +.Dq always +to indicate the certificate has no specified start time, +a date in YYYYMMDD format, a time in YYYYMMDDHHMM[SS] format, +a relative time (to the current time) consisting of a minus sign followed by +an interval in the format described in the +TIME FORMATS section of +.Xr sshd_config 5 . +.Pp +The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMM[SS] time, +a relative time starting with a plus character or the string +.Dq forever +to indicate that the certificate has no expiry date. +.Pp +For example: +.Dq +52w1d +(valid from now to 52 weeks and one day from now), +.Dq -4w:+4w +(valid from four weeks ago to four weeks from now), +.Dq 20100101123000:20110101123000 +(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011), +.Dq -1d:20110101 +(valid from yesterday to midnight, January 1st, 2011), +.Dq -1m:forever +(valid from one minute ago and never expiring). +.It Fl v +Verbose mode. +Causes +.Nm +to print debugging messages about its progress. +This is helpful for debugging moduli generation. +Multiple +.Fl v +options increase the verbosity. +The maximum is 3. +.It Fl w Ar provider +Specifies a path to a library that will be used when creating +FIDO authenticator-hosted keys, overriding the default of using +the internal USB HID support. +.It Fl Y Cm find-principals +Find the principal(s) associated with the public key of a signature, +provided using the +.Fl s +flag in an authorized signers file provided using the +.Fl f +flag. +The format of the allowed signers file is documented in the +.Sx ALLOWED SIGNERS +section below. +If one or more matching principals are found, they are returned on +standard output. +.It Fl Y Cm match-principals +Find principal matching the principal name provided using the +.Fl I +flag in the authorized signers file specified using the +.Fl f +flag. +If one or more matching principals are found, they are returned on +standard output. +.It Fl Y Cm check-novalidate +Checks that a signature generated using +.Nm +.Fl Y Cm sign +has a valid structure. +This does not validate if a signature comes from an authorized signer. +When testing a signature, +.Nm +accepts a message on standard input and a signature namespace using +.Fl n . +A file containing the corresponding signature must also be supplied using the +.Fl s +flag. +Successful testing of the signature is signalled by +.Nm +returning a zero exit status. +.It Fl Y Cm sign +Cryptographically sign a file or some data using a SSH key. +When signing, +.Nm +accepts zero or more files to sign on the command-line - if no files +are specified then +.Nm +will sign data presented on standard input. +Signatures are written to the path of the input file with +.Dq .sig +appended, or to standard output if the message to be signed was read from +standard input. +.Pp +The key used for signing is specified using the +.Fl f +option and may refer to either a private key, or a public key with the private +half available via +.Xr ssh-agent 1 . +An additional signature namespace, used to prevent signature confusion across +different domains of use (e.g. file signing vs email signing) must be provided +via the +.Fl n +flag. +Namespaces are arbitrary strings, and may include: +.Dq file +for file signing, +.Dq email +for email signing. +For custom uses, it is recommended to use names following a +NAMESPACE@YOUR.DOMAIN pattern to generate unambiguous namespaces. +.It Fl Y Cm verify +Request to verify a signature generated using +.Nm +.Fl Y Cm sign +as described above. +When verifying a signature, +.Nm +accepts a message on standard input and a signature namespace using +.Fl n . +A file containing the corresponding signature must also be supplied using the +.Fl s +flag, along with the identity of the signer using +.Fl I +and a list of allowed signers via the +.Fl f +flag. +The format of the allowed signers file is documented in the +.Sx ALLOWED SIGNERS +section below. +A file containing revoked keys can be passed using the +.Fl r +flag. +The revocation file may be a KRL or a one-per-line list of public keys. +Successful verification by an authorized signer is signalled by +.Nm +returning a zero exit status. +.It Fl y +This option will read a private +OpenSSH format file and print an OpenSSH public key to stdout. +.It Fl Z Ar cipher +Specifies the cipher to use for encryption when writing an OpenSSH-format +private key file. +The list of available ciphers may be obtained using +.Qq ssh -Q cipher . +The default is +.Dq aes256-ctr . +.It Fl z Ar serial_number +Specifies a serial number to be embedded in the certificate to distinguish +this certificate from others from the same CA. +If the +.Ar serial_number +is prefixed with a +.Sq + +character, then the serial number will be incremented for each certificate +signed on a single command-line. +The default serial number is zero. +.Pp +When generating a KRL, the +.Fl z +flag is used to specify a KRL version number. +.El +.Sh MODULI GENERATION +.Nm +may be used to generate groups for the Diffie-Hellman Group Exchange +(DH-GEX) protocol. +Generating these groups is a two-step process: first, candidate +primes are generated using a fast, but memory intensive process. +These candidate primes are then tested for suitability (a CPU-intensive +process). +.Pp +Generation of primes is performed using the +.Fl M Cm generate +option. +The desired length of the primes may be specified by the +.Fl O Cm bits +option. +For example: +.Pp +.Dl # ssh-keygen -M generate -O bits=2048 moduli-2048.candidates +.Pp +By default, the search for primes begins at a random point in the +desired length range. +This may be overridden using the +.Fl O Cm start +option, which specifies a different start point (in hex). +.Pp +Once a set of candidates have been generated, they must be screened for +suitability. +This may be performed using the +.Fl M Cm screen +option. +In this mode +.Nm +will read candidates from standard input (or a file specified using the +.Fl f +option). +For example: +.Pp +.Dl # ssh-keygen -M screen -f moduli-2048.candidates moduli-2048 +.Pp +By default, each candidate will be subjected to 100 primality tests. +This may be overridden using the +.Fl O Cm prime-tests +option. +The DH generator value will be chosen automatically for the +prime under consideration. +If a specific generator is desired, it may be requested using the +.Fl O Cm generator +option. +Valid generator values are 2, 3, and 5. +.Pp +Screened DH groups may be installed in +.Pa /etc/moduli . +It is important that this file contains moduli of a range of bit lengths. +.Pp +A number of options are available for moduli generation and screening via the +.Fl O +flag: +.Bl -tag -width Ds +.It Ic lines Ns = Ns Ar number +Exit after screening the specified number of lines while performing DH +candidate screening. +.It Ic start-line Ns = Ns Ar line-number +Start screening at the specified line number while performing DH candidate +screening. +.It Ic checkpoint Ns = Ns Ar filename +Write the last line processed to the specified file while performing DH +candidate screening. +This will be used to skip lines in the input file that have already been +processed if the job is restarted. +.It Ic memory Ns = Ns Ar mbytes +Specify the amount of memory to use (in megabytes) when generating +candidate moduli for DH-GEX. +.It Ic start Ns = Ns Ar hex-value +Specify start point (in hex) when generating candidate moduli for DH-GEX. +.It Ic generator Ns = Ns Ar value +Specify desired generator (in decimal) when testing candidate moduli for DH-GEX. +.El +.Sh CERTIFICATES +.Nm +supports signing of keys to produce certificates that may be used for +user or host authentication. +Certificates consist of a public key, some identity information, zero or +more principal (user or host) names and a set of options that +are signed by a Certification Authority (CA) key. +Clients or servers may then trust only the CA key and verify its signature +on a certificate rather than trusting many user/host keys. +Note that OpenSSH certificates are a different, and much simpler, format to +the X.509 certificates used in +.Xr ssl 8 . +.Pp +.Nm +supports two types of certificates: user and host. +User certificates authenticate users to servers, whereas host certificates +authenticate server hosts to users. +To generate a user certificate: +.Pp +.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub +.Pp +The resultant certificate will be placed in +.Pa /path/to/user_key-cert.pub . +A host certificate requires the +.Fl h +option: +.Pp +.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub +.Pp +The host certificate will be output to +.Pa /path/to/host_key-cert.pub . +.Pp +It is possible to sign using a CA key stored in a PKCS#11 token by +providing the token library using +.Fl D +and identifying the CA key by providing its public half as an argument +to +.Fl s : +.Pp +.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub +.Pp +Similarly, it is possible for the CA key to be hosted in a +.Xr ssh-agent 1 . +This is indicated by the +.Fl U +flag and, again, the CA key must be identified by its public half. +.Pp +.Dl $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub +.Pp +In all cases, +.Ar key_id +is a "key identifier" that is logged by the server when the certificate +is used for authentication. +.Pp +Certificates may be limited to be valid for a set of principal (user/host) +names. +By default, generated certificates are valid for all users or hosts. +To generate a certificate for a specified set of principals: +.Pp +.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub +.Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain host_key.pub" +.Pp +Additional limitations on the validity and use of user certificates may +be specified through certificate options. +A certificate option may disable features of the SSH session, may be +valid only when presented from particular source addresses or may +force the use of a specific command. +.Pp +The options that are valid for user certificates are: +.Pp +.Bl -tag -width Ds -compact +.It Ic clear +Clear all enabled permissions. +This is useful for clearing the default set of permissions so permissions may +be added individually. +.Pp +.It Ic critical : Ns Ar name Ns Op Ns = Ns Ar contents +.It Ic extension : Ns Ar name Ns Op Ns = Ns Ar contents +Includes an arbitrary certificate critical option or extension. +The specified +.Ar name +should include a domain suffix, e.g.\& +.Dq name@example.com . +If +.Ar contents +is specified then it is included as the contents of the extension/option +encoded as a string, otherwise the extension/option is created with no +contents (usually indicating a flag). +Extensions may be ignored by a client or server that does not recognise them, +whereas unknown critical options will cause the certificate to be refused. +.Pp +.It Ic force-command Ns = Ns Ar command +Forces the execution of +.Ar command +instead of any shell or command specified by the user when +the certificate is used for authentication. +.Pp +.It Ic no-agent-forwarding +Disable +.Xr ssh-agent 1 +forwarding (permitted by default). +.Pp +.It Ic no-port-forwarding +Disable port forwarding (permitted by default). +.Pp +.It Ic no-pty +Disable PTY allocation (permitted by default). +.Pp +.It Ic no-user-rc +Disable execution of +.Pa ~/.ssh/rc +by +.Xr sshd 8 +(permitted by default). +.Pp +.It Ic no-x11-forwarding +Disable X11 forwarding (permitted by default). +.Pp +.It Ic permit-agent-forwarding +Allows +.Xr ssh-agent 1 +forwarding. +.Pp +.It Ic permit-port-forwarding +Allows port forwarding. +.Pp +.It Ic permit-pty +Allows PTY allocation. +.Pp +.It Ic permit-user-rc +Allows execution of +.Pa ~/.ssh/rc +by +.Xr sshd 8 . +.Pp +.It Ic permit-X11-forwarding +Allows X11 forwarding. +.Pp +.It Ic no-touch-required +Do not require signatures made using this key include demonstration +of user presence (e.g. by having the user touch the authenticator). +This option only makes sense for the FIDO authenticator algorithms +.Cm ecdsa-sk +and +.Cm ed25519-sk . +.Pp +.It Ic source-address Ns = Ns Ar address_list +Restrict the source addresses from which the certificate is considered valid. +The +.Ar address_list +is a comma-separated list of one or more address/netmask pairs in CIDR +format. +.Pp +.It Ic verify-required +Require signatures made using this key indicate that the user was first +verified. +This option only makes sense for the FIDO authenticator algorithms +.Cm ecdsa-sk +and +.Cm ed25519-sk . +Currently PIN authentication is the only supported verification method, +but other methods may be supported in the future. +.El +.Pp +At present, no standard options are valid for host keys. +.Pp +Finally, certificates may be defined with a validity lifetime. +The +.Fl V +option allows specification of certificate start and end times. +A certificate that is presented at a time outside this range will not be +considered valid. +By default, certificates are valid from the +.Ux +Epoch to the distant future. +.Pp +For certificates to be used for user or host authentication, the CA +public key must be trusted by +.Xr sshd 8 +or +.Xr ssh 1 . +Refer to those manual pages for details. +.Sh KEY REVOCATION LISTS +.Nm +is able to manage OpenSSH format Key Revocation Lists (KRLs). +These binary files specify keys or certificates to be revoked using a +compact format, taking as little as one bit per certificate if they are being +revoked by serial number. +.Pp +KRLs may be generated using the +.Fl k +flag. +This option reads one or more files from the command line and generates a new +KRL. +The files may either contain a KRL specification (see below) or public keys, +listed one per line. +Plain public keys are revoked by listing their hash or contents in the KRL and +certificates revoked by serial number or key ID (if the serial is zero or +not available). +.Pp +Revoking keys using a KRL specification offers explicit control over the +types of record used to revoke keys and may be used to directly revoke +certificates by serial number or key ID without having the complete original +certificate on hand. +A KRL specification consists of lines containing one of the following directives +followed by a colon and some directive-specific information. +.Bl -tag -width Ds +.It Cm serial : Ar serial_number Ns Op - Ns Ar serial_number +Revokes a certificate with the specified serial number. +Serial numbers are 64-bit values, not including zero and may be expressed +in decimal, hex or octal. +If two serial numbers are specified separated by a hyphen, then the range +of serial numbers including and between each is revoked. +The CA key must have been specified on the +.Nm +command line using the +.Fl s +option. +.It Cm id : Ar key_id +Revokes a certificate with the specified key ID string. +The CA key must have been specified on the +.Nm +command line using the +.Fl s +option. +.It Cm key : Ar public_key +Revokes the specified key. +If a certificate is listed, then it is revoked as a plain public key. +.It Cm sha1 : Ar public_key +Revokes the specified key by including its SHA1 hash in the KRL. +.It Cm sha256 : Ar public_key +Revokes the specified key by including its SHA256 hash in the KRL. +KRLs that revoke keys by SHA256 hash are not supported by OpenSSH versions +prior to 7.9. +.It Cm hash : Ar fingerprint +Revokes a key using a fingerprint hash, as obtained from a +.Xr sshd 8 +authentication log message or the +.Nm +.Fl l +flag. +Only SHA256 fingerprints are supported here and resultant KRLs are +not supported by OpenSSH versions prior to 7.9. +.El +.Pp +KRLs may be updated using the +.Fl u +flag in addition to +.Fl k . +When this option is specified, keys listed via the command line are merged into +the KRL, adding to those already there. +.Pp +It is also possible, given a KRL, to test whether it revokes a particular key +(or keys). +The +.Fl Q +flag will query an existing KRL, testing each key specified on the command line. +If any key listed on the command line has been revoked (or an error encountered) +then +.Nm +will exit with a non-zero exit status. +A zero exit status will only be returned if no key was revoked. +.Sh ALLOWED SIGNERS +When verifying signatures, +.Nm +uses a simple list of identities and keys to determine whether a signature +comes from an authorized source. +This "allowed signers" file uses a format patterned after the +AUTHORIZED_KEYS FILE FORMAT described in +.Xr sshd 8 . +Each line of the file contains the following space-separated fields: +principals, options, keytype, base64-encoded key. +Empty lines and lines starting with a +.Ql # +are ignored as comments. +.Pp +The principals field is a pattern-list (see PATTERNS in +.Xr ssh_config 5 ) +consisting of one or more comma-separated USER@DOMAIN identity patterns +that are accepted for signing. +When verifying, the identity presented via the +.Fl I +option must match a principals pattern in order for the corresponding key to be +considered acceptable for verification. +.Pp +The options (if present) consist of comma-separated option specifications. +No spaces are permitted, except within double quotes. +The following option specifications are supported (note that option keywords +are case-insensitive): +.Bl -tag -width Ds +.It Cm cert-authority +Indicates that this key is accepted as a certificate authority (CA) and +that certificates signed by this CA may be accepted for verification. +.It Cm namespaces Ns = Ns "namespace-list" +Specifies a pattern-list of namespaces that are accepted for this key. +If this option is present, the signature namespace embedded in the +signature object and presented on the verification command-line must +match the specified list before the key will be considered acceptable. +.It Cm valid-after Ns = Ns "timestamp" +Indicates that the key is valid for use at or after the specified timestamp, +which may be a date in YYYYMMDD format or a time in YYYYMMDDHHMM[SS] format. +.It Cm valid-before Ns = Ns "timestamp" +Indicates that the key is valid for use at or before the specified timestamp. +.El +.Pp +When verifying signatures made by certificates, the expected principal +name must match both the principals pattern in the allowed signers file and +the principals embedded in the certificate itself. +.Pp +An example allowed signers file: +.Bd -literal -offset 3n +# Comments allowed at start of line +user1@example.com,user2@example.com ssh-rsa AAAAX1... +# A certificate authority, trusted for all principals in a domain. +*@example.com cert-authority ssh-ed25519 AAAB4... +# A key that is accepted only for file signing. +user2@example.com namespaces="file" ssh-ed25519 AAA41... +.Ed +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev SSH_SK_PROVIDER +Specifies a path to a library that will be used when loading any +FIDO authenticator-hosted keys, overriding the default of using +the built-in USB HID support. +.El +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa ~/.ssh/id_dsa +.It Pa ~/.ssh/id_ecdsa +.It Pa ~/.ssh/id_ecdsa_sk +.It Pa ~/.ssh/id_ed25519 +.It Pa ~/.ssh/id_ed25519_sk +.It Pa ~/.ssh/id_rsa +Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, +authenticator-hosted Ed25519 or RSA authentication identity of the user. +This file should not be readable by anyone but the user. +It is possible to +specify a passphrase when generating the key; that passphrase will be +used to encrypt the private part of this file using 128-bit AES. +This file is not automatically accessed by +.Nm +but it is offered as the default file for the private key. +.Xr ssh 1 +will read this file when a login attempt is made. +.Pp +.It Pa ~/.ssh/id_dsa.pub +.It Pa ~/.ssh/id_ecdsa.pub +.It Pa ~/.ssh/id_ecdsa_sk.pub +.It Pa ~/.ssh/id_ed25519.pub +.It Pa ~/.ssh/id_ed25519_sk.pub +.It Pa ~/.ssh/id_rsa.pub +Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, +authenticator-hosted Ed25519 or RSA public key for authentication. +The contents of this file should be added to +.Pa ~/.ssh/authorized_keys +on all machines +where the user wishes to log in using public key authentication. +There is no need to keep the contents of this file secret. +.Pp +.It Pa /etc/moduli +Contains Diffie-Hellman groups used for DH-GEX. +The file format is described in +.Xr moduli 5 . +.El +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr moduli 5 , +.Xr sshd 8 +.Rs +.%R RFC 4716 +.%T "The Secure Shell (SSH) Public Key File Format" +.%D 2006 +.Re +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. diff --git a/aosp/external/openssh/ssh-keygen.c b/aosp/external/openssh/ssh-keygen.c new file mode 100644 index 0000000000000000000000000000000000000000..d62fab3e818a7016af904cbf8843022d25f3fa0d --- /dev/null +++ b/aosp/external/openssh/ssh-keygen.c @@ -0,0 +1,3868 @@ +/* $OpenBSD: ssh-keygen.c,v 1.450 2022/03/18 02:32:22 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Identity and host key generation and maintenance. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include "openbsd-compat/openssl-compat.h" +#endif + +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "sshkey.h" +#include "authfile.h" +#include "sshbuf.h" +#include "pathnames.h" +#include "log.h" +#include "misc.h" +#include "match.h" +#include "hostfile.h" +#include "dns.h" +#include "ssh.h" +#include "ssh2.h" +#include "ssherr.h" +#include "ssh-pkcs11.h" +#include "atomicio.h" +#include "krl.h" +#include "digest.h" +#include "utf8.h" +#include "authfd.h" +#include "sshsig.h" +#include "ssh-sk.h" +#include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */ +#include "cipher.h" + +#ifdef WITH_OPENSSL +# define DEFAULT_KEY_TYPE_NAME "rsa" +#else +# define DEFAULT_KEY_TYPE_NAME "ed25519" +#endif + +/* + * Default number of bits in the RSA, DSA and ECDSA keys. These value can be + * overridden on the command line. + * + * These values, with the exception of DSA, provide security equivalent to at + * least 128 bits of security according to NIST Special Publication 800-57: + * Recommendation for Key Management Part 1 rev 4 section 5.6.1. + * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for + * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only + * SHA1 we limit the DSA key size 1k bits. + */ +#define DEFAULT_BITS 3072 +#define DEFAULT_BITS_DSA 1024 +#define DEFAULT_BITS_ECDSA 256 + +static int quiet = 0; + +/* Flag indicating that we just want to see the key fingerprint */ +static int print_fingerprint = 0; +static int print_bubblebabble = 0; + +/* Hash algorithm to use for fingerprints. */ +static int fingerprint_hash = SSH_FP_HASH_DEFAULT; + +/* The identity file name, given on the command line or entered by the user. */ +static char identity_file[PATH_MAX]; +static int have_identity = 0; + +/* This is set to the passphrase if given on the command line. */ +static char *identity_passphrase = NULL; + +/* This is set to the new passphrase if given on the command line. */ +static char *identity_new_passphrase = NULL; + +/* Key type when certifying */ +static u_int cert_key_type = SSH2_CERT_TYPE_USER; + +/* "key ID" of signed key */ +static char *cert_key_id = NULL; + +/* Comma-separated list of principal names for certifying keys */ +static char *cert_principals = NULL; + +/* Validity period for certificates */ +static u_int64_t cert_valid_from = 0; +static u_int64_t cert_valid_to = ~0ULL; + +/* Certificate options */ +#define CERTOPT_X_FWD (1) +#define CERTOPT_AGENT_FWD (1<<1) +#define CERTOPT_PORT_FWD (1<<2) +#define CERTOPT_PTY (1<<3) +#define CERTOPT_USER_RC (1<<4) +#define CERTOPT_NO_REQUIRE_USER_PRESENCE (1<<5) +#define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ + CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) +static u_int32_t certflags_flags = CERTOPT_DEFAULT; +static char *certflags_command = NULL; +static char *certflags_src_addr = NULL; + +/* Arbitrary extensions specified by user */ +struct cert_ext { + char *key; + char *val; + int crit; +}; +static struct cert_ext *cert_ext; +static size_t ncert_ext; + +/* Conversion to/from various formats */ +enum { + FMT_RFC4716, + FMT_PKCS8, + FMT_PEM +} convert_format = FMT_RFC4716; + +static char *key_type_name = NULL; + +/* Load key from this PKCS#11 provider */ +static char *pkcs11provider = NULL; + +/* FIDO/U2F provider to use */ +static char *sk_provider = NULL; + +/* Format for writing private keys */ +static int private_key_format = SSHKEY_PRIVATE_OPENSSH; + +/* Cipher for new-format private keys */ +static char *openssh_format_cipher = NULL; + +/* Number of KDF rounds to derive new format keys. */ +static int rounds = 0; + +/* argv0 */ +extern char *__progname; + +static char hostname[NI_MAXHOST]; + +#ifdef WITH_OPENSSL +/* moduli.c */ +int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); +int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, + unsigned long); +#endif + +static void +type_bits_valid(int type, const char *name, u_int32_t *bitsp) +{ + if (type == KEY_UNSPEC) + fatal("unknown key type %s", key_type_name); + if (*bitsp == 0) { +#ifdef WITH_OPENSSL + int nid; + + switch(type) { + case KEY_DSA: + *bitsp = DEFAULT_BITS_DSA; + break; + case KEY_ECDSA: + if (name != NULL && + (nid = sshkey_ecdsa_nid_from_name(name)) > 0) + *bitsp = sshkey_curve_nid_to_bits(nid); + if (*bitsp == 0) + *bitsp = DEFAULT_BITS_ECDSA; + break; + case KEY_RSA: + *bitsp = DEFAULT_BITS; + break; + } +#endif + } +#ifdef WITH_OPENSSL + switch (type) { + case KEY_DSA: + if (*bitsp != 1024) + fatal("Invalid DSA key length: must be 1024 bits"); + break; + case KEY_RSA: + if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE) + fatal("Invalid RSA key length: minimum is %d bits", + SSH_RSA_MINIMUM_MODULUS_SIZE); + else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS) + fatal("Invalid RSA key length: maximum is %d bits", + OPENSSL_RSA_MAX_MODULUS_BITS); + break; + case KEY_ECDSA: + if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1) +#ifdef OPENSSL_HAS_NISTP521 + fatal("Invalid ECDSA key length: valid lengths are " + "256, 384 or 521 bits"); +#else + fatal("Invalid ECDSA key length: valid lengths are " + "256 or 384 bits"); +#endif + } +#endif +} + +/* + * Checks whether a file exists and, if so, asks the user whether they wish + * to overwrite it. + * Returns nonzero if the file does not already exist or if the user agrees to + * overwrite, or zero otherwise. + */ +static int +confirm_overwrite(const char *filename) +{ + char yesno[3]; + struct stat st; + + if (stat(filename, &st) != 0) + return 1; + printf("%s already exists.\n", filename); + printf("Overwrite (y/n)? "); + fflush(stdout); + if (fgets(yesno, sizeof(yesno), stdin) == NULL) + return 0; + if (yesno[0] != 'y' && yesno[0] != 'Y') + return 0; + return 1; +} + +static void +ask_filename(struct passwd *pw, const char *prompt) +{ + char buf[1024]; + char *name = NULL; + + if (key_type_name == NULL) + name = _PATH_SSH_CLIENT_ID_RSA; + else { + switch (sshkey_type_from_name(key_type_name)) { + case KEY_DSA_CERT: + case KEY_DSA: + name = _PATH_SSH_CLIENT_ID_DSA; + break; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + name = _PATH_SSH_CLIENT_ID_ECDSA; + break; + case KEY_ECDSA_SK_CERT: + case KEY_ECDSA_SK: + name = _PATH_SSH_CLIENT_ID_ECDSA_SK; + break; +#endif + case KEY_RSA_CERT: + case KEY_RSA: + name = _PATH_SSH_CLIENT_ID_RSA; + break; + case KEY_ED25519: + case KEY_ED25519_CERT: + name = _PATH_SSH_CLIENT_ID_ED25519; + break; + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + name = _PATH_SSH_CLIENT_ID_ED25519_SK; + break; + case KEY_XMSS: + case KEY_XMSS_CERT: + name = _PATH_SSH_CLIENT_ID_XMSS; + break; + default: + fatal("bad key type"); + } + } + snprintf(identity_file, sizeof(identity_file), + "%s/%s", pw->pw_dir, name); + printf("%s (%s): ", prompt, identity_file); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + buf[strcspn(buf, "\n")] = '\0'; + if (strcmp(buf, "") != 0) + strlcpy(identity_file, buf, sizeof(identity_file)); + have_identity = 1; +} + +static struct sshkey * +load_identity(const char *filename, char **commentp) +{ + char *pass; + struct sshkey *prv; + int r; + + if (commentp != NULL) + *commentp = NULL; + if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0) + return prv; + if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal_r(r, "Load key \"%s\"", filename); + if (identity_passphrase) + pass = xstrdup(identity_passphrase); + else + pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); + r = sshkey_load_private(filename, pass, &prv, commentp); + freezero(pass, strlen(pass)); + if (r != 0) + fatal_r(r, "Load key \"%s\"", filename); + return prv; +} + +#define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" +#define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" +#define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" +#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb + +#ifdef WITH_OPENSSL +static void +do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) +{ + struct sshbuf *b; + char comment[61], *b64; + int r; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshkey_putb(k, b)) != 0) + fatal_fr(r, "put key"); + if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL) + fatal_f("sshbuf_dtob64_string failed"); + + /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ + snprintf(comment, sizeof(comment), + "%u-bit %s, converted by %s@%s from OpenSSH", + sshkey_size(k), sshkey_type(k), + pw->pw_name, hostname); + + sshkey_free(k); + sshbuf_free(b); + + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); + fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64); + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); + free(b64); + exit(0); +} + +static void +do_convert_to_pkcs8(struct sshkey *k) +{ + switch (sshkey_type_plain(k->type)) { + case KEY_RSA: + if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) + fatal("PEM_write_RSA_PUBKEY failed"); + break; + case KEY_DSA: + if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) + fatal("PEM_write_DSA_PUBKEY failed"); + break; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) + fatal("PEM_write_EC_PUBKEY failed"); + break; +#endif + default: + fatal_f("unsupported key type %s", sshkey_type(k)); + } + exit(0); +} + +static void +do_convert_to_pem(struct sshkey *k) +{ + switch (sshkey_type_plain(k->type)) { + case KEY_RSA: + if (!PEM_write_RSAPublicKey(stdout, k->rsa)) + fatal("PEM_write_RSAPublicKey failed"); + break; + case KEY_DSA: + if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) + fatal("PEM_write_DSA_PUBKEY failed"); + break; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) + fatal("PEM_write_EC_PUBKEY failed"); + break; +#endif + default: + fatal_f("unsupported key type %s", sshkey_type(k)); + } + exit(0); +} + +static void +do_convert_to(struct passwd *pw) +{ + struct sshkey *k; + struct stat st; + int r; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) == -1) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) + k = load_identity(identity_file, NULL); + switch (convert_format) { + case FMT_RFC4716: + do_convert_to_ssh2(pw, k); + break; + case FMT_PKCS8: + do_convert_to_pkcs8(k); + break; + case FMT_PEM: + do_convert_to_pem(k); + break; + default: + fatal_f("unknown key format %d", convert_format); + } + exit(0); +} + +/* + * This is almost exactly the bignum1 encoding, but with 32 bit for length + * instead of 16. + */ +static void +buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value) +{ + u_int bytes, bignum_bits; + int r; + + if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0) + fatal_fr(r, "parse"); + bytes = (bignum_bits + 7) / 8; + if (sshbuf_len(b) < bytes) + fatal_f("input buffer too small: need %d have %zu", + bytes, sshbuf_len(b)); + if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL) + fatal_f("BN_bin2bn failed"); + if ((r = sshbuf_consume(b, bytes)) != 0) + fatal_fr(r, "consume"); +} + +static struct sshkey * +do_convert_private_ssh2(struct sshbuf *b) +{ + struct sshkey *key = NULL; + char *type, *cipher; + u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; + int r, rlen, ktype; + u_int magic, i1, i2, i3, i4; + size_t slen; + u_long e; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; + BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL; + BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; + BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL; + + if ((r = sshbuf_get_u32(b, &magic)) != 0) + fatal_fr(r, "parse magic"); + + if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { + error("bad magic 0x%x != 0x%x", magic, + SSH_COM_PRIVATE_KEY_MAGIC); + return NULL; + } + if ((r = sshbuf_get_u32(b, &i1)) != 0 || + (r = sshbuf_get_cstring(b, &type, NULL)) != 0 || + (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 || + (r = sshbuf_get_u32(b, &i2)) != 0 || + (r = sshbuf_get_u32(b, &i3)) != 0 || + (r = sshbuf_get_u32(b, &i4)) != 0) + fatal_fr(r, "parse"); + debug("ignore (%d %d %d %d)", i1, i2, i3, i4); + if (strcmp(cipher, "none") != 0) { + error("unsupported cipher %s", cipher); + free(cipher); + free(type); + return NULL; + } + free(cipher); + + if (strstr(type, "dsa")) { + ktype = KEY_DSA; + } else if (strstr(type, "rsa")) { + ktype = KEY_RSA; + } else { + free(type); + return NULL; + } + if ((key = sshkey_new(ktype)) == NULL) + fatal("sshkey_new failed"); + free(type); + + switch (key->type) { + case KEY_DSA: + if ((dsa_p = BN_new()) == NULL || + (dsa_q = BN_new()) == NULL || + (dsa_g = BN_new()) == NULL || + (dsa_pub_key = BN_new()) == NULL || + (dsa_priv_key = BN_new()) == NULL) + fatal_f("BN_new"); + buffer_get_bignum_bits(b, dsa_p); + buffer_get_bignum_bits(b, dsa_g); + buffer_get_bignum_bits(b, dsa_q); + buffer_get_bignum_bits(b, dsa_pub_key); + buffer_get_bignum_bits(b, dsa_priv_key); + if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) + fatal_f("DSA_set0_pqg failed"); + dsa_p = dsa_q = dsa_g = NULL; /* transferred */ + if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key)) + fatal_f("DSA_set0_key failed"); + dsa_pub_key = dsa_priv_key = NULL; /* transferred */ + break; + case KEY_RSA: + if ((r = sshbuf_get_u8(b, &e1)) != 0 || + (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || + (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) + fatal_fr(r, "parse RSA"); + e = e1; + debug("e %lx", e); + if (e < 30) { + e <<= 8; + e += e2; + debug("e %lx", e); + e <<= 8; + e += e3; + debug("e %lx", e); + } + if ((rsa_e = BN_new()) == NULL) + fatal_f("BN_new"); + if (!BN_set_word(rsa_e, e)) { + BN_clear_free(rsa_e); + sshkey_free(key); + return NULL; + } + if ((rsa_n = BN_new()) == NULL || + (rsa_d = BN_new()) == NULL || + (rsa_p = BN_new()) == NULL || + (rsa_q = BN_new()) == NULL || + (rsa_iqmp = BN_new()) == NULL) + fatal_f("BN_new"); + buffer_get_bignum_bits(b, rsa_d); + buffer_get_bignum_bits(b, rsa_n); + buffer_get_bignum_bits(b, rsa_iqmp); + buffer_get_bignum_bits(b, rsa_q); + buffer_get_bignum_bits(b, rsa_p); + if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d)) + fatal_f("RSA_set0_key failed"); + rsa_n = rsa_e = rsa_d = NULL; /* transferred */ + if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q)) + fatal_f("RSA_set0_factors failed"); + rsa_p = rsa_q = NULL; /* transferred */ + if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0) + fatal_fr(r, "generate RSA parameters"); + BN_clear_free(rsa_iqmp); + break; + } + rlen = sshbuf_len(b); + if (rlen != 0) + error_f("remaining bytes in key blob %d", rlen); + + /* try the key */ + if (sshkey_sign(key, &sig, &slen, data, sizeof(data), + NULL, NULL, NULL, 0) != 0 || + sshkey_verify(key, sig, slen, data, sizeof(data), + NULL, 0, NULL) != 0) { + sshkey_free(key); + free(sig); + return NULL; + } + free(sig); + return key; +} + +static int +get_line(FILE *fp, char *line, size_t len) +{ + int c; + size_t pos = 0; + + line[0] = '\0'; + while ((c = fgetc(fp)) != EOF) { + if (pos >= len - 1) + fatal("input line too long."); + switch (c) { + case '\r': + c = fgetc(fp); + if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) + fatal("unget: %s", strerror(errno)); + return pos; + case '\n': + return pos; + } + line[pos++] = c; + line[pos] = '\0'; + } + /* We reached EOF */ + return -1; +} + +static void +do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) +{ + int r, blen, escaped = 0; + u_int len; + char line[1024]; + struct sshbuf *buf; + char encoded[8096]; + FILE *fp; + + if ((buf = sshbuf_new()) == NULL) + fatal("sshbuf_new failed"); + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + encoded[0] = '\0'; + while ((blen = get_line(fp, line, sizeof(line))) != -1) { + if (blen > 0 && line[blen - 1] == '\\') + escaped++; + if (strncmp(line, "----", 4) == 0 || + strstr(line, ": ") != NULL) { + if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) + *private = 1; + if (strstr(line, " END ") != NULL) { + break; + } + /* fprintf(stderr, "ignore: %s", line); */ + continue; + } + if (escaped) { + escaped--; + /* fprintf(stderr, "escaped: %s", line); */ + continue; + } + strlcat(encoded, line, sizeof(encoded)); + } + len = strlen(encoded); + if (((len % 4) == 3) && + (encoded[len-1] == '=') && + (encoded[len-2] == '=') && + (encoded[len-3] == '=')) + encoded[len-3] = '\0'; + if ((r = sshbuf_b64tod(buf, encoded)) != 0) + fatal_fr(r, "base64 decode"); + if (*private) { + if ((*k = do_convert_private_ssh2(buf)) == NULL) + fatal_f("private key conversion failed"); + } else if ((r = sshkey_fromb(buf, k)) != 0) + fatal_fr(r, "parse key"); + sshbuf_free(buf); + fclose(fp); +} + +static void +do_convert_from_pkcs8(struct sshkey **k, int *private) +{ + EVP_PKEY *pubkey; + FILE *fp; + + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { + fatal_f("%s is not a recognised public key format", + identity_file); + } + fclose(fp); + switch (EVP_PKEY_base_id(pubkey)) { + case EVP_PKEY_RSA: + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + (*k)->type = KEY_RSA; + (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); + break; + case EVP_PKEY_DSA: + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + (*k)->type = KEY_DSA; + (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); + break; +#ifdef OPENSSL_HAS_ECC + case EVP_PKEY_EC: + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + (*k)->type = KEY_ECDSA; + (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); + (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa); + break; +#endif + default: + fatal_f("unsupported pubkey type %d", + EVP_PKEY_base_id(pubkey)); + } + EVP_PKEY_free(pubkey); + return; +} + +static void +do_convert_from_pem(struct sshkey **k, int *private) +{ + FILE *fp; + RSA *rsa; + + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + (*k)->type = KEY_RSA; + (*k)->rsa = rsa; + fclose(fp); + return; + } + fatal_f("unrecognised raw private key format"); +} + +static void +do_convert_from(struct passwd *pw) +{ + struct sshkey *k = NULL; + int r, private = 0, ok = 0; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) == -1) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + + switch (convert_format) { + case FMT_RFC4716: + do_convert_from_ssh2(pw, &k, &private); + break; + case FMT_PKCS8: + do_convert_from_pkcs8(&k, &private); + break; + case FMT_PEM: + do_convert_from_pem(&k, &private); + break; + default: + fatal_f("unknown key format %d", convert_format); + } + + if (!private) { + if ((r = sshkey_write(k, stdout)) == 0) + ok = 1; + if (ok) + fprintf(stdout, "\n"); + } else { + switch (k->type) { + case KEY_DSA: + ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, + NULL, 0, NULL, NULL); + break; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, + NULL, 0, NULL, NULL); + break; +#endif + case KEY_RSA: + ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, + NULL, 0, NULL, NULL); + break; + default: + fatal_f("unsupported key type %s", sshkey_type(k)); + } + } + + if (!ok) + fatal("key write failed"); + sshkey_free(k); + exit(0); +} +#endif + +static void +do_print_public(struct passwd *pw) +{ + struct sshkey *prv; + struct stat st; + int r; + char *comment = NULL; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) == -1) + fatal("%s: %s", identity_file, strerror(errno)); + prv = load_identity(identity_file, &comment); + if ((r = sshkey_write(prv, stdout)) != 0) + fatal_fr(r, "write key"); + if (comment != NULL && *comment != '\0') + fprintf(stdout, " %s", comment); + fprintf(stdout, "\n"); + if (sshkey_is_sk(prv)) { + debug("sk_application: \"%s\", sk_flags 0x%02x", + prv->sk_application, prv->sk_flags); + } + sshkey_free(prv); + free(comment); + exit(0); +} + +static void +do_download(struct passwd *pw) +{ +#ifdef ENABLE_PKCS11 + struct sshkey **keys = NULL; + int i, nkeys; + enum sshkey_fp_rep rep; + int fptype; + char *fp, *ra, **comments = NULL; + + fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; + + pkcs11_init(1); + nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments); + if (nkeys <= 0) + fatal("cannot read public key from pkcs11"); + for (i = 0; i < nkeys; i++) { + if (print_fingerprint) { + fp = sshkey_fingerprint(keys[i], fptype, rep); + ra = sshkey_fingerprint(keys[i], fingerprint_hash, + SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal_f("sshkey_fingerprint fail"); + printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]), + fp, sshkey_type(keys[i])); + if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) + printf("%s\n", ra); + free(ra); + free(fp); + } else { + (void) sshkey_write(keys[i], stdout); /* XXX check */ + fprintf(stdout, "%s%s\n", + *(comments[i]) == '\0' ? "" : " ", comments[i]); + } + free(comments[i]); + sshkey_free(keys[i]); + } + free(comments); + free(keys); + pkcs11_terminate(); + exit(0); +#else + fatal("no pkcs11 support"); +#endif /* ENABLE_PKCS11 */ +} + +static struct sshkey * +try_read_key(char **cpp) +{ + struct sshkey *ret; + int r; + + if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + if ((r = sshkey_read(ret, cpp)) == 0) + return ret; + /* Not a key */ + sshkey_free(ret); + return NULL; +} + +static void +fingerprint_one_key(const struct sshkey *public, const char *comment) +{ + char *fp = NULL, *ra = NULL; + enum sshkey_fp_rep rep; + int fptype; + + fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; + fp = sshkey_fingerprint(public, fptype, rep); + ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal_f("sshkey_fingerprint failed"); + mprintf("%u %s %s (%s)\n", sshkey_size(public), fp, + comment ? comment : "no comment", sshkey_type(public)); + if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) + printf("%s\n", ra); + free(ra); + free(fp); +} + +static void +fingerprint_private(const char *path) +{ + struct stat st; + char *comment = NULL; + struct sshkey *privkey = NULL, *pubkey = NULL; + int r; + + if (stat(identity_file, &st) == -1) + fatal("%s: %s", path, strerror(errno)); + if ((r = sshkey_load_public(path, &pubkey, &comment)) != 0) + debug_r(r, "load public \"%s\"", path); + if (pubkey == NULL || comment == NULL || *comment == '\0') { + free(comment); + if ((r = sshkey_load_private(path, NULL, + &privkey, &comment)) != 0) + debug_r(r, "load private \"%s\"", path); + } + if (pubkey == NULL && privkey == NULL) + fatal("%s is not a key file.", path); + + fingerprint_one_key(pubkey == NULL ? privkey : pubkey, comment); + sshkey_free(pubkey); + sshkey_free(privkey); + free(comment); +} + +static void +do_fingerprint(struct passwd *pw) +{ + FILE *f; + struct sshkey *public = NULL; + char *comment = NULL, *cp, *ep, *line = NULL; + size_t linesize = 0; + int i, invalid = 1; + const char *path; + u_long lnum = 0; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + path = identity_file; + + if (strcmp(identity_file, "-") == 0) { + f = stdin; + path = "(stdin)"; + } else if ((f = fopen(path, "r")) == NULL) + fatal("%s: %s: %s", __progname, path, strerror(errno)); + + while (getline(&line, &linesize, f) != -1) { + lnum++; + cp = line; + cp[strcspn(cp, "\n")] = '\0'; + /* Trim leading space and comments */ + cp = line + strspn(line, " \t"); + if (*cp == '#' || *cp == '\0') + continue; + + /* + * Input may be plain keys, private keys, authorized_keys + * or known_hosts. + */ + + /* + * Try private keys first. Assume a key is private if + * "SSH PRIVATE KEY" appears on the first line and we're + * not reading from stdin (XXX support private keys on stdin). + */ + if (lnum == 1 && strcmp(identity_file, "-") != 0 && + strstr(cp, "PRIVATE KEY") != NULL) { + free(line); + fclose(f); + fingerprint_private(path); + exit(0); + } + + /* + * If it's not a private key, then this must be prepared to + * accept a public key prefixed with a hostname or options. + * Try a bare key first, otherwise skip the leading stuff. + */ + if ((public = try_read_key(&cp)) == NULL) { + i = strtol(cp, &ep, 10); + if (i == 0 || ep == NULL || + (*ep != ' ' && *ep != '\t')) { + int quoted = 0; + + comment = cp; + for (; *cp && (quoted || (*cp != ' ' && + *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + if (!*cp) + continue; + *cp++ = '\0'; + } + } + /* Retry after parsing leading hostname/key options */ + if (public == NULL && (public = try_read_key(&cp)) == NULL) { + debug("%s:%lu: not a public key", path, lnum); + continue; + } + + /* Find trailing comment, if any */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + if (*cp != '\0' && *cp != '#') + comment = cp; + + fingerprint_one_key(public, comment); + sshkey_free(public); + invalid = 0; /* One good key in the file is sufficient */ + } + fclose(f); + free(line); + + if (invalid) + fatal("%s is not a public key file.", path); + exit(0); +} + +static void +do_gen_all_hostkeys(struct passwd *pw) +{ + struct { + char *key_type; + char *key_type_display; + char *path; + } key_types[] = { +#ifdef WITH_OPENSSL + { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, + { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, +#ifdef OPENSSL_HAS_ECC + { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, +#ifdef WITH_XMSS + { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE }, +#endif /* WITH_XMSS */ + { NULL, NULL, NULL } + }; + + u_int32_t bits = 0; + int first = 0; + struct stat st; + struct sshkey *private, *public; + char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file; + int i, type, fd, r; + + for (i = 0; key_types[i].key_type; i++) { + public = private = NULL; + prv_tmp = pub_tmp = prv_file = pub_file = NULL; + + xasprintf(&prv_file, "%s%s", + identity_file, key_types[i].path); + + /* Check whether private key exists and is not zero-length */ + if (stat(prv_file, &st) == 0) { + if (st.st_size != 0) + goto next; + } else if (errno != ENOENT) { + error("Could not stat %s: %s", key_types[i].path, + strerror(errno)); + goto failnext; + } + + /* + * Private key doesn't exist or is invalid; proceed with + * key generation. + */ + xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX", + identity_file, key_types[i].path); + xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX", + identity_file, key_types[i].path); + xasprintf(&pub_file, "%s%s.pub", + identity_file, key_types[i].path); + + if (first == 0) { + first = 1; + printf("%s: generating new host keys: ", __progname); + } + printf("%s ", key_types[i].key_type_display); + fflush(stdout); + type = sshkey_type_from_name(key_types[i].key_type); + if ((fd = mkstemp(prv_tmp)) == -1) { + error("Could not save your private key in %s: %s", + prv_tmp, strerror(errno)); + goto failnext; + } + (void)close(fd); /* just using mkstemp() to reserve a name */ + bits = 0; + type_bits_valid(type, NULL, &bits); + if ((r = sshkey_generate(type, bits, &private)) != 0) { + error_r(r, "sshkey_generate failed"); + goto failnext; + } + if ((r = sshkey_from_private(private, &public)) != 0) + fatal_fr(r, "sshkey_from_private"); + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, + hostname); + if ((r = sshkey_save_private(private, prv_tmp, "", + comment, private_key_format, openssh_format_cipher, + rounds)) != 0) { + error_r(r, "Saving key \"%s\" failed", prv_tmp); + goto failnext; + } + if ((fd = mkstemp(pub_tmp)) == -1) { + error("Could not save your public key in %s: %s", + pub_tmp, strerror(errno)); + goto failnext; + } + (void)fchmod(fd, 0644); + (void)close(fd); + if ((r = sshkey_save_public(public, pub_tmp, comment)) != 0) { + error_r(r, "Unable to save public key to %s", + identity_file); + goto failnext; + } + + /* Rename temporary files to their permanent locations. */ + if (rename(pub_tmp, pub_file) != 0) { + error("Unable to move %s into position: %s", + pub_file, strerror(errno)); + goto failnext; + } + if (rename(prv_tmp, prv_file) != 0) { + error("Unable to move %s into position: %s", + key_types[i].path, strerror(errno)); + failnext: + first = 0; + goto next; + } + next: + sshkey_free(private); + sshkey_free(public); + free(prv_tmp); + free(pub_tmp); + free(prv_file); + free(pub_file); + } + if (first != 0) + printf("\n"); +} + +struct known_hosts_ctx { + const char *host; /* Hostname searched for in find/delete case */ + FILE *out; /* Output file, stdout for find_hosts case */ + int has_unhashed; /* When hashing, original had unhashed hosts */ + int found_key; /* For find/delete, host was found */ + int invalid; /* File contained invalid items; don't delete */ + int hash_hosts; /* Hash hostnames as we go */ + int find_host; /* Search for specific hostname */ + int delete_host; /* Delete host from known_hosts */ +}; + +static int +known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) +{ + struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; + char *hashed, *cp, *hosts, *ohosts; + int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); + int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM; + + switch (l->status) { + case HKF_STATUS_OK: + case HKF_STATUS_MATCHED: + /* + * Don't hash hosts already already hashed, with wildcard + * characters or a CA/revocation marker. + */ + if (was_hashed || has_wild || l->marker != MRK_NONE) { + fprintf(ctx->out, "%s\n", l->line); + if (has_wild && !ctx->find_host) { + logit("%s:%lu: ignoring host name " + "with wildcard: %.64s", l->path, + l->linenum, l->hosts); + } + return 0; + } + /* + * Split any comma-separated hostnames from the host list, + * hash and store separately. + */ + ohosts = hosts = xstrdup(l->hosts); + while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { + lowercase(cp); + if ((hashed = host_hash(cp, NULL, 0)) == NULL) + fatal("hash_host failed"); + fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); + free(hashed); + ctx->has_unhashed = 1; + } + free(ohosts); + return 0; + case HKF_STATUS_INVALID: + /* Retain invalid lines, but mark file as invalid. */ + ctx->invalid = 1; + logit("%s:%lu: invalid line", l->path, l->linenum); + /* FALLTHROUGH */ + default: + fprintf(ctx->out, "%s\n", l->line); + return 0; + } + /* NOTREACHED */ + return -1; +} + +static int +known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) +{ + struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; + enum sshkey_fp_rep rep; + int fptype; + char *fp = NULL, *ra = NULL; + + fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; + + if (l->status == HKF_STATUS_MATCHED) { + if (ctx->delete_host) { + if (l->marker != MRK_NONE) { + /* Don't remove CA and revocation lines */ + fprintf(ctx->out, "%s\n", l->line); + } else { + /* + * Hostname matches and has no CA/revoke + * marker, delete it by *not* writing the + * line to ctx->out. + */ + ctx->found_key = 1; + if (!quiet) + printf("# Host %s found: line %lu\n", + ctx->host, l->linenum); + } + return 0; + } else if (ctx->find_host) { + ctx->found_key = 1; + if (!quiet) { + printf("# Host %s found: line %lu %s\n", + ctx->host, + l->linenum, l->marker == MRK_CA ? "CA" : + (l->marker == MRK_REVOKE ? "REVOKED" : "")); + } + if (ctx->hash_hosts) + known_hosts_hash(l, ctx); + else if (print_fingerprint) { + fp = sshkey_fingerprint(l->key, fptype, rep); + ra = sshkey_fingerprint(l->key, + fingerprint_hash, SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal_f("sshkey_fingerprint failed"); + mprintf("%s %s %s%s%s\n", ctx->host, + sshkey_type(l->key), fp, + l->comment[0] ? " " : "", + l->comment); + if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) + printf("%s\n", ra); + free(ra); + free(fp); + } else + fprintf(ctx->out, "%s\n", l->line); + return 0; + } + } else if (ctx->delete_host) { + /* Retain non-matching hosts when deleting */ + if (l->status == HKF_STATUS_INVALID) { + ctx->invalid = 1; + logit("%s:%lu: invalid line", l->path, l->linenum); + } + fprintf(ctx->out, "%s\n", l->line); + } + return 0; +} + +static void +do_known_hosts(struct passwd *pw, const char *name, int find_host, + int delete_host, int hash_hosts) +{ + char *cp, tmp[PATH_MAX], old[PATH_MAX]; + int r, fd, oerrno, inplace = 0; + struct known_hosts_ctx ctx; + u_int foreach_options; + struct stat sb; + + if (!have_identity) { + cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); + if (strlcpy(identity_file, cp, sizeof(identity_file)) >= + sizeof(identity_file)) + fatal("Specified known hosts path too long"); + free(cp); + have_identity = 1; + } + if (stat(identity_file, &sb) != 0) + fatal("Cannot stat %s: %s", identity_file, strerror(errno)); + + memset(&ctx, 0, sizeof(ctx)); + ctx.out = stdout; + ctx.host = name; + ctx.hash_hosts = hash_hosts; + ctx.find_host = find_host; + ctx.delete_host = delete_host; + + /* + * Find hosts goes to stdout, hash and deletions happen in-place + * A corner case is ssh-keygen -HF foo, which should go to stdout + */ + if (!find_host && (hash_hosts || delete_host)) { + if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || + strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || + strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || + strlcat(old, ".old", sizeof(old)) >= sizeof(old)) + fatal("known_hosts path too long"); + umask(077); + if ((fd = mkstemp(tmp)) == -1) + fatal("mkstemp: %s", strerror(errno)); + if ((ctx.out = fdopen(fd, "w")) == NULL) { + oerrno = errno; + unlink(tmp); + fatal("fdopen: %s", strerror(oerrno)); + } + fchmod(fd, sb.st_mode & 0644); + inplace = 1; + } + /* XXX support identity_file == "-" for stdin */ + foreach_options = find_host ? HKF_WANT_MATCH : 0; + foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0; + if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ? + known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL, + foreach_options, 0)) != 0) { + if (inplace) + unlink(tmp); + fatal_fr(r, "hostkeys_foreach"); + } + + if (inplace) + fclose(ctx.out); + + if (ctx.invalid) { + error("%s is not a valid known_hosts file.", identity_file); + if (inplace) { + error("Not replacing existing known_hosts " + "file because of errors"); + unlink(tmp); + } + exit(1); + } else if (delete_host && !ctx.found_key) { + logit("Host %s not found in %s", name, identity_file); + if (inplace) + unlink(tmp); + } else if (inplace) { + /* Backup existing file */ + if (unlink(old) == -1 && errno != ENOENT) + fatal("unlink %.100s: %s", old, strerror(errno)); + if (link(identity_file, old) == -1) + fatal("link %.100s to %.100s: %s", identity_file, old, + strerror(errno)); + /* Move new one into place */ + if (rename(tmp, identity_file) == -1) { + error("rename\"%s\" to \"%s\": %s", tmp, identity_file, + strerror(errno)); + unlink(tmp); + unlink(old); + exit(1); + } + + printf("%s updated.\n", identity_file); + printf("Original contents retained as %s\n", old); + if (ctx.has_unhashed) { + logit("WARNING: %s contains unhashed entries", old); + logit("Delete this file to ensure privacy " + "of hostnames"); + } + } + + exit (find_host && !ctx.found_key); +} + +/* + * Perform changing a passphrase. The argument is the passwd structure + * for the current user. + */ +static void +do_change_passphrase(struct passwd *pw) +{ + char *comment; + char *old_passphrase, *passphrase1, *passphrase2; + struct stat st; + struct sshkey *private; + int r; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) == -1) + fatal("%s: %s", identity_file, strerror(errno)); + /* Try to load the file with empty passphrase. */ + r = sshkey_load_private(identity_file, "", &private, &comment); + if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) { + if (identity_passphrase) + old_passphrase = xstrdup(identity_passphrase); + else + old_passphrase = + read_passphrase("Enter old passphrase: ", + RP_ALLOW_STDIN); + r = sshkey_load_private(identity_file, old_passphrase, + &private, &comment); + freezero(old_passphrase, strlen(old_passphrase)); + if (r != 0) + goto badkey; + } else if (r != 0) { + badkey: + fatal_r(r, "Failed to load key %s", identity_file); + } + if (comment) + mprintf("Key has comment '%s'\n", comment); + + /* Ask the new passphrase (twice). */ + if (identity_new_passphrase) { + passphrase1 = xstrdup(identity_new_passphrase); + passphrase2 = NULL; + } else { + passphrase1 = + read_passphrase("Enter new passphrase (empty for no " + "passphrase): ", RP_ALLOW_STDIN); + passphrase2 = read_passphrase("Enter same passphrase again: ", + RP_ALLOW_STDIN); + + /* Verify that they are the same. */ + if (strcmp(passphrase1, passphrase2) != 0) { + explicit_bzero(passphrase1, strlen(passphrase1)); + explicit_bzero(passphrase2, strlen(passphrase2)); + free(passphrase1); + free(passphrase2); + printf("Pass phrases do not match. Try again.\n"); + exit(1); + } + /* Destroy the other copy. */ + freezero(passphrase2, strlen(passphrase2)); + } + + /* Save the file using the new passphrase. */ + if ((r = sshkey_save_private(private, identity_file, passphrase1, + comment, private_key_format, openssh_format_cipher, rounds)) != 0) { + error_r(r, "Saving key \"%s\" failed", identity_file); + freezero(passphrase1, strlen(passphrase1)); + sshkey_free(private); + free(comment); + exit(1); + } + /* Destroy the passphrase and the copy of the key in memory. */ + freezero(passphrase1, strlen(passphrase1)); + sshkey_free(private); /* Destroys contents */ + free(comment); + + printf("Your identification has been saved with the new passphrase.\n"); + exit(0); +} + +/* + * Print the SSHFP RR. + */ +static int +do_print_resource_record(struct passwd *pw, char *fname, char *hname, + int print_generic) +{ + struct sshkey *public; + char *comment = NULL; + struct stat st; + int r; + + if (fname == NULL) + fatal_f("no filename"); + if (stat(fname, &st) == -1) { + if (errno == ENOENT) + return 0; + fatal("%s: %s", fname, strerror(errno)); + } + if ((r = sshkey_load_public(fname, &public, &comment)) != 0) + fatal_r(r, "Failed to read v2 public key from \"%s\"", fname); + export_dns_rr(hname, public, stdout, print_generic); + sshkey_free(public); + free(comment); + return 1; +} + +/* + * Change the comment of a private key file. + */ +static void +do_change_comment(struct passwd *pw, const char *identity_comment) +{ + char new_comment[1024], *comment, *passphrase; + struct sshkey *private; + struct sshkey *public; + struct stat st; + int r; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) == -1) + fatal("%s: %s", identity_file, strerror(errno)); + if ((r = sshkey_load_private(identity_file, "", + &private, &comment)) == 0) + passphrase = xstrdup(""); + else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal_r(r, "Cannot load private key \"%s\"", identity_file); + else { + if (identity_passphrase) + passphrase = xstrdup(identity_passphrase); + else if (identity_new_passphrase) + passphrase = xstrdup(identity_new_passphrase); + else + passphrase = read_passphrase("Enter passphrase: ", + RP_ALLOW_STDIN); + /* Try to load using the passphrase. */ + if ((r = sshkey_load_private(identity_file, passphrase, + &private, &comment)) != 0) { + freezero(passphrase, strlen(passphrase)); + fatal_r(r, "Cannot load private key \"%s\"", + identity_file); + } + } + + if (private->type != KEY_ED25519 && private->type != KEY_XMSS && + private_key_format != SSHKEY_PRIVATE_OPENSSH) { + error("Comments are only supported for keys stored in " + "the new format (-o)."); + explicit_bzero(passphrase, strlen(passphrase)); + sshkey_free(private); + exit(1); + } + if (comment) + printf("Old comment: %s\n", comment); + else + printf("No existing comment\n"); + + if (identity_comment) { + strlcpy(new_comment, identity_comment, sizeof(new_comment)); + } else { + printf("New comment: "); + fflush(stdout); + if (!fgets(new_comment, sizeof(new_comment), stdin)) { + explicit_bzero(passphrase, strlen(passphrase)); + sshkey_free(private); + exit(1); + } + new_comment[strcspn(new_comment, "\n")] = '\0'; + } + if (comment != NULL && strcmp(comment, new_comment) == 0) { + printf("No change to comment\n"); + free(passphrase); + sshkey_free(private); + free(comment); + exit(0); + } + + /* Save the file using the new passphrase. */ + if ((r = sshkey_save_private(private, identity_file, passphrase, + new_comment, private_key_format, openssh_format_cipher, + rounds)) != 0) { + error_r(r, "Saving key \"%s\" failed", identity_file); + freezero(passphrase, strlen(passphrase)); + sshkey_free(private); + free(comment); + exit(1); + } + freezero(passphrase, strlen(passphrase)); + if ((r = sshkey_from_private(private, &public)) != 0) + fatal_fr(r, "sshkey_from_private"); + sshkey_free(private); + + strlcat(identity_file, ".pub", sizeof(identity_file)); + if ((r = sshkey_save_public(public, identity_file, new_comment)) != 0) + fatal_r(r, "Unable to save public key to %s", identity_file); + sshkey_free(public); + free(comment); + + if (strlen(new_comment) > 0) + printf("Comment '%s' applied\n", new_comment); + else + printf("Comment removed\n"); + + exit(0); +} + +static void +cert_ext_add(const char *key, const char *value, int iscrit) +{ + cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext)); + cert_ext[ncert_ext].key = xstrdup(key); + cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value); + cert_ext[ncert_ext].crit = iscrit; + ncert_ext++; +} + +/* qsort(3) comparison function for certificate extensions */ +static int +cert_ext_cmp(const void *_a, const void *_b) +{ + const struct cert_ext *a = (const struct cert_ext *)_a; + const struct cert_ext *b = (const struct cert_ext *)_b; + int r; + + if (a->crit != b->crit) + return (a->crit < b->crit) ? -1 : 1; + if ((r = strcmp(a->key, b->key)) != 0) + return r; + if ((a->val == NULL) != (b->val == NULL)) + return (a->val == NULL) ? -1 : 1; + if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0) + return r; + return 0; +} + +#define OPTIONS_CRITICAL 1 +#define OPTIONS_EXTENSIONS 2 +static void +prepare_options_buf(struct sshbuf *c, int which) +{ + struct sshbuf *b; + size_t i; + int r; + const struct cert_ext *ext; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + sshbuf_reset(c); + for (i = 0; i < ncert_ext; i++) { + ext = &cert_ext[i]; + if ((ext->crit && (which & OPTIONS_EXTENSIONS)) || + (!ext->crit && (which & OPTIONS_CRITICAL))) + continue; + if (ext->val == NULL) { + /* flag option */ + debug3_f("%s", ext->key); + if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || + (r = sshbuf_put_string(c, NULL, 0)) != 0) + fatal_fr(r, "prepare flag"); + } else { + /* key/value option */ + debug3_f("%s=%s", ext->key, ext->val); + sshbuf_reset(b); + if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || + (r = sshbuf_put_cstring(b, ext->val)) != 0 || + (r = sshbuf_put_stringb(c, b)) != 0) + fatal_fr(r, "prepare k/v"); + } + } + sshbuf_free(b); +} + +static void +finalise_cert_exts(void) +{ + /* critical options */ + if (certflags_command != NULL) + cert_ext_add("force-command", certflags_command, 1); + if (certflags_src_addr != NULL) + cert_ext_add("source-address", certflags_src_addr, 1); + /* extensions */ + if ((certflags_flags & CERTOPT_X_FWD) != 0) + cert_ext_add("permit-X11-forwarding", NULL, 0); + if ((certflags_flags & CERTOPT_AGENT_FWD) != 0) + cert_ext_add("permit-agent-forwarding", NULL, 0); + if ((certflags_flags & CERTOPT_PORT_FWD) != 0) + cert_ext_add("permit-port-forwarding", NULL, 0); + if ((certflags_flags & CERTOPT_PTY) != 0) + cert_ext_add("permit-pty", NULL, 0); + if ((certflags_flags & CERTOPT_USER_RC) != 0) + cert_ext_add("permit-user-rc", NULL, 0); + if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) + cert_ext_add("no-touch-required", NULL, 0); + /* order lexically by key */ + if (ncert_ext > 0) + qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp); +} + +static struct sshkey * +load_pkcs11_key(char *path) +{ +#ifdef ENABLE_PKCS11 + struct sshkey **keys = NULL, *public, *private = NULL; + int r, i, nkeys; + + if ((r = sshkey_load_public(path, &public, NULL)) != 0) + fatal_r(r, "Couldn't load CA public key \"%s\"", path); + + nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, + &keys, NULL); + debug3_f("%d keys", nkeys); + if (nkeys <= 0) + fatal("cannot read public key from pkcs11"); + for (i = 0; i < nkeys; i++) { + if (sshkey_equal_public(public, keys[i])) { + private = keys[i]; + continue; + } + sshkey_free(keys[i]); + } + free(keys); + sshkey_free(public); + return private; +#else + fatal("no pkcs11 support"); +#endif /* ENABLE_PKCS11 */ +} + +/* Signer for sshkey_certify_custom that uses the agent */ +static int +agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, + const char *alg, const char *provider, const char *pin, + u_int compat, void *ctx) +{ + int *agent_fdp = (int *)ctx; + + return ssh_agent_sign(*agent_fdp, key, sigp, lenp, + data, datalen, alg, compat); +} + +static void +do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, + unsigned long long cert_serial, int cert_serial_autoinc, + int argc, char **argv) +{ + int r, i, found, agent_fd = -1; + u_int n; + struct sshkey *ca, *public; + char valid[64], *otmp, *tmp, *cp, *out, *comment; + char *ca_fp = NULL, **plist = NULL, *pin = NULL; + struct ssh_identitylist *agent_ids; + size_t j; + struct notifier_ctx *notifier = NULL; + +#ifdef ENABLE_PKCS11 + pkcs11_init(1); +#endif + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if (pkcs11provider != NULL) { + /* If a PKCS#11 token was specified then try to use it */ + if ((ca = load_pkcs11_key(tmp)) == NULL) + fatal("No PKCS#11 key matching %s found", ca_key_path); + } else if (prefer_agent) { + /* + * Agent signature requested. Try to use agent after making + * sure the public key specified is actually present in the + * agent. + */ + if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) + fatal_r(r, "Cannot load CA public key %s", tmp); + if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) + fatal_r(r, "Cannot use public key for CA signature"); + if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0) + fatal_r(r, "Retrieve agent key list"); + found = 0; + for (j = 0; j < agent_ids->nkeys; j++) { + if (sshkey_equal(ca, agent_ids->keys[j])) { + found = 1; + break; + } + } + if (!found) + fatal("CA key %s not found in agent", tmp); + ssh_free_identitylist(agent_ids); + ca->flags |= SSHKEY_FLAG_EXT; + } else { + /* CA key is assumed to be a private key on the filesystem */ + ca = load_identity(tmp, NULL); + if (sshkey_is_sk(ca) && + (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { + if ((pin = read_passphrase("Enter PIN for CA key: ", + RP_ALLOW_STDIN)) == NULL) + fatal_f("couldn't read PIN"); + } + } + free(tmp); + + if (key_type_name != NULL) { + if (sshkey_type_from_name(key_type_name) != ca->type) { + fatal("CA key type %s doesn't match specified %s", + sshkey_ssh_name(ca), key_type_name); + } + } else if (ca->type == KEY_RSA) { + /* Default to a good signature algorithm */ + key_type_name = "rsa-sha2-512"; + } + ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); + + finalise_cert_exts(); + for (i = 0; i < argc; i++) { + /* Split list of principals */ + n = 0; + if (cert_principals != NULL) { + otmp = tmp = xstrdup(cert_principals); + plist = NULL; + for (; (cp = strsep(&tmp, ",")) != NULL; n++) { + plist = xreallocarray(plist, n + 1, sizeof(*plist)); + if (*(plist[n] = xstrdup(cp)) == '\0') + fatal("Empty principal name"); + } + free(otmp); + } + if (n > SSHKEY_CERT_MAX_PRINCIPALS) + fatal("Too many certificate principals specified"); + + tmp = tilde_expand_filename(argv[i], pw->pw_uid); + if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) + fatal_r(r, "load pubkey \"%s\"", tmp); + if (sshkey_is_cert(public)) + fatal_f("key \"%s\" type %s cannot be certified", + tmp, sshkey_type(public)); + + /* Prepare certificate to sign */ + if ((r = sshkey_to_certified(public)) != 0) + fatal_r(r, "Could not upgrade key %s to certificate", tmp); + public->cert->type = cert_key_type; + public->cert->serial = (u_int64_t)cert_serial; + public->cert->key_id = xstrdup(cert_key_id); + public->cert->nprincipals = n; + public->cert->principals = plist; + public->cert->valid_after = cert_valid_from; + public->cert->valid_before = cert_valid_to; + prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); + prepare_options_buf(public->cert->extensions, + OPTIONS_EXTENSIONS); + if ((r = sshkey_from_private(ca, + &public->cert->signature_key)) != 0) + fatal_r(r, "sshkey_from_private (ca key)"); + + if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { + if ((r = sshkey_certify_custom(public, ca, + key_type_name, sk_provider, NULL, agent_signer, + &agent_fd)) != 0) + fatal_r(r, "Couldn't certify %s via agent", tmp); + } else { + if (sshkey_is_sk(ca) && + (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { + notifier = notify_start(0, + "Confirm user presence for key %s %s", + sshkey_type(ca), ca_fp); + } + r = sshkey_certify(public, ca, key_type_name, + sk_provider, pin); + notify_complete(notifier, "User presence confirmed"); + if (r != 0) + fatal_r(r, "Couldn't certify key %s", tmp); + } + + if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) + *cp = '\0'; + xasprintf(&out, "%s-cert.pub", tmp); + free(tmp); + + if ((r = sshkey_save_public(public, out, comment)) != 0) { + fatal_r(r, "Unable to save public key to %s", + identity_file); + } + + if (!quiet) { + sshkey_format_cert_validity(public->cert, + valid, sizeof(valid)); + logit("Signed %s key %s: id \"%s\" serial %llu%s%s " + "valid %s", sshkey_cert_type(public), + out, public->cert->key_id, + (unsigned long long)public->cert->serial, + cert_principals != NULL ? " for " : "", + cert_principals != NULL ? cert_principals : "", + valid); + } + + sshkey_free(public); + free(out); + if (cert_serial_autoinc) + cert_serial++; + } + if (pin != NULL) + freezero(pin, strlen(pin)); + free(ca_fp); +#ifdef ENABLE_PKCS11 + pkcs11_terminate(); +#endif + exit(0); +} + +static u_int64_t +parse_relative_time(const char *s, time_t now) +{ + int64_t mul, secs; + + mul = *s == '-' ? -1 : 1; + + if ((secs = convtime(s + 1)) == -1) + fatal("Invalid relative certificate time %s", s); + if (mul == -1 && secs > now) + fatal("Certificate time %s cannot be represented", s); + return now + (u_int64_t)(secs * mul); +} + +static void +parse_cert_times(char *timespec) +{ + char *from, *to; + time_t now = time(NULL); + int64_t secs; + + /* +timespec relative to now */ + if (*timespec == '+' && strchr(timespec, ':') == NULL) { + if ((secs = convtime(timespec + 1)) == -1) + fatal("Invalid relative certificate life %s", timespec); + cert_valid_to = now + secs; + /* + * Backdate certificate one minute to avoid problems on hosts + * with poorly-synchronised clocks. + */ + cert_valid_from = ((now - 59)/ 60) * 60; + return; + } + + /* + * from:to, where + * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "always" + * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "forever" + */ + from = xstrdup(timespec); + to = strchr(from, ':'); + if (to == NULL || from == to || *(to + 1) == '\0') + fatal("Invalid certificate life specification %s", timespec); + *to++ = '\0'; + + if (*from == '-' || *from == '+') + cert_valid_from = parse_relative_time(from, now); + else if (strcmp(from, "always") == 0) + cert_valid_from = 0; + else if (parse_absolute_time(from, &cert_valid_from) != 0) + fatal("Invalid from time \"%s\"", from); + + if (*to == '-' || *to == '+') + cert_valid_to = parse_relative_time(to, now); + else if (strcmp(to, "forever") == 0) + cert_valid_to = ~(u_int64_t)0; + else if (parse_absolute_time(to, &cert_valid_to) != 0) + fatal("Invalid to time \"%s\"", to); + + if (cert_valid_to <= cert_valid_from) + fatal("Empty certificate validity interval"); + free(from); +} + +static void +add_cert_option(char *opt) +{ + char *val, *cp; + int iscrit = 0; + + if (strcasecmp(opt, "clear") == 0) + certflags_flags = 0; + else if (strcasecmp(opt, "no-x11-forwarding") == 0) + certflags_flags &= ~CERTOPT_X_FWD; + else if (strcasecmp(opt, "permit-x11-forwarding") == 0) + certflags_flags |= CERTOPT_X_FWD; + else if (strcasecmp(opt, "no-agent-forwarding") == 0) + certflags_flags &= ~CERTOPT_AGENT_FWD; + else if (strcasecmp(opt, "permit-agent-forwarding") == 0) + certflags_flags |= CERTOPT_AGENT_FWD; + else if (strcasecmp(opt, "no-port-forwarding") == 0) + certflags_flags &= ~CERTOPT_PORT_FWD; + else if (strcasecmp(opt, "permit-port-forwarding") == 0) + certflags_flags |= CERTOPT_PORT_FWD; + else if (strcasecmp(opt, "no-pty") == 0) + certflags_flags &= ~CERTOPT_PTY; + else if (strcasecmp(opt, "permit-pty") == 0) + certflags_flags |= CERTOPT_PTY; + else if (strcasecmp(opt, "no-user-rc") == 0) + certflags_flags &= ~CERTOPT_USER_RC; + else if (strcasecmp(opt, "permit-user-rc") == 0) + certflags_flags |= CERTOPT_USER_RC; + else if (strcasecmp(opt, "touch-required") == 0) + certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE; + else if (strcasecmp(opt, "no-touch-required") == 0) + certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE; + else if (strncasecmp(opt, "force-command=", 14) == 0) { + val = opt + 14; + if (*val == '\0') + fatal("Empty force-command option"); + if (certflags_command != NULL) + fatal("force-command already specified"); + certflags_command = xstrdup(val); + } else if (strncasecmp(opt, "source-address=", 15) == 0) { + val = opt + 15; + if (*val == '\0') + fatal("Empty source-address option"); + if (certflags_src_addr != NULL) + fatal("source-address already specified"); + if (addr_match_cidr_list(NULL, val) != 0) + fatal("Invalid source-address list"); + certflags_src_addr = xstrdup(val); + } else if (strncasecmp(opt, "extension:", 10) == 0 || + (iscrit = (strncasecmp(opt, "critical:", 9) == 0))) { + val = xstrdup(strchr(opt, ':') + 1); + if ((cp = strchr(val, '=')) != NULL) + *cp++ = '\0'; + cert_ext_add(val, cp, iscrit); + free(val); + } else + fatal("Unsupported certificate option \"%s\"", opt); +} + +static void +show_options(struct sshbuf *optbuf, int in_critical) +{ + char *name, *arg, *hex; + struct sshbuf *options, *option = NULL; + int r; + + if ((options = sshbuf_fromb(optbuf)) == NULL) + fatal_f("sshbuf_fromb failed"); + while (sshbuf_len(options) != 0) { + sshbuf_free(option); + option = NULL; + if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 || + (r = sshbuf_froms(options, &option)) != 0) + fatal_fr(r, "parse option"); + printf(" %s", name); + if (!in_critical && + (strcmp(name, "permit-X11-forwarding") == 0 || + strcmp(name, "permit-agent-forwarding") == 0 || + strcmp(name, "permit-port-forwarding") == 0 || + strcmp(name, "permit-pty") == 0 || + strcmp(name, "permit-user-rc") == 0 || + strcmp(name, "no-touch-required") == 0)) { + printf("\n"); + } else if (in_critical && + (strcmp(name, "force-command") == 0 || + strcmp(name, "source-address") == 0)) { + if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) + fatal_fr(r, "parse critical"); + printf(" %s\n", arg); + free(arg); + } else if (sshbuf_len(option) > 0) { + hex = sshbuf_dtob16(option); + printf(" UNKNOWN OPTION: %s (len %zu)\n", + hex, sshbuf_len(option)); + sshbuf_reset(option); + free(hex); + } else + printf(" UNKNOWN FLAG OPTION\n"); + free(name); + if (sshbuf_len(option) != 0) + fatal("Option corrupt: extra data at end"); + } + sshbuf_free(option); + sshbuf_free(options); +} + +static void +print_cert(struct sshkey *key) +{ + char valid[64], *key_fp, *ca_fp; + u_int i; + + key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT); + ca_fp = sshkey_fingerprint(key->cert->signature_key, + fingerprint_hash, SSH_FP_DEFAULT); + if (key_fp == NULL || ca_fp == NULL) + fatal_f("sshkey_fingerprint fail"); + sshkey_format_cert_validity(key->cert, valid, sizeof(valid)); + + printf(" Type: %s %s certificate\n", sshkey_ssh_name(key), + sshkey_cert_type(key)); + printf(" Public key: %s %s\n", sshkey_type(key), key_fp); + printf(" Signing CA: %s %s (using %s)\n", + sshkey_type(key->cert->signature_key), ca_fp, + key->cert->signature_type); + printf(" Key ID: \"%s\"\n", key->cert->key_id); + printf(" Serial: %llu\n", (unsigned long long)key->cert->serial); + printf(" Valid: %s\n", valid); + printf(" Principals: "); + if (key->cert->nprincipals == 0) + printf("(none)\n"); + else { + for (i = 0; i < key->cert->nprincipals; i++) + printf("\n %s", + key->cert->principals[i]); + printf("\n"); + } + printf(" Critical Options: "); + if (sshbuf_len(key->cert->critical) == 0) + printf("(none)\n"); + else { + printf("\n"); + show_options(key->cert->critical, 1); + } + printf(" Extensions: "); + if (sshbuf_len(key->cert->extensions) == 0) + printf("(none)\n"); + else { + printf("\n"); + show_options(key->cert->extensions, 0); + } +} + +static void +do_show_cert(struct passwd *pw) +{ + struct sshkey *key = NULL; + struct stat st; + int r, is_stdin = 0, ok = 0; + FILE *f; + char *cp, *line = NULL; + const char *path; + size_t linesize = 0; + u_long lnum = 0; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + + path = identity_file; + if (strcmp(path, "-") == 0) { + f = stdin; + path = "(stdin)"; + is_stdin = 1; + } else if ((f = fopen(identity_file, "r")) == NULL) + fatal("fopen %s: %s", identity_file, strerror(errno)); + + while (getline(&line, &linesize, f) != -1) { + lnum++; + sshkey_free(key); + key = NULL; + /* Trim leading space and comments */ + cp = line + strspn(line, " \t"); + if (*cp == '#' || *cp == '\0') + continue; + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new"); + if ((r = sshkey_read(key, &cp)) != 0) { + error_r(r, "%s:%lu: invalid key", path, lnum); + continue; + } + if (!sshkey_is_cert(key)) { + error("%s:%lu is not a certificate", path, lnum); + continue; + } + ok = 1; + if (!is_stdin && lnum == 1) + printf("%s:\n", path); + else + printf("%s:%lu:\n", path, lnum); + print_cert(key); + } + free(line); + sshkey_free(key); + fclose(f); + exit(ok ? 0 : 1); +} + +static void +load_krl(const char *path, struct ssh_krl **krlp) +{ + struct sshbuf *krlbuf; + int r; + + if ((r = sshbuf_load_file(path, &krlbuf)) != 0) + fatal_r(r, "Unable to load KRL %s", path); + /* XXX check sigs */ + if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 || + *krlp == NULL) + fatal_r(r, "Invalid KRL file %s", path); + sshbuf_free(krlbuf); +} + +static void +hash_to_blob(const char *cp, u_char **blobp, size_t *lenp, + const char *file, u_long lnum) +{ + char *tmp; + size_t tlen; + struct sshbuf *b; + int r; + + if (strncmp(cp, "SHA256:", 7) != 0) + fatal("%s:%lu: unsupported hash algorithm", file, lnum); + cp += 7; + + /* + * OpenSSH base64 hashes omit trailing '=' + * characters; put them back for decode. + */ + tlen = strlen(cp); + tmp = xmalloc(tlen + 4 + 1); + strlcpy(tmp, cp, tlen + 1); + while ((tlen % 4) != 0) { + tmp[tlen++] = '='; + tmp[tlen] = '\0'; + } + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_b64tod(b, tmp)) != 0) + fatal_r(r, "%s:%lu: decode hash failed", file, lnum); + free(tmp); + *lenp = sshbuf_len(b); + *blobp = xmalloc(*lenp); + memcpy(*blobp, sshbuf_ptr(b), *lenp); + sshbuf_free(b); +} + +static void +update_krl_from_file(struct passwd *pw, const char *file, int wild_ca, + const struct sshkey *ca, struct ssh_krl *krl) +{ + struct sshkey *key = NULL; + u_long lnum = 0; + char *path, *cp, *ep, *line = NULL; + u_char *blob = NULL; + size_t blen = 0, linesize = 0; + unsigned long long serial, serial2; + int i, was_explicit_key, was_sha1, was_sha256, was_hash, r; + FILE *krl_spec; + + path = tilde_expand_filename(file, pw->pw_uid); + if (strcmp(path, "-") == 0) { + krl_spec = stdin; + free(path); + path = xstrdup("(standard input)"); + } else if ((krl_spec = fopen(path, "r")) == NULL) + fatal("fopen %s: %s", path, strerror(errno)); + + if (!quiet) + printf("Revoking from %s\n", path); + while (getline(&line, &linesize, krl_spec) != -1) { + lnum++; + was_explicit_key = was_sha1 = was_sha256 = was_hash = 0; + cp = line + strspn(line, " \t"); + /* Trim trailing space, comments and strip \n */ + for (i = 0, r = -1; cp[i] != '\0'; i++) { + if (cp[i] == '#' || cp[i] == '\n') { + cp[i] = '\0'; + break; + } + if (cp[i] == ' ' || cp[i] == '\t') { + /* Remember the start of a span of whitespace */ + if (r == -1) + r = i; + } else + r = -1; + } + if (r != -1) + cp[r] = '\0'; + if (*cp == '\0') + continue; + if (strncasecmp(cp, "serial:", 7) == 0) { + if (ca == NULL && !wild_ca) { + fatal("revoking certificates by serial number " + "requires specification of a CA key"); + } + cp += 7; + cp = cp + strspn(cp, " \t"); + errno = 0; + serial = strtoull(cp, &ep, 0); + if (*cp == '\0' || (*ep != '\0' && *ep != '-')) + fatal("%s:%lu: invalid serial \"%s\"", + path, lnum, cp); + if (errno == ERANGE && serial == ULLONG_MAX) + fatal("%s:%lu: serial out of range", + path, lnum); + serial2 = serial; + if (*ep == '-') { + cp = ep + 1; + errno = 0; + serial2 = strtoull(cp, &ep, 0); + if (*cp == '\0' || *ep != '\0') + fatal("%s:%lu: invalid serial \"%s\"", + path, lnum, cp); + if (errno == ERANGE && serial2 == ULLONG_MAX) + fatal("%s:%lu: serial out of range", + path, lnum); + if (serial2 <= serial) + fatal("%s:%lu: invalid serial range " + "%llu:%llu", path, lnum, + (unsigned long long)serial, + (unsigned long long)serial2); + } + if (ssh_krl_revoke_cert_by_serial_range(krl, + ca, serial, serial2) != 0) { + fatal_f("revoke serial failed"); + } + } else if (strncasecmp(cp, "id:", 3) == 0) { + if (ca == NULL && !wild_ca) { + fatal("revoking certificates by key ID " + "requires specification of a CA key"); + } + cp += 3; + cp = cp + strspn(cp, " \t"); + if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0) + fatal_f("revoke key ID failed"); + } else if (strncasecmp(cp, "hash:", 5) == 0) { + cp += 5; + cp = cp + strspn(cp, " \t"); + hash_to_blob(cp, &blob, &blen, file, lnum); + r = ssh_krl_revoke_key_sha256(krl, blob, blen); + if (r != 0) + fatal_fr(r, "revoke key failed"); + } else { + if (strncasecmp(cp, "key:", 4) == 0) { + cp += 4; + cp = cp + strspn(cp, " \t"); + was_explicit_key = 1; + } else if (strncasecmp(cp, "sha1:", 5) == 0) { + cp += 5; + cp = cp + strspn(cp, " \t"); + was_sha1 = 1; + } else if (strncasecmp(cp, "sha256:", 7) == 0) { + cp += 7; + cp = cp + strspn(cp, " \t"); + was_sha256 = 1; + /* + * Just try to process the line as a key. + * Parsing will fail if it isn't. + */ + } + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new"); + if ((r = sshkey_read(key, &cp)) != 0) + fatal_r(r, "%s:%lu: invalid key", path, lnum); + if (was_explicit_key) + r = ssh_krl_revoke_key_explicit(krl, key); + else if (was_sha1) { + if (sshkey_fingerprint_raw(key, + SSH_DIGEST_SHA1, &blob, &blen) != 0) { + fatal("%s:%lu: fingerprint failed", + file, lnum); + } + r = ssh_krl_revoke_key_sha1(krl, blob, blen); + } else if (was_sha256) { + if (sshkey_fingerprint_raw(key, + SSH_DIGEST_SHA256, &blob, &blen) != 0) { + fatal("%s:%lu: fingerprint failed", + file, lnum); + } + r = ssh_krl_revoke_key_sha256(krl, blob, blen); + } else + r = ssh_krl_revoke_key(krl, key); + if (r != 0) + fatal_fr(r, "revoke key failed"); + freezero(blob, blen); + blob = NULL; + blen = 0; + sshkey_free(key); + } + } + if (strcmp(path, "-") != 0) + fclose(krl_spec); + free(line); + free(path); +} + +static void +do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path, + unsigned long long krl_version, const char *krl_comment, + int argc, char **argv) +{ + struct ssh_krl *krl; + struct stat sb; + struct sshkey *ca = NULL; + int i, r, wild_ca = 0; + char *tmp; + struct sshbuf *kbuf; + + if (*identity_file == '\0') + fatal("KRL generation requires an output file"); + if (stat(identity_file, &sb) == -1) { + if (errno != ENOENT) + fatal("Cannot access KRL \"%s\": %s", + identity_file, strerror(errno)); + if (updating) + fatal("KRL \"%s\" does not exist", identity_file); + } + if (ca_key_path != NULL) { + if (strcasecmp(ca_key_path, "none") == 0) + wild_ca = 1; + else { + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) + fatal_r(r, "Cannot load CA public key %s", tmp); + free(tmp); + } + } + + if (updating) + load_krl(identity_file, &krl); + else if ((krl = ssh_krl_init()) == NULL) + fatal("couldn't create KRL"); + + if (krl_version != 0) + ssh_krl_set_version(krl, krl_version); + if (krl_comment != NULL) + ssh_krl_set_comment(krl, krl_comment); + + for (i = 0; i < argc; i++) + update_krl_from_file(pw, argv[i], wild_ca, ca, krl); + + if ((kbuf = sshbuf_new()) == NULL) + fatal("sshbuf_new failed"); + if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0) + fatal("Couldn't generate KRL"); + if ((r = sshbuf_write_file(identity_file, kbuf)) != 0) + fatal("write %s: %s", identity_file, strerror(errno)); + sshbuf_free(kbuf); + ssh_krl_free(krl); + sshkey_free(ca); +} + +static void +do_check_krl(struct passwd *pw, int print_krl, int argc, char **argv) +{ + int i, r, ret = 0; + char *comment; + struct ssh_krl *krl; + struct sshkey *k; + + if (*identity_file == '\0') + fatal("KRL checking requires an input file"); + load_krl(identity_file, &krl); + if (print_krl) + krl_dump(krl, stdout); + for (i = 0; i < argc; i++) { + if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0) + fatal_r(r, "Cannot load public key %s", argv[i]); + r = ssh_krl_check_key(krl, k); + printf("%s%s%s%s: %s\n", argv[i], + *comment ? " (" : "", comment, *comment ? ")" : "", + r == 0 ? "ok" : "REVOKED"); + if (r != 0) + ret = 1; + sshkey_free(k); + free(comment); + } + ssh_krl_free(krl); + exit(ret); +} + +static struct sshkey * +load_sign_key(const char *keypath, const struct sshkey *pubkey) +{ + size_t i, slen, plen = strlen(keypath); + char *privpath = xstrdup(keypath); + static const char * const suffixes[] = { "-cert.pub", ".pub", NULL }; + struct sshkey *ret = NULL, *privkey = NULL; + int r; + + /* + * If passed a public key filename, then try to locate the corresponding + * private key. This lets us specify certificates on the command-line + * and have ssh-keygen find the appropriate private key. + */ + for (i = 0; suffixes[i]; i++) { + slen = strlen(suffixes[i]); + if (plen <= slen || + strcmp(privpath + plen - slen, suffixes[i]) != 0) + continue; + privpath[plen - slen] = '\0'; + debug_f("%s looks like a public key, using private key " + "path %s instead", keypath, privpath); + } + if ((privkey = load_identity(privpath, NULL)) == NULL) { + error("Couldn't load identity %s", keypath); + goto done; + } + if (!sshkey_equal_public(pubkey, privkey)) { + error("Public key %s doesn't match private %s", + keypath, privpath); + goto done; + } + if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) { + /* + * Graft the certificate onto the private key to make + * it capable of signing. + */ + if ((r = sshkey_to_certified(privkey)) != 0) { + error_fr(r, "sshkey_to_certified"); + goto done; + } + if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) { + error_fr(r, "sshkey_cert_copy"); + goto done; + } + } + /* success */ + ret = privkey; + privkey = NULL; + done: + sshkey_free(privkey); + free(privpath); + return ret; +} + +static int +sign_one(struct sshkey *signkey, const char *filename, int fd, + const char *sig_namespace, const char *hashalg, sshsig_signer *signer, + void *signer_ctx) +{ + struct sshbuf *sigbuf = NULL, *abuf = NULL; + int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; + char *wfile = NULL, *asig = NULL, *fp = NULL; + char *pin = NULL, *prompt = NULL; + + if (!quiet) { + if (fd == STDIN_FILENO) + fprintf(stderr, "Signing data on standard input\n"); + else + fprintf(stderr, "Signing file %s\n", filename); + } + if (signer == NULL && sshkey_is_sk(signkey)) { + if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { + xasprintf(&prompt, "Enter PIN for %s key: ", + sshkey_type(signkey)); + if ((pin = read_passphrase(prompt, + RP_ALLOW_STDIN)) == NULL) + fatal_f("couldn't read PIN"); + } + if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { + if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + fprintf(stderr, "Confirm user presence for key %s %s\n", + sshkey_type(signkey), fp); + free(fp); + } + } + if ((r = sshsig_sign_fd(signkey, hashalg, sk_provider, pin, + fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) { + error_r(r, "Signing %s failed", filename); + goto out; + } + if ((r = sshsig_armor(sigbuf, &abuf)) != 0) { + error_fr(r, "sshsig_armor"); + goto out; + } + if ((asig = sshbuf_dup_string(abuf)) == NULL) { + error_f("buffer error"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if (fd == STDIN_FILENO) { + fputs(asig, stdout); + fflush(stdout); + } else { + xasprintf(&wfile, "%s.sig", filename); + if (confirm_overwrite(wfile)) { + if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC, + 0666)) == -1) { + oerrno = errno; + error("Cannot open %s: %s", + wfile, strerror(errno)); + errno = oerrno; + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + if (atomicio(vwrite, wfd, asig, + strlen(asig)) != strlen(asig)) { + oerrno = errno; + error("Cannot write to %s: %s", + wfile, strerror(errno)); + errno = oerrno; + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + if (!quiet) { + fprintf(stderr, "Write signature to %s\n", + wfile); + } + } + } + /* success */ + r = 0; + out: + free(wfile); + free(prompt); + free(asig); + if (pin != NULL) + freezero(pin, strlen(pin)); + sshbuf_free(abuf); + sshbuf_free(sigbuf); + if (wfd != -1) + close(wfd); + return r; +} + +static int +sig_process_opts(char * const *opts, size_t nopts, char **hashalgp, + uint64_t *verify_timep, int *print_pubkey) +{ + size_t i; + time_t now; + + if (verify_timep != NULL) + *verify_timep = 0; + if (print_pubkey != NULL) + *print_pubkey = 0; + if (hashalgp != NULL) + *hashalgp = NULL; + for (i = 0; i < nopts; i++) { + if (hashalgp != NULL && + strncasecmp(opts[i], "hashalg=", 8) == 0) { + *hashalgp = xstrdup(opts[i] + 8); + } else if (verify_timep && + strncasecmp(opts[i], "verify-time=", 12) == 0) { + if (parse_absolute_time(opts[i] + 12, + verify_timep) != 0 || *verify_timep == 0) { + error("Invalid \"verify-time\" option"); + return SSH_ERR_INVALID_ARGUMENT; + } + } else if (print_pubkey && + strcasecmp(opts[i], "print-pubkey") == 0) { + *print_pubkey = 1; + } else { + error("Invalid option \"%s\"", opts[i]); + return SSH_ERR_INVALID_ARGUMENT; + } + } + if (verify_timep && *verify_timep == 0) { + if ((now = time(NULL)) < 0) { + error("Time is before epoch"); + return SSH_ERR_INVALID_ARGUMENT; + } + *verify_timep = (uint64_t)now; + } + return 0; +} + + +static int +sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv, + char * const *opts, size_t nopts) +{ + int i, fd = -1, r, ret = -1; + int agent_fd = -1; + struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL; + sshsig_signer *signer = NULL; + char *hashalg = NULL; + + /* Check file arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "-") != 0) + continue; + if (i > 0 || argc > 1) + fatal("Cannot sign mix of paths and standard input"); + } + + if (sig_process_opts(opts, nopts, &hashalg, NULL, NULL) != 0) + goto done; /* error already logged */ + + if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) { + error_r(r, "Couldn't load public key %s", keypath); + goto done; + } + + if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) + debug_r(r, "Couldn't get agent socket"); + else { + if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0) + signer = agent_signer; + else + debug_r(r, "Couldn't find key in agent"); + } + + if (signer == NULL) { + /* Not using agent - try to load private key */ + if ((privkey = load_sign_key(keypath, pubkey)) == NULL) + goto done; + signkey = privkey; + } else { + /* Will use key in agent */ + signkey = pubkey; + } + + if (argc == 0) { + if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO, + sig_namespace, hashalg, signer, &agent_fd)) != 0) + goto done; + } else { + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "-") == 0) + fd = STDIN_FILENO; + else if ((fd = open(argv[i], O_RDONLY)) == -1) { + error("Cannot open %s for signing: %s", + argv[i], strerror(errno)); + goto done; + } + if ((r = sign_one(signkey, argv[i], fd, sig_namespace, + hashalg, signer, &agent_fd)) != 0) + goto done; + if (fd != STDIN_FILENO) + close(fd); + fd = -1; + } + } + + ret = 0; +done: + if (fd != -1 && fd != STDIN_FILENO) + close(fd); + sshkey_free(pubkey); + sshkey_free(privkey); + free(hashalg); + return ret; +} + +static int +sig_verify(const char *signature, const char *sig_namespace, + const char *principal, const char *allowed_keys, const char *revoked_keys, + char * const *opts, size_t nopts) +{ + int r, ret = -1; + int print_pubkey = 0; + struct sshbuf *sigbuf = NULL, *abuf = NULL; + struct sshkey *sign_key = NULL; + char *fp = NULL; + struct sshkey_sig_details *sig_details = NULL; + uint64_t verify_time = 0; + + if (sig_process_opts(opts, nopts, NULL, &verify_time, + &print_pubkey) != 0) + goto done; /* error already logged */ + + memset(&sig_details, 0, sizeof(sig_details)); + if ((r = sshbuf_load_file(signature, &abuf)) != 0) { + error_r(r, "Couldn't read signature file"); + goto done; + } + + if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { + error_fr(r, "sshsig_armor"); + goto done; + } + if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, + &sign_key, &sig_details)) != 0) + goto done; /* sshsig_verify() prints error */ + + if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + debug("Valid (unverified) signature from key %s", fp); + if (sig_details != NULL) { + debug2_f("signature details: counter = %u, flags = 0x%02x", + sig_details->sk_counter, sig_details->sk_flags); + } + free(fp); + fp = NULL; + + if (revoked_keys != NULL) { + if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) { + debug3_fr(r, "sshkey_check_revoked"); + goto done; + } + } + + if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys, + sign_key, principal, sig_namespace, verify_time)) != 0) { + debug3_fr(r, "sshsig_check_allowed_keys"); + goto done; + } + /* success */ + ret = 0; +done: + if (!quiet) { + if (ret == 0) { + if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + if (principal == NULL) { + printf("Good \"%s\" signature with %s key %s\n", + sig_namespace, sshkey_type(sign_key), fp); + + } else { + printf("Good \"%s\" signature for %s with %s key %s\n", + sig_namespace, principal, + sshkey_type(sign_key), fp); + } + } else { + printf("Could not verify signature.\n"); + } + } + /* Print the signature key if requested */ + if (ret == 0 && print_pubkey && sign_key != NULL) { + if ((r = sshkey_write(sign_key, stdout)) == 0) + fputc('\n', stdout); + else { + error_r(r, "Could not print public key.\n"); + ret = -1; + } + } + sshbuf_free(sigbuf); + sshbuf_free(abuf); + sshkey_free(sign_key); + sshkey_sig_details_free(sig_details); + free(fp); + return ret; +} + +static int +sig_find_principals(const char *signature, const char *allowed_keys, + char * const *opts, size_t nopts) +{ + int r, ret = -1; + struct sshbuf *sigbuf = NULL, *abuf = NULL; + struct sshkey *sign_key = NULL; + char *principals = NULL, *cp, *tmp; + uint64_t verify_time = 0; + + if (sig_process_opts(opts, nopts, NULL, &verify_time, NULL) != 0) + goto done; /* error already logged */ + + if ((r = sshbuf_load_file(signature, &abuf)) != 0) { + error_r(r, "Couldn't read signature file"); + goto done; + } + if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { + error_fr(r, "sshsig_armor"); + goto done; + } + if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) { + error_fr(r, "sshsig_get_pubkey"); + goto done; + } + if ((r = sshsig_find_principals(allowed_keys, sign_key, + verify_time, &principals)) != 0) { + if (r != SSH_ERR_KEY_NOT_FOUND) + error_fr(r, "sshsig_find_principal"); + goto done; + } + ret = 0; +done: + if (ret == 0 ) { + /* Emit matching principals one per line */ + tmp = principals; + while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0') + puts(cp); + } else { + fprintf(stderr, "No principal matched.\n"); + } + sshbuf_free(sigbuf); + sshbuf_free(abuf); + sshkey_free(sign_key); + free(principals); + return ret; +} + +static int +sig_match_principals(const char *allowed_keys, char *principal, + char * const *opts, size_t nopts) +{ + int r; + char **principals = NULL; + size_t i, nprincipals = 0; + + if ((r = sig_process_opts(opts, nopts, NULL, NULL, NULL)) != 0) + return r; /* error already logged */ + + if ((r = sshsig_match_principals(allowed_keys, principal, + &principals, &nprincipals)) != 0) { + debug_f("match: %s", ssh_err(r)); + fprintf(stderr, "No principal matched.\n"); + return r; + } + for (i = 0; i < nprincipals; i++) { + printf("%s\n", principals[i]); + free(principals[i]); + } + free(principals); + + return 0; +} + +static void +do_moduli_gen(const char *out_file, char **opts, size_t nopts) +{ +#ifdef WITH_OPENSSL + /* Moduli generation/screening */ + u_int32_t memory = 0; + BIGNUM *start = NULL; + int moduli_bits = 0; + FILE *out; + size_t i; + const char *errstr; + + /* Parse options */ + for (i = 0; i < nopts; i++) { + if (strncmp(opts[i], "memory=", 7) == 0) { + memory = (u_int32_t)strtonum(opts[i]+7, 1, + UINT_MAX, &errstr); + if (errstr) { + fatal("Memory limit is %s: %s", + errstr, opts[i]+7); + } + } else if (strncmp(opts[i], "start=", 6) == 0) { + /* XXX - also compare length against bits */ + if (BN_hex2bn(&start, opts[i]+6) == 0) + fatal("Invalid start point."); + } else if (strncmp(opts[i], "bits=", 5) == 0) { + moduli_bits = (int)strtonum(opts[i]+5, 1, + INT_MAX, &errstr); + if (errstr) { + fatal("Invalid number: %s (%s)", + opts[i]+12, errstr); + } + } else { + fatal("Option \"%s\" is unsupported for moduli " + "generation", opts[i]); + } + } + + if ((out = fopen(out_file, "w")) == NULL) { + fatal("Couldn't open modulus candidate file \"%s\": %s", + out_file, strerror(errno)); + } + setvbuf(out, NULL, _IOLBF, 0); + + if (moduli_bits == 0) + moduli_bits = DEFAULT_BITS; + if (gen_candidates(out, memory, moduli_bits, start) != 0) + fatal("modulus candidate generation failed"); +#else /* WITH_OPENSSL */ + fatal("Moduli generation is not supported"); +#endif /* WITH_OPENSSL */ +} + +static void +do_moduli_screen(const char *out_file, char **opts, size_t nopts) +{ +#ifdef WITH_OPENSSL + /* Moduli generation/screening */ + char *checkpoint = NULL; + u_int32_t generator_wanted = 0; + unsigned long start_lineno = 0, lines_to_process = 0; + int prime_tests = 0; + FILE *out, *in = stdin; + size_t i; + const char *errstr; + + /* Parse options */ + for (i = 0; i < nopts; i++) { + if (strncmp(opts[i], "lines=", 6) == 0) { + lines_to_process = strtoul(opts[i]+6, NULL, 10); + } else if (strncmp(opts[i], "start-line=", 11) == 0) { + start_lineno = strtoul(opts[i]+11, NULL, 10); + } else if (strncmp(opts[i], "checkpoint=", 11) == 0) { + checkpoint = xstrdup(opts[i]+11); + } else if (strncmp(opts[i], "generator=", 10) == 0) { + generator_wanted = (u_int32_t)strtonum( + opts[i]+10, 1, UINT_MAX, &errstr); + if (errstr != NULL) { + fatal("Generator invalid: %s (%s)", + opts[i]+10, errstr); + } + } else if (strncmp(opts[i], "prime-tests=", 12) == 0) { + prime_tests = (int)strtonum(opts[i]+12, 1, + INT_MAX, &errstr); + if (errstr) { + fatal("Invalid number: %s (%s)", + opts[i]+12, errstr); + } + } else { + fatal("Option \"%s\" is unsupported for moduli " + "screening", opts[i]); + } + } + + if (have_identity && strcmp(identity_file, "-") != 0) { + if ((in = fopen(identity_file, "r")) == NULL) { + fatal("Couldn't open modulus candidate " + "file \"%s\": %s", identity_file, + strerror(errno)); + } + } + + if ((out = fopen(out_file, "a")) == NULL) { + fatal("Couldn't open moduli file \"%s\": %s", + out_file, strerror(errno)); + } + setvbuf(out, NULL, _IOLBF, 0); + if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests, + generator_wanted, checkpoint, + start_lineno, lines_to_process) != 0) + fatal("modulus screening failed"); +#else /* WITH_OPENSSL */ + fatal("Moduli screening is not supported"); +#endif /* WITH_OPENSSL */ +} + +static char * +private_key_passphrase(void) +{ + char *passphrase1, *passphrase2; + + /* Ask for a passphrase (twice). */ + if (identity_passphrase) + passphrase1 = xstrdup(identity_passphrase); + else if (identity_new_passphrase) + passphrase1 = xstrdup(identity_new_passphrase); + else { +passphrase_again: + passphrase1 = + read_passphrase("Enter passphrase (empty for no " + "passphrase): ", RP_ALLOW_STDIN); + passphrase2 = read_passphrase("Enter same passphrase again: ", + RP_ALLOW_STDIN); + if (strcmp(passphrase1, passphrase2) != 0) { + /* + * The passphrases do not match. Clear them and + * retry. + */ + freezero(passphrase1, strlen(passphrase1)); + freezero(passphrase2, strlen(passphrase2)); + printf("Passphrases do not match. Try again.\n"); + goto passphrase_again; + } + /* Clear the other copy of the passphrase. */ + freezero(passphrase2, strlen(passphrase2)); + } + return passphrase1; +} + +static char * +sk_suffix(const char *application, const uint8_t *user, size_t userlen) +{ + char *ret, *cp; + size_t slen, i; + + /* Trim off URL-like preamble */ + if (strncmp(application, "ssh://", 6) == 0) + ret = xstrdup(application + 6); + else if (strncmp(application, "ssh:", 4) == 0) + ret = xstrdup(application + 4); + else + ret = xstrdup(application); + + /* Count trailing zeros in user */ + for (i = 0; i < userlen; i++) { + if (user[userlen - i - 1] != 0) + break; + } + if (i >= userlen) + return ret; /* user-id was default all-zeros */ + + /* Append user-id, escaping non-UTF-8 characters */ + slen = userlen - i; + if (asmprintf(&cp, INT_MAX, NULL, "%.*s", (int)slen, user) == -1) + fatal_f("asmprintf failed"); + /* Don't emit a user-id that contains path or control characters */ + if (strchr(cp, '/') != NULL || strstr(cp, "..") != NULL || + strchr(cp, '\\') != NULL) { + free(cp); + cp = tohex(user, slen); + } + xextendf(&ret, "_", "%s", cp); + free(cp); + return ret; +} + +static int +do_download_sk(const char *skprovider, const char *device) +{ + struct sshsk_resident_key **srks; + size_t nsrks, i; + int r, ret = -1; + char *fp, *pin = NULL, *pass = NULL, *path, *pubpath; + const char *ext; + struct sshkey *key; + + if (skprovider == NULL) + fatal("Cannot download keys without provider"); + + pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); + if (!quiet) { + printf("You may need to touch your authenticator " + "to authorize key download.\n"); + } + if ((r = sshsk_load_resident(skprovider, device, pin, 0, + &srks, &nsrks)) != 0) { + if (pin != NULL) + freezero(pin, strlen(pin)); + error_r(r, "Unable to load resident keys"); + return -1; + } + if (nsrks == 0) + logit("No keys to download"); + if (pin != NULL) + freezero(pin, strlen(pin)); + + for (i = 0; i < nsrks; i++) { + key = srks[i]->key; + if (key->type != KEY_ECDSA_SK && key->type != KEY_ED25519_SK) { + error("Unsupported key type %s (%d)", + sshkey_type(key), key->type); + continue; + } + if ((fp = sshkey_fingerprint(key, fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + debug_f("key %zu: %s %s %s (flags 0x%02x)", i, + sshkey_type(key), fp, key->sk_application, key->sk_flags); + ext = sk_suffix(key->sk_application, + srks[i]->user_id, srks[i]->user_id_len); + xasprintf(&path, "id_%s_rk%s%s", + key->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk", + *ext == '\0' ? "" : "_", ext); + + /* If the file already exists, ask the user to confirm. */ + if (!confirm_overwrite(path)) { + free(path); + break; + } + + /* Save the key with the application string as the comment */ + if (pass == NULL) + pass = private_key_passphrase(); + if ((r = sshkey_save_private(key, path, pass, + key->sk_application, private_key_format, + openssh_format_cipher, rounds)) != 0) { + error_r(r, "Saving key \"%s\" failed", path); + free(path); + break; + } + if (!quiet) { + printf("Saved %s key%s%s to %s\n", sshkey_type(key), + *ext != '\0' ? " " : "", + *ext != '\0' ? key->sk_application : "", + path); + } + + /* Save public key too */ + xasprintf(&pubpath, "%s.pub", path); + free(path); + if ((r = sshkey_save_public(key, pubpath, + key->sk_application)) != 0) { + error_r(r, "Saving public key \"%s\" failed", pubpath); + free(pubpath); + break; + } + free(pubpath); + } + + if (i >= nsrks) + ret = 0; /* success */ + if (pass != NULL) + freezero(pass, strlen(pass)); + sshsk_free_resident_keys(srks, nsrks); + return ret; +} + +static void +save_attestation(struct sshbuf *attest, const char *path) +{ + mode_t omask; + int r; + + if (path == NULL) + return; /* nothing to do */ + if (attest == NULL || sshbuf_len(attest) == 0) + fatal("Enrollment did not return attestation data"); + omask = umask(077); + r = sshbuf_write_file(path, attest); + umask(omask); + if (r != 0) + fatal_r(r, "Unable to write attestation data \"%s\"", path); + if (!quiet) + printf("Your FIDO attestation certificate has been saved in " + "%s\n", path); +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n" + " [-m format] [-N new_passphrase] [-O option]\n" + " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n" + " [-w provider] [-Z cipher]\n" + " ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n" + " [-P old_passphrase] [-Z cipher]\n" +#ifdef WITH_OPENSSL + " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" + " ssh-keygen -e [-f input_keyfile] [-m key_format]\n" +#endif + " ssh-keygen -y [-f input_keyfile]\n" + " ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase]\n" + " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" + " ssh-keygen -B [-f input_keyfile]\n"); +#ifdef ENABLE_PKCS11 + fprintf(stderr, + " ssh-keygen -D pkcs11\n"); +#endif + fprintf(stderr, + " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" + " ssh-keygen -H [-f known_hosts_file]\n" + " ssh-keygen -K [-a rounds] [-w provider]\n" + " ssh-keygen -R hostname [-f known_hosts_file]\n" + " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" +#ifdef WITH_OPENSSL + " ssh-keygen -M generate [-O option] output_file\n" + " ssh-keygen -M screen [-f input_file] [-O option] output_file\n" +#endif + " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n" + " [-n principals] [-O option] [-V validity_interval]\n" + " [-z serial_number] file ...\n" + " ssh-keygen -L [-f input_keyfile]\n" + " ssh-keygen -A [-a rounds] [-f prefix_path]\n" + " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" + " file ...\n" + " ssh-keygen -Q [-l] -f krl_file [file ...]\n" + " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n" + " ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file\n" + " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" + " ssh-keygen -Y sign -f key_file -n namespace file [-O option] ...\n" + " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" + " -n namespace -s signature_file [-r krl_file] [-O option]\n"); + exit(1); +} + +/* + * Main program for key management. + */ +int +main(int argc, char **argv) +{ + char comment[1024], *passphrase; + char *rr_hostname = NULL, *ep, *fp, *ra; + struct sshkey *private, *public; + struct passwd *pw; + int r, opt, type; + int change_passphrase = 0, change_comment = 0, show_cert = 0; + int find_host = 0, delete_host = 0, hash_hosts = 0; + int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; + int prefer_agent = 0, convert_to = 0, convert_from = 0; + int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; + int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; + unsigned long long cert_serial = 0; + char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; + char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; + char *sk_attestation_path = NULL; + struct sshbuf *challenge = NULL, *attest = NULL; + size_t i, nopts = 0; + u_int32_t bits = 0; + uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; + const char *errstr; + int log_level = SYSLOG_LEVEL_INFO; + char *sign_op = NULL; + + extern int optind; + extern char *optarg; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + __progname = ssh_get_progname(argv[0]); + + seed_rng(); + + log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); + + msetlocale(); + + /* we need this for the home * directory. */ + pw = getpwuid(getuid()); + if (!pw) + fatal("No user exists for uid %lu", (u_long)getuid()); + pw = pwcopy(pw); + if (gethostname(hostname, sizeof(hostname)) == -1) + fatal("gethostname: %s", strerror(errno)); + + sk_provider = getenv("SSH_SK_PROVIDER"); + + /* Remaining characters: dGjJSTWx */ + while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy" + "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" + "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { + switch (opt) { + case 'A': + gen_all_hostkeys = 1; + break; + case 'b': + bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX, + &errstr); + if (errstr) + fatal("Bits has bad value %s (%s)", + optarg, errstr); + break; + case 'E': + fingerprint_hash = ssh_digest_alg_by_name(optarg); + if (fingerprint_hash == -1) + fatal("Invalid hash algorithm \"%s\"", optarg); + break; + case 'F': + find_host = 1; + rr_hostname = optarg; + break; + case 'H': + hash_hosts = 1; + break; + case 'I': + cert_key_id = optarg; + break; + case 'R': + delete_host = 1; + rr_hostname = optarg; + break; + case 'L': + show_cert = 1; + break; + case 'l': + print_fingerprint = 1; + break; + case 'B': + print_bubblebabble = 1; + break; + case 'm': + if (strcasecmp(optarg, "RFC4716") == 0 || + strcasecmp(optarg, "ssh2") == 0) { + convert_format = FMT_RFC4716; + break; + } + if (strcasecmp(optarg, "PKCS8") == 0) { + convert_format = FMT_PKCS8; + private_key_format = SSHKEY_PRIVATE_PKCS8; + break; + } + if (strcasecmp(optarg, "PEM") == 0) { + convert_format = FMT_PEM; + private_key_format = SSHKEY_PRIVATE_PEM; + break; + } + fatal("Unsupported conversion format \"%s\"", optarg); + case 'n': + cert_principals = optarg; + break; + case 'o': + /* no-op; new format is already the default */ + break; + case 'p': + change_passphrase = 1; + break; + case 'c': + change_comment = 1; + break; + case 'f': + if (strlcpy(identity_file, optarg, + sizeof(identity_file)) >= sizeof(identity_file)) + fatal("Identity filename too long"); + have_identity = 1; + break; + case 'g': + print_generic = 1; + break; + case 'K': + download_sk = 1; + break; + case 'P': + identity_passphrase = optarg; + break; + case 'N': + identity_new_passphrase = optarg; + break; + case 'Q': + check_krl = 1; + break; + case 'O': + opts = xrecallocarray(opts, nopts, nopts + 1, + sizeof(*opts)); + opts[nopts++] = xstrdup(optarg); + break; + case 'Z': + openssh_format_cipher = optarg; + if (cipher_by_name(openssh_format_cipher) == NULL) + fatal("Invalid OpenSSH-format cipher '%s'", + openssh_format_cipher); + break; + case 'C': + identity_comment = optarg; + break; + case 'q': + quiet = 1; + break; + case 'e': + /* export key */ + convert_to = 1; + break; + case 'h': + cert_key_type = SSH2_CERT_TYPE_HOST; + certflags_flags = 0; + break; + case 'k': + gen_krl = 1; + break; + case 'i': + case 'X': + /* import key */ + convert_from = 1; + break; + case 'y': + print_public = 1; + break; + case 's': + ca_key_path = optarg; + break; + case 't': + key_type_name = optarg; + break; + case 'D': + pkcs11provider = optarg; + break; + case 'U': + prefer_agent = 1; + break; + case 'u': + update_krl = 1; + break; + case 'v': + if (log_level == SYSLOG_LEVEL_INFO) + log_level = SYSLOG_LEVEL_DEBUG1; + else { + if (log_level >= SYSLOG_LEVEL_DEBUG1 && + log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + } + break; + case 'r': + rr_hostname = optarg; + break; + case 'a': + rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr); + if (errstr) + fatal("Invalid number: %s (%s)", + optarg, errstr); + break; + case 'V': + parse_cert_times(optarg); + break; + case 'Y': + sign_op = optarg; + break; + case 'w': + sk_provider = optarg; + break; + case 'z': + errno = 0; + if (*optarg == '+') { + cert_serial_autoinc = 1; + optarg++; + } + cert_serial = strtoull(optarg, &ep, 10); + if (*optarg < '0' || *optarg > '9' || *ep != '\0' || + (errno == ERANGE && cert_serial == ULLONG_MAX)) + fatal("Invalid serial number \"%s\"", optarg); + break; + case 'M': + if (strcmp(optarg, "generate") == 0) + do_gen_candidates = 1; + else if (strcmp(optarg, "screen") == 0) + do_screen_candidates = 1; + else + fatal("Unsupported moduli option %s", optarg); + break; + case '?': + default: + usage(); + } + } + +#ifdef ENABLE_SK_INTERNAL + if (sk_provider == NULL) + sk_provider = "internal"; +#endif + + /* reinit */ + log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); + + argv += optind; + argc -= optind; + + if (sign_op != NULL) { + if (strncmp(sign_op, "find-principals", 15) == 0) { + if (ca_key_path == NULL) { + error("Too few arguments for find-principals:" + "missing signature file"); + exit(1); + } + if (!have_identity) { + error("Too few arguments for find-principals:" + "missing allowed keys file"); + exit(1); + } + return sig_find_principals(ca_key_path, identity_file, + opts, nopts); + } else if (strncmp(sign_op, "match-principals", 16) == 0) { + if (!have_identity) { + error("Too few arguments for match-principals:" + "missing allowed keys file"); + exit(1); + } + if (cert_key_id == NULL) { + error("Too few arguments for match-principals: " + "missing principal ID"); + exit(1); + } + return sig_match_principals(identity_file, cert_key_id, + opts, nopts); + } else if (strncmp(sign_op, "sign", 4) == 0) { + /* NB. cert_principals is actually namespace, via -n */ + if (cert_principals == NULL || + *cert_principals == '\0') { + error("Too few arguments for sign: " + "missing namespace"); + exit(1); + } + if (!have_identity) { + error("Too few arguments for sign: " + "missing key"); + exit(1); + } + return sig_sign(identity_file, cert_principals, + argc, argv, opts, nopts); + } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { + /* NB. cert_principals is actually namespace, via -n */ + if (cert_principals == NULL || + *cert_principals == '\0') { + error("Too few arguments for check-novalidate: " + "missing namespace"); + exit(1); + } + if (ca_key_path == NULL) { + error("Too few arguments for check-novalidate: " + "missing signature file"); + exit(1); + } + return sig_verify(ca_key_path, cert_principals, + NULL, NULL, NULL, opts, nopts); + } else if (strncmp(sign_op, "verify", 6) == 0) { + /* NB. cert_principals is actually namespace, via -n */ + if (cert_principals == NULL || + *cert_principals == '\0') { + error("Too few arguments for verify: " + "missing namespace"); + exit(1); + } + if (ca_key_path == NULL) { + error("Too few arguments for verify: " + "missing signature file"); + exit(1); + } + if (!have_identity) { + error("Too few arguments for sign: " + "missing allowed keys file"); + exit(1); + } + if (cert_key_id == NULL) { + error("Too few arguments for verify: " + "missing principal identity"); + exit(1); + } + return sig_verify(ca_key_path, cert_principals, + cert_key_id, identity_file, rr_hostname, + opts, nopts); + } + error("Unsupported operation for -Y: \"%s\"", sign_op); + usage(); + /* NOTREACHED */ + } + + if (ca_key_path != NULL) { + if (argc < 1 && !gen_krl) { + error("Too few arguments."); + usage(); + } + } else if (argc > 0 && !gen_krl && !check_krl && + !do_gen_candidates && !do_screen_candidates) { + error("Too many arguments."); + usage(); + } + if (change_passphrase && change_comment) { + error("Can only have one of -p and -c."); + usage(); + } + if (print_fingerprint && (delete_host || hash_hosts)) { + error("Cannot use -l with -H or -R."); + usage(); + } + if (gen_krl) { + do_gen_krl(pw, update_krl, ca_key_path, + cert_serial, identity_comment, argc, argv); + return (0); + } + if (check_krl) { + do_check_krl(pw, print_fingerprint, argc, argv); + return (0); + } + if (ca_key_path != NULL) { + if (cert_key_id == NULL) + fatal("Must specify key id (-I) when certifying"); + for (i = 0; i < nopts; i++) + add_cert_option(opts[i]); + do_ca_sign(pw, ca_key_path, prefer_agent, + cert_serial, cert_serial_autoinc, argc, argv); + } + if (show_cert) + do_show_cert(pw); + if (delete_host || hash_hosts || find_host) { + do_known_hosts(pw, rr_hostname, find_host, + delete_host, hash_hosts); + } + if (pkcs11provider != NULL) + do_download(pw); + if (download_sk) { + for (i = 0; i < nopts; i++) { + if (strncasecmp(opts[i], "device=", 7) == 0) { + sk_device = xstrdup(opts[i] + 7); + } else { + fatal("Option \"%s\" is unsupported for " + "FIDO authenticator download", opts[i]); + } + } + return do_download_sk(sk_provider, sk_device); + } + if (print_fingerprint || print_bubblebabble) + do_fingerprint(pw); + if (change_passphrase) + do_change_passphrase(pw); + if (change_comment) + do_change_comment(pw, identity_comment); +#ifdef WITH_OPENSSL + if (convert_to) + do_convert_to(pw); + if (convert_from) + do_convert_from(pw); +#else /* WITH_OPENSSL */ + if (convert_to || convert_from) + fatal("key conversion disabled at compile time"); +#endif /* WITH_OPENSSL */ + if (print_public) + do_print_public(pw); + if (rr_hostname != NULL) { + unsigned int n = 0; + + if (have_identity) { + n = do_print_resource_record(pw, identity_file, + rr_hostname, print_generic); + if (n == 0) + fatal("%s: %s", identity_file, strerror(errno)); + exit(0); + } else { + + n += do_print_resource_record(pw, + _PATH_HOST_RSA_KEY_FILE, rr_hostname, + print_generic); + n += do_print_resource_record(pw, + _PATH_HOST_DSA_KEY_FILE, rr_hostname, + print_generic); + n += do_print_resource_record(pw, + _PATH_HOST_ECDSA_KEY_FILE, rr_hostname, + print_generic); + n += do_print_resource_record(pw, + _PATH_HOST_ED25519_KEY_FILE, rr_hostname, + print_generic); + n += do_print_resource_record(pw, + _PATH_HOST_XMSS_KEY_FILE, rr_hostname, + print_generic); + if (n == 0) + fatal("no keys found."); + exit(0); + } + } + + if (do_gen_candidates || do_screen_candidates) { + if (argc <= 0) + fatal("No output file specified"); + else if (argc > 1) + fatal("Too many output files specified"); + } + if (do_gen_candidates) { + do_moduli_gen(argv[0], opts, nopts); + return 0; + } + if (do_screen_candidates) { + do_moduli_screen(argv[0], opts, nopts); + return 0; + } + + if (gen_all_hostkeys) { + do_gen_all_hostkeys(pw); + return (0); + } + + if (key_type_name == NULL) + key_type_name = DEFAULT_KEY_TYPE_NAME; + + type = sshkey_type_from_name(key_type_name); + type_bits_valid(type, key_type_name, &bits); + + if (!quiet) + printf("Generating public/private %s key pair.\n", + key_type_name); + switch (type) { + case KEY_ECDSA_SK: + case KEY_ED25519_SK: + for (i = 0; i < nopts; i++) { + if (strcasecmp(opts[i], "no-touch-required") == 0) { + sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; + } else if (strcasecmp(opts[i], "verify-required") == 0) { + sk_flags |= SSH_SK_USER_VERIFICATION_REQD; + } else if (strcasecmp(opts[i], "resident") == 0) { + sk_flags |= SSH_SK_RESIDENT_KEY; + } else if (strncasecmp(opts[i], "device=", 7) == 0) { + sk_device = xstrdup(opts[i] + 7); + } else if (strncasecmp(opts[i], "user=", 5) == 0) { + sk_user = xstrdup(opts[i] + 5); + } else if (strncasecmp(opts[i], "challenge=", 10) == 0) { + if ((r = sshbuf_load_file(opts[i] + 10, + &challenge)) != 0) { + fatal_r(r, "Unable to load FIDO " + "enrollment challenge \"%s\"", + opts[i] + 10); + } + } else if (strncasecmp(opts[i], + "write-attestation=", 18) == 0) { + sk_attestation_path = opts[i] + 18; + } else if (strncasecmp(opts[i], + "application=", 12) == 0) { + sk_application = xstrdup(opts[i] + 12); + if (strncmp(sk_application, "ssh:", 4) != 0) { + fatal("FIDO application string must " + "begin with \"ssh:\""); + } + } else { + fatal("Option \"%s\" is unsupported for " + "FIDO authenticator enrollment", opts[i]); + } + } + if (!quiet) { + printf("You may need to touch your authenticator " + "to authorize key generation.\n"); + } + if ((attest = sshbuf_new()) == NULL) + fatal("sshbuf_new failed"); + if ((sk_flags & + (SSH_SK_USER_VERIFICATION_REQD|SSH_SK_RESIDENT_KEY))) { + passphrase = read_passphrase("Enter PIN for " + "authenticator: ", RP_ALLOW_STDIN); + } else { + passphrase = NULL; + } + for (i = 0 ; ; i++) { + fflush(stdout); + r = sshsk_enroll(type, sk_provider, sk_device, + sk_application == NULL ? "ssh:" : sk_application, + sk_user, sk_flags, passphrase, challenge, + &private, attest); + if (r == 0) + break; + if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) + fatal_r(r, "Key enrollment failed"); + else if (passphrase != NULL) { + error("PIN incorrect"); + freezero(passphrase, strlen(passphrase)); + passphrase = NULL; + } + if (i >= 3) + fatal("Too many incorrect PINs"); + passphrase = read_passphrase("Enter PIN for " + "authenticator: ", RP_ALLOW_STDIN); + if (!quiet) { + printf("You may need to touch your " + "authenticator (again) to authorize " + "key generation.\n"); + } + } + if (passphrase != NULL) { + freezero(passphrase, strlen(passphrase)); + passphrase = NULL; + } + break; + default: + if ((r = sshkey_generate(type, bits, &private)) != 0) + fatal("sshkey_generate failed"); + break; + } + if ((r = sshkey_from_private(private, &public)) != 0) + fatal_r(r, "sshkey_from_private"); + + if (!have_identity) + ask_filename(pw, "Enter file in which to save the key"); + + /* Create ~/.ssh directory if it doesn't already exist. */ + hostfile_create_user_ssh_dir(identity_file, !quiet); + + /* If the file already exists, ask the user to confirm. */ + if (!confirm_overwrite(identity_file)) + exit(1); + + /* Determine the passphrase for the private key */ + passphrase = private_key_passphrase(); + if (identity_comment) { + strlcpy(comment, identity_comment, sizeof(comment)); + } else { + /* Create default comment field for the passphrase. */ + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); + } + + /* Save the key with the given passphrase and comment. */ + if ((r = sshkey_save_private(private, identity_file, passphrase, + comment, private_key_format, openssh_format_cipher, rounds)) != 0) { + error_r(r, "Saving key \"%s\" failed", identity_file); + freezero(passphrase, strlen(passphrase)); + exit(1); + } + freezero(passphrase, strlen(passphrase)); + sshkey_free(private); + + if (!quiet) { + printf("Your identification has been saved in %s\n", + identity_file); + } + + strlcat(identity_file, ".pub", sizeof(identity_file)); + if ((r = sshkey_save_public(public, identity_file, comment)) != 0) + fatal_r(r, "Unable to save public key to %s", identity_file); + + if (!quiet) { + fp = sshkey_fingerprint(public, fingerprint_hash, + SSH_FP_DEFAULT); + ra = sshkey_fingerprint(public, fingerprint_hash, + SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal("sshkey_fingerprint failed"); + printf("Your public key has been saved in %s\n", + identity_file); + printf("The key fingerprint is:\n"); + printf("%s %s\n", fp, comment); + printf("The key's randomart image is:\n"); + printf("%s\n", ra); + free(ra); + free(fp); + } + + if (sk_attestation_path != NULL) + save_attestation(attest, sk_attestation_path); + + sshbuf_free(attest); + sshkey_free(public); + + exit(0); +} diff --git a/aosp/external/openssh/ssh-keyscan.1 b/aosp/external/openssh/ssh-keyscan.1 new file mode 100644 index 0000000000000000000000000000000000000000..f9df75d42f1ad1e79770df4120091b03b96d97ef --- /dev/null +++ b/aosp/external/openssh/ssh-keyscan.1 @@ -0,0 +1,158 @@ +.\" $OpenBSD: ssh-keyscan.1,v 1.45 2019/11/30 07:07:59 jmc Exp $ +.\" +.\" Copyright 1995, 1996 by David Mazieres . +.\" +.\" Modification and redistribution in source and binary forms is +.\" permitted provided that due credit is given to the author and the +.\" OpenBSD project by leaving this copyright notice intact. +.\" +.Dd $Mdocdate: November 30 2019 $ +.Dt SSH-KEYSCAN 1 +.Os +.Sh NAME +.Nm ssh-keyscan +.Nd gather SSH public keys from servers +.Sh SYNOPSIS +.Nm ssh-keyscan +.Op Fl 46cDHv +.Op Fl f Ar file +.Op Fl p Ar port +.Op Fl T Ar timeout +.Op Fl t Ar type +.Op Ar host | addrlist namelist +.Sh DESCRIPTION +.Nm +is a utility for gathering the public SSH host keys of a number of +hosts. +It was designed to aid in building and verifying +.Pa ssh_known_hosts +files, +the format of which is documented in +.Xr sshd 8 . +.Nm +provides a minimal interface suitable for use by shell and perl +scripts. +.Pp +.Nm +uses non-blocking socket I/O to contact as many hosts as possible in +parallel, so it is very efficient. +The keys from a domain of 1,000 +hosts can be collected in tens of seconds, even when some of those +hosts are down or do not run +.Xr sshd 8 . +For scanning, one does not need +login access to the machines that are being scanned, nor does the +scanning process involve any encryption. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 4 +Force +.Nm +to use IPv4 addresses only. +.It Fl 6 +Force +.Nm +to use IPv6 addresses only. +.It Fl c +Request certificates from target hosts instead of plain keys. +.It Fl D +Print keys found as SSHFP DNS records. +The default is to print keys in a format usable as a +.Xr ssh 1 +.Pa known_hosts +file. +.It Fl f Ar file +Read hosts or +.Dq addrlist namelist +pairs from +.Ar file , +one per line. +If +.Sq - +is supplied instead of a filename, +.Nm +will read from the standard input. +Input is expected in the format: +.Bd -literal +1.2.3.4,1.2.4.4 name.my.domain,name,n.my.domain,n,1.2.3.4,1.2.4.4 +.Ed +.It Fl H +Hash all hostnames and addresses in the output. +Hashed names may be used normally by +.Xr ssh 1 +and +.Xr sshd 8 , +but they do not reveal identifying information should the file's contents +be disclosed. +.It Fl p Ar port +Connect to +.Ar port +on the remote host. +.It Fl T Ar timeout +Set the timeout for connection attempts. +If +.Ar timeout +seconds have elapsed since a connection was initiated to a host or since the +last time anything was read from that host, the connection is +closed and the host in question considered unavailable. +The default is 5 seconds. +.It Fl t Ar type +Specify the type of the key to fetch from the scanned hosts. +The possible values are +.Dq dsa , +.Dq ecdsa , +.Dq ed25519 , +or +.Dq rsa . +Multiple values may be specified by separating them with commas. +The default is to fetch +.Dq rsa , +.Dq ecdsa , +and +.Dq ed25519 +keys. +.It Fl v +Verbose mode: +print debugging messages about progress. +.El +.Pp +If an ssh_known_hosts file is constructed using +.Nm +without verifying the keys, users will be vulnerable to +.Em man in the middle +attacks. +On the other hand, if the security model allows such a risk, +.Nm +can help in the detection of tampered keyfiles or man in the middle +attacks which have begun after the ssh_known_hosts file was created. +.Sh FILES +.Pa /etc/ssh/ssh_known_hosts +.Sh EXAMPLES +Print the RSA host key for machine +.Ar hostname : +.Pp +.Dl $ ssh-keyscan -t rsa hostname +.Pp +Find all hosts from the file +.Pa ssh_hosts +which have new or different keys from those in the sorted file +.Pa ssh_known_hosts : +.Bd -literal -offset indent +$ ssh-keyscan -t rsa,dsa,ecdsa,ed25519 -f ssh_hosts | \e + sort -u - ssh_known_hosts | diff ssh_known_hosts - +.Ed +.Sh SEE ALSO +.Xr ssh 1 , +.Xr sshd 8 +.Rs +.%D 2006 +.%R RFC 4255 +.%T Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints +.Re +.Sh AUTHORS +.An -nosplit +.An David Mazieres Aq Mt dm@lcs.mit.edu +wrote the initial version, and +.An Wayne Davison Aq Mt wayned@users.sourceforge.net +added support for protocol version 2. diff --git a/aosp/external/openssh/ssh-keyscan.c b/aosp/external/openssh/ssh-keyscan.c new file mode 100644 index 0000000000000000000000000000000000000000..d29a03b4e68a82772010786f5b93dd50cbc86cb9 --- /dev/null +++ b/aosp/external/openssh/ssh-keyscan.c @@ -0,0 +1,822 @@ +/* $OpenBSD: ssh-keyscan.c,v 1.145 2022/01/21 00:53:40 deraadt Exp $ */ +/* + * Copyright 1995, 1996 by David Mazieres . + * + * Modification and redistribution in source and binary forms is + * permitted provided that due credit is given to the author and the + * OpenBSD project by leaving this copyright notice intact. + */ + +#include "includes.h" + +#include +#include "openbsd-compat/sys-queue.h" +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include + +#ifdef WITH_OPENSSL +#include +#endif + +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "cipher.h" +#include "kex.h" +#include "compat.h" +#include "myproposal.h" +#include "packet.h" +#include "dispatch.h" +#include "log.h" +#include "atomicio.h" +#include "misc.h" +#include "hostfile.h" +#include "ssherr.h" +#include "ssh_api.h" +#include "dns.h" + +/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. + Default value is AF_UNSPEC means both IPv4 and IPv6. */ +int IPv4or6 = AF_UNSPEC; + +int ssh_port = SSH_DEFAULT_PORT; + +#define KT_DSA (1) +#define KT_RSA (1<<1) +#define KT_ECDSA (1<<2) +#define KT_ED25519 (1<<3) +#define KT_XMSS (1<<4) +#define KT_ECDSA_SK (1<<5) +#define KT_ED25519_SK (1<<6) + +#define KT_MIN KT_DSA +#define KT_MAX KT_ED25519_SK + +int get_cert = 0; +int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK; + +int hash_hosts = 0; /* Hash hostname on output */ + +int print_sshfp = 0; /* Print SSHFP records instead of known_hosts */ + +int found_one = 0; /* Successfully found a key */ + +#define MAXMAXFD 256 + +/* The number of seconds after which to give up on a TCP connection */ +int timeout = 5; + +int maxfd; +#define MAXCON (maxfd - 10) + +extern char *__progname; +struct pollfd *read_wait; +int ncon; + +/* + * Keep a connection structure for each file descriptor. The state + * associated with file descriptor n is held in fdcon[n]. + */ +typedef struct Connection { + u_char c_status; /* State of connection on this file desc. */ +#define CS_UNUSED 0 /* File descriptor unused */ +#define CS_CON 1 /* Waiting to connect/read greeting */ +#define CS_SIZE 2 /* Waiting to read initial packet size */ +#define CS_KEYS 3 /* Waiting to read public key packet */ + int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ + int c_plen; /* Packet length field for ssh packet */ + int c_len; /* Total bytes which must be read. */ + int c_off; /* Length of data read so far. */ + int c_keytype; /* Only one of KT_* */ + sig_atomic_t c_done; /* SSH2 done */ + char *c_namebase; /* Address to free for c_name and c_namelist */ + char *c_name; /* Hostname of connection for errors */ + char *c_namelist; /* Pointer to other possible addresses */ + char *c_output_name; /* Hostname of connection for output */ + char *c_data; /* Data read from this fd */ + struct ssh *c_ssh; /* SSH-connection */ + struct timespec c_ts; /* Time at which connection gets aborted */ + TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ +} con; + +TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ +con *fdcon; + +static void keyprint(con *c, struct sshkey *key); + +static int +fdlim_get(int hard) +{ +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rlfd; + + if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1) + return (-1); + if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) + return SSH_SYSFDMAX; + else + return hard ? rlfd.rlim_max : rlfd.rlim_cur; +#else + return SSH_SYSFDMAX; +#endif +} + +static int +fdlim_set(int lim) +{ +#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rlfd; +#endif + + if (lim <= 0) + return (-1); +#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) + if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1) + return (-1); + rlfd.rlim_cur = lim; + if (setrlimit(RLIMIT_NOFILE, &rlfd) == -1) + return (-1); +#elif defined (HAVE_SETDTABLESIZE) + setdtablesize(lim); +#endif + return (0); +} + +/* + * This is an strsep function that returns a null field for adjacent + * separators. This is the same as the 4.4BSD strsep, but different from the + * one in the GNU libc. + */ +static char * +xstrsep(char **str, const char *delim) +{ + char *s, *e; + + if (!**str) + return (NULL); + + s = *str; + e = s + strcspn(s, delim); + + if (*e != '\0') + *e++ = '\0'; + *str = e; + + return (s); +} + +/* + * Get the next non-null token (like GNU strsep). Strsep() will return a + * null token for two adjacent separators, so we may have to loop. + */ +static char * +strnnsep(char **stringp, char *delim) +{ + char *tok; + + do { + tok = xstrsep(stringp, delim); + } while (tok && *tok == '\0'); + return (tok); +} + + +static int +key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) +{ + con *c; + + if ((c = ssh_get_app_data(ssh)) != NULL) + keyprint(c, hostkey); + /* always abort key exchange */ + return -1; +} + +static int +ssh2_capable(int remote_major, int remote_minor) +{ + switch (remote_major) { + case 1: + if (remote_minor == 99) + return 1; + break; + case 2: + return 1; + default: + break; + } + return 0; +} + +static void +keygrab_ssh2(con *c) +{ + char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; + int r; + + switch (c->c_keytype) { + case KT_DSA: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "ssh-dss-cert-v01@openssh.com" : "ssh-dss"; + break; + case KT_RSA: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "rsa-sha2-512-cert-v01@openssh.com," + "rsa-sha2-256-cert-v01@openssh.com," + "ssh-rsa-cert-v01@openssh.com" : + "rsa-sha2-512," + "rsa-sha2-256," + "ssh-rsa"; + break; + case KT_ED25519: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; + break; + case KT_XMSS: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com"; + break; + case KT_ECDSA: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "ecdsa-sha2-nistp256-cert-v01@openssh.com," + "ecdsa-sha2-nistp384-cert-v01@openssh.com," + "ecdsa-sha2-nistp521-cert-v01@openssh.com" : + "ecdsa-sha2-nistp256," + "ecdsa-sha2-nistp384," + "ecdsa-sha2-nistp521"; + break; + case KT_ECDSA_SK: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" : + "sk-ecdsa-sha2-nistp256@openssh.com"; + break; + case KT_ED25519_SK: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + "sk-ssh-ed25519-cert-v01@openssh.com" : + "sk-ssh-ed25519@openssh.com"; + break; + default: + fatal("unknown key type %d", c->c_keytype); + break; + } + if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { + free(c->c_ssh); + fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); + exit(1); + } +#ifdef WITH_OPENSSL + c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; + c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; + c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; + c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; + c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; + c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; + c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; +# ifdef OPENSSL_HAS_ECC + c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; +# endif +#endif + c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; + c->c_ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; + ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); + /* + * do the key-exchange until an error occurs or until + * the key_print_wrapper() callback sets c_done. + */ + ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done); +} + +static void +keyprint_one(const char *host, struct sshkey *key) +{ + char *hostport = NULL, *hashed = NULL; + const char *known_host; + + found_one = 1; + + if (print_sshfp) { + export_dns_rr(host, key, stdout, 0); + return; + } + + hostport = put_host_port(host, ssh_port); + lowercase(hostport); + if (hash_hosts && (hashed = host_hash(hostport, NULL, 0)) == NULL) + fatal("host_hash failed"); + known_host = hash_hosts ? hashed : hostport; + if (!get_cert) + fprintf(stdout, "%s ", known_host); + sshkey_write(key, stdout); + fputs("\n", stdout); + free(hashed); + free(hostport); +} + +static void +keyprint(con *c, struct sshkey *key) +{ + char *hosts = c->c_output_name ? c->c_output_name : c->c_name; + char *host, *ohosts; + + if (key == NULL) + return; + if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) { + keyprint_one(hosts, key); + return; + } + ohosts = hosts = xstrdup(hosts); + while ((host = strsep(&hosts, ",")) != NULL) + keyprint_one(host, key); + free(ohosts); +} + +static int +tcpconnect(char *host) +{ + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr, s = -1; + + snprintf(strport, sizeof strport, "%d", ssh_port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { + error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); + return -1; + } + for (ai = aitop; ai; ai = ai->ai_next) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == -1) { + error("socket: %s", strerror(errno)); + continue; + } + if (set_nonblock(s) == -1) + fatal_f("set_nonblock(%d)", s); + if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1 && + errno != EINPROGRESS) + error("connect (`%s'): %s", host, strerror(errno)); + else + break; + close(s); + s = -1; + } + freeaddrinfo(aitop); + return s; +} + +static int +conalloc(char *iname, char *oname, int keytype) +{ + char *namebase, *name, *namelist; + int s; + + namebase = namelist = xstrdup(iname); + + do { + name = xstrsep(&namelist, ","); + if (!name) { + free(namebase); + return (-1); + } + } while ((s = tcpconnect(name)) < 0); + + if (s >= maxfd) + fatal("conalloc: fdno %d too high", s); + if (fdcon[s].c_status) + fatal("conalloc: attempt to reuse fdno %d", s); + + debug3_f("oname %s kt %d", oname, keytype); + fdcon[s].c_fd = s; + fdcon[s].c_status = CS_CON; + fdcon[s].c_namebase = namebase; + fdcon[s].c_name = name; + fdcon[s].c_namelist = namelist; + fdcon[s].c_output_name = xstrdup(oname); + fdcon[s].c_data = (char *) &fdcon[s].c_plen; + fdcon[s].c_len = 4; + fdcon[s].c_off = 0; + fdcon[s].c_keytype = keytype; + monotime_ts(&fdcon[s].c_ts); + fdcon[s].c_ts.tv_sec += timeout; + TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); + read_wait[s].fd = s; + read_wait[s].events = POLLIN; + ncon++; + return (s); +} + +static void +confree(int s) +{ + if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) + fatal("confree: attempt to free bad fdno %d", s); + free(fdcon[s].c_namebase); + free(fdcon[s].c_output_name); + if (fdcon[s].c_status == CS_KEYS) + free(fdcon[s].c_data); + fdcon[s].c_status = CS_UNUSED; + fdcon[s].c_keytype = 0; + if (fdcon[s].c_ssh) { + ssh_packet_close(fdcon[s].c_ssh); + free(fdcon[s].c_ssh); + fdcon[s].c_ssh = NULL; + } else + close(s); + TAILQ_REMOVE(&tq, &fdcon[s], c_link); + read_wait[s].fd = -1; + read_wait[s].events = 0; + ncon--; +} + +static void +contouch(int s) +{ + TAILQ_REMOVE(&tq, &fdcon[s], c_link); + monotime_ts(&fdcon[s].c_ts); + fdcon[s].c_ts.tv_sec += timeout; + TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); +} + +static int +conrecycle(int s) +{ + con *c = &fdcon[s]; + int ret; + + ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); + confree(s); + return (ret); +} + +static void +congreet(int s) +{ + int n = 0, remote_major = 0, remote_minor = 0; + char buf[256], *cp; + char remote_version[sizeof buf]; + size_t bufsiz; + con *c = &fdcon[s]; + + /* send client banner */ + n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", + PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2); + if (n < 0 || (size_t)n >= sizeof(buf)) { + error("snprintf: buffer too small"); + confree(s); + return; + } + if (atomicio(vwrite, s, buf, n) != (size_t)n) { + error("write (%s): %s", c->c_name, strerror(errno)); + confree(s); + return; + } + + for (;;) { + memset(buf, '\0', sizeof(buf)); + bufsiz = sizeof(buf); + cp = buf; + while (bufsiz-- && + (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { + if (*cp == '\r') + *cp = '\n'; + cp++; + } + if (n != 1 || strncmp(buf, "SSH-", 4) == 0) + break; + } + if (n == 0) { + switch (errno) { + case EPIPE: + error("%s: Connection closed by remote host", c->c_name); + break; + case ECONNREFUSED: + break; + default: + error("read (%s): %s", c->c_name, strerror(errno)); + break; + } + conrecycle(s); + return; + } + if (*cp != '\n' && *cp != '\r') { + error("%s: bad greeting", c->c_name); + confree(s); + return; + } + *cp = '\0'; + if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) + fatal("ssh_packet_set_connection failed"); + ssh_packet_set_timeout(c->c_ssh, timeout, 1); + ssh_set_app_data(c->c_ssh, c); /* back link */ + c->c_ssh->compat = 0; + if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", + &remote_major, &remote_minor, remote_version) == 3) + compat_banner(c->c_ssh, remote_version); + if (!ssh2_capable(remote_major, remote_minor)) { + debug("%s doesn't support ssh2", c->c_name); + confree(s); + return; + } + fprintf(stderr, "%c %s:%d %s\n", print_sshfp ? ';' : '#', + c->c_name, ssh_port, chop(buf)); + keygrab_ssh2(c); + confree(s); +} + +static void +conread(int s) +{ + con *c = &fdcon[s]; + size_t n; + + if (c->c_status == CS_CON) { + congreet(s); + return; + } + n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); + if (n == 0) { + error("read (%s): %s", c->c_name, strerror(errno)); + confree(s); + return; + } + c->c_off += n; + + if (c->c_off == c->c_len) + switch (c->c_status) { + case CS_SIZE: + c->c_plen = htonl(c->c_plen); + c->c_len = c->c_plen + 8 - (c->c_plen & 7); + c->c_off = 0; + c->c_data = xmalloc(c->c_len); + c->c_status = CS_KEYS; + break; + default: + fatal("conread: invalid status %d", c->c_status); + break; + } + + contouch(s); +} + +static void +conloop(void) +{ + struct timespec seltime, now; + con *c; + int i; + + monotime_ts(&now); + c = TAILQ_FIRST(&tq); + + if (c && timespeccmp(&c->c_ts, &now, >)) + timespecsub(&c->c_ts, &now, &seltime); + else + timespecclear(&seltime); + + while (ppoll(read_wait, maxfd, &seltime, NULL) == -1) { + if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) + continue; + error("poll error"); + } + + for (i = 0; i < maxfd; i++) { + if (read_wait[i].revents & (POLLHUP|POLLERR|POLLNVAL)) + confree(i); + else if (read_wait[i].revents & (POLLIN|POLLHUP)) + conread(i); + } + + c = TAILQ_FIRST(&tq); + while (c && timespeccmp(&c->c_ts, &now, <)) { + int s = c->c_fd; + + c = TAILQ_NEXT(c, c_link); + conrecycle(s); + } +} + +static void +do_host(char *host) +{ + char *name = strnnsep(&host, " \t\n"); + int j; + + if (name == NULL) + return; + for (j = KT_MIN; j <= KT_MAX; j *= 2) { + if (get_keytypes & j) { + while (ncon >= MAXCON) + conloop(); + conalloc(name, *host ? host : name, j); + } + } +} + +void +sshfatal(const char *file, const char *func, int line, int showfunc, + LogLevel level, const char *suffix, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + sshlogv(file, func, line, showfunc, level, suffix, fmt, args); + va_end(args); + cleanup_exit(255); +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: %s [-46cDHv] [-f file] [-p port] [-T timeout] [-t type]\n" + "\t\t [host | addrlist namelist]\n", + __progname); + exit(1); +} + +int +main(int argc, char **argv) +{ + int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; + int opt, fopt_count = 0, j; + char *tname, *cp, *line = NULL; + size_t linesize = 0; + FILE *fp; + + extern int optind; + extern char *optarg; + + __progname = ssh_get_progname(argv[0]); + seed_rng(); + TAILQ_INIT(&tq); + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + if (argc <= 1) + usage(); + + while ((opt = getopt(argc, argv, "cDHv46p:T:t:f:")) != -1) { + switch (opt) { + case 'H': + hash_hosts = 1; + break; + case 'c': + get_cert = 1; + break; + case 'D': + print_sshfp = 1; + break; + case 'p': + ssh_port = a2port(optarg); + if (ssh_port <= 0) { + fprintf(stderr, "Bad port '%s'\n", optarg); + exit(1); + } + break; + case 'T': + timeout = convtime(optarg); + if (timeout == -1 || timeout == 0) { + fprintf(stderr, "Bad timeout '%s'\n", optarg); + usage(); + } + break; + case 'v': + if (!debug_flag) { + debug_flag = 1; + log_level = SYSLOG_LEVEL_DEBUG1; + } + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + else + fatal("Too high debugging level."); + break; + case 'f': + if (strcmp(optarg, "-") == 0) + optarg = NULL; + argv[fopt_count++] = optarg; + break; + case 't': + get_keytypes = 0; + tname = strtok(optarg, ","); + while (tname) { + int type = sshkey_type_from_name(tname); + + switch (type) { + case KEY_DSA: + get_keytypes |= KT_DSA; + break; + case KEY_ECDSA: + get_keytypes |= KT_ECDSA; + break; + case KEY_RSA: + get_keytypes |= KT_RSA; + break; + case KEY_ED25519: + get_keytypes |= KT_ED25519; + break; + case KEY_XMSS: + get_keytypes |= KT_XMSS; + break; + case KEY_ED25519_SK: + get_keytypes |= KT_ED25519_SK; + break; + case KEY_ECDSA_SK: + get_keytypes |= KT_ECDSA_SK; + break; + case KEY_UNSPEC: + default: + fatal("Unknown key type \"%s\"", tname); + } + tname = strtok(NULL, ","); + } + break; + case '4': + IPv4or6 = AF_INET; + break; + case '6': + IPv4or6 = AF_INET6; + break; + case '?': + default: + usage(); + } + } + if (optind == argc && !fopt_count) + usage(); + + log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); + + maxfd = fdlim_get(1); + if (maxfd < 0) + fatal("%s: fdlim_get: bad value", __progname); + if (maxfd > MAXMAXFD) + maxfd = MAXMAXFD; + if (MAXCON <= 0) + fatal("%s: not enough file descriptors", __progname); + if (maxfd > fdlim_get(0)) + fdlim_set(maxfd); + fdcon = xcalloc(maxfd, sizeof(con)); + read_wait = xcalloc(maxfd, sizeof(struct pollfd)); + for (j = 0; j < maxfd; j++) + read_wait[j].fd = -1; + + for (j = 0; j < fopt_count; j++) { + if (argv[j] == NULL) + fp = stdin; + else if ((fp = fopen(argv[j], "r")) == NULL) + fatal("%s: %s: %s", __progname, argv[j], strerror(errno)); + + while (getline(&line, &linesize, fp) != -1) { + /* Chomp off trailing whitespace and comments */ + if ((cp = strchr(line, '#')) == NULL) + cp = line + strlen(line) - 1; + while (cp >= line) { + if (*cp == ' ' || *cp == '\t' || + *cp == '\n' || *cp == '#') + *cp-- = '\0'; + else + break; + } + + /* Skip empty lines */ + if (*line == '\0') + continue; + + do_host(line); + } + + if (ferror(fp)) + fatal("%s: %s: %s", __progname, argv[j], strerror(errno)); + + fclose(fp); + } + free(line); + + while (optind < argc) + do_host(argv[optind++]); + + while (ncon > 0) + conloop(); + + return found_one ? 0 : 1; +} diff --git a/aosp/external/openssh/ssh-keysign.8 b/aosp/external/openssh/ssh-keysign.8 new file mode 100644 index 0000000000000000000000000000000000000000..6b4b9b270ba93f13a7164bd1c79d9fc5b773a4a9 --- /dev/null +++ b/aosp/external/openssh/ssh-keysign.8 @@ -0,0 +1,93 @@ +.\" $OpenBSD: ssh-keysign.8,v 1.17 2022/03/31 17:27:27 naddy Exp $ +.\" +.\" Copyright (c) 2002 Markus Friedl. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd $Mdocdate: March 31 2022 $ +.Dt SSH-KEYSIGN 8 +.Os +.Sh NAME +.Nm ssh-keysign +.Nd OpenSSH helper for host-based authentication +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is used by +.Xr ssh 1 +to access the local host keys and generate the digital signature +required during host-based authentication. +.Pp +.Nm +is disabled by default and can only be enabled in the +global client configuration file +.Pa /etc/ssh/ssh_config +by setting +.Cm EnableSSHKeysign +to +.Dq yes . +.Pp +.Nm +is not intended to be invoked by the user, but from +.Xr ssh 1 . +See +.Xr ssh 1 +and +.Xr sshd 8 +for more information about host-based authentication. +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa /etc/ssh/ssh_config +Controls whether +.Nm +is enabled. +.Pp +.It Pa /etc/ssh/ssh_host_dsa_key +.It Pa /etc/ssh/ssh_host_ecdsa_key +.It Pa /etc/ssh/ssh_host_ed25519_key +.It Pa /etc/ssh/ssh_host_rsa_key +These files contain the private parts of the host keys used to +generate the digital signature. +They should be owned by root, readable only by root, and not +accessible to others. +Since they are readable only by root, +.Nm +must be set-uid root if host-based authentication is used. +.Pp +.It Pa /etc/ssh/ssh_host_dsa_key-cert.pub +.It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub +.It Pa /etc/ssh/ssh_host_ed25519_key-cert.pub +.It Pa /etc/ssh/ssh_host_rsa_key-cert.pub +If these files exist, they are assumed to contain public certificate +information corresponding with the private keys above. +.El +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-keygen 1 , +.Xr ssh_config 5 , +.Xr sshd 8 +.Sh HISTORY +.Nm +first appeared in +.Ox 3.2 . +.Sh AUTHORS +.An Markus Friedl Aq Mt markus@openbsd.org diff --git a/aosp/external/openssh/ssh-keysign.c b/aosp/external/openssh/ssh-keysign.c new file mode 100644 index 0000000000000000000000000000000000000000..c52321e220e7dba3f031389c3328a81f48220a30 --- /dev/null +++ b/aosp/external/openssh/ssh-keysign.c @@ -0,0 +1,308 @@ +/* $OpenBSD: ssh-keysign.c,v 1.70 2022/01/06 22:00:18 djm Exp $ */ +/* + * Copyright (c) 2002 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include "openbsd-compat/openssl-compat.h" +#endif + +#include "xmalloc.h" +#include "log.h" +#include "sshkey.h" +#include "ssh.h" +#include "ssh2.h" +#include "misc.h" +#include "sshbuf.h" +#include "authfile.h" +#include "msg.h" +#include "canohost.h" +#include "pathnames.h" +#include "readconf.h" +#include "uidswap.h" +#include "ssherr.h" + +extern char *__progname; + +static int +valid_request(struct passwd *pw, char *host, struct sshkey **ret, char **pkalgp, + u_char *data, size_t datalen) +{ + struct sshbuf *b; + struct sshkey *key = NULL; + u_char type, *pkblob; + char *p; + size_t blen, len; + char *pkalg, *luser; + int r, pktype, fail; + + if (ret != NULL) + *ret = NULL; + if (pkalgp != NULL) + *pkalgp = NULL; + fail = 0; + + if ((b = sshbuf_from(data, datalen)) == NULL) + fatal_f("sshbuf_from failed"); + + /* session id */ + if ((r = sshbuf_get_string(b, NULL, &len)) != 0) + fatal_fr(r, "parse session ID"); + if (len != 20 && /* SHA1 */ + len != 32 && /* SHA256 */ + len != 48 && /* SHA384 */ + len != 64) /* SHA512 */ + fail++; + + if ((r = sshbuf_get_u8(b, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != SSH2_MSG_USERAUTH_REQUEST) + fail++; + + /* server user */ + if ((r = sshbuf_skip_string(b)) != 0) + fatal_fr(r, "parse user"); + + /* service */ + if ((r = sshbuf_get_cstring(b, &p, NULL)) != 0) + fatal_fr(r, "parse service"); + if (strcmp("ssh-connection", p) != 0) + fail++; + free(p); + + /* method */ + if ((r = sshbuf_get_cstring(b, &p, NULL)) != 0) + fatal_fr(r, "parse method"); + if (strcmp("hostbased", p) != 0) + fail++; + free(p); + + /* pubkey */ + if ((r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0 || + (r = sshbuf_get_string(b, &pkblob, &blen)) != 0) + fatal_fr(r, "parse pk"); + + pktype = sshkey_type_from_name(pkalg); + if (pktype == KEY_UNSPEC) + fail++; + else if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + error_fr(r, "decode key"); + fail++; + } else if (key->type != pktype) + fail++; + + /* client host name, handle trailing dot */ + if ((r = sshbuf_get_cstring(b, &p, &len)) != 0) + fatal_fr(r, "parse hostname"); + debug2_f("check expect chost %s got %s", host, p); + if (strlen(host) != len - 1) + fail++; + else if (p[len - 1] != '.') + fail++; + else if (strncasecmp(host, p, len - 1) != 0) + fail++; + free(p); + + /* local user */ + if ((r = sshbuf_get_cstring(b, &luser, NULL)) != 0) + fatal_fr(r, "parse luser"); + + if (strcmp(pw->pw_name, luser) != 0) + fail++; + free(luser); + + /* end of message */ + if (sshbuf_len(b) != 0) + fail++; + sshbuf_free(b); + + debug3_f("fail %d", fail); + + if (fail) + sshkey_free(key); + else { + if (ret != NULL) { + *ret = key; + key = NULL; + } + if (pkalgp != NULL) { + *pkalgp = pkalg; + pkalg = NULL; + } + } + sshkey_free(key); + free(pkalg); + free(pkblob); + + return (fail ? -1 : 0); +} + +int +main(int argc, char **argv) +{ + struct sshbuf *b; + Options options; +#define NUM_KEYTYPES 5 + struct sshkey *keys[NUM_KEYTYPES], *key = NULL; + struct passwd *pw; + int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; + u_char *signature, *data, rver; + char *host, *fp, *pkalg; + size_t slen, dlen; + + if (pledge("stdio rpath getpw dns id", NULL) != 0) + fatal("%s: pledge: %s", __progname, strerror(errno)); + + /* Ensure that stdin and stdout are connected */ + if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) + exit(1); + /* Leave /dev/null fd iff it is attached to stderr */ + if (fd > 2) + close(fd); + + i = 0; + /* XXX This really needs to read sshd_config for the paths */ + key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); + key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); + key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY); + key_fd[i++] = open(_PATH_HOST_XMSS_KEY_FILE, O_RDONLY); + key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); + + if ((pw = getpwuid(getuid())) == NULL) + fatal("getpwuid failed"); + pw = pwcopy(pw); + + permanently_set_uid(pw); + + seed_rng(); + +#ifdef DEBUG_SSH_KEYSIGN + log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); +#endif + + /* verify that ssh-keysign is enabled by the admin */ + initialize_options(&options); + (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", + &options, 0, NULL); + (void)fill_default_options(&options); + if (options.enable_ssh_keysign != 1) + fatal("ssh-keysign not enabled in %s", + _PATH_HOST_CONFIG_FILE); + + if (pledge("stdio dns", NULL) != 0) + fatal("%s: pledge: %s", __progname, strerror(errno)); + + for (i = found = 0; i < NUM_KEYTYPES; i++) { + if (key_fd[i] != -1) + found = 1; + } + if (found == 0) + fatal("could not open any host key"); + + found = 0; + for (i = 0; i < NUM_KEYTYPES; i++) { + keys[i] = NULL; + if (key_fd[i] == -1) + continue; + r = sshkey_load_private_type_fd(key_fd[i], KEY_UNSPEC, + NULL, &key, NULL); + close(key_fd[i]); + if (r != 0) + debug_r(r, "parse key %d", i); + else if (key != NULL) { + keys[i] = key; + found = 1; + } + } + if (!found) + fatal("no hostkey found"); + + if ((b = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + if (ssh_msg_recv(STDIN_FILENO, b) < 0) + fatal("%s: ssh_msg_recv failed", __progname); + if ((r = sshbuf_get_u8(b, &rver)) != 0) + fatal_r(r, "%s: buffer error", __progname); + if (rver != version) + fatal("%s: bad version: received %d, expected %d", + __progname, rver, version); + if ((r = sshbuf_get_u32(b, (u_int *)&fd)) != 0) + fatal_r(r, "%s: buffer error", __progname); + if (fd < 0 || fd == STDIN_FILENO || fd == STDOUT_FILENO) + fatal("%s: bad fd = %d", __progname, fd); + if ((host = get_local_name(fd)) == NULL) + fatal("%s: cannot get local name for fd", __progname); + + if ((r = sshbuf_get_string(b, &data, &dlen)) != 0) + fatal_r(r, "%s: buffer error", __progname); + if (valid_request(pw, host, &key, &pkalg, data, dlen) < 0) + fatal("%s: not a valid request", __progname); + free(host); + + found = 0; + for (i = 0; i < NUM_KEYTYPES; i++) { + if (keys[i] != NULL && + sshkey_equal_public(key, keys[i])) { + found = 1; + break; + } + } + if (!found) { + if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal("%s: sshkey_fingerprint failed", __progname); + fatal("%s: no matching hostkey found for key %s %s", __progname, + sshkey_type(key), fp ? fp : ""); + } + + if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen, + pkalg, NULL, NULL, 0)) != 0) + fatal_r(r, "%s: sshkey_sign failed", __progname); + free(data); + + /* send reply */ + sshbuf_reset(b); + if ((r = sshbuf_put_string(b, signature, slen)) != 0) + fatal_r(r, "%s: buffer error", __progname); + if (ssh_msg_send(STDOUT_FILENO, version, b) == -1) + fatal("%s: ssh_msg_send failed", __progname); + + return (0); +} diff --git a/aosp/external/openssh/ssh-pkcs11-client.c b/aosp/external/openssh/ssh-pkcs11-client.c new file mode 100644 index 0000000000000000000000000000000000000000..cfd833d74054f45098c2aa1f8dcbc057ca042cee --- /dev/null +++ b/aosp/external/openssh/ssh-pkcs11-client.c @@ -0,0 +1,391 @@ +/* $OpenBSD: ssh-pkcs11-client.c,v 1.17 2020/10/18 11:32:02 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * Copyright (c) 2014 Pedro Martelletto. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef ENABLE_PKCS11 + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#include +#include +#include +#include + +#include +#include + +#include "openbsd-compat/openssl-compat.h" + +#include "pathnames.h" +#include "xmalloc.h" +#include "sshbuf.h" +#include "log.h" +#include "misc.h" +#include "sshkey.h" +#include "authfd.h" +#include "atomicio.h" +#include "ssh-pkcs11.h" +#include "ssherr.h" + +/* borrows code from sftp-server and ssh-agent */ + +static int fd = -1; +static pid_t pid = -1; + +static void +send_msg(struct sshbuf *m) +{ + u_char buf[4]; + size_t mlen = sshbuf_len(m); + int r; + + POKE_U32(buf, mlen); + if (atomicio(vwrite, fd, buf, 4) != 4 || + atomicio(vwrite, fd, sshbuf_mutable_ptr(m), + sshbuf_len(m)) != sshbuf_len(m)) + error("write to helper failed"); + if ((r = sshbuf_consume(m, mlen)) != 0) + fatal_fr(r, "consume"); +} + +static int +recv_msg(struct sshbuf *m) +{ + u_int l, len; + u_char c, buf[1024]; + int r; + + if ((len = atomicio(read, fd, buf, 4)) != 4) { + error("read from helper failed: %u", len); + return (0); /* XXX */ + } + len = PEEK_U32(buf); + if (len > 256 * 1024) + fatal("response too long: %u", len); + /* read len bytes into m */ + sshbuf_reset(m); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + if (atomicio(read, fd, buf, l) != l) { + error("response from helper failed."); + return (0); /* XXX */ + } + if ((r = sshbuf_put(m, buf, l)) != 0) + fatal_fr(r, "sshbuf_put"); + len -= l; + } + if ((r = sshbuf_get_u8(m, &c)) != 0) + fatal_fr(r, "parse type"); + return c; +} + +int +pkcs11_init(int interactive) +{ + return (0); +} + +void +pkcs11_terminate(void) +{ + if (fd >= 0) + close(fd); +} + +static int +rsa_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, int padding) +{ + struct sshkey *key = NULL; + struct sshbuf *msg = NULL; + u_char *blob = NULL, *signature = NULL; + size_t blen, slen = 0; + int r, ret = -1; + + if (padding != RSA_PKCS1_PADDING) + goto fail; + key = sshkey_new(KEY_UNSPEC); + if (key == NULL) { + error_f("sshkey_new failed"); + goto fail; + } + key->type = KEY_RSA; + RSA_up_ref(rsa); + key->rsa = rsa; + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { + error_fr(r, "encode key"); + goto fail; + } + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || + (r = sshbuf_put_string(msg, blob, blen)) != 0 || + (r = sshbuf_put_string(msg, from, flen)) != 0 || + (r = sshbuf_put_u32(msg, 0)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_reset(msg); + + if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { + if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) + fatal_fr(r, "parse"); + if (slen <= (size_t)RSA_size(rsa)) { + memcpy(to, signature, slen); + ret = slen; + } + free(signature); + } + fail: + free(blob); + sshkey_free(key); + sshbuf_free(msg); + return (ret); +} + +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) +static ECDSA_SIG * +ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, + const BIGNUM *rp, EC_KEY *ec) +{ + struct sshkey *key = NULL; + struct sshbuf *msg = NULL; + ECDSA_SIG *ret = NULL; + const u_char *cp; + u_char *blob = NULL, *signature = NULL; + size_t blen, slen = 0; + int r, nid; + + nid = sshkey_ecdsa_key_to_nid(ec); + if (nid < 0) { + error_f("couldn't get curve nid"); + goto fail; + } + + key = sshkey_new(KEY_UNSPEC); + if (key == NULL) { + error_f("sshkey_new failed"); + goto fail; + } + key->ecdsa = ec; + key->ecdsa_nid = nid; + key->type = KEY_ECDSA; + EC_KEY_up_ref(ec); + + if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) { + error_fr(r, "encode key"); + goto fail; + } + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || + (r = sshbuf_put_string(msg, blob, blen)) != 0 || + (r = sshbuf_put_string(msg, dgst, dgst_len)) != 0 || + (r = sshbuf_put_u32(msg, 0)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_reset(msg); + + if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { + if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) + fatal_fr(r, "parse"); + cp = signature; + ret = d2i_ECDSA_SIG(NULL, &cp, slen); + free(signature); + } + + fail: + free(blob); + sshkey_free(key); + sshbuf_free(msg); + return (ret); +} +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + +static RSA_METHOD *helper_rsa; +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) +static EC_KEY_METHOD *helper_ecdsa; +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + +/* redirect private key crypto operations to the ssh-pkcs11-helper */ +static void +wrap_key(struct sshkey *k) +{ + if (k->type == KEY_RSA) + RSA_set_method(k->rsa, helper_rsa); +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) + else if (k->type == KEY_ECDSA) + EC_KEY_set_method(k->ecdsa, helper_ecdsa); +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + else + fatal_f("unknown key type"); +} + +static int +pkcs11_start_helper_methods(void) +{ + if (helper_rsa != NULL) + return (0); + +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) + int (*orig_sign)(int, const unsigned char *, int, unsigned char *, + unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; + if (helper_ecdsa != NULL) + return (0); + helper_ecdsa = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); + if (helper_ecdsa == NULL) + return (-1); + EC_KEY_METHOD_get_sign(helper_ecdsa, &orig_sign, NULL, NULL); + EC_KEY_METHOD_set_sign(helper_ecdsa, orig_sign, NULL, ecdsa_do_sign); +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + + if ((helper_rsa = RSA_meth_dup(RSA_get_default_method())) == NULL) + fatal_f("RSA_meth_dup failed"); + if (!RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper") || + !RSA_meth_set_priv_enc(helper_rsa, rsa_encrypt)) + fatal_f("failed to prepare method"); + + return (0); +} + +static int +pkcs11_start_helper(void) +{ + int pair[2]; + char *helper, *verbosity = NULL; + + if (log_level_get() >= SYSLOG_LEVEL_DEBUG1) + verbosity = "-vvv"; + + if (pkcs11_start_helper_methods() == -1) { + error("pkcs11_start_helper_methods failed"); + return (-1); + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + error("socketpair: %s", strerror(errno)); + return (-1); + } + if ((pid = fork()) == -1) { + error("fork: %s", strerror(errno)); + return (-1); + } else if (pid == 0) { + if ((dup2(pair[1], STDIN_FILENO) == -1) || + (dup2(pair[1], STDOUT_FILENO) == -1)) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + _exit(1); + } + close(pair[0]); + close(pair[1]); + helper = getenv("SSH_PKCS11_HELPER"); + if (helper == NULL || strlen(helper) == 0) + helper = _PATH_SSH_PKCS11_HELPER; + debug_f("starting %s %s", helper, + verbosity == NULL ? "" : verbosity); + execlp(helper, helper, verbosity, (char *)NULL); + fprintf(stderr, "exec: %s: %s\n", helper, strerror(errno)); + _exit(1); + } + close(pair[1]); + fd = pair[0]; + return (0); +} + +int +pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp, + char ***labelsp) +{ + struct sshkey *k; + int r, type; + u_char *blob; + char *label; + size_t blen; + u_int nkeys, i; + struct sshbuf *msg; + + if (fd < 0 && pkcs11_start_helper() < 0) + return (-1); + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH_AGENTC_ADD_SMARTCARD_KEY)) != 0 || + (r = sshbuf_put_cstring(msg, name)) != 0 || + (r = sshbuf_put_cstring(msg, pin)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_reset(msg); + + type = recv_msg(msg); + if (type == SSH2_AGENT_IDENTITIES_ANSWER) { + if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) + fatal_fr(r, "parse nkeys"); + *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); + if (labelsp) + *labelsp = xcalloc(nkeys, sizeof(char *)); + for (i = 0; i < nkeys; i++) { + /* XXX clean up properly instead of fatal() */ + if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || + (r = sshbuf_get_cstring(msg, &label, NULL)) != 0) + fatal_fr(r, "parse key"); + if ((r = sshkey_from_blob(blob, blen, &k)) != 0) + fatal_fr(r, "decode key"); + wrap_key(k); + (*keysp)[i] = k; + if (labelsp) + (*labelsp)[i] = label; + else + free(label); + free(blob); + } + } else if (type == SSH2_AGENT_FAILURE) { + if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) + nkeys = -1; + } else { + nkeys = -1; + } + sshbuf_free(msg); + return (nkeys); +} + +int +pkcs11_del_provider(char *name) +{ + int r, ret = -1; + struct sshbuf *msg; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY)) != 0 || + (r = sshbuf_put_cstring(msg, name)) != 0 || + (r = sshbuf_put_cstring(msg, "")) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_reset(msg); + + if (recv_msg(msg) == SSH_AGENT_SUCCESS) + ret = 0; + sshbuf_free(msg); + return (ret); +} + +#endif /* ENABLE_PKCS11 */ diff --git a/aosp/external/openssh/ssh-pkcs11-helper.8 b/aosp/external/openssh/ssh-pkcs11-helper.8 new file mode 100644 index 0000000000000000000000000000000000000000..6a592b1f36e39077abc1b77ad7b989532fdd0f45 --- /dev/null +++ b/aosp/external/openssh/ssh-pkcs11-helper.8 @@ -0,0 +1,66 @@ +.\" $OpenBSD: ssh-pkcs11-helper.8,v 1.6 2019/11/30 07:07:59 jmc Exp $ +.\" +.\" Copyright (c) 2010 Markus Friedl. All rights reserved. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: November 30 2019 $ +.Dt SSH-PKCS11-HELPER 8 +.Os +.Sh NAME +.Nm ssh-pkcs11-helper +.Nd OpenSSH helper for PKCS#11 support +.Sh SYNOPSIS +.Nm +.Op Fl v +.Sh DESCRIPTION +.Nm +is used by +.Xr ssh-agent 1 +to access keys provided by a PKCS#11 token. +.Pp +.Nm +is not intended to be invoked by the user, but from +.Xr ssh-agent 1 . +.Pp +A single option is supported: +.Bl -tag -width Ds +.It Fl v +Verbose mode. +Causes +.Nm +to print debugging messages about its progress. +This is helpful in debugging problems. +Multiple +.Fl v +options increase the verbosity. +The maximum is 3. +.Pp +Note that +.Xr ssh-agent 1 +will automatically pass the +.Fl v +flag to +.Nm +when it has itself been placed in debug mode. +.El +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 +.Sh HISTORY +.Nm +first appeared in +.Ox 4.7 . +.Sh AUTHORS +.An Markus Friedl Aq Mt markus@openbsd.org diff --git a/aosp/external/openssh/ssh-pkcs11-helper.c b/aosp/external/openssh/ssh-pkcs11-helper.c new file mode 100644 index 0000000000000000000000000000000000000000..5c3eaaeb019770424c8526b50ca6e2db33334f5a --- /dev/null +++ b/aosp/external/openssh/ssh-pkcs11-helper.c @@ -0,0 +1,446 @@ +/* $OpenBSD: ssh-pkcs11-helper.c,v 1.26 2021/11/18 03:31:44 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include "openbsd-compat/sys-queue.h" + +#include +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include + +#include "xmalloc.h" +#include "sshbuf.h" +#include "log.h" +#include "misc.h" +#include "sshkey.h" +#include "authfd.h" +#include "ssh-pkcs11.h" +#include "ssherr.h" + +#ifdef ENABLE_PKCS11 + +#ifdef WITH_OPENSSL + +/* borrows code from sftp-server and ssh-agent */ + +struct pkcs11_keyinfo { + struct sshkey *key; + char *providername, *label; + TAILQ_ENTRY(pkcs11_keyinfo) next; +}; + +TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist; + +#define MAX_MSG_LENGTH 10240 /*XXX*/ + +/* input and output queue */ +struct sshbuf *iqueue; +struct sshbuf *oqueue; + +static void +add_key(struct sshkey *k, char *name, char *label) +{ + struct pkcs11_keyinfo *ki; + + ki = xcalloc(1, sizeof(*ki)); + ki->providername = xstrdup(name); + ki->key = k; + ki->label = xstrdup(label); + TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next); +} + +static void +del_keys_by_name(char *name) +{ + struct pkcs11_keyinfo *ki, *nxt; + + for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) { + nxt = TAILQ_NEXT(ki, next); + if (!strcmp(ki->providername, name)) { + TAILQ_REMOVE(&pkcs11_keylist, ki, next); + free(ki->providername); + free(ki->label); + sshkey_free(ki->key); + free(ki); + } + } +} + +/* lookup matching 'private' key */ +static struct sshkey * +lookup_key(struct sshkey *k) +{ + struct pkcs11_keyinfo *ki; + + TAILQ_FOREACH(ki, &pkcs11_keylist, next) { + debug("check %s %s %s", sshkey_type(ki->key), + ki->providername, ki->label); + if (sshkey_equal(k, ki->key)) + return (ki->key); + } + return (NULL); +} + +static void +send_msg(struct sshbuf *m) +{ + int r; + + if ((r = sshbuf_put_stringb(oqueue, m)) != 0) + fatal_fr(r, "enqueue"); +} + +static void +process_add(void) +{ + char *name, *pin; + struct sshkey **keys = NULL; + int r, i, nkeys; + u_char *blob; + size_t blen; + struct sshbuf *msg; + char **labels = NULL; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0) + fatal_fr(r, "parse"); + if ((nkeys = pkcs11_add_provider(name, pin, &keys, &labels)) > 0) { + if ((r = sshbuf_put_u8(msg, + SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || + (r = sshbuf_put_u32(msg, nkeys)) != 0) + fatal_fr(r, "compose"); + for (i = 0; i < nkeys; i++) { + if ((r = sshkey_to_blob(keys[i], &blob, &blen)) != 0) { + debug_fr(r, "encode key"); + continue; + } + if ((r = sshbuf_put_string(msg, blob, blen)) != 0 || + (r = sshbuf_put_cstring(msg, labels[i])) != 0) + fatal_fr(r, "compose key"); + free(blob); + add_key(keys[i], name, labels[i]); + free(labels[i]); + } + } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0 || + (r = sshbuf_put_u32(msg, -nkeys)) != 0) + fatal_fr(r, "compose"); + free(labels); + free(keys); /* keys themselves are transferred to pkcs11_keylist */ + free(pin); + free(name); + send_msg(msg); + sshbuf_free(msg); +} + +static void +process_del(void) +{ + char *name, *pin; + struct sshbuf *msg; + int r; + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = sshbuf_get_cstring(iqueue, &pin, NULL)) != 0) + fatal_fr(r, "parse"); + del_keys_by_name(name); + if ((r = sshbuf_put_u8(msg, pkcs11_del_provider(name) == 0 ? + SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) + fatal_fr(r, "compose"); + free(pin); + free(name); + send_msg(msg); + sshbuf_free(msg); +} + +static void +process_sign(void) +{ + u_char *blob, *data, *signature = NULL; + size_t blen, dlen, slen = 0; + int r, ok = -1; + struct sshkey *key, *found; + struct sshbuf *msg; + + /* XXX support SHA2 signature flags */ + if ((r = sshbuf_get_string(iqueue, &blob, &blen)) != 0 || + (r = sshbuf_get_string(iqueue, &data, &dlen)) != 0 || + (r = sshbuf_get_u32(iqueue, NULL)) != 0) + fatal_fr(r, "parse"); + + if ((r = sshkey_from_blob(blob, blen, &key)) != 0) + fatal_fr(r, "decode key"); + else { + if ((found = lookup_key(key)) != NULL) { +#ifdef WITH_OPENSSL + int ret; + + if (key->type == KEY_RSA) { + slen = RSA_size(key->rsa); + signature = xmalloc(slen); + ret = RSA_private_encrypt(dlen, data, signature, + found->rsa, RSA_PKCS1_PADDING); + if (ret != -1) { + slen = ret; + ok = 0; + } +#ifdef OPENSSL_HAS_ECC + } else if (key->type == KEY_ECDSA) { + u_int xslen = ECDSA_size(key->ecdsa); + + signature = xmalloc(xslen); + /* "The parameter type is ignored." */ + ret = ECDSA_sign(-1, data, dlen, signature, + &xslen, found->ecdsa); + if (ret != 0) + ok = 0; + else + error_f("ECDSA_sign returned %d", ret); + slen = xslen; +#endif /* OPENSSL_HAS_ECC */ + } else + error_f("don't know how to sign with key " + "type %d", (int)key->type); +#endif /* WITH_OPENSSL */ + } + sshkey_free(key); + } + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (ok == 0) { + if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || + (r = sshbuf_put_string(msg, signature, slen)) != 0) + fatal_fr(r, "compose response"); + } else { + if ((r = sshbuf_put_u8(msg, SSH2_AGENT_FAILURE)) != 0) + fatal_fr(r, "compose failure response"); + } + free(data); + free(blob); + free(signature); + send_msg(msg); + sshbuf_free(msg); +} + +static void +process(void) +{ + u_int msg_len; + u_int buf_len; + u_int consumed; + u_char type; + const u_char *cp; + int r; + + buf_len = sshbuf_len(iqueue); + if (buf_len < 5) + return; /* Incomplete message. */ + cp = sshbuf_ptr(iqueue); + msg_len = get_u32(cp); + if (msg_len > MAX_MSG_LENGTH) { + error("bad message len %d", msg_len); + cleanup_exit(11); + } + if (buf_len < msg_len + 4) + return; + if ((r = sshbuf_consume(iqueue, 4)) != 0 || + (r = sshbuf_get_u8(iqueue, &type)) != 0) + fatal_fr(r, "parse type/len"); + buf_len -= 4; + switch (type) { + case SSH_AGENTC_ADD_SMARTCARD_KEY: + debug("process_add"); + process_add(); + break; + case SSH_AGENTC_REMOVE_SMARTCARD_KEY: + debug("process_del"); + process_del(); + break; + case SSH2_AGENTC_SIGN_REQUEST: + debug("process_sign"); + process_sign(); + break; + default: + error("Unknown message %d", type); + break; + } + /* discard the remaining bytes from the current packet */ + if (buf_len < sshbuf_len(iqueue)) { + error("iqueue grew unexpectedly"); + cleanup_exit(255); + } + consumed = buf_len - sshbuf_len(iqueue); + if (msg_len < consumed) { + error("msg_len %d < consumed %d", msg_len, consumed); + cleanup_exit(255); + } + if (msg_len > consumed) { + if ((r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) + fatal_fr(r, "consume"); + } +} + +void +cleanup_exit(int i) +{ + /* XXX */ + _exit(i); +} + + +int +main(int argc, char **argv) +{ + int r, ch, in, out, log_stderr = 0; + ssize_t len; + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_ERROR; + char buf[4*4096]; + extern char *__progname; + struct pollfd pfd[2]; + + __progname = ssh_get_progname(argv[0]); + seed_rng(); + TAILQ_INIT(&pkcs11_keylist); + + log_init(__progname, log_level, log_facility, log_stderr); + + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + log_stderr = 1; + if (log_level == SYSLOG_LEVEL_ERROR) + log_level = SYSLOG_LEVEL_DEBUG1; + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + break; + default: + fprintf(stderr, "usage: %s [-v]\n", __progname); + exit(1); + } + } + + log_init(__progname, log_level, log_facility, log_stderr); + + pkcs11_init(0); + in = STDIN_FILENO; + out = STDOUT_FILENO; + + if ((iqueue = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((oqueue = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + while (1) { + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = in; + pfd[1].fd = out; + + /* + * Ensure that we can read a full buffer and handle + * the worst-case length packet it can generate, + * otherwise apply backpressure by stopping reads. + */ + if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && + (r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0) + pfd[0].events = POLLIN; + else if (r != SSH_ERR_NO_BUFFER_SPACE) + fatal_fr(r, "reserve"); + + if (sshbuf_len(oqueue) > 0) + pfd[1].events = POLLOUT; + + if ((r = poll(pfd, 2, -1 /* INFTIM */)) <= 0) { + if (r == 0 || errno == EINTR) + continue; + fatal("poll: %s", strerror(errno)); + } + + /* copy stdin to iqueue */ + if ((pfd[0].revents & (POLLIN|POLLHUP|POLLERR)) != 0) { + len = read(in, buf, sizeof buf); + if (len == 0) { + debug("read eof"); + cleanup_exit(0); + } else if (len < 0) { + error("read: %s", strerror(errno)); + cleanup_exit(1); + } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) + fatal_fr(r, "sshbuf_put"); + } + /* send oqueue to stdout */ + if ((pfd[1].revents & (POLLOUT|POLLHUP)) != 0) { + len = write(out, sshbuf_ptr(oqueue), + sshbuf_len(oqueue)); + if (len < 0) { + error("write: %s", strerror(errno)); + cleanup_exit(1); + } else if ((r = sshbuf_consume(oqueue, len)) != 0) + fatal_fr(r, "consume"); + } + + /* + * Process requests from client if we can fit the results + * into the output buffer, otherwise stop processing input + * and let the output queue drain. + */ + if ((r = sshbuf_check_reserve(oqueue, MAX_MSG_LENGTH)) == 0) + process(); + else if (r != SSH_ERR_NO_BUFFER_SPACE) + fatal_fr(r, "reserve"); + } +} + +#else /* WITH_OPENSSL */ +void +cleanup_exit(int i) +{ + _exit(i); +} + +int +main(int argc, char **argv) +{ + fprintf(stderr, "PKCS#11 code is not enabled\n"); + return 1; +} +#endif /* WITH_OPENSSL */ +#else /* ENABLE_PKCS11 */ +int +main(int argc, char **argv) +{ + extern char *__progname; + + __progname = ssh_get_progname(argv[0]); + log_init(__progname, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTH, 0); + fatal("PKCS#11 support disabled at compile time"); +} +#endif /* ENABLE_PKCS11 */ diff --git a/aosp/external/openssh/ssh-pkcs11.c b/aosp/external/openssh/ssh-pkcs11.c new file mode 100644 index 0000000000000000000000000000000000000000..b2e2b32a5078f355fc10524273d23306fbc3b355 --- /dev/null +++ b/aosp/external/openssh/ssh-pkcs11.c @@ -0,0 +1,1887 @@ +/* $OpenBSD: ssh-pkcs11.c,v 1.55 2021/11/18 21:11:01 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * Copyright (c) 2014 Pedro Martelletto. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef ENABLE_PKCS11 + +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include + +#include +#include +#include + +#include "openbsd-compat/sys-queue.h" +#include "openbsd-compat/openssl-compat.h" + +#include +#include +#include + +#define CRYPTOKI_COMPAT +#include "pkcs11.h" + +#include "log.h" +#include "misc.h" +#include "sshkey.h" +#include "ssh-pkcs11.h" +#include "digest.h" +#include "xmalloc.h" + +struct pkcs11_slotinfo { + CK_TOKEN_INFO token; + CK_SESSION_HANDLE session; + int logged_in; +}; + +struct pkcs11_provider { + char *name; + void *handle; + CK_FUNCTION_LIST *function_list; + CK_INFO info; + CK_ULONG nslots; + CK_SLOT_ID *slotlist; + struct pkcs11_slotinfo *slotinfo; + int valid; + int refcount; + TAILQ_ENTRY(pkcs11_provider) next; +}; + +TAILQ_HEAD(, pkcs11_provider) pkcs11_providers; + +struct pkcs11_key { + struct pkcs11_provider *provider; + CK_ULONG slotidx; + char *keyid; + int keyid_len; +}; + +int pkcs11_interactive = 0; + +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) +static void +ossl_error(const char *msg) +{ + unsigned long e; + + error_f("%s", msg); + while ((e = ERR_get_error()) != 0) + error_f("libcrypto error: %s", ERR_error_string(e, NULL)); +} +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + +int +pkcs11_init(int interactive) +{ + pkcs11_interactive = interactive; + TAILQ_INIT(&pkcs11_providers); + return (0); +} + +/* + * finalize a provider shared library, it's no longer usable. + * however, there might still be keys referencing this provider, + * so the actual freeing of memory is handled by pkcs11_provider_unref(). + * this is called when a provider gets unregistered. + */ +static void +pkcs11_provider_finalize(struct pkcs11_provider *p) +{ + CK_RV rv; + CK_ULONG i; + + debug_f("provider \"%s\" refcount %d valid %d", + p->name, p->refcount, p->valid); + if (!p->valid) + return; + for (i = 0; i < p->nslots; i++) { + if (p->slotinfo[i].session && + (rv = p->function_list->C_CloseSession( + p->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } + if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); + p->valid = 0; + p->function_list = NULL; + dlclose(p->handle); +} + +/* + * remove a reference to the provider. + * called when a key gets destroyed or when the provider is unregistered. + */ +static void +pkcs11_provider_unref(struct pkcs11_provider *p) +{ + debug_f("provider \"%s\" refcount %d", p->name, p->refcount); + if (--p->refcount <= 0) { + if (p->valid) + error_f("provider \"%s\" still valid", p->name); + free(p->name); + free(p->slotlist); + free(p->slotinfo); + free(p); + } +} + +/* unregister all providers, keys might still point to the providers */ +void +pkcs11_terminate(void) +{ + struct pkcs11_provider *p; + + while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + } +} + +/* lookup provider by name */ +static struct pkcs11_provider * +pkcs11_provider_lookup(char *provider_id) +{ + struct pkcs11_provider *p; + + TAILQ_FOREACH(p, &pkcs11_providers, next) { + debug("check provider \"%s\"", p->name); + if (!strcmp(provider_id, p->name)) + return (p); + } + return (NULL); +} + +/* unregister provider by name */ +int +pkcs11_del_provider(char *provider_id) +{ + struct pkcs11_provider *p; + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + return (0); + } + return (-1); +} + +static RSA_METHOD *rsa_method; +static int rsa_idx = 0; +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) +static EC_KEY_METHOD *ec_key_method; +static int ec_key_idx = 0; +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + +/* release a wrapped object */ +static void +pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, + long argl, void *argp) +{ + struct pkcs11_key *k11 = ptr; + + debug_f("parent %p ptr %p idx %d", parent, ptr, idx); + if (k11 == NULL) + return; + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); + free(k11); +} + +/* find a single 'obj' for given attributes */ +static int +pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, + CK_ULONG nattr, CK_OBJECT_HANDLE *obj) +{ + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + CK_ULONG nfound = 0; + CK_RV rv; + int ret = -1; + + f = p->function_list; + session = p->slotinfo[slotidx].session; + if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { + error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); + return (-1); + } + if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK || + nfound != 1) { + debug("C_FindObjects failed (nfound %lu nattr %lu): %lu", + nfound, nattr, rv); + } else + ret = 0; + if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) + error("C_FindObjectsFinal failed: %lu", rv); + return (ret); +} + +static int +pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si, + CK_USER_TYPE type) +{ + char *pin = NULL, prompt[1024]; + CK_RV rv; + + if (provider == NULL || si == NULL || !provider->valid) { + error("no pkcs11 (valid) provider found"); + return (-1); + } + + if (!pkcs11_interactive) { + error("need pin entry%s", + (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ? + " on reader keypad" : ""); + return (-1); + } + if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) + verbose("Deferring PIN entry to reader keypad."); + else { + snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ", + si->token.label); + if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) { + debug_f("no pin specified"); + return (-1); /* bail out */ + } + } + rv = provider->function_list->C_Login(si->session, type, (u_char *)pin, + (pin != NULL) ? strlen(pin) : 0); + if (pin != NULL) + freezero(pin, strlen(pin)); + + switch (rv) { + case CKR_OK: + case CKR_USER_ALREADY_LOGGED_IN: + /* success */ + break; + case CKR_PIN_LEN_RANGE: + error("PKCS#11 login failed: PIN length out of range"); + return -1; + case CKR_PIN_INCORRECT: + error("PKCS#11 login failed: PIN incorrect"); + return -1; + case CKR_PIN_LOCKED: + error("PKCS#11 login failed: PIN locked"); + return -1; + default: + error("PKCS#11 login failed: error %lu", rv); + return -1; + } + si->logged_in = 1; + return (0); +} + +static int +pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type) +{ + if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) { + error("no pkcs11 (valid) provider found"); + return (-1); + } + + return pkcs11_login_slot(k11->provider, + &k11->provider->slotinfo[k11->slotidx], type); +} + + +static int +pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj, + CK_ATTRIBUTE_TYPE type, int *val) +{ + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_BBOOL flag = 0; + CK_ATTRIBUTE attr; + CK_RV rv; + + *val = 0; + + if (!k11->provider || !k11->provider->valid) { + error("no pkcs11 (valid) provider found"); + return (-1); + } + + f = k11->provider->function_list; + si = &k11->provider->slotinfo[k11->slotidx]; + + attr.type = type; + attr.pValue = &flag; + attr.ulValueLen = sizeof(flag); + + rv = f->C_GetAttributeValue(si->session, obj, &attr, 1); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return (-1); + } + *val = flag != 0; + debug_f("provider \"%s\" slot %lu object %lu: attrib %lu = %d", + k11->provider->name, k11->slotidx, obj, type, *val); + return (0); +} + +static int +pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type) +{ + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_OBJECT_HANDLE obj; + CK_RV rv; + CK_OBJECT_CLASS private_key_class; + CK_BBOOL true_val; + CK_MECHANISM mech; + CK_ATTRIBUTE key_filter[3]; + int always_auth = 0; + int did_login = 0; + + if (!k11->provider || !k11->provider->valid) { + error("no pkcs11 (valid) provider found"); + return (-1); + } + + f = k11->provider->function_list; + si = &k11->provider->slotinfo[k11->slotidx]; + + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (pkcs11_login(k11, CKU_USER) < 0) { + error("login failed"); + return (-1); + } + did_login = 1; + } + + memset(&key_filter, 0, sizeof(key_filter)); + private_key_class = CKO_PRIVATE_KEY; + key_filter[0].type = CKA_CLASS; + key_filter[0].pValue = &private_key_class; + key_filter[0].ulValueLen = sizeof(private_key_class); + + key_filter[1].type = CKA_ID; + key_filter[1].pValue = k11->keyid; + key_filter[1].ulValueLen = k11->keyid_len; + + true_val = CK_TRUE; + key_filter[2].type = CKA_SIGN; + key_filter[2].pValue = &true_val; + key_filter[2].ulValueLen = sizeof(true_val); + + /* try to find object w/CKA_SIGN first, retry w/o */ + if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && + pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { + error("cannot find private key"); + return (-1); + } + + memset(&mech, 0, sizeof(mech)); + mech.mechanism = mech_type; + mech.pParameter = NULL_PTR; + mech.ulParameterLen = 0; + + if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { + error("C_SignInit failed: %lu", rv); + return (-1); + } + + pkcs11_check_obj_bool_attrib(k11, obj, CKA_ALWAYS_AUTHENTICATE, + &always_auth); /* ignore errors here */ + if (always_auth && !did_login) { + debug_f("always-auth key"); + if (pkcs11_login(k11, CKU_CONTEXT_SPECIFIC) < 0) { + error("login failed for always-auth key"); + return (-1); + } + } + + return (0); +} + +/* openssl callback doing the actual signing operation */ +static int +pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + struct pkcs11_key *k11; + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_ULONG tlen = 0; + CK_RV rv; + int rval = -1; + + if ((k11 = RSA_get_ex_data(rsa, rsa_idx)) == NULL) { + error("RSA_get_ex_data failed"); + return (-1); + } + + if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) { + error("pkcs11_get_key failed"); + return (-1); + } + + f = k11->provider->function_list; + si = &k11->provider->slotinfo[k11->slotidx]; + tlen = RSA_size(rsa); + + /* XXX handle CKR_BUFFER_TOO_SMALL */ + rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); + if (rv == CKR_OK) + rval = tlen; + else + error("C_Sign failed: %lu", rv); + + return (rval); +} + +static int +pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + return (-1); +} + +static int +pkcs11_rsa_start_wrapper(void) +{ + if (rsa_method != NULL) + return (0); + rsa_method = RSA_meth_dup(RSA_get_default_method()); + if (rsa_method == NULL) + return (-1); + rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa", + NULL, NULL, pkcs11_k11_free); + if (rsa_idx == -1) + return (-1); + if (!RSA_meth_set1_name(rsa_method, "pkcs11") || + !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) || + !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) { + error_f("setup pkcs11 method failed"); + return (-1); + } + return (0); +} + +/* redirect private key operations for rsa key to pkcs11 token */ +static int +pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + CK_ATTRIBUTE *keyid_attrib, RSA *rsa) +{ + struct pkcs11_key *k11; + + if (pkcs11_rsa_start_wrapper() == -1) + return (-1); + + k11 = xcalloc(1, sizeof(*k11)); + k11->provider = provider; + provider->refcount++; /* provider referenced by RSA key */ + k11->slotidx = slotidx; + /* identify key object on smartcard */ + k11->keyid_len = keyid_attrib->ulValueLen; + if (k11->keyid_len > 0) { + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } + + RSA_set_method(rsa, rsa_method); + RSA_set_ex_data(rsa, rsa_idx, k11); + return (0); +} + +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) +/* openssl callback doing the actual signing operation */ +static ECDSA_SIG * +ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, + const BIGNUM *rp, EC_KEY *ec) +{ + struct pkcs11_key *k11; + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_ULONG siglen = 0, bnlen; + CK_RV rv; + ECDSA_SIG *ret = NULL; + u_char *sig; + BIGNUM *r = NULL, *s = NULL; + + if ((k11 = EC_KEY_get_ex_data(ec, ec_key_idx)) == NULL) { + ossl_error("EC_KEY_get_key_method_data failed for ec"); + return (NULL); + } + + if (pkcs11_get_key(k11, CKM_ECDSA) == -1) { + error("pkcs11_get_key failed"); + return (NULL); + } + + f = k11->provider->function_list; + si = &k11->provider->slotinfo[k11->slotidx]; + + siglen = ECDSA_size(ec); + sig = xmalloc(siglen); + + /* XXX handle CKR_BUFFER_TOO_SMALL */ + rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen); + if (rv != CKR_OK) { + error("C_Sign failed: %lu", rv); + goto done; + } + if (siglen < 64 || siglen > 132 || siglen % 2) { + ossl_error("d2i_ECDSA_SIG failed"); + goto done; + } + bnlen = siglen/2; + if ((ret = ECDSA_SIG_new()) == NULL) { + error("ECDSA_SIG_new failed"); + goto done; + } + if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL || + (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) { + ossl_error("d2i_ECDSA_SIG failed"); + ECDSA_SIG_free(ret); + ret = NULL; + goto done; + } + if (!ECDSA_SIG_set0(ret, r, s)) { + error_f("ECDSA_SIG_set0 failed"); + ECDSA_SIG_free(ret); + ret = NULL; + goto done; + } + r = s = NULL; /* now owned by ret */ + /* success */ + done: + BN_free(r); + BN_free(s); + free(sig); + + return (ret); +} + +static int +pkcs11_ecdsa_start_wrapper(void) +{ + int (*orig_sign)(int, const unsigned char *, int, unsigned char *, + unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; + + if (ec_key_method != NULL) + return (0); + ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa", + NULL, NULL, pkcs11_k11_free); + if (ec_key_idx == -1) + return (-1); + ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); + if (ec_key_method == NULL) + return (-1); + EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL); + EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign); + return (0); +} + +static int +pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec) +{ + struct pkcs11_key *k11; + + if (pkcs11_ecdsa_start_wrapper() == -1) + return (-1); + + k11 = xcalloc(1, sizeof(*k11)); + k11->provider = provider; + provider->refcount++; /* provider referenced by ECDSA key */ + k11->slotidx = slotidx; + /* identify key object on smartcard */ + k11->keyid_len = keyid_attrib->ulValueLen; + if (k11->keyid_len > 0) { + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } + EC_KEY_set_method(ec, ec_key_method); + EC_KEY_set_ex_data(ec, ec_key_idx, k11); + + return (0); +} +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + +/* remove trailing spaces */ +static void +rmspace(u_char *buf, size_t len) +{ + size_t i; + + if (!len) + return; + for (i = len - 1; i > 0; i--) + if (i == len - 1 || buf[i] == ' ') + buf[i] = '\0'; + else + break; +} + +/* + * open a pkcs11 session and login if required. + * if pin == NULL we delay login until key use + */ +static int +pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin, + CK_ULONG user) +{ + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_RV rv; + CK_SESSION_HANDLE session; + int login_required, ret; + + f = p->function_list; + si = &p->slotinfo[slotidx]; + + login_required = si->token.flags & CKF_LOGIN_REQUIRED; + + /* fail early before opening session */ + if (login_required && !pkcs11_interactive && + (pin == NULL || strlen(pin) == 0)) { + error("pin required"); + return (-SSH_PKCS11_ERR_PIN_REQUIRED); + } + if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| + CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) { + error("C_OpenSession failed: %lu", rv); + return (-1); + } + if (login_required && pin != NULL && strlen(pin) != 0) { + rv = f->C_Login(session, user, (u_char *)pin, strlen(pin)); + if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { + error("C_Login failed: %lu", rv); + ret = (rv == CKR_PIN_LOCKED) ? + -SSH_PKCS11_ERR_PIN_LOCKED : + -SSH_PKCS11_ERR_LOGIN_FAIL; + if ((rv = f->C_CloseSession(session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + return (ret); + } + si->logged_in = 1; + } + si->session = session; + return (0); +} + +static int +pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) +{ + int i; + + for (i = 0; i < *nkeys; i++) + if (sshkey_equal(key, (*keysp)[i])) + return (1); + return (0); +} + +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) +static struct sshkey * +pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj) +{ + CK_ATTRIBUTE key_attr[3]; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; + ASN1_OCTET_STRING *octet = NULL; + EC_KEY *ec = NULL; + EC_GROUP *group = NULL; + struct sshkey *key = NULL; + const unsigned char *attrp = NULL; + int i; + int nid; + + memset(&key_attr, 0, sizeof(key_attr)); + key_attr[0].type = CKA_ID; + key_attr[1].type = CKA_EC_POINT; + key_attr[2].type = CKA_EC_PARAMS; + + session = p->slotinfo[slotidx].session; + f = p->function_list; + + /* figure out size of the attributes */ + rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return (NULL); + } + + /* + * Allow CKA_ID (always first attribute) to be empty, but + * ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ + if (key_attr[1].ulValueLen == 0 || + key_attr[2].ulValueLen == 0) { + error("invalid attribute length"); + return (NULL); + } + + /* allocate buffers for attributes */ + for (i = 0; i < 3; i++) + if (key_attr[i].ulValueLen > 0) + key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); + + /* retrieve ID, public point and curve parameters of EC key */ + rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; + } + + ec = EC_KEY_new(); + if (ec == NULL) { + error("EC_KEY_new failed"); + goto fail; + } + + attrp = key_attr[2].pValue; + group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen); + if (group == NULL) { + ossl_error("d2i_ECPKParameters failed"); + goto fail; + } + + if (EC_KEY_set_group(ec, group) == 0) { + ossl_error("EC_KEY_set_group failed"); + goto fail; + } + + if (key_attr[1].ulValueLen <= 2) { + error("CKA_EC_POINT too small"); + goto fail; + } + + attrp = key_attr[1].pValue; + octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen); + if (octet == NULL) { + ossl_error("d2i_ASN1_OCTET_STRING failed"); + goto fail; + } + attrp = octet->data; + if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) { + ossl_error("o2i_ECPublicKey failed"); + goto fail; + } + + nid = sshkey_ecdsa_key_to_nid(ec); + if (nid < 0) { + error("couldn't get curve nid"); + goto fail; + } + + if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); + if (key == NULL) { + error("sshkey_new failed"); + goto fail; + } + + key->ecdsa = ec; + key->ecdsa_nid = nid; + key->type = KEY_ECDSA; + key->flags |= SSHKEY_FLAG_EXT; + ec = NULL; /* now owned by key */ + +fail: + for (i = 0; i < 3; i++) + free(key_attr[i].pValue); + if (ec) + EC_KEY_free(ec); + if (group) + EC_GROUP_free(group); + if (octet) + ASN1_OCTET_STRING_free(octet); + + return (key); +} +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + +static struct sshkey * +pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj) +{ + CK_ATTRIBUTE key_attr[3]; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; + RSA *rsa = NULL; + BIGNUM *rsa_n, *rsa_e; + struct sshkey *key = NULL; + int i; + + memset(&key_attr, 0, sizeof(key_attr)); + key_attr[0].type = CKA_ID; + key_attr[1].type = CKA_MODULUS; + key_attr[2].type = CKA_PUBLIC_EXPONENT; + + session = p->slotinfo[slotidx].session; + f = p->function_list; + + /* figure out size of the attributes */ + rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return (NULL); + } + + /* + * Allow CKA_ID (always first attribute) to be empty, but + * ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ + if (key_attr[1].ulValueLen == 0 || + key_attr[2].ulValueLen == 0) { + error("invalid attribute length"); + return (NULL); + } + + /* allocate buffers for attributes */ + for (i = 0; i < 3; i++) + if (key_attr[i].ulValueLen > 0) + key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); + + /* retrieve ID, modulus and public exponent of RSA key */ + rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; + } + + rsa = RSA_new(); + if (rsa == NULL) { + error("RSA_new failed"); + goto fail; + } + + rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL); + rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL); + if (rsa_n == NULL || rsa_e == NULL) { + error("BN_bin2bn failed"); + goto fail; + } + if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) + fatal_f("set key"); + rsa_n = rsa_e = NULL; /* transferred */ + + if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); + if (key == NULL) { + error("sshkey_new failed"); + goto fail; + } + + key->rsa = rsa; + key->type = KEY_RSA; + key->flags |= SSHKEY_FLAG_EXT; + rsa = NULL; /* now owned by key */ + +fail: + for (i = 0; i < 3; i++) + free(key_attr[i].pValue); + RSA_free(rsa); + + return (key); +} + +static int +pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp) +{ + CK_ATTRIBUTE cert_attr[3]; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; + X509 *x509 = NULL; + X509_NAME *x509_name = NULL; + EVP_PKEY *evp; + RSA *rsa = NULL; +#ifdef OPENSSL_HAS_ECC + EC_KEY *ec = NULL; +#endif + struct sshkey *key = NULL; + int i; +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) + int nid; +#endif + const u_char *cp; + char *subject = NULL; + + *keyp = NULL; + *labelp = NULL; + + memset(&cert_attr, 0, sizeof(cert_attr)); + cert_attr[0].type = CKA_ID; + cert_attr[1].type = CKA_SUBJECT; + cert_attr[2].type = CKA_VALUE; + + session = p->slotinfo[slotidx].session; + f = p->function_list; + + /* figure out size of the attributes */ + rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return -1; + } + + /* + * Allow CKA_ID (always first attribute) to be empty, but + * ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ + if (cert_attr[1].ulValueLen == 0 || + cert_attr[2].ulValueLen == 0) { + error("invalid attribute length"); + return -1; + } + + /* allocate buffers for attributes */ + for (i = 0; i < 3; i++) + if (cert_attr[i].ulValueLen > 0) + cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen); + + /* retrieve ID, subject and value of certificate */ + rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto out; + } + + /* Decode DER-encoded cert subject */ + cp = cert_attr[1].pValue; + if ((x509_name = d2i_X509_NAME(NULL, &cp, + cert_attr[1].ulValueLen)) == NULL || + (subject = X509_NAME_oneline(x509_name, NULL, 0)) == NULL) + subject = xstrdup("invalid subject"); + X509_NAME_free(x509_name); + + cp = cert_attr[2].pValue; + if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) { + error("d2i_x509 failed"); + goto out; + } + + if ((evp = X509_get_pubkey(x509)) == NULL) { + error("X509_get_pubkey failed"); + goto out; + } + + if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) { + if (EVP_PKEY_get0_RSA(evp) == NULL) { + error("invalid x509; no rsa key"); + goto out; + } + if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) { + error("RSAPublicKey_dup failed"); + goto out; + } + + if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa)) + goto out; + + key = sshkey_new(KEY_UNSPEC); + if (key == NULL) { + error("sshkey_new failed"); + goto out; + } + + key->rsa = rsa; + key->type = KEY_RSA; + key->flags |= SSHKEY_FLAG_EXT; + rsa = NULL; /* now owned by key */ +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) + } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) { + if (EVP_PKEY_get0_EC_KEY(evp) == NULL) { + error("invalid x509; no ec key"); + goto out; + } + if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) { + error("EC_KEY_dup failed"); + goto out; + } + + nid = sshkey_ecdsa_key_to_nid(ec); + if (nid < 0) { + error("couldn't get curve nid"); + goto out; + } + + if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec)) + goto out; + + key = sshkey_new(KEY_UNSPEC); + if (key == NULL) { + error("sshkey_new failed"); + goto out; + } + + key->ecdsa = ec; + key->ecdsa_nid = nid; + key->type = KEY_ECDSA; + key->flags |= SSHKEY_FLAG_EXT; + ec = NULL; /* now owned by key */ +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + } else { + error("unknown certificate key type"); + goto out; + } + out: + for (i = 0; i < 3; i++) + free(cert_attr[i].pValue); + X509_free(x509); + RSA_free(rsa); +#ifdef OPENSSL_HAS_ECC + EC_KEY_free(ec); +#endif + if (key == NULL) { + free(subject); + return -1; + } + /* success */ + *keyp = key; + *labelp = subject; + return 0; +} + +#if 0 +static int +have_rsa_key(const RSA *rsa) +{ + const BIGNUM *rsa_n, *rsa_e; + + RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL); + return rsa_n != NULL && rsa_e != NULL; +} +#endif + +static void +note_key(struct pkcs11_provider *p, CK_ULONG slotidx, const char *context, + struct sshkey *key) +{ + char *fp; + + if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) { + error_f("sshkey_fingerprint failed"); + return; + } + debug2("%s: provider %s slot %lu: %s %s", context, p->name, + (u_long)slotidx, sshkey_type(key), fp); + free(fp); +} + +/* + * lookup certificates for token in slot identified by slotidx, + * add 'wrapped' public keys to the 'keysp' array and increment nkeys. + * keysp points to an (possibly empty) array with *nkeys keys. + */ +static int +pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, + struct sshkey ***keysp, char ***labelsp, int *nkeys) +{ + struct sshkey *key = NULL; + CK_OBJECT_CLASS key_class; + CK_ATTRIBUTE key_attr[1]; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; + CK_OBJECT_HANDLE obj; + CK_ULONG n = 0; + int ret = -1; + char *label; + + memset(&key_attr, 0, sizeof(key_attr)); + memset(&obj, 0, sizeof(obj)); + + key_class = CKO_CERTIFICATE; + key_attr[0].type = CKA_CLASS; + key_attr[0].pValue = &key_class; + key_attr[0].ulValueLen = sizeof(key_class); + + session = p->slotinfo[slotidx].session; + f = p->function_list; + + rv = f->C_FindObjectsInit(session, key_attr, 1); + if (rv != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + goto fail; + } + + while (1) { + CK_CERTIFICATE_TYPE ck_cert_type; + + rv = f->C_FindObjects(session, &obj, 1, &n); + if (rv != CKR_OK) { + error("C_FindObjects failed: %lu", rv); + goto fail; + } + if (n == 0) + break; + + memset(&ck_cert_type, 0, sizeof(ck_cert_type)); + memset(&key_attr, 0, sizeof(key_attr)); + key_attr[0].type = CKA_CERTIFICATE_TYPE; + key_attr[0].pValue = &ck_cert_type; + key_attr[0].ulValueLen = sizeof(ck_cert_type); + + rv = f->C_GetAttributeValue(session, obj, key_attr, 1); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; + } + + key = NULL; + label = NULL; + switch (ck_cert_type) { + case CKC_X_509: + if (pkcs11_fetch_x509_pubkey(p, slotidx, &obj, + &key, &label) != 0) { + error("failed to fetch key"); + continue; + } + break; + default: + error("skipping unsupported certificate type %lu", + ck_cert_type); + continue; + } + note_key(p, slotidx, __func__, key); + if (pkcs11_key_included(keysp, nkeys, key)) { + debug2_f("key already included");; + sshkey_free(key); + } else { + /* expand key array and add key */ + *keysp = xrecallocarray(*keysp, *nkeys, + *nkeys + 1, sizeof(struct sshkey *)); + (*keysp)[*nkeys] = key; + if (labelsp != NULL) { + *labelsp = xrecallocarray(*labelsp, *nkeys, + *nkeys + 1, sizeof(char *)); + (*labelsp)[*nkeys] = xstrdup((char *)label); + } + *nkeys = *nkeys + 1; + debug("have %d keys", *nkeys); + } + } + + ret = 0; +fail: + rv = f->C_FindObjectsFinal(session); + if (rv != CKR_OK) { + error("C_FindObjectsFinal failed: %lu", rv); + ret = -1; + } + + return (ret); +} + +/* + * lookup public keys for token in slot identified by slotidx, + * add 'wrapped' public keys to the 'keysp' array and increment nkeys. + * keysp points to an (possibly empty) array with *nkeys keys. + */ +static int +pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, + struct sshkey ***keysp, char ***labelsp, int *nkeys) +{ + struct sshkey *key = NULL; + CK_OBJECT_CLASS key_class; + CK_ATTRIBUTE key_attr[2]; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; + CK_OBJECT_HANDLE obj; + CK_ULONG n = 0; + int ret = -1; + + memset(&key_attr, 0, sizeof(key_attr)); + memset(&obj, 0, sizeof(obj)); + + key_class = CKO_PUBLIC_KEY; + key_attr[0].type = CKA_CLASS; + key_attr[0].pValue = &key_class; + key_attr[0].ulValueLen = sizeof(key_class); + + session = p->slotinfo[slotidx].session; + f = p->function_list; + + rv = f->C_FindObjectsInit(session, key_attr, 1); + if (rv != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + goto fail; + } + + while (1) { + CK_KEY_TYPE ck_key_type; + CK_UTF8CHAR label[256]; + + rv = f->C_FindObjects(session, &obj, 1, &n); + if (rv != CKR_OK) { + error("C_FindObjects failed: %lu", rv); + goto fail; + } + if (n == 0) + break; + + memset(&ck_key_type, 0, sizeof(ck_key_type)); + memset(&key_attr, 0, sizeof(key_attr)); + key_attr[0].type = CKA_KEY_TYPE; + key_attr[0].pValue = &ck_key_type; + key_attr[0].ulValueLen = sizeof(ck_key_type); + key_attr[1].type = CKA_LABEL; + key_attr[1].pValue = &label; + key_attr[1].ulValueLen = sizeof(label) - 1; + + rv = f->C_GetAttributeValue(session, obj, key_attr, 2); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; + } + + label[key_attr[1].ulValueLen] = '\0'; + + switch (ck_key_type) { + case CKK_RSA: + key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj); + break; +#if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) + case CKK_ECDSA: + key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj); + break; +#endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ + default: + /* XXX print key type? */ + key = NULL; + error("skipping unsupported key type"); + } + + if (key == NULL) { + error("failed to fetch key"); + continue; + } + note_key(p, slotidx, __func__, key); + if (pkcs11_key_included(keysp, nkeys, key)) { + debug2_f("key already included");; + sshkey_free(key); + } else { + /* expand key array and add key */ + *keysp = xrecallocarray(*keysp, *nkeys, + *nkeys + 1, sizeof(struct sshkey *)); + (*keysp)[*nkeys] = key; + if (labelsp != NULL) { + *labelsp = xrecallocarray(*labelsp, *nkeys, + *nkeys + 1, sizeof(char *)); + (*labelsp)[*nkeys] = xstrdup((char *)label); + } + *nkeys = *nkeys + 1; + debug("have %d keys", *nkeys); + } + } + + ret = 0; +fail: + rv = f->C_FindObjectsFinal(session); + if (rv != CKR_OK) { + error("C_FindObjectsFinal failed: %lu", rv); + ret = -1; + } + + return (ret); +} + +#ifdef WITH_PKCS11_KEYGEN +#define FILL_ATTR(attr, idx, typ, val, len) \ + { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; } + +static struct sshkey * +pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx, + char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err) +{ + struct pkcs11_slotinfo *si; + char *plabel = label ? label : ""; + int npub = 0, npriv = 0; + CK_RV rv; + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE; + CK_OBJECT_HANDLE pubKey, privKey; + CK_ATTRIBUTE tpub[16], tpriv[16]; + CK_MECHANISM mech = { + CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0 + }; + CK_BYTE pubExponent[] = { + 0x01, 0x00, 0x01 /* RSA_F4 in bytes */ + }; + pubkey_filter[0].pValue = &pubkey_class; + cert_filter[0].pValue = &cert_class; + + *err = 0; + + FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val)); + FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel)); + FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val)); + FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val, + sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits)); + FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent, + sizeof(pubExponent)); + FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid)); + + FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel)); + FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val, + sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid)); + + f = p->function_list; + si = &p->slotinfo[slotidx]; + session = si->session; + + if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv, + &pubKey, &privKey)) != CKR_OK) { + error_f("key generation failed: error 0x%lx", rv); + *err = rv; + return NULL; + } + + return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey); +} + +static int +pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen) +{ + size_t i, len; + char ptr[3]; + + if (dest) + *dest = NULL; + if (rlen) + *rlen = 0; + + if ((len = strlen(hex)) % 2) + return -1; + len /= 2; + + *dest = xmalloc(len); + + ptr[2] = '\0'; + for (i = 0; i < len; i++) { + ptr[0] = hex[2 * i]; + ptr[1] = hex[(2 * i) + 1]; + if (!isxdigit(ptr[0]) || !isxdigit(ptr[1])) + return -1; + (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16); + } + + if (rlen) + *rlen = len; + + return 0; +} + +static struct ec_curve_info { + const char *name; + const char *oid; + const char *oid_encoded; + size_t size; +} ec_curve_infos[] = { + {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256}, + {"secp384r1", "1.3.132.0.34", "06052B81040022", 384}, + {"secp521r1", "1.3.132.0.35", "06052B81040023", 521}, + {NULL, NULL, NULL, 0}, +}; + +static struct sshkey * +pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx, + char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err) +{ + struct pkcs11_slotinfo *si; + char *plabel = label ? label : ""; + int i; + size_t ecparams_size; + unsigned char *ecparams = NULL; + int npub = 0, npriv = 0; + CK_RV rv; + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE; + CK_OBJECT_HANDLE pubKey, privKey; + CK_MECHANISM mech = { + CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0 + }; + CK_ATTRIBUTE tpub[16], tpriv[16]; + + *err = 0; + + for (i = 0; ec_curve_infos[i].name; i++) { + if (ec_curve_infos[i].size == bits) + break; + } + if (!ec_curve_infos[i].name) { + error_f("invalid key size %lu", bits); + return NULL; + } + if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams, + &ecparams_size) == -1) { + error_f("invalid oid"); + return NULL; + } + + FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val)); + FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel)); + FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val)); + FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val, + sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val)); + FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size); + FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid)); + + FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel)); + FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val)); + FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val, + sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val)); + FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid)); + + f = p->function_list; + si = &p->slotinfo[slotidx]; + session = si->session; + + if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv, + &pubKey, &privKey)) != CKR_OK) { + error_f("key generation failed: error 0x%lx", rv); + *err = rv; + return NULL; + } + + return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey); +} +#endif /* WITH_PKCS11_KEYGEN */ + +/* + * register a new provider, fails if provider already exists. if + * keyp is provided, fetch keys. + */ +static int +pkcs11_register_provider(char *provider_id, char *pin, + struct sshkey ***keyp, char ***labelsp, + struct pkcs11_provider **providerp, CK_ULONG user) +{ + int nkeys, need_finalize = 0; + int ret = -1; + struct pkcs11_provider *p = NULL; + void *handle = NULL; + CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); + CK_RV rv; + CK_FUNCTION_LIST *f = NULL; + CK_TOKEN_INFO *token; + CK_ULONG i; + + if (providerp == NULL) + goto fail; + *providerp = NULL; + + if (keyp != NULL) + *keyp = NULL; + if (labelsp != NULL) + *labelsp = NULL; + + if (pkcs11_provider_lookup(provider_id) != NULL) { + debug_f("provider already registered: %s", provider_id); + goto fail; + } + /* open shared pkcs11-library */ + if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { + error("dlopen %s failed: %s", provider_id, dlerror()); + goto fail; + } + if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { + error("dlsym(C_GetFunctionList) failed: %s", dlerror()); + goto fail; + } + p = xcalloc(1, sizeof(*p)); + p->name = xstrdup(provider_id); + p->handle = handle; + /* setup the pkcs11 callbacks */ + if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { + error("C_GetFunctionList for provider %s failed: %lu", + provider_id, rv); + goto fail; + } + p->function_list = f; + if ((rv = f->C_Initialize(NULL)) != CKR_OK) { + error("C_Initialize for provider %s failed: %lu", + provider_id, rv); + goto fail; + } + need_finalize = 1; + if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { + error("C_GetInfo for provider %s failed: %lu", + provider_id, rv); + goto fail; + } + rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); + rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); + debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" + " libraryDescription <%s> libraryVersion %d.%d", + provider_id, + p->info.manufacturerID, + p->info.cryptokiVersion.major, + p->info.cryptokiVersion.minor, + p->info.libraryDescription, + p->info.libraryVersion.major, + p->info.libraryVersion.minor); + if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } + if (p->nslots == 0) { + debug_f("provider %s returned no slots", provider_id); + ret = -SSH_PKCS11_ERR_NO_SLOTS; + goto fail; + } + p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); + if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) + != CKR_OK) { + error("C_GetSlotList for provider %s failed: %lu", + provider_id, rv); + goto fail; + } + p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); + p->valid = 1; + nkeys = 0; + for (i = 0; i < p->nslots; i++) { + token = &p->slotinfo[i].token; + if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) + != CKR_OK) { + error("C_GetTokenInfo for provider %s slot %lu " + "failed: %lu", provider_id, (u_long)i, rv); + continue; + } + if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { + debug2_f("ignoring uninitialised token in " + "provider %s slot %lu", provider_id, (u_long)i); + continue; + } + rmspace(token->label, sizeof(token->label)); + rmspace(token->manufacturerID, sizeof(token->manufacturerID)); + rmspace(token->model, sizeof(token->model)); + rmspace(token->serialNumber, sizeof(token->serialNumber)); + debug("provider %s slot %lu: label <%s> manufacturerID <%s> " + "model <%s> serial <%s> flags 0x%lx", + provider_id, (unsigned long)i, + token->label, token->manufacturerID, token->model, + token->serialNumber, token->flags); + /* + * open session, login with pin and retrieve public + * keys (if keyp is provided) + */ + if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 || + keyp == NULL) + continue; + pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys); + pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys); + if (nkeys == 0 && !p->slotinfo[i].logged_in && + pkcs11_interactive) { + /* + * Some tokens require login before they will + * expose keys. + */ + if (pkcs11_login_slot(p, &p->slotinfo[i], + CKU_USER) < 0) { + error("login failed"); + continue; + } + pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys); + pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys); + } + } + + /* now owned by caller */ + *providerp = p; + + TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); + p->refcount++; /* add to provider list */ + + return (nkeys); +fail: + if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize for provider %s failed: %lu", + provider_id, rv); + if (p) { + free(p->name); + free(p->slotlist); + free(p->slotinfo); + free(p); + } + if (handle) + dlclose(handle); + if (ret > 0) + ret = -1; + return (ret); +} + +/* + * register a new provider and get number of keys hold by the token, + * fails if provider already exists + */ +int +pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp, + char ***labelsp) +{ + struct pkcs11_provider *p = NULL; + int nkeys; + + nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp, + &p, CKU_USER); + + /* no keys found or some other error, de-register provider */ + if (nkeys <= 0 && p != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + } + if (nkeys == 0) + debug_f("provider %s returned no keys", provider_id); + + return (nkeys); +} + +#ifdef WITH_PKCS11_KEYGEN +struct sshkey * +pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label, + unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err) +{ + struct pkcs11_provider *p = NULL; + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + struct sshkey *k = NULL; + int ret = -1, reset_pin = 0, reset_provider = 0; + CK_RV rv; + + *err = 0; + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) + debug_f("provider \"%s\" available", provider_id); + else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, NULL, + &p, CKU_SO)) < 0) { + debug_f("could not register provider %s", provider_id); + goto out; + } else + reset_provider = 1; + + f = p->function_list; + si = &p->slotinfo[slotidx]; + session = si->session; + + if ((rv = f->C_SetOperationState(session , pin, strlen(pin), + CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) { + debug_f("could not supply SO pin: %lu", rv); + reset_pin = 0; + } else + reset_pin = 1; + + switch (type) { + case KEY_RSA: + if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label, + bits, keyid, err)) == NULL) { + debug_f("failed to generate RSA key"); + goto out; + } + break; + case KEY_ECDSA: + if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label, + bits, keyid, err)) == NULL) { + debug_f("failed to generate ECDSA key"); + goto out; + } + break; + default: + *err = SSH_PKCS11_ERR_GENERIC; + debug_f("unknown type %d", type); + goto out; + } + +out: + if (reset_pin) + f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE, + CK_INVALID_HANDLE); + + if (reset_provider) + pkcs11_del_provider(provider_id); + + return (k); +} + +struct sshkey * +pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx, + unsigned char keyid, u_int32_t *err) +{ + struct pkcs11_provider *p = NULL; + struct pkcs11_slotinfo *si; + struct sshkey *k = NULL; + int reset_pin = 0, reset_provider = 0; + CK_ULONG nattrs; + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + CK_ATTRIBUTE attrs[16]; + CK_OBJECT_CLASS key_class; + CK_KEY_TYPE key_type; + CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; + CK_RV rv; + + *err = 0; + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { + debug_f("using provider \"%s\"", provider_id); + } else if (pkcs11_register_provider(provider_id, pin, NULL, NULL, &p, + CKU_SO) < 0) { + debug_f("could not register provider %s", + provider_id); + goto out; + } else + reset_provider = 1; + + f = p->function_list; + si = &p->slotinfo[slotidx]; + session = si->session; + + if ((rv = f->C_SetOperationState(session , pin, strlen(pin), + CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) { + debug_f("could not supply SO pin: %lu", rv); + reset_pin = 0; + } else + reset_pin = 1; + + /* private key */ + nattrs = 0; + key_class = CKO_PRIVATE_KEY; + FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class)); + FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid)); + + if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 && + obj != CK_INVALID_HANDLE) { + if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) { + debug_f("could not destroy private key 0x%hhx", + keyid); + *err = rv; + goto out; + } + } + + /* public key */ + nattrs = 0; + key_class = CKO_PUBLIC_KEY; + FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class)); + FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid)); + + if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 && + obj != CK_INVALID_HANDLE) { + + /* get key type */ + nattrs = 0; + FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type, + sizeof(key_type)); + rv = f->C_GetAttributeValue(session, obj, attrs, nattrs); + if (rv != CKR_OK) { + debug_f("could not get key type of public key 0x%hhx", + keyid); + *err = rv; + key_type = -1; + } + if (key_type == CKK_RSA) + k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj); + else if (key_type == CKK_ECDSA) + k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj); + + if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) { + debug_f("could not destroy public key 0x%hhx", keyid); + *err = rv; + goto out; + } + } + +out: + if (reset_pin) + f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE, + CK_INVALID_HANDLE); + + if (reset_provider) + pkcs11_del_provider(provider_id); + + return (k); +} +#endif /* WITH_PKCS11_KEYGEN */ +#else /* ENABLE_PKCS11 */ + +#include +#include +#include + +#include "log.h" +#include "sshkey.h" + +int +pkcs11_init(int interactive) +{ + error("%s: dlopen() not supported", __func__); + return (-1); +} + +int +pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp, + char ***labelsp) +{ + error("%s: dlopen() not supported", __func__); + return (-1); +} + +void +pkcs11_terminate(void) +{ + error("%s: dlopen() not supported", __func__); +} +#endif /* ENABLE_PKCS11 */ diff --git a/aosp/external/openssh/ssh-pkcs11.h b/aosp/external/openssh/ssh-pkcs11.h new file mode 100644 index 0000000000000000000000000000000000000000..81f1d7c5d392781dd261468a8f6bbde7783bed5c --- /dev/null +++ b/aosp/external/openssh/ssh-pkcs11.h @@ -0,0 +1,40 @@ +/* $OpenBSD: ssh-pkcs11.h,v 1.6 2020/01/25 00:03:36 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Errors for pkcs11_add_provider() */ +#define SSH_PKCS11_ERR_GENERIC 1 +#define SSH_PKCS11_ERR_LOGIN_FAIL 2 +#define SSH_PKCS11_ERR_NO_SLOTS 3 +#define SSH_PKCS11_ERR_PIN_REQUIRED 4 +#define SSH_PKCS11_ERR_PIN_LOCKED 5 + +int pkcs11_init(int); +void pkcs11_terminate(void); +int pkcs11_add_provider(char *, char *, struct sshkey ***, char ***); +int pkcs11_del_provider(char *); +#ifdef WITH_PKCS11_KEYGEN +struct sshkey * + pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int, + unsigned int, unsigned char, u_int32_t *); +struct sshkey * + pkcs11_destroy_keypair(char *, char *, unsigned long, unsigned char, + u_int32_t *); +#endif + +#if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) +#undef ENABLE_PKCS11 +#endif diff --git a/aosp/external/openssh/ssh-rsa.c b/aosp/external/openssh/ssh-rsa.c new file mode 100644 index 0000000000000000000000000000000000000000..d8c3d95f680a991a70acda8c5d19db1eb2eded6d --- /dev/null +++ b/aosp/external/openssh/ssh-rsa.c @@ -0,0 +1,451 @@ +/* $OpenBSD: ssh-rsa.c,v 1.67 2018/07/03 11:39:54 djm Exp $ */ +/* + * Copyright (c) 2000, 2003 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef WITH_OPENSSL + +#include + +#include +#include + +#include +#include + +#include "sshbuf.h" +#include "compat.h" +#include "ssherr.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" +#include "digest.h" +#include "log.h" + +#include "openbsd-compat/openssl-compat.h" + +static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); + +static const char * +rsa_hash_alg_ident(int hash_alg) +{ + switch (hash_alg) { + case SSH_DIGEST_SHA1: + return "ssh-rsa"; + case SSH_DIGEST_SHA256: + return "rsa-sha2-256"; + case SSH_DIGEST_SHA512: + return "rsa-sha2-512"; + } + return NULL; +} + +/* + * Returns the hash algorithm ID for a given algorithm identifier as used + * inside the signature blob, + */ +static int +rsa_hash_id_from_ident(const char *ident) +{ + if (strcmp(ident, "ssh-rsa") == 0) + return SSH_DIGEST_SHA1; + if (strcmp(ident, "rsa-sha2-256") == 0) + return SSH_DIGEST_SHA256; + if (strcmp(ident, "rsa-sha2-512") == 0) + return SSH_DIGEST_SHA512; + return -1; +} + +/* + * Return the hash algorithm ID for the specified key name. This includes + * all the cases of rsa_hash_id_from_ident() but also the certificate key + * types. + */ +static int +rsa_hash_id_from_keyname(const char *alg) +{ + int r; + + if ((r = rsa_hash_id_from_ident(alg)) != -1) + return r; + if (strcmp(alg, "ssh-rsa-cert-v01@openssh.com") == 0) + return SSH_DIGEST_SHA1; + if (strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0) + return SSH_DIGEST_SHA256; + if (strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0) + return SSH_DIGEST_SHA512; + return -1; +} + +static int +rsa_hash_alg_nid(int type) +{ + switch (type) { + case SSH_DIGEST_SHA1: + return NID_sha1; + case SSH_DIGEST_SHA256: + return NID_sha256; + case SSH_DIGEST_SHA512: + return NID_sha512; + default: + return -1; + } +} + +int +ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp) +{ + const BIGNUM *rsa_p, *rsa_q, *rsa_d; + BIGNUM *aux = NULL, *d_consttime = NULL; + BIGNUM *rsa_dmq1 = NULL, *rsa_dmp1 = NULL, *rsa_iqmp = NULL; + BN_CTX *ctx = NULL; + int r; + + if (key == NULL || key->rsa == NULL || + sshkey_type_plain(key->type) != KEY_RSA) + return SSH_ERR_INVALID_ARGUMENT; + + RSA_get0_key(key->rsa, NULL, NULL, &rsa_d); + RSA_get0_factors(key->rsa, &rsa_p, &rsa_q); + + if ((ctx = BN_CTX_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((aux = BN_new()) == NULL || + (rsa_dmq1 = BN_new()) == NULL || + (rsa_dmp1 = BN_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((d_consttime = BN_dup(rsa_d)) == NULL || + (rsa_iqmp = BN_dup(iqmp)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +#if !defined(OPENSSL_IS_BORINGSSL) + BN_set_flags(aux, BN_FLG_CONSTTIME); + BN_set_flags(d_consttime, BN_FLG_CONSTTIME); +#endif + + if ((BN_sub(aux, rsa_q, BN_value_one()) == 0) || + (BN_mod(rsa_dmq1, d_consttime, aux, ctx) == 0) || + (BN_sub(aux, rsa_p, BN_value_one()) == 0) || + (BN_mod(rsa_dmp1, d_consttime, aux, ctx) == 0)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (!RSA_set0_crt_params(key->rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + rsa_dmp1 = rsa_dmq1 = rsa_iqmp = NULL; /* transferred */ + /* success */ + r = 0; + out: + BN_clear_free(aux); + BN_clear_free(d_consttime); + BN_clear_free(rsa_dmp1); + BN_clear_free(rsa_dmq1); + BN_clear_free(rsa_iqmp); + BN_CTX_free(ctx); + return r; +} + +/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ +int +ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, const char *alg_ident) +{ + const BIGNUM *rsa_n; + u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; + size_t slen = 0; + u_int dlen, len; + int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (alg_ident == NULL || strlen(alg_ident) == 0) + hash_alg = SSH_DIGEST_SHA1; + else + hash_alg = rsa_hash_id_from_keyname(alg_ident); + if (key == NULL || key->rsa == NULL || hash_alg == -1 || + sshkey_type_plain(key->type) != KEY_RSA) + return SSH_ERR_INVALID_ARGUMENT; + RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); + if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + slen = RSA_size(key->rsa); + if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; + + /* hash the data */ + nid = rsa_hash_alg_nid(hash_alg); + if ((dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + if ((sig = malloc(slen)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (len < slen) { + size_t diff = slen - len; + memmove(sig + diff, sig, len); + explicit_bzero(sig, diff); + } else if (len > slen) { + ret = SSH_ERR_INTERNAL_ERROR; + goto out; + } + /* encode signature */ + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 || + (ret = sshbuf_put_string(b, sig, slen)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } + if (lenp != NULL) + *lenp = len; + ret = 0; + out: + explicit_bzero(digest, sizeof(digest)); + freezero(sig, slen); + sshbuf_free(b); + return ret; +} + +int +ssh_rsa_verify(const struct sshkey *key, + const u_char *sig, size_t siglen, const u_char *data, size_t datalen, + const char *alg) +{ + const BIGNUM *rsa_n; + char *sigtype = NULL; + int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR; + size_t len = 0, diff, modlen, dlen; + struct sshbuf *b = NULL; + u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; + + if (key == NULL || key->rsa == NULL || + sshkey_type_plain(key->type) != KEY_RSA || + sig == NULL || siglen == 0) + return SSH_ERR_INVALID_ARGUMENT; + RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); + if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + + if ((b = sshbuf_from(sig, siglen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &sigtype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((hash_alg = rsa_hash_id_from_ident(sigtype)) == -1) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + /* + * Allow ssh-rsa-cert-v01 certs to generate SHA2 signatures for + * legacy reasons, but otherwise the signature type should match. + */ + if (alg != NULL && strcmp(alg, "ssh-rsa-cert-v01@openssh.com") != 0) { + if ((want_alg = rsa_hash_id_from_keyname(alg)) == -1) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (hash_alg != want_alg) { + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + } + if (sshbuf_get_string(b, &sigblob, &len) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + /* RSA_verify expects a signature of RSA_size */ + modlen = RSA_size(key->rsa); + if (len > modlen) { + ret = SSH_ERR_KEY_BITS_MISMATCH; + goto out; + } else if (len < modlen) { + diff = modlen - len; + osigblob = sigblob; + if ((sigblob = realloc(sigblob, modlen)) == NULL) { + sigblob = osigblob; /* put it back for clear/free */ + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memmove(sigblob + diff, sigblob, len); + explicit_bzero(sigblob, diff); + len = modlen; + } + if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { + ret = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, + key->rsa); + out: + freezero(sigblob, len); + free(sigtype); + sshbuf_free(b); + explicit_bzero(digest, sizeof(digest)); + return ret; +} + +/* + * See: + * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ + * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn + */ + +/* + * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) + * oiw(14) secsig(3) algorithms(2) 26 } + */ +static const u_char id_sha1[] = { + 0x30, 0x21, /* type Sequence, length 0x21 (33) */ + 0x30, 0x09, /* type Sequence, length 0x09 */ + 0x06, 0x05, /* type OID, length 0x05 */ + 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ + 0x05, 0x00, /* NULL */ + 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ +}; + +/* + * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html + * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) + * id-sha256(1) } + */ +static const u_char id_sha256[] = { + 0x30, 0x31, /* type Sequence, length 0x31 (49) */ + 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ + 0x06, 0x09, /* type OID, length 0x09 */ + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ + 0x05, 0x00, /* NULL */ + 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ +}; + +/* + * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html + * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) + * id-sha256(3) } + */ +static const u_char id_sha512[] = { + 0x30, 0x51, /* type Sequence, length 0x51 (81) */ + 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ + 0x06, 0x09, /* type OID, length 0x09 */ + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ + 0x05, 0x00, /* NULL */ + 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ +}; + +static int +rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) +{ + switch (hash_alg) { + case SSH_DIGEST_SHA1: + *oidp = id_sha1; + *oidlenp = sizeof(id_sha1); + break; + case SSH_DIGEST_SHA256: + *oidp = id_sha256; + *oidlenp = sizeof(id_sha256); + break; + case SSH_DIGEST_SHA512: + *oidp = id_sha512; + *oidlenp = sizeof(id_sha512); + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + return 0; +} + +static int +openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, + u_char *sigbuf, size_t siglen, RSA *rsa) +{ + size_t rsasize = 0, oidlen = 0, hlen = 0; + int ret, len, oidmatch, hashmatch; + const u_char *oid = NULL; + u_char *decrypted = NULL; + + if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) + return ret; + ret = SSH_ERR_INTERNAL_ERROR; + hlen = ssh_digest_bytes(hash_alg); + if (hashlen != hlen) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto done; + } + rsasize = RSA_size(rsa); + if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || + siglen == 0 || siglen > rsasize) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto done; + } + if ((decrypted = malloc(rsasize)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, + RSA_PKCS1_PADDING)) < 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto done; + } + if (len < 0 || (size_t)len != hlen + oidlen) { + ret = SSH_ERR_INVALID_FORMAT; + goto done; + } + oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; + hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; + if (!oidmatch || !hashmatch) { + ret = SSH_ERR_SIGNATURE_INVALID; + goto done; + } + ret = 0; +done: + freezero(decrypted, rsasize); + return ret; +} +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/ssh-sandbox.h b/aosp/external/openssh/ssh-sandbox.h new file mode 100644 index 0000000000000000000000000000000000000000..bd5fd83722a0dc2d79e116e182549c97445407c2 --- /dev/null +++ b/aosp/external/openssh/ssh-sandbox.h @@ -0,0 +1,24 @@ +/* $OpenBSD: ssh-sandbox.h,v 1.1 2011/06/23 09:34:13 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct monitor; +struct ssh_sandbox; + +struct ssh_sandbox *ssh_sandbox_init(struct monitor *); +void ssh_sandbox_child(struct ssh_sandbox *); +void ssh_sandbox_parent_finish(struct ssh_sandbox *); +void ssh_sandbox_parent_preauth(struct ssh_sandbox *, pid_t); diff --git a/aosp/external/openssh/ssh-sk-client.c b/aosp/external/openssh/ssh-sk-client.c new file mode 100644 index 0000000000000000000000000000000000000000..321fe53a2d9195b04ede36d32e71e7529b5af209 --- /dev/null +++ b/aosp/external/openssh/ssh-sk-client.c @@ -0,0 +1,480 @@ +/* $OpenBSD: ssh-sk-client.c,v 1.12 2022/01/14 03:34:00 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "msg.h" +#include "digest.h" +#include "pathnames.h" +#include "ssh-sk.h" +#include "misc.h" + +/* #define DEBUG_SK 1 */ + +static int +start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int)) +{ + void (*osigchld)(int); + int oerrno, pair[2]; + pid_t pid; + char *helper, *verbosity = NULL; + + *fdp = -1; + *pidp = 0; + *osigchldp = SIG_DFL; + + helper = getenv("SSH_SK_HELPER"); + if (helper == NULL || strlen(helper) == 0) + helper = _PATH_SSH_SK_HELPER; + if (access(helper, X_OK) != 0) { + oerrno = errno; + error_f("helper \"%s\" unusable: %s", helper, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } +#ifdef DEBUG_SK + verbosity = "-vvv"; +#endif + + /* Start helper */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + error("socketpair: %s", strerror(errno)); + return SSH_ERR_SYSTEM_ERROR; + } + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + if ((pid = fork()) == -1) { + oerrno = errno; + error("fork: %s", strerror(errno)); + close(pair[0]); + close(pair[1]); + ssh_signal(SIGCHLD, osigchld); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + if (pid == 0) { + if ((dup2(pair[1], STDIN_FILENO) == -1) || + (dup2(pair[1], STDOUT_FILENO) == -1)) { + error_f("dup2: %s", strerror(errno)); + _exit(1); + } + close(pair[0]); + close(pair[1]); + closefrom(STDERR_FILENO + 1); + debug_f("starting %s %s", helper, + verbosity == NULL ? "" : verbosity); + execlp(helper, helper, verbosity, (char *)NULL); + error_f("execlp: %s", strerror(errno)); + _exit(1); + } + close(pair[1]); + + /* success */ + debug3_f("started pid=%ld", (long)pid); + *fdp = pair[0]; + *pidp = pid; + *osigchldp = osigchld; + return 0; +} + +static int +reap_helper(pid_t pid) +{ + int status, oerrno; + + debug3_f("pid=%ld", (long)pid); + + errno = 0; + while (waitpid(pid, &status, 0) == -1) { + if (errno == EINTR) { + errno = 0; + continue; + } + oerrno = errno; + error_f("waitpid: %s", strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + if (!WIFEXITED(status)) { + error_f("helper exited abnormally"); + return SSH_ERR_AGENT_FAILURE; + } else if (WEXITSTATUS(status) != 0) { + error_f("helper exited with non-zero exit status"); + return SSH_ERR_AGENT_FAILURE; + } + return 0; +} + +static int +client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type) +{ + int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR; + u_int rtype, rerr; + pid_t pid; + u_char version; + void (*osigchld)(int); + struct sshbuf *req = NULL, *resp = NULL; + *respp = NULL; + + if ((r = start_helper(&fd, &pid, &osigchld)) != 0) + return r; + + if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* Request preamble: type, log_on_stderr, log_level */ + ll = log_level_get(); + if ((r = sshbuf_put_u32(req, type)) != 0 || + (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 || + (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 || + (r = sshbuf_putb(req, msg)) != 0) { + error_fr(r, "compose"); + goto out; + } + if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) { + error_fr(r, "send"); + goto out; + } + if ((r = ssh_msg_recv(fd, resp)) != 0) { + error_fr(r, "receive"); + goto out; + } + if ((r = sshbuf_get_u8(resp, &version)) != 0) { + error_fr(r, "parse version"); + goto out; + } + if (version != SSH_SK_HELPER_VERSION) { + error_f("unsupported version: got %u, expected %u", + version, SSH_SK_HELPER_VERSION); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_get_u32(resp, &rtype)) != 0) { + error_fr(r, "parse message type"); + goto out; + } + if (rtype == SSH_SK_HELPER_ERROR) { + if ((r = sshbuf_get_u32(resp, &rerr)) != 0) { + error_fr(r, "parse"); + goto out; + } + debug_f("helper returned error -%u", rerr); + /* OpenSSH error values are negative; encoded as -err on wire */ + if (rerr == 0 || rerr >= INT_MAX) + r = SSH_ERR_INTERNAL_ERROR; + else + r = -(int)rerr; + goto out; + } else if (rtype != type) { + error_f("helper returned incorrect message type %u, " + "expecting %u", rtype, type); + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + /* success */ + r = 0; + out: + oerrno = errno; + close(fd); + if ((r2 = reap_helper(pid)) != 0) { + if (r == 0) { + r = r2; + oerrno = errno; + } + } + if (r == 0) { + *respp = resp; + resp = NULL; + } + sshbuf_free(req); + sshbuf_free(resp); + ssh_signal(SIGCHLD, osigchld); + errno = oerrno; + return r; + +} + +int +sshsk_sign(const char *provider, struct sshkey *key, + u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, + u_int compat, const char *pin) +{ + int oerrno, r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL; + + *sigp = NULL; + *lenp = 0; + +#ifndef ENABLE_SK + return SSH_ERR_KEY_TYPE_UNKNOWN; +#endif + + if ((kbuf = sshbuf_new()) == NULL || + (req = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((r = sshkey_private_serialize(key, kbuf)) != 0) { + error_fr(r, "encode key"); + goto out; + } + if ((r = sshbuf_put_stringb(req, kbuf)) != 0 || + (r = sshbuf_put_cstring(req, provider)) != 0 || + (r = sshbuf_put_string(req, data, datalen)) != 0 || + (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */ + (r = sshbuf_put_u32(req, compat)) != 0 || + (r = sshbuf_put_cstring(req, pin)) != 0) { + error_fr(r, "compose"); + goto out; + } + + if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0) + goto out; + + if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) { + error_fr(r, "parse signature"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(resp) != 0) { + error_f("trailing data in response"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + r = 0; + out: + oerrno = errno; + if (r != 0) { + freezero(*sigp, *lenp); + *sigp = NULL; + *lenp = 0; + } + sshbuf_free(kbuf); + sshbuf_free(req); + sshbuf_free(resp); + errno = oerrno; + return r; +} + +int +sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest) +{ + int oerrno, r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL; + struct sshkey *key = NULL; + + *keyp = NULL; + if (attest != NULL) + sshbuf_reset(attest); + +#ifndef ENABLE_SK + return SSH_ERR_KEY_TYPE_UNKNOWN; +#endif + + if (type < 0) + return SSH_ERR_INVALID_ARGUMENT; + + if ((abuf = sshbuf_new()) == NULL || + (kbuf = sshbuf_new()) == NULL || + (req = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 || + (r = sshbuf_put_cstring(req, provider_path)) != 0 || + (r = sshbuf_put_cstring(req, device)) != 0 || + (r = sshbuf_put_cstring(req, application)) != 0 || + (r = sshbuf_put_cstring(req, userid)) != 0 || + (r = sshbuf_put_u8(req, flags)) != 0 || + (r = sshbuf_put_cstring(req, pin)) != 0 || + (r = sshbuf_put_stringb(req, challenge_buf)) != 0) { + error_fr(r, "compose"); + goto out; + } + + if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0) + goto out; + + if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || + (r = sshbuf_get_stringb(resp, abuf)) != 0) { + error_fr(r, "parse"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(resp) != 0) { + error_f("trailing data in response"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { + error_fr(r, "encode"); + goto out; + } + if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) { + error_fr(r, "encode attestation information"); + goto out; + } + + /* success */ + r = 0; + *keyp = key; + key = NULL; + out: + oerrno = errno; + sshkey_free(key); + sshbuf_free(kbuf); + sshbuf_free(abuf); + sshbuf_free(req); + sshbuf_free(resp); + errno = oerrno; + return r; +} + +static void +sshsk_free_resident_key(struct sshsk_resident_key *srk) +{ + if (srk == NULL) + return; + sshkey_free(srk->key); + freezero(srk->user_id, srk->user_id_len); + free(srk); +} + + +void +sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks) +{ + size_t i; + + if (srks == NULL || nsrks == 0) + return; + + for (i = 0; i < nsrks; i++) + sshsk_free_resident_key(srks[i]); + free(srks); +} + +int +sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, u_int flags, struct sshsk_resident_key ***srksp, + size_t *nsrksp) +{ + int oerrno, r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL; + struct sshkey *key = NULL; + struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp; + u_char *userid = NULL; + size_t userid_len = 0, nsrks = 0; + + *srksp = NULL; + *nsrksp = 0; + + if ((kbuf = sshbuf_new()) == NULL || + (req = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((r = sshbuf_put_cstring(req, provider_path)) != 0 || + (r = sshbuf_put_cstring(req, device)) != 0 || + (r = sshbuf_put_cstring(req, pin)) != 0 || + (r = sshbuf_put_u32(req, flags)) != 0) { + error_fr(r, "compose"); + goto out; + } + + if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0) + goto out; + + while (sshbuf_len(resp) != 0) { + /* key, comment, user_id */ + if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || + (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0 || + (r = sshbuf_get_string(resp, &userid, &userid_len)) != 0) { + error_fr(r, "parse"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { + error_fr(r, "decode key"); + goto out; + } + if ((srk = calloc(1, sizeof(*srk))) == NULL) { + error_f("calloc failed"); + goto out; + } + srk->key = key; + key = NULL; + srk->user_id = userid; + srk->user_id_len = userid_len; + userid = NULL; + userid_len = 0; + if ((tmp = recallocarray(srks, nsrks, nsrks + 1, + sizeof(*srks))) == NULL) { + error_f("recallocarray keys failed"); + goto out; + } + debug_f("srks[%zu]: %s %s uidlen %zu", nsrks, + sshkey_type(srk->key), srk->key->sk_application, + srk->user_id_len); + srks = tmp; + srks[nsrks++] = srk; + srk = NULL; + } + + /* success */ + r = 0; + *srksp = srks; + *nsrksp = nsrks; + srks = NULL; + nsrks = 0; + out: + oerrno = errno; + sshsk_free_resident_key(srk); + sshsk_free_resident_keys(srks, nsrks); + freezero(userid, userid_len); + sshkey_free(key); + sshbuf_free(kbuf); + sshbuf_free(req); + sshbuf_free(resp); + errno = oerrno; + return r; +} diff --git a/aosp/external/openssh/ssh-sk-helper.8 b/aosp/external/openssh/ssh-sk-helper.8 new file mode 100644 index 0000000000000000000000000000000000000000..3c53da1ec796029b64b53e6dd6471ba5716626b0 --- /dev/null +++ b/aosp/external/openssh/ssh-sk-helper.8 @@ -0,0 +1,66 @@ +.\" $OpenBSD: ssh-sk-helper.8,v 1.3 2019/12/21 20:22:34 naddy Exp $ +.\" +.\" Copyright (c) 2010 Markus Friedl. All rights reserved. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: December 21 2019 $ +.Dt SSH-SK-HELPER 8 +.Os +.Sh NAME +.Nm ssh-sk-helper +.Nd OpenSSH helper for FIDO authenticator support +.Sh SYNOPSIS +.Nm +.Op Fl v +.Sh DESCRIPTION +.Nm +is used by +.Xr ssh-agent 1 +to access keys provided by a FIDO authenticator. +.Pp +.Nm +is not intended to be invoked by the user, but from +.Xr ssh-agent 1 . +.Pp +A single option is supported: +.Bl -tag -width Ds +.It Fl v +Verbose mode. +Causes +.Nm +to print debugging messages about its progress. +This is helpful in debugging problems. +Multiple +.Fl v +options increase the verbosity. +The maximum is 3. +.Pp +Note that +.Xr ssh-agent 1 +will automatically pass the +.Fl v +flag to +.Nm +when it has itself been placed in debug mode. +.El +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 +.Sh HISTORY +.Nm +first appeared in +.Ox 6.7 . +.Sh AUTHORS +.An Damien Miller Aq Mt djm@openbsd.org diff --git a/aosp/external/openssh/ssh-sk-helper.c b/aosp/external/openssh/ssh-sk-helper.c new file mode 100644 index 0000000000000000000000000000000000000000..b1d22631f2f6ee42ee7f5854d3c43b0efa388366 --- /dev/null +++ b/aosp/external/openssh/ssh-sk-helper.c @@ -0,0 +1,367 @@ +/* $OpenBSD: ssh-sk-helper.c,v 1.12 2021/10/28 02:54:18 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This is a tiny program used to isolate the address space used for + * security key middleware signing operations from ssh-agent. It is similar + * to ssh-pkcs11-helper.c but considerably simpler as the operations for + * security keys are stateless. + * + * Please crank SSH_SK_HELPER_VERSION in sshkey.h for any incompatible + * protocol changes. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "log.h" +#include "sshkey.h" +#include "authfd.h" +#include "misc.h" +#include "sshbuf.h" +#include "msg.h" +#include "uidswap.h" +#include "sshkey.h" +#include "ssherr.h" +#include "ssh-sk.h" + +#ifdef ENABLE_SK +extern char *__progname; + +static struct sshbuf *reply_error(int r, char *fmt, ...) + __attribute__((__format__ (printf, 2, 3))); + +static struct sshbuf * +reply_error(int r, char *fmt, ...) +{ + char *msg; + va_list ap; + struct sshbuf *resp; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + debug("%s: %s", __progname, msg); + free(msg); + + if (r >= 0) + fatal_f("invalid error code %d", r); + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + if (sshbuf_put_u32(resp, SSH_SK_HELPER_ERROR) != 0 || + sshbuf_put_u32(resp, (u_int)-r) != 0) + fatal("%s: buffer error", __progname); + return resp; +} + +/* If the specified string is zero length, then free it and replace with NULL */ +static void +null_empty(char **s) +{ + if (s == NULL || *s == NULL || **s != '\0') + return; + + free(*s); + *s = NULL; +} + +static struct sshbuf * +process_sign(struct sshbuf *req) +{ + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *resp, *kbuf; + struct sshkey *key = NULL; + uint32_t compat; + const u_char *message; + u_char *sig = NULL; + size_t msglen, siglen = 0; + char *provider = NULL, *pin = NULL; + + if ((r = sshbuf_froms(req, &kbuf)) != 0 || + (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || + (r = sshbuf_get_cstring(req, NULL, NULL)) != 0 || /* alg */ + (r = sshbuf_get_u32(req, &compat)) != 0 || + (r = sshbuf_get_cstring(req, &pin, NULL)) != 0) + fatal_r(r, "%s: parse", __progname); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) + fatal_r(r, "%s: Unable to parse private key", __progname); + if (!sshkey_is_sk(key)) { + fatal("%s: Unsupported key type %s", + __progname, sshkey_ssh_name(key)); + } + + debug_f("ready to sign with key %s, provider %s: " + "msg len %zu, compat 0x%lx", sshkey_type(key), + provider, msglen, (u_long)compat); + + null_empty(&pin); + + if ((r = sshsk_sign(provider, key, &sig, &siglen, + message, msglen, compat, pin)) != 0) { + resp = reply_error(r, "Signing failed: %s", ssh_err(r)); + goto out; + } + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_SIGN)) != 0 || + (r = sshbuf_put_string(resp, sig, siglen)) != 0) + fatal_r(r, "%s: compose", __progname); + out: + sshkey_free(key); + sshbuf_free(kbuf); + free(provider); + if (sig != NULL) + freezero(sig, siglen); + if (pin != NULL) + freezero(pin, strlen(pin)); + return resp; +} + +static struct sshbuf * +process_enroll(struct sshbuf *req) +{ + int r; + u_int type; + char *provider, *application, *pin, *device, *userid; + uint8_t flags; + struct sshbuf *challenge, *attest, *kbuf, *resp; + struct sshkey *key; + + if ((attest = sshbuf_new()) == NULL || + (kbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_get_u32(req, &type)) != 0 || + (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &device, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &userid, NULL)) != 0 || + (r = sshbuf_get_u8(req, &flags)) != 0 || + (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 || + (r = sshbuf_froms(req, &challenge)) != 0) + fatal_r(r, "%s: parse", __progname); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + if (type > INT_MAX) + fatal("%s: bad type %u", __progname, type); + if (sshbuf_len(challenge) == 0) { + sshbuf_free(challenge); + challenge = NULL; + } + null_empty(&device); + null_empty(&userid); + null_empty(&pin); + + if ((r = sshsk_enroll((int)type, provider, device, application, userid, + flags, pin, challenge, &key, attest)) != 0) { + resp = reply_error(r, "Enrollment failed: %s", ssh_err(r)); + goto out; + } + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + if ((r = sshkey_private_serialize(key, kbuf)) != 0) + fatal_r(r, "%s: encode key", __progname); + if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_ENROLL)) != 0 || + (r = sshbuf_put_stringb(resp, kbuf)) != 0 || + (r = sshbuf_put_stringb(resp, attest)) != 0) + fatal_r(r, "%s: compose", __progname); + + out: + sshkey_free(key); + sshbuf_free(kbuf); + sshbuf_free(attest); + sshbuf_free(challenge); + free(provider); + free(application); + if (pin != NULL) + freezero(pin, strlen(pin)); + + return resp; +} + +static struct sshbuf * +process_load_resident(struct sshbuf *req) +{ + int r; + char *provider, *pin, *device; + struct sshbuf *kbuf, *resp; + struct sshsk_resident_key **srks = NULL; + size_t nsrks = 0, i; + u_int flags; + + if ((kbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &device, NULL)) != 0 || + (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 || + (r = sshbuf_get_u32(req, &flags)) != 0) + fatal_r(r, "%s: parse", __progname); + if (sshbuf_len(req) != 0) + fatal("%s: trailing data in request", __progname); + + null_empty(&device); + null_empty(&pin); + + if ((r = sshsk_load_resident(provider, device, pin, flags, + &srks, &nsrks)) != 0) { + resp = reply_error(r, "sshsk_load_resident failed: %s", + ssh_err(r)); + goto out; + } + + if ((resp = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + + if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0) + fatal_r(r, "%s: compose", __progname); + + for (i = 0; i < nsrks; i++) { + debug_f("key %zu %s %s uidlen %zu", i, + sshkey_type(srks[i]->key), srks[i]->key->sk_application, + srks[i]->user_id_len); + sshbuf_reset(kbuf); + if ((r = sshkey_private_serialize(srks[i]->key, kbuf)) != 0) + fatal_r(r, "%s: encode key", __progname); + if ((r = sshbuf_put_stringb(resp, kbuf)) != 0 || + (r = sshbuf_put_cstring(resp, "")) != 0 || /* comment */ + (r = sshbuf_put_string(resp, srks[i]->user_id, + srks[i]->user_id_len)) != 0) + fatal_r(r, "%s: compose key", __progname); + } + + out: + sshsk_free_resident_keys(srks, nsrks); + sshbuf_free(kbuf); + free(provider); + if (pin != NULL) + freezero(pin, strlen(pin)); + return resp; +} + +int +main(int argc, char **argv) +{ + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_ERROR; + struct sshbuf *req, *resp; + int in, out, ch, r, vflag = 0; + u_int rtype, ll = 0; + uint8_t version, log_stderr = 0; + + sanitise_stdfd(); + log_init(__progname, log_level, log_facility, log_stderr); + + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + vflag = 1; + if (log_level == SYSLOG_LEVEL_ERROR) + log_level = SYSLOG_LEVEL_DEBUG1; + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; + break; + default: + fprintf(stderr, "usage: %s [-v]\n", __progname); + exit(1); + } + } + log_init(__progname, log_level, log_facility, vflag); + + /* + * Rearrange our file descriptors a little; we don't trust the + * providers not to fiddle with stdin/out. + */ + closefrom(STDERR_FILENO + 1); + if ((in = dup(STDIN_FILENO)) == -1 || (out = dup(STDOUT_FILENO)) == -1) + fatal("%s: dup: %s", __progname, strerror(errno)); + close(STDIN_FILENO); + close(STDOUT_FILENO); + sanitise_stdfd(); /* resets to /dev/null */ + + if ((req = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __progname); + if (ssh_msg_recv(in, req) < 0) + fatal("ssh_msg_recv failed"); + close(in); + debug_f("received message len %zu", sshbuf_len(req)); + + if ((r = sshbuf_get_u8(req, &version)) != 0) + fatal_r(r, "%s: parse version", __progname); + if (version != SSH_SK_HELPER_VERSION) { + fatal("unsupported version: received %d, expected %d", + version, SSH_SK_HELPER_VERSION); + } + + if ((r = sshbuf_get_u32(req, &rtype)) != 0 || + (r = sshbuf_get_u8(req, &log_stderr)) != 0 || + (r = sshbuf_get_u32(req, &ll)) != 0) + fatal_r(r, "%s: parse", __progname); + + if (!vflag && log_level_name((LogLevel)ll) != NULL) + log_init(__progname, (LogLevel)ll, log_facility, log_stderr); + + switch (rtype) { + case SSH_SK_HELPER_SIGN: + resp = process_sign(req); + break; + case SSH_SK_HELPER_ENROLL: + resp = process_enroll(req); + break; + case SSH_SK_HELPER_LOAD_RESIDENT: + resp = process_load_resident(req); + break; + default: + fatal("%s: unsupported request type %u", __progname, rtype); + } + sshbuf_free(req); + debug_f("reply len %zu", sshbuf_len(resp)); + + if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1) + fatal("ssh_msg_send failed"); + sshbuf_free(resp); + close(out); + + return (0); +} +#else /* ENABLE_SK */ +#include + +int +main(int argc, char **argv) +{ + fprintf(stderr, "ssh-sk-helper: disabled at compile time\n"); + return -1; +} +#endif /* ENABLE_SK */ diff --git a/aosp/external/openssh/ssh-sk.c b/aosp/external/openssh/ssh-sk.c new file mode 100644 index 0000000000000000000000000000000000000000..a1ff5cc485e8a788caf97b590b17760794455de7 --- /dev/null +++ b/aosp/external/openssh/ssh-sk.c @@ -0,0 +1,874 @@ +/* $OpenBSD: ssh-sk.c,v 1.38 2022/01/14 03:35:10 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* #define DEBUG_SK 1 */ + +#include "includes.h" + +#ifdef ENABLE_SK + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) +#include +#include +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ + +#include "log.h" +#include "misc.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "ssherr.h" +#include "digest.h" + +#include "ssh-sk.h" +#include "sk-api.h" +#include "crypto_api.h" + +/* + * Almost every use of OpenSSL in this file is for ECDSA-NISTP256. + * This is strictly a larger hammer than necessary, but it reduces changes + * with upstream. + */ +#ifndef OPENSSL_HAS_ECC +# undef WITH_OPENSSL +#endif + +struct sshsk_provider { + char *path; + void *dlhandle; + + /* Return the version of the middleware API */ + uint32_t (*sk_api_version)(void); + + /* Enroll a U2F key (private key generation) */ + int (*sk_enroll)(int alg, const uint8_t *challenge, + size_t challenge_len, const char *application, uint8_t flags, + const char *pin, struct sk_option **opts, + struct sk_enroll_response **enroll_response); + + /* Sign a challenge */ + int (*sk_sign)(int alg, const uint8_t *message, size_t message_len, + const char *application, + const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, const char *pin, struct sk_option **opts, + struct sk_sign_response **sign_response); + + /* Enumerate resident keys */ + int (*sk_load_resident_keys)(const char *pin, struct sk_option **opts, + struct sk_resident_key ***rks, size_t *nrks); +}; + +/* Built-in version */ +int ssh_sk_enroll(int alg, const uint8_t *challenge, + size_t challenge_len, const char *application, uint8_t flags, + const char *pin, struct sk_option **opts, + struct sk_enroll_response **enroll_response); +int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len, + const char *application, + const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, const char *pin, struct sk_option **opts, + struct sk_sign_response **sign_response); +int ssh_sk_load_resident_keys(const char *pin, struct sk_option **opts, + struct sk_resident_key ***rks, size_t *nrks); + +static void +sshsk_free(struct sshsk_provider *p) +{ + if (p == NULL) + return; + free(p->path); + if (p->dlhandle != NULL) + dlclose(p->dlhandle); + free(p); +} + +static struct sshsk_provider * +sshsk_open(const char *path) +{ + struct sshsk_provider *ret = NULL; + uint32_t version; + + if (path == NULL || *path == '\0') { + error("No FIDO SecurityKeyProvider specified"); + return NULL; + } + if ((ret = calloc(1, sizeof(*ret))) == NULL) { + error_f("calloc failed"); + return NULL; + } + if ((ret->path = strdup(path)) == NULL) { + error_f("strdup failed"); + goto fail; + } + /* Skip the rest if we're using the linked in middleware */ + if (strcasecmp(ret->path, "internal") == 0) { +#ifdef ENABLE_SK_INTERNAL + ret->sk_enroll = ssh_sk_enroll; + ret->sk_sign = ssh_sk_sign; + ret->sk_load_resident_keys = ssh_sk_load_resident_keys; +#else + error("internal security key support not enabled"); +#endif + return ret; + } + if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) { + error("Provider \"%s\" dlopen failed: %s", path, dlerror()); + goto fail; + } + if ((ret->sk_api_version = dlsym(ret->dlhandle, + "sk_api_version")) == NULL) { + error("Provider \"%s\" dlsym(sk_api_version) failed: %s", + path, dlerror()); + goto fail; + } + version = ret->sk_api_version(); + debug_f("provider %s implements version 0x%08lx", ret->path, + (u_long)version); + if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) { + error("Provider \"%s\" implements unsupported " + "version 0x%08lx (supported: 0x%08lx)", + path, (u_long)version, (u_long)SSH_SK_VERSION_MAJOR); + goto fail; + } + if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) { + error("Provider %s dlsym(sk_enroll) failed: %s", + path, dlerror()); + goto fail; + } + if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) { + error("Provider \"%s\" dlsym(sk_sign) failed: %s", + path, dlerror()); + goto fail; + } + if ((ret->sk_load_resident_keys = dlsym(ret->dlhandle, + "sk_load_resident_keys")) == NULL) { + error("Provider \"%s\" dlsym(sk_load_resident_keys) " + "failed: %s", path, dlerror()); + goto fail; + } + /* success */ + return ret; +fail: + sshsk_free(ret); + return NULL; +} + +static void +sshsk_free_enroll_response(struct sk_enroll_response *r) +{ + if (r == NULL) + return; + freezero(r->key_handle, r->key_handle_len); + freezero(r->public_key, r->public_key_len); + freezero(r->signature, r->signature_len); + freezero(r->attestation_cert, r->attestation_cert_len); + freezero(r->authdata, r->authdata_len); + freezero(r, sizeof(*r)); +} + +static void +sshsk_free_sign_response(struct sk_sign_response *r) +{ + if (r == NULL) + return; + freezero(r->sig_r, r->sig_r_len); + freezero(r->sig_s, r->sig_s_len); + freezero(r, sizeof(*r)); +} + +#ifdef WITH_OPENSSL +/* Assemble key from response */ +static int +sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) +{ + struct sshkey *key = NULL; + struct sshbuf *b = NULL; + EC_POINT *q = NULL; + int r; + + *keyp = NULL; + if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) { + error_f("sshkey_new failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + key->ecdsa_nid = NID_X9_62_prime256v1; + if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL || + (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL || + (b = sshbuf_new()) == NULL) { + error_f("allocation failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_string(b, + resp->public_key, resp->public_key_len)) != 0) { + error_fr(r, "sshbuf_put_string"); + goto out; + } + if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) { + error_fr(r, "parse"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) { + error("Authenticator returned invalid ECDSA key"); + r = SSH_ERR_KEY_INVALID_EC_VALUE; + goto out; + } + if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { + /* XXX assume it is a allocation error */ + error_f("allocation failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* success */ + *keyp = key; + key = NULL; /* transferred */ + r = 0; + out: + EC_POINT_free(q); + sshkey_free(key); + sshbuf_free(b); + return r; +} +#endif /* WITH_OPENSSL */ + +static int +sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp) +{ + struct sshkey *key = NULL; + int r; + + *keyp = NULL; + if (resp->public_key_len != ED25519_PK_SZ) { + error_f("invalid size: %zu", resp->public_key_len); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) { + error_f("sshkey_new failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { + error_f("malloc failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ); + /* success */ + *keyp = key; + key = NULL; /* transferred */ + r = 0; + out: + sshkey_free(key); + return r; +} + +static int +sshsk_key_from_response(int alg, const char *application, uint8_t flags, + struct sk_enroll_response *resp, struct sshkey **keyp) +{ + struct sshkey *key = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + *keyp = NULL; + + /* Check response validity */ + if (resp->public_key == NULL || resp->key_handle == NULL) { + error_f("sk_enroll response invalid"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + switch (alg) { +#ifdef WITH_OPENSSL + case SSH_SK_ECDSA: + if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0) + goto out; + break; +#endif /* WITH_OPENSSL */ + case SSH_SK_ED25519: + if ((r = sshsk_ed25519_assemble(resp, &key)) != 0) + goto out; + break; + default: + error_f("unsupported algorithm %d", alg); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + key->sk_flags = flags; + if ((key->sk_key_handle = sshbuf_new()) == NULL || + (key->sk_reserved = sshbuf_new()) == NULL) { + error_f("allocation failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((key->sk_application = strdup(application)) == NULL) { + error_f("strdup application failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle, + resp->key_handle_len)) != 0) { + error_fr(r, "put key handle"); + goto out; + } + /* success */ + r = 0; + *keyp = key; + key = NULL; + out: + sshkey_free(key); + return r; +} + +static int +skerr_to_ssherr(int skerr) +{ + switch (skerr) { + case SSH_SK_ERR_UNSUPPORTED: + return SSH_ERR_FEATURE_UNSUPPORTED; + case SSH_SK_ERR_PIN_REQUIRED: + return SSH_ERR_KEY_WRONG_PASSPHRASE; + case SSH_SK_ERR_DEVICE_NOT_FOUND: + return SSH_ERR_DEVICE_NOT_FOUND; + case SSH_SK_ERR_GENERAL: + default: + return SSH_ERR_INVALID_FORMAT; + } +} + +static void +sshsk_free_options(struct sk_option **opts) +{ + size_t i; + + if (opts == NULL) + return; + for (i = 0; opts[i] != NULL; i++) { + free(opts[i]->name); + free(opts[i]->value); + free(opts[i]); + } + free(opts); +} + +static int +sshsk_add_option(struct sk_option ***optsp, size_t *noptsp, + const char *name, const char *value, uint8_t required) +{ + struct sk_option **opts = *optsp; + size_t nopts = *noptsp; + + if ((opts = recallocarray(opts, nopts, nopts + 2, /* extra for NULL */ + sizeof(*opts))) == NULL) { + error_f("array alloc failed"); + return SSH_ERR_ALLOC_FAIL; + } + *optsp = opts; + *noptsp = nopts + 1; + if ((opts[nopts] = calloc(1, sizeof(**opts))) == NULL) { + error_f("alloc failed"); + return SSH_ERR_ALLOC_FAIL; + } + if ((opts[nopts]->name = strdup(name)) == NULL || + (opts[nopts]->value = strdup(value)) == NULL) { + error_f("alloc failed"); + return SSH_ERR_ALLOC_FAIL; + } + opts[nopts]->required = required; + return 0; +} + +static int +make_options(const char *device, const char *user_id, + struct sk_option ***optsp) +{ + struct sk_option **opts = NULL; + size_t nopts = 0; + int r, ret = SSH_ERR_INTERNAL_ERROR; + + if (device != NULL && + (r = sshsk_add_option(&opts, &nopts, "device", device, 0)) != 0) { + ret = r; + goto out; + } + if (user_id != NULL && + (r = sshsk_add_option(&opts, &nopts, "user", user_id, 0)) != 0) { + ret = r; + goto out; + } + /* success */ + *optsp = opts; + opts = NULL; + nopts = 0; + ret = 0; + out: + sshsk_free_options(opts); + return ret; +} + + +static int +fill_attestation_blob(const struct sk_enroll_response *resp, + struct sshbuf *attest) +{ + int r; + + if (attest == NULL) + return 0; /* nothing to do */ + if ((r = sshbuf_put_cstring(attest, "ssh-sk-attest-v01")) != 0 || + (r = sshbuf_put_string(attest, + resp->attestation_cert, resp->attestation_cert_len)) != 0 || + (r = sshbuf_put_string(attest, + resp->signature, resp->signature_len)) != 0 || + (r = sshbuf_put_string(attest, + resp->authdata, resp->authdata_len)) != 0 || + (r = sshbuf_put_u32(attest, 0)) != 0 || /* resvd flags */ + (r = sshbuf_put_string(attest, NULL, 0)) != 0 /* resvd */) { + error_fr(r, "compose"); + return r; + } + /* success */ + return 0; +} + +int +sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest) +{ + struct sshsk_provider *skp = NULL; + struct sshkey *key = NULL; + u_char randchall[32]; + const u_char *challenge; + size_t challenge_len; + struct sk_enroll_response *resp = NULL; + struct sk_option **opts = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + int alg; + + debug_f("provider \"%s\", device \"%s\", application \"%s\", " + "userid \"%s\", flags 0x%02x, challenge len %zu%s", + provider_path, device, application, userid, flags, + challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf), + (pin != NULL && *pin != '\0') ? " with-pin" : ""); + + *keyp = NULL; + if (attest) + sshbuf_reset(attest); + + if ((r = make_options(device, userid, &opts)) != 0) + goto out; + + switch (type) { +#ifdef WITH_OPENSSL + case KEY_ECDSA_SK: + alg = SSH_SK_ECDSA; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + alg = SSH_SK_ED25519; + break; + default: + error_f("unsupported key type"); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (provider_path == NULL) { + error_f("missing provider"); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (application == NULL || *application == '\0') { + error_f("missing application"); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (challenge_buf == NULL) { + debug_f("using random challenge"); + arc4random_buf(randchall, sizeof(randchall)); + challenge = randchall; + challenge_len = sizeof(randchall); + } else if (sshbuf_len(challenge_buf) == 0) { + error("Missing enrollment challenge"); + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } else { + challenge = sshbuf_ptr(challenge_buf); + challenge_len = sshbuf_len(challenge_buf); + debug3_f("using explicit challenge len=%zd", challenge_len); + } + if ((skp = sshsk_open(provider_path)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ + goto out; + } + /* XXX validate flags? */ + /* enroll key */ + if ((r = skp->sk_enroll(alg, challenge, challenge_len, application, + flags, pin, opts, &resp)) != 0) { + debug_f("provider \"%s\" failure %d", provider_path, r); + r = skerr_to_ssherr(r); + goto out; + } + + if ((r = sshsk_key_from_response(alg, application, resp->flags, + resp, &key)) != 0) + goto out; + + /* Optionally fill in the attestation information */ + if ((r = fill_attestation_blob(resp, attest)) != 0) + goto out; + + /* success */ + *keyp = key; + key = NULL; /* transferred */ + r = 0; + out: + sshsk_free_options(opts); + sshsk_free(skp); + sshkey_free(key); + sshsk_free_enroll_response(resp); + explicit_bzero(randchall, sizeof(randchall)); + return r; +} + +#ifdef WITH_OPENSSL +static int +sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig) +{ + struct sshbuf *inner_sig = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + /* Check response validity */ + if (resp->sig_r == NULL || resp->sig_s == NULL) { + error_f("sk_sign response invalid"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((inner_sig = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* Prepare and append inner signature object */ + if ((r = sshbuf_put_bignum2_bytes(inner_sig, + resp->sig_r, resp->sig_r_len)) != 0 || + (r = sshbuf_put_bignum2_bytes(inner_sig, + resp->sig_s, resp->sig_s_len)) != 0) { + error_fr(r, "compose inner"); + goto out; + } + if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 || + (r = sshbuf_put_u8(sig, resp->flags)) != 0 || + (r = sshbuf_put_u32(sig, resp->counter)) != 0) { + error_fr(r, "compose"); + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: sig_r:\n", __func__); + sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); + fprintf(stderr, "%s: sig_s:\n", __func__); + sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr); + fprintf(stderr, "%s: inner:\n", __func__); + sshbuf_dump(inner_sig, stderr); +#endif + r = 0; + out: + sshbuf_free(inner_sig); + return r; +} +#endif /* WITH_OPENSSL */ + +static int +sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig) +{ + int r = SSH_ERR_INTERNAL_ERROR; + + /* Check response validity */ + if (resp->sig_r == NULL) { + error_f("sk_sign response invalid"); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((r = sshbuf_put_string(sig, + resp->sig_r, resp->sig_r_len)) != 0 || + (r = sshbuf_put_u8(sig, resp->flags)) != 0 || + (r = sshbuf_put_u32(sig, resp->counter)) != 0) { + error_fr(r, "compose"); + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: sig_r:\n", __func__); + sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr); +#endif + r = 0; + out: + return r; +} + +int +sshsk_sign(const char *provider_path, struct sshkey *key, + u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, + u_int compat, const char *pin) +{ + struct sshsk_provider *skp = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + int type, alg; + struct sk_sign_response *resp = NULL; + struct sshbuf *inner_sig = NULL, *sig = NULL; + struct sk_option **opts = NULL; + + debug_f("provider \"%s\", key %s, flags 0x%02x%s", + provider_path, sshkey_type(key), key->sk_flags, + (pin != NULL && *pin != '\0') ? " with-pin" : ""); + + if (sigp != NULL) + *sigp = NULL; + if (lenp != NULL) + *lenp = 0; + type = sshkey_type_plain(key->type); + switch (type) { +#ifdef WITH_OPENSSL + case KEY_ECDSA_SK: + alg = SSH_SK_ECDSA; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + alg = SSH_SK_ED25519; + break; + default: + return SSH_ERR_INVALID_ARGUMENT; + } + if (provider_path == NULL || + key->sk_key_handle == NULL || + key->sk_application == NULL || *key->sk_application == '\0') { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((skp = sshsk_open(provider_path)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ + goto out; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: sk_flags = 0x%02x, sk_application = \"%s\"\n", + __func__, key->sk_flags, key->sk_application); + fprintf(stderr, "%s: sk_key_handle:\n", __func__); + sshbuf_dump(key->sk_key_handle, stderr); +#endif + if ((r = skp->sk_sign(alg, data, datalen, key->sk_application, + sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle), + key->sk_flags, pin, opts, &resp)) != 0) { + debug_f("sk_sign failed with code %d", r); + r = skerr_to_ssherr(r); + goto out; + } + /* Assemble signature */ + if ((sig = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) { + error_fr(r, "compose outer"); + goto out; + } + switch (type) { +#ifdef WITH_OPENSSL + case KEY_ECDSA_SK: + if ((r = sshsk_ecdsa_sig(resp, sig)) != 0) + goto out; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + if ((r = sshsk_ed25519_sig(resp, sig)) != 0) + goto out; + break; + } +#ifdef DEBUG_SK + fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", + __func__, resp->flags, resp->counter); + fprintf(stderr, "%s: data to sign:\n", __func__); + sshbuf_dump_data(data, datalen, stderr); + fprintf(stderr, "%s: sigbuf:\n", __func__); + sshbuf_dump(sig, stderr); +#endif + if (sigp != NULL) { + if ((*sigp = malloc(sshbuf_len(sig))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig)); + } + if (lenp != NULL) + *lenp = sshbuf_len(sig); + /* success */ + r = 0; + out: + sshsk_free_options(opts); + sshsk_free(skp); + sshsk_free_sign_response(resp); + sshbuf_free(sig); + sshbuf_free(inner_sig); + return r; +} + +static void +sshsk_free_sk_resident_keys(struct sk_resident_key **rks, size_t nrks) +{ + size_t i; + + if (nrks == 0 || rks == NULL) + return; + for (i = 0; i < nrks; i++) { + free(rks[i]->application); + freezero(rks[i]->user_id, rks[i]->user_id_len); + freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len); + freezero(rks[i]->key.public_key, rks[i]->key.public_key_len); + freezero(rks[i]->key.signature, rks[i]->key.signature_len); + freezero(rks[i]->key.attestation_cert, + rks[i]->key.attestation_cert_len); + freezero(rks[i], sizeof(**rks)); + } + free(rks); +} + +static void +sshsk_free_resident_key(struct sshsk_resident_key *srk) +{ + if (srk == NULL) + return; + sshkey_free(srk->key); + freezero(srk->user_id, srk->user_id_len); + free(srk); +} + + +void +sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks) +{ + size_t i; + + if (srks == NULL || nsrks == 0) + return; + + for (i = 0; i < nsrks; i++) + sshsk_free_resident_key(srks[i]); + free(srks); +} + +int +sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, u_int flags, struct sshsk_resident_key ***srksp, + size_t *nsrksp) +{ + struct sshsk_provider *skp = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + struct sk_resident_key **rks = NULL; + size_t i, nrks = 0, nsrks = 0; + struct sshkey *key = NULL; + struct sshsk_resident_key *srk = NULL, **srks = NULL, **tmp; + uint8_t sk_flags; + struct sk_option **opts = NULL; + + debug_f("provider \"%s\"%s", provider_path, + (pin != NULL && *pin != '\0') ? ", have-pin": ""); + + if (srksp == NULL || nsrksp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + *srksp = NULL; + *nsrksp = 0; + + if ((r = make_options(device, NULL, &opts)) != 0) + goto out; + if ((skp = sshsk_open(provider_path)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */ + goto out; + } + if ((r = skp->sk_load_resident_keys(pin, opts, &rks, &nrks)) != 0) { + error("Provider \"%s\" returned failure %d", provider_path, r); + r = skerr_to_ssherr(r); + goto out; + } + for (i = 0; i < nrks; i++) { + debug3_f("rk %zu: slot %zu, alg %d, app \"%s\", uidlen %zu", + i, rks[i]->slot, rks[i]->alg, rks[i]->application, + rks[i]->user_id_len); + /* XXX need better filter here */ + if (strncmp(rks[i]->application, "ssh:", 4) != 0) + continue; + switch (rks[i]->alg) { + case SSH_SK_ECDSA: + case SSH_SK_ED25519: + break; + default: + continue; + } + sk_flags = SSH_SK_USER_PRESENCE_REQD|SSH_SK_RESIDENT_KEY; + if ((rks[i]->flags & SSH_SK_USER_VERIFICATION_REQD)) + sk_flags |= SSH_SK_USER_VERIFICATION_REQD; + if ((r = sshsk_key_from_response(rks[i]->alg, + rks[i]->application, sk_flags, &rks[i]->key, &key)) != 0) + goto out; + if ((srk = calloc(1, sizeof(*srk))) == NULL) { + error_f("calloc failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + srk->key = key; + key = NULL; /* transferred */ + if ((srk->user_id = calloc(1, rks[i]->user_id_len)) == NULL) { + error_f("calloc failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(srk->user_id, rks[i]->user_id, rks[i]->user_id_len); + srk->user_id_len = rks[i]->user_id_len; + if ((tmp = recallocarray(srks, nsrks, nsrks + 1, + sizeof(*tmp))) == NULL) { + error_f("recallocarray failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + srks = tmp; + srks[nsrks++] = srk; + srk = NULL; + /* XXX synthesise comment */ + } + /* success */ + *srksp = srks; + *nsrksp = nsrks; + srks = NULL; + nsrks = 0; + r = 0; + out: + sshsk_free_options(opts); + sshsk_free(skp); + sshsk_free_sk_resident_keys(rks, nrks); + sshkey_free(key); + sshsk_free_resident_key(srk); + sshsk_free_resident_keys(srks, nsrks); + return r; +} + +#endif /* ENABLE_SK */ diff --git a/aosp/external/openssh/ssh-sk.h b/aosp/external/openssh/ssh-sk.h new file mode 100644 index 0000000000000000000000000000000000000000..89d1b6627854b7905baa59c2ba8fac3beccca91d --- /dev/null +++ b/aosp/external/openssh/ssh-sk.h @@ -0,0 +1,79 @@ +/* $OpenBSD: ssh-sk.h,v 1.11 2021/10/28 02:54:18 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SSH_SK_H +#define _SSH_SK_H 1 + +struct sshbuf; +struct sshkey; +struct sk_option; + +/* Version of protocol expected from ssh-sk-helper */ +#define SSH_SK_HELPER_VERSION 5 + +/* ssh-sk-helper messages */ +#define SSH_SK_HELPER_ERROR 0 /* Only valid H->C */ +#define SSH_SK_HELPER_SIGN 1 +#define SSH_SK_HELPER_ENROLL 2 +#define SSH_SK_HELPER_LOAD_RESIDENT 3 + +struct sshsk_resident_key { + struct sshkey *key; + uint8_t *user_id; + size_t user_id_len; +}; + +/* + * Enroll (generate) a new security-key hosted private key of given type + * via the specified provider middleware. + * If challenge_buf is NULL then a random 256 bit challenge will be used. + * + * Returns 0 on success or a ssherr.h error code on failure. + * + * If successful and the attest_data buffer is not NULL then attestation + * information is placed there. + */ +int sshsk_enroll(int type, const char *provider_path, const char *device, + const char *application, const char *userid, uint8_t flags, + const char *pin, struct sshbuf *challenge_buf, + struct sshkey **keyp, struct sshbuf *attest); + +/* + * Calculate an ECDSA_SK or ED25519_SK signature using the specified key + * and provider middleware. + * + * Returns 0 on success or a ssherr.h error code on failure. + */ +int sshsk_sign(const char *provider_path, struct sshkey *key, + u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, + u_int compat, const char *pin); + +/* + * Enumerates and loads all SSH-compatible resident keys from a security + * key. + * + * Returns 0 on success or a ssherr.h error code on failure. + */ +int sshsk_load_resident(const char *provider_path, const char *device, + const char *pin, u_int flags, struct sshsk_resident_key ***srksp, + size_t *nsrksp); + +/* Free an array of sshsk_resident_key (as returned from sshsk_load_resident) */ +void sshsk_free_resident_keys(struct sshsk_resident_key **srks, size_t nsrks); + +#endif /* _SSH_SK_H */ + diff --git a/aosp/external/openssh/ssh-xmss.c b/aosp/external/openssh/ssh-xmss.c new file mode 100644 index 0000000000000000000000000000000000000000..7bd3a96a3bf599029aa50993dbc7a94a801aeef0 --- /dev/null +++ b/aosp/external/openssh/ssh-xmss.c @@ -0,0 +1,185 @@ +/* $OpenBSD: ssh-xmss.c,v 1.4 2020/10/19 22:49:23 dtucker Exp $*/ +/* + * Copyright (c) 2017 Stefan-Lukas Gazdag. + * Copyright (c) 2017 Markus Friedl. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "includes.h" +#ifdef WITH_XMSS + +#define SSHKEY_INTERNAL +#include +#include + +#include +#include +#include + +#include "log.h" +#include "sshbuf.h" +#include "sshkey.h" +#include "sshkey-xmss.h" +#include "ssherr.h" +#include "ssh.h" + +#include "xmss_fast.h" + +int +ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) +{ + u_char *sig = NULL; + size_t slen = 0, len = 0, required_siglen; + unsigned long long smlen; + int r, ret; + struct sshbuf *b = NULL; + + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_XMSS || + key->xmss_sk == NULL || + sshkey_xmss_params(key) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) + return r; + if (datalen >= INT_MAX - required_siglen) + return SSH_ERR_INVALID_ARGUMENT; + smlen = slen = datalen + required_siglen; + if ((sig = malloc(slen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_xmss_get_state(key, 1)) != 0) + goto out; + if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen, + data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) { + r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ + goto out; + } + /* encode signature */ + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 || + (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); + } + if (lenp != NULL) + *lenp = len; + /* success */ + r = 0; + out: + if ((ret = sshkey_xmss_update_state(key, 1)) != 0) { + /* discard signature since we cannot update the state */ + if (r == 0 && sigp != NULL && *sigp != NULL) { + explicit_bzero(*sigp, len); + free(*sigp); + } + if (sigp != NULL) + *sigp = NULL; + if (lenp != NULL) + *lenp = 0; + r = ret; + } + sshbuf_free(b); + if (sig != NULL) + freezero(sig, slen); + + return r; +} + +int +ssh_xmss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) +{ + struct sshbuf *b = NULL; + char *ktype = NULL; + const u_char *sigblob; + u_char *sm = NULL, *m = NULL; + size_t len, required_siglen; + unsigned long long smlen = 0, mlen = 0; + int r, ret; + + if (key == NULL || + sshkey_type_plain(key->type) != KEY_XMSS || + key->xmss_pk == NULL || + sshkey_xmss_params(key) == NULL || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) + return r; + if (datalen >= INT_MAX - required_siglen) + return SSH_ERR_INVALID_ARGUMENT; + + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || + (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) + goto out; + if (strcmp("ssh-xmss@openssh.com", ktype) != 0) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + r = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + if (len != required_siglen) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (datalen >= SIZE_MAX - len) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + smlen = len + datalen; + mlen = smlen; + if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(sm, sigblob, len); + memcpy(sm+len, data, datalen); + if ((ret = xmss_sign_open(m, &mlen, sm, smlen, + key->xmss_pk, sshkey_xmss_params(key))) != 0) { + debug2_f("xmss_sign_open failed: %d", ret); + } + if (ret != 0 || mlen != datalen) { + r = SSH_ERR_SIGNATURE_INVALID; + goto out; + } + /* XXX compare 'm' and 'data' ? */ + /* success */ + r = 0; + out: + if (sm != NULL) + freezero(sm, smlen); + if (m != NULL) + freezero(m, smlen); + sshbuf_free(b); + free(ktype); + return r; +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/ssh.1 b/aosp/external/openssh/ssh.1 new file mode 100644 index 0000000000000000000000000000000000000000..4a4f1683a82a21475c26f0c5fa6c00484f03dfe3 --- /dev/null +++ b/aosp/external/openssh/ssh.1 @@ -0,0 +1,1778 @@ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: ssh.1,v 1.430 2022/03/31 17:27:27 naddy Exp $ +.Dd $Mdocdate: March 31 2022 $ +.Dt SSH 1 +.Os +.Sh NAME +.Nm ssh +.Nd OpenSSH remote login client +.Sh SYNOPSIS +.Nm ssh +.Op Fl 46AaCfGgKkMNnqsTtVvXxYy +.Op Fl B Ar bind_interface +.Op Fl b Ar bind_address +.Op Fl c Ar cipher_spec +.Op Fl D Oo Ar bind_address : Oc Ns Ar port +.Op Fl E Ar log_file +.Op Fl e Ar escape_char +.Op Fl F Ar configfile +.Op Fl I Ar pkcs11 +.Op Fl i Ar identity_file +.Op Fl J Ar destination +.Op Fl L Ar address +.Op Fl l Ar login_name +.Op Fl m Ar mac_spec +.Op Fl O Ar ctl_cmd +.Op Fl o Ar option +.Op Fl p Ar port +.Op Fl Q Ar query_option +.Op Fl R Ar address +.Op Fl S Ar ctl_path +.Op Fl W Ar host : Ns Ar port +.Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun +.Ar destination +.Op Ar command Op Ar argument ... +.Sh DESCRIPTION +.Nm +(SSH client) is a program for logging into a remote machine and for +executing commands on a remote machine. +It is intended to provide secure encrypted communications between +two untrusted hosts over an insecure network. +X11 connections, arbitrary TCP ports and +.Ux Ns -domain +sockets can also be forwarded over the secure channel. +.Pp +.Nm +connects and logs into the specified +.Ar destination , +which may be specified as either +.Sm off +.Oo user @ Oc hostname +.Sm on +or a URI of the form +.Sm off +.No ssh:// Oo user @ Oc hostname Op : port . +.Sm on +The user must prove +their identity to the remote machine using one of several methods +(see below). +.Pp +If a +.Ar command +is specified, +it will be executed on the remote host instead of a login shell. +A complete command line may be specified as +.Ar command , +or it may have additional arguments. +If supplied, the arguments will be appended to the command, separated by +spaces, before it is sent to the server to be executed. +.Pp +The options are as follows: +.Pp +.Bl -tag -width Ds -compact +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.Pp +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.Pp +.It Fl A +Enables forwarding of connections from an authentication agent such as +.Xr ssh-agent 1 . +This can also be specified on a per-host basis in a configuration file. +.Pp +Agent forwarding should be enabled with caution. +Users with the ability to bypass file permissions on the remote host +(for the agent's +.Ux Ns -domain +socket) can access the local agent through the forwarded connection. +An attacker cannot obtain key material from the agent, +however they can perform operations on the keys that enable them to +authenticate using the identities loaded into the agent. +A safer alternative may be to use a jump host +(see +.Fl J ) . +.Pp +.It Fl a +Disables forwarding of the authentication agent connection. +.Pp +.It Fl B Ar bind_interface +Bind to the address of +.Ar bind_interface +before attempting to connect to the destination host. +This is only useful on systems with more than one address. +.Pp +.It Fl b Ar bind_address +Use +.Ar bind_address +on the local machine as the source address +of the connection. +Only useful on systems with more than one address. +.Pp +.It Fl C +Requests compression of all data (including stdin, stdout, stderr, and +data for forwarded X11, TCP and +.Ux Ns -domain +connections). +The compression algorithm is the same used by +.Xr gzip 1 . +Compression is desirable on modem lines and other +slow connections, but will only slow down things on fast networks. +The default value can be set on a host-by-host basis in the +configuration files; see the +.Cm Compression +option. +.Pp +.It Fl c Ar cipher_spec +Selects the cipher specification for encrypting the session. +.Ar cipher_spec +is a comma-separated list of ciphers +listed in order of preference. +See the +.Cm Ciphers +keyword in +.Xr ssh_config 5 +for more information. +.Pp +.It Fl D Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port +.Sm on +.Xc +Specifies a local +.Dq dynamic +application-level port forwarding. +This works by allocating a socket to listen to +.Ar port +on the local side, optionally bound to the specified +.Ar bind_address . +Whenever a connection is made to this port, the +connection is forwarded over the secure channel, and the application +protocol is then used to determine where to connect to from the +remote machine. +Currently the SOCKS4 and SOCKS5 protocols are supported, and +.Nm +will act as a SOCKS server. +Only root can forward privileged ports. +Dynamic port forwardings can also be specified in the configuration file. +.Pp +IPv6 addresses can be specified by enclosing the address in square brackets. +Only the superuser can forward privileged ports. +By default, the local port is bound in accordance with the +.Cm GatewayPorts +setting. +However, an explicit +.Ar bind_address +may be used to bind the connection to a specific address. +The +.Ar bind_address +of +.Dq localhost +indicates that the listening port be bound for local use only, while an +empty address or +.Sq * +indicates that the port should be available from all interfaces. +.Pp +.It Fl E Ar log_file +Append debug logs to +.Ar log_file +instead of standard error. +.Pp +.It Fl e Ar escape_char +Sets the escape character for sessions with a pty (default: +.Ql ~ ) . +The escape character is only recognized at the beginning of a line. +The escape character followed by a dot +.Pq Ql \&. +closes the connection; +followed by control-Z suspends the connection; +and followed by itself sends the escape character once. +Setting the character to +.Dq none +disables any escapes and makes the session fully transparent. +.Pp +.It Fl F Ar configfile +Specifies an alternative per-user configuration file. +If a configuration file is given on the command line, +the system-wide configuration file +.Pq Pa /etc/ssh/ssh_config +will be ignored. +The default for the per-user configuration file is +.Pa ~/.ssh/config . +If set to +.Dq none , +no configuration files will be read. +.Pp +.It Fl f +Requests +.Nm +to go to background just before command execution. +This is useful if +.Nm +is going to ask for passwords or passphrases, but the user +wants it in the background. +This implies +.Fl n . +The recommended way to start X11 programs at a remote site is with +something like +.Ic ssh -f host xterm . +.Pp +If the +.Cm ExitOnForwardFailure +configuration option is set to +.Dq yes , +then a client started with +.Fl f +will wait for all remote port forwards to be successfully established +before placing itself in the background. +Refer to the description of +.Cm ForkAfterAuthentication +in +.Xr ssh_config 5 +for details. +.Pp +.It Fl G +Causes +.Nm +to print its configuration after evaluating +.Cm Host +and +.Cm Match +blocks and exit. +.Pp +.It Fl g +Allows remote hosts to connect to local forwarded ports. +If used on a multiplexed connection, then this option must be specified +on the master process. +.Pp +.It Fl I Ar pkcs11 +Specify the PKCS#11 shared library +.Nm +should use to communicate with a PKCS#11 token providing keys for user +authentication. +.Pp +.It Fl i Ar identity_file +Selects a file from which the identity (private key) for +public key authentication is read. +You can also specify a public key file to use the corresponding +private key that is loaded in +.Xr ssh-agent 1 +when the private key file is not present locally. +The default is +.Pa ~/.ssh/id_rsa , +.Pa ~/.ssh/id_ecdsa , +.Pa ~/.ssh/id_ecdsa_sk , +.Pa ~/.ssh/id_ed25519 , +.Pa ~/.ssh/id_ed25519_sk +and +.Pa ~/.ssh/id_dsa . +Identity files may also be specified on +a per-host basis in the configuration file. +It is possible to have multiple +.Fl i +options (and multiple identities specified in +configuration files). +If no certificates have been explicitly specified by the +.Cm CertificateFile +directive, +.Nm +will also try to load certificate information from the filename obtained +by appending +.Pa -cert.pub +to identity filenames. +.Pp +.It Fl J Ar destination +Connect to the target host by first making a +.Nm +connection to the jump host described by +.Ar destination +and then establishing a TCP forwarding to the ultimate destination from +there. +Multiple jump hops may be specified separated by comma characters. +This is a shortcut to specify a +.Cm ProxyJump +configuration directive. +Note that configuration directives supplied on the command-line generally +apply to the destination host and not any specified jump hosts. +Use +.Pa ~/.ssh/config +to specify configuration for jump hosts. +.Pp +.It Fl K +Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI +credentials to the server. +.Pp +.It Fl k +Disables forwarding (delegation) of GSSAPI credentials to the server. +.Pp +.It Fl L Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port : host : hostport +.Sm on +.Xc +.It Fl L Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port : remote_socket +.Sm on +.Xc +.It Fl L Xo +.Sm off +.Ar local_socket : host : hostport +.Sm on +.Xc +.It Fl L Xo +.Sm off +.Ar local_socket : remote_socket +.Sm on +.Xc +Specifies that connections to the given TCP port or Unix socket on the local +(client) host are to be forwarded to the given host and port, or Unix socket, +on the remote side. +This works by allocating a socket to listen to either a TCP +.Ar port +on the local side, optionally bound to the specified +.Ar bind_address , +or to a Unix socket. +Whenever a connection is made to the local port or socket, the +connection is forwarded over the secure channel, and a connection is +made to either +.Ar host +port +.Ar hostport , +or the Unix socket +.Ar remote_socket , +from the remote machine. +.Pp +Port forwardings can also be specified in the configuration file. +Only the superuser can forward privileged ports. +IPv6 addresses can be specified by enclosing the address in square brackets. +.Pp +By default, the local port is bound in accordance with the +.Cm GatewayPorts +setting. +However, an explicit +.Ar bind_address +may be used to bind the connection to a specific address. +The +.Ar bind_address +of +.Dq localhost +indicates that the listening port be bound for local use only, while an +empty address or +.Sq * +indicates that the port should be available from all interfaces. +.Pp +.It Fl l Ar login_name +Specifies the user to log in as on the remote machine. +This also may be specified on a per-host basis in the configuration file. +.Pp +.It Fl M +Places the +.Nm +client into +.Dq master +mode for connection sharing. +Multiple +.Fl M +options places +.Nm +into +.Dq master +mode but with confirmation required using +.Xr ssh-askpass 1 +before each operation that changes the multiplexing state +(e.g. opening a new session). +Refer to the description of +.Cm ControlMaster +in +.Xr ssh_config 5 +for details. +.Pp +.It Fl m Ar mac_spec +A comma-separated list of MAC (message authentication code) algorithms, +specified in order of preference. +See the +.Cm MACs +keyword for more information. +.Pp +.It Fl N +Do not execute a remote command. +This is useful for just forwarding ports. +Refer to the description of +.Cm SessionType +in +.Xr ssh_config 5 +for details. +.Pp +.It Fl n +Redirects stdin from +.Pa /dev/null +(actually, prevents reading from stdin). +This must be used when +.Nm +is run in the background. +A common trick is to use this to run X11 programs on a remote machine. +For example, +.Ic ssh -n shadows.cs.hut.fi emacs & +will start an emacs on shadows.cs.hut.fi, and the X11 +connection will be automatically forwarded over an encrypted channel. +The +.Nm +program will be put in the background. +(This does not work if +.Nm +needs to ask for a password or passphrase; see also the +.Fl f +option.) +Refer to the description of +.Cm StdinNull +in +.Xr ssh_config 5 +for details. +.Pp +.It Fl O Ar ctl_cmd +Control an active connection multiplexing master process. +When the +.Fl O +option is specified, the +.Ar ctl_cmd +argument is interpreted and passed to the master process. +Valid commands are: +.Dq check +(check that the master process is running), +.Dq forward +(request forwardings without command execution), +.Dq cancel +(cancel forwardings), +.Dq exit +(request the master to exit), and +.Dq stop +(request the master to stop accepting further multiplexing requests). +.Pp +.It Fl o Ar option +Can be used to give options in the format used in the configuration file. +This is useful for specifying options for which there is no separate +command-line flag. +For full details of the options listed below, and their possible values, see +.Xr ssh_config 5 . +.Pp +.Bl -tag -width Ds -offset indent -compact +.It AddKeysToAgent +.It AddressFamily +.It BatchMode +.It BindAddress +.It CanonicalDomains +.It CanonicalizeFallbackLocal +.It CanonicalizeHostname +.It CanonicalizeMaxDots +.It CanonicalizePermittedCNAMEs +.It CASignatureAlgorithms +.It CertificateFile +.It CheckHostIP +.It Ciphers +.It ClearAllForwardings +.It Compression +.It ConnectionAttempts +.It ConnectTimeout +.It ControlMaster +.It ControlPath +.It ControlPersist +.It DynamicForward +.It EscapeChar +.It ExitOnForwardFailure +.It FingerprintHash +.It ForkAfterAuthentication +.It ForwardAgent +.It ForwardX11 +.It ForwardX11Timeout +.It ForwardX11Trusted +.It GatewayPorts +.It GlobalKnownHostsFile +.It GSSAPIAuthentication +.It GSSAPIDelegateCredentials +.It HashKnownHosts +.It Host +.It HostbasedAcceptedAlgorithms +.It HostbasedAuthentication +.It HostKeyAlgorithms +.It HostKeyAlias +.It Hostname +.It IdentitiesOnly +.It IdentityAgent +.It IdentityFile +.It IPQoS +.It KbdInteractiveAuthentication +.It KbdInteractiveDevices +.It KexAlgorithms +.It KnownHostsCommand +.It LocalCommand +.It LocalForward +.It LogLevel +.It MACs +.It Match +.It NoHostAuthenticationForLocalhost +.It NumberOfPasswordPrompts +.It PasswordAuthentication +.It PermitLocalCommand +.It PermitRemoteOpen +.It PKCS11Provider +.It Port +.It PreferredAuthentications +.It ProxyCommand +.It ProxyJump +.It ProxyUseFdpass +.It PubkeyAcceptedAlgorithms +.It PubkeyAuthentication +.It RekeyLimit +.It RemoteCommand +.It RemoteForward +.It RequestTTY +.It SendEnv +.It ServerAliveInterval +.It ServerAliveCountMax +.It SessionType +.It SetEnv +.It StdinNull +.It StreamLocalBindMask +.It StreamLocalBindUnlink +.It StrictHostKeyChecking +.It TCPKeepAlive +.It Tunnel +.It TunnelDevice +.It UpdateHostKeys +.It User +.It UserKnownHostsFile +.It VerifyHostKeyDNS +.It VisualHostKey +.It XAuthLocation +.El +.Pp +.It Fl p Ar port +Port to connect to on the remote host. +This can be specified on a +per-host basis in the configuration file. +.Pp +.It Fl Q Ar query_option +Queries for the algorithms supported by one of the following features: +.Ar cipher +(supported symmetric ciphers), +.Ar cipher-auth +(supported symmetric ciphers that support authenticated encryption), +.Ar help +(supported query terms for use with the +.Fl Q +flag), +.Ar mac +(supported message integrity codes), +.Ar kex +(key exchange algorithms), +.Ar key +(key types), +.Ar key-cert +(certificate key types), +.Ar key-plain +(non-certificate key types), +.Ar key-sig +(all key types and signature algorithms), +.Ar protocol-version +(supported SSH protocol versions), and +.Ar sig +(supported signature algorithms). +Alternatively, any keyword from +.Xr ssh_config 5 +or +.Xr sshd_config 5 +that takes an algorithm list may be used as an alias for the corresponding +query_option. +.Pp +.It Fl q +Quiet mode. +Causes most warning and diagnostic messages to be suppressed. +.Pp +.It Fl R Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port : host : hostport +.Sm on +.Xc +.It Fl R Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port : local_socket +.Sm on +.Xc +.It Fl R Xo +.Sm off +.Ar remote_socket : host : hostport +.Sm on +.Xc +.It Fl R Xo +.Sm off +.Ar remote_socket : local_socket +.Sm on +.Xc +.It Fl R Xo +.Sm off +.Oo Ar bind_address : Oc +.Ar port +.Sm on +.Xc +Specifies that connections to the given TCP port or Unix socket on the remote +(server) host are to be forwarded to the local side. +.Pp +This works by allocating a socket to listen to either a TCP +.Ar port +or to a Unix socket on the remote side. +Whenever a connection is made to this port or Unix socket, the +connection is forwarded over the secure channel, and a connection +is made from the local machine to either an explicit destination specified by +.Ar host +port +.Ar hostport , +or +.Ar local_socket , +or, if no explicit destination was specified, +.Nm +will act as a SOCKS 4/5 proxy and forward connections to the destinations +requested by the remote SOCKS client. +.Pp +Port forwardings can also be specified in the configuration file. +Privileged ports can be forwarded only when +logging in as root on the remote machine. +IPv6 addresses can be specified by enclosing the address in square brackets. +.Pp +By default, TCP listening sockets on the server will be bound to the loopback +interface only. +This may be overridden by specifying a +.Ar bind_address . +An empty +.Ar bind_address , +or the address +.Ql * , +indicates that the remote socket should listen on all interfaces. +Specifying a remote +.Ar bind_address +will only succeed if the server's +.Cm GatewayPorts +option is enabled (see +.Xr sshd_config 5 ) . +.Pp +If the +.Ar port +argument is +.Ql 0 , +the listen port will be dynamically allocated on the server and reported +to the client at run time. +When used together with +.Ic -O forward , +the allocated port will be printed to the standard output. +.Pp +.It Fl S Ar ctl_path +Specifies the location of a control socket for connection sharing, +or the string +.Dq none +to disable connection sharing. +Refer to the description of +.Cm ControlPath +and +.Cm ControlMaster +in +.Xr ssh_config 5 +for details. +.Pp +.It Fl s +May be used to request invocation of a subsystem on the remote system. +Subsystems facilitate the use of SSH +as a secure transport for other applications (e.g.\& +.Xr sftp 1 ) . +The subsystem is specified as the remote command. +Refer to the description of +.Cm SessionType +in +.Xr ssh_config 5 +for details. +.Pp +.It Fl T +Disable pseudo-terminal allocation. +.Pp +.It Fl t +Force pseudo-terminal allocation. +This can be used to execute arbitrary +screen-based programs on a remote machine, which can be very useful, +e.g. when implementing menu services. +Multiple +.Fl t +options force tty allocation, even if +.Nm +has no local tty. +.Pp +.It Fl V +Display the version number and exit. +.Pp +.It Fl v +Verbose mode. +Causes +.Nm +to print debugging messages about its progress. +This is helpful in +debugging connection, authentication, and configuration problems. +Multiple +.Fl v +options increase the verbosity. +The maximum is 3. +.Pp +.It Fl W Ar host : Ns Ar port +Requests that standard input and output on the client be forwarded to +.Ar host +on +.Ar port +over the secure channel. +Implies +.Fl N , +.Fl T , +.Cm ExitOnForwardFailure +and +.Cm ClearAllForwardings , +though these can be overridden in the configuration file or using +.Fl o +command line options. +.Pp +.It Fl w Xo +.Ar local_tun Ns Op : Ns Ar remote_tun +.Xc +Requests +tunnel +device forwarding with the specified +.Xr tun 4 +devices between the client +.Pq Ar local_tun +and the server +.Pq Ar remote_tun . +.Pp +The devices may be specified by numerical ID or the keyword +.Dq any , +which uses the next available tunnel device. +If +.Ar remote_tun +is not specified, it defaults to +.Dq any . +See also the +.Cm Tunnel +and +.Cm TunnelDevice +directives in +.Xr ssh_config 5 . +.Pp +If the +.Cm Tunnel +directive is unset, it will be set to the default tunnel mode, which is +.Dq point-to-point . +If a different +.Cm Tunnel +forwarding mode it desired, then it should be specified before +.Fl w . +.Pp +.It Fl X +Enables X11 forwarding. +This can also be specified on a per-host basis in a configuration file. +.Pp +X11 forwarding should be enabled with caution. +Users with the ability to bypass file permissions on the remote host +(for the user's X authorization database) +can access the local X11 display through the forwarded connection. +An attacker may then be able to perform activities such as keystroke monitoring. +.Pp +For this reason, X11 forwarding is subjected to X11 SECURITY extension +restrictions by default. +Refer to the +.Nm +.Fl Y +option and the +.Cm ForwardX11Trusted +directive in +.Xr ssh_config 5 +for more information. +.Pp +.It Fl x +Disables X11 forwarding. +.Pp +.It Fl Y +Enables trusted X11 forwarding. +Trusted X11 forwardings are not subjected to the X11 SECURITY extension +controls. +.Pp +.It Fl y +Send log information using the +.Xr syslog 3 +system module. +By default this information is sent to stderr. +.El +.Pp +.Nm +may additionally obtain configuration data from +a per-user configuration file and a system-wide configuration file. +The file format and configuration options are described in +.Xr ssh_config 5 . +.Sh AUTHENTICATION +The OpenSSH SSH client supports SSH protocol 2. +.Pp +The methods available for authentication are: +GSSAPI-based authentication, +host-based authentication, +public key authentication, +keyboard-interactive authentication, +and password authentication. +Authentication methods are tried in the order specified above, +though +.Cm PreferredAuthentications +can be used to change the default order. +.Pp +Host-based authentication works as follows: +If the machine the user logs in from is listed in +.Pa /etc/hosts.equiv +or +.Pa /etc/shosts.equiv +on the remote machine, the user is non-root and the user names are +the same on both sides, or if the files +.Pa ~/.rhosts +or +.Pa ~/.shosts +exist in the user's home directory on the +remote machine and contain a line containing the name of the client +machine and the name of the user on that machine, the user is +considered for login. +Additionally, the server +.Em must +be able to verify the client's +host key (see the description of +.Pa /etc/ssh/ssh_known_hosts +and +.Pa ~/.ssh/known_hosts , +below) +for login to be permitted. +This authentication method closes security holes due to IP +spoofing, DNS spoofing, and routing spoofing. +[Note to the administrator: +.Pa /etc/hosts.equiv , +.Pa ~/.rhosts , +and the rlogin/rsh protocol in general, are inherently insecure and should be +disabled if security is desired.] +.Pp +Public key authentication works as follows: +The scheme is based on public-key cryptography, +using cryptosystems +where encryption and decryption are done using separate keys, +and it is unfeasible to derive the decryption key from the encryption key. +The idea is that each user creates a public/private +key pair for authentication purposes. +The server knows the public key, and only the user knows the private key. +.Nm +implements public key authentication protocol automatically, +using one of the DSA, ECDSA, Ed25519 or RSA algorithms. +The HISTORY section of +.Xr ssl 8 +contains a brief discussion of the DSA and RSA algorithms. +.Pp +The file +.Pa ~/.ssh/authorized_keys +lists the public keys that are permitted for logging in. +When the user logs in, the +.Nm +program tells the server which key pair it would like to use for +authentication. +The client proves that it has access to the private key +and the server checks that the corresponding public key +is authorized to accept the account. +.Pp +The server may inform the client of errors that prevented public key +authentication from succeeding after authentication completes using a +different method. +These may be viewed by increasing the +.Cm LogLevel +to +.Cm DEBUG +or higher (e.g. by using the +.Fl v +flag). +.Pp +The user creates their key pair by running +.Xr ssh-keygen 1 . +This stores the private key in +.Pa ~/.ssh/id_dsa +(DSA), +.Pa ~/.ssh/id_ecdsa +(ECDSA), +.Pa ~/.ssh/id_ecdsa_sk +(authenticator-hosted ECDSA), +.Pa ~/.ssh/id_ed25519 +(Ed25519), +.Pa ~/.ssh/id_ed25519_sk +(authenticator-hosted Ed25519), +or +.Pa ~/.ssh/id_rsa +(RSA) +and stores the public key in +.Pa ~/.ssh/id_dsa.pub +(DSA), +.Pa ~/.ssh/id_ecdsa.pub +(ECDSA), +.Pa ~/.ssh/id_ecdsa_sk.pub +(authenticator-hosted ECDSA), +.Pa ~/.ssh/id_ed25519.pub +(Ed25519), +.Pa ~/.ssh/id_ed25519_sk.pub +(authenticator-hosted Ed25519), +or +.Pa ~/.ssh/id_rsa.pub +(RSA) +in the user's home directory. +The user should then copy the public key +to +.Pa ~/.ssh/authorized_keys +in their home directory on the remote machine. +The +.Pa authorized_keys +file corresponds to the conventional +.Pa ~/.rhosts +file, and has one key +per line, though the lines can be very long. +After this, the user can log in without giving the password. +.Pp +A variation on public key authentication +is available in the form of certificate authentication: +instead of a set of public/private keys, +signed certificates are used. +This has the advantage that a single trusted certification authority +can be used in place of many public/private keys. +See the CERTIFICATES section of +.Xr ssh-keygen 1 +for more information. +.Pp +The most convenient way to use public key or certificate authentication +may be with an authentication agent. +See +.Xr ssh-agent 1 +and (optionally) the +.Cm AddKeysToAgent +directive in +.Xr ssh_config 5 +for more information. +.Pp +Keyboard-interactive authentication works as follows: +The server sends an arbitrary +.Qq challenge +text and prompts for a response, possibly multiple times. +Examples of keyboard-interactive authentication include +.Bx +Authentication (see +.Xr login.conf 5 ) +and PAM (some +.Pf non- Ox +systems). +.Pp +Finally, if other authentication methods fail, +.Nm +prompts the user for a password. +The password is sent to the remote +host for checking; however, since all communications are encrypted, +the password cannot be seen by someone listening on the network. +.Pp +.Nm +automatically maintains and checks a database containing +identification for all hosts it has ever been used with. +Host keys are stored in +.Pa ~/.ssh/known_hosts +in the user's home directory. +Additionally, the file +.Pa /etc/ssh/ssh_known_hosts +is automatically checked for known hosts. +Any new hosts are automatically added to the user's file. +If a host's identification ever changes, +.Nm +warns about this and disables password authentication to prevent +server spoofing or man-in-the-middle attacks, +which could otherwise be used to circumvent the encryption. +The +.Cm StrictHostKeyChecking +option can be used to control logins to machines whose +host key is not known or has changed. +.Pp +When the user's identity has been accepted by the server, the server +either executes the given command in a non-interactive session or, +if no command has been specified, logs into the machine and gives +the user a normal shell as an interactive session. +All communication with +the remote command or shell will be automatically encrypted. +.Pp +If an interactive session is requested, +.Nm +by default will only request a pseudo-terminal (pty) for interactive +sessions when the client has one. +The flags +.Fl T +and +.Fl t +can be used to override this behaviour. +.Pp +If a pseudo-terminal has been allocated, the +user may use the escape characters noted below. +.Pp +If no pseudo-terminal has been allocated, +the session is transparent and can be used to reliably transfer binary data. +On most systems, setting the escape character to +.Dq none +will also make the session transparent even if a tty is used. +.Pp +The session terminates when the command or shell on the remote +machine exits and all X11 and TCP connections have been closed. +.Sh ESCAPE CHARACTERS +When a pseudo-terminal has been requested, +.Nm +supports a number of functions through the use of an escape character. +.Pp +A single tilde character can be sent as +.Ic ~~ +or by following the tilde by a character other than those described below. +The escape character must always follow a newline to be interpreted as +special. +The escape character can be changed in configuration files using the +.Cm EscapeChar +configuration directive or on the command line by the +.Fl e +option. +.Pp +The supported escapes (assuming the default +.Ql ~ ) +are: +.Bl -tag -width Ds +.It Cm ~. +Disconnect. +.It Cm ~^Z +Background +.Nm . +.It Cm ~# +List forwarded connections. +.It Cm ~& +Background +.Nm +at logout when waiting for forwarded connection / X11 sessions to terminate. +.It Cm ~? +Display a list of escape characters. +.It Cm ~B +Send a BREAK to the remote system +(only useful if the peer supports it). +.It Cm ~C +Open command line. +Currently this allows the addition of port forwardings using the +.Fl L , +.Fl R +and +.Fl D +options (see above). +It also allows the cancellation of existing port-forwardings +with +.Sm off +.Fl KL Oo Ar bind_address : Oc Ar port +.Sm on +for local, +.Sm off +.Fl KR Oo Ar bind_address : Oc Ar port +.Sm on +for remote and +.Sm off +.Fl KD Oo Ar bind_address : Oc Ar port +.Sm on +for dynamic port-forwardings. +.Ic !\& Ns Ar command +allows the user to execute a local command if the +.Ic PermitLocalCommand +option is enabled in +.Xr ssh_config 5 . +Basic help is available, using the +.Fl h +option. +.It Cm ~R +Request rekeying of the connection +(only useful if the peer supports it). +.It Cm ~V +Decrease the verbosity +.Pq Ic LogLevel +when errors are being written to stderr. +.It Cm ~v +Increase the verbosity +.Pq Ic LogLevel +when errors are being written to stderr. +.El +.Sh TCP FORWARDING +Forwarding of arbitrary TCP connections over a secure channel +can be specified either on the command line or in a configuration file. +One possible application of TCP forwarding is a secure connection to a +mail server; another is going through firewalls. +.Pp +In the example below, we look at encrypting communication for an IRC client, +even though the IRC server it connects to does not directly +support encrypted communication. +This works as follows: +the user connects to the remote host using +.Nm , +specifying the ports to be used to forward the connection. +After that it is possible to start the program locally, +and +.Nm +will encrypt and forward the connection to the remote server. +.Pp +The following example tunnels an IRC session from the client +to an IRC server at +.Dq server.example.com , +joining channel +.Dq #users , +nickname +.Dq pinky , +using the standard IRC port, 6667: +.Bd -literal -offset 4n +$ ssh -f -L 6667:localhost:6667 server.example.com sleep 10 +$ irc -c '#users' pinky IRC/127.0.0.1 +.Ed +.Pp +The +.Fl f +option backgrounds +.Nm +and the remote command +.Dq sleep 10 +is specified to allow an amount of time +(10 seconds, in the example) +to start the program which is going to use the tunnel. +If no connections are made within the time specified, +.Nm +will exit. +.Sh X11 FORWARDING +If the +.Cm ForwardX11 +variable is set to +.Dq yes +(or see the description of the +.Fl X , +.Fl x , +and +.Fl Y +options above) +and the user is using X11 (the +.Ev DISPLAY +environment variable is set), the connection to the X11 display is +automatically forwarded to the remote side in such a way that any X11 +programs started from the shell (or command) will go through the +encrypted channel, and the connection to the real X server will be made +from the local machine. +The user should not manually set +.Ev DISPLAY . +Forwarding of X11 connections can be +configured on the command line or in configuration files. +.Pp +The +.Ev DISPLAY +value set by +.Nm +will point to the server machine, but with a display number greater than zero. +This is normal, and happens because +.Nm +creates a +.Dq proxy +X server on the server machine for forwarding the +connections over the encrypted channel. +.Pp +.Nm +will also automatically set up Xauthority data on the server machine. +For this purpose, it will generate a random authorization cookie, +store it in Xauthority on the server, and verify that any forwarded +connections carry this cookie and replace it by the real cookie when +the connection is opened. +The real authentication cookie is never +sent to the server machine (and no cookies are sent in the plain). +.Pp +If the +.Cm ForwardAgent +variable is set to +.Dq yes +(or see the description of the +.Fl A +and +.Fl a +options above) and +the user is using an authentication agent, the connection to the agent +is automatically forwarded to the remote side. +.Sh VERIFYING HOST KEYS +When connecting to a server for the first time, +a fingerprint of the server's public key is presented to the user +(unless the option +.Cm StrictHostKeyChecking +has been disabled). +Fingerprints can be determined using +.Xr ssh-keygen 1 : +.Pp +.Dl $ ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key +.Pp +If the fingerprint is already known, it can be matched +and the key can be accepted or rejected. +If only legacy (MD5) fingerprints for the server are available, the +.Xr ssh-keygen 1 +.Fl E +option may be used to downgrade the fingerprint algorithm to match. +.Pp +Because of the difficulty of comparing host keys +just by looking at fingerprint strings, +there is also support to compare host keys visually, +using +.Em random art . +By setting the +.Cm VisualHostKey +option to +.Dq yes , +a small ASCII graphic gets displayed on every login to a server, no matter +if the session itself is interactive or not. +By learning the pattern a known server produces, a user can easily +find out that the host key has changed when a completely different pattern +is displayed. +Because these patterns are not unambiguous however, a pattern that looks +similar to the pattern remembered only gives a good probability that the +host key is the same, not guaranteed proof. +.Pp +To get a listing of the fingerprints along with their random art for +all known hosts, the following command line can be used: +.Pp +.Dl $ ssh-keygen -lv -f ~/.ssh/known_hosts +.Pp +If the fingerprint is unknown, +an alternative method of verification is available: +SSH fingerprints verified by DNS. +An additional resource record (RR), +SSHFP, +is added to a zonefile +and the connecting client is able to match the fingerprint +with that of the key presented. +.Pp +In this example, we are connecting a client to a server, +.Dq host.example.com . +The SSHFP resource records should first be added to the zonefile for +host.example.com: +.Bd -literal -offset indent +$ ssh-keygen -r host.example.com. +.Ed +.Pp +The output lines will have to be added to the zonefile. +To check that the zone is answering fingerprint queries: +.Pp +.Dl $ dig -t SSHFP host.example.com +.Pp +Finally the client connects: +.Bd -literal -offset indent +$ ssh -o "VerifyHostKeyDNS ask" host.example.com +[...] +Matching host key fingerprint found in DNS. +Are you sure you want to continue connecting (yes/no)? +.Ed +.Pp +See the +.Cm VerifyHostKeyDNS +option in +.Xr ssh_config 5 +for more information. +.Sh SSH-BASED VIRTUAL PRIVATE NETWORKS +.Nm +contains support for Virtual Private Network (VPN) tunnelling +using the +.Xr tun 4 +network pseudo-device, +allowing two networks to be joined securely. +The +.Xr sshd_config 5 +configuration option +.Cm PermitTunnel +controls whether the server supports this, +and at what level (layer 2 or 3 traffic). +.Pp +The following example would connect client network 10.0.50.0/24 +with remote network 10.0.99.0/24 using a point-to-point connection +from 10.1.1.1 to 10.1.1.2, +provided that the SSH server running on the gateway to the remote network, +at 192.168.1.15, allows it. +.Pp +On the client: +.Bd -literal -offset indent +# ssh -f -w 0:1 192.168.1.15 true +# ifconfig tun0 10.1.1.1 10.1.1.2 netmask 255.255.255.252 +# route add 10.0.99.0/24 10.1.1.2 +.Ed +.Pp +On the server: +.Bd -literal -offset indent +# ifconfig tun1 10.1.1.2 10.1.1.1 netmask 255.255.255.252 +# route add 10.0.50.0/24 10.1.1.1 +.Ed +.Pp +Client access may be more finely tuned via the +.Pa /root/.ssh/authorized_keys +file (see below) and the +.Cm PermitRootLogin +server option. +The following entry would permit connections on +.Xr tun 4 +device 1 from user +.Dq jane +and on tun device 2 from user +.Dq john , +if +.Cm PermitRootLogin +is set to +.Dq forced-commands-only : +.Bd -literal -offset 2n +tunnel="1",command="sh /etc/netstart tun1" ssh-rsa ... jane +tunnel="2",command="sh /etc/netstart tun2" ssh-rsa ... john +.Ed +.Pp +Since an SSH-based setup entails a fair amount of overhead, +it may be more suited to temporary setups, +such as for wireless VPNs. +More permanent VPNs are better provided by tools such as +.Xr ipsecctl 8 +and +.Xr isakmpd 8 . +.Sh ENVIRONMENT +.Nm +will normally set the following environment variables: +.Bl -tag -width "SSH_ORIGINAL_COMMAND" +.It Ev DISPLAY +The +.Ev DISPLAY +variable indicates the location of the X11 server. +It is automatically set by +.Nm +to point to a value of the form +.Dq hostname:n , +where +.Dq hostname +indicates the host where the shell runs, and +.Sq n +is an integer \*(Ge 1. +.Nm +uses this special value to forward X11 connections over the secure +channel. +The user should normally not set +.Ev DISPLAY +explicitly, as that +will render the X11 connection insecure (and will require the user to +manually copy any required authorization cookies). +.It Ev HOME +Set to the path of the user's home directory. +.It Ev LOGNAME +Synonym for +.Ev USER ; +set for compatibility with systems that use this variable. +.It Ev MAIL +Set to the path of the user's mailbox. +.It Ev PATH +Set to the default +.Ev PATH , +as specified when compiling +.Nm . +.It Ev SSH_ASKPASS +If +.Nm +needs a passphrase, it will read the passphrase from the current +terminal if it was run from a terminal. +If +.Nm +does not have a terminal associated with it but +.Ev DISPLAY +and +.Ev SSH_ASKPASS +are set, it will execute the program specified by +.Ev SSH_ASKPASS +and open an X11 window to read the passphrase. +This is particularly useful when calling +.Nm +from a +.Pa .xsession +or related script. +(Note that on some machines it +may be necessary to redirect the input from +.Pa /dev/null +to make this work.) +.It Ev SSH_ASKPASS_REQUIRE +Allows further control over the use of an askpass program. +If this variable is set to +.Dq never +then +.Nm +will never attempt to use one. +If it is set to +.Dq prefer , +then +.Nm +will prefer to use the askpass program instead of the TTY when requesting +passwords. +Finally, if the variable is set to +.Dq force , +then the askpass program will be used for all passphrase input regardless +of whether +.Ev DISPLAY +is set. +.It Ev SSH_AUTH_SOCK +Identifies the path of a +.Ux Ns -domain +socket used to communicate with the agent. +.It Ev SSH_CONNECTION +Identifies the client and server ends of the connection. +The variable contains +four space-separated values: client IP address, client port number, +server IP address, and server port number. +.It Ev SSH_ORIGINAL_COMMAND +This variable contains the original command line if a forced command +is executed. +It can be used to extract the original arguments. +.It Ev SSH_TTY +This is set to the name of the tty (path to the device) associated +with the current shell or command. +If the current session has no tty, +this variable is not set. +.It Ev SSH_TUNNEL +Optionally set by +.Xr sshd 8 +to contain the interface names assigned if tunnel forwarding was +requested by the client. +.It Ev SSH_USER_AUTH +Optionally set by +.Xr sshd 8 , +this variable may contain a pathname to a file that lists the authentication +methods successfully used when the session was established, including any +public keys that were used. +.It Ev TZ +This variable is set to indicate the present time zone if it +was set when the daemon was started (i.e. the daemon passes the value +on to new connections). +.It Ev USER +Set to the name of the user logging in. +.El +.Pp +Additionally, +.Nm +reads +.Pa ~/.ssh/environment , +and adds lines of the format +.Dq VARNAME=value +to the environment if the file exists and users are allowed to +change their environment. +For more information, see the +.Cm PermitUserEnvironment +option in +.Xr sshd_config 5 . +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa ~/.rhosts +This file is used for host-based authentication (see above). +On some machines this file may need to be +world-readable if the user's home directory is on an NFS partition, +because +.Xr sshd 8 +reads it as root. +Additionally, this file must be owned by the user, +and must not have write permissions for anyone else. +The recommended +permission for most machines is read/write for the user, and not +accessible by others. +.Pp +.It Pa ~/.shosts +This file is used in exactly the same way as +.Pa .rhosts , +but allows host-based authentication without permitting login with +rlogin/rsh. +.Pp +.It Pa ~/.ssh/ +This directory is the default location for all user-specific configuration +and authentication information. +There is no general requirement to keep the entire contents of this directory +secret, but the recommended permissions are read/write/execute for the user, +and not accessible by others. +.Pp +.It Pa ~/.ssh/authorized_keys +Lists the public keys (DSA, ECDSA, Ed25519, RSA) +that can be used for logging in as this user. +The format of this file is described in the +.Xr sshd 8 +manual page. +This file is not highly sensitive, but the recommended +permissions are read/write for the user, and not accessible by others. +.Pp +.It Pa ~/.ssh/config +This is the per-user configuration file. +The file format and configuration options are described in +.Xr ssh_config 5 . +Because of the potential for abuse, this file must have strict permissions: +read/write for the user, and not writable by others. +.Pp +.It Pa ~/.ssh/environment +Contains additional definitions for environment variables; see +.Sx ENVIRONMENT , +above. +.Pp +.It Pa ~/.ssh/id_dsa +.It Pa ~/.ssh/id_ecdsa +.It Pa ~/.ssh/id_ecdsa_sk +.It Pa ~/.ssh/id_ed25519 +.It Pa ~/.ssh/id_ed25519_sk +.It Pa ~/.ssh/id_rsa +Contains the private key for authentication. +These files +contain sensitive data and should be readable by the user but not +accessible by others (read/write/execute). +.Nm +will simply ignore a private key file if it is accessible by others. +It is possible to specify a passphrase when +generating the key which will be used to encrypt the +sensitive part of this file using AES-128. +.Pp +.It Pa ~/.ssh/id_dsa.pub +.It Pa ~/.ssh/id_ecdsa.pub +.It Pa ~/.ssh/id_ecdsa_sk.pub +.It Pa ~/.ssh/id_ed25519.pub +.It Pa ~/.ssh/id_ed25519_sk.pub +.It Pa ~/.ssh/id_rsa.pub +Contains the public key for authentication. +These files are not +sensitive and can (but need not) be readable by anyone. +.Pp +.It Pa ~/.ssh/known_hosts +Contains a list of host keys for all hosts the user has logged into +that are not already in the systemwide list of known host keys. +See +.Xr sshd 8 +for further details of the format of this file. +.Pp +.It Pa ~/.ssh/rc +Commands in this file are executed by +.Nm +when the user logs in, just before the user's shell (or command) is +started. +See the +.Xr sshd 8 +manual page for more information. +.Pp +.It Pa /etc/hosts.equiv +This file is for host-based authentication (see above). +It should only be writable by root. +.Pp +.It Pa /etc/shosts.equiv +This file is used in exactly the same way as +.Pa hosts.equiv , +but allows host-based authentication without permitting login with +rlogin/rsh. +.Pp +.It Pa /etc/ssh/ssh_config +Systemwide configuration file. +The file format and configuration options are described in +.Xr ssh_config 5 . +.Pp +.It Pa /etc/ssh/ssh_host_key +.It Pa /etc/ssh/ssh_host_dsa_key +.It Pa /etc/ssh/ssh_host_ecdsa_key +.It Pa /etc/ssh/ssh_host_ed25519_key +.It Pa /etc/ssh/ssh_host_rsa_key +These files contain the private parts of the host keys +and are used for host-based authentication. +.Pp +.It Pa /etc/ssh/ssh_known_hosts +Systemwide list of known host keys. +This file should be prepared by the +system administrator to contain the public host keys of all machines in the +organization. +It should be world-readable. +See +.Xr sshd 8 +for further details of the format of this file. +.Pp +.It Pa /etc/ssh/sshrc +Commands in this file are executed by +.Nm +when the user logs in, just before the user's shell (or command) is started. +See the +.Xr sshd 8 +manual page for more information. +.El +.Sh EXIT STATUS +.Nm +exits with the exit status of the remote command or with 255 +if an error occurred. +.Sh SEE ALSO +.Xr scp 1 , +.Xr sftp 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr ssh-keyscan 1 , +.Xr tun 4 , +.Xr ssh_config 5 , +.Xr ssh-keysign 8 , +.Xr sshd 8 +.Sh STANDARDS +.Rs +.%A S. Lehtinen +.%A C. Lonvick +.%D January 2006 +.%R RFC 4250 +.%T The Secure Shell (SSH) Protocol Assigned Numbers +.Re +.Pp +.Rs +.%A T. Ylonen +.%A C. Lonvick +.%D January 2006 +.%R RFC 4251 +.%T The Secure Shell (SSH) Protocol Architecture +.Re +.Pp +.Rs +.%A T. Ylonen +.%A C. Lonvick +.%D January 2006 +.%R RFC 4252 +.%T The Secure Shell (SSH) Authentication Protocol +.Re +.Pp +.Rs +.%A T. Ylonen +.%A C. Lonvick +.%D January 2006 +.%R RFC 4253 +.%T The Secure Shell (SSH) Transport Layer Protocol +.Re +.Pp +.Rs +.%A T. Ylonen +.%A C. Lonvick +.%D January 2006 +.%R RFC 4254 +.%T The Secure Shell (SSH) Connection Protocol +.Re +.Pp +.Rs +.%A J. Schlyter +.%A W. Griffin +.%D January 2006 +.%R RFC 4255 +.%T Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints +.Re +.Pp +.Rs +.%A F. Cusack +.%A M. Forssen +.%D January 2006 +.%R RFC 4256 +.%T Generic Message Exchange Authentication for the Secure Shell Protocol (SSH) +.Re +.Pp +.Rs +.%A J. Galbraith +.%A P. Remaker +.%D January 2006 +.%R RFC 4335 +.%T The Secure Shell (SSH) Session Channel Break Extension +.Re +.Pp +.Rs +.%A M. Bellare +.%A T. Kohno +.%A C. Namprempre +.%D January 2006 +.%R RFC 4344 +.%T The Secure Shell (SSH) Transport Layer Encryption Modes +.Re +.Pp +.Rs +.%A B. Harris +.%D January 2006 +.%R RFC 4345 +.%T Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol +.Re +.Pp +.Rs +.%A M. Friedl +.%A N. Provos +.%A W. Simpson +.%D March 2006 +.%R RFC 4419 +.%T Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol +.Re +.Pp +.Rs +.%A J. Galbraith +.%A R. Thayer +.%D November 2006 +.%R RFC 4716 +.%T The Secure Shell (SSH) Public Key File Format +.Re +.Pp +.Rs +.%A D. Stebila +.%A J. Green +.%D December 2009 +.%R RFC 5656 +.%T Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer +.Re +.Pp +.Rs +.%A A. Perrig +.%A D. Song +.%D 1999 +.%O International Workshop on Cryptographic Techniques and E-Commerce (CrypTEC '99) +.%T Hash Visualization: a New Technique to improve Real-World Security +.Re +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. diff --git a/aosp/external/openssh/ssh.c b/aosp/external/openssh/ssh.c new file mode 100644 index 0000000000000000000000000000000000000000..89ca1940c1ff6b8e14cd2269ddb704a73e98fa00 --- /dev/null +++ b/aosp/external/openssh/ssh.c @@ -0,0 +1,2366 @@ +/* $OpenBSD: ssh.c,v 1.574 2022/03/30 04:33:09 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Ssh client program. This program can be used to log into a remote machine. + * The software supports strong authentication, encryption, and forwarding + * of X11, TCP/IP, and authentication connections. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 1999 Niels Provos. All rights reserved. + * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. + * + * Modified to work with SSLeay by Niels Provos + * in Canada (German citizen). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#endif +#include "openbsd-compat/openssl-compat.h" +#include "openbsd-compat/sys-queue.h" + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "canohost.h" +#include "compat.h" +#include "cipher.h" +#include "packet.h" +#include "sshbuf.h" +#include "channels.h" +#include "sshkey.h" +#include "authfd.h" +#include "authfile.h" +#include "pathnames.h" +#include "dispatch.h" +#include "clientloop.h" +#include "log.h" +#include "misc.h" +#include "readconf.h" +#include "sshconnect.h" +#include "kex.h" +#include "mac.h" +#include "sshpty.h" +#include "match.h" +#include "msg.h" +#include "version.h" +#include "ssherr.h" +#include "myproposal.h" +#include "utf8.h" + +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" +#endif + +extern char *__progname; + +/* Saves a copy of argv for setproctitle emulation */ +#ifndef HAVE_SETPROCTITLE +static char **saved_av; +#endif + +/* Flag indicating whether debug mode is on. May be set on the command line. */ +int debug_flag = 0; + +/* Flag indicating whether a tty should be requested */ +int tty_flag = 0; + +/* + * Flag indicating that the current process should be backgrounded and + * a new mux-client launched in the foreground for ControlPersist. + */ +int need_controlpersist_detach = 0; + +/* Copies of flags for ControlPersist foreground mux-client */ +int ostdin_null_flag, osession_type, otty_flag, orequest_tty; + +/* + * General data structure for command line options and options configurable + * in configuration files. See readconf.h. + */ +Options options; + +/* optional user configfile */ +char *config = NULL; + +/* + * Name of the host we are connecting to. This is the name given on the + * command line, or the Hostname specified for the user-supplied name in a + * configuration file. + */ +char *host; + +/* + * A config can specify a path to forward, overriding SSH_AUTH_SOCK. If this is + * not NULL, forward the socket at this path instead. + */ +char *forward_agent_sock_path = NULL; + +/* socket address the host resolves to */ +struct sockaddr_storage hostaddr; + +/* Private host keys. */ +Sensitive sensitive_data; + +/* command to be executed */ +struct sshbuf *command; + +/* # of replies received for global requests */ +static int forward_confirms_pending = -1; + +/* mux.c */ +extern int muxserver_sock; +extern u_int muxclient_command; + +/* Prints a help message to the user. This function never returns. */ + +static void +usage(void) +{ + fprintf(stderr, +"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]\n" +" [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]\n" +" [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]\n" +" [-i identity_file] [-J [user@]host[:port]] [-L address]\n" +" [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n" +" [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n" +" [-w local_tun[:remote_tun]] destination [command [argument ...]]\n" + ); + exit(255); +} + +static int ssh_session2(struct ssh *, const struct ssh_conn_info *); +static void load_public_identity_files(const struct ssh_conn_info *); +static void main_sigchld_handler(int); + +/* ~/ expand a list of paths. NB. assumes path[n] is heap-allocated. */ +static void +tilde_expand_paths(char **paths, u_int num_paths) +{ + u_int i; + char *cp; + + for (i = 0; i < num_paths; i++) { + cp = tilde_expand_filename(paths[i], getuid()); + free(paths[i]); + paths[i] = cp; + } +} + +/* + * Expands the set of percent_expand options used by the majority of keywords + * in the client that support percent expansion. + * Caller must free returned string. + */ +static char * +default_client_percent_expand(const char *str, + const struct ssh_conn_info *cinfo) +{ + return percent_expand(str, + DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), + (char *)NULL); +} + +/* + * Expands the set of percent_expand options used by the majority of keywords + * AND perform environment variable substitution. + * Caller must free returned string. + */ +static char * +default_client_percent_dollar_expand(const char *str, + const struct ssh_conn_info *cinfo) +{ + char *ret; + + ret = percent_dollar_expand(str, + DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), + (char *)NULL); + if (ret == NULL) + fatal("invalid environment variable expansion"); + return ret; +} + +/* + * Attempt to resolve a host name / port to a set of addresses and + * optionally return any CNAMEs encountered along the way. + * Returns NULL on failure. + * NB. this function must operate with a options having undefined members. + */ +static struct addrinfo * +resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) +{ + char strport[NI_MAXSERV]; + struct addrinfo hints, *res; + int gaierr; + LogLevel loglevel = SYSLOG_LEVEL_DEBUG1; + + if (port <= 0) + port = default_ssh_port(); + if (cname != NULL) + *cname = '\0'; + debug3_f("lookup %s:%d", name, port); + + snprintf(strport, sizeof strport, "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = options.address_family == -1 ? + AF_UNSPEC : options.address_family; + hints.ai_socktype = SOCK_STREAM; + if (cname != NULL) + hints.ai_flags = AI_CANONNAME; + if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { + if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA)) + loglevel = SYSLOG_LEVEL_ERROR; + do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s", + __progname, name, ssh_gai_strerror(gaierr)); + return NULL; + } + if (cname != NULL && res->ai_canonname != NULL) { + if (strlcpy(cname, res->ai_canonname, clen) >= clen) { + error_f("host \"%s\" cname \"%s\" too long (max %lu)", + name, res->ai_canonname, (u_long)clen); + if (clen > 0) + *cname = '\0'; + } + } + return res; +} + +/* Returns non-zero if name can only be an address and not a hostname */ +static int +is_addr_fast(const char *name) +{ + return (strchr(name, '%') != NULL || strchr(name, ':') != NULL || + strspn(name, "0123456789.") == strlen(name)); +} + +/* Returns non-zero if name represents a valid, single address */ +static int +is_addr(const char *name) +{ + char strport[NI_MAXSERV]; + struct addrinfo hints, *res; + + if (is_addr_fast(name)) + return 1; + + snprintf(strport, sizeof strport, "%u", default_ssh_port()); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = options.address_family == -1 ? + AF_UNSPEC : options.address_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; + if (getaddrinfo(name, strport, &hints, &res) != 0) + return 0; + if (res == NULL || res->ai_next != NULL) { + freeaddrinfo(res); + return 0; + } + freeaddrinfo(res); + return 1; +} + +/* + * Attempt to resolve a numeric host address / port to a single address. + * Returns a canonical address string. + * Returns NULL on failure. + * NB. this function must operate with a options having undefined members. + */ +static struct addrinfo * +resolve_addr(const char *name, int port, char *caddr, size_t clen) +{ + char addr[NI_MAXHOST], strport[NI_MAXSERV]; + struct addrinfo hints, *res; + int gaierr; + + if (port <= 0) + port = default_ssh_port(); + snprintf(strport, sizeof strport, "%u", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = options.address_family == -1 ? + AF_UNSPEC : options.address_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; + if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { + debug2_f("could not resolve name %.100s as address: %s", + name, ssh_gai_strerror(gaierr)); + return NULL; + } + if (res == NULL) { + debug_f("getaddrinfo %.100s returned no addresses", name); + return NULL; + } + if (res->ai_next != NULL) { + debug_f("getaddrinfo %.100s returned multiple addresses", name); + goto fail; + } + if ((gaierr = getnameinfo(res->ai_addr, res->ai_addrlen, + addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) { + debug_f("Could not format address for name %.100s: %s", + name, ssh_gai_strerror(gaierr)); + goto fail; + } + if (strlcpy(caddr, addr, clen) >= clen) { + error_f("host \"%s\" addr \"%s\" too long (max %lu)", + name, addr, (u_long)clen); + if (clen > 0) + *caddr = '\0'; + fail: + freeaddrinfo(res); + return NULL; + } + return res; +} + +/* + * Check whether the cname is a permitted replacement for the hostname + * and perform the replacement if it is. + * NB. this function must operate with a options having undefined members. + */ +static int +check_follow_cname(int direct, char **namep, const char *cname) +{ + int i; + struct allowed_cname *rule; + + if (*cname == '\0' || !config_has_permitted_cnames(&options) || + strcmp(*namep, cname) == 0) + return 0; + if (options.canonicalize_hostname == SSH_CANONICALISE_NO) + return 0; + /* + * Don't attempt to canonicalize names that will be interpreted by + * a proxy or jump host unless the user specifically requests so. + */ + if (!direct && + options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) + return 0; + debug3_f("check \"%s\" CNAME \"%s\"", *namep, cname); + for (i = 0; i < options.num_permitted_cnames; i++) { + rule = options.permitted_cnames + i; + if (match_pattern_list(*namep, rule->source_list, 1) != 1 || + match_pattern_list(cname, rule->target_list, 1) != 1) + continue; + verbose("Canonicalized DNS aliased hostname " + "\"%s\" => \"%s\"", *namep, cname); + free(*namep); + *namep = xstrdup(cname); + return 1; + } + return 0; +} + +/* + * Attempt to resolve the supplied hostname after applying the user's + * canonicalization rules. Returns the address list for the host or NULL + * if no name was found after canonicalization. + * NB. this function must operate with a options having undefined members. + */ +static struct addrinfo * +resolve_canonicalize(char **hostp, int port) +{ + int i, direct, ndots; + char *cp, *fullhost, newname[NI_MAXHOST]; + struct addrinfo *addrs; + + /* + * Attempt to canonicalise addresses, regardless of + * whether hostname canonicalisation was requested + */ + if ((addrs = resolve_addr(*hostp, port, + newname, sizeof(newname))) != NULL) { + debug2_f("hostname %.100s is address", *hostp); + if (strcasecmp(*hostp, newname) != 0) { + debug2_f("canonicalised address \"%s\" => \"%s\"", + *hostp, newname); + free(*hostp); + *hostp = xstrdup(newname); + } + return addrs; + } + + /* + * If this looks like an address but didn't parse as one, it might + * be an address with an invalid interface scope. Skip further + * attempts at canonicalisation. + */ + if (is_addr_fast(*hostp)) { + debug_f("hostname %.100s is an unrecognised address", *hostp); + return NULL; + } + + if (options.canonicalize_hostname == SSH_CANONICALISE_NO) + return NULL; + + /* + * Don't attempt to canonicalize names that will be interpreted by + * a proxy unless the user specifically requests so. + */ + direct = option_clear_or_none(options.proxy_command) && + options.jump_host == NULL; + if (!direct && + options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) + return NULL; + + /* If domain name is anchored, then resolve it now */ + if ((*hostp)[strlen(*hostp) - 1] == '.') { + debug3_f("name is fully qualified"); + fullhost = xstrdup(*hostp); + if ((addrs = resolve_host(fullhost, port, 0, + newname, sizeof(newname))) != NULL) + goto found; + free(fullhost); + goto notfound; + } + + /* Don't apply canonicalization to sufficiently-qualified hostnames */ + ndots = 0; + for (cp = *hostp; *cp != '\0'; cp++) { + if (*cp == '.') + ndots++; + } + if (ndots > options.canonicalize_max_dots) { + debug3_f("not canonicalizing hostname \"%s\" (max dots %d)", + *hostp, options.canonicalize_max_dots); + return NULL; + } + /* Attempt each supplied suffix */ + for (i = 0; i < options.num_canonical_domains; i++) { + if (strcasecmp(options.canonical_domains[i], "none") == 0) + break; + xasprintf(&fullhost, "%s.%s.", *hostp, + options.canonical_domains[i]); + debug3_f("attempting \"%s\" => \"%s\"", *hostp, fullhost); + if ((addrs = resolve_host(fullhost, port, 0, + newname, sizeof(newname))) == NULL) { + free(fullhost); + continue; + } + found: + /* Remove trailing '.' */ + fullhost[strlen(fullhost) - 1] = '\0'; + /* Follow CNAME if requested */ + if (!check_follow_cname(direct, &fullhost, newname)) { + debug("Canonicalized hostname \"%s\" => \"%s\"", + *hostp, fullhost); + } + free(*hostp); + *hostp = fullhost; + return addrs; + } + notfound: + if (!options.canonicalize_fallback_local) + fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); + debug2_f("host %s not found in any suffix", *hostp); + return NULL; +} + +/* + * Check the result of hostkey loading, ignoring some errors and + * fatal()ing for others. + */ +static void +check_load(int r, const char *path, const char *message) +{ + switch (r) { + case 0: + break; + case SSH_ERR_INTERNAL_ERROR: + case SSH_ERR_ALLOC_FAIL: + fatal_r(r, "load %s \"%s\"", message, path); + case SSH_ERR_SYSTEM_ERROR: + /* Ignore missing files */ + if (errno == ENOENT) + break; + /* FALLTHROUGH */ + default: + error_r(r, "load %s \"%s\"", message, path); + break; + } +} + +/* + * Read per-user configuration file. Ignore the system wide config + * file if the user specifies a config file on the command line. + */ +static void +process_config_files(const char *host_name, struct passwd *pw, int final_pass, + int *want_final_pass) +{ + char buf[PATH_MAX]; + int r; + + if (config != NULL) { + if (strcasecmp(config, "none") != 0 && + !read_config_file(config, pw, host, host_name, &options, + SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0), + want_final_pass)) + fatal("Can't open user config file %.100s: " + "%.100s", config, strerror(errno)); + } else { + r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, + _PATH_SSH_USER_CONFFILE); + if (r > 0 && (size_t)r < sizeof(buf)) + (void)read_config_file(buf, pw, host, host_name, + &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | + (final_pass ? SSHCONF_FINAL : 0), want_final_pass); + + /* Read systemwide configuration file after user config. */ + (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, + host, host_name, &options, + final_pass ? SSHCONF_FINAL : 0, want_final_pass); + } +} + +/* Rewrite the port number in an addrinfo list of addresses */ +static void +set_addrinfo_port(struct addrinfo *addrs, int port) +{ + struct addrinfo *addr; + + for (addr = addrs; addr != NULL; addr = addr->ai_next) { + switch (addr->ai_family) { + case AF_INET: + ((struct sockaddr_in *)addr->ai_addr)-> + sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)addr->ai_addr)-> + sin6_port = htons(port); + break; + } + } +} + +static void +ssh_conn_info_free(struct ssh_conn_info *cinfo) +{ + if (cinfo == NULL) + return; + free(cinfo->conn_hash_hex); + free(cinfo->shorthost); + free(cinfo->uidstr); + free(cinfo->keyalias); + free(cinfo->thishost); + free(cinfo->host_arg); + free(cinfo->portstr); + free(cinfo->remhost); + free(cinfo->remuser); + free(cinfo->homedir); + free(cinfo->locuser); + free(cinfo); +} + +/* + * Main program for the ssh client. + */ +int +main(int ac, char **av) +{ + struct ssh *ssh = NULL; + int i, r, opt, exit_status, use_syslog, direct, timeout_ms; + int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; + char *p, *cp, *line, *argv0, *logfile, *host_arg; + char cname[NI_MAXHOST], thishost[NI_MAXHOST]; + struct stat st; + struct passwd *pw; + extern int optind, optreset; + extern char *optarg; + struct Forward fwd; + struct addrinfo *addrs = NULL; + size_t n, len; + u_int j; + struct ssh_conn_info *cinfo = NULL; + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + /* + * Discard other fds that are hanging around. These can cause problem + * with backgrounded ssh processes started by ControlPersist. + */ + closefrom(STDERR_FILENO + 1); + + __progname = ssh_get_progname(av[0]); + +#ifndef HAVE_SETPROCTITLE + /* Prepare for later setproctitle emulation */ + /* Save argv so it isn't clobbered by setproctitle() emulation */ + saved_av = xcalloc(ac + 1, sizeof(*saved_av)); + for (i = 0; i < ac; i++) + saved_av[i] = xstrdup(av[i]); + saved_av[i] = NULL; + compat_init_setproctitle(ac, av); + av = saved_av; +#endif + + seed_rng(); + + /* Get user data. */ + pw = getpwuid(getuid()); + if (!pw) { + logit("No user exists for uid %lu", (u_long)getuid()); + exit(255); + } + /* Take a copy of the returned structure. */ + pw = pwcopy(pw); + + /* + * Set our umask to something reasonable, as some files are created + * with the default umask. This will make them world-readable but + * writable only by the owner, which is ok for all files for which we + * don't set the modes explicitly. + */ + umask(022); + + msetlocale(); + + /* + * Initialize option structure to indicate that no values have been + * set. + */ + initialize_options(&options); + + /* + * Prepare main ssh transport/connection structures + */ + if ((ssh = ssh_alloc_session_state()) == NULL) + fatal("Couldn't allocate session state"); + channel_init_channels(ssh); + + /* Parse command-line arguments. */ + host = NULL; + use_syslog = 0; + logfile = NULL; + argv0 = av[0]; + + again: + while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" + "AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { /* HUZdhjruz */ + switch (opt) { + case '1': + fatal("SSH protocol v.1 is no longer supported"); + break; + case '2': + /* Ignored */ + break; + case '4': + options.address_family = AF_INET; + break; + case '6': + options.address_family = AF_INET6; + break; + case 'n': + options.stdin_null = 1; + break; + case 'f': + options.fork_after_authentication = 1; + options.stdin_null = 1; + break; + case 'x': + options.forward_x11 = 0; + break; + case 'X': + options.forward_x11 = 1; + break; + case 'y': + use_syslog = 1; + break; + case 'E': + logfile = optarg; + break; + case 'G': + config_test = 1; + break; + case 'Y': + options.forward_x11 = 1; + options.forward_x11_trusted = 1; + break; + case 'g': + options.fwd_opts.gateway_ports = 1; + break; + case 'O': + if (options.stdio_forward_host != NULL) + fatal("Cannot specify multiplexing " + "command with -W"); + else if (muxclient_command != 0) + fatal("Multiplexing command already specified"); + if (strcmp(optarg, "check") == 0) + muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; + else if (strcmp(optarg, "forward") == 0) + muxclient_command = SSHMUX_COMMAND_FORWARD; + else if (strcmp(optarg, "exit") == 0) + muxclient_command = SSHMUX_COMMAND_TERMINATE; + else if (strcmp(optarg, "stop") == 0) + muxclient_command = SSHMUX_COMMAND_STOP; + else if (strcmp(optarg, "cancel") == 0) + muxclient_command = SSHMUX_COMMAND_CANCEL_FWD; + else if (strcmp(optarg, "proxy") == 0) + muxclient_command = SSHMUX_COMMAND_PROXY; + else + fatal("Invalid multiplex command."); + break; + case 'P': /* deprecated */ + break; + case 'Q': + cp = NULL; + if (strcmp(optarg, "cipher") == 0 || + strcasecmp(optarg, "Ciphers") == 0) + cp = cipher_alg_list('\n', 0); + else if (strcmp(optarg, "cipher-auth") == 0) + cp = cipher_alg_list('\n', 1); + else if (strcmp(optarg, "mac") == 0 || + strcasecmp(optarg, "MACs") == 0) + cp = mac_alg_list('\n'); + else if (strcmp(optarg, "kex") == 0 || + strcasecmp(optarg, "KexAlgorithms") == 0) + cp = kex_alg_list('\n'); + else if (strcmp(optarg, "key") == 0) + cp = sshkey_alg_list(0, 0, 0, '\n'); + else if (strcmp(optarg, "key-cert") == 0) + cp = sshkey_alg_list(1, 0, 0, '\n'); + else if (strcmp(optarg, "key-plain") == 0) + cp = sshkey_alg_list(0, 1, 0, '\n'); + else if (strcmp(optarg, "key-sig") == 0 || + strcasecmp(optarg, "PubkeyAcceptedKeyTypes") == 0 || /* deprecated name */ + strcasecmp(optarg, "PubkeyAcceptedAlgorithms") == 0 || + strcasecmp(optarg, "HostKeyAlgorithms") == 0 || + strcasecmp(optarg, "HostbasedKeyTypes") == 0 || /* deprecated name */ + strcasecmp(optarg, "HostbasedAcceptedKeyTypes") == 0 || /* deprecated name */ + strcasecmp(optarg, "HostbasedAcceptedAlgorithms") == 0) + cp = sshkey_alg_list(0, 0, 1, '\n'); + else if (strcmp(optarg, "sig") == 0) + cp = sshkey_alg_list(0, 1, 1, '\n'); + else if (strcmp(optarg, "protocol-version") == 0) + cp = xstrdup("2"); + else if (strcmp(optarg, "compression") == 0) { + cp = xstrdup(compression_alg_list(0)); + len = strlen(cp); + for (n = 0; n < len; n++) + if (cp[n] == ',') + cp[n] = '\n'; + } else if (strcmp(optarg, "help") == 0) { + cp = xstrdup( + "cipher\ncipher-auth\ncompression\nkex\n" + "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" + "protocol-version\nsig"); + } + if (cp == NULL) + fatal("Unsupported query \"%s\"", optarg); + printf("%s\n", cp); + free(cp); + exit(0); + break; + case 'a': + options.forward_agent = 0; + break; + case 'A': + options.forward_agent = 1; + break; + case 'k': + options.gss_deleg_creds = 0; + break; + case 'K': + options.gss_authentication = 1; + options.gss_deleg_creds = 1; + break; + case 'i': + p = tilde_expand_filename(optarg, getuid()); + if (stat(p, &st) == -1) + fprintf(stderr, "Warning: Identity file %s " + "not accessible: %s.\n", p, + strerror(errno)); + else + add_identity_file(&options, NULL, p, 1); + free(p); + break; + case 'I': +#ifdef ENABLE_PKCS11 + free(options.pkcs11_provider); + options.pkcs11_provider = xstrdup(optarg); +#else + fprintf(stderr, "no support for PKCS#11.\n"); +#endif + break; + case 'J': + if (options.jump_host != NULL) { + fatal("Only a single -J option is permitted " + "(use commas to separate multiple " + "jump hops)"); + } + if (options.proxy_command != NULL) + fatal("Cannot specify -J with ProxyCommand"); + if (parse_jump(optarg, &options, 1) == -1) + fatal("Invalid -J argument"); + options.proxy_command = xstrdup("none"); + break; + case 't': + if (options.request_tty == REQUEST_TTY_YES) + options.request_tty = REQUEST_TTY_FORCE; + else + options.request_tty = REQUEST_TTY_YES; + break; + case 'v': + if (debug_flag == 0) { + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG1; + } else { + if (options.log_level < SYSLOG_LEVEL_DEBUG3) { + debug_flag++; + options.log_level++; + } + } + break; + case 'V': + fprintf(stderr, "%s, %s\n", + SSH_RELEASE, SSH_OPENSSL_VERSION); + if (opt == 'V') + exit(0); + break; + case 'w': + if (options.tun_open == -1) + options.tun_open = SSH_TUNMODE_DEFAULT; + options.tun_local = a2tun(optarg, &options.tun_remote); + if (options.tun_local == SSH_TUNID_ERR) { + fprintf(stderr, + "Bad tun device '%s'\n", optarg); + exit(255); + } + break; + case 'W': + if (options.stdio_forward_host != NULL) + fatal("stdio forward already specified"); + if (muxclient_command != 0) + fatal("Cannot specify stdio forward with -O"); + if (parse_forward(&fwd, optarg, 1, 0)) { + options.stdio_forward_host = fwd.listen_host; + options.stdio_forward_port = fwd.listen_port; + free(fwd.connect_host); + } else { + fprintf(stderr, + "Bad stdio forwarding specification '%s'\n", + optarg); + exit(255); + } + options.request_tty = REQUEST_TTY_NO; + options.session_type = SESSION_TYPE_NONE; + break; + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + case 'e': + if (optarg[0] == '^' && optarg[2] == 0 && + (u_char) optarg[1] >= 64 && + (u_char) optarg[1] < 128) + options.escape_char = (u_char) optarg[1] & 31; + else if (strlen(optarg) == 1) + options.escape_char = (u_char) optarg[0]; + else if (strcmp(optarg, "none") == 0) + options.escape_char = SSH_ESCAPECHAR_NONE; + else { + fprintf(stderr, "Bad escape character '%s'.\n", + optarg); + exit(255); + } + break; + case 'c': + if (!ciphers_valid(*optarg == '+' || *optarg == '^' ? + optarg + 1 : optarg)) { + fprintf(stderr, "Unknown cipher type '%s'\n", + optarg); + exit(255); + } + free(options.ciphers); + options.ciphers = xstrdup(optarg); + break; + case 'm': + if (mac_valid(optarg)) { + free(options.macs); + options.macs = xstrdup(optarg); + } else { + fprintf(stderr, "Unknown mac type '%s'\n", + optarg); + exit(255); + } + break; + case 'M': + if (options.control_master == SSHCTL_MASTER_YES) + options.control_master = SSHCTL_MASTER_ASK; + else + options.control_master = SSHCTL_MASTER_YES; + break; + case 'p': + if (options.port == -1) { + options.port = a2port(optarg); + if (options.port <= 0) { + fprintf(stderr, "Bad port '%s'\n", + optarg); + exit(255); + } + } + break; + case 'l': + if (options.user == NULL) + options.user = optarg; + break; + + case 'L': + if (parse_forward(&fwd, optarg, 0, 0)) + add_local_forward(&options, &fwd); + else { + fprintf(stderr, + "Bad local forwarding specification '%s'\n", + optarg); + exit(255); + } + break; + + case 'R': + if (parse_forward(&fwd, optarg, 0, 1) || + parse_forward(&fwd, optarg, 1, 1)) { + add_remote_forward(&options, &fwd); + } else { + fprintf(stderr, + "Bad remote forwarding specification " + "'%s'\n", optarg); + exit(255); + } + break; + + case 'D': + if (parse_forward(&fwd, optarg, 1, 0)) { + add_local_forward(&options, &fwd); + } else { + fprintf(stderr, + "Bad dynamic forwarding specification " + "'%s'\n", optarg); + exit(255); + } + break; + + case 'C': +#ifdef WITH_ZLIB + options.compression = 1; +#else + error("Compression not supported, disabling."); +#endif + break; + case 'N': + if (options.session_type != -1 && + options.session_type != SESSION_TYPE_NONE) + fatal("Cannot specify -N with -s/SessionType"); + options.session_type = SESSION_TYPE_NONE; + options.request_tty = REQUEST_TTY_NO; + break; + case 'T': + options.request_tty = REQUEST_TTY_NO; + break; + case 'o': + line = xstrdup(optarg); + if (process_config_line(&options, pw, + host ? host : "", host ? host : "", line, + "command-line", 0, NULL, SSHCONF_USERCONF) != 0) + exit(255); + free(line); + break; + case 's': + if (options.session_type != -1 && + options.session_type != SESSION_TYPE_SUBSYSTEM) + fatal("Cannot specify -s with -N/SessionType"); + options.session_type = SESSION_TYPE_SUBSYSTEM; + break; + case 'S': + free(options.control_path); + options.control_path = xstrdup(optarg); + break; + case 'b': + options.bind_address = optarg; + break; + case 'B': + options.bind_interface = optarg; + break; + case 'F': + config = optarg; + break; + default: + usage(); + } + } + + if (optind > 1 && strcmp(av[optind - 1], "--") == 0) + opt_terminated = 1; + + ac -= optind; + av += optind; + + if (ac > 0 && !host) { + int tport; + char *tuser; + switch (parse_ssh_uri(*av, &tuser, &host, &tport)) { + case -1: + usage(); + break; + case 0: + if (options.user == NULL) { + options.user = tuser; + tuser = NULL; + } + free(tuser); + if (options.port == -1 && tport != -1) + options.port = tport; + break; + default: + p = xstrdup(*av); + cp = strrchr(p, '@'); + if (cp != NULL) { + if (cp == p) + usage(); + if (options.user == NULL) { + options.user = p; + p = NULL; + } + *cp++ = '\0'; + host = xstrdup(cp); + free(p); + } else + host = p; + break; + } + if (ac > 1 && !opt_terminated) { + optind = optreset = 1; + goto again; + } + ac--, av++; + } + + /* Check that we got a host name. */ + if (!host) + usage(); + + host_arg = xstrdup(host); + + /* Initialize the command to execute on remote host. */ + if ((command = sshbuf_new()) == NULL) + fatal("sshbuf_new failed"); + + /* + * Save the command to execute on the remote host in a buffer. There + * is no limit on the length of the command, except by the maximum + * packet size. Also sets the tty flag if there is no command. + */ + if (!ac) { + /* No command specified - execute shell on a tty. */ + if (options.session_type == SESSION_TYPE_SUBSYSTEM) { + fprintf(stderr, + "You must specify a subsystem to invoke.\n"); + usage(); + } + } else { + /* A command has been specified. Store it into the buffer. */ + for (i = 0; i < ac; i++) { + if ((r = sshbuf_putf(command, "%s%s", + i ? " " : "", av[i])) != 0) + fatal_fr(r, "buffer error"); + } + } + + /* + * Initialize "log" output. Since we are the client all output + * goes to stderr unless otherwise specified by -y or -E. + */ + if (use_syslog && logfile != NULL) + fatal("Can't specify both -y and -E"); + if (logfile != NULL) + log_redirect_stderr_to(logfile); + log_init(argv0, + options.log_level == SYSLOG_LEVEL_NOT_SET ? + SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == SYSLOG_FACILITY_NOT_SET ? + SYSLOG_FACILITY_USER : options.log_facility, + !use_syslog); + + if (debug_flag) + logit("%s, %s", SSH_RELEASE, SSH_OPENSSL_VERSION); + + /* Parse the configuration files */ + process_config_files(host_arg, pw, 0, &want_final_pass); + if (want_final_pass) + debug("configuration requests final Match pass"); + + /* Hostname canonicalisation needs a few options filled. */ + fill_default_options_for_canonicalization(&options); + + /* If the user has replaced the hostname then take it into use now */ + if (options.hostname != NULL) { + /* NB. Please keep in sync with readconf.c:match_cfg_line() */ + cp = percent_expand(options.hostname, + "h", host, (char *)NULL); + free(host); + host = cp; + free(options.hostname); + options.hostname = xstrdup(host); + } + + /* Don't lowercase addresses, they will be explicitly canonicalised */ + if ((was_addr = is_addr(host)) == 0) + lowercase(host); + + /* + * Try to canonicalize if requested by configuration or the + * hostname is an address. + */ + if (options.canonicalize_hostname != SSH_CANONICALISE_NO || was_addr) + addrs = resolve_canonicalize(&host, options.port); + + /* + * If CanonicalizePermittedCNAMEs have been specified but + * other canonicalization did not happen (by not being requested + * or by failing with fallback) then the hostname may still be changed + * as a result of CNAME following. + * + * Try to resolve the bare hostname name using the system resolver's + * usual search rules and then apply the CNAME follow rules. + * + * Skip the lookup if a ProxyCommand is being used unless the user + * has specifically requested canonicalisation for this case via + * CanonicalizeHostname=always + */ + direct = option_clear_or_none(options.proxy_command) && + options.jump_host == NULL; + if (addrs == NULL && config_has_permitted_cnames(&options) && (direct || + options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { + if ((addrs = resolve_host(host, options.port, + direct, cname, sizeof(cname))) == NULL) { + /* Don't fatal proxied host names not in the DNS */ + if (direct) + cleanup_exit(255); /* logged in resolve_host */ + } else + check_follow_cname(direct, &host, cname); + } + + /* + * If canonicalisation is enabled then re-parse the configuration + * files as new stanzas may match. + */ + if (options.canonicalize_hostname != 0 && !want_final_pass) { + debug("hostname canonicalisation enabled, " + "will re-parse configuration"); + want_final_pass = 1; + } + + if (want_final_pass) { + debug("re-parsing configuration"); + free(options.hostname); + options.hostname = xstrdup(host); + process_config_files(host_arg, pw, 1, NULL); + /* + * Address resolution happens early with canonicalisation + * enabled and the port number may have changed since, so + * reset it in address list + */ + if (addrs != NULL && options.port > 0) + set_addrinfo_port(addrs, options.port); + } + + /* Fill configuration defaults. */ + if (fill_default_options(&options) != 0) + cleanup_exit(255); + + if (options.user == NULL) + options.user = xstrdup(pw->pw_name); + + /* + * If ProxyJump option specified, then construct a ProxyCommand now. + */ + if (options.jump_host != NULL) { + char port_s[8]; + const char *jumpuser = options.jump_user, *sshbin = argv0; + int port = options.port, jumpport = options.jump_port; + + if (port <= 0) + port = default_ssh_port(); + if (jumpport <= 0) + jumpport = default_ssh_port(); + if (jumpuser == NULL) + jumpuser = options.user; + if (strcmp(options.jump_host, host) == 0 && port == jumpport && + strcmp(options.user, jumpuser) == 0) + fatal("jumphost loop via %s", options.jump_host); + + /* + * Try to use SSH indicated by argv[0], but fall back to + * "ssh" if it appears unavailable. + */ + if (strchr(argv0, '/') != NULL && access(argv0, X_OK) != 0) + sshbin = "ssh"; + + /* Consistency check */ + if (options.proxy_command != NULL) + fatal("inconsistent options: ProxyCommand+ProxyJump"); + /* Never use FD passing for ProxyJump */ + options.proxy_use_fdpass = 0; + snprintf(port_s, sizeof(port_s), "%d", options.jump_port); + xasprintf(&options.proxy_command, + "%s%s%s%s%s%s%s%s%s%s%.*s -W '[%%h]:%%p' %s", + sshbin, + /* Optional "-l user" argument if jump_user set */ + options.jump_user == NULL ? "" : " -l ", + options.jump_user == NULL ? "" : options.jump_user, + /* Optional "-p port" argument if jump_port set */ + options.jump_port <= 0 ? "" : " -p ", + options.jump_port <= 0 ? "" : port_s, + /* Optional additional jump hosts ",..." */ + options.jump_extra == NULL ? "" : " -J ", + options.jump_extra == NULL ? "" : options.jump_extra, + /* Optional "-F" argument if -F specified */ + config == NULL ? "" : " -F ", + config == NULL ? "" : config, + /* Optional "-v" arguments if -v set */ + debug_flag ? " -" : "", + debug_flag, "vvv", + /* Mandatory hostname */ + options.jump_host); + debug("Setting implicit ProxyCommand from ProxyJump: %s", + options.proxy_command); + } + + if (options.port == 0) + options.port = default_ssh_port(); + channel_set_af(ssh, options.address_family); + + /* Tidy and check options */ + if (options.host_key_alias != NULL) + lowercase(options.host_key_alias); + if (options.proxy_command != NULL && + strcmp(options.proxy_command, "-") == 0 && + options.proxy_use_fdpass) + fatal("ProxyCommand=- and ProxyUseFDPass are incompatible"); + if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { + if (options.control_persist && options.control_path != NULL) { + debug("UpdateHostKeys=ask is incompatible with " + "ControlPersist; disabling"); + options.update_hostkeys = 0; + } else if (sshbuf_len(command) != 0 || + options.remote_command != NULL || + options.request_tty == REQUEST_TTY_NO) { + debug("UpdateHostKeys=ask is incompatible with " + "remote command execution; disabling"); + options.update_hostkeys = 0; + } else if (options.log_level < SYSLOG_LEVEL_INFO) { + /* no point logging anything; user won't see it */ + options.update_hostkeys = 0; + } + } + if (options.connection_attempts <= 0) + fatal("Invalid number of ConnectionAttempts"); + + if (sshbuf_len(command) != 0 && options.remote_command != NULL) + fatal("Cannot execute command-line and remote command."); + + /* Cannot fork to background if no command. */ + if (options.fork_after_authentication && sshbuf_len(command) == 0 && + options.remote_command == NULL && + options.session_type != SESSION_TYPE_NONE) + fatal("Cannot fork into background without a command " + "to execute."); + + /* reinit */ + log_init(argv0, options.log_level, options.log_facility, !use_syslog); + for (j = 0; j < options.num_log_verbose; j++) { + if (strcasecmp(options.log_verbose[j], "none") == 0) + break; + log_verbose_add(options.log_verbose[j]); + } + + if (options.request_tty == REQUEST_TTY_YES || + options.request_tty == REQUEST_TTY_FORCE) + tty_flag = 1; + + /* Allocate a tty by default if no command specified. */ + if (sshbuf_len(command) == 0 && options.remote_command == NULL) + tty_flag = options.request_tty != REQUEST_TTY_NO; + + /* Force no tty */ + if (options.request_tty == REQUEST_TTY_NO || + (muxclient_command && muxclient_command != SSHMUX_COMMAND_PROXY) || + options.session_type == SESSION_TYPE_NONE) + tty_flag = 0; + /* Do not allocate a tty if stdin is not a tty. */ + if ((!isatty(fileno(stdin)) || options.stdin_null) && + options.request_tty != REQUEST_TTY_FORCE) { + if (tty_flag) + logit("Pseudo-terminal will not be allocated because " + "stdin is not a terminal."); + tty_flag = 0; + } + + /* Set up strings used to percent_expand() arguments */ + cinfo = xcalloc(1, sizeof(*cinfo)); + if (gethostname(thishost, sizeof(thishost)) == -1) + fatal("gethostname: %s", strerror(errno)); + cinfo->thishost = xstrdup(thishost); + thishost[strcspn(thishost, ".")] = '\0'; + cinfo->shorthost = xstrdup(thishost); + xasprintf(&cinfo->portstr, "%d", options.port); + xasprintf(&cinfo->uidstr, "%llu", + (unsigned long long)pw->pw_uid); + cinfo->keyalias = xstrdup(options.host_key_alias ? + options.host_key_alias : host_arg); + cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost, host, + cinfo->portstr, options.user); + cinfo->host_arg = xstrdup(host_arg); + cinfo->remhost = xstrdup(host); + cinfo->remuser = xstrdup(options.user); + cinfo->homedir = xstrdup(pw->pw_dir); + cinfo->locuser = xstrdup(pw->pw_name); + + /* + * Expand tokens in arguments. NB. LocalCommand is expanded later, + * after port-forwarding is set up, so it may pick up any local + * tunnel interface name allocated. + */ + if (options.remote_command != NULL) { + debug3("expanding RemoteCommand: %s", options.remote_command); + cp = options.remote_command; + options.remote_command = default_client_percent_expand(cp, + cinfo); + debug3("expanded RemoteCommand: %s", options.remote_command); + free(cp); + if ((r = sshbuf_put(command, options.remote_command, + strlen(options.remote_command))) != 0) + fatal_fr(r, "buffer error"); + } + + if (options.control_path != NULL) { + cp = tilde_expand_filename(options.control_path, getuid()); + free(options.control_path); + options.control_path = default_client_percent_dollar_expand(cp, + cinfo); + free(cp); + } + + if (options.identity_agent != NULL) { + p = tilde_expand_filename(options.identity_agent, getuid()); + cp = default_client_percent_dollar_expand(p, cinfo); + free(p); + free(options.identity_agent); + options.identity_agent = cp; + } + + if (options.forward_agent_sock_path != NULL) { + p = tilde_expand_filename(options.forward_agent_sock_path, + getuid()); + cp = default_client_percent_dollar_expand(p, cinfo); + free(p); + free(options.forward_agent_sock_path); + options.forward_agent_sock_path = cp; + if (stat(options.forward_agent_sock_path, &st) != 0) { + error("Cannot forward agent socket path \"%s\": %s", + options.forward_agent_sock_path, strerror(errno)); + if (options.exit_on_forward_failure) + cleanup_exit(255); + } + } + + if (options.num_system_hostfiles > 0 && + strcasecmp(options.system_hostfiles[0], "none") == 0) { + if (options.num_system_hostfiles > 1) + fatal("Invalid GlobalKnownHostsFiles: \"none\" " + "appears with other entries"); + free(options.system_hostfiles[0]); + options.system_hostfiles[0] = NULL; + options.num_system_hostfiles = 0; + } + + if (options.num_user_hostfiles > 0 && + strcasecmp(options.user_hostfiles[0], "none") == 0) { + if (options.num_user_hostfiles > 1) + fatal("Invalid UserKnownHostsFiles: \"none\" " + "appears with other entries"); + free(options.user_hostfiles[0]); + options.user_hostfiles[0] = NULL; + options.num_user_hostfiles = 0; + } + for (j = 0; j < options.num_user_hostfiles; j++) { + if (options.user_hostfiles[j] == NULL) + continue; + cp = tilde_expand_filename(options.user_hostfiles[j], getuid()); + p = default_client_percent_dollar_expand(cp, cinfo); + if (strcmp(options.user_hostfiles[j], p) != 0) + debug3("expanded UserKnownHostsFile '%s' -> " + "'%s'", options.user_hostfiles[j], p); + free(options.user_hostfiles[j]); + free(cp); + options.user_hostfiles[j] = p; + } + + for (i = 0; i < options.num_local_forwards; i++) { + if (options.local_forwards[i].listen_path != NULL) { + cp = options.local_forwards[i].listen_path; + p = options.local_forwards[i].listen_path = + default_client_percent_expand(cp, cinfo); + if (strcmp(cp, p) != 0) + debug3("expanded LocalForward listen path " + "'%s' -> '%s'", cp, p); + free(cp); + } + if (options.local_forwards[i].connect_path != NULL) { + cp = options.local_forwards[i].connect_path; + p = options.local_forwards[i].connect_path = + default_client_percent_expand(cp, cinfo); + if (strcmp(cp, p) != 0) + debug3("expanded LocalForward connect path " + "'%s' -> '%s'", cp, p); + free(cp); + } + } + + for (i = 0; i < options.num_remote_forwards; i++) { + if (options.remote_forwards[i].listen_path != NULL) { + cp = options.remote_forwards[i].listen_path; + p = options.remote_forwards[i].listen_path = + default_client_percent_expand(cp, cinfo); + if (strcmp(cp, p) != 0) + debug3("expanded RemoteForward listen path " + "'%s' -> '%s'", cp, p); + free(cp); + } + if (options.remote_forwards[i].connect_path != NULL) { + cp = options.remote_forwards[i].connect_path; + p = options.remote_forwards[i].connect_path = + default_client_percent_expand(cp, cinfo); + if (strcmp(cp, p) != 0) + debug3("expanded RemoteForward connect path " + "'%s' -> '%s'", cp, p); + free(cp); + } + } + + if (config_test) { + dump_client_config(&options, host); + exit(0); + } + + /* Expand SecurityKeyProvider if it refers to an environment variable */ + if (options.sk_provider != NULL && *options.sk_provider == '$' && + strlen(options.sk_provider) > 1) { + if ((cp = getenv(options.sk_provider + 1)) == NULL) { + debug("Authenticator provider %s did not resolve; " + "disabling", options.sk_provider); + free(options.sk_provider); + options.sk_provider = NULL; + } else { + debug2("resolved SecurityKeyProvider %s => %s", + options.sk_provider, cp); + free(options.sk_provider); + options.sk_provider = xstrdup(cp); + } + } + + if (muxclient_command != 0 && options.control_path == NULL) + fatal("No ControlPath specified for \"-O\" command"); + if (options.control_path != NULL) { + int sock; + if ((sock = muxclient(options.control_path)) >= 0) { + ssh_packet_set_connection(ssh, sock, sock); + ssh_packet_set_mux(ssh); + goto skip_connect; + } + } + + /* + * If hostname canonicalisation was not enabled, then we may not + * have yet resolved the hostname. Do so now. + */ + if (addrs == NULL && options.proxy_command == NULL) { + debug2("resolving \"%s\" port %d", host, options.port); + if ((addrs = resolve_host(host, options.port, 1, + cname, sizeof(cname))) == NULL) + cleanup_exit(255); /* resolve_host logs the error */ + } + + if (options.connection_timeout >= INT_MAX/1000) + timeout_ms = INT_MAX; + else + timeout_ms = options.connection_timeout * 1000; + + /* Open a connection to the remote host. */ + if (ssh_connect(ssh, host, host_arg, addrs, &hostaddr, options.port, + options.connection_attempts, + &timeout_ms, options.tcp_keep_alive) != 0) + exit(255); + + if (addrs != NULL) + freeaddrinfo(addrs); + + ssh_packet_set_timeout(ssh, options.server_alive_interval, + options.server_alive_count_max); + + if (timeout_ms > 0) + debug3("timeout: %d ms remain after connect", timeout_ms); + + /* + * If we successfully made the connection and we have hostbased auth + * enabled, load the public keys so we can later use the ssh-keysign + * helper to sign challenges. + */ + sensitive_data.nkeys = 0; + sensitive_data.keys = NULL; + if (options.hostbased_authentication) { + sensitive_data.nkeys = 10; + sensitive_data.keys = xcalloc(sensitive_data.nkeys, + sizeof(struct sshkey)); + + /* XXX check errors? */ +#define L_PUBKEY(p,o) do { \ + if ((o) >= sensitive_data.nkeys) \ + fatal_f("pubkey out of array bounds"); \ + check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \ + p, "pubkey"); \ + if (sensitive_data.keys[o] != NULL) \ + debug2("hostbased key %d: %s key from \"%s\"", o, \ + sshkey_ssh_name(sensitive_data.keys[o]), p); \ +} while (0) +#define L_CERT(p,o) do { \ + if ((o) >= sensitive_data.nkeys) \ + fatal_f("cert out of array bounds"); \ + check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), p, "cert"); \ + if (sensitive_data.keys[o] != NULL) \ + debug2("hostbased key %d: %s cert from \"%s\"", o, \ + sshkey_ssh_name(sensitive_data.keys[o]), p); \ +} while (0) + + if (options.hostbased_authentication == 1) { + L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0); + L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1); + L_CERT(_PATH_HOST_RSA_KEY_FILE, 2); + L_CERT(_PATH_HOST_DSA_KEY_FILE, 3); + L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4); + L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5); + L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6); + L_PUBKEY(_PATH_HOST_DSA_KEY_FILE, 7); + L_CERT(_PATH_HOST_XMSS_KEY_FILE, 8); + L_PUBKEY(_PATH_HOST_XMSS_KEY_FILE, 9); + } + } + + /* load options.identity_files */ + load_public_identity_files(cinfo); + + /* optionally set the SSH_AUTHSOCKET_ENV_NAME variable */ + if (options.identity_agent && + strcmp(options.identity_agent, SSH_AUTHSOCKET_ENV_NAME) != 0) { + if (strcmp(options.identity_agent, "none") == 0) { + unsetenv(SSH_AUTHSOCKET_ENV_NAME); + } else { + cp = options.identity_agent; + /* legacy (limited) format */ + if (cp[0] == '$' && cp[1] != '{') { + if (!valid_env_name(cp + 1)) { + fatal("Invalid IdentityAgent " + "environment variable name %s", cp); + } + if ((p = getenv(cp + 1)) == NULL) + unsetenv(SSH_AUTHSOCKET_ENV_NAME); + else + setenv(SSH_AUTHSOCKET_ENV_NAME, p, 1); + } else { + /* identity_agent specifies a path directly */ + setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1); + } + } + } + + if (options.forward_agent && options.forward_agent_sock_path != NULL) { + cp = options.forward_agent_sock_path; + if (cp[0] == '$') { + if (!valid_env_name(cp + 1)) { + fatal("Invalid ForwardAgent environment variable name %s", cp); + } + if ((p = getenv(cp + 1)) != NULL) + forward_agent_sock_path = xstrdup(p); + else + options.forward_agent = 0; + free(cp); + } else { + forward_agent_sock_path = cp; + } + } + + /* Expand ~ in known host file names. */ + tilde_expand_paths(options.system_hostfiles, + options.num_system_hostfiles); + tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles); + + ssh_signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ + ssh_signal(SIGCHLD, main_sigchld_handler); + + /* Log into the remote system. Never returns if the login fails. */ + ssh_login(ssh, &sensitive_data, host, (struct sockaddr *)&hostaddr, + options.port, pw, timeout_ms, cinfo); + + /* We no longer need the private host keys. Clear them now. */ + if (sensitive_data.nkeys != 0) { + for (i = 0; i < sensitive_data.nkeys; i++) { + if (sensitive_data.keys[i] != NULL) { + /* Destroys contents safely */ + debug3("clear hostkey %d", i); + sshkey_free(sensitive_data.keys[i]); + sensitive_data.keys[i] = NULL; + } + } + free(sensitive_data.keys); + } + for (i = 0; i < options.num_identity_files; i++) { + free(options.identity_files[i]); + options.identity_files[i] = NULL; + if (options.identity_keys[i]) { + sshkey_free(options.identity_keys[i]); + options.identity_keys[i] = NULL; + } + } + for (i = 0; i < options.num_certificate_files; i++) { + free(options.certificate_files[i]); + options.certificate_files[i] = NULL; + } + +#ifdef ENABLE_PKCS11 + (void)pkcs11_del_provider(options.pkcs11_provider); +#endif + + skip_connect: + exit_status = ssh_session2(ssh, cinfo); + ssh_conn_info_free(cinfo); + ssh_packet_close(ssh); + + if (options.control_path != NULL && muxserver_sock != -1) + unlink(options.control_path); + + /* Kill ProxyCommand if it is running. */ + ssh_kill_proxy_command(); + + return exit_status; +} + +static void +control_persist_detach(void) +{ + pid_t pid; + + debug_f("backgrounding master process"); + + /* + * master (current process) into the background, and make the + * foreground process a client of the backgrounded master. + */ + switch ((pid = fork())) { + case -1: + fatal_f("fork: %s", strerror(errno)); + case 0: + /* Child: master process continues mainloop */ + break; + default: + /* Parent: set up mux client to connect to backgrounded master */ + debug2_f("background process is %ld", (long)pid); + options.stdin_null = ostdin_null_flag; + options.request_tty = orequest_tty; + tty_flag = otty_flag; + options.session_type = osession_type; + close(muxserver_sock); + muxserver_sock = -1; + options.control_master = SSHCTL_MASTER_NO; + muxclient(options.control_path); + /* muxclient() doesn't return on success. */ + fatal("Failed to connect to new control master"); + } + if (stdfd_devnull(1, 1, !(log_is_on_stderr() && debug_flag)) == -1) + error_f("stdfd_devnull failed"); + daemon(1, 1); + setproctitle("%s [mux]", options.control_path); +} + +/* Do fork() after authentication. Used by "ssh -f" */ +static void +fork_postauth(void) +{ + if (need_controlpersist_detach) + control_persist_detach(); + debug("forking to background"); + options.fork_after_authentication = 0; + if (daemon(1, 1) == -1) + fatal("daemon() failed: %.200s", strerror(errno)); + if (stdfd_devnull(1, 1, !(log_is_on_stderr() && debug_flag)) == -1) + error_f("stdfd_devnull failed"); +} + +static void +forwarding_success(void) +{ + if (forward_confirms_pending == -1) + return; + if (--forward_confirms_pending == 0) { + debug_f("all expected forwarding replies received"); + if (options.fork_after_authentication) + fork_postauth(); + } else { + debug2_f("%d expected forwarding replies remaining", + forward_confirms_pending); + } +} + +/* Callback for remote forward global requests */ +static void +ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) +{ + struct Forward *rfwd = (struct Forward *)ctxt; + u_int port; + int r; + + /* XXX verbose() on failure? */ + debug("remote forward %s for: listen %s%s%d, connect %s:%d", + type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", + rfwd->listen_path ? rfwd->listen_path : + rfwd->listen_host ? rfwd->listen_host : "", + (rfwd->listen_path || rfwd->listen_host) ? ":" : "", + rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : + rfwd->connect_host, rfwd->connect_port); + if (rfwd->listen_path == NULL && rfwd->listen_port == 0) { + if (type == SSH2_MSG_REQUEST_SUCCESS) { + if ((r = sshpkt_get_u32(ssh, &port)) != 0) + fatal_fr(r, "parse packet"); + if (port > 65535) { + error("Invalid allocated port %u for remote " + "forward to %s:%d", port, + rfwd->connect_host, rfwd->connect_port); + /* Ensure failure processing runs below */ + type = SSH2_MSG_REQUEST_FAILURE; + channel_update_permission(ssh, + rfwd->handle, -1); + } else { + rfwd->allocated_port = (int)port; + logit("Allocated port %u for remote " + "forward to %s:%d", + rfwd->allocated_port, rfwd->connect_path ? + rfwd->connect_path : rfwd->connect_host, + rfwd->connect_port); + channel_update_permission(ssh, + rfwd->handle, rfwd->allocated_port); + } + } else { + channel_update_permission(ssh, rfwd->handle, -1); + } + } + + if (type == SSH2_MSG_REQUEST_FAILURE) { + if (options.exit_on_forward_failure) { + if (rfwd->listen_path != NULL) + fatal("Error: remote port forwarding failed " + "for listen path %s", rfwd->listen_path); + else + fatal("Error: remote port forwarding failed " + "for listen port %d", rfwd->listen_port); + } else { + if (rfwd->listen_path != NULL) + logit("Warning: remote port forwarding failed " + "for listen path %s", rfwd->listen_path); + else + logit("Warning: remote port forwarding failed " + "for listen port %d", rfwd->listen_port); + } + } + forwarding_success(); +} + +static void +client_cleanup_stdio_fwd(struct ssh *ssh, int id, void *arg) +{ + debug("stdio forwarding: done"); + cleanup_exit(0); +} + +static void +ssh_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) +{ + if (!success) + fatal("stdio forwarding failed"); +} + +static void +ssh_tun_confirm(struct ssh *ssh, int id, int success, void *arg) +{ + if (!success) { + error("Tunnel forwarding failed"); + if (options.exit_on_forward_failure) + cleanup_exit(255); + } + + debug_f("tunnel forward established, id=%d", id); + forwarding_success(); +} + +static void +ssh_init_stdio_forwarding(struct ssh *ssh) +{ + Channel *c; + int in, out; + + if (options.stdio_forward_host == NULL) + return; + + debug3_f("%s:%d", options.stdio_forward_host, + options.stdio_forward_port); + + if ((in = dup(STDIN_FILENO)) == -1 || + (out = dup(STDOUT_FILENO)) == -1) + fatal_f("dup() in/out failed"); + if ((c = channel_connect_stdio_fwd(ssh, options.stdio_forward_host, + options.stdio_forward_port, in, out, + CHANNEL_NONBLOCK_STDIO)) == NULL) + fatal_f("channel_connect_stdio_fwd failed"); + channel_register_cleanup(ssh, c->self, client_cleanup_stdio_fwd, 0); + channel_register_open_confirm(ssh, c->self, ssh_stdio_confirm, NULL); +} + +static void +ssh_init_forward_permissions(struct ssh *ssh, const char *what, char **opens, + u_int num_opens) +{ + u_int i; + int port; + char *addr, *arg, *oarg; + int where = FORWARD_LOCAL; + + channel_clear_permission(ssh, FORWARD_ADM, where); + if (num_opens == 0) + return; /* permit any */ + + /* handle keywords: "any" / "none" */ + if (num_opens == 1 && strcmp(opens[0], "any") == 0) + return; + if (num_opens == 1 && strcmp(opens[0], "none") == 0) { + channel_disable_admin(ssh, where); + return; + } + /* Otherwise treat it as a list of permitted host:port */ + for (i = 0; i < num_opens; i++) { + oarg = arg = xstrdup(opens[i]); + addr = hpdelim(&arg); + if (addr == NULL) + fatal_f("missing host in %s", what); + addr = cleanhostname(addr); + if (arg == NULL || ((port = permitopen_port(arg)) < 0)) + fatal_f("bad port number in %s", what); + /* Send it to channels layer */ + channel_add_permission(ssh, FORWARD_ADM, + where, addr, port); + free(oarg); + } +} + +static void +ssh_init_forwarding(struct ssh *ssh, char **ifname) +{ + int success = 0; + int i; + + ssh_init_forward_permissions(ssh, "permitremoteopen", + options.permitted_remote_opens, + options.num_permitted_remote_opens); + + if (options.exit_on_forward_failure) + forward_confirms_pending = 0; /* track pending requests */ + /* Initiate local TCP/IP port forwardings. */ + for (i = 0; i < options.num_local_forwards; i++) { + debug("Local connections to %.200s:%d forwarded to remote " + "address %.200s:%d", + (options.local_forwards[i].listen_path != NULL) ? + options.local_forwards[i].listen_path : + (options.local_forwards[i].listen_host == NULL) ? + (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : + options.local_forwards[i].listen_host, + options.local_forwards[i].listen_port, + (options.local_forwards[i].connect_path != NULL) ? + options.local_forwards[i].connect_path : + options.local_forwards[i].connect_host, + options.local_forwards[i].connect_port); + success += channel_setup_local_fwd_listener(ssh, + &options.local_forwards[i], &options.fwd_opts); + } + if (i > 0 && success != i && options.exit_on_forward_failure) + fatal("Could not request local forwarding."); + if (i > 0 && success == 0) + error("Could not request local forwarding."); + + /* Initiate remote TCP/IP port forwardings. */ + for (i = 0; i < options.num_remote_forwards; i++) { + debug("Remote connections from %.200s:%d forwarded to " + "local address %.200s:%d", + (options.remote_forwards[i].listen_path != NULL) ? + options.remote_forwards[i].listen_path : + (options.remote_forwards[i].listen_host == NULL) ? + "LOCALHOST" : options.remote_forwards[i].listen_host, + options.remote_forwards[i].listen_port, + (options.remote_forwards[i].connect_path != NULL) ? + options.remote_forwards[i].connect_path : + options.remote_forwards[i].connect_host, + options.remote_forwards[i].connect_port); + if ((options.remote_forwards[i].handle = + channel_request_remote_forwarding(ssh, + &options.remote_forwards[i])) >= 0) { + client_register_global_confirm( + ssh_confirm_remote_forward, + &options.remote_forwards[i]); + forward_confirms_pending++; + } else if (options.exit_on_forward_failure) + fatal("Could not request remote forwarding."); + else + logit("Warning: Could not request remote forwarding."); + } + + /* Initiate tunnel forwarding. */ + if (options.tun_open != SSH_TUNMODE_NO) { + if ((*ifname = client_request_tun_fwd(ssh, + options.tun_open, options.tun_local, + options.tun_remote, ssh_tun_confirm, NULL)) != NULL) + forward_confirms_pending++; + else if (options.exit_on_forward_failure) + fatal("Could not request tunnel forwarding."); + else + error("Could not request tunnel forwarding."); + } + if (forward_confirms_pending > 0) { + debug_f("expecting replies for %d forwards", + forward_confirms_pending); + } +} + +static void +check_agent_present(void) +{ + int r; + + if (options.forward_agent) { + /* Clear agent forwarding if we don't have an agent. */ + if ((r = ssh_get_authentication_socket(NULL)) != 0) { + options.forward_agent = 0; + if (r != SSH_ERR_AGENT_NOT_PRESENT) + debug_r(r, "ssh_get_authentication_socket"); + } + } +} + +static void +ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) +{ + extern char **environ; + const char *display, *term; + int r, interactive = tty_flag; + char *proto = NULL, *data = NULL; + + if (!success) + return; /* No need for error message, channels code sens one */ + + display = getenv("DISPLAY"); + if (display == NULL && options.forward_x11) + debug("X11 forwarding requested but DISPLAY not set"); + if (options.forward_x11 && client_x11_get_proto(ssh, display, + options.xauth_location, options.forward_x11_trusted, + options.forward_x11_timeout, &proto, &data) == 0) { + /* Request forwarding with authentication spoofing. */ + debug("Requesting X11 forwarding with authentication " + "spoofing."); + x11_request_forwarding_with_spoofing(ssh, id, display, proto, + data, 1); + client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); + /* XXX exit_on_forward_failure */ + interactive = 1; + } + + check_agent_present(); + if (options.forward_agent) { + debug("Requesting authentication agent forwarding."); + channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); + if ((r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + } + + /* Tell the packet module whether this is an interactive session. */ + ssh_packet_set_interactive(ssh, interactive, + options.ip_qos_interactive, options.ip_qos_bulk); + + if ((term = lookup_env_in_list("TERM", options.setenv, + options.num_setenv)) == NULL || *term == '\0') + term = getenv("TERM"); + client_session2_setup(ssh, id, tty_flag, + options.session_type == SESSION_TYPE_SUBSYSTEM, term, + NULL, fileno(stdin), command, environ); +} + +/* open new channel for a session */ +static int +ssh_session2_open(struct ssh *ssh) +{ + Channel *c; + int window, packetmax, in, out, err; + + if (options.stdin_null) { + in = open(_PATH_DEVNULL, O_RDONLY); + } else { + in = dup(STDIN_FILENO); + } + out = dup(STDOUT_FILENO); + err = dup(STDERR_FILENO); + + if (in == -1 || out == -1 || err == -1) + fatal("dup() in/out/err failed"); + + window = CHAN_SES_WINDOW_DEFAULT; + packetmax = CHAN_SES_PACKET_DEFAULT; + if (tty_flag) { + window >>= 1; + packetmax >>= 1; + } + c = channel_new(ssh, + "session", SSH_CHANNEL_OPENING, in, out, err, + window, packetmax, CHAN_EXTENDED_WRITE, + "client-session", CHANNEL_NONBLOCK_STDIO); + + debug3_f("channel_new: %d", c->self); + + channel_send_open(ssh, c->self); + if (options.session_type != SESSION_TYPE_NONE) + channel_register_open_confirm(ssh, c->self, + ssh_session2_setup, NULL); + + return c->self; +} + +static int +ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) +{ + int r, id = -1; + char *cp, *tun_fwd_ifname = NULL; + + /* XXX should be pre-session */ + if (!options.control_persist) + ssh_init_stdio_forwarding(ssh); + + ssh_init_forwarding(ssh, &tun_fwd_ifname); + + if (options.local_command != NULL) { + debug3("expanding LocalCommand: %s", options.local_command); + cp = options.local_command; + options.local_command = percent_expand(cp, + DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), + "T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname, + (char *)NULL); + debug3("expanded LocalCommand: %s", options.local_command); + free(cp); + } + + /* Start listening for multiplex clients */ + if (!ssh_packet_get_mux(ssh)) + muxserver_listen(ssh); + + /* + * If we are in control persist mode and have a working mux listen + * socket, then prepare to background ourselves and have a foreground + * client attach as a control client. + * NB. we must save copies of the flags that we override for + * the backgrounding, since we defer attachment of the client until + * after the connection is fully established (in particular, + * async rfwd replies have been received for ExitOnForwardFailure). + */ + if (options.control_persist && muxserver_sock != -1) { + ostdin_null_flag = options.stdin_null; + osession_type = options.session_type; + orequest_tty = options.request_tty; + otty_flag = tty_flag; + options.stdin_null = 1; + options.session_type = SESSION_TYPE_NONE; + tty_flag = 0; + if (!options.fork_after_authentication && + (osession_type != SESSION_TYPE_NONE || + options.stdio_forward_host != NULL)) + need_controlpersist_detach = 1; + options.fork_after_authentication = 1; + } + /* + * ControlPersist mux listen socket setup failed, attempt the + * stdio forward setup that we skipped earlier. + */ + if (options.control_persist && muxserver_sock == -1) + ssh_init_stdio_forwarding(ssh); + + if (options.session_type != SESSION_TYPE_NONE) + id = ssh_session2_open(ssh); + else { + ssh_packet_set_interactive(ssh, + options.control_master == SSHCTL_MASTER_NO, + options.ip_qos_interactive, options.ip_qos_bulk); + } + + /* If we don't expect to open a new session, then disallow it */ + if (options.control_master == SSHCTL_MASTER_NO && + (ssh->compat & SSH_NEW_OPENSSH)) { + debug("Requesting no-more-sessions@openssh.com"); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, + "no-more-sessions@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + } + + /* Execute a local command */ + if (options.local_command != NULL && + options.permit_local_command) + ssh_local_cmd(options.local_command); + + /* + * stdout is now owned by the session channel; clobber it here + * so future channel closes are propagated to the local fd. + * NB. this can only happen after LocalCommand has completed, + * as it may want to write to stdout. + */ + if (!need_controlpersist_detach && stdfd_devnull(0, 1, 0) == -1) + error_f("stdfd_devnull failed"); + + /* + * If requested and we are not interested in replies to remote + * forwarding requests, then let ssh continue in the background. + */ + if (options.fork_after_authentication) { + if (options.exit_on_forward_failure && + options.num_remote_forwards > 0) { + debug("deferring postauth fork until remote forward " + "confirmation received"); + } else + fork_postauth(); + } + + return client_loop(ssh, tty_flag, tty_flag ? + options.escape_char : SSH_ESCAPECHAR_NONE, id); +} + +/* Loads all IdentityFile and CertificateFile keys */ +static void +load_public_identity_files(const struct ssh_conn_info *cinfo) +{ + char *filename, *cp; + struct sshkey *public; + int i; + u_int n_ids, n_certs; + char *identity_files[SSH_MAX_IDENTITY_FILES]; + struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; + int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; + int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; +#ifdef ENABLE_PKCS11 + struct sshkey **keys = NULL; + char **comments = NULL; + int nkeys; +#endif /* PKCS11 */ + + n_ids = n_certs = 0; + memset(identity_files, 0, sizeof(identity_files)); + memset(identity_keys, 0, sizeof(identity_keys)); + memset(identity_file_userprovided, 0, + sizeof(identity_file_userprovided)); + memset(certificate_files, 0, sizeof(certificate_files)); + memset(certificates, 0, sizeof(certificates)); + memset(certificate_file_userprovided, 0, + sizeof(certificate_file_userprovided)); + +#ifdef ENABLE_PKCS11 + if (options.pkcs11_provider != NULL && + options.num_identity_files < SSH_MAX_IDENTITY_FILES && + (pkcs11_init(!options.batch_mode) == 0) && + (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, + &keys, &comments)) > 0) { + for (i = 0; i < nkeys; i++) { + if (n_ids >= SSH_MAX_IDENTITY_FILES) { + sshkey_free(keys[i]); + free(comments[i]); + continue; + } + identity_keys[n_ids] = keys[i]; + identity_files[n_ids] = comments[i]; /* transferred */ + n_ids++; + } + free(keys); + free(comments); + } +#endif /* ENABLE_PKCS11 */ + for (i = 0; i < options.num_identity_files; i++) { + if (n_ids >= SSH_MAX_IDENTITY_FILES || + strcasecmp(options.identity_files[i], "none") == 0) { + free(options.identity_files[i]); + options.identity_files[i] = NULL; + continue; + } + cp = tilde_expand_filename(options.identity_files[i], getuid()); + filename = default_client_percent_dollar_expand(cp, cinfo); + free(cp); + check_load(sshkey_load_public(filename, &public, NULL), + filename, "pubkey"); + debug("identity file %s type %d", filename, + public ? public->type : -1); + free(options.identity_files[i]); + identity_files[n_ids] = filename; + identity_keys[n_ids] = public; + identity_file_userprovided[n_ids] = + options.identity_file_userprovided[i]; + if (++n_ids >= SSH_MAX_IDENTITY_FILES) + continue; + + /* + * If no certificates have been explicitly listed then try + * to add the default certificate variant too. + */ + if (options.num_certificate_files != 0) + continue; + xasprintf(&cp, "%s-cert", filename); + check_load(sshkey_load_public(cp, &public, NULL), + filename, "pubkey"); + debug("identity file %s type %d", cp, + public ? public->type : -1); + if (public == NULL) { + free(cp); + continue; + } + if (!sshkey_is_cert(public)) { + debug_f("key %s type %s is not a certificate", + cp, sshkey_type(public)); + sshkey_free(public); + free(cp); + continue; + } + /* NB. leave filename pointing to private key */ + identity_files[n_ids] = xstrdup(filename); + identity_keys[n_ids] = public; + identity_file_userprovided[n_ids] = + options.identity_file_userprovided[i]; + n_ids++; + } + + if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES) + fatal_f("too many certificates"); + for (i = 0; i < options.num_certificate_files; i++) { + cp = tilde_expand_filename(options.certificate_files[i], + getuid()); + filename = default_client_percent_dollar_expand(cp, cinfo); + free(cp); + + check_load(sshkey_load_public(filename, &public, NULL), + filename, "certificate"); + debug("certificate file %s type %d", filename, + public ? public->type : -1); + free(options.certificate_files[i]); + options.certificate_files[i] = NULL; + if (public == NULL) { + free(filename); + continue; + } + if (!sshkey_is_cert(public)) { + debug_f("key %s type %s is not a certificate", + filename, sshkey_type(public)); + sshkey_free(public); + free(filename); + continue; + } + certificate_files[n_certs] = filename; + certificates[n_certs] = public; + certificate_file_userprovided[n_certs] = + options.certificate_file_userprovided[i]; + ++n_certs; + } + + options.num_identity_files = n_ids; + memcpy(options.identity_files, identity_files, sizeof(identity_files)); + memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); + memcpy(options.identity_file_userprovided, + identity_file_userprovided, sizeof(identity_file_userprovided)); + + options.num_certificate_files = n_certs; + memcpy(options.certificate_files, + certificate_files, sizeof(certificate_files)); + memcpy(options.certificates, certificates, sizeof(certificates)); + memcpy(options.certificate_file_userprovided, + certificate_file_userprovided, + sizeof(certificate_file_userprovided)); +} + +static void +main_sigchld_handler(int sig) +{ + int save_errno = errno; + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || + (pid == -1 && errno == EINTR)) + ; + errno = save_errno; +} diff --git a/aosp/external/openssh/ssh.h b/aosp/external/openssh/ssh.h new file mode 100644 index 0000000000000000000000000000000000000000..8110c060287fa618766579a1231263f066bf350c --- /dev/null +++ b/aosp/external/openssh/ssh.h @@ -0,0 +1,104 @@ +/* $OpenBSD: ssh.h,v 1.90 2020/07/14 23:57:01 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* Cipher used for encrypting authentication files. */ +#define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES + +/* Default port number. */ +#define SSH_DEFAULT_PORT 22 + +/* + * Maximum number of certificate files that can be specified + * in configuration files or on the command line. + */ +#define SSH_MAX_CERTIFICATE_FILES 100 + +/* + * Maximum number of RSA authentication identity files that can be specified + * in configuration files or on the command line. + */ +#define SSH_MAX_IDENTITY_FILES 100 + +/* + * Major protocol version. Different version indicates major incompatibility + * that prevents communication. + * + * Minor protocol version. Different version indicates minor incompatibility + * that does not prevent interoperation. + */ +#define PROTOCOL_MAJOR_1 1 +#define PROTOCOL_MINOR_1 5 + +/* We support only SSH2 */ +#define PROTOCOL_MAJOR_2 2 +#define PROTOCOL_MINOR_2 0 + +/* + * Name for the service. The port named by this service overrides the + * default port if present. + */ +#define SSH_SERVICE_NAME "ssh" + +/* + * Name of the environment variable containing the process ID of the + * authentication agent. + */ +#define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" + +/* + * Name of the environment variable containing the pathname of the + * authentication socket. + */ +#define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" + +/* + * Environment variable for overwriting the default location of askpass + */ +#define SSH_ASKPASS_ENV "SSH_ASKPASS" + +/* + * Environment variable to control whether or not askpass is used. + */ +#define SSH_ASKPASS_REQUIRE_ENV "SSH_ASKPASS_REQUIRE" + +/* + * Force host key length and server key length to differ by at least this + * many bits. This is to make double encryption with rsaref work. + */ +#define SSH_KEY_BITS_RESERVED 128 + +/* + * Length of the session key in bytes. (Specified as 256 bits in the + * protocol.) + */ +#define SSH_SESSION_KEY_LENGTH 32 + +/* Used to identify ``EscapeChar none'' */ +#define SSH_ESCAPECHAR_NONE -2 + +/* + * unprivileged user when UsePrivilegeSeparation=yes; + * sshd will change its privileges to this user and its + * primary group. + */ +#ifndef SSH_PRIVSEP_USER +#define SSH_PRIVSEP_USER "sshd" +#endif + +/* Listen backlog for sshd, ssh-agent and forwarding sockets */ +#define SSH_LISTEN_BACKLOG 128 + +/* Limits for banner exchange */ +#define SSH_MAX_BANNER_LEN 8192 +#define SSH_MAX_PRE_BANNER_LINES 1024 diff --git a/aosp/external/openssh/ssh2.h b/aosp/external/openssh/ssh2.h new file mode 100644 index 0000000000000000000000000000000000000000..32ddae89b33bef83f9b6059b61b345d5293a9c4a --- /dev/null +++ b/aosp/external/openssh/ssh2.h @@ -0,0 +1,174 @@ +/* $OpenBSD: ssh2.h,v 1.19 2020/11/19 23:05:05 dtucker Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * RFC4251: + * + * Transport layer protocol: + * + * 1-19 Transport layer generic (e.g. disconnect, ignore, debug, + * etc) + * 20-29 Algorithm negotiation + * 30-49 Key exchange method specific (numbers can be reused for + * different authentication methods) + * + * User authentication protocol: + * + * 50-59 User authentication generic + * 60-79 User authentication method specific (numbers can be reused + * for different authentication methods) + * + * Connection protocol: + * + * 80-89 Connection protocol generic + * 90-127 Channel related messages + * + * Reserved for client protocols: + * + * 128-191 Reserved + * + * Local extensions: + * + * 192-255 Local extensions + */ + +/* special marker for no message */ + +#define SSH_MSG_NONE 0 + +/* ranges */ + +#define SSH2_MSG_TRANSPORT_MIN 1 +#define SSH2_MSG_TRANSPORT_MAX 49 +#define SSH2_MSG_USERAUTH_MIN 50 +#define SSH2_MSG_USERAUTH_MAX 79 +#define SSH2_MSG_USERAUTH_PER_METHOD_MIN 60 +#define SSH2_MSG_USERAUTH_PER_METHOD_MAX SSH2_MSG_USERAUTH_MAX +#define SSH2_MSG_CONNECTION_MIN 80 +#define SSH2_MSG_CONNECTION_MAX 127 +#define SSH2_MSG_RESERVED_MIN 128 +#define SSH2_MSG_RESERVED_MAX 191 +#define SSH2_MSG_LOCAL_MIN 192 +#define SSH2_MSG_LOCAL_MAX 255 +#define SSH2_MSG_MIN 1 +#define SSH2_MSG_MAX 255 + +/* transport layer: generic */ + +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 +#define SSH2_MSG_EXT_INFO 7 + +/* transport layer: alg negotiation */ + +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 + +/* transport layer: kex specific messages, can be reused */ + +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 + +/* dh-group-exchange */ +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 +#define SSH2_MSG_KEX_DH_GEX_INIT 32 +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 + +/* ecdh */ +#define SSH2_MSG_KEX_ECDH_INIT 30 +#define SSH2_MSG_KEX_ECDH_REPLY 31 + +/* user authentication: generic */ + +#define SSH2_MSG_USERAUTH_REQUEST 50 +#define SSH2_MSG_USERAUTH_FAILURE 51 +#define SSH2_MSG_USERAUTH_SUCCESS 52 +#define SSH2_MSG_USERAUTH_BANNER 53 + +/* user authentication: method specific, can be reused */ + +#define SSH2_MSG_USERAUTH_PK_OK 60 +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 + +/* connection protocol: generic */ + +#define SSH2_MSG_GLOBAL_REQUEST 80 +#define SSH2_MSG_REQUEST_SUCCESS 81 +#define SSH2_MSG_REQUEST_FAILURE 82 + +/* channel related messages */ + +#define SSH2_MSG_CHANNEL_OPEN 90 +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH2_MSG_CHANNEL_DATA 94 +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH2_MSG_CHANNEL_EOF 96 +#define SSH2_MSG_CHANNEL_CLOSE 97 +#define SSH2_MSG_CHANNEL_REQUEST 98 +#define SSH2_MSG_CHANNEL_SUCCESS 99 +#define SSH2_MSG_CHANNEL_FAILURE 100 + +/* disconnect reason code */ + +#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 +#define SSH2_DISCONNECT_RESERVED 4 +#define SSH2_DISCONNECT_MAC_ERROR 5 +#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH2_DISCONNECT_CONNECTION_LOST 10 +#define SSH2_DISCONNECT_BY_APPLICATION 11 +#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 + +/* misc */ + +#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH2_OPEN_CONNECT_FAILED 2 +#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH2_OPEN_RESOURCE_SHORTAGE 4 + +#define SSH2_EXTENDED_DATA_STDERR 1 + +/* Certificate types for OpenSSH certificate keys extension */ +#define SSH2_CERT_TYPE_USER 1 +#define SSH2_CERT_TYPE_HOST 2 diff --git a/aosp/external/openssh/ssh_api.c b/aosp/external/openssh/ssh_api.c new file mode 100644 index 0000000000000000000000000000000000000000..d3c6617616bdecbaf2c14858f32922298fe10893 --- /dev/null +++ b/aosp/external/openssh/ssh_api.c @@ -0,0 +1,570 @@ +/* $OpenBSD: ssh_api.c,v 1.27 2021/04/03 06:18:41 djm Exp $ */ +/* + * Copyright (c) 2012 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include +#include + +#include "ssh_api.h" +#include "compat.h" +#include "log.h" +#include "authfile.h" +#include "sshkey.h" +#include "misc.h" +#include "ssh2.h" +#include "version.h" +#include "myproposal.h" +#include "ssherr.h" +#include "sshbuf.h" + +#include "openbsd-compat/openssl-compat.h" + +#include + +int _ssh_exchange_banner(struct ssh *); +int _ssh_send_banner(struct ssh *, struct sshbuf *); +int _ssh_read_banner(struct ssh *, struct sshbuf *); +int _ssh_order_hostkeyalgs(struct ssh *); +int _ssh_verify_host_key(struct sshkey *, struct ssh *); +struct sshkey *_ssh_host_public_key(int, int, struct ssh *); +struct sshkey *_ssh_host_private_key(int, int, struct ssh *); +int _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *, + u_char **, size_t *, const u_char *, size_t, const char *); + +/* + * stubs for the server side implementation of kex. + * disable privsep so our stubs will never be called. + */ +int use_privsep = 0; +int mm_sshkey_sign(struct sshkey *, u_char **, u_int *, + const u_char *, u_int, const char *, const char *, const char *, u_int); + +#ifdef WITH_OPENSSL +DH *mm_choose_dh(int, int, int); +#endif + +int +mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp, + const u_char *data, u_int datalen, const char *alg, + const char *sk_provider, const char *sk_pin, u_int compat) +{ + return (-1); +} + +#ifdef WITH_OPENSSL +DH * +mm_choose_dh(int min, int nbits, int max) +{ + return (NULL); +} +#endif + +/* API */ + +int +ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params) +{ + char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; + struct ssh *ssh; + char **proposal; + static int called; + int r; + + if (!called) { + seed_rng(); + called = 1; + } + + if ((ssh = ssh_packet_set_connection(NULL, -1, -1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (is_server) + ssh_packet_set_server(ssh); + + /* Initialize key exchange */ + proposal = kex_params ? kex_params->proposal : myproposal; + if ((r = kex_ready(ssh, proposal)) != 0) { + ssh_free(ssh); + return r; + } + ssh->kex->server = is_server; + if (is_server) { +#ifdef WITH_OPENSSL + ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; + ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; + ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; + ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; + ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; + ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; +# ifdef OPENSSL_HAS_ECC + ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_server; +# endif +#endif /* WITH_OPENSSL */ + ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server; + ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; + ssh->kex->load_host_public_key=&_ssh_host_public_key; + ssh->kex->load_host_private_key=&_ssh_host_private_key; + ssh->kex->sign=&_ssh_host_key_sign; + } else { +#ifdef WITH_OPENSSL + ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; + ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; + ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; +# ifdef OPENSSL_HAS_ECC + ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; +# endif +#endif /* WITH_OPENSSL */ + ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; + ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; + ssh->kex->verify_host_key =&_ssh_verify_host_key; + } + *sshp = ssh; + return 0; +} + +void +ssh_free(struct ssh *ssh) +{ + struct key_entry *k; + + if (ssh == NULL) + return; + + /* + * we've only created the public keys variants in case we + * are a acting as a server. + */ + while ((k = TAILQ_FIRST(&ssh->public_keys)) != NULL) { + TAILQ_REMOVE(&ssh->public_keys, k, next); + if (ssh->kex && ssh->kex->server) + sshkey_free(k->key); + free(k); + } + while ((k = TAILQ_FIRST(&ssh->private_keys)) != NULL) { + TAILQ_REMOVE(&ssh->private_keys, k, next); + free(k); + } + ssh_packet_close(ssh); + free(ssh); +} + +void +ssh_set_app_data(struct ssh *ssh, void *app_data) +{ + ssh->app_data = app_data; +} + +void * +ssh_get_app_data(struct ssh *ssh) +{ + return ssh->app_data; +} + +/* Returns < 0 on error, 0 otherwise */ +int +ssh_add_hostkey(struct ssh *ssh, struct sshkey *key) +{ + struct sshkey *pubkey = NULL; + struct key_entry *k = NULL, *k_prv = NULL; + int r; + + if (ssh->kex->server) { + if ((r = sshkey_from_private(key, &pubkey)) != 0) + return r; + if ((k = malloc(sizeof(*k))) == NULL || + (k_prv = malloc(sizeof(*k_prv))) == NULL) { + free(k); + sshkey_free(pubkey); + return SSH_ERR_ALLOC_FAIL; + } + k_prv->key = key; + TAILQ_INSERT_TAIL(&ssh->private_keys, k_prv, next); + + /* add the public key, too */ + k->key = pubkey; + TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); + r = 0; + } else { + if ((k = malloc(sizeof(*k))) == NULL) + return SSH_ERR_ALLOC_FAIL; + k->key = key; + TAILQ_INSERT_TAIL(&ssh->public_keys, k, next); + r = 0; + } + + return r; +} + +int +ssh_set_verify_host_key_callback(struct ssh *ssh, + int (*cb)(struct sshkey *, struct ssh *)) +{ + if (cb == NULL || ssh->kex == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + ssh->kex->verify_host_key = cb; + + return 0; +} + +int +ssh_input_append(struct ssh *ssh, const u_char *data, size_t len) +{ + return sshbuf_put(ssh_packet_get_input(ssh), data, len); +} + +int +ssh_packet_next(struct ssh *ssh, u_char *typep) +{ + int r; + u_int32_t seqnr; + u_char type; + + /* + * Try to read a packet. Return SSH_MSG_NONE if no packet or not + * enough data. + */ + *typep = SSH_MSG_NONE; + if (sshbuf_len(ssh->kex->client_version) == 0 || + sshbuf_len(ssh->kex->server_version) == 0) + return _ssh_exchange_banner(ssh); + /* + * If we enough data and a dispatch function then + * call the function and get the next packet. + * Otherwise return the packet type to the caller so it + * can decide how to go on. + * + * We will only call the dispatch function for: + * 20-29 Algorithm negotiation + * 30-49 Key exchange method specific (numbers can be reused for + * different authentication methods) + */ + for (;;) { + if ((r = ssh_packet_read_poll2(ssh, &type, &seqnr)) != 0) + return r; + if (type > 0 && type < DISPATCH_MAX && + type >= SSH2_MSG_KEXINIT && type <= SSH2_MSG_TRANSPORT_MAX && + ssh->dispatch[type] != NULL) { + if ((r = (*ssh->dispatch[type])(type, seqnr, ssh)) != 0) + return r; + } else { + *typep = type; + return 0; + } + } +} + +const u_char * +ssh_packet_payload(struct ssh *ssh, size_t *lenp) +{ + return sshpkt_ptr(ssh, lenp); +} + +int +ssh_packet_put(struct ssh *ssh, int type, const u_char *data, size_t len) +{ + int r; + + if ((r = sshpkt_start(ssh, type)) != 0 || + (r = sshpkt_put(ssh, data, len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + return 0; +} + +const u_char * +ssh_output_ptr(struct ssh *ssh, size_t *len) +{ + struct sshbuf *output = ssh_packet_get_output(ssh); + + *len = sshbuf_len(output); + return sshbuf_ptr(output); +} + +int +ssh_output_consume(struct ssh *ssh, size_t len) +{ + return sshbuf_consume(ssh_packet_get_output(ssh), len); +} + +int +ssh_output_space(struct ssh *ssh, size_t len) +{ + return (0 == sshbuf_check_reserve(ssh_packet_get_output(ssh), len)); +} + +int +ssh_input_space(struct ssh *ssh, size_t len) +{ + return (0 == sshbuf_check_reserve(ssh_packet_get_input(ssh), len)); +} + +/* Read other side's version identification. */ +int +_ssh_read_banner(struct ssh *ssh, struct sshbuf *banner) +{ + struct sshbuf *input = ssh_packet_get_input(ssh); + const char *mismatch = "Protocol mismatch.\r\n"; + const u_char *s = sshbuf_ptr(input); + u_char c; + char *cp = NULL, *remote_version = NULL; + int r = 0, remote_major, remote_minor, expect_nl; + size_t n, j; + + for (j = n = 0;;) { + sshbuf_reset(banner); + expect_nl = 0; + for (;;) { + if (j >= sshbuf_len(input)) + return 0; /* insufficient data in input buf */ + c = s[j++]; + if (c == '\r') { + expect_nl = 1; + continue; + } + if (c == '\n') + break; + if (expect_nl) + goto bad; + if ((r = sshbuf_put_u8(banner, c)) != 0) + return r; + if (sshbuf_len(banner) > SSH_MAX_BANNER_LEN) + goto bad; + } + if (sshbuf_len(banner) >= 4 && + memcmp(sshbuf_ptr(banner), "SSH-", 4) == 0) + break; + debug_f("%.*s", (int)sshbuf_len(banner), + sshbuf_ptr(banner)); + /* Accept lines before banner only on client */ + if (ssh->kex->server || ++n > SSH_MAX_PRE_BANNER_LINES) { + bad: + if ((r = sshbuf_put(ssh_packet_get_output(ssh), + mismatch, strlen(mismatch))) != 0) + return r; + return SSH_ERR_NO_PROTOCOL_VERSION; + } + } + if ((r = sshbuf_consume(input, j)) != 0) + return r; + + /* XXX remote version must be the same size as banner for sscanf */ + if ((cp = sshbuf_dup_string(banner)) == NULL || + (remote_version = calloc(1, sshbuf_len(banner))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* + * Check that the versions match. In future this might accept + * several versions and set appropriate flags to handle them. + */ + if (sscanf(cp, "SSH-%d.%d-%[^\n]\n", + &remote_major, &remote_minor, remote_version) != 3) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + debug("Remote protocol version %d.%d, remote software version %.100s", + remote_major, remote_minor, remote_version); + + compat_banner(ssh, remote_version); + if (remote_major == 1 && remote_minor == 99) { + remote_major = 2; + remote_minor = 0; + } + if (remote_major != 2) + r = SSH_ERR_PROTOCOL_MISMATCH; + + debug("Remote version string %.100s", cp); + out: + free(cp); + free(remote_version); + return r; +} + +/* Send our own protocol version identification. */ +int +_ssh_send_banner(struct ssh *ssh, struct sshbuf *banner) +{ + char *cp; + int r; + + if ((r = sshbuf_putf(banner, "SSH-2.0-%.100s\r\n", SSH_VERSION)) != 0) + return r; + if ((r = sshbuf_putb(ssh_packet_get_output(ssh), banner)) != 0) + return r; + /* Remove trailing \r\n */ + if ((r = sshbuf_consume_end(banner, 2)) != 0) + return r; + if ((cp = sshbuf_dup_string(banner)) == NULL) + return SSH_ERR_ALLOC_FAIL; + debug("Local version string %.100s", cp); + free(cp); + return 0; +} + +int +_ssh_exchange_banner(struct ssh *ssh) +{ + struct kex *kex = ssh->kex; + int r; + + /* + * if _ssh_read_banner() cannot parse a full version string + * it will return NULL and we end up calling it again. + */ + + r = 0; + if (kex->server) { + if (sshbuf_len(ssh->kex->server_version) == 0) + r = _ssh_send_banner(ssh, ssh->kex->server_version); + if (r == 0 && + sshbuf_len(ssh->kex->server_version) != 0 && + sshbuf_len(ssh->kex->client_version) == 0) + r = _ssh_read_banner(ssh, ssh->kex->client_version); + } else { + if (sshbuf_len(ssh->kex->server_version) == 0) + r = _ssh_read_banner(ssh, ssh->kex->server_version); + if (r == 0 && + sshbuf_len(ssh->kex->server_version) != 0 && + sshbuf_len(ssh->kex->client_version) == 0) + r = _ssh_send_banner(ssh, ssh->kex->client_version); + } + if (r != 0) + return r; + /* start initial kex as soon as we have exchanged the banners */ + if (sshbuf_len(ssh->kex->server_version) != 0 && + sshbuf_len(ssh->kex->client_version) != 0) { + if ((r = _ssh_order_hostkeyalgs(ssh)) != 0 || + (r = kex_send_kexinit(ssh)) != 0) + return r; + } + return 0; +} + +struct sshkey * +_ssh_host_public_key(int type, int nid, struct ssh *ssh) +{ + struct key_entry *k; + + debug3_f("need %d", type); + TAILQ_FOREACH(k, &ssh->public_keys, next) { + debug3_f("check %s", sshkey_type(k->key)); + if (k->key->type == type && + (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) + return (k->key); + } + return (NULL); +} + +struct sshkey * +_ssh_host_private_key(int type, int nid, struct ssh *ssh) +{ + struct key_entry *k; + + debug3_f("need %d", type); + TAILQ_FOREACH(k, &ssh->private_keys, next) { + debug3_f("check %s", sshkey_type(k->key)); + if (k->key->type == type && + (type != KEY_ECDSA || k->key->ecdsa_nid == nid)) + return (k->key); + } + return (NULL); +} + +int +_ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh) +{ + struct key_entry *k; + + debug3_f("need %s", sshkey_type(hostkey)); + TAILQ_FOREACH(k, &ssh->public_keys, next) { + debug3_f("check %s", sshkey_type(k->key)); + if (sshkey_equal_public(hostkey, k->key)) + return (0); /* ok */ + } + return (-1); /* failed */ +} + +/* offer hostkey algorithms in kexinit depending on registered keys */ +int +_ssh_order_hostkeyalgs(struct ssh *ssh) +{ + struct key_entry *k; + char *orig, *avail, *oavail = NULL, *alg, *replace = NULL; + char **proposal; + size_t maxlen; + int ktype, r; + + /* XXX we de-serialize ssh->kex->my, modify it, and change it */ + if ((r = kex_buf2prop(ssh->kex->my, NULL, &proposal)) != 0) + return r; + orig = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; + if ((oavail = avail = strdup(orig)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + maxlen = strlen(avail) + 1; + if ((replace = calloc(1, maxlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + *replace = '\0'; + while ((alg = strsep(&avail, ",")) && *alg != '\0') { + if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) + continue; + TAILQ_FOREACH(k, &ssh->public_keys, next) { + if (k->key->type == ktype || + (sshkey_is_cert(k->key) && k->key->type == + sshkey_type_plain(ktype))) { + if (*replace != '\0') + strlcat(replace, ",", maxlen); + strlcat(replace, alg, maxlen); + break; + } + } + } + if (*replace != '\0') { + debug2_f("orig/%d %s", ssh->kex->server, orig); + debug2_f("replace/%d %s", ssh->kex->server, replace); + free(orig); + proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = replace; + replace = NULL; /* owned by proposal */ + r = kex_prop2buf(ssh->kex->my, proposal); + } + out: + free(oavail); + free(replace); + kex_prop_free(proposal); + return r; +} + +int +_ssh_host_key_sign(struct ssh *ssh, struct sshkey *privkey, + struct sshkey *pubkey, u_char **signature, size_t *slen, + const u_char *data, size_t dlen, const char *alg) +{ + return sshkey_sign(privkey, signature, slen, data, dlen, + alg, NULL, NULL, ssh->compat); +} diff --git a/aosp/external/openssh/ssh_api.h b/aosp/external/openssh/ssh_api.h new file mode 100644 index 0000000000000000000000000000000000000000..584f896a78c4f36d6aa5119faebe874d72a888d6 --- /dev/null +++ b/aosp/external/openssh/ssh_api.h @@ -0,0 +1,137 @@ +/* $OpenBSD: ssh_api.h,v 1.2 2018/04/10 00:10:49 djm Exp $ */ +/* + * Copyright (c) 2012 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef API_H +#define API_H + +#include +#include + +#include "openbsd-compat/sys-queue.h" + +#include "cipher.h" +#include "sshkey.h" +#include "kex.h" +#include "ssh.h" +#include "ssh2.h" +#include "packet.h" + +struct kex_params { + char *proposal[PROPOSAL_MAX]; +}; + +/* public SSH API functions */ + +/* + * ssh_init() create a ssh connection object with given (optional) + * key exchange parameters. + */ +int ssh_init(struct ssh **, int is_server, struct kex_params *kex_params); + +/* + * release ssh connection state. + */ +void ssh_free(struct ssh *); + +/* + * attach application specific data to the connection state + */ +void ssh_set_app_data(struct ssh *, void *); +void *ssh_get_app_data(struct ssh *); + +/* + * ssh_add_hostkey() registers a private/public hostkey for an ssh + * connection. + * ssh_add_hostkey() needs to be called before a key exchange is + * initiated with ssh_packet_next(). + * private hostkeys are required if we need to act as a server. + * public hostkeys are used to verify the servers hostkey. + */ +int ssh_add_hostkey(struct ssh *ssh, struct sshkey *key); + +/* + * ssh_set_verify_host_key_callback() registers a callback function + * which should be called instead of the default verification. The + * function given must return 0 if the hostkey is ok, -1 if the + * verification has failed. + */ +int ssh_set_verify_host_key_callback(struct ssh *ssh, + int (*cb)(struct sshkey *, struct ssh *)); + +/* + * ssh_packet_next() advances to the next input packet and returns + * the packet type in typep. + * ssh_packet_next() works by processing an input byte-stream, + * decrypting the received data and hiding the key-exchange from + * the caller. + * ssh_packet_next() sets typep if there is no new packet available. + * in this case the caller must fill the input byte-stream by passing + * the data received over network to ssh_input_append(). + * additionally, the caller needs to send the resulting output + * byte-stream back over the network. otherwise the key exchange + * would not proceed. the output byte-stream is accessed through + * ssh_output_ptr(). + */ +int ssh_packet_next(struct ssh *ssh, u_char *typep); + +/* + * ssh_packet_payload() returns a pointer to the raw payload data of + * the current input packet and the length of this payload. + * the payload is accessible until ssh_packet_next() is called again. + */ +const u_char *ssh_packet_payload(struct ssh *ssh, size_t *lenp); + +/* + * ssh_packet_put() creates an encrypted packet with the given type + * and payload. + * the encrypted packet is appended to the output byte-stream. + */ +int ssh_packet_put(struct ssh *ssh, int type, const u_char *data, + size_t len); + +/* + * ssh_input_space() checks if 'len' bytes can be appended to the + * input byte-stream. + */ +int ssh_input_space(struct ssh *ssh, size_t len); + +/* + * ssh_input_append() appends data to the input byte-stream. + */ +int ssh_input_append(struct ssh *ssh, const u_char *data, size_t len); + +/* + * ssh_output_space() checks if 'len' bytes can be appended to the + * output byte-stream. XXX + */ +int ssh_output_space(struct ssh *ssh, size_t len); + +/* + * ssh_output_ptr() retrieves both a pointer and the length of the + * current output byte-stream. the bytes need to be sent over the + * network. the number of bytes that have been successfully sent can + * be removed from the output byte-stream with ssh_output_consume(). + */ +const u_char *ssh_output_ptr(struct ssh *ssh, size_t *len); + +/* + * ssh_output_consume() removes the given number of bytes from + * the output byte-stream. + */ +int ssh_output_consume(struct ssh *ssh, size_t len); + +#endif diff --git a/aosp/external/openssh/ssh_config b/aosp/external/openssh/ssh_config new file mode 100644 index 0000000000000000000000000000000000000000..842ea866c72a620b06a1b99d38feca1afa64d523 --- /dev/null +++ b/aosp/external/openssh/ssh_config @@ -0,0 +1,46 @@ +# $OpenBSD: ssh_config,v 1.35 2020/07/17 03:43:42 dtucker Exp $ + +# This is the ssh client system-wide configuration file. See +# ssh_config(5) for more information. This file provides defaults for +# users, and the values can be changed in per-user configuration files +# or on the command line. + +# Configuration data is parsed as follows: +# 1. command line options +# 2. user-specific file +# 3. system-wide file +# Any configuration value is only changed the first time it is set. +# Thus, host-specific definitions should be at the beginning of the +# configuration file, and defaults at the end. + +# Site-wide defaults for some commonly used options. For a comprehensive +# list of available options, their meanings and defaults, please see the +# ssh_config(5) man page. + +# Host * +# ForwardAgent no +# ForwardX11 no +# PasswordAuthentication yes +# HostbasedAuthentication no +# GSSAPIAuthentication no +# GSSAPIDelegateCredentials no +# BatchMode no +# CheckHostIP yes +# AddressFamily any +# ConnectTimeout 0 +# StrictHostKeyChecking ask +# IdentityFile ~/.ssh/id_rsa +# IdentityFile ~/.ssh/id_dsa +# IdentityFile ~/.ssh/id_ecdsa +# IdentityFile ~/.ssh/id_ed25519 +# Port 22 +# Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc +# MACs hmac-md5,hmac-sha1,umac-64@openssh.com +# EscapeChar ~ +# Tunnel no +# TunnelDevice any:any +# PermitLocalCommand no +# VisualHostKey no +# ProxyCommand ssh -q -W %h:%p gateway.example.com +# RekeyLimit 1G 1h +# UserKnownHostsFile ~/.ssh/known_hosts.d/%k diff --git a/aosp/external/openssh/ssh_config.5 b/aosp/external/openssh/ssh_config.5 new file mode 100644 index 0000000000000000000000000000000000000000..59ff96465e1ce1fee39e9bb3cd4e491791b9df1a --- /dev/null +++ b/aosp/external/openssh/ssh_config.5 @@ -0,0 +1,2180 @@ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: ssh_config.5,v 1.371 2022/03/31 17:58:44 naddy Exp $ +.Dd $Mdocdate: March 31 2022 $ +.Dt SSH_CONFIG 5 +.Os +.Sh NAME +.Nm ssh_config +.Nd OpenSSH client configuration file +.Sh DESCRIPTION +.Xr ssh 1 +obtains configuration data from the following sources in +the following order: +.Pp +.Bl -enum -offset indent -compact +.It +command-line options +.It +user's configuration file +.Pq Pa ~/.ssh/config +.It +system-wide configuration file +.Pq Pa /etc/ssh/ssh_config +.El +.Pp +For each parameter, the first obtained value +will be used. +The configuration files contain sections separated by +.Cm Host +specifications, and that section is only applied for hosts that +match one of the patterns given in the specification. +The matched host name is usually the one given on the command line +(see the +.Cm CanonicalizeHostname +option for exceptions). +.Pp +Since the first obtained value for each parameter is used, more +host-specific declarations should be given near the beginning of the +file, and general defaults at the end. +.Pp +The file contains keyword-argument pairs, one per line. +Lines starting with +.Ql # +and empty lines are interpreted as comments. +Arguments may optionally be enclosed in double quotes +.Pq \&" +in order to represent arguments containing spaces. +Configuration options may be separated by whitespace or +optional whitespace and exactly one +.Ql = ; +the latter format is useful to avoid the need to quote whitespace +when specifying configuration options using the +.Nm ssh , +.Nm scp , +and +.Nm sftp +.Fl o +option. +.Pp +The possible +keywords and their meanings are as follows (note that +keywords are case-insensitive and arguments are case-sensitive): +.Bl -tag -width Ds +.It Cm Host +Restricts the following declarations (up to the next +.Cm Host +or +.Cm Match +keyword) to be only for those hosts that match one of the patterns +given after the keyword. +If more than one pattern is provided, they should be separated by whitespace. +A single +.Ql * +as a pattern can be used to provide global +defaults for all hosts. +The host is usually the +.Ar hostname +argument given on the command line +(see the +.Cm CanonicalizeHostname +keyword for exceptions). +.Pp +A pattern entry may be negated by prefixing it with an exclamation mark +.Pq Sq !\& . +If a negated entry is matched, then the +.Cm Host +entry is ignored, regardless of whether any other patterns on the line +match. +Negated matches are therefore useful to provide exceptions for wildcard +matches. +.Pp +See +.Sx PATTERNS +for more information on patterns. +.It Cm Match +Restricts the following declarations (up to the next +.Cm Host +or +.Cm Match +keyword) to be used only when the conditions following the +.Cm Match +keyword are satisfied. +Match conditions are specified using one or more criteria +or the single token +.Cm all +which always matches. +The available criteria keywords are: +.Cm canonical , +.Cm final , +.Cm exec , +.Cm host , +.Cm originalhost , +.Cm user , +and +.Cm localuser . +The +.Cm all +criteria must appear alone or immediately after +.Cm canonical +or +.Cm final . +Other criteria may be combined arbitrarily. +All criteria but +.Cm all , +.Cm canonical , +and +.Cm final +require an argument. +Criteria may be negated by prepending an exclamation mark +.Pq Sq !\& . +.Pp +The +.Cm canonical +keyword matches only when the configuration file is being re-parsed +after hostname canonicalization (see the +.Cm CanonicalizeHostname +option). +This may be useful to specify conditions that work with canonical host +names only. +.Pp +The +.Cm final +keyword requests that the configuration be re-parsed (regardless of whether +.Cm CanonicalizeHostname +is enabled), and matches only during this final pass. +If +.Cm CanonicalizeHostname +is enabled, then +.Cm canonical +and +.Cm final +match during the same pass. +.Pp +The +.Cm exec +keyword executes the specified command under the user's shell. +If the command returns a zero exit status then the condition is considered true. +Commands containing whitespace characters must be quoted. +Arguments to +.Cm exec +accept the tokens described in the +.Sx TOKENS +section. +.Pp +The other keywords' criteria must be single entries or comma-separated +lists and may use the wildcard and negation operators described in the +.Sx PATTERNS +section. +The criteria for the +.Cm host +keyword are matched against the target hostname, after any substitution +by the +.Cm Hostname +or +.Cm CanonicalizeHostname +options. +The +.Cm originalhost +keyword matches against the hostname as it was specified on the command-line. +The +.Cm user +keyword matches against the target username on the remote host. +The +.Cm localuser +keyword matches against the name of the local user running +.Xr ssh 1 +(this keyword may be useful in system-wide +.Nm +files). +.It Cm AddKeysToAgent +Specifies whether keys should be automatically added to a running +.Xr ssh-agent 1 . +If this option is set to +.Cm yes +and a key is loaded from a file, the key and its passphrase are added to +the agent with the default lifetime, as if by +.Xr ssh-add 1 . +If this option is set to +.Cm ask , +.Xr ssh 1 +will require confirmation using the +.Ev SSH_ASKPASS +program before adding a key (see +.Xr ssh-add 1 +for details). +If this option is set to +.Cm confirm , +each use of the key must be confirmed, as if the +.Fl c +option was specified to +.Xr ssh-add 1 . +If this option is set to +.Cm no , +no keys are added to the agent. +Alternately, this option may be specified as a time interval +using the format described in the +.Sx TIME FORMATS +section of +.Xr sshd_config 5 +to specify the key's lifetime in +.Xr ssh-agent 1 , +after which it will automatically be removed. +The argument must be +.Cm no +(the default), +.Cm yes , +.Cm confirm +(optionally followed by a time interval), +.Cm ask +or a time interval. +.It Cm AddressFamily +Specifies which address family to use when connecting. +Valid arguments are +.Cm any +(the default), +.Cm inet +(use IPv4 only), or +.Cm inet6 +(use IPv6 only). +.It Cm BatchMode +If set to +.Cm yes , +user interaction such as password prompts and host key confirmation requests +will be disabled. +This option is useful in scripts and other batch jobs where no user +is present to interact with +.Xr ssh 1 . +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm BindAddress +Use the specified address on the local machine as the source address of +the connection. +Only useful on systems with more than one address. +.It Cm BindInterface +Use the address of the specified interface on the local machine as the +source address of the connection. +.It Cm CanonicalDomains +When +.Cm CanonicalizeHostname +is enabled, this option specifies the list of domain suffixes in which to +search for the specified destination host. +.It Cm CanonicalizeFallbackLocal +Specifies whether to fail with an error when hostname canonicalization fails. +The default, +.Cm yes , +will attempt to look up the unqualified hostname using the system resolver's +search rules. +A value of +.Cm no +will cause +.Xr ssh 1 +to fail instantly if +.Cm CanonicalizeHostname +is enabled and the target hostname cannot be found in any of the domains +specified by +.Cm CanonicalDomains . +.It Cm CanonicalizeHostname +Controls whether explicit hostname canonicalization is performed. +The default, +.Cm no , +is not to perform any name rewriting and let the system resolver handle all +hostname lookups. +If set to +.Cm yes +then, for connections that do not use a +.Cm ProxyCommand +or +.Cm ProxyJump , +.Xr ssh 1 +will attempt to canonicalize the hostname specified on the command line +using the +.Cm CanonicalDomains +suffixes and +.Cm CanonicalizePermittedCNAMEs +rules. +If +.Cm CanonicalizeHostname +is set to +.Cm always , +then canonicalization is applied to proxied connections too. +.Pp +If this option is enabled, then the configuration files are processed +again using the new target name to pick up any new configuration in matching +.Cm Host +and +.Cm Match +stanzas. +A value of +.Cm none +disables the use of a +.Cm ProxyJump +host. +.It Cm CanonicalizeMaxDots +Specifies the maximum number of dot characters in a hostname before +canonicalization is disabled. +The default, 1, +allows a single dot (i.e. hostname.subdomain). +.It Cm CanonicalizePermittedCNAMEs +Specifies rules to determine whether CNAMEs should be followed when +canonicalizing hostnames. +The rules consist of one or more arguments of +.Ar source_domain_list : Ns Ar target_domain_list , +where +.Ar source_domain_list +is a pattern-list of domains that may follow CNAMEs in canonicalization, +and +.Ar target_domain_list +is a pattern-list of domains that they may resolve to. +.Pp +For example, +.Qq *.a.example.com:*.b.example.com,*.c.example.com +will allow hostnames matching +.Qq *.a.example.com +to be canonicalized to names in the +.Qq *.b.example.com +or +.Qq *.c.example.com +domains. +.Pp +A single argument of +.Qq none +causes no CNAMEs to be considered for canonicalization. +This is the default behaviour. +.It Cm CASignatureAlgorithms +Specifies which algorithms are allowed for signing of certificates +by certificate authorities (CAs). +The default is: +.Bd -literal -offset indent +ssh-ed25519,ecdsa-sha2-nistp256, +ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +If the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +.Pp +.Xr ssh 1 +will not accept host certificates signed using algorithms other than those +specified. +.It Cm CertificateFile +Specifies a file from which the user's certificate is read. +A corresponding private key must be provided separately in order +to use this certificate either +from an +.Cm IdentityFile +directive or +.Fl i +flag to +.Xr ssh 1 , +via +.Xr ssh-agent 1 , +or via a +.Cm PKCS11Provider +or +.Cm SecurityKeyProvider . +.Pp +Arguments to +.Cm CertificateFile +may use the tilde syntax to refer to a user's home directory, +the tokens described in the +.Sx TOKENS +section and environment variables as described in the +.Sx ENVIRONMENT VARIABLES +section. +.Pp +It is possible to have multiple certificate files specified in +configuration files; these certificates will be tried in sequence. +Multiple +.Cm CertificateFile +directives will add to the list of certificates used for +authentication. +.It Cm CheckHostIP +If set to +.Cm yes , +.Xr ssh 1 +will additionally check the host IP address in the +.Pa known_hosts +file. +This allows it to detect if a host key changed due to DNS spoofing +and will add addresses of destination hosts to +.Pa ~/.ssh/known_hosts +in the process, regardless of the setting of +.Cm StrictHostKeyChecking . +If the option is set to +.Cm no +(the default), +the check will not be executed. +.It Cm Ciphers +Specifies the ciphers allowed and their order of preference. +Multiple ciphers must be comma-separated. +If the specified list begins with a +.Sq + +character, then the specified ciphers will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified ciphers (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified ciphers will be placed at the head of the +default set. +.Pp +The supported ciphers are: +.Bd -literal -offset indent +3des-cbc +aes128-cbc +aes192-cbc +aes256-cbc +aes128-ctr +aes192-ctr +aes256-ctr +aes128-gcm@openssh.com +aes256-gcm@openssh.com +chacha20-poly1305@openssh.com +.Ed +.Pp +The default is: +.Bd -literal -offset indent +chacha20-poly1305@openssh.com, +aes128-ctr,aes192-ctr,aes256-ctr, +aes128-gcm@openssh.com,aes256-gcm@openssh.com +.Ed +.Pp +The list of available ciphers may also be obtained using +.Qq ssh -Q cipher . +.It Cm ClearAllForwardings +Specifies that all local, remote, and dynamic port forwardings +specified in the configuration files or on the command line be +cleared. +This option is primarily useful when used from the +.Xr ssh 1 +command line to clear port forwardings set in +configuration files, and is automatically set by +.Xr scp 1 +and +.Xr sftp 1 . +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm Compression +Specifies whether to use compression. +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm ConnectionAttempts +Specifies the number of tries (one per second) to make before exiting. +The argument must be an integer. +This may be useful in scripts if the connection sometimes fails. +The default is 1. +.It Cm ConnectTimeout +Specifies the timeout (in seconds) used when connecting to the +SSH server, instead of using the default system TCP timeout. +This timeout is applied both to establishing the connection and to performing +the initial SSH protocol handshake and key exchange. +.It Cm ControlMaster +Enables the sharing of multiple sessions over a single network connection. +When set to +.Cm yes , +.Xr ssh 1 +will listen for connections on a control socket specified using the +.Cm ControlPath +argument. +Additional sessions can connect to this socket using the same +.Cm ControlPath +with +.Cm ControlMaster +set to +.Cm no +(the default). +These sessions will try to reuse the master instance's network connection +rather than initiating new ones, but will fall back to connecting normally +if the control socket does not exist, or is not listening. +.Pp +Setting this to +.Cm ask +will cause +.Xr ssh 1 +to listen for control connections, but require confirmation using +.Xr ssh-askpass 1 . +If the +.Cm ControlPath +cannot be opened, +.Xr ssh 1 +will continue without connecting to a master instance. +.Pp +X11 and +.Xr ssh-agent 1 +forwarding is supported over these multiplexed connections, however the +display and agent forwarded will be the one belonging to the master +connection i.e. it is not possible to forward multiple displays or agents. +.Pp +Two additional options allow for opportunistic multiplexing: try to use a +master connection but fall back to creating a new one if one does not already +exist. +These options are: +.Cm auto +and +.Cm autoask . +The latter requires confirmation like the +.Cm ask +option. +.It Cm ControlPath +Specify the path to the control socket used for connection sharing as described +in the +.Cm ControlMaster +section above or the string +.Cm none +to disable connection sharing. +Arguments to +.Cm ControlPath +may use the tilde syntax to refer to a user's home directory, +the tokens described in the +.Sx TOKENS +section and environment variables as described in the +.Sx ENVIRONMENT VARIABLES +section. +It is recommended that any +.Cm ControlPath +used for opportunistic connection sharing include +at least %h, %p, and %r (or alternatively %C) and be placed in a directory +that is not writable by other users. +This ensures that shared connections are uniquely identified. +.It Cm ControlPersist +When used in conjunction with +.Cm ControlMaster , +specifies that the master connection should remain open +in the background (waiting for future client connections) +after the initial client connection has been closed. +If set to +.Cm no +(the default), +then the master connection will not be placed into the background, +and will close as soon as the initial client connection is closed. +If set to +.Cm yes +or 0, +then the master connection will remain in the background indefinitely +(until killed or closed via a mechanism such as the +.Qq ssh -O exit ) . +If set to a time in seconds, or a time in any of the formats documented in +.Xr sshd_config 5 , +then the backgrounded master connection will automatically terminate +after it has remained idle (with no client connections) for the +specified time. +.It Cm DynamicForward +Specifies that a TCP port on the local machine be forwarded +over the secure channel, and the application +protocol is then used to determine where to connect to from the +remote machine. +.Pp +The argument must be +.Sm off +.Oo Ar bind_address : Oc Ar port . +.Sm on +IPv6 addresses can be specified by enclosing addresses in square brackets. +By default, the local port is bound in accordance with the +.Cm GatewayPorts +setting. +However, an explicit +.Ar bind_address +may be used to bind the connection to a specific address. +The +.Ar bind_address +of +.Cm localhost +indicates that the listening port be bound for local use only, while an +empty address or +.Sq * +indicates that the port should be available from all interfaces. +.Pp +Currently the SOCKS4 and SOCKS5 protocols are supported, and +.Xr ssh 1 +will act as a SOCKS server. +Multiple forwardings may be specified, and +additional forwardings can be given on the command line. +Only the superuser can forward privileged ports. +.It Cm EnableSSHKeysign +Setting this option to +.Cm yes +in the global client configuration file +.Pa /etc/ssh/ssh_config +enables the use of the helper program +.Xr ssh-keysign 8 +during +.Cm HostbasedAuthentication . +The argument must be +.Cm yes +or +.Cm no +(the default). +This option should be placed in the non-hostspecific section. +See +.Xr ssh-keysign 8 +for more information. +.It Cm EscapeChar +Sets the escape character (default: +.Ql ~ ) . +The escape character can also +be set on the command line. +The argument should be a single character, +.Ql ^ +followed by a letter, or +.Cm none +to disable the escape +character entirely (making the connection transparent for binary +data). +.It Cm ExitOnForwardFailure +Specifies whether +.Xr ssh 1 +should terminate the connection if it cannot set up all requested +dynamic, tunnel, local, and remote port forwardings, (e.g.\& +if either end is unable to bind and listen on a specified port). +Note that +.Cm ExitOnForwardFailure +does not apply to connections made over port forwardings and will not, +for example, cause +.Xr ssh 1 +to exit if TCP connections to the ultimate forwarding destination fail. +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm FingerprintHash +Specifies the hash algorithm used when displaying key fingerprints. +Valid options are: +.Cm md5 +and +.Cm sha256 +(the default). +.It Cm ForkAfterAuthentication +Requests +.Nm ssh +to go to background just before command execution. +This is useful if +.Nm ssh +is going to ask for passwords or passphrases, but the user +wants it in the background. +This implies the +.Cm StdinNull +configuration option being set to +.Dq yes . +The recommended way to start X11 programs at a remote site is with +something like +.Ic ssh -f host xterm , +which is the same as +.Ic ssh host xterm +if the +.Cm ForkAfterAuthentication +configuration option is set to +.Dq yes . +.Pp +If the +.Cm ExitOnForwardFailure +configuration option is set to +.Dq yes , +then a client started with the +.Cm ForkAfterAuthentication +configuration option being set to +.Dq yes +will wait for all remote port forwards to be successfully established +before placing itself in the background. +The argument to this keyword must be +.Cm yes +(same as the +.Fl f +option) or +.Cm no +(the default). +.It Cm ForwardAgent +Specifies whether the connection to the authentication agent (if any) +will be forwarded to the remote machine. +The argument may be +.Cm yes , +.Cm no +(the default), +an explicit path to an agent socket or the name of an environment variable +(beginning with +.Sq $ ) +in which to find the path. +.Pp +Agent forwarding should be enabled with caution. +Users with the ability to bypass file permissions on the remote host +(for the agent's Unix-domain socket) +can access the local agent through the forwarded connection. +An attacker cannot obtain key material from the agent, +however they can perform operations on the keys that enable them to +authenticate using the identities loaded into the agent. +.It Cm ForwardX11 +Specifies whether X11 connections will be automatically redirected +over the secure channel and +.Ev DISPLAY +set. +The argument must be +.Cm yes +or +.Cm no +(the default). +.Pp +X11 forwarding should be enabled with caution. +Users with the ability to bypass file permissions on the remote host +(for the user's X11 authorization database) +can access the local X11 display through the forwarded connection. +An attacker may then be able to perform activities such as keystroke monitoring +if the +.Cm ForwardX11Trusted +option is also enabled. +.It Cm ForwardX11Timeout +Specify a timeout for untrusted X11 forwarding +using the format described in the +.Sx TIME FORMATS +section of +.Xr sshd_config 5 . +X11 connections received by +.Xr ssh 1 +after this time will be refused. +Setting +.Cm ForwardX11Timeout +to zero will disable the timeout and permit X11 forwarding for the life +of the connection. +The default is to disable untrusted X11 forwarding after twenty minutes has +elapsed. +.It Cm ForwardX11Trusted +If this option is set to +.Cm yes , +remote X11 clients will have full access to the original X11 display. +.Pp +If this option is set to +.Cm no +(the default), +remote X11 clients will be considered untrusted and prevented +from stealing or tampering with data belonging to trusted X11 +clients. +Furthermore, the +.Xr xauth 1 +token used for the session will be set to expire after 20 minutes. +Remote clients will be refused access after this time. +.Pp +See the X11 SECURITY extension specification for full details on +the restrictions imposed on untrusted clients. +.It Cm GatewayPorts +Specifies whether remote hosts are allowed to connect to local +forwarded ports. +By default, +.Xr ssh 1 +binds local port forwardings to the loopback address. +This prevents other remote hosts from connecting to forwarded ports. +.Cm GatewayPorts +can be used to specify that ssh +should bind local port forwardings to the wildcard address, +thus allowing remote hosts to connect to forwarded ports. +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm GlobalKnownHostsFile +Specifies one or more files to use for the global +host key database, separated by whitespace. +The default is +.Pa /etc/ssh/ssh_known_hosts , +.Pa /etc/ssh/ssh_known_hosts2 . +.It Cm GSSAPIAuthentication +Specifies whether user authentication based on GSSAPI is allowed. +The default is +.Cm no . +.It Cm GSSAPIDelegateCredentials +Forward (delegate) credentials to the server. +The default is +.Cm no . +.It Cm HashKnownHosts +Indicates that +.Xr ssh 1 +should hash host names and addresses when they are added to +.Pa ~/.ssh/known_hosts . +These hashed names may be used normally by +.Xr ssh 1 +and +.Xr sshd 8 , +but they do not visually reveal identifying information if the +file's contents are disclosed. +The default is +.Cm no . +Note that existing names and addresses in known hosts files +will not be converted automatically, +but may be manually hashed using +.Xr ssh-keygen 1 . +.It Cm HostbasedAcceptedAlgorithms +Specifies the signature algorithms that will be used for hostbased +authentication as a comma-separated list of patterns. +Alternately if the specified list begins with a +.Sq + +character, then the specified signature algorithms will be appended +to the default set instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified signature algorithms (including wildcards) +will be removed from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified signature algorithms will be placed +at the head of the default set. +The default for this option is: +.Bd -literal -offset 3n +ssh-ed25519-cert-v01@openssh.com, +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +sk-ssh-ed25519-cert-v01@openssh.com, +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, +rsa-sha2-512-cert-v01@openssh.com, +rsa-sha2-256-cert-v01@openssh.com, +ssh-ed25519, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +The +.Fl Q +option of +.Xr ssh 1 +may be used to list supported signature algorithms. +This was formerly named HostbasedKeyTypes. +.It Cm HostbasedAuthentication +Specifies whether to try rhosts based authentication with public key +authentication. +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm HostKeyAlgorithms +Specifies the host key signature algorithms +that the client wants to use in order of preference. +Alternately if the specified list begins with a +.Sq + +character, then the specified signature algorithms will be appended to +the default set instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified signature algorithms (including wildcards) +will be removed from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified signature algorithms will be placed +at the head of the default set. +The default for this option is: +.Bd -literal -offset 3n +ssh-ed25519-cert-v01@openssh.com, +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +sk-ssh-ed25519-cert-v01@openssh.com, +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, +rsa-sha2-512-cert-v01@openssh.com, +rsa-sha2-256-cert-v01@openssh.com, +ssh-ed25519, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ecdsa-sha2-nistp256@openssh.com, +sk-ssh-ed25519@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +If hostkeys are known for the destination host then this default is modified +to prefer their algorithms. +.Pp +The list of available signature algorithms may also be obtained using +.Qq ssh -Q HostKeyAlgorithms . +.It Cm HostKeyAlias +Specifies an alias that should be used instead of the +real host name when looking up or saving the host key +in the host key database files and when validating host certificates. +This option is useful for tunneling SSH connections +or for multiple servers running on a single host. +.It Cm Hostname +Specifies the real host name to log into. +This can be used to specify nicknames or abbreviations for hosts. +Arguments to +.Cm Hostname +accept the tokens described in the +.Sx TOKENS +section. +Numeric IP addresses are also permitted (both on the command line and in +.Cm Hostname +specifications). +The default is the name given on the command line. +.It Cm IdentitiesOnly +Specifies that +.Xr ssh 1 +should only use the configured authentication identity and certificate files +(either the default files, or those explicitly configured in the +.Nm +files +or passed on the +.Xr ssh 1 +command-line), +even if +.Xr ssh-agent 1 +or a +.Cm PKCS11Provider +or +.Cm SecurityKeyProvider +offers more identities. +The argument to this keyword must be +.Cm yes +or +.Cm no +(the default). +This option is intended for situations where ssh-agent +offers many different identities. +.It Cm IdentityAgent +Specifies the +.Ux Ns -domain +socket used to communicate with the authentication agent. +.Pp +This option overrides the +.Ev SSH_AUTH_SOCK +environment variable and can be used to select a specific agent. +Setting the socket name to +.Cm none +disables the use of an authentication agent. +If the string +.Qq SSH_AUTH_SOCK +is specified, the location of the socket will be read from the +.Ev SSH_AUTH_SOCK +environment variable. +Otherwise if the specified value begins with a +.Sq $ +character, then it will be treated as an environment variable containing +the location of the socket. +.Pp +Arguments to +.Cm IdentityAgent +may use the tilde syntax to refer to a user's home directory, +the tokens described in the +.Sx TOKENS +section and environment variables as described in the +.Sx ENVIRONMENT VARIABLES +section. +.It Cm IdentityFile +Specifies a file from which the user's DSA, ECDSA, authenticator-hosted ECDSA, +Ed25519, authenticator-hosted Ed25519 or RSA authentication identity is read. +The default is +.Pa ~/.ssh/id_rsa , +.Pa ~/.ssh/id_ecdsa , +.Pa ~/.ssh/id_ecdsa_sk , +.Pa ~/.ssh/id_ed25519 , +.Pa ~/.ssh/id_ed25519_sk +and +.Pa ~/.ssh/id_dsa . +Additionally, any identities represented by the authentication agent +will be used for authentication unless +.Cm IdentitiesOnly +is set. +If no certificates have been explicitly specified by +.Cm CertificateFile , +.Xr ssh 1 +will try to load certificate information from the filename obtained by +appending +.Pa -cert.pub +to the path of a specified +.Cm IdentityFile . +.Pp +Arguments to +.Cm IdentityFile +may use the tilde syntax to refer to a user's home directory +or the tokens described in the +.Sx TOKENS +section. +.Pp +It is possible to have +multiple identity files specified in configuration files; all these +identities will be tried in sequence. +Multiple +.Cm IdentityFile +directives will add to the list of identities tried (this behaviour +differs from that of other configuration directives). +.Pp +.Cm IdentityFile +may be used in conjunction with +.Cm IdentitiesOnly +to select which identities in an agent are offered during authentication. +.Cm IdentityFile +may also be used in conjunction with +.Cm CertificateFile +in order to provide any certificate also needed for authentication with +the identity. +.It Cm IgnoreUnknown +Specifies a pattern-list of unknown options to be ignored if they are +encountered in configuration parsing. +This may be used to suppress errors if +.Nm +contains options that are unrecognised by +.Xr ssh 1 . +It is recommended that +.Cm IgnoreUnknown +be listed early in the configuration file as it will not be applied +to unknown options that appear before it. +.It Cm Include +Include the specified configuration file(s). +Multiple pathnames may be specified and each pathname may contain +.Xr glob 7 +wildcards and, for user configurations, shell-like +.Sq ~ +references to user home directories. +Wildcards will be expanded and processed in lexical order. +Files without absolute paths are assumed to be in +.Pa ~/.ssh +if included in a user configuration file or +.Pa /etc/ssh +if included from the system configuration file. +.Cm Include +directive may appear inside a +.Cm Match +or +.Cm Host +block +to perform conditional inclusion. +.It Cm IPQoS +Specifies the IPv4 type-of-service or DSCP class for connections. +Accepted values are +.Cm af11 , +.Cm af12 , +.Cm af13 , +.Cm af21 , +.Cm af22 , +.Cm af23 , +.Cm af31 , +.Cm af32 , +.Cm af33 , +.Cm af41 , +.Cm af42 , +.Cm af43 , +.Cm cs0 , +.Cm cs1 , +.Cm cs2 , +.Cm cs3 , +.Cm cs4 , +.Cm cs5 , +.Cm cs6 , +.Cm cs7 , +.Cm ef , +.Cm le , +.Cm lowdelay , +.Cm throughput , +.Cm reliability , +a numeric value, or +.Cm none +to use the operating system default. +This option may take one or two arguments, separated by whitespace. +If one argument is specified, it is used as the packet class unconditionally. +If two values are specified, the first is automatically selected for +interactive sessions and the second for non-interactive sessions. +The default is +.Cm af21 +(Low-Latency Data) +for interactive sessions and +.Cm cs1 +(Lower Effort) +for non-interactive sessions. +.It Cm KbdInteractiveAuthentication +Specifies whether to use keyboard-interactive authentication. +The argument to this keyword must be +.Cm yes +(the default) +or +.Cm no . +.Cm ChallengeResponseAuthentication +is a deprecated alias for this. +.It Cm KbdInteractiveDevices +Specifies the list of methods to use in keyboard-interactive authentication. +Multiple method names must be comma-separated. +The default is to use the server specified list. +The methods available vary depending on what the server supports. +For an OpenSSH server, +it may be zero or more of: +.Cm bsdauth +and +.Cm pam . +.It Cm KexAlgorithms +Specifies the available KEX (Key Exchange) algorithms. +Multiple algorithms must be comma-separated. +If the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified algorithms will be placed at the head of the +default set. +The default is: +.Bd -literal -offset indent +sntrup761x25519-sha512@openssh.com, +curve25519-sha256,curve25519-sha256@libssh.org, +ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, +diffie-hellman-group-exchange-sha256, +diffie-hellman-group16-sha512, +diffie-hellman-group18-sha512, +diffie-hellman-group14-sha256 +.Ed +.Pp +The list of available key exchange algorithms may also be obtained using +.Qq ssh -Q kex . +.It Cm KnownHostsCommand +Specifies a command to use to obtain a list of host keys, in addition to +those listed in +.Cm UserKnownHostsFile +and +.Cm GlobalKnownHostsFile . +This command is executed after the files have been read. +It may write host key lines to standard output in identical format to the +usual files (described in the +.Sx VERIFYING HOST KEYS +section in +.Xr ssh 1 ) . +Arguments to +.Cm KnownHostsCommand +accept the tokens described in the +.Sx TOKENS +section. +The command may be invoked multiple times per connection: once when preparing +the preference list of host key algorithms to use, again to obtain the +host key for the requested host name and, if +.Cm CheckHostIP +is enabled, one more time to obtain the host key matching the server's +address. +If the command exits abnormally or returns a non-zero exit status then the +connection is terminated. +.It Cm LocalCommand +Specifies a command to execute on the local machine after successfully +connecting to the server. +The command string extends to the end of the line, and is executed with +the user's shell. +Arguments to +.Cm LocalCommand +accept the tokens described in the +.Sx TOKENS +section. +.Pp +The command is run synchronously and does not have access to the +session of the +.Xr ssh 1 +that spawned it. +It should not be used for interactive commands. +.Pp +This directive is ignored unless +.Cm PermitLocalCommand +has been enabled. +.It Cm LocalForward +Specifies that a TCP port on the local machine be forwarded over +the secure channel to the specified host and port from the remote machine. +The first argument specifies the listener and may be +.Sm off +.Oo Ar bind_address : Oc Ar port +.Sm on +or a Unix domain socket path. +The second argument is the destination and may be +.Ar host : Ns Ar hostport +or a Unix domain socket path if the remote host supports it. +.Pp +IPv6 addresses can be specified by enclosing addresses in square brackets. +Multiple forwardings may be specified, and additional forwardings can be +given on the command line. +Only the superuser can forward privileged ports. +By default, the local port is bound in accordance with the +.Cm GatewayPorts +setting. +However, an explicit +.Ar bind_address +may be used to bind the connection to a specific address. +The +.Ar bind_address +of +.Cm localhost +indicates that the listening port be bound for local use only, while an +empty address or +.Sq * +indicates that the port should be available from all interfaces. +Unix domain socket paths may use the tokens described in the +.Sx TOKENS +section and environment variables as described in the +.Sx ENVIRONMENT VARIABLES +section. +.It Cm LogLevel +Gives the verbosity level that is used when logging messages from +.Xr ssh 1 . +The possible values are: +QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. +The default is INFO. +DEBUG and DEBUG1 are equivalent. +DEBUG2 and DEBUG3 each specify higher levels of verbose output. +.It Cm LogVerbose +Specify one or more overrides to LogLevel. +An override consists of a pattern lists that matches the source file, function +and line number to force detailed logging for. +For example, an override pattern of: +.Bd -literal -offset indent +kex.c:*:1000,*:kex_exchange_identification():*,packet.c:* +.Ed +.Pp +would enable detailed logging for line 1000 of +.Pa kex.c , +everything in the +.Fn kex_exchange_identification +function, and all code in the +.Pa packet.c +file. +This option is intended for debugging and no overrides are enabled by default. +.It Cm MACs +Specifies the MAC (message authentication code) algorithms +in order of preference. +The MAC algorithm is used for data integrity protection. +Multiple algorithms must be comma-separated. +If the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified algorithms will be placed at the head of the +default set. +.Pp +The algorithms that contain +.Qq -etm +calculate the MAC after encryption (encrypt-then-mac). +These are considered safer and their use recommended. +.Pp +The default is: +.Bd -literal -offset indent +umac-64-etm@openssh.com,umac-128-etm@openssh.com, +hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, +hmac-sha1-etm@openssh.com, +umac-64@openssh.com,umac-128@openssh.com, +hmac-sha2-256,hmac-sha2-512,hmac-sha1 +.Ed +.Pp +The list of available MAC algorithms may also be obtained using +.Qq ssh -Q mac . +.It Cm NoHostAuthenticationForLocalhost +Disable host authentication for localhost (loopback addresses). +The argument to this keyword must be +.Cm yes +or +.Cm no +(the default). +.It Cm NumberOfPasswordPrompts +Specifies the number of password prompts before giving up. +The argument to this keyword must be an integer. +The default is 3. +.It Cm PasswordAuthentication +Specifies whether to use password authentication. +The argument to this keyword must be +.Cm yes +(the default) +or +.Cm no . +.It Cm PermitLocalCommand +Allow local command execution via the +.Ic LocalCommand +option or using the +.Ic !\& Ns Ar command +escape sequence in +.Xr ssh 1 . +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm PermitRemoteOpen +Specifies the destinations to which remote TCP port forwarding is permitted when +.Cm RemoteForward +is used as a SOCKS proxy. +The forwarding specification must be one of the following forms: +.Pp +.Bl -item -offset indent -compact +.It +.Cm PermitRemoteOpen +.Sm off +.Ar host : port +.Sm on +.It +.Cm PermitRemoteOpen +.Sm off +.Ar IPv4_addr : port +.Sm on +.It +.Cm PermitRemoteOpen +.Sm off +.Ar \&[ IPv6_addr \&] : port +.Sm on +.El +.Pp +Multiple forwards may be specified by separating them with whitespace. +An argument of +.Cm any +can be used to remove all restrictions and permit any forwarding requests. +An argument of +.Cm none +can be used to prohibit all forwarding requests. +The wildcard +.Sq * +can be used for host or port to allow all hosts or ports respectively. +Otherwise, no pattern matching or address lookups are performed on supplied +names. +.It Cm PKCS11Provider +Specifies which PKCS#11 provider to use or +.Cm none +to indicate that no provider should be used (the default). +The argument to this keyword is a path to the PKCS#11 shared library +.Xr ssh 1 +should use to communicate with a PKCS#11 token providing keys for user +authentication. +.It Cm Port +Specifies the port number to connect on the remote host. +The default is 22. +.It Cm PreferredAuthentications +Specifies the order in which the client should try authentication methods. +This allows a client to prefer one method (e.g.\& +.Cm keyboard-interactive ) +over another method (e.g.\& +.Cm password ) . +The default is: +.Bd -literal -offset indent +gssapi-with-mic,hostbased,publickey, +keyboard-interactive,password +.Ed +.It Cm ProxyCommand +Specifies the command to use to connect to the server. +The command +string extends to the end of the line, and is executed +using the user's shell +.Ql exec +directive to avoid a lingering shell process. +.Pp +Arguments to +.Cm ProxyCommand +accept the tokens described in the +.Sx TOKENS +section. +The command can be basically anything, +and should read from its standard input and write to its standard output. +It should eventually connect an +.Xr sshd 8 +server running on some machine, or execute +.Ic sshd -i +somewhere. +Host key management will be done using the +.Cm Hostname +of the host being connected (defaulting to the name typed by the user). +Setting the command to +.Cm none +disables this option entirely. +Note that +.Cm CheckHostIP +is not available for connects with a proxy command. +.Pp +This directive is useful in conjunction with +.Xr nc 1 +and its proxy support. +For example, the following directive would connect via an HTTP proxy at +192.0.2.0: +.Bd -literal -offset 3n +ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p +.Ed +.It Cm ProxyJump +Specifies one or more jump proxies as either +.Xo +.Sm off +.Op Ar user No @ +.Ar host +.Op : Ns Ar port +.Sm on +or an ssh URI +.Xc . +Multiple proxies may be separated by comma characters and will be visited +sequentially. +Setting this option will cause +.Xr ssh 1 +to connect to the target host by first making a +.Xr ssh 1 +connection to the specified +.Cm ProxyJump +host and then establishing a +TCP forwarding to the ultimate target from there. +Setting the host to +.Cm none +disables this option entirely. +.Pp +Note that this option will compete with the +.Cm ProxyCommand +option - whichever is specified first will prevent later instances of the +other from taking effect. +.Pp +Note also that the configuration for the destination host (either supplied +via the command-line or the configuration file) is not generally applied +to jump hosts. +.Pa ~/.ssh/config +should be used if specific configuration is required for jump hosts. +.It Cm ProxyUseFdpass +Specifies that +.Cm ProxyCommand +will pass a connected file descriptor back to +.Xr ssh 1 +instead of continuing to execute and pass data. +The default is +.Cm no . +.It Cm PubkeyAcceptedAlgorithms +Specifies the signature algorithms that will be used for public key +authentication as a comma-separated list of patterns. +If the specified list begins with a +.Sq + +character, then the algorithms after it will be appended to the default +instead of replacing it. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified algorithms will be placed at the head of the +default set. +The default for this option is: +.Bd -literal -offset 3n +ssh-ed25519-cert-v01@openssh.com, +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +sk-ssh-ed25519-cert-v01@openssh.com, +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, +rsa-sha2-512-cert-v01@openssh.com, +rsa-sha2-256-cert-v01@openssh.com, +ssh-ed25519, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +The list of available signature algorithms may also be obtained using +.Qq ssh -Q PubkeyAcceptedAlgorithms . +.It Cm PubkeyAuthentication +Specifies whether to try public key authentication. +The argument to this keyword must be +.Cm yes +(the default), +.Cm no , +.Cm unbound +or +.Cm host-bound . +The final two options enable public key authentication while respectively +disabling or enabling the OpenSSH host-bound authentication protocol +extension required for restricted +.Xr ssh-agent 1 +forwarding. +.It Cm RekeyLimit +Specifies the maximum amount of data that may be transmitted before the +session key is renegotiated, optionally followed by a maximum amount of +time that may pass before the session key is renegotiated. +The first argument is specified in bytes and may have a suffix of +.Sq K , +.Sq M , +or +.Sq G +to indicate Kilobytes, Megabytes, or Gigabytes, respectively. +The default is between +.Sq 1G +and +.Sq 4G , +depending on the cipher. +The optional second value is specified in seconds and may use any of the +units documented in the TIME FORMATS section of +.Xr sshd_config 5 . +The default value for +.Cm RekeyLimit +is +.Cm default none , +which means that rekeying is performed after the cipher's default amount +of data has been sent or received and no time based rekeying is done. +.It Cm RemoteCommand +Specifies a command to execute on the remote machine after successfully +connecting to the server. +The command string extends to the end of the line, and is executed with +the user's shell. +Arguments to +.Cm RemoteCommand +accept the tokens described in the +.Sx TOKENS +section. +.It Cm RemoteForward +Specifies that a TCP port on the remote machine be forwarded over +the secure channel. +The remote port may either be forwarded to a specified host and port +from the local machine, or may act as a SOCKS 4/5 proxy that allows a remote +client to connect to arbitrary destinations from the local machine. +The first argument is the listening specification and may be +.Sm off +.Oo Ar bind_address : Oc Ar port +.Sm on +or, if the remote host supports it, a Unix domain socket path. +If forwarding to a specific destination then the second argument must be +.Ar host : Ns Ar hostport +or a Unix domain socket path, +otherwise if no destination argument is specified then the remote forwarding +will be established as a SOCKS proxy. +When acting as a SOCKS proxy, the destination of the connection can be +restricted by +.Cm PermitRemoteOpen . +.Pp +IPv6 addresses can be specified by enclosing addresses in square brackets. +Multiple forwardings may be specified, and additional +forwardings can be given on the command line. +Privileged ports can be forwarded only when +logging in as root on the remote machine. +Unix domain socket paths may use the tokens described in the +.Sx TOKENS +section and environment variables as described in the +.Sx ENVIRONMENT VARIABLES +section. +.Pp +If the +.Ar port +argument is 0, +the listen port will be dynamically allocated on the server and reported +to the client at run time. +.Pp +If the +.Ar bind_address +is not specified, the default is to only bind to loopback addresses. +If the +.Ar bind_address +is +.Ql * +or an empty string, then the forwarding is requested to listen on all +interfaces. +Specifying a remote +.Ar bind_address +will only succeed if the server's +.Cm GatewayPorts +option is enabled (see +.Xr sshd_config 5 ) . +.It Cm RequestTTY +Specifies whether to request a pseudo-tty for the session. +The argument may be one of: +.Cm no +(never request a TTY), +.Cm yes +(always request a TTY when standard input is a TTY), +.Cm force +(always request a TTY) or +.Cm auto +(request a TTY when opening a login session). +This option mirrors the +.Fl t +and +.Fl T +flags for +.Xr ssh 1 . +.It Cm RevokedHostKeys +Specifies revoked host public keys. +Keys listed in this file will be refused for host authentication. +Note that if this file does not exist or is not readable, +then host authentication will be refused for all hosts. +Keys may be specified as a text file, listing one public key per line, or as +an OpenSSH Key Revocation List (KRL) as generated by +.Xr ssh-keygen 1 . +For more information on KRLs, see the KEY REVOCATION LISTS section in +.Xr ssh-keygen 1 . +.It Cm SecurityKeyProvider +Specifies a path to a library that will be used when loading any +FIDO authenticator-hosted keys, overriding the default of using +the built-in USB HID support. +.Pp +If the specified value begins with a +.Sq $ +character, then it will be treated as an environment variable containing +the path to the library. +.It Cm SendEnv +Specifies what variables from the local +.Xr environ 7 +should be sent to the server. +The server must also support it, and the server must be configured to +accept these environment variables. +Note that the +.Ev TERM +environment variable is always sent whenever a +pseudo-terminal is requested as it is required by the protocol. +Refer to +.Cm AcceptEnv +in +.Xr sshd_config 5 +for how to configure the server. +Variables are specified by name, which may contain wildcard characters. +Multiple environment variables may be separated by whitespace or spread +across multiple +.Cm SendEnv +directives. +.Pp +See +.Sx PATTERNS +for more information on patterns. +.Pp +It is possible to clear previously set +.Cm SendEnv +variable names by prefixing patterns with +.Pa - . +The default is not to send any environment variables. +.It Cm ServerAliveCountMax +Sets the number of server alive messages (see below) which may be +sent without +.Xr ssh 1 +receiving any messages back from the server. +If this threshold is reached while server alive messages are being sent, +ssh will disconnect from the server, terminating the session. +It is important to note that the use of server alive messages is very +different from +.Cm TCPKeepAlive +(below). +The server alive messages are sent through the encrypted channel +and therefore will not be spoofable. +The TCP keepalive option enabled by +.Cm TCPKeepAlive +is spoofable. +The server alive mechanism is valuable when the client or +server depend on knowing when a connection has become unresponsive. +.Pp +The default value is 3. +If, for example, +.Cm ServerAliveInterval +(see below) is set to 15 and +.Cm ServerAliveCountMax +is left at the default, if the server becomes unresponsive, +ssh will disconnect after approximately 45 seconds. +.It Cm ServerAliveInterval +Sets a timeout interval in seconds after which if no data has been received +from the server, +.Xr ssh 1 +will send a message through the encrypted +channel to request a response from the server. +The default +is 0, indicating that these messages will not be sent to the server. +.It Cm SessionType +May be used to either request invocation of a subsystem on the remote system, +or to prevent the execution of a remote command at all. +The latter is useful for just forwarding ports. +The argument to this keyword must be +.Cm none +(same as the +.Fl N +option), +.Cm subsystem +(same as the +.Fl s +option) or +.Cm default +(shell or command execution). +.It Cm SetEnv +Directly specify one or more environment variables and their contents to +be sent to the server. +Similarly to +.Cm SendEnv , +with the exception of the +.Ev TERM +variable, the server must be prepared to accept the environment variable. +.It Cm StdinNull +Redirects stdin from +.Pa /dev/null +(actually, prevents reading from stdin). +Either this or the equivalent +.Fl n +option must be used when +.Nm ssh +is run in the background. +The argument to this keyword must be +.Cm yes +(same as the +.Fl n +option) or +.Cm no +(the default). +.It Cm StreamLocalBindMask +Sets the octal file creation mode mask +.Pq umask +used when creating a Unix-domain socket file for local or remote +port forwarding. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The default value is 0177, which creates a Unix-domain socket file that is +readable and writable only by the owner. +Note that not all operating systems honor the file mode on Unix-domain +socket files. +.It Cm StreamLocalBindUnlink +Specifies whether to remove an existing Unix-domain socket file for local +or remote port forwarding before creating a new one. +If the socket file already exists and +.Cm StreamLocalBindUnlink +is not enabled, +.Nm ssh +will be unable to forward the port to the Unix-domain socket file. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The argument must be +.Cm yes +or +.Cm no +(the default). +.It Cm StrictHostKeyChecking +If this flag is set to +.Cm yes , +.Xr ssh 1 +will never automatically add host keys to the +.Pa ~/.ssh/known_hosts +file, and refuses to connect to hosts whose host key has changed. +This provides maximum protection against man-in-the-middle (MITM) attacks, +though it can be annoying when the +.Pa /etc/ssh/ssh_known_hosts +file is poorly maintained or when connections to new hosts are +frequently made. +This option forces the user to manually +add all new hosts. +.Pp +If this flag is set to +.Cm accept-new +then ssh will automatically add new host keys to the user's +.Pa known_hosts +file, but will not permit connections to hosts with +changed host keys. +If this flag is set to +.Cm no +or +.Cm off , +ssh will automatically add new host keys to the user known hosts files +and allow connections to hosts with changed hostkeys to proceed, +subject to some restrictions. +If this flag is set to +.Cm ask +(the default), +new host keys +will be added to the user known host files only after the user +has confirmed that is what they really want to do, and +ssh will refuse to connect to hosts whose host key has changed. +The host keys of +known hosts will be verified automatically in all cases. +.It Cm SyslogFacility +Gives the facility code that is used when logging messages from +.Xr ssh 1 . +The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, +LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. +The default is USER. +.It Cm TCPKeepAlive +Specifies whether the system should send TCP keepalive messages to the +other side. +If they are sent, death of the connection or crash of one +of the machines will be properly noticed. +However, this means that +connections will die if the route is down temporarily, and some people +find it annoying. +.Pp +The default is +.Cm yes +(to send TCP keepalive messages), and the client will notice +if the network goes down or the remote host dies. +This is important in scripts, and many users want it too. +.Pp +To disable TCP keepalive messages, the value should be set to +.Cm no . +See also +.Cm ServerAliveInterval +for protocol-level keepalives. +.It Cm Tunnel +Request +.Xr tun 4 +device forwarding between the client and the server. +The argument must be +.Cm yes , +.Cm point-to-point +(layer 3), +.Cm ethernet +(layer 2), +or +.Cm no +(the default). +Specifying +.Cm yes +requests the default tunnel mode, which is +.Cm point-to-point . +.It Cm TunnelDevice +Specifies the +.Xr tun 4 +devices to open on the client +.Pq Ar local_tun +and the server +.Pq Ar remote_tun . +.Pp +The argument must be +.Sm off +.Ar local_tun Op : Ar remote_tun . +.Sm on +The devices may be specified by numerical ID or the keyword +.Cm any , +which uses the next available tunnel device. +If +.Ar remote_tun +is not specified, it defaults to +.Cm any . +The default is +.Cm any:any . +.It Cm UpdateHostKeys +Specifies whether +.Xr ssh 1 +should accept notifications of additional hostkeys from the server sent +after authentication has completed and add them to +.Cm UserKnownHostsFile . +The argument must be +.Cm yes , +.Cm no +or +.Cm ask . +This option allows learning alternate hostkeys for a server +and supports graceful key rotation by allowing a server to send replacement +public keys before old ones are removed. +.Pp +Additional hostkeys are only accepted if the key used to authenticate the +host was already trusted or explicitly accepted by the user, the host was +authenticated via +.Cm UserKnownHostsFile +(i.e. not +.Cm GlobalKnownHostsFile ) +and the host was authenticated using a plain key and not a certificate. +.Pp +.Cm UpdateHostKeys +is enabled by default if the user has not overridden the default +.Cm UserKnownHostsFile +setting and has not enabled +.Cm VerifyHostKeyDNS , +otherwise +.Cm UpdateHostKeys +will be set to +.Cm no . +.Pp +If +.Cm UpdateHostKeys +is set to +.Cm ask , +then the user is asked to confirm the modifications to the known_hosts file. +Confirmation is currently incompatible with +.Cm ControlPersist , +and will be disabled if it is enabled. +.Pp +Presently, only +.Xr sshd 8 +from OpenSSH 6.8 and greater support the +.Qq hostkeys@openssh.com +protocol extension used to inform the client of all the server's hostkeys. +.It Cm User +Specifies the user to log in as. +This can be useful when a different user name is used on different machines. +This saves the trouble of +having to remember to give the user name on the command line. +.It Cm UserKnownHostsFile +Specifies one or more files to use for the user +host key database, separated by whitespace. +Each filename may use tilde notation to refer to the user's home directory, +the tokens described in the +.Sx TOKENS +section and environment variables as described in the +.Sx ENVIRONMENT VARIABLES +section. +The default is +.Pa ~/.ssh/known_hosts , +.Pa ~/.ssh/known_hosts2 . +.It Cm VerifyHostKeyDNS +Specifies whether to verify the remote key using DNS and SSHFP resource +records. +If this option is set to +.Cm yes , +the client will implicitly trust keys that match a secure fingerprint +from DNS. +Insecure fingerprints will be handled as if this option was set to +.Cm ask . +If this option is set to +.Cm ask , +information on fingerprint match will be displayed, but the user will still +need to confirm new host keys according to the +.Cm StrictHostKeyChecking +option. +The default is +.Cm no . +.Pp +See also +.Sx VERIFYING HOST KEYS +in +.Xr ssh 1 . +.It Cm VisualHostKey +If this flag is set to +.Cm yes , +an ASCII art representation of the remote host key fingerprint is +printed in addition to the fingerprint string at login and +for unknown host keys. +If this flag is set to +.Cm no +(the default), +no fingerprint strings are printed at login and +only the fingerprint string will be printed for unknown host keys. +.It Cm XAuthLocation +Specifies the full pathname of the +.Xr xauth 1 +program. +The default is +.Pa /usr/X11R6/bin/xauth . +.El +.Sh PATTERNS +A +.Em pattern +consists of zero or more non-whitespace characters, +.Sq * +(a wildcard that matches zero or more characters), +or +.Sq ?\& +(a wildcard that matches exactly one character). +For example, to specify a set of declarations for any host in the +.Qq .co.uk +set of domains, +the following pattern could be used: +.Pp +.Dl Host *.co.uk +.Pp +The following pattern +would match any host in the 192.168.0.[0-9] network range: +.Pp +.Dl Host 192.168.0.? +.Pp +A +.Em pattern-list +is a comma-separated list of patterns. +Patterns within pattern-lists may be negated +by preceding them with an exclamation mark +.Pq Sq !\& . +For example, +to allow a key to be used from anywhere within an organization +except from the +.Qq dialup +pool, +the following entry (in authorized_keys) could be used: +.Pp +.Dl from=\&"!*.dialup.example.com,*.example.com\&" +.Pp +Note that a negated match will never produce a positive result by itself. +For example, attempting to match +.Qq host3 +against the following pattern-list will fail: +.Pp +.Dl from=\&"!host1,!host2\&" +.Pp +The solution here is to include a term that will yield a positive match, +such as a wildcard: +.Pp +.Dl from=\&"!host1,!host2,*\&" +.Sh TOKENS +Arguments to some keywords can make use of tokens, +which are expanded at runtime: +.Pp +.Bl -tag -width XXXX -offset indent -compact +.It %% +A literal +.Sq % . +.It \&%C +Hash of %l%h%p%r. +.It %d +Local user's home directory. +.It %f +The fingerprint of the server's host key. +.It %H +The +.Pa known_hosts +hostname or address that is being searched for. +.It %h +The remote hostname. +.It \%%I +A string describing the reason for a +.Cm KnownHostsCommand +execution: either +.Cm ADDRESS +when looking up a host by address (only when +.Cm CheckHostIP +is enabled), +.Cm HOSTNAME +when searching by hostname, or +.Cm ORDER +when preparing the host key algorithm preference list to use for the +destination host. +.It %i +The local user ID. +.It %K +The base64 encoded host key. +.It %k +The host key alias if specified, otherwise the original remote hostname given +on the command line. +.It %L +The local hostname. +.It %l +The local hostname, including the domain name. +.It %n +The original remote hostname, as given on the command line. +.It %p +The remote port. +.It %r +The remote username. +.It \&%T +The local +.Xr tun 4 +or +.Xr tap 4 +network interface assigned if +tunnel forwarding was requested, or +.Qq NONE +otherwise. +.It %t +The type of the server host key, e.g. +.Cm ssh-ed25519 . +.It %u +The local username. +.El +.Pp +.Cm CertificateFile , +.Cm ControlPath , +.Cm IdentityAgent , +.Cm IdentityFile , +.Cm KnownHostsCommand , +.Cm LocalForward , +.Cm Match exec , +.Cm RemoteCommand , +.Cm RemoteForward , +and +.Cm UserKnownHostsFile +accept the tokens %%, %C, %d, %h, %i, %k, %L, %l, %n, %p, %r, and %u. +.Pp +.Cm KnownHostsCommand +additionally accepts the tokens %f, %H, %I, %K and %t. +.Pp +.Cm Hostname +accepts the tokens %% and %h. +.Pp +.Cm LocalCommand +accepts all tokens. +.Pp +.Cm ProxyCommand +accepts the tokens %%, %h, %n, %p, and %r. +.Sh ENVIRONMENT VARIABLES +Arguments to some keywords can be expanded at runtime from environment +variables on the client by enclosing them in +.Ic ${} , +for example +.Ic ${HOME}/.ssh +would refer to the user's .ssh directory. +If a specified environment variable does not exist then an error will be +returned and the setting for that keyword will be ignored. +.Pp +The keywords +.Cm CertificateFile , +.Cm ControlPath , +.Cm IdentityAgent , +.Cm IdentityFile , +.Cm KnownHostsCommand , +and +.Cm UserKnownHostsFile +support environment variables. +The keywords +.Cm LocalForward +and +.Cm RemoteForward +support environment variables only for Unix domain socket paths. +.Sh FILES +.Bl -tag -width Ds +.It Pa ~/.ssh/config +This is the per-user configuration file. +The format of this file is described above. +This file is used by the SSH client. +Because of the potential for abuse, this file must have strict permissions: +read/write for the user, and not writable by others. +.It Pa /etc/ssh/ssh_config +Systemwide configuration file. +This file provides defaults for those +values that are not specified in the user's configuration file, and +for those users who do not have a configuration file. +This file must be world-readable. +.El +.Sh SEE ALSO +.Xr ssh 1 +.Sh AUTHORS +.An -nosplit +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by +.An Tatu Ylonen . +.An Aaron Campbell , Bob Beck , Markus Friedl , +.An Niels Provos , Theo de Raadt +and +.An Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +.An Markus Friedl +contributed the support for SSH protocol versions 1.5 and 2.0. diff --git a/aosp/external/openssh/sshbuf-getput-basic.c b/aosp/external/openssh/sshbuf-getput-basic.c new file mode 100644 index 0000000000000000000000000000000000000000..9803fb5ed904c388c2a81f3c56d194c9fa632107 --- /dev/null +++ b/aosp/external/openssh/sshbuf-getput-basic.c @@ -0,0 +1,633 @@ +/* $OpenBSD: sshbuf-getput-basic.c,v 1.11 2020/06/05 03:25:35 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define SSHBUF_INTERNAL +#include "includes.h" + +#include + +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif + +#include "ssherr.h" +#include "sshbuf.h" + +int +sshbuf_get(struct sshbuf *buf, void *v, size_t len) +{ + const u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, len)) < 0) + return r; + if (v != NULL && len != 0) + memcpy(v, p, len); + return 0; +} + +int +sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) +{ + const u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 8)) < 0) + return r; + if (valp != NULL) + *valp = PEEK_U64(p); + return 0; +} + +int +sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) +{ + const u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 4)) < 0) + return r; + if (valp != NULL) + *valp = PEEK_U32(p); + return 0; +} + +int +sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) +{ + const u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 2)) < 0) + return r; + if (valp != NULL) + *valp = PEEK_U16(p); + return 0; +} + +int +sshbuf_get_u8(struct sshbuf *buf, u_char *valp) +{ + const u_char *p = sshbuf_ptr(buf); + int r; + + if ((r = sshbuf_consume(buf, 1)) < 0) + return r; + if (valp != NULL) + *valp = (u_int8_t)*p; + return 0; +} + +static int +check_offset(const struct sshbuf *buf, int wr, size_t offset, size_t len) +{ + if (sshbuf_ptr(buf) == NULL) /* calls sshbuf_check_sanity() */ + return SSH_ERR_INTERNAL_ERROR; + if (offset >= SIZE_MAX - len) + return SSH_ERR_INVALID_ARGUMENT; + if (offset + len > sshbuf_len(buf)) { + return wr ? + SSH_ERR_NO_BUFFER_SPACE : SSH_ERR_MESSAGE_INCOMPLETE; + } + return 0; +} + +static int +check_roffset(const struct sshbuf *buf, size_t offset, size_t len, + const u_char **p) +{ + int r; + + *p = NULL; + if ((r = check_offset(buf, 0, offset, len)) != 0) + return r; + *p = sshbuf_ptr(buf) + offset; + return 0; +} + +int +sshbuf_peek_u64(const struct sshbuf *buf, size_t offset, u_int64_t *valp) +{ + const u_char *p = NULL; + int r; + + if (valp != NULL) + *valp = 0; + if ((r = check_roffset(buf, offset, 8, &p)) != 0) + return r; + if (valp != NULL) + *valp = PEEK_U64(p); + return 0; +} + +int +sshbuf_peek_u32(const struct sshbuf *buf, size_t offset, u_int32_t *valp) +{ + const u_char *p = NULL; + int r; + + if (valp != NULL) + *valp = 0; + if ((r = check_roffset(buf, offset, 4, &p)) != 0) + return r; + if (valp != NULL) + *valp = PEEK_U32(p); + return 0; +} + +int +sshbuf_peek_u16(const struct sshbuf *buf, size_t offset, u_int16_t *valp) +{ + const u_char *p = NULL; + int r; + + if (valp != NULL) + *valp = 0; + if ((r = check_roffset(buf, offset, 2, &p)) != 0) + return r; + if (valp != NULL) + *valp = PEEK_U16(p); + return 0; +} + +int +sshbuf_peek_u8(const struct sshbuf *buf, size_t offset, u_char *valp) +{ + const u_char *p = NULL; + int r; + + if (valp != NULL) + *valp = 0; + if ((r = check_roffset(buf, offset, 1, &p)) != 0) + return r; + if (valp != NULL) + *valp = *p; + return 0; +} + +int +sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) +{ + const u_char *val; + size_t len; + int r; + + if (valp != NULL) + *valp = NULL; + if (lenp != NULL) + *lenp = 0; + if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) + return r; + if (valp != NULL) { + if ((*valp = malloc(len + 1)) == NULL) { + SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); + return SSH_ERR_ALLOC_FAIL; + } + if (len != 0) + memcpy(*valp, val, len); + (*valp)[len] = '\0'; + } + if (lenp != NULL) + *lenp = len; + return 0; +} + +int +sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) +{ + size_t len; + const u_char *p; + int r; + + if (valp != NULL) + *valp = NULL; + if (lenp != NULL) + *lenp = 0; + if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) + return r; + if (valp != NULL) + *valp = p; + if (lenp != NULL) + *lenp = len; + if (sshbuf_consume(buf, len + 4) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +int +sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, + size_t *lenp) +{ + u_int32_t len; + const u_char *p = sshbuf_ptr(buf); + + if (valp != NULL) + *valp = NULL; + if (lenp != NULL) + *lenp = 0; + if (sshbuf_len(buf) < 4) { + SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); + return SSH_ERR_MESSAGE_INCOMPLETE; + } + len = PEEK_U32(p); + if (len > SSHBUF_SIZE_MAX - 4) { + SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE")); + return SSH_ERR_STRING_TOO_LARGE; + } + if (sshbuf_len(buf) - 4 < len) { + SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); + return SSH_ERR_MESSAGE_INCOMPLETE; + } + if (valp != NULL) + *valp = p + 4; + if (lenp != NULL) + *lenp = len; + return 0; +} + +int +sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) +{ + size_t len; + const u_char *p, *z; + int r; + + if (valp != NULL) + *valp = NULL; + if (lenp != NULL) + *lenp = 0; + if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) + return r; + /* Allow a \0 only at the end of the string */ + if (len > 0 && + (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) { + SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); + return SSH_ERR_INVALID_FORMAT; + } + if ((r = sshbuf_skip_string(buf)) != 0) + return -1; + if (valp != NULL) { + if ((*valp = malloc(len + 1)) == NULL) { + SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); + return SSH_ERR_ALLOC_FAIL; + } + if (len != 0) + memcpy(*valp, p, len); + (*valp)[len] = '\0'; + } + if (lenp != NULL) + *lenp = (size_t)len; + return 0; +} + +int +sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v) +{ + u_int32_t len; + u_char *p; + int r; + + /* + * Use sshbuf_peek_string_direct() to figure out if there is + * a complete string in 'buf' and copy the string directly + * into 'v'. + */ + if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 || + (r = sshbuf_get_u32(buf, &len)) != 0 || + (r = sshbuf_reserve(v, len, &p)) != 0 || + (r = sshbuf_get(buf, p, len)) != 0) + return r; + return 0; +} + +int +sshbuf_put(struct sshbuf *buf, const void *v, size_t len) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, len, &p)) < 0) + return r; + if (len != 0) + memcpy(p, v, len); + return 0; +} + +int +sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) +{ + if (v == NULL) + return 0; + return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); +} + +int +sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) +{ + va_list ap; + int r; + + va_start(ap, fmt); + r = sshbuf_putfv(buf, fmt, ap); + va_end(ap); + return r; +} + +int +sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) +{ + va_list ap2; + int r, len; + u_char *p; + + VA_COPY(ap2, ap); + if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (len == 0) { + r = 0; + goto out; /* Nothing to do */ + } + va_end(ap2); + VA_COPY(ap2, ap); + if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) + goto out; + if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; /* Shouldn't happen */ + } + /* Consume terminating \0 */ + if ((r = sshbuf_consume_end(buf, 1)) != 0) + goto out; + r = 0; + out: + va_end(ap2); + return r; +} + +int +sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 8, &p)) < 0) + return r; + POKE_U64(p, val); + return 0; +} + +int +sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 4, &p)) < 0) + return r; + POKE_U32(p, val); + return 0; +} + +int +sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 2, &p)) < 0) + return r; + POKE_U16(p, val); + return 0; +} + +int +sshbuf_put_u8(struct sshbuf *buf, u_char val) +{ + u_char *p; + int r; + + if ((r = sshbuf_reserve(buf, 1, &p)) < 0) + return r; + p[0] = val; + return 0; +} + +static int +check_woffset(struct sshbuf *buf, size_t offset, size_t len, u_char **p) +{ + int r; + + *p = NULL; + if ((r = check_offset(buf, 1, offset, len)) != 0) + return r; + if (sshbuf_mutable_ptr(buf) == NULL) + return SSH_ERR_BUFFER_READ_ONLY; + *p = sshbuf_mutable_ptr(buf) + offset; + return 0; +} + +int +sshbuf_poke_u64(struct sshbuf *buf, size_t offset, u_int64_t val) +{ + u_char *p = NULL; + int r; + + if ((r = check_woffset(buf, offset, 8, &p)) != 0) + return r; + POKE_U64(p, val); + return 0; +} + +int +sshbuf_poke_u32(struct sshbuf *buf, size_t offset, u_int32_t val) +{ + u_char *p = NULL; + int r; + + if ((r = check_woffset(buf, offset, 4, &p)) != 0) + return r; + POKE_U32(p, val); + return 0; +} + +int +sshbuf_poke_u16(struct sshbuf *buf, size_t offset, u_int16_t val) +{ + u_char *p = NULL; + int r; + + if ((r = check_woffset(buf, offset, 2, &p)) != 0) + return r; + POKE_U16(p, val); + return 0; +} + +int +sshbuf_poke_u8(struct sshbuf *buf, size_t offset, u_char val) +{ + u_char *p = NULL; + int r; + + if ((r = check_woffset(buf, offset, 1, &p)) != 0) + return r; + *p = val; + return 0; +} + +int +sshbuf_poke(struct sshbuf *buf, size_t offset, void *v, size_t len) +{ + u_char *p = NULL; + int r; + + if ((r = check_woffset(buf, offset, len, &p)) != 0) + return r; + memcpy(p, v, len); + return 0; +} + +int +sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) +{ + u_char *d; + int r; + + if (len > SSHBUF_SIZE_MAX - 4) { + SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); + return SSH_ERR_NO_BUFFER_SPACE; + } + if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) + return r; + POKE_U32(d, len); + if (len != 0) + memcpy(d + 4, v, len); + return 0; +} + +int +sshbuf_put_cstring(struct sshbuf *buf, const char *v) +{ + return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); +} + +int +sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) +{ + if (v == NULL) + return sshbuf_put_string(buf, NULL, 0); + + return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); +} + +int +sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp) +{ + const u_char *p; + size_t len; + struct sshbuf *ret; + int r; + + if (buf == NULL || bufp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + *bufp = NULL; + if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) + return r; + if ((ret = sshbuf_from(p, len)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */ + (r = sshbuf_set_parent(ret, buf)) != 0) { + sshbuf_free(ret); + return r; + } + *bufp = ret; + return 0; +} + +int +sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len) +{ + u_char *d; + const u_char *s = (const u_char *)v; + int r, prepend; + + if (len > SSHBUF_SIZE_MAX - 5) { + SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); + return SSH_ERR_NO_BUFFER_SPACE; + } + /* Skip leading zero bytes */ + for (; len > 0 && *s == 0; len--, s++) + ; + /* + * If most significant bit is set then prepend a zero byte to + * avoid interpretation as a negative number. + */ + prepend = len > 0 && (s[0] & 0x80) != 0; + if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0) + return r; + POKE_U32(d, len + prepend); + if (prepend) + d[4] = 0; + if (len != 0) + memcpy(d + 4 + prepend, s, len); + return 0; +} + +int +sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, + const u_char **valp, size_t *lenp) +{ + const u_char *d; + size_t len, olen; + int r; + + if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0) + return r; + len = olen; + /* Refuse negative (MSB set) bignums */ + if ((len != 0 && (*d & 0x80) != 0)) + return SSH_ERR_BIGNUM_IS_NEGATIVE; + /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ + if (len > SSHBUF_MAX_BIGNUM + 1 || + (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) + return SSH_ERR_BIGNUM_TOO_LARGE; + /* Trim leading zeros */ + while (len > 0 && *d == 0x00) { + d++; + len--; + } + if (valp != NULL) + *valp = d; + if (lenp != NULL) + *lenp = len; + if (sshbuf_consume(buf, olen + 4) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} diff --git a/aosp/external/openssh/sshbuf-getput-crypto.c b/aosp/external/openssh/sshbuf-getput-crypto.c new file mode 100644 index 0000000000000000000000000000000000000000..2e61d3bcd809532058bf8e3d03aefab92ee2d7e4 --- /dev/null +++ b/aosp/external/openssh/sshbuf-getput-crypto.c @@ -0,0 +1,180 @@ +/* $OpenBSD: sshbuf-getput-crypto.c,v 1.8 2019/11/15 06:00:20 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define SSHBUF_INTERNAL +#include "includes.h" + +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#ifdef OPENSSL_HAS_ECC +# include +#endif /* OPENSSL_HAS_ECC */ + +#include "ssherr.h" +#include "sshbuf.h" + +int +sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM **valp) +{ + BIGNUM *v; + const u_char *d; + size_t len; + int r; + + if (valp != NULL) + *valp = NULL; + if ((r = sshbuf_get_bignum2_bytes_direct(buf, &d, &len)) != 0) + return r; + if (valp != NULL) { + if ((v = BN_new()) == NULL || + BN_bin2bn(d, len, v) == NULL) { + BN_clear_free(v); + return SSH_ERR_ALLOC_FAIL; + } + *valp = v; + } + return 0; +} + +#ifdef OPENSSL_HAS_ECC +static int +get_ec(const u_char *d, size_t len, EC_POINT *v, const EC_GROUP *g) +{ + /* Refuse overlong bignums */ + if (len == 0 || len > SSHBUF_MAX_ECPOINT) + return SSH_ERR_ECPOINT_TOO_LARGE; + /* Only handle uncompressed points */ + if (*d != POINT_CONVERSION_UNCOMPRESSED) + return SSH_ERR_INVALID_FORMAT; + if (v != NULL && EC_POINT_oct2point(g, v, d, len, NULL) != 1) + return SSH_ERR_INVALID_FORMAT; /* XXX assumption */ + return 0; +} + +int +sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g) +{ + const u_char *d; + size_t len; + int r; + + if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) + return r; + if ((r = get_ec(d, len, v, g)) != 0) + return r; + /* Skip string */ + if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +int +sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v) +{ + EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v)); + int r; + const u_char *d; + size_t len; + + if (pt == NULL) { + SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); + return SSH_ERR_ALLOC_FAIL; + } + if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) { + EC_POINT_free(pt); + return r; + } + if ((r = get_ec(d, len, pt, EC_KEY_get0_group(v))) != 0) { + EC_POINT_free(pt); + return r; + } + if (EC_KEY_set_public_key(v, pt) != 1) { + EC_POINT_free(pt); + return SSH_ERR_ALLOC_FAIL; /* XXX assumption */ + } + EC_POINT_free(pt); + /* Skip string */ + if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { + /* Shouldn't happen */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + SSHBUF_ABORT(); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} +#endif /* OPENSSL_HAS_ECC */ + +int +sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v) +{ + u_char d[SSHBUF_MAX_BIGNUM + 1]; + int len = BN_num_bytes(v), prepend = 0, r; + + if (len < 0 || len > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; + *d = '\0'; + if (BN_bn2bin(v, d + 1) != len) + return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ + /* If MSB is set, prepend a \0 */ + if (len > 0 && (d[1] & 0x80) != 0) + prepend = 1; + if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) { + explicit_bzero(d, sizeof(d)); + return r; + } + explicit_bzero(d, sizeof(d)); + return 0; +} + +#ifdef OPENSSL_HAS_ECC +int +sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g) +{ + u_char d[SSHBUF_MAX_ECPOINT]; + size_t len; + int ret; + + if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, NULL)) > SSHBUF_MAX_ECPOINT) { + return SSH_ERR_INVALID_ARGUMENT; + } + if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, + d, len, NULL) != len) { + return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ + } + ret = sshbuf_put_string(buf, d, len); + explicit_bzero(d, len); + return ret; +} + +int +sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v) +{ + return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v), + EC_KEY_get0_group(v)); +} +#endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ diff --git a/aosp/external/openssh/sshbuf-io.c b/aosp/external/openssh/sshbuf-io.c new file mode 100644 index 0000000000000000000000000000000000000000..13ef40e7d272b70b52401d3b699a0ad6970cf5eb --- /dev/null +++ b/aosp/external/openssh/sshbuf-io.c @@ -0,0 +1,117 @@ +/* $OpenBSD: sshbuf-io.c,v 1.2 2020/01/25 23:28:06 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include + +#include "ssherr.h" +#include "sshbuf.h" +#include "atomicio.h" + +/* Load a file from a fd into a buffer */ +int +sshbuf_load_fd(int fd, struct sshbuf **blobp) +{ + u_char buf[4096]; + size_t len; + struct stat st; + int r; + struct sshbuf *blob; + + *blobp = NULL; + + if (fstat(fd, &st) == -1) + return SSH_ERR_SYSTEM_ERROR; + if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && + st.st_size > SSHBUF_SIZE_MAX) + return SSH_ERR_INVALID_FORMAT; + if ((blob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + for (;;) { + if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { + if (errno == EPIPE) + break; + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + if ((r = sshbuf_put(blob, buf, len)) != 0) + goto out; + if (sshbuf_len(blob) > SSHBUF_SIZE_MAX) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && + st.st_size != (off_t)sshbuf_len(blob)) { + r = SSH_ERR_FILE_CHANGED; + goto out; + } + /* success */ + *blobp = blob; + blob = NULL; /* transferred */ + r = 0; + out: + explicit_bzero(buf, sizeof(buf)); + sshbuf_free(blob); + return r; +} + +int +sshbuf_load_file(const char *path, struct sshbuf **bufp) +{ + int r, fd, oerrno; + + *bufp = NULL; + if ((fd = open(path, O_RDONLY)) == -1) + return SSH_ERR_SYSTEM_ERROR; + if ((r = sshbuf_load_fd(fd, bufp)) != 0) + goto out; + /* success */ + r = 0; + out: + oerrno = errno; + close(fd); + if (r != 0) + errno = oerrno; + return r; +} + +int +sshbuf_write_file(const char *path, struct sshbuf *buf) +{ + int fd, oerrno; + + if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) + return SSH_ERR_SYSTEM_ERROR; + if (atomicio(vwrite, fd, sshbuf_mutable_ptr(buf), + sshbuf_len(buf)) != sshbuf_len(buf) || close(fd) != 0) { + oerrno = errno; + close(fd); + unlink(path); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + return 0; +} + diff --git a/aosp/external/openssh/sshbuf-misc.c b/aosp/external/openssh/sshbuf-misc.c new file mode 100644 index 0000000000000000000000000000000000000000..9c5c42bba70ac1042c080da1744ddeb2274a6dfb --- /dev/null +++ b/aosp/external/openssh/sshbuf-misc.c @@ -0,0 +1,308 @@ +/* $OpenBSD: sshbuf-misc.c,v 1.18 2022/01/22 00:43:43 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include +#include +#include + +#include "ssherr.h" +#define SSHBUF_INTERNAL +#include "sshbuf.h" + +void +sshbuf_dump_data(const void *s, size_t len, FILE *f) +{ + size_t i, j; + const u_char *p = (const u_char *)s; + + for (i = 0; i < len; i += 16) { + fprintf(f, "%.4zu: ", i); + for (j = i; j < i + 16; j++) { + if (j < len) + fprintf(f, "%02x ", p[j]); + else + fprintf(f, " "); + } + fprintf(f, " "); + for (j = i; j < i + 16; j++) { + if (j < len) { + if (isascii(p[j]) && isprint(p[j])) + fprintf(f, "%c", p[j]); + else + fprintf(f, "."); + } + } + fprintf(f, "\n"); + } +} + +void +sshbuf_dump(const struct sshbuf *buf, FILE *f) +{ + fprintf(f, "buffer len = %zu\n", sshbuf_len(buf)); + sshbuf_dump_data(sshbuf_ptr(buf), sshbuf_len(buf), f); +} + +char * +sshbuf_dtob16(struct sshbuf *buf) +{ + size_t i, j, len = sshbuf_len(buf); + const u_char *p = sshbuf_ptr(buf); + char *ret; + const char hex[] = "0123456789abcdef"; + + if (len == 0) + return strdup(""); + if (SIZE_MAX / 2 <= len || (ret = malloc(len * 2 + 1)) == NULL) + return NULL; + for (i = j = 0; i < len; i++) { + ret[j++] = hex[(p[i] >> 4) & 0xf]; + ret[j++] = hex[p[i] & 0xf]; + } + ret[j] = '\0'; + return ret; +} + +int +sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap) +{ + size_t i, slen = 0; + char *s = NULL; + int r; + + if (d == NULL || b64 == NULL || sshbuf_len(d) >= SIZE_MAX / 2) + return SSH_ERR_INVALID_ARGUMENT; + if (sshbuf_len(d) == 0) + return 0; + slen = ((sshbuf_len(d) + 2) / 3) * 4 + 1; + if ((s = malloc(slen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (b64_ntop(sshbuf_ptr(d), sshbuf_len(d), s, slen) == -1) { + r = SSH_ERR_INTERNAL_ERROR; + goto fail; + } + if (wrap) { + for (i = 0; s[i] != '\0'; i++) { + if ((r = sshbuf_put_u8(b64, s[i])) != 0) + goto fail; + if (i % 70 == 69 && (r = sshbuf_put_u8(b64, '\n')) != 0) + goto fail; + } + if ((i - 1) % 70 != 69 && (r = sshbuf_put_u8(b64, '\n')) != 0) + goto fail; + } else { + if ((r = sshbuf_put(b64, s, strlen(s))) != 0) + goto fail; + } + /* Success */ + r = 0; + fail: + freezero(s, slen); + return r; +} + +char * +sshbuf_dtob64_string(const struct sshbuf *buf, int wrap) +{ + struct sshbuf *tmp; + char *ret; + + if ((tmp = sshbuf_new()) == NULL) + return NULL; + if (sshbuf_dtob64(buf, tmp, wrap) != 0) { + sshbuf_free(tmp); + return NULL; + } + ret = sshbuf_dup_string(tmp); + sshbuf_free(tmp); + return ret; +} + +int +sshbuf_b64tod(struct sshbuf *buf, const char *b64) +{ + size_t plen = strlen(b64); + int nlen, r; + u_char *p; + + if (plen == 0) + return 0; + if ((p = malloc(plen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((nlen = b64_pton(b64, p, plen)) < 0) { + freezero(p, plen); + return SSH_ERR_INVALID_FORMAT; + } + if ((r = sshbuf_put(buf, p, nlen)) < 0) { + freezero(p, plen); + return r; + } + freezero(p, plen); + return 0; +} + +int +sshbuf_dtourlb64(const struct sshbuf *d, struct sshbuf *b64, int wrap) +{ + int r = SSH_ERR_INTERNAL_ERROR; + u_char *p; + struct sshbuf *b = NULL; + size_t i, l; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + /* Encode using regular base64; we'll transform it once done */ + if ((r = sshbuf_dtob64(d, b, wrap)) != 0) + goto out; + /* remove padding from end of encoded string*/ + for (;;) { + l = sshbuf_len(b); + if (l <= 1 || sshbuf_ptr(b) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if (sshbuf_ptr(b)[l - 1] != '=') + break; + if ((r = sshbuf_consume_end(b, 1)) != 0) + goto out; + } + /* Replace characters with rfc4648 equivalents */ + l = sshbuf_len(b); + if ((p = sshbuf_mutable_ptr(b)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + for (i = 0; i < l; i++) { + if (p[i] == '+') + p[i] = '-'; + else if (p[i] == '/') + p[i] = '_'; + } + r = sshbuf_putb(b64, b); + out: + sshbuf_free(b); + return r; +} + +char * +sshbuf_dup_string(struct sshbuf *buf) +{ + const u_char *p = NULL, *s = sshbuf_ptr(buf); + size_t l = sshbuf_len(buf); + char *r; + + if (s == NULL || l > SIZE_MAX) + return NULL; + /* accept a nul only as the last character in the buffer */ + if (l > 0 && (p = memchr(s, '\0', l)) != NULL) { + if (p != s + l - 1) + return NULL; + l--; /* the nul is put back below */ + } + if ((r = malloc(l + 1)) == NULL) + return NULL; + if (l > 0) + memcpy(r, s, l); + r[l] = '\0'; + return r; +} + +int +sshbuf_cmp(const struct sshbuf *b, size_t offset, + const void *s, size_t len) +{ + if (sshbuf_ptr(b) == NULL) + return SSH_ERR_INTERNAL_ERROR; + if (offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0) + return SSH_ERR_INVALID_ARGUMENT; + if (offset + len > sshbuf_len(b)) + return SSH_ERR_MESSAGE_INCOMPLETE; + if (timingsafe_bcmp(sshbuf_ptr(b) + offset, s, len) != 0) + return SSH_ERR_INVALID_FORMAT; + return 0; +} + +int +sshbuf_find(const struct sshbuf *b, size_t start_offset, + const void *s, size_t len, size_t *offsetp) +{ + void *p; + + if (offsetp != NULL) + *offsetp = 0; + if (sshbuf_ptr(b) == NULL) + return SSH_ERR_INTERNAL_ERROR; + if (start_offset > SSHBUF_SIZE_MAX || len > SSHBUF_SIZE_MAX || len == 0) + return SSH_ERR_INVALID_ARGUMENT; + if (start_offset > sshbuf_len(b) || start_offset + len > sshbuf_len(b)) + return SSH_ERR_MESSAGE_INCOMPLETE; + if ((p = memmem(sshbuf_ptr(b) + start_offset, + sshbuf_len(b) - start_offset, s, len)) == NULL) + return SSH_ERR_INVALID_FORMAT; + if (offsetp != NULL) + *offsetp = (const u_char *)p - sshbuf_ptr(b); + return 0; +} + +int +sshbuf_read(int fd, struct sshbuf *buf, size_t maxlen, size_t *rlen) +{ + int r, oerrno; + size_t adjust; + ssize_t rr; + u_char *d; + + if (rlen != NULL) + *rlen = 0; + if ((r = sshbuf_reserve(buf, maxlen, &d)) != 0) + return r; + rr = read(fd, d, maxlen); + oerrno = errno; + + /* Adjust the buffer to include only what was actually read */ + if ((adjust = maxlen - (rr > 0 ? rr : 0)) != 0) { + if ((r = sshbuf_consume_end(buf, adjust)) != 0) { + /* avoid returning uninitialised data to caller */ + memset(d + rr, '\0', adjust); + return SSH_ERR_INTERNAL_ERROR; /* shouldn't happen */ + } + } + if (rr < 0) { + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } else if (rr == 0) { + errno = EPIPE; + return SSH_ERR_SYSTEM_ERROR; + } + /* success */ + if (rlen != NULL) + *rlen = (size_t)rr; + return 0; +} diff --git a/aosp/external/openssh/sshbuf.c b/aosp/external/openssh/sshbuf.c new file mode 100644 index 0000000000000000000000000000000000000000..368ba7980b755a04efbcd99e3999f222b4510ec1 --- /dev/null +++ b/aosp/external/openssh/sshbuf.c @@ -0,0 +1,401 @@ +/* $OpenBSD: sshbuf.c,v 1.15 2020/02/26 13:40:09 jsg Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define SSHBUF_INTERNAL +#include "includes.h" + +#include +#include +#include +#include +#include + +#include "ssherr.h" +#include "sshbuf.h" +#include "misc.h" + +static inline int +sshbuf_check_sanity(const struct sshbuf *buf) +{ + SSHBUF_TELL("sanity"); + if (__predict_false(buf == NULL || + (!buf->readonly && buf->d != buf->cd) || + buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX || + buf->cd == NULL || + buf->max_size > SSHBUF_SIZE_MAX || + buf->alloc > buf->max_size || + buf->size > buf->alloc || + buf->off > buf->size)) { + /* Do not try to recover from corrupted buffer internals */ + SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); + ssh_signal(SIGSEGV, SIG_DFL); + raise(SIGSEGV); + return SSH_ERR_INTERNAL_ERROR; + } + return 0; +} + +static void +sshbuf_maybe_pack(struct sshbuf *buf, int force) +{ + SSHBUF_DBG(("force %d", force)); + SSHBUF_TELL("pre-pack"); + if (buf->off == 0 || buf->readonly || buf->refcount > 1) + return; + if (force || + (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) { + memmove(buf->d, buf->d + buf->off, buf->size - buf->off); + buf->size -= buf->off; + buf->off = 0; + SSHBUF_TELL("packed"); + } +} + +struct sshbuf * +sshbuf_new(void) +{ + struct sshbuf *ret; + + if ((ret = calloc(sizeof(*ret), 1)) == NULL) + return NULL; + ret->alloc = SSHBUF_SIZE_INIT; + ret->max_size = SSHBUF_SIZE_MAX; + ret->readonly = 0; + ret->refcount = 1; + ret->parent = NULL; + if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) { + free(ret); + return NULL; + } + return ret; +} + +struct sshbuf * +sshbuf_from(const void *blob, size_t len) +{ + struct sshbuf *ret; + + if (blob == NULL || len > SSHBUF_SIZE_MAX || + (ret = calloc(sizeof(*ret), 1)) == NULL) + return NULL; + ret->alloc = ret->size = ret->max_size = len; + ret->readonly = 1; + ret->refcount = 1; + ret->parent = NULL; + ret->cd = blob; + ret->d = NULL; + return ret; +} + +int +sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent) +{ + int r; + + if ((r = sshbuf_check_sanity(child)) != 0 || + (r = sshbuf_check_sanity(parent)) != 0) + return r; + child->parent = parent; + child->parent->refcount++; + return 0; +} + +struct sshbuf * +sshbuf_fromb(struct sshbuf *buf) +{ + struct sshbuf *ret; + + if (sshbuf_check_sanity(buf) != 0) + return NULL; + if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL) + return NULL; + if (sshbuf_set_parent(ret, buf) != 0) { + sshbuf_free(ret); + return NULL; + } + return ret; +} + +void +sshbuf_free(struct sshbuf *buf) +{ + if (buf == NULL) + return; + /* + * The following will leak on insane buffers, but this is the safest + * course of action - an invalid pointer or already-freed pointer may + * have been passed to us and continuing to scribble over memory would + * be bad. + */ + if (sshbuf_check_sanity(buf) != 0) + return; + + /* + * If we are a parent with still-extant children, then don't free just + * yet. The last child's call to sshbuf_free should decrement our + * refcount to 0 and trigger the actual free. + */ + buf->refcount--; + if (buf->refcount > 0) + return; + + /* + * If we are a child, the free our parent to decrement its reference + * count and possibly free it. + */ + sshbuf_free(buf->parent); + buf->parent = NULL; + + if (!buf->readonly) { + explicit_bzero(buf->d, buf->alloc); + free(buf->d); + } + freezero(buf, sizeof(*buf)); +} + +void +sshbuf_reset(struct sshbuf *buf) +{ + u_char *d; + + if (buf->readonly || buf->refcount > 1) { + /* Nonsensical. Just make buffer appear empty */ + buf->off = buf->size; + return; + } + (void) sshbuf_check_sanity(buf); + buf->off = buf->size = 0; + if (buf->alloc != SSHBUF_SIZE_INIT) { + if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT, + 1)) != NULL) { + buf->cd = buf->d = d; + buf->alloc = SSHBUF_SIZE_INIT; + } + } + explicit_bzero(buf->d, SSHBUF_SIZE_INIT); +} + +size_t +sshbuf_max_size(const struct sshbuf *buf) +{ + return buf->max_size; +} + +size_t +sshbuf_alloc(const struct sshbuf *buf) +{ + return buf->alloc; +} + +const struct sshbuf * +sshbuf_parent(const struct sshbuf *buf) +{ + return buf->parent; +} + +u_int +sshbuf_refcount(const struct sshbuf *buf) +{ + return buf->refcount; +} + +int +sshbuf_set_max_size(struct sshbuf *buf, size_t max_size) +{ + size_t rlen; + u_char *dp; + int r; + + SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size)); + if ((r = sshbuf_check_sanity(buf)) != 0) + return r; + if (max_size == buf->max_size) + return 0; + if (buf->readonly || buf->refcount > 1) + return SSH_ERR_BUFFER_READ_ONLY; + if (max_size > SSHBUF_SIZE_MAX) + return SSH_ERR_NO_BUFFER_SPACE; + /* pack and realloc if necessary */ + sshbuf_maybe_pack(buf, max_size < buf->size); + if (max_size < buf->alloc && max_size > buf->size) { + if (buf->size < SSHBUF_SIZE_INIT) + rlen = SSHBUF_SIZE_INIT; + else + rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC); + if (rlen > max_size) + rlen = max_size; + SSHBUF_DBG(("new alloc = %zu", rlen)); + if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + buf->cd = buf->d = dp; + buf->alloc = rlen; + } + SSHBUF_TELL("new-max"); + if (max_size < buf->alloc) + return SSH_ERR_NO_BUFFER_SPACE; + buf->max_size = max_size; + return 0; +} + +size_t +sshbuf_len(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0) + return 0; + return buf->size - buf->off; +} + +size_t +sshbuf_avail(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) + return 0; + return buf->max_size - (buf->size - buf->off); +} + +const u_char * +sshbuf_ptr(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0) + return NULL; + return buf->cd + buf->off; +} + +u_char * +sshbuf_mutable_ptr(const struct sshbuf *buf) +{ + if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) + return NULL; + return buf->d + buf->off; +} + +int +sshbuf_check_reserve(const struct sshbuf *buf, size_t len) +{ + int r; + + if ((r = sshbuf_check_sanity(buf)) != 0) + return r; + if (buf->readonly || buf->refcount > 1) + return SSH_ERR_BUFFER_READ_ONLY; + SSHBUF_TELL("check"); + /* Check that len is reasonable and that max_size + available < len */ + if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) + return SSH_ERR_NO_BUFFER_SPACE; + return 0; +} + +int +sshbuf_allocate(struct sshbuf *buf, size_t len) +{ + size_t rlen, need; + u_char *dp; + int r; + + SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len)); + if ((r = sshbuf_check_reserve(buf, len)) != 0) + return r; + /* + * If the requested allocation appended would push us past max_size + * then pack the buffer, zeroing buf->off. + */ + sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); + SSHBUF_TELL("allocate"); + if (len + buf->size <= buf->alloc) + return 0; /* already have it. */ + + /* + * Prefer to alloc in SSHBUF_SIZE_INC units, but + * allocate less if doing so would overflow max_size. + */ + need = len + buf->size - buf->alloc; + rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC); + SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); + if (rlen > buf->max_size) + rlen = buf->alloc + need; + SSHBUF_DBG(("adjusted rlen %zu", rlen)); + if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) { + SSHBUF_DBG(("realloc fail")); + return SSH_ERR_ALLOC_FAIL; + } + buf->alloc = rlen; + buf->cd = buf->d = dp; + if ((r = sshbuf_check_reserve(buf, len)) < 0) { + /* shouldn't fail */ + return r; + } + SSHBUF_TELL("done"); + return 0; +} + +int +sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) +{ + u_char *dp; + int r; + + if (dpp != NULL) + *dpp = NULL; + + SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); + if ((r = sshbuf_allocate(buf, len)) != 0) + return r; + + dp = buf->d + buf->size; + buf->size += len; + if (dpp != NULL) + *dpp = dp; + return 0; +} + +int +sshbuf_consume(struct sshbuf *buf, size_t len) +{ + int r; + + SSHBUF_DBG(("len = %zu", len)); + if ((r = sshbuf_check_sanity(buf)) != 0) + return r; + if (len == 0) + return 0; + if (len > sshbuf_len(buf)) + return SSH_ERR_MESSAGE_INCOMPLETE; + buf->off += len; + /* deal with empty buffer */ + if (buf->off == buf->size) + buf->off = buf->size = 0; + SSHBUF_TELL("done"); + return 0; +} + +int +sshbuf_consume_end(struct sshbuf *buf, size_t len) +{ + int r; + + SSHBUF_DBG(("len = %zu", len)); + if ((r = sshbuf_check_sanity(buf)) != 0) + return r; + if (len == 0) + return 0; + if (len > sshbuf_len(buf)) + return SSH_ERR_MESSAGE_INCOMPLETE; + buf->size -= len; + SSHBUF_TELL("done"); + return 0; +} + diff --git a/aosp/external/openssh/sshbuf.h b/aosp/external/openssh/sshbuf.h new file mode 100644 index 0000000000000000000000000000000000000000..07d54f0a988d3a517f5e0eae55d41f704c3de777 --- /dev/null +++ b/aosp/external/openssh/sshbuf.h @@ -0,0 +1,415 @@ +/* $OpenBSD: sshbuf.h,v 1.25 2022/01/22 00:43:43 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SSHBUF_H +#define _SSHBUF_H + +#include +#include +#include +#ifdef WITH_OPENSSL +# include +# ifdef OPENSSL_HAS_ECC +# include +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + +#define SSHBUF_SIZE_MAX 0x8000000 /* Hard maximum size */ +#define SSHBUF_REFS_MAX 0x100000 /* Max child buffers */ +#define SSHBUF_MAX_BIGNUM (16384 / 8) /* Max bignum *bytes* */ +#define SSHBUF_MAX_ECPOINT ((528 * 2 / 8) + 1) /* Max EC point *bytes* */ + +/* + * NB. do not depend on the internals of this. It will be made opaque + * one day. + */ +struct sshbuf { + u_char *d; /* Data */ + const u_char *cd; /* Const data */ + size_t off; /* First available byte is buf->d + buf->off */ + size_t size; /* Last byte is buf->d + buf->size - 1 */ + size_t max_size; /* Maximum size of buffer */ + size_t alloc; /* Total bytes allocated to buf->d */ + int readonly; /* Refers to external, const data */ + int dont_free; /* Kludge to support sshbuf_init */ + u_int refcount; /* Tracks self and number of child buffers */ + struct sshbuf *parent; /* If child, pointer to parent */ +}; + +/* + * Create a new sshbuf buffer. + * Returns pointer to buffer on success, or NULL on allocation failure. + */ +struct sshbuf *sshbuf_new(void); + +/* + * Create a new, read-only sshbuf buffer from existing data. + * Returns pointer to buffer on success, or NULL on allocation failure. + */ +struct sshbuf *sshbuf_from(const void *blob, size_t len); + +/* + * Create a new, read-only sshbuf buffer from the contents of an existing + * buffer. The contents of "buf" must not change in the lifetime of the + * resultant buffer. + * Returns pointer to buffer on success, or NULL on allocation failure. + */ +struct sshbuf *sshbuf_fromb(struct sshbuf *buf); + +/* + * Create a new, read-only sshbuf buffer from the contents of a string in + * an existing buffer (the string is consumed in the process). + * The contents of "buf" must not change in the lifetime of the resultant + * buffer. + * Returns pointer to buffer on success, or NULL on allocation failure. + */ +int sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp); + +/* + * Clear and free buf + */ +void sshbuf_free(struct sshbuf *buf); + +/* + * Reset buf, clearing its contents. NB. max_size is preserved. + */ +void sshbuf_reset(struct sshbuf *buf); + +/* + * Return the maximum size of buf + */ +size_t sshbuf_max_size(const struct sshbuf *buf); + +/* + * Set the maximum size of buf + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_set_max_size(struct sshbuf *buf, size_t max_size); + +/* + * Returns the length of data in buf + */ +size_t sshbuf_len(const struct sshbuf *buf); + +/* + * Returns number of bytes left in buffer before hitting max_size. + */ +size_t sshbuf_avail(const struct sshbuf *buf); + +/* + * Returns a read-only pointer to the start of the data in buf + */ +const u_char *sshbuf_ptr(const struct sshbuf *buf); + +/* + * Returns a mutable pointer to the start of the data in buf, or + * NULL if the buffer is read-only. + */ +u_char *sshbuf_mutable_ptr(const struct sshbuf *buf); + +/* + * Check whether a reservation of size len will succeed in buf + * Safer to use than direct comparisons again sshbuf_avail as it copes + * with unsigned overflows correctly. + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_check_reserve(const struct sshbuf *buf, size_t len); + +/* + * Preallocates len additional bytes in buf. + * Useful for cases where the caller knows how many bytes will ultimately be + * required to avoid realloc in the buffer code. + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_allocate(struct sshbuf *buf, size_t len); + +/* + * Reserve len bytes in buf. + * Returns 0 on success and a pointer to the first reserved byte via the + * optional dpp parameter or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp); + +/* + * Consume len bytes from the start of buf + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_consume(struct sshbuf *buf, size_t len); + +/* + * Consume len bytes from the end of buf + * Returns 0 on success, or a negative SSH_ERR_* error code on failure. + */ +int sshbuf_consume_end(struct sshbuf *buf, size_t len); + +/* Extract or deposit some bytes */ +int sshbuf_get(struct sshbuf *buf, void *v, size_t len); +int sshbuf_put(struct sshbuf *buf, const void *v, size_t len); +int sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v); + +/* Append using a printf(3) format */ +int sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +int sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap); + +/* Functions to extract or store big-endian words of various sizes */ +int sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp); +int sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp); +int sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp); +int sshbuf_get_u8(struct sshbuf *buf, u_char *valp); +int sshbuf_put_u64(struct sshbuf *buf, u_int64_t val); +int sshbuf_put_u32(struct sshbuf *buf, u_int32_t val); +int sshbuf_put_u16(struct sshbuf *buf, u_int16_t val); +int sshbuf_put_u8(struct sshbuf *buf, u_char val); + +/* Functions to peek at the contents of a buffer without modifying it. */ +int sshbuf_peek_u64(const struct sshbuf *buf, size_t offset, + u_int64_t *valp); +int sshbuf_peek_u32(const struct sshbuf *buf, size_t offset, + u_int32_t *valp); +int sshbuf_peek_u16(const struct sshbuf *buf, size_t offset, + u_int16_t *valp); +int sshbuf_peek_u8(const struct sshbuf *buf, size_t offset, + u_char *valp); + +/* + * Functions to poke values into an existing buffer (e.g. a length header + * to a packet). The destination bytes must already exist in the buffer. + */ +int sshbuf_poke_u64(struct sshbuf *buf, size_t offset, u_int64_t val); +int sshbuf_poke_u32(struct sshbuf *buf, size_t offset, u_int32_t val); +int sshbuf_poke_u16(struct sshbuf *buf, size_t offset, u_int16_t val); +int sshbuf_poke_u8(struct sshbuf *buf, size_t offset, u_char val); +int sshbuf_poke(struct sshbuf *buf, size_t offset, void *v, size_t len); + +/* + * Functions to extract or store SSH wire encoded strings (u32 len || data) + * The "cstring" variants admit no \0 characters in the string contents. + * Caller must free *valp. + */ +int sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp); +int sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp); +int sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v); +int sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len); +int sshbuf_put_cstring(struct sshbuf *buf, const char *v); +int sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v); + +/* + * "Direct" variant of sshbuf_get_string, returns pointer into the sshbuf to + * avoid an malloc+memcpy. The pointer is guaranteed to be valid until the + * next sshbuf-modifying function call. Caller does not free. + */ +int sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, + size_t *lenp); + +/* Skip past a string */ +#define sshbuf_skip_string(buf) sshbuf_get_string_direct(buf, NULL, NULL) + +/* Another variant: "peeks" into the buffer without modifying it */ +int sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, + size_t *lenp); + +/* + * Functions to extract or store SSH wire encoded bignums and elliptic + * curve points. + */ +int sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len); +int sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, + const u_char **valp, size_t *lenp); +#ifdef WITH_OPENSSL +int sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM **valp); +int sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v); +# ifdef OPENSSL_HAS_ECC +int sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g); +int sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v); +int sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g); +int sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v); +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + +/* Dump the contents of the buffer in a human-readable format */ +void sshbuf_dump(const struct sshbuf *buf, FILE *f); + +/* Dump specified memory in a human-readable format */ +void sshbuf_dump_data(const void *s, size_t len, FILE *f); + +/* Return the hexadecimal representation of the contents of the buffer */ +char *sshbuf_dtob16(struct sshbuf *buf); + +/* Encode the contents of the buffer as base64 */ +char *sshbuf_dtob64_string(const struct sshbuf *buf, int wrap); +int sshbuf_dtob64(const struct sshbuf *d, struct sshbuf *b64, int wrap); +/* RFC4648 "base64url" encoding variant */ +int sshbuf_dtourlb64(const struct sshbuf *d, struct sshbuf *b64, int wrap); + +/* Decode base64 data and append it to the buffer */ +int sshbuf_b64tod(struct sshbuf *buf, const char *b64); + +/* + * Tests whether the buffer contains the specified byte sequence at the + * specified offset. Returns 0 on successful match, or a ssherr.h code + * otherwise. SSH_ERR_INVALID_FORMAT indicates sufficient bytes were + * present but the buffer contents did not match those supplied. Zero- + * length comparisons are not allowed. + * + * If sufficient data is present to make a comparison, then it is + * performed with timing independent of the value of the data. If + * insufficient data is present then the comparison is not attempted at + * all. + */ +int sshbuf_cmp(const struct sshbuf *b, size_t offset, + const void *s, size_t len); + +/* + * Searches the buffer for the specified string. Returns 0 on success + * and updates *offsetp with the offset of the first match, relative to + * the start of the buffer. Otherwise sshbuf_find will return a ssherr.h + * error code. SSH_ERR_INVALID_FORMAT indicates sufficient bytes were + * present in the buffer for a match to be possible but none was found. + * Searches for zero-length data are not allowed. + */ +int +sshbuf_find(const struct sshbuf *b, size_t start_offset, + const void *s, size_t len, size_t *offsetp); + +/* + * Duplicate the contents of a buffer to a string (caller to free). + * Returns NULL on buffer error, or if the buffer contains a premature + * nul character. + */ +char *sshbuf_dup_string(struct sshbuf *buf); + +/* + * Fill a buffer from a file descriptor or filename. Both allocate the + * buffer for the caller. + */ +int sshbuf_load_fd(int, struct sshbuf **) + __attribute__((__nonnull__ (2))); +int sshbuf_load_file(const char *, struct sshbuf **) + __attribute__((__nonnull__ (2))); + +/* + * Write a buffer to a path, creating/truncating as needed (mode 0644, + * subject to umask). The buffer contents are not modified. + */ +int sshbuf_write_file(const char *path, struct sshbuf *buf) + __attribute__((__nonnull__ (2))); + +/* Read up to maxlen bytes from a fd directly to a buffer */ +int sshbuf_read(int, struct sshbuf *, size_t, size_t *) + __attribute__((__nonnull__ (2))); + +/* Macros for decoding/encoding integers */ +#define PEEK_U64(p) \ + (((u_int64_t)(((const u_char *)(p))[0]) << 56) | \ + ((u_int64_t)(((const u_char *)(p))[1]) << 48) | \ + ((u_int64_t)(((const u_char *)(p))[2]) << 40) | \ + ((u_int64_t)(((const u_char *)(p))[3]) << 32) | \ + ((u_int64_t)(((const u_char *)(p))[4]) << 24) | \ + ((u_int64_t)(((const u_char *)(p))[5]) << 16) | \ + ((u_int64_t)(((const u_char *)(p))[6]) << 8) | \ + (u_int64_t)(((const u_char *)(p))[7])) +#define PEEK_U32(p) \ + (((u_int32_t)(((const u_char *)(p))[0]) << 24) | \ + ((u_int32_t)(((const u_char *)(p))[1]) << 16) | \ + ((u_int32_t)(((const u_char *)(p))[2]) << 8) | \ + (u_int32_t)(((const u_char *)(p))[3])) +#define PEEK_U16(p) \ + (((u_int16_t)(((const u_char *)(p))[0]) << 8) | \ + (u_int16_t)(((const u_char *)(p))[1])) + +#define POKE_U64(p, v) \ + do { \ + const u_int64_t __v = (v); \ + ((u_char *)(p))[0] = (__v >> 56) & 0xff; \ + ((u_char *)(p))[1] = (__v >> 48) & 0xff; \ + ((u_char *)(p))[2] = (__v >> 40) & 0xff; \ + ((u_char *)(p))[3] = (__v >> 32) & 0xff; \ + ((u_char *)(p))[4] = (__v >> 24) & 0xff; \ + ((u_char *)(p))[5] = (__v >> 16) & 0xff; \ + ((u_char *)(p))[6] = (__v >> 8) & 0xff; \ + ((u_char *)(p))[7] = __v & 0xff; \ + } while (0) +#define POKE_U32(p, v) \ + do { \ + const u_int32_t __v = (v); \ + ((u_char *)(p))[0] = (__v >> 24) & 0xff; \ + ((u_char *)(p))[1] = (__v >> 16) & 0xff; \ + ((u_char *)(p))[2] = (__v >> 8) & 0xff; \ + ((u_char *)(p))[3] = __v & 0xff; \ + } while (0) +#define POKE_U16(p, v) \ + do { \ + const u_int16_t __v = (v); \ + ((u_char *)(p))[0] = (__v >> 8) & 0xff; \ + ((u_char *)(p))[1] = __v & 0xff; \ + } while (0) + +/* Internal definitions follow. Exposed for regress tests */ +#ifdef SSHBUF_INTERNAL + +/* + * Return the allocation size of buf + */ +size_t sshbuf_alloc(const struct sshbuf *buf); + +/* + * Increment the reference count of buf. + */ +int sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent); + +/* + * Return the parent buffer of buf, or NULL if it has no parent. + */ +const struct sshbuf *sshbuf_parent(const struct sshbuf *buf); + +/* + * Return the reference count of buf + */ +u_int sshbuf_refcount(const struct sshbuf *buf); + +# define SSHBUF_SIZE_INIT 256 /* Initial allocation */ +# define SSHBUF_SIZE_INC 256 /* Preferred increment length */ +# define SSHBUF_PACK_MIN 8192 /* Minimum packable offset */ + +/* # define SSHBUF_ABORT abort */ +/* # define SSHBUF_DEBUG */ + +# ifndef SSHBUF_ABORT +# define SSHBUF_ABORT() +# endif + +# ifdef SSHBUF_DEBUG +# define SSHBUF_TELL(what) do { \ + printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \ + __FILE__, __LINE__, __func__, what, \ + buf->size, buf->alloc, buf->off, buf->max_size); \ + fflush(stdout); \ + } while (0) +# define SSHBUF_DBG(x) do { \ + printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \ + printf x; \ + printf("\n"); \ + fflush(stdout); \ + } while (0) +# else +# define SSHBUF_TELL(what) +# define SSHBUF_DBG(x) +# endif +#endif /* SSHBUF_INTERNAL */ + +#endif /* _SSHBUF_H */ diff --git a/aosp/external/openssh/sshconnect.c b/aosp/external/openssh/sshconnect.c new file mode 100644 index 0000000000000000000000000000000000000000..ebecc83747bbb6704c9b1bcf8e72f7b9c2b7a24b --- /dev/null +++ b/aosp/external/openssh/sshconnect.c @@ -0,0 +1,1708 @@ +/* $OpenBSD: sshconnect.c,v 1.356 2021/12/19 22:10:24 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Code to connect to a remote host, and to perform the client side of the + * login (authentication) dialog. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef HAVE_IFADDRS_H +# include +#endif + +#include "xmalloc.h" +#include "hostfile.h" +#include "ssh.h" +#include "sshbuf.h" +#include "packet.h" +#include "compat.h" +#include "sshkey.h" +#include "sshconnect.h" +#include "log.h" +#include "misc.h" +#include "readconf.h" +#include "atomicio.h" +#include "dns.h" +#include "monitor_fdpass.h" +#include "ssh2.h" +#include "version.h" +#include "authfile.h" +#include "ssherr.h" +#include "authfd.h" +#include "kex.h" + +struct sshkey *previous_host_key = NULL; + +static int matching_host_key_dns = 0; + +static pid_t proxy_command_pid = 0; + +/* import */ +extern int debug_flag; +extern Options options; +extern char *__progname; + +static int show_other_keys(struct hostkeys *, struct sshkey *); +static void warn_changed_key(struct sshkey *); + +/* Expand a proxy command */ +static char * +expand_proxy_command(const char *proxy_command, const char *user, + const char *host, const char *host_arg, int port) +{ + char *tmp, *ret, strport[NI_MAXSERV]; + const char *keyalias = options.host_key_alias ? + options.host_key_alias : host_arg; + + snprintf(strport, sizeof strport, "%d", port); + xasprintf(&tmp, "exec %s", proxy_command); + ret = percent_expand(tmp, + "h", host, + "k", keyalias, + "n", host_arg, + "p", strport, + "r", options.user, + (char *)NULL); + free(tmp); + return ret; +} + +/* + * Connect to the given ssh server using a proxy command that passes a + * a connected fd back to us. + */ +static int +ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, + const char *host_arg, u_short port, const char *proxy_command) +{ + char *command_string; + int sp[2], sock; + pid_t pid; + char *shell; + + if ((shell = getenv("SHELL")) == NULL) + shell = _PATH_BSHELL; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) + fatal("Could not create socketpair to communicate with " + "proxy dialer: %.100s", strerror(errno)); + + command_string = expand_proxy_command(proxy_command, options.user, + host, host_arg, port); + debug("Executing proxy dialer command: %.500s", command_string); + + /* Fork and execute the proxy command. */ + if ((pid = fork()) == 0) { + char *argv[10]; + + close(sp[1]); + /* Redirect stdin and stdout. */ + if (sp[0] != 0) { + if (dup2(sp[0], 0) == -1) + perror("dup2 stdin"); + } + if (sp[0] != 1) { + if (dup2(sp[0], 1) == -1) + perror("dup2 stdout"); + } + if (sp[0] >= 2) + close(sp[0]); + + /* + * Stderr is left for non-ControlPersist connections is so + * error messages may be printed on the user's terminal. + */ + if (!debug_flag && options.control_path != NULL && + options.control_persist && stdfd_devnull(0, 0, 1) == -1) + error_f("stdfd_devnull failed"); + + argv[0] = shell; + argv[1] = "-c"; + argv[2] = command_string; + argv[3] = NULL; + + /* + * Execute the proxy command. + * Note that we gave up any extra privileges above. + */ + execv(argv[0], argv); + perror(argv[0]); + exit(1); + } + /* Parent. */ + if (pid == -1) + fatal("fork failed: %.100s", strerror(errno)); + close(sp[0]); + free(command_string); + + if ((sock = mm_receive_fd(sp[1])) == -1) + fatal("proxy dialer did not pass back a connection"); + close(sp[1]); + + while (waitpid(pid, NULL, 0) == -1) + if (errno != EINTR) + fatal("Couldn't wait for child: %s", strerror(errno)); + + /* Set the connection file descriptors. */ + if (ssh_packet_set_connection(ssh, sock, sock) == NULL) + return -1; /* ssh_packet_set_connection logs error */ + + return 0; +} + +/* + * Connect to the given ssh server using a proxy command. + */ +static int +ssh_proxy_connect(struct ssh *ssh, const char *host, const char *host_arg, + u_short port, const char *proxy_command) +{ + char *command_string; + int pin[2], pout[2]; + pid_t pid; + char *shell; + + if ((shell = getenv("SHELL")) == NULL || *shell == '\0') + shell = _PATH_BSHELL; + + /* Create pipes for communicating with the proxy. */ + if (pipe(pin) == -1 || pipe(pout) == -1) + fatal("Could not create pipes to communicate with the proxy: %.100s", + strerror(errno)); + + command_string = expand_proxy_command(proxy_command, options.user, + host, host_arg, port); + debug("Executing proxy command: %.500s", command_string); + + /* Fork and execute the proxy command. */ + if ((pid = fork()) == 0) { + char *argv[10]; + + /* Redirect stdin and stdout. */ + close(pin[1]); + if (pin[0] != 0) { + if (dup2(pin[0], 0) == -1) + perror("dup2 stdin"); + close(pin[0]); + } + close(pout[0]); + if (dup2(pout[1], 1) == -1) + perror("dup2 stdout"); + /* Cannot be 1 because pin allocated two descriptors. */ + close(pout[1]); + + /* + * Stderr is left for non-ControlPersist connections is so + * error messages may be printed on the user's terminal. + */ + if (!debug_flag && options.control_path != NULL && + options.control_persist && stdfd_devnull(0, 0, 1) == -1) + error_f("stdfd_devnull failed"); + + argv[0] = shell; + argv[1] = "-c"; + argv[2] = command_string; + argv[3] = NULL; + + /* + * Execute the proxy command. Note that we gave up any + * extra privileges above. + */ + ssh_signal(SIGPIPE, SIG_DFL); + execv(argv[0], argv); + perror(argv[0]); + exit(1); + } + /* Parent. */ + if (pid == -1) + fatal("fork failed: %.100s", strerror(errno)); + else + proxy_command_pid = pid; /* save pid to clean up later */ + + /* Close child side of the descriptors. */ + close(pin[0]); + close(pout[1]); + + /* Free the command name. */ + free(command_string); + + /* Set the connection file descriptors. */ + if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) + return -1; /* ssh_packet_set_connection logs error */ + + return 0; +} + +void +ssh_kill_proxy_command(void) +{ + /* + * Send SIGHUP to proxy command if used. We don't wait() in + * case it hangs and instead rely on init to reap the child + */ + if (proxy_command_pid > 1) + kill(proxy_command_pid, SIGHUP); +} + +#ifdef HAVE_IFADDRS_H +/* + * Search a interface address list (returned from getifaddrs(3)) for an + * address that matches the desired address family on the specified interface. + * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure. + */ +static int +check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, + struct sockaddr_storage *resultp, socklen_t *rlenp) +{ + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa; + struct in6_addr *v6addr; + const struct ifaddrs *ifa; + int allow_local; + + /* + * Prefer addresses that are not loopback or linklocal, but use them + * if nothing else matches. + */ + for (allow_local = 0; allow_local < 2; allow_local++) { + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || + (ifa->ifa_flags & IFF_UP) == 0 || + ifa->ifa_addr->sa_family != af || + strcmp(ifa->ifa_name, options.bind_interface) != 0) + continue; + switch (ifa->ifa_addr->sa_family) { + case AF_INET: + sa = (struct sockaddr_in *)ifa->ifa_addr; + if (!allow_local && sa->sin_addr.s_addr == + htonl(INADDR_LOOPBACK)) + continue; + if (*rlenp < sizeof(struct sockaddr_in)) { + error_f("v4 addr doesn't fit"); + return -1; + } + *rlenp = sizeof(struct sockaddr_in); + memcpy(resultp, sa, *rlenp); + return 0; + case AF_INET6: + sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; + v6addr = &sa6->sin6_addr; + if (!allow_local && + (IN6_IS_ADDR_LINKLOCAL(v6addr) || + IN6_IS_ADDR_LOOPBACK(v6addr))) + continue; + if (*rlenp < sizeof(struct sockaddr_in6)) { + error_f("v6 addr doesn't fit"); + return -1; + } + *rlenp = sizeof(struct sockaddr_in6); + memcpy(resultp, sa6, *rlenp); + return 0; + } + } + } + return -1; +} +#endif + +/* + * Creates a socket for use as the ssh connection. + */ +static int +ssh_create_socket(struct addrinfo *ai) +{ + int sock, r; + struct sockaddr_storage bindaddr; + socklen_t bindaddrlen = 0; + struct addrinfo hints, *res = NULL; +#ifdef HAVE_IFADDRS_H + struct ifaddrs *ifaddrs = NULL; +#endif + char ntop[NI_MAXHOST]; + + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock == -1) { + error("socket: %s", strerror(errno)); + return -1; + } + fcntl(sock, F_SETFD, FD_CLOEXEC); + + /* Use interactive QOS (if specified) until authentication completed */ + if (options.ip_qos_interactive != INT_MAX) + set_sock_tos(sock, options.ip_qos_interactive); + + /* Bind the socket to an alternative local IP address */ + if (options.bind_address == NULL && options.bind_interface == NULL) + return sock; + + if (options.bind_address != NULL) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = ai->ai_socktype; + hints.ai_protocol = ai->ai_protocol; + hints.ai_flags = AI_PASSIVE; + if ((r = getaddrinfo(options.bind_address, NULL, + &hints, &res)) != 0) { + error("getaddrinfo: %s: %s", options.bind_address, + ssh_gai_strerror(r)); + goto fail; + } + if (res == NULL) { + error("getaddrinfo: no addrs"); + goto fail; + } + memcpy(&bindaddr, res->ai_addr, res->ai_addrlen); + bindaddrlen = res->ai_addrlen; + } else if (options.bind_interface != NULL) { +#ifdef HAVE_IFADDRS_H + if ((r = getifaddrs(&ifaddrs)) != 0) { + error("getifaddrs: %s: %s", options.bind_interface, + strerror(errno)); + goto fail; + } + bindaddrlen = sizeof(bindaddr); + if (check_ifaddrs(options.bind_interface, ai->ai_family, + ifaddrs, &bindaddr, &bindaddrlen) != 0) { + logit("getifaddrs: %s: no suitable addresses", + options.bind_interface); + goto fail; + } +#else + error("BindInterface not supported on this platform."); +#endif + } + if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen, + ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) { + error_f("getnameinfo failed: %s", ssh_gai_strerror(r)); + goto fail; + } + if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) { + error("bind %s: %s", ntop, strerror(errno)); + goto fail; + } + debug_f("bound to %s", ntop); + /* success */ + goto out; +fail: + close(sock); + sock = -1; + out: + if (res != NULL) + freeaddrinfo(res); +#ifdef HAVE_IFADDRS_H + if (ifaddrs != NULL) + freeifaddrs(ifaddrs); +#endif + return sock; +} + +/* + * Opens a TCP/IP connection to the remote server on the given host. + * The address of the remote host will be returned in hostaddr. + * If port is 0, the default port will be used. + * Connection_attempts specifies the maximum number of tries (one per + * second). If proxy_command is non-NULL, it specifies the command (with %h + * and %p substituted for host and port, respectively) to use to contact + * the daemon. + */ +static int +ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, + struct sockaddr_storage *hostaddr, u_short port, int connection_attempts, + int *timeout_ms, int want_keepalive) +{ + int on = 1, saved_timeout_ms = *timeout_ms; + int oerrno, sock = -1, attempt; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + struct addrinfo *ai; + + debug3_f("entering"); + memset(ntop, 0, sizeof(ntop)); + memset(strport, 0, sizeof(strport)); + + for (attempt = 0; attempt < connection_attempts; attempt++) { + if (attempt > 0) { + /* Sleep a moment before retrying. */ + sleep(1); + debug("Trying again..."); + } + /* + * Loop through addresses for this host, and try each one in + * sequence until the connection succeeds. + */ + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && + ai->ai_family != AF_INET6) { + errno = EAFNOSUPPORT; + continue; + } + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + oerrno = errno; + error_f("getnameinfo failed"); + errno = oerrno; + continue; + } + debug("Connecting to %.200s [%.100s] port %s.", + host, ntop, strport); + + /* Create a socket for connecting. */ + sock = ssh_create_socket(ai); + if (sock < 0) { + /* Any error is already output */ + errno = 0; + continue; + } + + *timeout_ms = saved_timeout_ms; + if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, + timeout_ms) >= 0) { + /* Successful connection. */ + memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); + break; + } else { + oerrno = errno; + debug("connect to address %s port %s: %s", + ntop, strport, strerror(errno)); + close(sock); + sock = -1; + errno = oerrno; + } + } + if (sock != -1) + break; /* Successful connection. */ + } + + /* Return failure if we didn't get a successful connection. */ + if (sock == -1) { + error("ssh: connect to host %s port %s: %s", + host, strport, errno == 0 ? "failure" : strerror(errno)); + return -1; + } + + debug("Connection established."); + + /* Set SO_KEEPALIVE if requested. */ + if (want_keepalive && + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, + sizeof(on)) == -1) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); + + /* Set the connection. */ + if (ssh_packet_set_connection(ssh, sock, sock) == NULL) + return -1; /* ssh_packet_set_connection logs error */ + + return 0; +} + +int +ssh_connect(struct ssh *ssh, const char *host, const char *host_arg, + struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port, + int connection_attempts, int *timeout_ms, int want_keepalive) +{ + int in, out; + + if (options.proxy_command == NULL) { + return ssh_connect_direct(ssh, host, addrs, hostaddr, port, + connection_attempts, timeout_ms, want_keepalive); + } else if (strcmp(options.proxy_command, "-") == 0) { + if ((in = dup(STDIN_FILENO)) == -1 || + (out = dup(STDOUT_FILENO)) == -1) { + if (in >= 0) + close(in); + error_f("dup() in/out failed"); + return -1; /* ssh_packet_set_connection logs error */ + } + if ((ssh_packet_set_connection(ssh, in, out)) == NULL) + return -1; /* ssh_packet_set_connection logs error */ + return 0; + } else if (options.proxy_use_fdpass) { + return ssh_proxy_fdpass_connect(ssh, host, host_arg, port, + options.proxy_command); + } + return ssh_proxy_connect(ssh, host, host_arg, port, + options.proxy_command); +} + +/* defaults to 'no' */ +static int +confirm(const char *prompt, const char *fingerprint) +{ + const char *msg, *again = "Please type 'yes' or 'no': "; + const char *again_fp = "Please type 'yes', 'no' or the fingerprint: "; + char *p, *cp; + int ret = -1; + + if (options.batch_mode) + return 0; + for (msg = prompt;;msg = fingerprint ? again_fp : again) { + cp = p = read_passphrase(msg, RP_ECHO); + if (p == NULL) + return 0; + p += strspn(p, " \t"); /* skip leading whitespace */ + p[strcspn(p, " \t\n")] = '\0'; /* remove trailing whitespace */ + if (p[0] == '\0' || strcasecmp(p, "no") == 0) + ret = 0; + else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL && + strcmp(p, fingerprint) == 0)) + ret = 1; + free(cp); + if (ret != -1) + return ret; + } +} + +static int +sockaddr_is_local(struct sockaddr *hostaddr) +{ + switch (hostaddr->sa_family) { + case AF_INET: + return (ntohl(((struct sockaddr_in *)hostaddr)-> + sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; + case AF_INET6: + return IN6_IS_ADDR_LOOPBACK( + &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); + default: + return 0; + } +} + +/* + * Prepare the hostname and ip address strings that are used to lookup + * host keys in known_hosts files. These may have a port number appended. + */ +void +get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, + u_short port, char **hostfile_hostname, char **hostfile_ipaddr) +{ + char ntop[NI_MAXHOST]; + socklen_t addrlen; + + switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { + case -1: + addrlen = 0; + break; + case AF_INET: + addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addrlen = sizeof(struct sockaddr_in6); + break; + default: + addrlen = sizeof(struct sockaddr); + break; + } + + /* + * We don't have the remote ip-address for connections + * using a proxy command + */ + if (hostfile_ipaddr != NULL) { + if (options.proxy_command == NULL) { + if (getnameinfo(hostaddr, addrlen, + ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) + fatal_f("getnameinfo failed"); + *hostfile_ipaddr = put_host_port(ntop, port); + } else { + *hostfile_ipaddr = xstrdup(""); + } + } + + /* + * Allow the user to record the key under a different name or + * differentiate a non-standard port. This is useful for ssh + * tunneling over forwarded connections or if you run multiple + * sshd's on different ports on the same machine. + */ + if (hostfile_hostname != NULL) { + if (options.host_key_alias != NULL) { + *hostfile_hostname = xstrdup(options.host_key_alias); + debug("using hostkeyalias: %s", *hostfile_hostname); + } else { + *hostfile_hostname = put_host_port(hostname, port); + } + } +} + +/* returns non-zero if path appears in hostfiles, or 0 if not. */ +static int +path_in_hostfiles(const char *path, char **hostfiles, u_int num_hostfiles) +{ + u_int i; + + for (i = 0; i < num_hostfiles; i++) { + if (strcmp(path, hostfiles[i]) == 0) + return 1; + } + return 0; +} + +struct find_by_key_ctx { + const char *host, *ip; + const struct sshkey *key; + char **names; + u_int nnames; +}; + +/* Try to replace home directory prefix (per $HOME) with a ~/ sequence */ +static char * +try_tilde_unexpand(const char *path) +{ + char *home, *ret = NULL; + size_t l; + + if (*path != '/') + return xstrdup(path); + if ((home = getenv("HOME")) == NULL || (l = strlen(home)) == 0) + return xstrdup(path); + if (strncmp(path, home, l) != 0) + return xstrdup(path); + /* + * ensure we have matched on a path boundary: either the $HOME that + * we just compared ends with a '/' or the next character of the path + * must be a '/'. + */ + if (home[l - 1] != '/' && path[l] != '/') + return xstrdup(path); + if (path[l] == '/') + l++; + xasprintf(&ret, "~/%s", path + l); + return ret; +} + +static int +hostkeys_find_by_key_cb(struct hostkey_foreach_line *l, void *_ctx) +{ + struct find_by_key_ctx *ctx = (struct find_by_key_ctx *)_ctx; + char *path; + + /* we are looking for keys with names that *do not* match */ + if ((l->match & HKF_MATCH_HOST) != 0) + return 0; + /* not interested in marker lines */ + if (l->marker != MRK_NONE) + return 0; + /* we are only interested in exact key matches */ + if (l->key == NULL || !sshkey_equal(ctx->key, l->key)) + return 0; + path = try_tilde_unexpand(l->path); + debug_f("found matching key in %s:%lu", path, l->linenum); + ctx->names = xrecallocarray(ctx->names, + ctx->nnames, ctx->nnames + 1, sizeof(*ctx->names)); + xasprintf(&ctx->names[ctx->nnames], "%s:%lu: %s", path, l->linenum, + strncmp(l->hosts, HASH_MAGIC, strlen(HASH_MAGIC)) == 0 ? + "[hashed name]" : l->hosts); + ctx->nnames++; + free(path); + return 0; +} + +static int +hostkeys_find_by_key_hostfile(const char *file, const char *which, + struct find_by_key_ctx *ctx) +{ + int r; + + debug3_f("trying %s hostfile \"%s\"", which, file); + if ((r = hostkeys_foreach(file, hostkeys_find_by_key_cb, ctx, + ctx->host, ctx->ip, HKF_WANT_PARSE_KEY, 0)) != 0) { + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { + debug_f("hostkeys file %s does not exist", file); + return 0; + } + error_fr(r, "hostkeys_foreach failed for %s", file); + return r; + } + return 0; +} + +/* + * Find 'key' in known hosts file(s) that do not match host/ip. + * Used to display also-known-as information for previously-unseen hostkeys. + */ +static void +hostkeys_find_by_key(const char *host, const char *ip, const struct sshkey *key, + char **user_hostfiles, u_int num_user_hostfiles, + char **system_hostfiles, u_int num_system_hostfiles, + char ***names, u_int *nnames) +{ + struct find_by_key_ctx ctx = {0, 0, 0, 0, 0}; + u_int i; + + *names = NULL; + *nnames = 0; + + if (key == NULL || sshkey_is_cert(key)) + return; + + ctx.host = host; + ctx.ip = ip; + ctx.key = key; + + for (i = 0; i < num_user_hostfiles; i++) { + if (hostkeys_find_by_key_hostfile(user_hostfiles[i], + "user", &ctx) != 0) + goto fail; + } + for (i = 0; i < num_system_hostfiles; i++) { + if (hostkeys_find_by_key_hostfile(system_hostfiles[i], + "system", &ctx) != 0) + goto fail; + } + /* success */ + *names = ctx.names; + *nnames = ctx.nnames; + ctx.names = NULL; + ctx.nnames = 0; + return; + fail: + for (i = 0; i < ctx.nnames; i++) + free(ctx.names[i]); + free(ctx.names); +} + +#define MAX_OTHER_NAMES 8 /* Maximum number of names to list */ +static char * +other_hostkeys_message(const char *host, const char *ip, + const struct sshkey *key, + char **user_hostfiles, u_int num_user_hostfiles, + char **system_hostfiles, u_int num_system_hostfiles) +{ + char *ret = NULL, **othernames = NULL; + u_int i, n, num_othernames = 0; + + hostkeys_find_by_key(host, ip, key, + user_hostfiles, num_user_hostfiles, + system_hostfiles, num_system_hostfiles, + &othernames, &num_othernames); + if (num_othernames == 0) + return xstrdup("This key is not known by any other names"); + + xasprintf(&ret, "This host key is known by the following other " + "names/addresses:"); + + n = num_othernames; + if (n > MAX_OTHER_NAMES) + n = MAX_OTHER_NAMES; + for (i = 0; i < n; i++) { + xextendf(&ret, "\n", " %s", othernames[i]); + } + if (n < num_othernames) { + xextendf(&ret, "\n", " (%d additional names omitted)", + num_othernames - n); + } + for (i = 0; i < num_othernames; i++) + free(othernames[i]); + free(othernames); + return ret; +} + +void +load_hostkeys_command(struct hostkeys *hostkeys, const char *command_template, + const char *invocation, const struct ssh_conn_info *cinfo, + const struct sshkey *host_key, const char *hostfile_hostname) +{ + int r, i, ac = 0; + char *key_fp = NULL, *keytext = NULL, *tmp; + char *command = NULL, *tag = NULL, **av = NULL; + FILE *f = NULL; + pid_t pid; + void (*osigchld)(int); + + xasprintf(&tag, "KnownHostsCommand-%s", invocation); + + if (host_key != NULL) { + if ((key_fp = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + if ((r = sshkey_to_base64(host_key, &keytext)) != 0) + fatal_fr(r, "sshkey_to_base64 failed"); + } + /* + * NB. all returns later this function should go via "out" to + * ensure the original SIGCHLD handler is restored properly. + */ + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + + /* Turn the command into an argument vector */ + if (argv_split(command_template, &ac, &av, 0) != 0) { + error("%s \"%s\" contains invalid quotes", tag, + command_template); + goto out; + } + if (ac == 0) { + error("%s \"%s\" yielded no arguments", tag, + command_template); + goto out; + } + for (i = 1; i < ac; i++) { + tmp = percent_dollar_expand(av[i], + DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), + "H", hostfile_hostname, + "I", invocation, + "t", host_key == NULL ? "NONE" : sshkey_ssh_name(host_key), + "f", key_fp == NULL ? "NONE" : key_fp, + "K", keytext == NULL ? "NONE" : keytext, + (char *)NULL); + if (tmp == NULL) + fatal_f("percent_expand failed"); + free(av[i]); + av[i] = tmp; + } + /* Prepare a printable command for logs, etc. */ + command = argv_assemble(ac, av); + + if ((pid = subprocess(tag, command, ac, av, &f, + SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_UNSAFE_PATH| + SSH_SUBPROCESS_PRESERVE_ENV, NULL, NULL, NULL)) == 0) + goto out; + + load_hostkeys_file(hostkeys, hostfile_hostname, tag, f, 1); + + if (exited_cleanly(pid, tag, command, 0) != 0) + fatal("KnownHostsCommand failed"); + + out: + if (f != NULL) + fclose(f); + ssh_signal(SIGCHLD, osigchld); + for (i = 0; i < ac; i++) + free(av[i]); + free(av); + free(tag); + free(command); + free(key_fp); + free(keytext); +} + +/* + * check whether the supplied host key is valid, return -1 if the key + * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. + */ +#define RDRW 0 +#define RDONLY 1 +#define ROQUIET 2 +static int +check_host_key(char *hostname, const struct ssh_conn_info *cinfo, + struct sockaddr *hostaddr, u_short port, + struct sshkey *host_key, int readonly, int clobber_port, + char **user_hostfiles, u_int num_user_hostfiles, + char **system_hostfiles, u_int num_system_hostfiles, + const char *hostfile_command) +{ + HostStatus host_status = -1, ip_status = -1; + struct sshkey *raw_key = NULL; + char *ip = NULL, *host = NULL; + char hostline[1000], *hostp, *fp, *ra; + char msg[1024]; + const char *type, *fail_reason; + const struct hostkey_entry *host_found = NULL, *ip_found = NULL; + int len, cancelled_forwarding = 0, confirmed; + int local = sockaddr_is_local(hostaddr); + int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; + int hostkey_trusted = 0; /* Known or explicitly accepted by user */ + struct hostkeys *host_hostkeys, *ip_hostkeys; + u_int i; + + /* + * Force accepting of the host key for loopback/localhost. The + * problem is that if the home directory is NFS-mounted to multiple + * machines, localhost will refer to a different machine in each of + * them, and the user will get bogus HOST_CHANGED warnings. This + * essentially disables host authentication for localhost; however, + * this is probably not a real problem. + */ + if (options.no_host_authentication_for_localhost == 1 && local && + options.host_key_alias == NULL) { + debug("Forcing accepting of host key for " + "loopback/localhost."); + options.update_hostkeys = 0; + return 0; + } + + /* + * Prepare the hostname and address strings used for hostkey lookup. + * In some cases, these will have a port number appended. + */ + get_hostfile_hostname_ipaddr(hostname, hostaddr, + clobber_port ? 0 : port, &host, &ip); + + /* + * Turn off check_host_ip if the connection is to localhost, via proxy + * command or if we don't have a hostname to compare with + */ + if (options.check_host_ip && (local || + strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) + options.check_host_ip = 0; + + host_hostkeys = init_hostkeys(); + for (i = 0; i < num_user_hostfiles; i++) + load_hostkeys(host_hostkeys, host, user_hostfiles[i], 0); + for (i = 0; i < num_system_hostfiles; i++) + load_hostkeys(host_hostkeys, host, system_hostfiles[i], 0); + if (hostfile_command != NULL && !clobber_port) { + load_hostkeys_command(host_hostkeys, hostfile_command, + "HOSTNAME", cinfo, host_key, host); + } + + ip_hostkeys = NULL; + if (!want_cert && options.check_host_ip) { + ip_hostkeys = init_hostkeys(); + for (i = 0; i < num_user_hostfiles; i++) + load_hostkeys(ip_hostkeys, ip, user_hostfiles[i], 0); + for (i = 0; i < num_system_hostfiles; i++) + load_hostkeys(ip_hostkeys, ip, system_hostfiles[i], 0); + if (hostfile_command != NULL && !clobber_port) { + load_hostkeys_command(ip_hostkeys, hostfile_command, + "ADDRESS", cinfo, host_key, ip); + } + } + + retry: + /* Reload these as they may have changed on cert->key downgrade */ + want_cert = sshkey_is_cert(host_key); + type = sshkey_type(host_key); + + /* + * Check if the host key is present in the user's list of known + * hosts or in the systemwide list. + */ + host_status = check_key_in_hostkeys(host_hostkeys, host_key, + &host_found); + + /* + * If there are no hostfiles, or if the hostkey was found via + * KnownHostsCommand, then don't try to touch the disk. + */ + if (!readonly && (num_user_hostfiles == 0 || + (host_found != NULL && host_found->note != 0))) + readonly = RDONLY; + + /* + * Also perform check for the ip address, skip the check if we are + * localhost, looking for a certificate, or the hostname was an ip + * address to begin with. + */ + if (!want_cert && ip_hostkeys != NULL) { + ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, + &ip_found); + if (host_status == HOST_CHANGED && + (ip_status != HOST_CHANGED || + (ip_found != NULL && + !sshkey_equal(ip_found->key, host_found->key)))) + host_ip_differ = 1; + } else + ip_status = host_status; + + switch (host_status) { + case HOST_OK: + /* The host is known and the key matches. */ + debug("Host '%.200s' is known and matches the %s host %s.", + host, type, want_cert ? "certificate" : "key"); + debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", + host_found->file, host_found->line); + if (want_cert) { + if (sshkey_cert_check_host(host_key, + options.host_key_alias == NULL ? + hostname : options.host_key_alias, 0, + options.ca_sign_algorithms, &fail_reason) != 0) { + error("%s", fail_reason); + goto fail; + } + /* + * Do not attempt hostkey update if a certificate was + * successfully matched. + */ + if (options.update_hostkeys != 0) { + options.update_hostkeys = 0; + debug3_f("certificate host key in use; " + "disabling UpdateHostkeys"); + } + } + /* Turn off UpdateHostkeys if key was in system known_hosts */ + if (options.update_hostkeys != 0 && + (path_in_hostfiles(host_found->file, + system_hostfiles, num_system_hostfiles) || + (ip_status == HOST_OK && ip_found != NULL && + path_in_hostfiles(ip_found->file, + system_hostfiles, num_system_hostfiles)))) { + options.update_hostkeys = 0; + debug3_f("host key found in GlobalKnownHostsFile; " + "disabling UpdateHostkeys"); + } + if (options.update_hostkeys != 0 && host_found->note) { + options.update_hostkeys = 0; + debug3_f("host key found via KnownHostsCommand; " + "disabling UpdateHostkeys"); + } + if (options.check_host_ip && ip_status == HOST_NEW) { + if (readonly || want_cert) + logit("%s host key for IP address " + "'%.128s' not in list of known hosts.", + type, ip); + else if (!add_host_to_hostfile(user_hostfiles[0], ip, + host_key, options.hash_known_hosts)) + logit("Failed to add the %s host key for IP " + "address '%.128s' to the list of known " + "hosts (%.500s).", type, ip, + user_hostfiles[0]); + else + logit("Warning: Permanently added the %s host " + "key for IP address '%.128s' to the list " + "of known hosts.", type, ip); + } else if (options.visual_host_key) { + fp = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT); + ra = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal_f("sshkey_fingerprint failed"); + logit("Host key fingerprint is %s\n%s", fp, ra); + free(ra); + free(fp); + } + hostkey_trusted = 1; + break; + case HOST_NEW: + if (options.host_key_alias == NULL && port != 0 && + port != SSH_DEFAULT_PORT && !clobber_port) { + debug("checking without port identifier"); + if (check_host_key(hostname, cinfo, hostaddr, 0, + host_key, ROQUIET, 1, + user_hostfiles, num_user_hostfiles, + system_hostfiles, num_system_hostfiles, + hostfile_command) == 0) { + debug("found matching key w/out port"); + break; + } + } + if (readonly || want_cert) + goto fail; + /* The host is new. */ + if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_YES) { + /* + * User has requested strict host key checking. We + * will not add the host key automatically. The only + * alternative left is to abort. + */ + error("No %s host key is known for %.200s and you " + "have requested strict checking.", type, host); + goto fail; + } else if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_ASK) { + char *msg1 = NULL, *msg2 = NULL; + + xasprintf(&msg1, "The authenticity of host " + "'%.200s (%s)' can't be established", host, ip); + + if (show_other_keys(host_hostkeys, host_key)) { + xextendf(&msg1, "\n", "but keys of different " + "type are already known for this host."); + } else + xextendf(&msg1, "", "."); + + fp = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT); + ra = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal_f("sshkey_fingerprint failed"); + xextendf(&msg1, "\n", "%s key fingerprint is %s.", + type, fp); + if (options.visual_host_key) + xextendf(&msg1, "\n", "%s", ra); + if (options.verify_host_key_dns) { + xextendf(&msg1, "\n", + "%s host key fingerprint found in DNS.", + matching_host_key_dns ? + "Matching" : "No matching"); + } + /* msg2 informs for other names matching this key */ + if ((msg2 = other_hostkeys_message(host, ip, host_key, + user_hostfiles, num_user_hostfiles, + system_hostfiles, num_system_hostfiles)) != NULL) + xextendf(&msg1, "\n", "%s", msg2); + + xextendf(&msg1, "\n", + "Are you sure you want to continue connecting " + "(yes/no/[fingerprint])? "); + + confirmed = confirm(msg1, fp); + free(ra); + free(fp); + free(msg1); + free(msg2); + if (!confirmed) + goto fail; + hostkey_trusted = 1; /* user explicitly confirmed */ + } + /* + * If in "new" or "off" strict mode, add the key automatically + * to the local known_hosts file. + */ + if (options.check_host_ip && ip_status == HOST_NEW) { + snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); + hostp = hostline; + if (options.hash_known_hosts) { + /* Add hash of host and IP separately */ + r = add_host_to_hostfile(user_hostfiles[0], + host, host_key, options.hash_known_hosts) && + add_host_to_hostfile(user_hostfiles[0], ip, + host_key, options.hash_known_hosts); + } else { + /* Add unhashed "host,ip" */ + r = add_host_to_hostfile(user_hostfiles[0], + hostline, host_key, + options.hash_known_hosts); + } + } else { + r = add_host_to_hostfile(user_hostfiles[0], host, + host_key, options.hash_known_hosts); + hostp = host; + } + + if (!r) + logit("Failed to add the host to the list of known " + "hosts (%.500s).", user_hostfiles[0]); + else + logit("Warning: Permanently added '%.200s' (%s) to the " + "list of known hosts.", hostp, type); + break; + case HOST_REVOKED: + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: REVOKED HOST KEY DETECTED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("The %s host key for %s is marked as revoked.", type, host); + error("This could mean that a stolen key is being used to"); + error("impersonate this host."); + + /* + * If strict host key checking is in use, the user will have + * to edit the key manually and we can only abort. + */ + if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { + error("%s host key for %.200s was revoked and you have " + "requested strict checking.", type, host); + goto fail; + } + goto continue_unsafe; + + case HOST_CHANGED: + if (want_cert) { + /* + * This is only a debug() since it is valid to have + * CAs with wildcard DNS matches that don't match + * all hosts that one might visit. + */ + debug("Host certificate authority does not " + "match %s in %s:%lu", CA_MARKER, + host_found->file, host_found->line); + goto fail; + } + if (readonly == ROQUIET) + goto fail; + if (options.check_host_ip && host_ip_differ) { + char *key_msg; + if (ip_status == HOST_NEW) + key_msg = "is unknown"; + else if (ip_status == HOST_OK) + key_msg = "is unchanged"; + else + key_msg = "has a different value"; + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("The %s host key for %s has changed,", type, host); + error("and the key for the corresponding IP address %s", ip); + error("%s. This could either mean that", key_msg); + error("DNS SPOOFING is happening or the IP address for the host"); + error("and its host key have changed at the same time."); + if (ip_status != HOST_NEW) + error("Offending key for IP in %s:%lu", + ip_found->file, ip_found->line); + } + /* The host key has changed. */ + warn_changed_key(host_key); + error("Add correct host key in %.100s to get rid of this message.", + user_hostfiles[0]); + error("Offending %s key in %s:%lu", + sshkey_type(host_found->key), + host_found->file, host_found->line); + + /* + * If strict host key checking is in use, the user will have + * to edit the key manually and we can only abort. + */ + if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { + error("Host key for %.200s has changed and you have " + "requested strict checking.", host); + goto fail; + } + + continue_unsafe: + /* + * If strict host key checking has not been requested, allow + * the connection but without MITM-able authentication or + * forwarding. + */ + if (options.password_authentication) { + error("Password authentication is disabled to avoid " + "man-in-the-middle attacks."); + options.password_authentication = 0; + cancelled_forwarding = 1; + } + if (options.kbd_interactive_authentication) { + error("Keyboard-interactive authentication is disabled" + " to avoid man-in-the-middle attacks."); + options.kbd_interactive_authentication = 0; + cancelled_forwarding = 1; + } + if (options.forward_agent) { + error("Agent forwarding is disabled to avoid " + "man-in-the-middle attacks."); + options.forward_agent = 0; + cancelled_forwarding = 1; + } + if (options.forward_x11) { + error("X11 forwarding is disabled to avoid " + "man-in-the-middle attacks."); + options.forward_x11 = 0; + cancelled_forwarding = 1; + } + if (options.num_local_forwards > 0 || + options.num_remote_forwards > 0) { + error("Port forwarding is disabled to avoid " + "man-in-the-middle attacks."); + options.num_local_forwards = + options.num_remote_forwards = 0; + cancelled_forwarding = 1; + } + if (options.tun_open != SSH_TUNMODE_NO) { + error("Tunnel forwarding is disabled to avoid " + "man-in-the-middle attacks."); + options.tun_open = SSH_TUNMODE_NO; + cancelled_forwarding = 1; + } + if (options.update_hostkeys != 0) { + error("UpdateHostkeys is disabled because the host " + "key is not trusted."); + options.update_hostkeys = 0; + } + if (options.exit_on_forward_failure && cancelled_forwarding) + fatal("Error: forwarding disabled due to host key " + "check failure"); + + /* + * XXX Should permit the user to change to use the new id. + * This could be done by converting the host key to an + * identifying sentence, tell that the host identifies itself + * by that sentence, and ask the user if they wish to + * accept the authentication. + */ + break; + case HOST_FOUND: + fatal("internal error"); + break; + } + + if (options.check_host_ip && host_status != HOST_CHANGED && + ip_status == HOST_CHANGED) { + snprintf(msg, sizeof(msg), + "Warning: the %s host key for '%.200s' " + "differs from the key for the IP address '%.128s'" + "\nOffending key for IP in %s:%lu", + type, host, ip, ip_found->file, ip_found->line); + if (host_status == HOST_OK) { + len = strlen(msg); + snprintf(msg + len, sizeof(msg) - len, + "\nMatching host key in %s:%lu", + host_found->file, host_found->line); + } + if (options.strict_host_key_checking == + SSH_STRICT_HOSTKEY_ASK) { + strlcat(msg, "\nAre you sure you want " + "to continue connecting (yes/no)? ", sizeof(msg)); + if (!confirm(msg, NULL)) + goto fail; + } else if (options.strict_host_key_checking != + SSH_STRICT_HOSTKEY_OFF) { + logit("%s", msg); + error("Exiting, you have requested strict checking."); + goto fail; + } else { + logit("%s", msg); + } + } + + if (!hostkey_trusted && options.update_hostkeys) { + debug_f("hostkey not known or explicitly trusted: " + "disabling UpdateHostkeys"); + options.update_hostkeys = 0; + } + + free(ip); + free(host); + if (host_hostkeys != NULL) + free_hostkeys(host_hostkeys); + if (ip_hostkeys != NULL) + free_hostkeys(ip_hostkeys); + return 0; + +fail: + if (want_cert && host_status != HOST_REVOKED) { + /* + * No matching certificate. Downgrade cert to raw key and + * search normally. + */ + debug("No matching CA found. Retry with plain key"); + if ((r = sshkey_from_private(host_key, &raw_key)) != 0) + fatal_fr(r, "decode key"); + if ((r = sshkey_drop_cert(raw_key)) != 0) + fatal_r(r, "Couldn't drop certificate"); + host_key = raw_key; + goto retry; + } + sshkey_free(raw_key); + free(ip); + free(host); + if (host_hostkeys != NULL) + free_hostkeys(host_hostkeys); + if (ip_hostkeys != NULL) + free_hostkeys(ip_hostkeys); + return -1; +} + +/* returns 0 if key verifies or -1 if key does NOT verify */ +int +verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key, + const struct ssh_conn_info *cinfo) +{ + u_int i; + int r = -1, flags = 0; + char valid[64], *fp = NULL, *cafp = NULL; + struct sshkey *plain = NULL; + + if ((fp = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { + error_fr(r, "fingerprint host key"); + r = -1; + goto out; + } + + if (sshkey_is_cert(host_key)) { + if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { + error_fr(r, "fingerprint CA key"); + r = -1; + goto out; + } + sshkey_format_cert_validity(host_key->cert, + valid, sizeof(valid)); + debug("Server host certificate: %s %s, serial %llu " + "ID \"%s\" CA %s %s valid %s", + sshkey_ssh_name(host_key), fp, + (unsigned long long)host_key->cert->serial, + host_key->cert->key_id, + sshkey_ssh_name(host_key->cert->signature_key), cafp, + valid); + for (i = 0; i < host_key->cert->nprincipals; i++) { + debug2("Server host certificate hostname: %s", + host_key->cert->principals[i]); + } + } else { + debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); + } + + if (sshkey_equal(previous_host_key, host_key)) { + debug2_f("server host key %s %s matches cached key", + sshkey_type(host_key), fp); + r = 0; + goto out; + } + + /* Check in RevokedHostKeys file if specified */ + if (options.revoked_host_keys != NULL) { + r = sshkey_check_revoked(host_key, options.revoked_host_keys); + switch (r) { + case 0: + break; /* not revoked */ + case SSH_ERR_KEY_REVOKED: + error("Host key %s %s revoked by file %s", + sshkey_type(host_key), fp, + options.revoked_host_keys); + r = -1; + goto out; + default: + error_r(r, "Error checking host key %s %s in " + "revoked keys file %s", sshkey_type(host_key), + fp, options.revoked_host_keys); + r = -1; + goto out; + } + } + + if (options.verify_host_key_dns) { + /* + * XXX certs are not yet supported for DNS, so downgrade + * them and try the plain key. + */ + if ((r = sshkey_from_private(host_key, &plain)) != 0) + goto out; + if (sshkey_is_cert(plain)) + sshkey_drop_cert(plain); + if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { + if (flags & DNS_VERIFY_FOUND) { + if (options.verify_host_key_dns == 1 && + flags & DNS_VERIFY_MATCH && + flags & DNS_VERIFY_SECURE) { + r = 0; + goto out; + } + if (flags & DNS_VERIFY_MATCH) { + matching_host_key_dns = 1; + } else { + warn_changed_key(plain); + error("Update the SSHFP RR in DNS " + "with the new host key to get rid " + "of this message."); + } + } + } + } + r = check_host_key(host, cinfo, hostaddr, options.port, host_key, + RDRW, 0, options.user_hostfiles, options.num_user_hostfiles, + options.system_hostfiles, options.num_system_hostfiles, + options.known_hosts_command); + +out: + sshkey_free(plain); + free(fp); + free(cafp); + if (r == 0 && host_key != NULL) { + sshkey_free(previous_host_key); + r = sshkey_from_private(host_key, &previous_host_key); + } + + return r; +} + +/* + * Starts a dialog with the server, and authenticates the current user on the + * server. This does not need any extra privileges. The basic connection + * to the server must already have been established before this is called. + * If login fails, this function prints an error and never returns. + * This function does not require super-user privileges. + */ +void +ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost, + struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms, + const struct ssh_conn_info *cinfo) +{ + char *host; + char *server_user, *local_user; + int r; + + local_user = xstrdup(pw->pw_name); + server_user = options.user ? options.user : local_user; + + /* Convert the user-supplied hostname into all lowercase. */ + host = xstrdup(orighost); + lowercase(host); + + /* Exchange protocol version identification strings with the server. */ + if ((r = kex_exchange_identification(ssh, timeout_ms, NULL)) != 0) + sshpkt_fatal(ssh, r, "banner exchange"); + + /* Put the connection into non-blocking mode. */ + ssh_packet_set_nonblocking(ssh); + + /* key exchange */ + /* authenticate user */ + debug("Authenticating to %s:%d as '%s'", host, port, server_user); + ssh_kex2(ssh, host, hostaddr, port, cinfo); + ssh_userauth2(ssh, local_user, server_user, host, sensitive); + free(local_user); + free(host); +} + +/* print all known host keys for a given host, but skip keys of given type */ +static int +show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) +{ + int type[] = { + KEY_RSA, + KEY_DSA, + KEY_ECDSA, + KEY_ED25519, + KEY_XMSS, + -1 + }; + int i, ret = 0; + char *fp, *ra; + const struct hostkey_entry *found; + + for (i = 0; type[i] != -1; i++) { + if (type[i] == key->type) + continue; + if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], + -1, &found)) + continue; + fp = sshkey_fingerprint(found->key, + options.fingerprint_hash, SSH_FP_DEFAULT); + ra = sshkey_fingerprint(found->key, + options.fingerprint_hash, SSH_FP_RANDOMART); + if (fp == NULL || ra == NULL) + fatal_f("sshkey_fingerprint fail"); + logit("WARNING: %s key found for host %s\n" + "in %s:%lu\n" + "%s key fingerprint %s.", + sshkey_type(found->key), + found->host, found->file, found->line, + sshkey_type(found->key), fp); + if (options.visual_host_key) + logit("%s", ra); + free(ra); + free(fp); + ret = 1; + } + return ret; +} + +static void +warn_changed_key(struct sshkey *host_key) +{ + char *fp; + + fp = sshkey_fingerprint(host_key, options.fingerprint_hash, + SSH_FP_DEFAULT); + if (fp == NULL) + fatal_f("sshkey_fingerprint fail"); + + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); + error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); + error("It is also possible that a host key has just been changed."); + error("The fingerprint for the %s key sent by the remote host is\n%s.", + sshkey_type(host_key), fp); + error("Please contact your system administrator."); + + free(fp); +} + +/* + * Execute a local command + */ +int +ssh_local_cmd(const char *args) +{ + char *shell; + pid_t pid; + int status; + void (*osighand)(int); + + if (!options.permit_local_command || + args == NULL || !*args) + return (1); + + if ((shell = getenv("SHELL")) == NULL || *shell == '\0') + shell = _PATH_BSHELL; + + osighand = ssh_signal(SIGCHLD, SIG_DFL); + pid = fork(); + if (pid == 0) { + ssh_signal(SIGPIPE, SIG_DFL); + debug3("Executing %s -c \"%s\"", shell, args); + execl(shell, shell, "-c", args, (char *)NULL); + error("Couldn't execute %s -c \"%s\": %s", + shell, args, strerror(errno)); + _exit(1); + } else if (pid == -1) + fatal("fork failed: %.100s", strerror(errno)); + while (waitpid(pid, &status, 0) == -1) + if (errno != EINTR) + fatal("Couldn't wait for child: %s", strerror(errno)); + ssh_signal(SIGCHLD, osighand); + + if (!WIFEXITED(status)) + return (1); + + return (WEXITSTATUS(status)); +} + +void +maybe_add_key_to_agent(const char *authfile, struct sshkey *private, + const char *comment, const char *passphrase) +{ + int auth_sock = -1, r; + const char *skprovider = NULL; + + if (options.add_keys_to_agent == 0) + return; + + if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { + debug3("no authentication agent, not adding key"); + return; + } + + if (options.add_keys_to_agent == 2 && + !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { + debug3("user denied adding this key"); + close(auth_sock); + return; + } + if (sshkey_is_sk(private)) + skprovider = options.sk_provider; + if ((r = ssh_add_identity_constrained(auth_sock, private, + comment == NULL ? authfile : comment, + options.add_keys_to_agent_lifespan, + (options.add_keys_to_agent == 3), 0, skprovider, NULL, 0)) == 0) + debug("identity added to agent: %s", authfile); + else + debug("could not add identity to agent: %s (%d)", authfile, r); + close(auth_sock); +} diff --git a/aosp/external/openssh/sshconnect.h b/aosp/external/openssh/sshconnect.h new file mode 100644 index 0000000000000000000000000000000000000000..f518a9a1302f5192269e9dd73008084198f07a85 --- /dev/null +++ b/aosp/external/openssh/sshconnect.h @@ -0,0 +1,94 @@ +/* $OpenBSD: sshconnect.h,v 1.46 2020/12/22 00:15:23 djm Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +typedef struct Sensitive Sensitive; +struct Sensitive { + struct sshkey **keys; + int nkeys; +}; + +struct ssh_conn_info { + char *conn_hash_hex; + char *shorthost; + char *uidstr; + char *keyalias; + char *thishost; + char *host_arg; + char *portstr; + char *remhost; + char *remuser; + char *homedir; + char *locuser; +}; + +struct addrinfo; +struct ssh; +struct hostkeys; +struct ssh_conn_info; + +/* default argument for client percent expansions */ +#define DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(conn_info) \ + "C", conn_info->conn_hash_hex, \ + "L", conn_info->shorthost, \ + "i", conn_info->uidstr, \ + "k", conn_info->keyalias, \ + "l", conn_info->thishost, \ + "n", conn_info->host_arg, \ + "p", conn_info->portstr, \ + "d", conn_info->homedir, \ + "h", conn_info->remhost, \ + "r", conn_info->remuser, \ + "u", conn_info->locuser + +int ssh_connect(struct ssh *, const char *, const char *, + struct addrinfo *, struct sockaddr_storage *, u_short, + int, int *, int); +void ssh_kill_proxy_command(void); + +void ssh_login(struct ssh *, Sensitive *, const char *, + struct sockaddr *, u_short, struct passwd *, int, + const struct ssh_conn_info *); + +int verify_host_key(char *, struct sockaddr *, struct sshkey *, + const struct ssh_conn_info *); + +void get_hostfile_hostname_ipaddr(char *, struct sockaddr *, u_short, + char **, char **); + +void ssh_kex2(struct ssh *ssh, char *, struct sockaddr *, u_short, + const struct ssh_conn_info *); + +void ssh_userauth2(struct ssh *ssh, const char *, const char *, + char *, Sensitive *); + +int ssh_local_cmd(const char *); + +void maybe_add_key_to_agent(const char *, struct sshkey *, + const char *, const char *); + +void load_hostkeys_command(struct hostkeys *, const char *, + const char *, const struct ssh_conn_info *, + const struct sshkey *, const char *); diff --git a/aosp/external/openssh/sshconnect2.c b/aosp/external/openssh/sshconnect2.c new file mode 100644 index 0000000000000000000000000000000000000000..b25225e645cb80349e4d4029da9d8945d2c69f30 --- /dev/null +++ b/aosp/external/openssh/sshconnect2.c @@ -0,0 +1,2376 @@ +/* $OpenBSD: sshconnect2.c,v 1.356 2022/02/01 23:32:51 djm Exp $ */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2008 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +#include +#endif + +#include "openbsd-compat/sys-queue.h" + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "sshbuf.h" +#include "packet.h" +#include "compat.h" +#include "cipher.h" +#include "sshkey.h" +#include "kex.h" +#include "myproposal.h" +#include "sshconnect.h" +#include "authfile.h" +#include "dh.h" +#include "authfd.h" +#include "log.h" +#include "misc.h" +#include "readconf.h" +#include "match.h" +#include "dispatch.h" +#include "canohost.h" +#include "msg.h" +#include "pathnames.h" +#include "uidswap.h" +#include "hostfile.h" +#include "ssherr.h" +#include "utf8.h" +#include "ssh-sk.h" +#include "sk-api.h" + +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + +/* import */ +extern char *client_version_string; +extern char *server_version_string; +extern Options options; + +/* + * SSH2 key exchange + */ + +static char *xxx_host; +static struct sockaddr *xxx_hostaddr; +static const struct ssh_conn_info *xxx_conn_info; + +static int +verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) +{ + if (verify_host_key(xxx_host, xxx_hostaddr, hostkey, + xxx_conn_info) == -1) + fatal("Host key verification failed."); + return 0; +} + +/* Returns the first item from a comma-separated algorithm list */ +static char * +first_alg(const char *algs) +{ + char *ret, *cp; + + ret = xstrdup(algs); + if ((cp = strchr(ret, ',')) != NULL) + *cp = '\0'; + return ret; +} + +static char * +order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port, + const struct ssh_conn_info *cinfo) +{ + char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL; + char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL; + size_t maxlen; + struct hostkeys *hostkeys = NULL; + int ktype; + u_int i; + + /* Find all hostkeys for this hostname */ + get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL); + hostkeys = init_hostkeys(); + for (i = 0; i < options.num_user_hostfiles; i++) + load_hostkeys(hostkeys, hostname, options.user_hostfiles[i], 0); + for (i = 0; i < options.num_system_hostfiles; i++) { + load_hostkeys(hostkeys, hostname, + options.system_hostfiles[i], 0); + } + if (options.known_hosts_command != NULL) { + load_hostkeys_command(hostkeys, options.known_hosts_command, + "ORDER", cinfo, NULL, host); + } + /* + * If a plain public key exists that matches the type of the best + * preference HostkeyAlgorithms, then use the whole list as is. + * Note that we ignore whether the best preference algorithm is a + * certificate type, as sshconnect.c will downgrade certs to + * plain keys if necessary. + */ + best = first_alg(options.hostkeyalgorithms); + if (lookup_key_in_hostkeys_by_type(hostkeys, + sshkey_type_plain(sshkey_type_from_name(best)), + sshkey_ecdsa_nid_from_name(best), NULL)) { + debug3_f("have matching best-preference key type %s, " + "using HostkeyAlgorithms verbatim", best); + ret = xstrdup(options.hostkeyalgorithms); + goto out; + } + + /* + * Otherwise, prefer the host key algorithms that match known keys + * while keeping the ordering of HostkeyAlgorithms as much as possible. + */ + oavail = avail = xstrdup(options.hostkeyalgorithms); + maxlen = strlen(avail) + 1; + first = xmalloc(maxlen); + last = xmalloc(maxlen); + *first = *last = '\0'; + +#define ALG_APPEND(to, from) \ + do { \ + if (*to != '\0') \ + strlcat(to, ",", maxlen); \ + strlcat(to, from, maxlen); \ + } while (0) + + while ((alg = strsep(&avail, ",")) && *alg != '\0') { + if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) + fatal_f("unknown alg %s", alg); + /* + * If we have a @cert-authority marker in known_hosts then + * prefer all certificate algorithms. + */ + if (sshkey_type_is_cert(ktype) && + lookup_marker_in_hostkeys(hostkeys, MRK_CA)) { + ALG_APPEND(first, alg); + continue; + } + /* If the key appears in known_hosts then prefer it */ + if (lookup_key_in_hostkeys_by_type(hostkeys, + sshkey_type_plain(ktype), + sshkey_ecdsa_nid_from_name(alg), NULL)) { + ALG_APPEND(first, alg); + continue; + } + /* Otherwise, put it last */ + ALG_APPEND(last, alg); + } +#undef ALG_APPEND + xasprintf(&ret, "%s%s%s", first, + (*first == '\0' || *last == '\0') ? "" : ",", last); + if (*first != '\0') + debug3_f("prefer hostkeyalgs: %s", first); + else + debug3_f("no algorithms matched; accept original"); + out: + free(best); + free(first); + free(last); + free(hostname); + free(oavail); + free_hostkeys(hostkeys); + + return ret; +} + +void +ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port, + const struct ssh_conn_info *cinfo) +{ + char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; + char *s, *all_key; + int r, use_known_hosts_order = 0; + + xxx_host = host; + xxx_hostaddr = hostaddr; + xxx_conn_info = cinfo; + + /* + * If the user has not specified HostkeyAlgorithms, or has only + * appended or removed algorithms from that list then prefer algorithms + * that are in the list that are supported by known_hosts keys. + */ + if (options.hostkeyalgorithms == NULL || + options.hostkeyalgorithms[0] == '-' || + options.hostkeyalgorithms[0] == '+') + use_known_hosts_order = 1; + + /* Expand or fill in HostkeyAlgorithms */ + all_key = sshkey_alg_list(0, 0, 1, ','); + if ((r = kex_assemble_names(&options.hostkeyalgorithms, + kex_default_pk_alg(), all_key)) != 0) + fatal_fr(r, "kex_assemble_namelist"); + free(all_key); + + if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) + fatal_f("kex_names_cat"); + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(ssh, s); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + compat_cipher_proposal(ssh, options.ciphers); + myproposal[PROPOSAL_ENC_ALGS_STOC] = + compat_cipher_proposal(ssh, options.ciphers); + myproposal[PROPOSAL_COMP_ALGS_CTOS] = + myproposal[PROPOSAL_COMP_ALGS_STOC] = + (char *)compression_alg_list(options.compression); + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; + if (use_known_hosts_order) { + /* Query known_hosts and prefer algorithms that appear there */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal(ssh, + order_hostkeyalgs(host, hostaddr, port, cinfo)); + } else { + /* Use specified HostkeyAlgorithms exactly */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal(ssh, options.hostkeyalgorithms); + } + + if (options.rekey_limit || options.rekey_interval) + ssh_packet_set_rekey_limits(ssh, options.rekey_limit, + options.rekey_interval); + + /* start key exchange */ + if ((r = kex_setup(ssh, myproposal)) != 0) + fatal_r(r, "kex_setup"); +#ifdef WITH_OPENSSL + ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; + ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; + ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; + ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; +# ifdef OPENSSL_HAS_ECC + ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; +# endif +#endif + ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; + ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; + ssh->kex->verify_host_key=&verify_host_key_callback; + + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); + + /* remove ext-info from the KEX proposals for rekeying */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(ssh, options.kex_algorithms); + if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) + fatal_r(r, "kex_prop2buf"); + +#ifdef DEBUG_KEXDH + /* send 1st encrypted/maced/compressed message */ + if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || + (r = sshpkt_put_cstring(ssh, "markus")) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal_fr(r, "send packet"); +#endif +} + +/* + * Authenticate user + */ + +typedef struct cauthctxt Authctxt; +typedef struct cauthmethod Authmethod; +typedef struct identity Identity; +typedef struct idlist Idlist; + +struct identity { + TAILQ_ENTRY(identity) next; + int agent_fd; /* >=0 if agent supports key */ + struct sshkey *key; /* public/private key */ + char *filename; /* comment for agent-only keys */ + int tried; + int isprivate; /* key points to the private key */ + int userprovided; +}; +TAILQ_HEAD(idlist, identity); + +struct cauthctxt { + const char *server_user; + const char *local_user; + const char *host; + const char *service; + struct cauthmethod *method; + sig_atomic_t success; + char *authlist; +#ifdef GSSAPI + /* gssapi */ + gss_OID_set gss_supported_mechs; + u_int mech_tried; +#endif + /* pubkey */ + struct idlist keys; + int agent_fd; + /* hostbased */ + Sensitive *sensitive; + char *oktypes, *ktypes; + const char *active_ktype; + /* kbd-interactive */ + int info_req_seen; + int attempt_kbdint; + /* password */ + int attempt_passwd; + /* generic */ + void *methoddata; +}; + +struct cauthmethod { + char *name; /* string to compare against server's list */ + int (*userauth)(struct ssh *ssh); + void (*cleanup)(struct ssh *ssh); + int *enabled; /* flag in option struct that enables method */ + int *batch_flag; /* flag in option struct that disables method */ +}; + +static int input_userauth_service_accept(int, u_int32_t, struct ssh *); +static int input_userauth_ext_info(int, u_int32_t, struct ssh *); +static int input_userauth_success(int, u_int32_t, struct ssh *); +static int input_userauth_failure(int, u_int32_t, struct ssh *); +static int input_userauth_banner(int, u_int32_t, struct ssh *); +static int input_userauth_error(int, u_int32_t, struct ssh *); +static int input_userauth_info_req(int, u_int32_t, struct ssh *); +static int input_userauth_pk_ok(int, u_int32_t, struct ssh *); +static int input_userauth_passwd_changereq(int, u_int32_t, struct ssh *); + +static int userauth_none(struct ssh *); +static int userauth_pubkey(struct ssh *); +static int userauth_passwd(struct ssh *); +static int userauth_kbdint(struct ssh *); +static int userauth_hostbased(struct ssh *); + +#ifdef GSSAPI +static int userauth_gssapi(struct ssh *); +static void userauth_gssapi_cleanup(struct ssh *); +static int input_gssapi_response(int type, u_int32_t, struct ssh *); +static int input_gssapi_token(int type, u_int32_t, struct ssh *); +static int input_gssapi_error(int, u_int32_t, struct ssh *); +static int input_gssapi_errtok(int, u_int32_t, struct ssh *); +#endif + +void userauth(struct ssh *, char *); + +static void pubkey_cleanup(struct ssh *); +static int sign_and_send_pubkey(struct ssh *ssh, Identity *); +static void pubkey_prepare(struct ssh *, Authctxt *); +static void pubkey_reset(Authctxt *); +static struct sshkey *load_identity_file(Identity *); + +static Authmethod *authmethod_get(char *authlist); +static Authmethod *authmethod_lookup(const char *name); +static char *authmethods_get(void); + +Authmethod authmethods[] = { +#ifdef GSSAPI + {"gssapi-with-mic", + userauth_gssapi, + userauth_gssapi_cleanup, + &options.gss_authentication, + NULL}, +#endif + {"hostbased", + userauth_hostbased, + NULL, + &options.hostbased_authentication, + NULL}, + {"publickey", + userauth_pubkey, + NULL, + &options.pubkey_authentication, + NULL}, + {"keyboard-interactive", + userauth_kbdint, + NULL, + &options.kbd_interactive_authentication, + &options.batch_mode}, + {"password", + userauth_passwd, + NULL, + &options.password_authentication, + &options.batch_mode}, + {"none", + userauth_none, + NULL, + NULL, + NULL}, + {NULL, NULL, NULL, NULL, NULL} +}; + +void +ssh_userauth2(struct ssh *ssh, const char *local_user, + const char *server_user, char *host, Sensitive *sensitive) +{ + Authctxt authctxt; + int r; + + if (options.preferred_authentications == NULL) + options.preferred_authentications = authmethods_get(); + + /* setup authentication context */ + memset(&authctxt, 0, sizeof(authctxt)); + authctxt.server_user = server_user; + authctxt.local_user = local_user; + authctxt.host = host; + authctxt.service = "ssh-connection"; /* service name */ + authctxt.success = 0; + authctxt.method = authmethod_lookup("none"); + authctxt.authlist = NULL; + authctxt.methoddata = NULL; + authctxt.sensitive = sensitive; + authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL; + authctxt.info_req_seen = 0; + authctxt.attempt_kbdint = 0; + authctxt.attempt_passwd = 0; +#if GSSAPI + authctxt.gss_supported_mechs = NULL; + authctxt.mech_tried = 0; +#endif + authctxt.agent_fd = -1; + pubkey_prepare(ssh, &authctxt); + if (authctxt.method == NULL) { + fatal_f("internal error: cannot send userauth none request"); + } + + if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + + ssh->authctxt = &authctxt; + ssh_dispatch_init(ssh, &input_userauth_error); + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info); + ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept); + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */ + pubkey_cleanup(ssh); + ssh->authctxt = NULL; + + ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); + + if (!authctxt.success) + fatal("Authentication failed."); + if (ssh_packet_connection_is_on_socket(ssh)) { + verbose("Authenticated to %s ([%s]:%d) using \"%s\".", host, + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + authctxt.method->name); + } else { + verbose("Authenticated to %s (via proxy) using \"%s\".", host, + authctxt.method->name); + } +} + +/* ARGSUSED */ +static int +input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh) +{ + int r; + + if (ssh_packet_remaining(ssh) > 0) { + char *reply; + + if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0) + goto out; + debug2("service_accept: %s", reply); + free(reply); + } else { + debug2("buggy server: service_accept w/o service"); + } + if ((r = sshpkt_get_end(ssh)) != 0) + goto out; + debug("SSH2_MSG_SERVICE_ACCEPT received"); + + /* initial userauth request */ + userauth_none(ssh); + + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); + r = 0; + out: + return r; +} + +/* ARGSUSED */ +static int +input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh) +{ + return kex_input_ext_info(type, seqnr, ssh); +} + +void +userauth(struct ssh *ssh, char *authlist) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + + if (authctxt->method != NULL && authctxt->method->cleanup != NULL) + authctxt->method->cleanup(ssh); + + free(authctxt->methoddata); + authctxt->methoddata = NULL; + if (authlist == NULL) { + authlist = authctxt->authlist; + } else { + free(authctxt->authlist); + authctxt->authlist = authlist; + } + for (;;) { + Authmethod *method = authmethod_get(authlist); + if (method == NULL) + fatal("%s@%s: Permission denied (%s).", + authctxt->server_user, authctxt->host, authlist); + authctxt->method = method; + + /* reset the per method handler */ + ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_PER_METHOD_MIN, + SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL); + + /* and try new method */ + if (method->userauth(ssh) != 0) { + debug2("we sent a %s packet, wait for reply", method->name); + break; + } else { + debug2("we did not send a packet, disable method"); + method->enabled = NULL; + } + } +} + +/* ARGSUSED */ +static int +input_userauth_error(int type, u_int32_t seq, struct ssh *ssh) +{ + fatal_f("bad message during authentication: type %d", type); + return 0; +} + +/* ARGSUSED */ +static int +input_userauth_banner(int type, u_int32_t seq, struct ssh *ssh) +{ + char *msg = NULL; + size_t len; + int r; + + debug3_f("entering"); + if ((r = sshpkt_get_cstring(ssh, &msg, &len)) != 0 || + (r = sshpkt_get_cstring(ssh, NULL, NULL)) != 0) + goto out; + if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) + fmprintf(stderr, "%s", msg); + r = 0; + out: + free(msg); + return r; +} + +/* ARGSUSED */ +static int +input_userauth_success(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + + if (authctxt == NULL) + fatal_f("no authentication context"); + free(authctxt->authlist); + authctxt->authlist = NULL; + if (authctxt->method != NULL && authctxt->method->cleanup != NULL) + authctxt->method->cleanup(ssh); + free(authctxt->methoddata); + authctxt->methoddata = NULL; + authctxt->success = 1; /* break out */ + return 0; +} + +#if 0 +static int +input_userauth_success_unexpected(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + + if (authctxt == NULL) + fatal_f("no authentication context"); + + fatal("Unexpected authentication success during %s.", + authctxt->method->name); + return 0; +} +#endif + +/* ARGSUSED */ +static int +input_userauth_failure(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + char *authlist = NULL; + u_char partial; + + if (authctxt == NULL) + fatal("input_userauth_failure: no authentication context"); + + if (sshpkt_get_cstring(ssh, &authlist, NULL) != 0 || + sshpkt_get_u8(ssh, &partial) != 0 || + sshpkt_get_end(ssh) != 0) + goto out; + + if (partial != 0) { + verbose("Authenticated using \"%s\" with partial success.", + authctxt->method->name); + /* reset state */ + pubkey_reset(authctxt); + } + debug("Authentications that can continue: %s", authlist); + + userauth(ssh, authlist); + authlist = NULL; + out: + free(authlist); + return 0; +} + +/* + * Format an identity for logging including filename, key type, fingerprint + * and location (agent, etc.). Caller must free. + */ +static char * +format_identity(Identity *id) +{ + char *fp = NULL, *ret = NULL; + const char *note = ""; + + if (id->key != NULL) { + fp = sshkey_fingerprint(id->key, options.fingerprint_hash, + SSH_FP_DEFAULT); + } + if (id->key) { + if ((id->key->flags & SSHKEY_FLAG_EXT) != 0) + note = " token"; + else if (sshkey_is_sk(id->key)) + note = " authenticator"; + } + xasprintf(&ret, "%s %s%s%s%s%s%s", + id->filename, + id->key ? sshkey_type(id->key) : "", id->key ? " " : "", + fp ? fp : "", + id->userprovided ? " explicit" : "", note, + id->agent_fd != -1 ? " agent" : ""); + free(fp); + return ret; +} + +/* ARGSUSED */ +static int +input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + struct sshkey *key = NULL; + Identity *id = NULL; + int pktype, found = 0, sent = 0; + size_t blen; + char *pkalg = NULL, *fp = NULL, *ident = NULL; + u_char *pkblob = NULL; + int r; + + if (authctxt == NULL) + fatal("input_userauth_pk_ok: no authentication context"); + + if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || + (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto done; + + if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) { + debug_f("server sent unknown pkalg %s", pkalg); + goto done; + } + if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { + debug_r(r, "no key from blob. pkalg %s", pkalg); + goto done; + } + if (key->type != pktype) { + error("input_userauth_pk_ok: type mismatch " + "for decoded key (received %d, expected %d)", + key->type, pktype); + goto done; + } + + /* + * search keys in the reverse order, because last candidate has been + * moved to the end of the queue. this also avoids confusion by + * duplicate keys + */ + TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { + if (sshkey_equal(key, id->key)) { + found = 1; + break; + } + } + if (!found || id == NULL) { + fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); + error_f("server replied with unknown key: %s %s", + sshkey_type(key), fp == NULL ? "" : fp); + goto done; + } + ident = format_identity(id); + debug("Server accepts key: %s", ident); + sent = sign_and_send_pubkey(ssh, id); + r = 0; + done: + sshkey_free(key); + free(ident); + free(fp); + free(pkalg); + free(pkblob); + + /* try another method if we did not send a packet */ + if (r == 0 && sent == 0) + userauth(ssh, NULL); + return r; +} + +#ifdef GSSAPI +static int +userauth_gssapi(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + Gssctxt *gssctxt = NULL; + OM_uint32 min; + int r, ok = 0; + gss_OID mech = NULL; + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (authctxt->gss_supported_mechs == NULL) + gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); + + /* Check to see whether the mechanism is usable before we offer it */ + while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && + !ok) { + mech = &authctxt->gss_supported_mechs-> + elements[authctxt->mech_tried]; + /* My DER encoding requires length<128 */ + if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, + mech, authctxt->host)) { + ok = 1; /* Mechanism works */ + } else { + authctxt->mech_tried++; + } + } + + if (!ok || mech == NULL) + return 0; + + authctxt->methoddata=(void *)gssctxt; + + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_u32(ssh, 1)) != 0 || + (r = sshpkt_put_u32(ssh, (mech->length) + 2)) != 0 || + (r = sshpkt_put_u8(ssh, SSH_GSS_OIDTYPE)) != 0 || + (r = sshpkt_put_u8(ssh, mech->length)) != 0 || + (r = sshpkt_put(ssh, mech->elements, mech->length)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); + + authctxt->mech_tried++; /* Move along to next candidate */ + + return 1; +} + +static void +userauth_gssapi_cleanup(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + Gssctxt *gssctxt = (Gssctxt *)authctxt->methoddata; + + ssh_gssapi_delete_ctx(&gssctxt); + authctxt->methoddata = NULL; + + free(authctxt->gss_supported_mechs); + authctxt->gss_supported_mechs = NULL; +} + +static OM_uint32 +process_gssapi_token(struct ssh *ssh, gss_buffer_t recv_tok) +{ + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt = authctxt->methoddata; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gssbuf; + OM_uint32 status, ms, flags; + int r; + + status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, + recv_tok, &send_tok, &flags); + + if (send_tok.length > 0) { + u_char type = GSS_ERROR(status) ? + SSH2_MSG_USERAUTH_GSSAPI_ERRTOK : + SSH2_MSG_USERAUTH_GSSAPI_TOKEN; + + if ((r = sshpkt_start(ssh, type)) != 0 || + (r = sshpkt_put_string(ssh, send_tok.value, + send_tok.length)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send %u packet", type); + + gss_release_buffer(&ms, &send_tok); + } + + if (status == GSS_S_COMPLETE) { + /* send either complete or MIC, depending on mechanism */ + if (!(flags & GSS_C_INTEG_FLAG)) { + if ((r = sshpkt_start(ssh, + SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send completion"); + } else { + struct sshbuf *b; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + ssh_gssapi_buildmic(b, authctxt->server_user, + authctxt->service, "gssapi-with-mic", + ssh->kex->session_id); + + if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) + fatal_f("sshbuf_mutable_ptr failed"); + gssbuf.length = sshbuf_len(b); + + status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic); + + if (!GSS_ERROR(status)) { + if ((r = sshpkt_start(ssh, + SSH2_MSG_USERAUTH_GSSAPI_MIC)) != 0 || + (r = sshpkt_put_string(ssh, mic.value, + mic.length)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send MIC"); + } + + sshbuf_free(b); + gss_release_buffer(&ms, &mic); + } + } + + return status; +} + +/* ARGSUSED */ +static int +input_gssapi_response(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt; + size_t oidlen; + u_char *oidv = NULL; + int r; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); + gssctxt = authctxt->methoddata; + + /* Setup our OID */ + if ((r = sshpkt_get_string(ssh, &oidv, &oidlen)) != 0) + goto done; + + if (oidlen <= 2 || + oidv[0] != SSH_GSS_OIDTYPE || + oidv[1] != oidlen - 2) { + debug("Badly encoded mechanism OID received"); + userauth(ssh, NULL); + goto ok; + } + + if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) + fatal("Server returned different OID than expected"); + + if ((r = sshpkt_get_end(ssh)) != 0) + goto done; + + if (GSS_ERROR(process_gssapi_token(ssh, GSS_C_NO_BUFFER))) { + /* Start again with next method on list */ + debug("Trying to start again"); + userauth(ssh, NULL); + goto ok; + } + ok: + r = 0; + done: + free(oidv); + return r; +} + +/* ARGSUSED */ +static int +input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + gss_buffer_desc recv_tok; + u_char *p = NULL; + size_t len; + OM_uint32 status; + int r; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); + + if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + + recv_tok.value = p; + recv_tok.length = len; + status = process_gssapi_token(ssh, &recv_tok); + + /* Start again with the next method in the list */ + if (GSS_ERROR(status)) { + userauth(ssh, NULL); + /* ok */ + } + r = 0; + out: + free(p); + return r; +} + +/* ARGSUSED */ +static int +input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt; + gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; + gss_buffer_desc recv_tok; + OM_uint32 ms; + u_char *p = NULL; + size_t len; + int r; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); + gssctxt = authctxt->methoddata; + + if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) { + free(p); + return r; + } + + /* Stick it into GSSAPI and see what it says */ + recv_tok.value = p; + recv_tok.length = len; + (void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, + &recv_tok, &send_tok, NULL); + free(p); + gss_release_buffer(&ms, &send_tok); + + /* Server will be returning a failed packet after this one */ + return 0; +} + +/* ARGSUSED */ +static int +input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) +{ + char *msg = NULL; + char *lang = NULL; + int r; + + if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* maj */ + (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* min */ + (r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0) + goto out; + r = sshpkt_get_end(ssh); + debug("Server GSSAPI Error:\n%s", msg); + out: + free(msg); + free(lang); + return r; +} +#endif /* GSSAPI */ + +static int +userauth_none(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + int r; + + /* initial userauth request */ + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + return 1; +} + +static int +userauth_passwd(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + char *password, *prompt = NULL; + const char *host = options.host_key_alias ? options.host_key_alias : + authctxt->host; + int r; + + if (authctxt->attempt_passwd++ >= options.number_of_password_prompts) + return 0; + + if (authctxt->attempt_passwd != 1) + error("Permission denied, please try again."); + + xasprintf(&prompt, "%s@%s's password: ", authctxt->server_user, host); + password = read_passphrase(prompt, 0); + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0 || + (r = sshpkt_put_cstring(ssh, password)) != 0 || + (r = sshpkt_add_padding(ssh, 64)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + + free(prompt); + if (password != NULL) + freezero(password, strlen(password)); + + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, + &input_userauth_passwd_changereq); + + return 1; +} + +/* + * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST + */ +/* ARGSUSED */ +static int +input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + char *info = NULL, *lang = NULL, *password = NULL, *retype = NULL; + char prompt[256]; + const char *host; + int r; + + debug2("input_userauth_passwd_changereq"); + + if (authctxt == NULL) + fatal("input_userauth_passwd_changereq: " + "no authentication context"); + host = options.host_key_alias ? options.host_key_alias : authctxt->host; + + if ((r = sshpkt_get_cstring(ssh, &info, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0) + goto out; + if (strlen(info) > 0) + logit("%s", info); + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0) /* additional info */ + goto out; + + snprintf(prompt, sizeof(prompt), + "Enter %.30s@%.128s's old password: ", + authctxt->server_user, host); + password = read_passphrase(prompt, 0); + if ((r = sshpkt_put_cstring(ssh, password)) != 0) + goto out; + + freezero(password, strlen(password)); + password = NULL; + while (password == NULL) { + snprintf(prompt, sizeof(prompt), + "Enter %.30s@%.128s's new password: ", + authctxt->server_user, host); + password = read_passphrase(prompt, RP_ALLOW_EOF); + if (password == NULL) { + /* bail out */ + r = 0; + goto out; + } + snprintf(prompt, sizeof(prompt), + "Retype %.30s@%.128s's new password: ", + authctxt->server_user, host); + retype = read_passphrase(prompt, 0); + if (strcmp(password, retype) != 0) { + freezero(password, strlen(password)); + logit("Mismatch; try again, EOF to quit."); + password = NULL; + } + freezero(retype, strlen(retype)); + } + if ((r = sshpkt_put_cstring(ssh, password)) != 0 || + (r = sshpkt_add_padding(ssh, 64)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, + &input_userauth_passwd_changereq); + r = 0; + out: + if (password) + freezero(password, strlen(password)); + free(info); + free(lang); + return r; +} + +/* + * Select an algorithm for publickey signatures. + * Returns algorithm (caller must free) or NULL if no mutual algorithm found. + * + * Call with ssh==NULL to ignore server-sig-algs extension list and + * only attempt with the key's base signature type. + */ +static char * +key_sig_algorithm(struct ssh *ssh, const struct sshkey *key) +{ + char *allowed, *oallowed, *cp, *tmp, *alg = NULL; + const char *server_sig_algs; + + /* + * The signature algorithm will only differ from the key algorithm + * for RSA keys/certs and when the server advertises support for + * newer (SHA2) algorithms. + */ + if (ssh == NULL || ssh->kex->server_sig_algs == NULL || + (key->type != KEY_RSA && key->type != KEY_RSA_CERT) || + (key->type == KEY_RSA_CERT && (ssh->compat & SSH_BUG_SIGTYPE))) { + /* Filter base key signature alg against our configuration */ + return match_list(sshkey_ssh_name(key), + options.pubkey_accepted_algos, NULL); + } + + /* + * Workaround OpenSSH 7.4 bug: this version supports RSA/SHA-2 but + * fails to advertise it via SSH2_MSG_EXT_INFO. + */ + server_sig_algs = ssh->kex->server_sig_algs; + if (key->type == KEY_RSA && (ssh->compat & SSH_BUG_SIGTYPE74)) + server_sig_algs = "rsa-sha2-256,rsa-sha2-512"; + + /* + * For RSA keys/certs, since these might have a different sig type: + * find the first entry in PubkeyAcceptedAlgorithms of the right type + * that also appears in the supported signature algorithms list from + * the server. + */ + oallowed = allowed = xstrdup(options.pubkey_accepted_algos); + while ((cp = strsep(&allowed, ",")) != NULL) { + if (sshkey_type_from_name(cp) != key->type) + continue; + tmp = match_list(sshkey_sigalg_by_name(cp), + server_sig_algs, NULL); + if (tmp != NULL) + alg = xstrdup(cp); + free(tmp); + if (alg != NULL) + break; + } + free(oallowed); + return alg; +} + +static int +identity_sign(struct identity *id, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat, const char *alg) +{ + struct sshkey *sign_key = NULL, *prv = NULL; + int retried = 0, r = SSH_ERR_INTERNAL_ERROR; + struct notifier_ctx *notifier = NULL; + char *fp = NULL, *pin = NULL, *prompt = NULL; + + *sigp = NULL; + *lenp = 0; + + /* The agent supports this key. */ + if (id->key != NULL && id->agent_fd != -1) { + return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, + data, datalen, alg, compat); + } + + /* + * We have already loaded the private key or the private key is + * stored in external hardware. + */ + if (id->key != NULL && + (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) { + sign_key = id->key; + } else { + /* Load the private key from the file. */ + if ((prv = load_identity_file(id)) == NULL) + return SSH_ERR_KEY_NOT_FOUND; + if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { + error_f("private key %s contents do not match public", + id->filename); + r = SSH_ERR_KEY_NOT_FOUND; + goto out; + } + sign_key = prv; + if (sshkey_is_sk(sign_key)) { + if ((sign_key->sk_flags & + SSH_SK_USER_VERIFICATION_REQD)) { + retry_pin: + xasprintf(&prompt, "Enter PIN for %s key %s: ", + sshkey_type(sign_key), id->filename); + pin = read_passphrase(prompt, 0); + } + if ((sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { + /* XXX should batch mode just skip these? */ + if ((fp = sshkey_fingerprint(sign_key, + options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + notifier = notify_start(options.batch_mode, + "Confirm user presence for key %s %s", + sshkey_type(sign_key), fp); + free(fp); + } + } + } + if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, + alg, options.sk_provider, pin, compat)) != 0) { + debug_fr(r, "sshkey_sign"); + if (pin == NULL && !retried && sshkey_is_sk(sign_key) && + r == SSH_ERR_KEY_WRONG_PASSPHRASE) { + notify_complete(notifier, NULL); + notifier = NULL; + retried = 1; + goto retry_pin; + } + goto out; + } + + /* + * PKCS#11 tokens may not support all signature algorithms, + * so check what we get back. + */ + if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) { + debug_fr(r, "sshkey_check_sigtype"); + goto out; + } + /* success */ + r = 0; + out: + free(prompt); + if (pin != NULL) + freezero(pin, strlen(pin)); + notify_complete(notifier, r == 0 ? "User presence confirmed" : NULL); + sshkey_free(prv); + return r; +} + +static int +id_filename_matches(Identity *id, Identity *private_id) +{ + static const char * const suffixes[] = { ".pub", "-cert.pub", NULL }; + size_t len = strlen(id->filename), plen = strlen(private_id->filename); + size_t i, slen; + + if (strcmp(id->filename, private_id->filename) == 0) + return 1; + for (i = 0; suffixes[i]; i++) { + slen = strlen(suffixes[i]); + if (len > slen && plen == len - slen && + strcmp(id->filename + (len - slen), suffixes[i]) == 0 && + memcmp(id->filename, private_id->filename, plen) == 0) + return 1; + } + return 0; +} + +static int +sign_and_send_pubkey(struct ssh *ssh, Identity *id) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + struct sshbuf *b = NULL; + Identity *private_id, *sign_id = NULL; + u_char *signature = NULL; + size_t slen = 0, skip = 0; + int r, fallback_sigtype, sent = 0; + char *alg = NULL, *fp = NULL; + const char *loc = "", *method = "publickey"; + int hostbound = 0; + + /* prefer host-bound pubkey signatures if supported by server */ + if ((ssh->kex->flags & KEX_HAS_PUBKEY_HOSTBOUND) != 0 && + (options.pubkey_authentication & SSH_PUBKEY_AUTH_HBOUND) != 0) { + hostbound = 1; + method = "publickey-hostbound-v00@openssh.com"; + } + + if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + return 0; + + debug3_f("using %s with %s %s", method, sshkey_type(id->key), fp); + + /* + * If the key is an certificate, try to find a matching private key + * and use it to complete the signature. + * If no such private key exists, fall back to trying the certificate + * key itself in case it has a private half already loaded. + * This will try to set sign_id to the private key that will perform + * the signature. + */ + if (sshkey_is_cert(id->key)) { + TAILQ_FOREACH(private_id, &authctxt->keys, next) { + if (sshkey_equal_public(id->key, private_id->key) && + id->key->type != private_id->key->type) { + sign_id = private_id; + break; + } + } + /* + * Exact key matches are preferred, but also allow + * filename matches for non-PKCS#11/agent keys that + * didn't load public keys. This supports the case + * of keeping just a private key file and public + * certificate on disk. + */ + if (sign_id == NULL && + !id->isprivate && id->agent_fd == -1 && + (id->key->flags & SSHKEY_FLAG_EXT) == 0) { + TAILQ_FOREACH(private_id, &authctxt->keys, next) { + if (private_id->key == NULL && + id_filename_matches(id, private_id)) { + sign_id = private_id; + break; + } + } + } + if (sign_id != NULL) { + debug2_f("using private key \"%s\"%s for " + "certificate", sign_id->filename, + sign_id->agent_fd != -1 ? " from agent" : ""); + } else { + debug_f("no separate private key for certificate " + "\"%s\"", id->filename); + } + } + + /* + * If the above didn't select another identity to do the signing + * then default to the one we started with. + */ + if (sign_id == NULL) + sign_id = id; + + /* assemble and sign data */ + for (fallback_sigtype = 0; fallback_sigtype <= 1; fallback_sigtype++) { + free(alg); + slen = 0; + signature = NULL; + if ((alg = key_sig_algorithm(fallback_sigtype ? NULL : ssh, + id->key)) == NULL) { + error_f("no mutual signature supported"); + goto out; + } + debug3_f("signing using %s %s", alg, fp); + + sshbuf_free(b); + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (ssh->compat & SSH_OLD_SESSIONID) { + if ((r = sshbuf_putb(b, ssh->kex->session_id)) != 0) + fatal_fr(r, "sshbuf_putb"); + } else { + if ((r = sshbuf_put_stringb(b, + ssh->kex->session_id)) != 0) + fatal_fr(r, "sshbuf_put_stringb"); + } + skip = sshbuf_len(b); + if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || + (r = sshbuf_put_cstring(b, method)) != 0 || + (r = sshbuf_put_u8(b, 1)) != 0 || + (r = sshbuf_put_cstring(b, alg)) != 0 || + (r = sshkey_puts(id->key, b)) != 0) { + fatal_fr(r, "assemble signed data"); + } + if (hostbound) { + if (ssh->kex->initial_hostkey == NULL) { + fatal_f("internal error: initial hostkey " + "not recorded"); + } + if ((r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0) + fatal_fr(r, "assemble %s hostkey", method); + } + /* generate signature */ + r = identity_sign(sign_id, &signature, &slen, + sshbuf_ptr(b), sshbuf_len(b), ssh->compat, alg); + if (r == 0) + break; + else if (r == SSH_ERR_KEY_NOT_FOUND) + goto out; /* soft failure */ + else if (r == SSH_ERR_SIGN_ALG_UNSUPPORTED && + !fallback_sigtype) { + if (sign_id->agent_fd != -1) + loc = "agent "; + else if ((sign_id->key->flags & SSHKEY_FLAG_EXT) != 0) + loc = "token "; + logit("%skey %s %s returned incorrect signature type", + loc, sshkey_type(id->key), fp); + continue; + } + error_fr(r, "signing failed for %s \"%s\"%s", + sshkey_type(sign_id->key), sign_id->filename, + id->agent_fd != -1 ? " from agent" : ""); + goto out; + } + if (slen == 0 || signature == NULL) /* shouldn't happen */ + fatal_f("no signature"); + + /* append signature */ + if ((r = sshbuf_put_string(b, signature, slen)) != 0) + fatal_fr(r, "append signature"); + +#ifdef DEBUG_PK + sshbuf_dump(b, stderr); +#endif + /* skip session id and packet type */ + if ((r = sshbuf_consume(b, skip + 1)) != 0) + fatal_fr(r, "consume"); + + /* put remaining data from buffer into packet */ + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_putb(ssh, b)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "enqueue request"); + + /* success */ + sent = 1; + + out: + free(fp); + free(alg); + sshbuf_free(b); + freezero(signature, slen); + return sent; +} + +static int +send_pubkey_test(struct ssh *ssh, Identity *id) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + u_char *blob = NULL; + char *alg = NULL; + size_t bloblen; + u_int have_sig = 0; + int sent = 0, r; + + if ((alg = key_sig_algorithm(ssh, id->key)) == NULL) { + debug_f("no mutual signature algorithm"); + goto out; + } + + if ((r = sshkey_to_blob(id->key, &blob, &bloblen)) != 0) { + /* we cannot handle this key */ + debug3_f("cannot handle key"); + goto out; + } + /* register callback for USERAUTH_PK_OK message */ + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); + + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_u8(ssh, have_sig)) != 0 || + (r = sshpkt_put_cstring(ssh, alg)) != 0 || + (r = sshpkt_put_string(ssh, blob, bloblen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + sent = 1; + + out: + free(alg); + free(blob); + return sent; +} + +static struct sshkey * +load_identity_file(Identity *id) +{ + struct sshkey *private = NULL; + char prompt[300], *passphrase, *comment; + int r, quit = 0, i; + struct stat st; + + if (stat(id->filename, &st) == -1) { + do_log2(id->userprovided ? + SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_DEBUG3, + "no such identity: %s: %s", id->filename, strerror(errno)); + return NULL; + } + snprintf(prompt, sizeof prompt, + "Enter passphrase for key '%.100s': ", id->filename); + for (i = 0; i <= options.number_of_password_prompts; i++) { + if (i == 0) + passphrase = ""; + else { + passphrase = read_passphrase(prompt, 0); + if (*passphrase == '\0') { + debug2("no passphrase given, try next key"); + free(passphrase); + break; + } + } + switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename, + passphrase, &private, &comment))) { + case 0: + break; + case SSH_ERR_KEY_WRONG_PASSPHRASE: + if (options.batch_mode) { + quit = 1; + break; + } + if (i != 0) + debug2("bad passphrase given, try again..."); + break; + case SSH_ERR_SYSTEM_ERROR: + if (errno == ENOENT) { + debug2_r(r, "Load key \"%s\"", id->filename); + quit = 1; + break; + } + /* FALLTHROUGH */ + default: + error_r(r, "Load key \"%s\"", id->filename); + quit = 1; + break; + } + if (private != NULL && sshkey_is_sk(private) && + options.sk_provider == NULL) { + debug("key \"%s\" is an authenticator-hosted key, " + "but no provider specified", id->filename); + sshkey_free(private); + private = NULL; + quit = 1; + } + if (!quit && private != NULL && id->agent_fd == -1 && + !(id->key && id->isprivate)) + maybe_add_key_to_agent(id->filename, private, comment, + passphrase); + if (i > 0) + freezero(passphrase, strlen(passphrase)); + free(comment); + if (private != NULL || quit) + break; + } + return private; +} + +static int +key_type_allowed_by_config(struct sshkey *key) +{ + if (match_pattern_list(sshkey_ssh_name(key), + options.pubkey_accepted_algos, 0) == 1) + return 1; + + /* RSA keys/certs might be allowed by alternate signature types */ + switch (key->type) { + case KEY_RSA: + if (match_pattern_list("rsa-sha2-512", + options.pubkey_accepted_algos, 0) == 1) + return 1; + if (match_pattern_list("rsa-sha2-256", + options.pubkey_accepted_algos, 0) == 1) + return 1; + break; + case KEY_RSA_CERT: + if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", + options.pubkey_accepted_algos, 0) == 1) + return 1; + if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", + options.pubkey_accepted_algos, 0) == 1) + return 1; + break; + } + return 0; +} + +/* obtain a list of keys from the agent */ +static int +get_agent_identities(struct ssh *ssh, int *agent_fdp, + struct ssh_identitylist **idlistp) +{ + int r, agent_fd; + struct ssh_identitylist *idlist; + + if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { + if (r != SSH_ERR_AGENT_NOT_PRESENT) + debug_fr(r, "ssh_get_authentication_socket"); + return r; + } + if ((r = ssh_agent_bind_hostkey(agent_fd, ssh->kex->initial_hostkey, + ssh->kex->session_id, ssh->kex->initial_sig, 0)) == 0) + debug_f("bound agent to hostkey"); + else + debug2_fr(r, "ssh_agent_bind_hostkey"); + + if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { + debug_fr(r, "ssh_fetch_identitylist"); + close(agent_fd); + return r; + } + /* success */ + *agent_fdp = agent_fd; + *idlistp = idlist; + debug_f("agent returned %zu keys", idlist->nkeys); + return 0; +} + +/* + * try keys in the following order: + * 1. certificates listed in the config file + * 2. other input certificates + * 3. agent keys that are found in the config file + * 4. other agent keys + * 5. keys that are only listed in the config file + */ +static void +pubkey_prepare(struct ssh *ssh, Authctxt *authctxt) +{ + struct identity *id, *id2, *tmp; + struct idlist agent, files, *preferred; + struct sshkey *key; + int agent_fd = -1, i, r, found; + size_t j; + struct ssh_identitylist *idlist; + char *ident; + + TAILQ_INIT(&agent); /* keys from the agent */ + TAILQ_INIT(&files); /* keys from the config file */ + preferred = &authctxt->keys; + TAILQ_INIT(preferred); /* preferred order of keys */ + + /* list of keys stored in the filesystem and PKCS#11 */ + for (i = 0; i < options.num_identity_files; i++) { + key = options.identity_keys[i]; + if (key && key->cert && + key->cert->type != SSH2_CERT_TYPE_USER) { + debug_f("ignoring certificate %s: not a user " + "certificate", options.identity_files[i]); + continue; + } + if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { + debug_f("ignoring authenticator-hosted key %s as no " + "SecurityKeyProvider has been specified", + options.identity_files[i]); + continue; + } + options.identity_keys[i] = NULL; + id = xcalloc(1, sizeof(*id)); + id->agent_fd = -1; + id->key = key; + id->filename = xstrdup(options.identity_files[i]); + id->userprovided = options.identity_file_userprovided[i]; + TAILQ_INSERT_TAIL(&files, id, next); + } + /* list of certificates specified by user */ + for (i = 0; i < options.num_certificate_files; i++) { + key = options.certificates[i]; + if (!sshkey_is_cert(key) || key->cert == NULL || + key->cert->type != SSH2_CERT_TYPE_USER) { + debug_f("ignoring certificate %s: not a user " + "certificate", options.identity_files[i]); + continue; + } + if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { + debug_f("ignoring authenticator-hosted key " + "certificate %s as no " + "SecurityKeyProvider has been specified", + options.identity_files[i]); + continue; + } + id = xcalloc(1, sizeof(*id)); + id->agent_fd = -1; + id->key = key; + id->filename = xstrdup(options.certificate_files[i]); + id->userprovided = options.certificate_file_userprovided[i]; + TAILQ_INSERT_TAIL(preferred, id, next); + } + /* list of keys supported by the agent */ + if ((r = get_agent_identities(ssh, &agent_fd, &idlist)) == 0) { + for (j = 0; j < idlist->nkeys; j++) { + found = 0; + TAILQ_FOREACH(id, &files, next) { + /* + * agent keys from the config file are + * preferred + */ + if (sshkey_equal(idlist->keys[j], id->key)) { + TAILQ_REMOVE(&files, id, next); + TAILQ_INSERT_TAIL(preferred, id, next); + id->agent_fd = agent_fd; + found = 1; + break; + } + } + if (!found && !options.identities_only) { + id = xcalloc(1, sizeof(*id)); + /* XXX "steals" key/comment from idlist */ + id->key = idlist->keys[j]; + id->filename = idlist->comments[j]; + idlist->keys[j] = NULL; + idlist->comments[j] = NULL; + id->agent_fd = agent_fd; + TAILQ_INSERT_TAIL(&agent, id, next); + } + } + ssh_free_identitylist(idlist); + /* append remaining agent keys */ + TAILQ_CONCAT(preferred, &agent, next); + authctxt->agent_fd = agent_fd; + } + /* Prefer PKCS11 keys that are explicitly listed */ + TAILQ_FOREACH_SAFE(id, &files, next, tmp) { + if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0) + continue; + found = 0; + TAILQ_FOREACH(id2, &files, next) { + if (id2->key == NULL || + (id2->key->flags & SSHKEY_FLAG_EXT) != 0) + continue; + if (sshkey_equal(id->key, id2->key)) { + TAILQ_REMOVE(&files, id, next); + TAILQ_INSERT_TAIL(preferred, id, next); + found = 1; + break; + } + } + /* If IdentitiesOnly set and key not found then don't use it */ + if (!found && options.identities_only) { + TAILQ_REMOVE(&files, id, next); + freezero(id, sizeof(*id)); + } + } + /* append remaining keys from the config file */ + TAILQ_CONCAT(preferred, &files, next); + /* finally, filter by PubkeyAcceptedAlgorithms */ + TAILQ_FOREACH_SAFE(id, preferred, next, id2) { + if (id->key != NULL && !key_type_allowed_by_config(id->key)) { + debug("Skipping %s key %s - " + "corresponding algo not in PubkeyAcceptedAlgorithms", + sshkey_ssh_name(id->key), id->filename); + TAILQ_REMOVE(preferred, id, next); + sshkey_free(id->key); + free(id->filename); + memset(id, 0, sizeof(*id)); + continue; + } + } + /* List the keys we plan on using */ + TAILQ_FOREACH_SAFE(id, preferred, next, id2) { + ident = format_identity(id); + debug("Will attempt key: %s", ident); + free(ident); + } + debug2_f("done"); +} + +static void +pubkey_cleanup(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + Identity *id; + + if (authctxt->agent_fd != -1) { + ssh_close_authentication_socket(authctxt->agent_fd); + authctxt->agent_fd = -1; + } + for (id = TAILQ_FIRST(&authctxt->keys); id; + id = TAILQ_FIRST(&authctxt->keys)) { + TAILQ_REMOVE(&authctxt->keys, id, next); + sshkey_free(id->key); + free(id->filename); + free(id); + } +} + +static void +pubkey_reset(Authctxt *authctxt) +{ + Identity *id; + + TAILQ_FOREACH(id, &authctxt->keys, next) + id->tried = 0; +} + +static int +try_identity(struct ssh *ssh, Identity *id) +{ + if (!id->key) + return (0); + if (sshkey_type_plain(id->key->type) == KEY_RSA && + (ssh->compat & SSH_BUG_RSASIGMD5) != 0) { + debug("Skipped %s key %s for RSA/MD5 server", + sshkey_type(id->key), id->filename); + return (0); + } + return 1; +} + +static int +userauth_pubkey(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + Identity *id; + int sent = 0; + char *ident; + + while ((id = TAILQ_FIRST(&authctxt->keys))) { + if (id->tried++) + return (0); + /* move key to the end of the queue */ + TAILQ_REMOVE(&authctxt->keys, id, next); + TAILQ_INSERT_TAIL(&authctxt->keys, id, next); + /* + * send a test message if we have the public key. for + * encrypted keys we cannot do this and have to load the + * private key instead + */ + if (id->key != NULL) { + if (try_identity(ssh, id)) { + ident = format_identity(id); + debug("Offering public key: %s", ident); + free(ident); + sent = send_pubkey_test(ssh, id); + } + } else { + debug("Trying private key: %s", id->filename); + id->key = load_identity_file(id); + if (id->key != NULL) { + if (try_identity(ssh, id)) { + id->isprivate = 1; + sent = sign_and_send_pubkey(ssh, id); + } + sshkey_free(id->key); + id->key = NULL; + id->isprivate = 0; + } + } + if (sent) + return (sent); + } + return (0); +} + +/* + * Send userauth request message specifying keyboard-interactive method. + */ +static int +userauth_kbdint(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + int r; + + if (authctxt->attempt_kbdint++ >= options.number_of_password_prompts) + return 0; + /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */ + if (authctxt->attempt_kbdint > 1 && !authctxt->info_req_seen) { + debug3("userauth_kbdint: disable: no info_req_seen"); + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); + return 0; + } + + debug2("userauth_kbdint"); + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || /* lang */ + (r = sshpkt_put_cstring(ssh, options.kbd_interactive_devices ? + options.kbd_interactive_devices : "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); + return 1; +} + +/* + * parse INFO_REQUEST, prompt user and send INFO_RESPONSE + */ +static int +input_userauth_info_req(int type, u_int32_t seq, struct ssh *ssh) +{ + Authctxt *authctxt = ssh->authctxt; + char *name = NULL, *inst = NULL, *lang = NULL, *prompt = NULL; + char *display_prompt = NULL, *response = NULL; + u_char echo = 0; + u_int num_prompts, i; + int r; + + debug2_f("entering"); + + if (authctxt == NULL) + fatal_f("no authentication context"); + + authctxt->info_req_seen = 1; + + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &inst, NULL)) != 0 || + (r = sshpkt_get_cstring(ssh, &lang, NULL)) != 0) + goto out; + if (strlen(name) > 0) + logit("%s", name); + if (strlen(inst) > 0) + logit("%s", inst); + + if ((r = sshpkt_get_u32(ssh, &num_prompts)) != 0) + goto out; + /* + * Begin to build info response packet based on prompts requested. + * We commit to providing the correct number of responses, so if + * further on we run into a problem that prevents this, we have to + * be sure and clean this up and send a correct error response. + */ + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE)) != 0 || + (r = sshpkt_put_u32(ssh, num_prompts)) != 0) + goto out; + + debug2_f("num_prompts %d", num_prompts); + for (i = 0; i < num_prompts; i++) { + if ((r = sshpkt_get_cstring(ssh, &prompt, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &echo)) != 0) + goto out; + if (asmprintf(&display_prompt, INT_MAX, NULL, "(%s@%s) %s", + authctxt->server_user, options.host_key_alias ? + options.host_key_alias : authctxt->host, prompt) == -1) + fatal_f("asmprintf failed"); + response = read_passphrase(display_prompt, echo ? RP_ECHO : 0); + if ((r = sshpkt_put_cstring(ssh, response)) != 0) + goto out; + freezero(response, strlen(response)); + free(prompt); + free(display_prompt); + display_prompt = response = prompt = NULL; + } + /* done with parsing incoming message. */ + if ((r = sshpkt_get_end(ssh)) != 0 || + (r = sshpkt_add_padding(ssh, 64)) != 0) + goto out; + r = sshpkt_send(ssh); + out: + if (response) + freezero(response, strlen(response)); + free(prompt); + free(display_prompt); + free(name); + free(inst); + free(lang); + return r; +} + +static int +ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen) +{ + struct sshbuf *b; + struct stat st; + pid_t pid; + int r, to[2], from[2], status; + int sock = ssh_packet_get_connection_in(ssh); + u_char rversion = 0, version = 2; + void (*osigchld)(int); + + *sigp = NULL; + *lenp = 0; + + if (stat(_PATH_SSH_KEY_SIGN, &st) == -1) { + error_f("not installed: %s", strerror(errno)); + return -1; + } + if (fflush(stdout) != 0) { + error_f("fflush: %s", strerror(errno)); + return -1; + } + if (pipe(to) == -1) { + error_f("pipe: %s", strerror(errno)); + return -1; + } + if (pipe(from) == -1) { + error_f("pipe: %s", strerror(errno)); + return -1; + } + if ((pid = fork()) == -1) { + error_f("fork: %s", strerror(errno)); + return -1; + } + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + if (pid == 0) { + close(from[0]); + if (dup2(from[1], STDOUT_FILENO) == -1) + fatal_f("dup2: %s", strerror(errno)); + close(to[1]); + if (dup2(to[0], STDIN_FILENO) == -1) + fatal_f("dup2: %s", strerror(errno)); + close(from[1]); + close(to[0]); + + if (dup2(sock, STDERR_FILENO + 1) == -1) + fatal_f("dup2: %s", strerror(errno)); + sock = STDERR_FILENO + 1; + fcntl(sock, F_SETFD, 0); /* keep the socket on exec */ + closefrom(sock + 1); + + debug3_f("[child] pid=%ld, exec %s", + (long)getpid(), _PATH_SSH_KEY_SIGN); + execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL); + fatal_f("exec(%s): %s", _PATH_SSH_KEY_SIGN, + strerror(errno)); + } + close(from[1]); + close(to[0]); + sock = STDERR_FILENO + 1; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + /* send # of sock, data to be signed */ + if ((r = sshbuf_put_u32(b, sock)) != 0 || + (r = sshbuf_put_string(b, data, datalen)) != 0) + fatal_fr(r, "buffer error"); + if (ssh_msg_send(to[1], version, b) == -1) + fatal_f("couldn't send request"); + sshbuf_reset(b); + r = ssh_msg_recv(from[0], b); + close(from[0]); + close(to[1]); + if (r < 0) { + error_f("no reply"); + goto fail; + } + + errno = 0; + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + error_f("waitpid %ld: %s", (long)pid, strerror(errno)); + goto fail; + } + } + if (!WIFEXITED(status)) { + error_f("exited abnormally"); + goto fail; + } + if (WEXITSTATUS(status) != 0) { + error_f("exited with status %d", WEXITSTATUS(status)); + goto fail; + } + if ((r = sshbuf_get_u8(b, &rversion)) != 0) { + error_fr(r, "buffer error"); + goto fail; + } + if (rversion != version) { + error_f("bad version"); + goto fail; + } + if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) { + error_fr(r, "buffer error"); + fail: + ssh_signal(SIGCHLD, osigchld); + sshbuf_free(b); + return -1; + } + ssh_signal(SIGCHLD, osigchld); + sshbuf_free(b); + + return 0; +} + +static int +userauth_hostbased(struct ssh *ssh) +{ + Authctxt *authctxt = (Authctxt *)ssh->authctxt; + struct sshkey *private = NULL; + struct sshbuf *b = NULL; + u_char *sig = NULL, *keyblob = NULL; + char *fp = NULL, *chost = NULL, *lname = NULL; + size_t siglen = 0, keylen = 0; + int i, r, success = 0; + + if (authctxt->ktypes == NULL) { + authctxt->oktypes = xstrdup(options.hostbased_accepted_algos); + authctxt->ktypes = authctxt->oktypes; + } + + /* + * Work through each listed type pattern in HostbasedAcceptedAlgorithms, + * trying each hostkey that matches the type in turn. + */ + for (;;) { + if (authctxt->active_ktype == NULL) + authctxt->active_ktype = strsep(&authctxt->ktypes, ","); + if (authctxt->active_ktype == NULL || + *authctxt->active_ktype == '\0') + break; + debug3_f("trying key type %s", authctxt->active_ktype); + + /* check for a useful key */ + private = NULL; + for (i = 0; i < authctxt->sensitive->nkeys; i++) { + if (authctxt->sensitive->keys[i] == NULL || + authctxt->sensitive->keys[i]->type == KEY_UNSPEC) + continue; + if (!sshkey_match_keyname_to_sigalgs( + sshkey_ssh_name(authctxt->sensitive->keys[i]), + authctxt->active_ktype)) + continue; + /* we take and free the key */ + private = authctxt->sensitive->keys[i]; + authctxt->sensitive->keys[i] = NULL; + break; + } + /* Found one */ + if (private != NULL) + break; + /* No more keys of this type; advance */ + authctxt->active_ktype = NULL; + } + if (private == NULL) { + free(authctxt->oktypes); + authctxt->oktypes = authctxt->ktypes = NULL; + authctxt->active_ktype = NULL; + debug("No more client hostkeys for hostbased authentication."); + goto out; + } + + if ((fp = sshkey_fingerprint(private, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) { + error_f("sshkey_fingerprint failed"); + goto out; + } + debug_f("trying hostkey %s %s using sigalg %s", + sshkey_ssh_name(private), fp, authctxt->active_ktype); + + /* figure out a name for the client host */ + lname = get_local_name(ssh_packet_get_connection_in(ssh)); + if (lname == NULL) { + error_f("cannot get local ipaddr/name"); + goto out; + } + + /* XXX sshbuf_put_stringf? */ + xasprintf(&chost, "%s.", lname); + debug2_f("chost %s", chost); + + /* construct data */ + if ((b = sshbuf_new()) == NULL) { + error_f("sshbuf_new failed"); + goto out; + } + if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) { + error_fr(r, "sshkey_to_blob"); + goto out; + } + if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->active_ktype)) != 0 || + (r = sshbuf_put_string(b, keyblob, keylen)) != 0 || + (r = sshbuf_put_cstring(b, chost)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) { + error_fr(r, "buffer error"); + goto out; + } + +#ifdef DEBUG_PK + sshbuf_dump(b, stderr); +#endif + if ((r = ssh_keysign(ssh, private, &sig, &siglen, + sshbuf_ptr(b), sshbuf_len(b))) != 0) { + error("sign using hostkey %s %s failed", + sshkey_ssh_name(private), fp); + goto out; + } + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->active_ktype)) != 0 || + (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 || + (r = sshpkt_put_cstring(ssh, chost)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 || + (r = sshpkt_put_string(ssh, sig, siglen)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_fr(r, "packet error"); + goto out; + } + success = 1; + + out: + if (sig != NULL) + freezero(sig, siglen); + free(keyblob); + free(lname); + free(fp); + free(chost); + sshkey_free(private); + sshbuf_free(b); + + return success; +} + +/* find auth method */ + +/* + * given auth method name, if configurable options permit this method fill + * in auth_ident field and return true, otherwise return false. + */ +static int +authmethod_is_enabled(Authmethod *method) +{ + if (method == NULL) + return 0; + /* return false if options indicate this method is disabled */ + if (method->enabled == NULL || *method->enabled == 0) + return 0; + /* return false if batch mode is enabled but method needs interactive mode */ + if (method->batch_flag != NULL && *method->batch_flag != 0) + return 0; + return 1; +} + +static Authmethod * +authmethod_lookup(const char *name) +{ + Authmethod *method = NULL; + if (name != NULL) + for (method = authmethods; method->name != NULL; method++) + if (strcmp(name, method->name) == 0) + return method; + debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); + return NULL; +} + +/* XXX internal state */ +static Authmethod *current = NULL; +static char *supported = NULL; +static char *preferred = NULL; + +/* + * Given the authentication method list sent by the server, return the + * next method we should try. If the server initially sends a nil list, + * use a built-in default list. + */ +static Authmethod * +authmethod_get(char *authlist) +{ + char *name = NULL; + u_int next; + + /* Use a suitable default if we're passed a nil list. */ + if (authlist == NULL || strlen(authlist) == 0) + authlist = options.preferred_authentications; + + if (supported == NULL || strcmp(authlist, supported) != 0) { + debug3("start over, passed a different list %s", authlist); + free(supported); + supported = xstrdup(authlist); + preferred = options.preferred_authentications; + debug3("preferred %s", preferred); + current = NULL; + } else if (current != NULL && authmethod_is_enabled(current)) + return current; + + for (;;) { + if ((name = match_list(preferred, supported, &next)) == NULL) { + debug("No more authentication methods to try."); + current = NULL; + return NULL; + } + preferred += next; + debug3("authmethod_lookup %s", name); + debug3("remaining preferred: %s", preferred); + if ((current = authmethod_lookup(name)) != NULL && + authmethod_is_enabled(current)) { + debug3("authmethod_is_enabled %s", name); + debug("Next authentication method: %s", name); + free(name); + return current; + } + free(name); + } +} + +static char * +authmethods_get(void) +{ + Authmethod *method = NULL; + struct sshbuf *b; + char *list; + int r; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + for (method = authmethods; method->name != NULL; method++) { + if (authmethod_is_enabled(method)) { + if ((r = sshbuf_putf(b, "%s%s", + sshbuf_len(b) ? "," : "", method->name)) != 0) + fatal_fr(r, "buffer error"); + } + } + if ((list = sshbuf_dup_string(b)) == NULL) + fatal_f("sshbuf_dup_string failed"); + sshbuf_free(b); + return list; +} diff --git a/aosp/external/openssh/sshd.8 b/aosp/external/openssh/sshd.8 new file mode 100644 index 0000000000000000000000000000000000000000..350b08ad2380ec8af98cf3df78b2d1a4453a10d1 --- /dev/null +++ b/aosp/external/openssh/sshd.8 @@ -0,0 +1,1028 @@ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: sshd.8,v 1.318 2022/03/31 17:27:27 naddy Exp $ +.Dd $Mdocdate: March 31 2022 $ +.Dt SSHD 8 +.Os +.Sh NAME +.Nm sshd +.Nd OpenSSH daemon +.Sh SYNOPSIS +.Nm sshd +.Bk -words +.Op Fl 46DdeiqTt +.Op Fl C Ar connection_spec +.Op Fl c Ar host_certificate_file +.Op Fl E Ar log_file +.Op Fl f Ar config_file +.Op Fl g Ar login_grace_time +.Op Fl h Ar host_key_file +.Op Fl o Ar option +.Op Fl p Ar port +.Op Fl u Ar len +.Ek +.Sh DESCRIPTION +.Nm +(OpenSSH Daemon) is the daemon program for +.Xr ssh 1 . +It provides secure encrypted communications between two untrusted hosts +over an insecure network. +.Pp +.Nm +listens for connections from clients. +It is normally started at boot from +.Pa /etc/rc . +It forks a new +daemon for each incoming connection. +The forked daemons handle +key exchange, encryption, authentication, command execution, +and data exchange. +.Pp +.Nm +can be configured using command-line options or a configuration file +(by default +.Xr sshd_config 5 ) ; +command-line options override values specified in the +configuration file. +.Nm +rereads its configuration file when it receives a hangup signal, +.Dv SIGHUP , +by executing itself with the name and options it was started with, e.g.\& +.Pa /usr/sbin/sshd . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl C Ar connection_spec +Specify the connection parameters to use for the +.Fl T +extended test mode. +If provided, any +.Cm Match +directives in the configuration file that would apply are applied before the +configuration is written to standard output. +The connection parameters are supplied as keyword=value pairs and may be +supplied in any order, either with multiple +.Fl C +options or as a comma-separated list. +The keywords are +.Dq addr , +.Dq user , +.Dq host , +.Dq laddr , +.Dq lport , +and +.Dq rdomain +and correspond to source address, user, resolved source host name, +local address, local port number and routing domain respectively. +.It Fl c Ar host_certificate_file +Specifies a path to a certificate file to identify +.Nm +during key exchange. +The certificate file must match a host key file specified using the +.Fl h +option or the +.Cm HostKey +configuration directive. +.It Fl D +When this option is specified, +.Nm +will not detach and does not become a daemon. +This allows easy monitoring of +.Nm sshd . +.It Fl d +Debug mode. +The server sends verbose debug output to standard error, +and does not put itself in the background. +The server also will not +.Xr fork 2 +and will only process one connection. +This option is only intended for debugging for the server. +Multiple +.Fl d +options increase the debugging level. +Maximum is 3. +.It Fl E Ar log_file +Append debug logs to +.Ar log_file +instead of the system log. +.It Fl e +Write debug logs to standard error instead of the system log. +.It Fl f Ar config_file +Specifies the name of the configuration file. +The default is +.Pa /etc/ssh/sshd_config . +.Nm +refuses to start if there is no configuration file. +.It Fl g Ar login_grace_time +Gives the grace time for clients to authenticate themselves (default +120 seconds). +If the client fails to authenticate the user within +this many seconds, the server disconnects and exits. +A value of zero indicates no limit. +.It Fl h Ar host_key_file +Specifies a file from which a host key is read. +This option must be given if +.Nm +is not run as root (as the normal +host key files are normally not readable by anyone but root). +The default is +.Pa /etc/ssh/ssh_host_ecdsa_key , +.Pa /etc/ssh/ssh_host_ed25519_key +and +.Pa /etc/ssh/ssh_host_rsa_key . +It is possible to have multiple host key files for +the different host key algorithms. +.It Fl i +Specifies that +.Nm +is being run from +.Xr inetd 8 . +.It Fl o Ar option +Can be used to give options in the format used in the configuration file. +This is useful for specifying options for which there is no separate +command-line flag. +For full details of the options, and their values, see +.Xr sshd_config 5 . +.It Fl p Ar port +Specifies the port on which the server listens for connections +(default 22). +Multiple port options are permitted. +Ports specified in the configuration file with the +.Cm Port +option are ignored when a command-line port is specified. +Ports specified using the +.Cm ListenAddress +option override command-line ports. +.It Fl q +Quiet mode. +Nothing is sent to the system log. +Normally the beginning, +authentication, and termination of each connection is logged. +.It Fl T +Extended test mode. +Check the validity of the configuration file, output the effective configuration +to stdout and then exit. +Optionally, +.Cm Match +rules may be applied by specifying the connection parameters using one or more +.Fl C +options. +.It Fl t +Test mode. +Only check the validity of the configuration file and sanity of the keys. +This is useful for updating +.Nm +reliably as configuration options may change. +.It Fl u Ar len +This option is used to specify the size of the field +in the +.Li utmp +structure that holds the remote host name. +If the resolved host name is longer than +.Ar len , +the dotted decimal value will be used instead. +This allows hosts with very long host names that +overflow this field to still be uniquely identified. +Specifying +.Fl u0 +indicates that only dotted decimal addresses +should be put into the +.Pa utmp +file. +.Fl u0 +may also be used to prevent +.Nm +from making DNS requests unless the authentication +mechanism or configuration requires it. +Authentication mechanisms that may require DNS include +.Cm HostbasedAuthentication +and using a +.Cm from="pattern-list" +option in a key file. +Configuration options that require DNS include using a +USER@HOST pattern in +.Cm AllowUsers +or +.Cm DenyUsers . +.El +.Sh AUTHENTICATION +The OpenSSH SSH daemon supports SSH protocol 2 only. +Each host has a host-specific key, +used to identify the host. +Whenever a client connects, the daemon responds with its public +host key. +The client compares the +host key against its own database to verify that it has not changed. +Forward secrecy is provided through a Diffie-Hellman key agreement. +This key agreement results in a shared session key. +The rest of the session is encrypted using a symmetric cipher. +The client selects the encryption algorithm +to use from those offered by the server. +Additionally, session integrity is provided +through a cryptographic message authentication code (MAC). +.Pp +Finally, the server and the client enter an authentication dialog. +The client tries to authenticate itself using +host-based authentication, +public key authentication, +challenge-response authentication, +or password authentication. +.Pp +Regardless of the authentication type, the account is checked to +ensure that it is accessible. An account is not accessible if it is +locked, listed in +.Cm DenyUsers +or its group is listed in +.Cm DenyGroups +\&. The definition of a locked account is system dependent. Some platforms +have their own account database (eg AIX) and some modify the passwd field ( +.Ql \&*LK\&* +on Solaris and UnixWare, +.Ql \&* +on HP-UX, containing +.Ql Nologin +on Tru64, +a leading +.Ql \&*LOCKED\&* +on FreeBSD and a leading +.Ql \&! +on most Linuxes). +If there is a requirement to disable password authentication +for the account while allowing still public-key, then the passwd field +should be set to something other than these values (eg +.Ql NP +or +.Ql \&*NP\&* +). +.Pp +If the client successfully authenticates itself, a dialog for +preparing the session is entered. +At this time the client may request +things like allocating a pseudo-tty, forwarding X11 connections, +forwarding TCP connections, or forwarding the authentication agent +connection over the secure channel. +.Pp +After this, the client either requests an interactive shell or execution +or a non-interactive command, which +.Nm +will execute via the user's shell using its +.Fl c +option. +The sides then enter session mode. +In this mode, either side may send +data at any time, and such data is forwarded to/from the shell or +command on the server side, and the user terminal in the client side. +.Pp +When the user program terminates and all forwarded X11 and other +connections have been closed, the server sends command exit status to +the client, and both sides exit. +.Sh LOGIN PROCESS +When a user successfully logs in, +.Nm +does the following: +.Bl -enum -offset indent +.It +If the login is on a tty, and no command has been specified, +prints last login time and +.Pa /etc/motd +(unless prevented in the configuration file or by +.Pa ~/.hushlogin ; +see the +.Sx FILES +section). +.It +If the login is on a tty, records login time. +.It +Checks +.Pa /etc/nologin ; +if it exists, prints contents and quits +(unless root). +.It +Changes to run with normal user privileges. +.It +Sets up basic environment. +.It +Reads the file +.Pa ~/.ssh/environment , +if it exists, and users are allowed to change their environment. +See the +.Cm PermitUserEnvironment +option in +.Xr sshd_config 5 . +.It +Changes to user's home directory. +.It +If +.Pa ~/.ssh/rc +exists and the +.Xr sshd_config 5 +.Cm PermitUserRC +option is set, runs it; else if +.Pa /etc/ssh/sshrc +exists, runs +it; otherwise runs +.Xr xauth 1 . +The +.Dq rc +files are given the X11 +authentication protocol and cookie in standard input. +See +.Sx SSHRC , +below. +.It +Runs user's shell or command. +All commands are run under the user's login shell as specified in the +system password database. +.El +.Sh SSHRC +If the file +.Pa ~/.ssh/rc +exists, +.Xr sh 1 +runs it after reading the +environment files but before starting the user's shell or command. +It must not produce any output on stdout; stderr must be used +instead. +If X11 forwarding is in use, it will receive the "proto cookie" pair in +its standard input (and +.Ev DISPLAY +in its environment). +The script must call +.Xr xauth 1 +because +.Nm +will not run xauth automatically to add X11 cookies. +.Pp +The primary purpose of this file is to run any initialization routines +which may be needed before the user's home directory becomes +accessible; AFS is a particular example of such an environment. +.Pp +This file will probably contain some initialization code followed by +something similar to: +.Bd -literal -offset 3n +if read proto cookie && [ -n "$DISPLAY" ]; then + if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then + # X11UseLocalhost=yes + echo add unix:`echo $DISPLAY | + cut -c11-` $proto $cookie + else + # X11UseLocalhost=no + echo add $DISPLAY $proto $cookie + fi | xauth -q - +fi +.Ed +.Pp +If this file does not exist, +.Pa /etc/ssh/sshrc +is run, and if that +does not exist either, xauth is used to add the cookie. +.Sh AUTHORIZED_KEYS FILE FORMAT +.Cm AuthorizedKeysFile +specifies the files containing public keys for +public key authentication; +if this option is not specified, the default is +.Pa ~/.ssh/authorized_keys +and +.Pa ~/.ssh/authorized_keys2 . +Each line of the file contains one +key (empty lines and lines starting with a +.Ql # +are ignored as +comments). +Public keys consist of the following space-separated fields: +options, keytype, base64-encoded key, comment. +The options field is optional. +The supported key types are: +.Pp +.Bl -item -compact -offset indent +.It +sk-ecdsa-sha2-nistp256@openssh.com +.It +ecdsa-sha2-nistp256 +.It +ecdsa-sha2-nistp384 +.It +ecdsa-sha2-nistp521 +.It +sk-ssh-ed25519@openssh.com +.It +ssh-ed25519 +.It +ssh-dss +.It +ssh-rsa +.El +.Pp +The comment field is not used for anything (but may be convenient for the +user to identify the key). +.Pp +Note that lines in this file can be several hundred bytes long +(because of the size of the public key encoding) up to a limit of +8 kilobytes, which permits RSA keys up to 16 kilobits. +You don't want to type them in; instead, copy the +.Pa id_dsa.pub , +.Pa id_ecdsa.pub , +.Pa id_ecdsa_sk.pub , +.Pa id_ed25519.pub , +.Pa id_ed25519_sk.pub , +or the +.Pa id_rsa.pub +file and edit it. +.Pp +.Nm +enforces a minimum RSA key modulus size of 1024 bits. +.Pp +The options (if present) consist of comma-separated option +specifications. +No spaces are permitted, except within double quotes. +The following option specifications are supported (note +that option keywords are case-insensitive): +.Bl -tag -width Ds +.It Cm agent-forwarding +Enable authentication agent forwarding previously disabled by the +.Cm restrict +option. +.It Cm cert-authority +Specifies that the listed key is a certification authority (CA) that is +trusted to validate signed certificates for user authentication. +.Pp +Certificates may encode access restrictions similar to these key options. +If both certificate restrictions and key options are present, the most +restrictive union of the two is applied. +.It Cm command="command" +Specifies that the command is executed whenever this key is used for +authentication. +The command supplied by the user (if any) is ignored. +The command is run on a pty if the client requests a pty; +otherwise it is run without a tty. +If an 8-bit clean channel is required, +one must not request a pty or should specify +.Cm no-pty . +A quote may be included in the command by quoting it with a backslash. +.Pp +This option might be useful +to restrict certain public keys to perform just a specific operation. +An example might be a key that permits remote backups but nothing else. +Note that the client may specify TCP and/or X11 +forwarding unless they are explicitly prohibited, e.g. using the +.Cm restrict +key option. +.Pp +The command originally supplied by the client is available in the +.Ev SSH_ORIGINAL_COMMAND +environment variable. +Note that this option applies to shell, command or subsystem execution. +Also note that this command may be superseded by a +.Xr sshd_config 5 +.Cm ForceCommand +directive. +.Pp +If a command is specified and a forced-command is embedded in a certificate +used for authentication, then the certificate will be accepted only if the +two commands are identical. +.It Cm environment="NAME=value" +Specifies that the string is to be added to the environment when +logging in using this key. +Environment variables set this way +override other default environment values. +Multiple options of this type are permitted. +Environment processing is disabled by default and is +controlled via the +.Cm PermitUserEnvironment +option. +.It Cm expiry-time="timespec" +Specifies a time after which the key will not be accepted. +The time may be specified as a YYYYMMDD date or a YYYYMMDDHHMM[SS] time +in the system time-zone. +.It Cm from="pattern-list" +Specifies that in addition to public key authentication, either the canonical +name of the remote host or its IP address must be present in the +comma-separated list of patterns. +See PATTERNS in +.Xr ssh_config 5 +for more information on patterns. +.Pp +In addition to the wildcard matching that may be applied to hostnames or +addresses, a +.Cm from +stanza may match IP addresses using CIDR address/masklen notation. +.Pp +The purpose of this option is to optionally increase security: public key +authentication by itself does not trust the network or name servers or +anything (but the key); however, if somebody somehow steals the key, the key +permits an intruder to log in from anywhere in the world. +This additional option makes using a stolen key more difficult (name +servers and/or routers would have to be compromised in addition to +just the key). +.It Cm no-agent-forwarding +Forbids authentication agent forwarding when this key is used for +authentication. +.It Cm no-port-forwarding +Forbids TCP forwarding when this key is used for authentication. +Any port forward requests by the client will return an error. +This might be used, e.g. in connection with the +.Cm command +option. +.It Cm no-pty +Prevents tty allocation (a request to allocate a pty will fail). +.It Cm no-user-rc +Disables execution of +.Pa ~/.ssh/rc . +.It Cm no-X11-forwarding +Forbids X11 forwarding when this key is used for authentication. +Any X11 forward requests by the client will return an error. +.It Cm permitlisten="[host:]port" +Limit remote port forwarding with the +.Xr ssh 1 +.Fl R +option such that it may only listen on the specified host (optional) and port. +IPv6 addresses can be specified by enclosing the address in square brackets. +Multiple +.Cm permitlisten +options may be applied separated by commas. +Hostnames may include wildcards as described in the PATTERNS section in +.Xr ssh_config 5 . +A port specification of +.Cm * +matches any port. +Note that the setting of +.Cm GatewayPorts +may further restrict listen addresses. +Note that +.Xr ssh 1 +will send a hostname of +.Dq localhost +if a listen host was not specified when the forwarding was requested, and +that this name is treated differently to the explicit localhost addresses +.Dq 127.0.0.1 +and +.Dq ::1 . +.It Cm permitopen="host:port" +Limit local port forwarding with the +.Xr ssh 1 +.Fl L +option such that it may only connect to the specified host and port. +IPv6 addresses can be specified by enclosing the address in square brackets. +Multiple +.Cm permitopen +options may be applied separated by commas. +No pattern matching or name lookup is performed on the +specified hostnames, they must be literal host names and/or addresses. +A port specification of +.Cm * +matches any port. +.It Cm port-forwarding +Enable port forwarding previously disabled by the +.Cm restrict +option. +.It Cm principals="principals" +On a +.Cm cert-authority +line, specifies allowed principals for certificate authentication as a +comma-separated list. +At least one name from the list must appear in the certificate's +list of principals for the certificate to be accepted. +This option is ignored for keys that are not marked as trusted certificate +signers using the +.Cm cert-authority +option. +.It Cm pty +Permits tty allocation previously disabled by the +.Cm restrict +option. +.It Cm no-touch-required +Do not require demonstration of user presence +for signatures made using this key. +This option only makes sense for the FIDO authenticator algorithms +.Cm ecdsa-sk +and +.Cm ed25519-sk . +.It Cm verify-required +Require that signatures made using this key attest that they verified +the user, e.g. via a PIN. +This option only makes sense for the FIDO authenticator algorithms +.Cm ecdsa-sk +and +.Cm ed25519-sk . +.It Cm restrict +Enable all restrictions, i.e. disable port, agent and X11 forwarding, +as well as disabling PTY allocation +and execution of +.Pa ~/.ssh/rc . +If any future restriction capabilities are added to authorized_keys files, +they will be included in this set. +.It Cm tunnel="n" +Force a +.Xr tun 4 +device on the server. +Without this option, the next available device will be used if +the client requests a tunnel. +.It Cm user-rc +Enables execution of +.Pa ~/.ssh/rc +previously disabled by the +.Cm restrict +option. +.It Cm X11-forwarding +Permits X11 forwarding previously disabled by the +.Cm restrict +option. +.El +.Pp +An example authorized_keys file: +.Bd -literal -offset 3n +# Comments are allowed at start of line. Blank lines are allowed. +# Plain key, no restrictions +ssh-rsa ... +# Forced command, disable PTY and all forwarding +restrict,command="dump /home" ssh-rsa ... +# Restriction of ssh -L forwarding destinations +permitopen="192.0.2.1:80",permitopen="192.0.2.2:25" ssh-rsa ... +# Restriction of ssh -R forwarding listeners +permitlisten="localhost:8080",permitlisten="[::1]:22000" ssh-rsa ... +# Configuration for tunnel forwarding +tunnel="0",command="sh /etc/netstart tun0" ssh-rsa ... +# Override of restriction to allow PTY allocation +restrict,pty,command="nethack" ssh-rsa ... +# Allow FIDO key without requiring touch +no-touch-required sk-ecdsa-sha2-nistp256@openssh.com ... +# Require user-verification (e.g. PIN or biometric) for FIDO key +verify-required sk-ecdsa-sha2-nistp256@openssh.com ... +# Trust CA key, allow touch-less FIDO if requested in certificate +cert-authority,no-touch-required,principals="user_a" ssh-rsa ... +.Ed +.Sh SSH_KNOWN_HOSTS FILE FORMAT +The +.Pa /etc/ssh/ssh_known_hosts +and +.Pa ~/.ssh/known_hosts +files contain host public keys for all known hosts. +The global file should +be prepared by the administrator (optional), and the per-user file is +maintained automatically: whenever the user connects to an unknown host, +its key is added to the per-user file. +.Pp +Each line in these files contains the following fields: marker (optional), +hostnames, keytype, base64-encoded key, comment. +The fields are separated by spaces. +.Pp +The marker is optional, but if it is present then it must be one of +.Dq @cert-authority , +to indicate that the line contains a certification authority (CA) key, +or +.Dq @revoked , +to indicate that the key contained on the line is revoked and must not ever +be accepted. +Only one marker should be used on a key line. +.Pp +Hostnames is a comma-separated list of patterns +.Pf ( Ql * +and +.Ql \&? +act as +wildcards); each pattern in turn is matched against the host name. +When +.Nm sshd +is authenticating a client, such as when using +.Cm HostbasedAuthentication , +this will be the canonical client host name. +When +.Xr ssh 1 +is authenticating a server, this will be the host name +given by the user, the value of the +.Xr ssh 1 +.Cm HostkeyAlias +if it was specified, or the canonical server hostname if the +.Xr ssh 1 +.Cm CanonicalizeHostname +option was used. +.Pp +A pattern may also be preceded by +.Ql \&! +to indicate negation: if the host name matches a negated +pattern, it is not accepted (by that line) even if it matched another +pattern on the line. +A hostname or address may optionally be enclosed within +.Ql \&[ +and +.Ql \&] +brackets then followed by +.Ql \&: +and a non-standard port number. +.Pp +Alternately, hostnames may be stored in a hashed form which hides host names +and addresses should the file's contents be disclosed. +Hashed hostnames start with a +.Ql | +character. +Only one hashed hostname may appear on a single line and none of the above +negation or wildcard operators may be applied. +.Pp +The keytype and base64-encoded key are taken directly from the host key; they +can be obtained, for example, from +.Pa /etc/ssh/ssh_host_rsa_key.pub . +The optional comment field continues to the end of the line, and is not used. +.Pp +Lines starting with +.Ql # +and empty lines are ignored as comments. +.Pp +When performing host authentication, authentication is accepted if any +matching line has the proper key; either one that matches exactly or, +if the server has presented a certificate for authentication, the key +of the certification authority that signed the certificate. +For a key to be trusted as a certification authority, it must use the +.Dq @cert-authority +marker described above. +.Pp +The known hosts file also provides a facility to mark keys as revoked, +for example when it is known that the associated private key has been +stolen. +Revoked keys are specified by including the +.Dq @revoked +marker at the beginning of the key line, and are never accepted for +authentication or as certification authorities, but instead will +produce a warning from +.Xr ssh 1 +when they are encountered. +.Pp +It is permissible (but not +recommended) to have several lines or different host keys for the same +names. +This will inevitably happen when short forms of host names +from different domains are put in the file. +It is possible +that the files contain conflicting information; authentication is +accepted if valid information can be found from either file. +.Pp +Note that the lines in these files are typically hundreds of characters +long, and you definitely don't want to type in the host keys by hand. +Rather, generate them by a script, +.Xr ssh-keyscan 1 +or by taking, for example, +.Pa /etc/ssh/ssh_host_rsa_key.pub +and adding the host names at the front. +.Xr ssh-keygen 1 +also offers some basic automated editing for +.Pa ~/.ssh/known_hosts +including removing hosts matching a host name and converting all host +names to their hashed representations. +.Pp +An example ssh_known_hosts file: +.Bd -literal -offset 3n +# Comments allowed at start of line +closenet,...,192.0.2.53 1024 37 159...93 closenet.example.net +cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= +# A hashed hostname +|1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa +AAAA1234.....= +# A revoked key +@revoked * ssh-rsa AAAAB5W... +# A CA key, accepted for any host in *.mydomain.com or *.mydomain.org +@cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... +.Ed +.Sh FILES +.Bl -tag -width Ds -compact +.It Pa ~/.hushlogin +This file is used to suppress printing the last login time and +.Pa /etc/motd , +if +.Cm PrintLastLog +and +.Cm PrintMotd , +respectively, +are enabled. +It does not suppress printing of the banner specified by +.Cm Banner . +.Pp +.It Pa ~/.rhosts +This file is used for host-based authentication (see +.Xr ssh 1 +for more information). +On some machines this file may need to be +world-readable if the user's home directory is on an NFS partition, +because +.Nm +reads it as root. +Additionally, this file must be owned by the user, +and must not have write permissions for anyone else. +The recommended +permission for most machines is read/write for the user, and not +accessible by others. +.Pp +.It Pa ~/.shosts +This file is used in exactly the same way as +.Pa .rhosts , +but allows host-based authentication without permitting login with +rlogin/rsh. +.Pp +.It Pa ~/.ssh/ +This directory is the default location for all user-specific configuration +and authentication information. +There is no general requirement to keep the entire contents of this directory +secret, but the recommended permissions are read/write/execute for the user, +and not accessible by others. +.Pp +.It Pa ~/.ssh/authorized_keys +Lists the public keys (DSA, ECDSA, Ed25519, RSA) +that can be used for logging in as this user. +The format of this file is described above. +The content of the file is not highly sensitive, but the recommended +permissions are read/write for the user, and not accessible by others. +.Pp +If this file, the +.Pa ~/.ssh +directory, or the user's home directory are writable +by other users, then the file could be modified or replaced by unauthorized +users. +In this case, +.Nm +will not allow it to be used unless the +.Cm StrictModes +option has been set to +.Dq no . +.Pp +.It Pa ~/.ssh/environment +This file is read into the environment at login (if it exists). +It can only contain empty lines, comment lines (that start with +.Ql # ) , +and assignment lines of the form name=value. +The file should be writable +only by the user; it need not be readable by anyone else. +Environment processing is disabled by default and is +controlled via the +.Cm PermitUserEnvironment +option. +.Pp +.It Pa ~/.ssh/known_hosts +Contains a list of host keys for all hosts the user has logged into +that are not already in the systemwide list of known host keys. +The format of this file is described above. +This file should be writable only by root/the owner and +can, but need not be, world-readable. +.Pp +.It Pa ~/.ssh/rc +Contains initialization routines to be run before +the user's home directory becomes accessible. +This file should be writable only by the user, and need not be +readable by anyone else. +.Pp +.It Pa /etc/hosts.equiv +This file is for host-based authentication (see +.Xr ssh 1 ) . +It should only be writable by root. +.Pp +.It Pa /etc/moduli +Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange" +key exchange method. +The file format is described in +.Xr moduli 5 . +If no usable groups are found in this file then fixed internal groups will +be used. +.Pp +.It Pa /etc/motd +See +.Xr motd 5 . +.Pp +.It Pa /etc/nologin +If this file exists, +.Nm +refuses to let anyone except root log in. +The contents of the file +are displayed to anyone trying to log in, and non-root connections are +refused. +The file should be world-readable. +.Pp +.It Pa /etc/shosts.equiv +This file is used in exactly the same way as +.Pa hosts.equiv , +but allows host-based authentication without permitting login with +rlogin/rsh. +.Pp +.It Pa /etc/ssh/ssh_host_ecdsa_key +.It Pa /etc/ssh/ssh_host_ed25519_key +.It Pa /etc/ssh/ssh_host_rsa_key +These files contain the private parts of the host keys. +These files should only be owned by root, readable only by root, and not +accessible to others. +Note that +.Nm +does not start if these files are group/world-accessible. +.Pp +.It Pa /etc/ssh/ssh_host_ecdsa_key.pub +.It Pa /etc/ssh/ssh_host_ed25519_key.pub +.It Pa /etc/ssh/ssh_host_rsa_key.pub +These files contain the public parts of the host keys. +These files should be world-readable but writable only by +root. +Their contents should match the respective private parts. +These files are not +really used for anything; they are provided for the convenience of +the user so their contents can be copied to known hosts files. +These files are created using +.Xr ssh-keygen 1 . +.Pp +.It Pa /etc/ssh/ssh_known_hosts +Systemwide list of known host keys. +This file should be prepared by the +system administrator to contain the public host keys of all machines in the +organization. +The format of this file is described above. +This file should be writable only by root/the owner and +should be world-readable. +.Pp +.It Pa /etc/ssh/sshd_config +Contains configuration data for +.Nm sshd . +The file format and configuration options are described in +.Xr sshd_config 5 . +.Pp +.It Pa /etc/ssh/sshrc +Similar to +.Pa ~/.ssh/rc , +it can be used to specify +machine-specific login-time initializations globally. +This file should be writable only by root, and should be world-readable. +.Pp +.It Pa /var/empty +.Xr chroot 2 +directory used by +.Nm +during privilege separation in the pre-authentication phase. +The directory should not contain any files and must be owned by root +and not group or world-writable. +.Pp +.It Pa /var/run/sshd.pid +Contains the process ID of the +.Nm +listening for connections (if there are several daemons running +concurrently for different ports, this contains the process ID of the one +started last). +The content of this file is not sensitive; it can be world-readable. +.El +.Sh SEE ALSO +.Xr scp 1 , +.Xr sftp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr ssh-keyscan 1 , +.Xr chroot 2 , +.Xr login.conf 5 , +.Xr moduli 5 , +.Xr sshd_config 5 , +.Xr inetd 8 , +.Xr sftp-server 8 +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. +Niels Provos and Markus Friedl contributed support +for privilege separation. diff --git a/aosp/external/openssh/sshd.c b/aosp/external/openssh/sshd.c new file mode 100644 index 0000000000000000000000000000000000000000..eeada391256cd8eedd26964937b74d514599e063 --- /dev/null +++ b/aosp/external/openssh/sshd.c @@ -0,0 +1,2449 @@ +/* $OpenBSD: sshd.c,v 1.585 2022/03/18 04:04:11 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This program is the ssh daemon. It listens for connections from clients, + * and performs authentication, executes use commands or shell, and forwards + * information to/from the application to the user client over an encrypted + * connection. This can also handle forwarding of X11, TCP/IP, and + * authentication agent connections. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation: + * Privilege Separation: + * + * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. + * Copyright (c) 2002 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include "openbsd-compat/sys-tree.h" +#include "openbsd-compat/sys-queue.h" +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif +#include +#ifdef HAVE_POLL_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#include "openbsd-compat/openssl-compat.h" +#endif + +#ifdef HAVE_SECUREWARE +#include +#include +#endif + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh2.h" +#include "sshpty.h" +#include "packet.h" +#include "log.h" +#include "sshbuf.h" +#include "misc.h" +#include "match.h" +#include "servconf.h" +#include "uidswap.h" +#include "compat.h" +#include "cipher.h" +#include "digest.h" +#include "sshkey.h" +#include "kex.h" +#include "myproposal.h" +#include "authfile.h" +#include "pathnames.h" +#include "atomicio.h" +#include "canohost.h" +#include "hostfile.h" +#include "auth.h" +#include "authfd.h" +#include "msg.h" +#include "dispatch.h" +#include "channels.h" +#include "session.h" +#include "monitor.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif +#include "monitor_wrap.h" +#include "ssh-sandbox.h" +#include "auth-options.h" +#include "version.h" +#include "ssherr.h" +#include "sk-api.h" +#include "srclimit.h" +#include "dh.h" + +/* Re-exec fds */ +#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) +#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) +#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) +#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) + +extern char *__progname; + +/* Server configuration options. */ +ServerOptions options; + +/* Name of the server configuration file. */ +char *config_file_name = _PATH_SERVER_CONFIG_FILE; + +/* + * Debug mode flag. This can be set on the command line. If debug + * mode is enabled, extra debugging output will be sent to the system + * log, the daemon will not go to background, and will exit after processing + * the first connection. + */ +int debug_flag = 0; + +/* + * Indicating that the daemon should only test the configuration and keys. + * If test_flag > 1 ("-T" flag), then sshd will also dump the effective + * configuration, optionally using connection information provided by the + * "-C" flag. + */ +static int test_flag = 0; + +/* Flag indicating that the daemon is being started from inetd. */ +static int inetd_flag = 0; + +/* Flag indicating that sshd should not detach and become a daemon. */ +static int no_daemon_flag = 0; + +/* debug goes to stderr unless inetd_flag is set */ +static int log_stderr = 0; + +/* Saved arguments to main(). */ +static char **saved_argv; +static int saved_argc; + +/* re-exec */ +static int rexeced_flag = 0; +static int rexec_flag = 1; +static int rexec_argc = 0; +static char **rexec_argv; + +/* + * The sockets that the server is listening; this is used in the SIGHUP + * signal handler. + */ +#define MAX_LISTEN_SOCKS 16 +static int listen_socks[MAX_LISTEN_SOCKS]; +static int num_listen_socks = 0; + +/* Daemon's agent connection */ +int auth_sock = -1; +static int have_agent = 0; + +/* + * Any really sensitive data in the application is contained in this + * structure. The idea is that this structure could be locked into memory so + * that the pages do not get written into swap. However, there are some + * problems. The private key contains BIGNUMs, and we do not (in principle) + * have access to the internals of them, and locking just the structure is + * not very useful. Currently, memory locking is not implemented. + */ +struct { + struct sshkey **host_keys; /* all private host keys */ + struct sshkey **host_pubkeys; /* all public host keys */ + struct sshkey **host_certificates; /* all public host certificates */ + int have_ssh2_key; +} sensitive_data; + +/* This is set to true when a signal is received. */ +static volatile sig_atomic_t received_sighup = 0; +static volatile sig_atomic_t received_sigterm = 0; + +/* record remote hostname or ip */ +u_int utmp_len = HOST_NAME_MAX+1; + +/* + * startup_pipes/flags are used for tracking children of the listening sshd + * process early in their lifespans. This tracking is needed for three things: + * + * 1) Implementing the MaxStartups limit of concurrent unauthenticated + * connections. + * 2) Avoiding a race condition for SIGHUP processing, where child processes + * may have listen_socks open that could collide with main listener process + * after it restarts. + * 3) Ensuring that rexec'd sshd processes have received their initial state + * from the parent listen process before handling SIGHUP. + * + * Child processes signal that they have completed closure of the listen_socks + * and (if applicable) received their rexec state by sending a char over their + * sock. Child processes signal that authentication has completed by closing + * the sock (or by exiting). + */ +static int *startup_pipes = NULL; +static int *startup_flags = NULL; /* Indicates child closed listener */ +static int startup_pipe = -1; /* in child */ + +/* variables used for privilege separation */ +int use_privsep = -1; +struct monitor *pmonitor = NULL; +int privsep_is_preauth = 1; +static int privsep_chroot = 1; + +/* global connection state and authentication contexts */ +Authctxt *the_authctxt = NULL; +struct ssh *the_active_state; + +/* global key/cert auth options. XXX move to permanent ssh->authctxt? */ +struct sshauthopt *auth_opts = NULL; + +/* sshd_config buffer */ +struct sshbuf *cfg; + +/* Included files from the configuration file */ +struct include_list includes = TAILQ_HEAD_INITIALIZER(includes); + +/* message to be displayed after login */ +struct sshbuf *loginmsg; + +/* Unprivileged user */ +struct passwd *privsep_pw = NULL; + +/* Prototypes for various functions defined later in this file. */ +void destroy_sensitive_data(void); +void demote_sensitive_data(void); +static void do_ssh2_kex(struct ssh *); + +static char *listener_proctitle; + +/* + * Close all listening sockets + */ +static void +close_listen_socks(void) +{ + int i; + + for (i = 0; i < num_listen_socks; i++) + close(listen_socks[i]); + num_listen_socks = 0; +} + +static void +close_startup_pipes(void) +{ + int i; + + if (startup_pipes) + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1) + close(startup_pipes[i]); +} + +/* + * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; + * the effect is to reread the configuration file (and to regenerate + * the server key). + */ + +/*ARGSUSED*/ +static void +sighup_handler(int sig) +{ + received_sighup = 1; +} + +/* + * Called from the main program after receiving SIGHUP. + * Restarts the server. + */ +static void +sighup_restart(void) +{ + logit("Received SIGHUP; restarting."); + if (options.pid_file != NULL) + unlink(options.pid_file); + platform_pre_restart(); + close_listen_socks(); + close_startup_pipes(); + ssh_signal(SIGHUP, SIG_IGN); /* will be restored after exec */ + execv(saved_argv[0], saved_argv); + logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], + strerror(errno)); + exit(1); +} + +/* + * Generic signal handler for terminating signals in the master daemon. + */ +/*ARGSUSED*/ +static void +sigterm_handler(int sig) +{ + received_sigterm = sig; +} + +/* + * SIGCHLD handler. This is called whenever a child dies. This will then + * reap any zombies left by exited children. + */ +/*ARGSUSED*/ +static void +main_sigchld_handler(int sig) +{ + int save_errno = errno; + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || + (pid == -1 && errno == EINTR)) + ; + errno = save_errno; +} + +/* + * Signal handler for the alarm after the login grace period has expired. + */ +/*ARGSUSED*/ +static void +grace_alarm_handler(int sig) +{ + /* + * Try to kill any processes that we have spawned, E.g. authorized + * keys command helpers or privsep children. + */ + if (getpgid(0) == getpid()) { + ssh_signal(SIGTERM, SIG_IGN); + kill(0, SIGTERM); + } + + /* Log error and exit. */ + sigdie("Timeout before authentication for %s port %d", + ssh_remote_ipaddr(the_active_state), + ssh_remote_port(the_active_state)); +} + +/* Destroy the host and server keys. They will no longer be needed. */ +void +destroy_sensitive_data(void) +{ + u_int i; + + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { + sshkey_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = NULL; + } + if (sensitive_data.host_certificates[i]) { + sshkey_free(sensitive_data.host_certificates[i]); + sensitive_data.host_certificates[i] = NULL; + } + } +} + +/* Demote private to public keys for network child */ +void +demote_sensitive_data(void) +{ + struct sshkey *tmp; + u_int i; + int r; + + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { + if ((r = sshkey_from_private( + sensitive_data.host_keys[i], &tmp)) != 0) + fatal_r(r, "could not demote host %s key", + sshkey_type(sensitive_data.host_keys[i])); + sshkey_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = tmp; + } + /* Certs do not need demotion */ + } +} + +static void +reseed_prngs(void) +{ + u_int32_t rnd[256]; + +#ifdef WITH_OPENSSL + RAND_poll(); +#endif + arc4random_stir(); /* noop on recent arc4random() implementations */ + arc4random_buf(rnd, sizeof(rnd)); /* let arc4random notice PID change */ + +#ifdef WITH_OPENSSL + RAND_seed(rnd, sizeof(rnd)); + /* give libcrypto a chance to notice the PID change */ + if ((RAND_bytes((u_char *)rnd, 1)) != 1) + fatal("%s: RAND_bytes failed", __func__); +#endif + + explicit_bzero(rnd, sizeof(rnd)); +} + +static void +privsep_preauth_child(void) +{ + gid_t gidset[1]; + + /* Enable challenge-response authentication for privilege separation */ + privsep_challenge_enable(); + +#ifdef GSSAPI + /* Cache supported mechanism OIDs for later use */ + ssh_gssapi_prepare_supported_oids(); +#endif + + reseed_prngs(); + + /* Demote the private keys to public keys. */ + demote_sensitive_data(); + + /* Demote the child */ + if (privsep_chroot) { + /* Change our root directory */ + if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) + fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, + strerror(errno)); + if (chdir("/") == -1) + fatal("chdir(\"/\"): %s", strerror(errno)); + + /* Drop our privileges */ + debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, + (u_int)privsep_pw->pw_gid); + gidset[0] = privsep_pw->pw_gid; + if (setgroups(1, gidset) == -1) + fatal("setgroups: %.100s", strerror(errno)); + permanently_set_uid(privsep_pw); + } +} + +static int +privsep_preauth(struct ssh *ssh) +{ + int status, r; + pid_t pid; + struct ssh_sandbox *box = NULL; + + /* Set up unprivileged child process to deal with network data */ + pmonitor = monitor_init(); + /* Store a pointer to the kex for later rekeying */ + pmonitor->m_pkex = &ssh->kex; + + if (use_privsep == PRIVSEP_ON) + box = ssh_sandbox_init(pmonitor); + pid = fork(); + if (pid == -1) { + fatal("fork of unprivileged child failed"); + } else if (pid != 0) { + debug2("Network child is on pid %ld", (long)pid); + + pmonitor->m_pid = pid; + if (have_agent) { + r = ssh_get_authentication_socket(&auth_sock); + if (r != 0) { + error_r(r, "Could not get agent socket"); + have_agent = 0; + } + } + if (box != NULL) + ssh_sandbox_parent_preauth(box, pid); + monitor_child_preauth(ssh, pmonitor); + + /* Wait for the child's exit status */ + while (waitpid(pid, &status, 0) == -1) { + if (errno == EINTR) + continue; + pmonitor->m_pid = -1; + fatal_f("waitpid: %s", strerror(errno)); + } + privsep_is_preauth = 0; + pmonitor->m_pid = -1; + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + fatal_f("preauth child exited with status %d", + WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) + fatal_f("preauth child terminated by signal %d", + WTERMSIG(status)); + if (box != NULL) + ssh_sandbox_parent_finish(box); + return 1; + } else { + /* child */ + close(pmonitor->m_sendfd); + close(pmonitor->m_log_recvfd); + + /* Arrange for logging to be sent to the monitor */ + set_log_handler(mm_log_handler, pmonitor); + + privsep_preauth_child(); + setproctitle("%s", "[net]"); + if (box != NULL) + ssh_sandbox_child(box); + + return 0; + } +} + +static void +privsep_postauth(struct ssh *ssh, Authctxt *authctxt) +{ +#ifdef DISABLE_FD_PASSING + if (1) { +#else + if (authctxt->pw->pw_uid == 0) { +#endif + /* File descriptor passing is broken or root login */ + use_privsep = 0; + goto skip; + } + + /* New socket pair */ + monitor_reinit(pmonitor); + + pmonitor->m_pid = fork(); + if (pmonitor->m_pid == -1) + fatal("fork of unprivileged child failed"); + else if (pmonitor->m_pid != 0) { + verbose("User child is on pid %ld", (long)pmonitor->m_pid); + sshbuf_reset(loginmsg); + monitor_clear_keystate(ssh, pmonitor); + monitor_child_postauth(ssh, pmonitor); + + /* NEVERREACHED */ + exit(0); + } + + /* child */ + + close(pmonitor->m_sendfd); + pmonitor->m_sendfd = -1; + + /* Demote the private keys to public keys. */ + demote_sensitive_data(); + + reseed_prngs(); + + /* Drop privileges */ + do_setusercontext(authctxt->pw); + + skip: + /* It is safe now to apply the key state */ + monitor_apply_keystate(ssh, pmonitor); + + /* + * Tell the packet layer that authentication was successful, since + * this information is not part of the key state. + */ + ssh_packet_set_authenticated(ssh); +} + +static void +append_hostkey_type(struct sshbuf *b, const char *s) +{ + int r; + + if (match_pattern_list(s, options.hostkeyalgorithms, 0) != 1) { + debug3_f("%s key not permitted by HostkeyAlgorithms", s); + return; + } + if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0) + fatal_fr(r, "sshbuf_putf"); +} + +static char * +list_hostkey_types(void) +{ + struct sshbuf *b; + struct sshkey *key; + char *ret; + u_int i; + + if ((b = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + for (i = 0; i < options.num_host_key_files; i++) { + key = sensitive_data.host_keys[i]; + if (key == NULL) + key = sensitive_data.host_pubkeys[i]; + if (key == NULL) + continue; + switch (key->type) { + case KEY_RSA: + /* for RSA we also support SHA2 signatures */ + append_hostkey_type(b, "rsa-sha2-512"); + append_hostkey_type(b, "rsa-sha2-256"); + /* FALLTHROUGH */ + case KEY_DSA: + case KEY_ECDSA: + case KEY_ED25519: + case KEY_ECDSA_SK: + case KEY_ED25519_SK: + case KEY_XMSS: + append_hostkey_type(b, sshkey_ssh_name(key)); + break; + } + /* If the private key has a cert peer, then list that too */ + key = sensitive_data.host_certificates[i]; + if (key == NULL) + continue; + switch (key->type) { + case KEY_RSA_CERT: + /* for RSA we also support SHA2 signatures */ + append_hostkey_type(b, + "rsa-sha2-512-cert-v01@openssh.com"); + append_hostkey_type(b, + "rsa-sha2-256-cert-v01@openssh.com"); + /* FALLTHROUGH */ + case KEY_DSA_CERT: + case KEY_ECDSA_CERT: + case KEY_ED25519_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_ED25519_SK_CERT: + case KEY_XMSS_CERT: + append_hostkey_type(b, sshkey_ssh_name(key)); + break; + } + } + if ((ret = sshbuf_dup_string(b)) == NULL) + fatal_f("sshbuf_dup_string failed"); + sshbuf_free(b); + debug_f("%s", ret); + return ret; +} + +static struct sshkey * +get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) +{ + u_int i; + struct sshkey *key; + + for (i = 0; i < options.num_host_key_files; i++) { + switch (type) { + case KEY_RSA_CERT: + case KEY_DSA_CERT: + case KEY_ECDSA_CERT: + case KEY_ED25519_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_ED25519_SK_CERT: + case KEY_XMSS_CERT: + key = sensitive_data.host_certificates[i]; + break; + default: + key = sensitive_data.host_keys[i]; + if (key == NULL && !need_private) + key = sensitive_data.host_pubkeys[i]; + break; + } + if (key == NULL || key->type != type) + continue; + switch (type) { + case KEY_ECDSA: + case KEY_ECDSA_SK: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: + if (key->ecdsa_nid != nid) + continue; + /* FALLTHROUGH */ + default: + return need_private ? + sensitive_data.host_keys[i] : key; + } + } + return NULL; +} + +struct sshkey * +get_hostkey_public_by_type(int type, int nid, struct ssh *ssh) +{ + return get_hostkey_by_type(type, nid, 0, ssh); +} + +struct sshkey * +get_hostkey_private_by_type(int type, int nid, struct ssh *ssh) +{ + return get_hostkey_by_type(type, nid, 1, ssh); +} + +struct sshkey * +get_hostkey_by_index(int ind) +{ + if (ind < 0 || (u_int)ind >= options.num_host_key_files) + return (NULL); + return (sensitive_data.host_keys[ind]); +} + +struct sshkey * +get_hostkey_public_by_index(int ind, struct ssh *ssh) +{ + if (ind < 0 || (u_int)ind >= options.num_host_key_files) + return (NULL); + return (sensitive_data.host_pubkeys[ind]); +} + +int +get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh) +{ + u_int i; + + for (i = 0; i < options.num_host_key_files; i++) { + if (sshkey_is_cert(key)) { + if (key == sensitive_data.host_certificates[i] || + (compare && sensitive_data.host_certificates[i] && + sshkey_equal(key, + sensitive_data.host_certificates[i]))) + return (i); + } else { + if (key == sensitive_data.host_keys[i] || + (compare && sensitive_data.host_keys[i] && + sshkey_equal(key, sensitive_data.host_keys[i]))) + return (i); + if (key == sensitive_data.host_pubkeys[i] || + (compare && sensitive_data.host_pubkeys[i] && + sshkey_equal(key, sensitive_data.host_pubkeys[i]))) + return (i); + } + } + return (-1); +} + +/* Inform the client of all hostkeys */ +static void +notify_hostkeys(struct ssh *ssh) +{ + struct sshbuf *buf; + struct sshkey *key; + u_int i, nkeys; + int r; + char *fp; + + /* Some clients cannot cope with the hostkeys message, skip those. */ + if (ssh->compat & SSH_BUG_HOSTKEYS) + return; + + if ((buf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + for (i = nkeys = 0; i < options.num_host_key_files; i++) { + key = get_hostkey_public_by_index(i, ssh); + if (key == NULL || key->type == KEY_UNSPEC || + sshkey_is_cert(key)) + continue; + fp = sshkey_fingerprint(key, options.fingerprint_hash, + SSH_FP_DEFAULT); + debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp); + free(fp); + if (nkeys == 0) { + /* + * Start building the request when we find the + * first usable key. + */ + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "hostkeys-00@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 0)) != 0) /* want reply */ + sshpkt_fatal(ssh, r, "%s: start request", __func__); + } + /* Append the key to the request */ + sshbuf_reset(buf); + if ((r = sshkey_putb(key, buf)) != 0) + fatal_fr(r, "couldn't put hostkey %d", i); + if ((r = sshpkt_put_stringb(ssh, buf)) != 0) + sshpkt_fatal(ssh, r, "%s: append key", __func__); + nkeys++; + } + debug3_f("sent %u hostkeys", nkeys); + if (nkeys == 0) + fatal_f("no hostkeys"); + if ((r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send", __func__); + sshbuf_free(buf); +} + +/* + * returns 1 if connection should be dropped, 0 otherwise. + * dropping starts at connection #max_startups_begin with a probability + * of (max_startups_rate/100). the probability increases linearly until + * all connections are dropped for startups > max_startups + */ +static int +should_drop_connection(int startups) +{ + int p, r; + + if (startups < options.max_startups_begin) + return 0; + if (startups >= options.max_startups) + return 1; + if (options.max_startups_rate == 100) + return 1; + + p = 100 - options.max_startups_rate; + p *= startups - options.max_startups_begin; + p /= options.max_startups - options.max_startups_begin; + p += options.max_startups_rate; + r = arc4random_uniform(100); + + debug_f("p %d, r %d", p, r); + return (r < p) ? 1 : 0; +} + +/* + * Check whether connection should be accepted by MaxStartups. + * Returns 0 if the connection is accepted. If the connection is refused, + * returns 1 and attempts to send notification to client. + * Logs when the MaxStartups condition is entered or exited, and periodically + * while in that state. + */ +static int +drop_connection(int sock, int startups, int notify_pipe) +{ + char *laddr, *raddr; + const char msg[] = "Exceeded MaxStartups\r\n"; + static time_t last_drop, first_drop; + static u_int ndropped; + LogLevel drop_level = SYSLOG_LEVEL_VERBOSE; + time_t now; + + now = monotime(); + if (!should_drop_connection(startups) && + srclimit_check_allow(sock, notify_pipe) == 1) { + if (last_drop != 0 && + startups < options.max_startups_begin - 1) { + /* XXX maybe need better hysteresis here */ + logit("exited MaxStartups throttling after %s, " + "%u connections dropped", + fmt_timeframe(now - first_drop), ndropped); + last_drop = 0; + } + return 0; + } + +#define SSHD_MAXSTARTUPS_LOG_INTERVAL (5 * 60) + if (last_drop == 0) { + error("beginning MaxStartups throttling"); + drop_level = SYSLOG_LEVEL_INFO; + first_drop = now; + ndropped = 0; + } else if (last_drop + SSHD_MAXSTARTUPS_LOG_INTERVAL < now) { + /* Periodic logs */ + error("in MaxStartups throttling for %s, " + "%u connections dropped", + fmt_timeframe(now - first_drop), ndropped + 1); + drop_level = SYSLOG_LEVEL_INFO; + } + last_drop = now; + ndropped++; + + laddr = get_local_ipaddr(sock); + raddr = get_peer_ipaddr(sock); + do_log2(drop_level, "drop connection #%d from [%s]:%d on [%s]:%d " + "past MaxStartups", startups, raddr, get_peer_port(sock), + laddr, get_local_port(sock)); + free(laddr); + free(raddr); + /* best-effort notification to client */ + (void)write(sock, msg, sizeof(msg) - 1); + return 1; +} + +static void +usage(void) +{ + fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSH_OPENSSL_VERSION); + fprintf(stderr, +"usage: sshd [-46DdeiqTt] [-C connection_spec] [-c host_cert_file]\n" +" [-E log_file] [-f config_file] [-g login_grace_time]\n" +" [-h host_key_file] [-o option] [-p port] [-u len]\n" + ); + exit(1); +} + +static void +send_rexec_state(int fd, struct sshbuf *conf) +{ + struct sshbuf *m = NULL, *inc = NULL; + struct include_item *item = NULL; + int r; + + debug3_f("entering fd = %d config len %zu", fd, + sshbuf_len(conf)); + + if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + /* pack includes into a string */ + TAILQ_FOREACH(item, &includes, entry) { + if ((r = sshbuf_put_cstring(inc, item->selector)) != 0 || + (r = sshbuf_put_cstring(inc, item->filename)) != 0 || + (r = sshbuf_put_stringb(inc, item->contents)) != 0) + fatal_fr(r, "compose includes"); + } + + /* + * Protocol from reexec master to child: + * string configuration + * string included_files[] { + * string selector + * string filename + * string contents + * } + * string rng_seed (if required) + */ + if ((r = sshbuf_put_stringb(m, conf)) != 0 || + (r = sshbuf_put_stringb(m, inc)) != 0) + fatal_fr(r, "compose config"); +#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) + rexec_send_rng_seed(m); +#endif + if (ssh_msg_send(fd, 0, m) == -1) + error_f("ssh_msg_send failed"); + + sshbuf_free(m); + sshbuf_free(inc); + + debug3_f("done"); +} + +static void +recv_rexec_state(int fd, struct sshbuf *conf) +{ + struct sshbuf *m, *inc; + u_char *cp, ver; + size_t len; + int r; + struct include_item *item; + + debug3_f("entering fd = %d", fd); + + if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (ssh_msg_recv(fd, m) == -1) + fatal_f("ssh_msg_recv failed"); + if ((r = sshbuf_get_u8(m, &ver)) != 0) + fatal_fr(r, "parse version"); + if (ver != 0) + fatal_f("rexec version mismatch"); + if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || + (r = sshbuf_get_stringb(m, inc)) != 0) + fatal_fr(r, "parse config"); + +#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) + rexec_recv_rng_seed(m); +#endif + + if (conf != NULL && (r = sshbuf_put(conf, cp, len))) + fatal_fr(r, "sshbuf_put"); + + while (sshbuf_len(inc) != 0) { + item = xcalloc(1, sizeof(*item)); + if ((item->contents = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 || + (r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 || + (r = sshbuf_get_stringb(inc, item->contents)) != 0) + fatal_fr(r, "parse includes"); + TAILQ_INSERT_TAIL(&includes, item, entry); + } + + free(cp); + sshbuf_free(m); + + debug3_f("done"); +} + +/* Accept a connection from inetd */ +static void +server_accept_inetd(int *sock_in, int *sock_out) +{ + if (rexeced_flag) { + close(REEXEC_CONFIG_PASS_FD); + *sock_in = *sock_out = dup(STDIN_FILENO); + } else { + *sock_in = dup(STDIN_FILENO); + *sock_out = dup(STDOUT_FILENO); + } + /* + * We intentionally do not close the descriptors 0, 1, and 2 + * as our code for setting the descriptors won't work if + * ttyfd happens to be one of those. + */ + if (stdfd_devnull(1, 1, !log_stderr) == -1) + error_f("stdfd_devnull failed"); + debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); +} + +/* + * Listen for TCP connections + */ +static void +listen_on_addrs(struct listenaddr *la) +{ + int ret, listen_sock; + struct addrinfo *ai; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + + for (ai = la->addrs; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (num_listen_socks >= MAX_LISTEN_SOCKS) + fatal("Too many listen sockets. " + "Enlarge MAX_LISTEN_SOCKS"); + if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + error("getnameinfo failed: %.100s", + ssh_gai_strerror(ret)); + continue; + } + /* Create socket for listening. */ + listen_sock = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (listen_sock == -1) { + /* kernel may not support ipv6 */ + verbose("socket: %.100s", strerror(errno)); + continue; + } + if (set_nonblock(listen_sock) == -1) { + close(listen_sock); + continue; + } + if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) { + verbose("socket: CLOEXEC: %s", strerror(errno)); + close(listen_sock); + continue; + } + /* Socket options */ + set_reuseaddr(listen_sock); + if (la->rdomain != NULL && + set_rdomain(listen_sock, la->rdomain) == -1) { + close(listen_sock); + continue; + } + + /* Only communicate in IPv6 over AF_INET6 sockets. */ + if (ai->ai_family == AF_INET6) + sock_set_v6only(listen_sock); + + debug("Bind to port %s on %s.", strport, ntop); + + /* Bind the socket to the desired port. */ + if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) == -1) { + error("Bind to port %s on %s failed: %.200s.", + strport, ntop, strerror(errno)); + close(listen_sock); + continue; + } + listen_socks[num_listen_socks] = listen_sock; + num_listen_socks++; + + /* Start listening on the port. */ + if (listen(listen_sock, SSH_LISTEN_BACKLOG) == -1) + fatal("listen on [%s]:%s: %.100s", + ntop, strport, strerror(errno)); + logit("Server listening on %s port %s%s%s.", + ntop, strport, + la->rdomain == NULL ? "" : " rdomain ", + la->rdomain == NULL ? "" : la->rdomain); + } +} + +static void +server_listen(void) +{ + u_int i; + + /* Initialise per-source limit tracking. */ + srclimit_init(options.max_startups, options.per_source_max_startups, + options.per_source_masklen_ipv4, options.per_source_masklen_ipv6); + + for (i = 0; i < options.num_listen_addrs; i++) { + listen_on_addrs(&options.listen_addrs[i]); + freeaddrinfo(options.listen_addrs[i].addrs); + free(options.listen_addrs[i].rdomain); + memset(&options.listen_addrs[i], 0, + sizeof(options.listen_addrs[i])); + } + free(options.listen_addrs); + options.listen_addrs = NULL; + options.num_listen_addrs = 0; + + if (!num_listen_socks) + fatal("Cannot bind any address."); +} + +/* + * The main TCP accept loop. Note that, for the non-debug case, returns + * from this function are in a forked subprocess. + */ +static void +server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) +{ + struct pollfd *pfd = NULL; + int i, j, ret, npfd; + int ostartups = -1, startups = 0, listening = 0, lameduck = 0; + int startup_p[2] = { -1 , -1 }, *startup_pollfd; + char c = 0; + struct sockaddr_storage from; + socklen_t fromlen; + pid_t pid; + u_char rnd[256]; + sigset_t nsigset, osigset; + + /* pipes connected to unauthenticated child sshd processes */ + startup_pipes = xcalloc(options.max_startups, sizeof(int)); + startup_flags = xcalloc(options.max_startups, sizeof(int)); + startup_pollfd = xcalloc(options.max_startups, sizeof(int)); + for (i = 0; i < options.max_startups; i++) + startup_pipes[i] = -1; + + /* + * Prepare signal mask that we use to block signals that might set + * received_sigterm or received_sighup, so that we are guaranteed + * to immediately wake up the ppoll if a signal is received after + * the flag is checked. + */ + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGHUP); + sigaddset(&nsigset, SIGCHLD); + sigaddset(&nsigset, SIGTERM); + sigaddset(&nsigset, SIGQUIT); + + /* sized for worst-case */ + pfd = xcalloc(num_listen_socks + options.max_startups, + sizeof(struct pollfd)); + + /* + * Stay listening for connections until the system crashes or + * the daemon is killed with a signal. + */ + for (;;) { + sigprocmask(SIG_BLOCK, &nsigset, &osigset); + if (received_sigterm) { + logit("Received signal %d; terminating.", + (int) received_sigterm); + close_listen_socks(); + if (options.pid_file != NULL) + unlink(options.pid_file); + exit(received_sigterm == SIGTERM ? 0 : 255); + } + if (ostartups != startups) { + setproctitle("%s [listener] %d of %d-%d startups", + listener_proctitle, startups, + options.max_startups_begin, options.max_startups); + ostartups = startups; + } + if (received_sighup) { + if (!lameduck) { + debug("Received SIGHUP; waiting for children"); + close_listen_socks(); + lameduck = 1; + } + if (listening <= 0) { + sigprocmask(SIG_SETMASK, &osigset, NULL); + sighup_restart(); + } + } + + for (i = 0; i < num_listen_socks; i++) { + pfd[i].fd = listen_socks[i]; + pfd[i].events = POLLIN; + } + npfd = num_listen_socks; + for (i = 0; i < options.max_startups; i++) { + startup_pollfd[i] = -1; + if (startup_pipes[i] != -1) { + pfd[npfd].fd = startup_pipes[i]; + pfd[npfd].events = POLLIN; + startup_pollfd[i] = npfd++; + } + } + + /* Wait until a connection arrives or a child exits. */ + ret = ppoll(pfd, npfd, NULL, &osigset); + if (ret == -1 && errno != EINTR) { + error("ppoll: %.100s", strerror(errno)); + if (errno == EINVAL) + cleanup_exit(1); /* can't recover */ + } + sigprocmask(SIG_SETMASK, &osigset, NULL); + if (ret == -1) + continue; + + for (i = 0; i < options.max_startups; i++) { + if (startup_pipes[i] == -1 || + startup_pollfd[i] == -1 || + !(pfd[startup_pollfd[i]].revents & (POLLIN|POLLHUP))) + continue; + switch (read(startup_pipes[i], &c, sizeof(c))) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + if (errno != EPIPE) { + error_f("startup pipe %d (fd=%d): " + "read %s", i, startup_pipes[i], + strerror(errno)); + } + /* FALLTHROUGH */ + case 0: + /* child exited or completed auth */ + close(startup_pipes[i]); + srclimit_done(startup_pipes[i]); + startup_pipes[i] = -1; + startups--; + if (startup_flags[i]) + listening--; + break; + case 1: + /* child has finished preliminaries */ + if (startup_flags[i]) { + listening--; + startup_flags[i] = 0; + } + break; + } + } + for (i = 0; i < num_listen_socks; i++) { + if (!(pfd[i].revents & POLLIN)) + continue; + fromlen = sizeof(from); + *newsock = accept(listen_socks[i], + (struct sockaddr *)&from, &fromlen); + if (*newsock == -1) { + if (errno != EINTR && errno != EWOULDBLOCK && + errno != ECONNABORTED && errno != EAGAIN) + error("accept: %.100s", + strerror(errno)); + if (errno == EMFILE || errno == ENFILE) + usleep(100 * 1000); + continue; + } + if (unset_nonblock(*newsock) == -1 || + pipe(startup_p) == -1) { + close(*newsock); + continue; + } + if (drop_connection(*newsock, startups, startup_p[0])) { + close(*newsock); + close(startup_p[0]); + close(startup_p[1]); + continue; + } + + if (rexec_flag && socketpair(AF_UNIX, + SOCK_STREAM, 0, config_s) == -1) { + error("reexec socketpair: %s", + strerror(errno)); + close(*newsock); + close(startup_p[0]); + close(startup_p[1]); + continue; + } + + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] == -1) { + startup_pipes[j] = startup_p[0]; + startups++; + startup_flags[j] = 1; + break; + } + + /* + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. + */ + if (debug_flag) { + /* + * In debugging mode. Close the listening + * socket, and start processing the + * connection without forking. + */ + debug("Server will not fork when running in debugging mode."); + close_listen_socks(); + *sock_in = *newsock; + *sock_out = *newsock; + close(startup_p[0]); + close(startup_p[1]); + startup_pipe = -1; + pid = getpid(); + if (rexec_flag) { + send_rexec_state(config_s[0], cfg); + close(config_s[0]); + } + free(pfd); + return; + } + + /* + * Normal production daemon. Fork, and have + * the child process the connection. The + * parent continues listening. + */ + platform_pre_fork(); + listening++; + if ((pid = fork()) == 0) { + /* + * Child. Close the listening and + * max_startup sockets. Start using + * the accepted socket. Reinitialize + * logging (since our pid has changed). + * We return from this function to handle + * the connection. + */ + platform_post_fork_child(); + startup_pipe = startup_p[1]; + close_startup_pipes(); + close_listen_socks(); + *sock_in = *newsock; + *sock_out = *newsock; + log_init(__progname, + options.log_level, + options.log_facility, + log_stderr); + if (rexec_flag) + close(config_s[0]); + else { + /* + * Signal parent that the preliminaries + * for this child are complete. For the + * re-exec case, this happens after the + * child has received the rexec state + * from the server. + */ + (void)atomicio(vwrite, startup_pipe, + "\0", 1); + } + free(pfd); + return; + } + + /* Parent. Stay in the loop. */ + platform_post_fork_parent(pid); + if (pid == -1) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %ld.", (long)pid); + + close(startup_p[1]); + + if (rexec_flag) { + close(config_s[1]); + send_rexec_state(config_s[0], cfg); + close(config_s[0]); + } + close(*newsock); + + /* + * Ensure that our random state differs + * from that of the child + */ + arc4random_stir(); + arc4random_buf(rnd, sizeof(rnd)); +#ifdef WITH_OPENSSL + RAND_seed(rnd, sizeof(rnd)); + if ((RAND_bytes((u_char *)rnd, 1)) != 1) + fatal("%s: RAND_bytes failed", __func__); +#endif + explicit_bzero(rnd, sizeof(rnd)); + } + } +} + +/* + * If IP options are supported, make sure there are none (log and + * return an error if any are found). Basically we are worried about + * source routing; it can be used to pretend you are somebody + * (ip-address) you are not. That itself may be "almost acceptable" + * under certain circumstances, but rhosts authentication is useless + * if source routing is accepted. Notice also that if we just dropped + * source routing here, the other side could use IP spoofing to do + * rest of the interaction and could still bypass security. So we + * exit here if we detect any IP options. + */ +static void +check_ip_options(struct ssh *ssh) +{ +#ifdef IP_OPTIONS + int sock_in = ssh_packet_get_connection_in(ssh); + struct sockaddr_storage from; + u_char opts[200]; + socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from); + char text[sizeof(opts) * 3 + 1]; + + memset(&from, 0, sizeof(from)); + if (getpeername(sock_in, (struct sockaddr *)&from, + &fromlen) == -1) + return; + if (from.ss_family != AF_INET) + return; + /* XXX IPv6 options? */ + + if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts, + &option_size) >= 0 && option_size != 0) { + text[0] = '\0'; + for (i = 0; i < option_size; i++) + snprintf(text + i*3, sizeof(text) - i*3, + " %2.2x", opts[i]); + fatal("Connection from %.100s port %d with IP opts: %.800s", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text); + } + return; +#endif /* IP_OPTIONS */ +} + +/* Set the routing domain for this process */ +static void +set_process_rdomain(struct ssh *ssh, const char *name) +{ +#if defined(HAVE_SYS_SET_PROCESS_RDOMAIN) + if (name == NULL) + return; /* default */ + + if (strcmp(name, "%D") == 0) { + /* "expands" to routing domain of connection */ + if ((name = ssh_packet_rdomain_in(ssh)) == NULL) + return; + } + /* NB. We don't pass 'ssh' to sys_set_process_rdomain() */ + return sys_set_process_rdomain(name); +#elif defined(__OpenBSD__) + int rtable, ortable = getrtable(); + const char *errstr; + + if (name == NULL) + return; /* default */ + + if (strcmp(name, "%D") == 0) { + /* "expands" to routing domain of connection */ + if ((name = ssh_packet_rdomain_in(ssh)) == NULL) + return; + } + + rtable = (int)strtonum(name, 0, 255, &errstr); + if (errstr != NULL) /* Shouldn't happen */ + fatal("Invalid routing domain \"%s\": %s", name, errstr); + if (rtable != ortable && setrtable(rtable) != 0) + fatal("Unable to set routing domain %d: %s", + rtable, strerror(errno)); + debug_f("set routing domain %d (was %d)", rtable, ortable); +#else /* defined(__OpenBSD__) */ + fatal("Unable to set routing domain: not supported in this platform"); +#endif +} + +static void +accumulate_host_timing_secret(struct sshbuf *server_cfg, + struct sshkey *key) +{ + static struct ssh_digest_ctx *ctx; + u_char *hash; + size_t len; + struct sshbuf *buf; + int r; + + if (ctx == NULL && (ctx = ssh_digest_start(SSH_DIGEST_SHA512)) == NULL) + fatal_f("ssh_digest_start"); + if (key == NULL) { /* finalize */ + /* add server config in case we are using agent for host keys */ + if (ssh_digest_update(ctx, sshbuf_ptr(server_cfg), + sshbuf_len(server_cfg)) != 0) + fatal_f("ssh_digest_update"); + len = ssh_digest_bytes(SSH_DIGEST_SHA512); + hash = xmalloc(len); + if (ssh_digest_final(ctx, hash, len) != 0) + fatal_f("ssh_digest_final"); + options.timing_secret = PEEK_U64(hash); + freezero(hash, len); + ssh_digest_free(ctx); + ctx = NULL; + return; + } + if ((buf = sshbuf_new()) == NULL) + fatal_f("could not allocate buffer"); + if ((r = sshkey_private_serialize(key, buf)) != 0) + fatal_fr(r, "decode key"); + if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0) + fatal_f("ssh_digest_update"); + sshbuf_reset(buf); + sshbuf_free(buf); +} + +static char * +prepare_proctitle(int ac, char **av) +{ + char *ret = NULL; + int i; + + for (i = 0; i < ac; i++) + xextendf(&ret, " ", "%s", av[i]); + return ret; +} + +/* + * Main program for the daemon. + */ +int +main(int ac, char **av) +{ + struct ssh *ssh = NULL; + extern char *optarg; + extern int optind; + int r, opt, on = 1, already_daemon, remote_port; + int sock_in = -1, sock_out = -1, newsock = -1; + const char *remote_ip, *rdomain; + char *fp, *line, *laddr, *logfile = NULL; + int config_s[2] = { -1 , -1 }; + u_int i, j; + u_int64_t ibytes, obytes; + mode_t new_umask; + struct sshkey *key; + struct sshkey *pubkey; + int keytype; + Authctxt *authctxt; + struct connection_info *connection_info = NULL; + +#ifdef HAVE_SECUREWARE + (void)set_auth_parameters(ac, av); +#endif + __progname = ssh_get_progname(av[0]); + + /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ + saved_argc = ac; + rexec_argc = ac; + saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); + for (i = 0; (int)i < ac; i++) + saved_argv[i] = xstrdup(av[i]); + saved_argv[i] = NULL; + +#ifndef HAVE_SETPROCTITLE + /* Prepare for later setproctitle emulation */ + compat_init_setproctitle(ac, av); + av = saved_argv; +#endif + + if (geteuid() == 0 && setgroups(0, NULL) == -1) + debug("setgroups(): %.200s", strerror(errno)); + + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); + + seed_rng(); + + /* Initialize configuration options to their default values. */ + initialize_server_options(&options); + + /* Parse command-line arguments. */ + while ((opt = getopt(ac, av, + "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) { + switch (opt) { + case '4': + options.address_family = AF_INET; + break; + case '6': + options.address_family = AF_INET6; + break; + case 'f': + config_file_name = optarg; + break; + case 'c': + servconf_add_hostcert("[command-line]", 0, + &options, optarg); + break; + case 'd': + if (debug_flag == 0) { + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG1; + } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) + options.log_level++; + break; + case 'D': + no_daemon_flag = 1; + break; + case 'E': + logfile = optarg; + /* FALLTHROUGH */ + case 'e': + log_stderr = 1; + break; + case 'i': + inetd_flag = 1; + break; + case 'r': + rexec_flag = 0; + break; + case 'R': + rexeced_flag = 1; + inetd_flag = 1; + break; + case 'Q': + /* ignored */ + break; + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + case 'b': + /* protocol 1, ignored */ + break; + case 'p': + options.ports_from_cmdline = 1; + if (options.num_ports >= MAX_PORTS) { + fprintf(stderr, "too many ports.\n"); + exit(1); + } + options.ports[options.num_ports++] = a2port(optarg); + if (options.ports[options.num_ports-1] <= 0) { + fprintf(stderr, "Bad port number.\n"); + exit(1); + } + break; + case 'g': + if ((options.login_grace_time = convtime(optarg)) == -1) { + fprintf(stderr, "Invalid login grace time.\n"); + exit(1); + } + break; + case 'k': + /* protocol 1, ignored */ + break; + case 'h': + servconf_add_hostkey("[command-line]", 0, + &options, optarg, 1); + break; + case 't': + test_flag = 1; + break; + case 'T': + test_flag = 2; + break; + case 'C': + connection_info = get_connection_info(ssh, 0, 0); + if (parse_server_match_testspec(connection_info, + optarg) == -1) + exit(1); + break; + case 'u': + utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL); + if (utmp_len > HOST_NAME_MAX+1) { + fprintf(stderr, "Invalid utmp length.\n"); + exit(1); + } + break; + case 'o': + line = xstrdup(optarg); + if (process_server_config_line(&options, line, + "command-line", 0, NULL, NULL, &includes) != 0) + exit(1); + free(line); + break; + case '?': + default: + usage(); + break; + } + } + if (rexeced_flag || inetd_flag) + rexec_flag = 0; + if (!test_flag && rexec_flag && !path_absolute(av[0])) + fatal("sshd re-exec requires execution with an absolute path"); + if (rexeced_flag) + closefrom(REEXEC_MIN_FREE_FD); + else + closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); + + /* If requested, redirect the logs to the specified logfile. */ + if (logfile != NULL) + log_redirect_stderr_to(logfile); + /* + * Force logging to stderr until we have loaded the private host + * key (unless started from inetd) + */ + log_init(__progname, + options.log_level == SYSLOG_LEVEL_NOT_SET ? + SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == SYSLOG_FACILITY_NOT_SET ? + SYSLOG_FACILITY_AUTH : options.log_facility, + log_stderr || !inetd_flag || debug_flag); + + /* + * Unset KRB5CCNAME, otherwise the user's session may inherit it from + * root's environment + */ + if (getenv("KRB5CCNAME") != NULL) + (void) unsetenv("KRB5CCNAME"); + + sensitive_data.have_ssh2_key = 0; + + /* + * If we're not doing an extended test do not silently ignore connection + * test params. + */ + if (test_flag < 2 && connection_info != NULL) + fatal("Config test connection parameter (-C) provided without " + "test mode (-T)"); + + /* Fetch our configuration */ + if ((cfg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (rexeced_flag) { + setproctitle("%s", "[rexeced]"); + recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg); + if (!debug_flag) { + startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); + close(REEXEC_STARTUP_PIPE_FD); + /* + * Signal parent that this child is at a point where + * they can go away if they have a SIGHUP pending. + */ + (void)atomicio(vwrite, startup_pipe, "\0", 1); + } + } else if (strcasecmp(config_file_name, "none") != 0) + load_server_config(config_file_name, cfg); + + parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, + cfg, &includes, NULL, rexeced_flag); + +#ifdef WITH_OPENSSL + if (options.moduli_file != NULL) + dh_set_moduli_file(options.moduli_file); +#endif + + /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + + /* Check that options are sensible */ + if (options.authorized_keys_command_user == NULL && + (options.authorized_keys_command != NULL && + strcasecmp(options.authorized_keys_command, "none") != 0)) + fatal("AuthorizedKeysCommand set without " + "AuthorizedKeysCommandUser"); + if (options.authorized_principals_command_user == NULL && + (options.authorized_principals_command != NULL && + strcasecmp(options.authorized_principals_command, "none") != 0)) + fatal("AuthorizedPrincipalsCommand set without " + "AuthorizedPrincipalsCommandUser"); + + /* + * Check whether there is any path through configured auth methods. + * Unfortunately it is not possible to verify this generally before + * daemonisation in the presence of Match block, but this catches + * and warns for trivial misconfigurations that could break login. + */ + if (options.num_auth_methods != 0) { + for (i = 0; i < options.num_auth_methods; i++) { + if (auth2_methods_valid(options.auth_methods[i], + 1) == 0) + break; + } + if (i >= options.num_auth_methods) + fatal("AuthenticationMethods cannot be satisfied by " + "enabled authentication methods"); + } + + /* Check that there are no remaining arguments. */ + if (optind < ac) { + fprintf(stderr, "Extra argument %s.\n", av[optind]); + exit(1); + } + + debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION); + + /* Store privilege separation user for later use if required. */ + privsep_chroot = use_privsep && (getuid() == 0 || geteuid() == 0); + if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { + if (privsep_chroot || options.kerberos_authentication) + fatal("Privilege separation user %s does not exist", + SSH_PRIVSEP_USER); + } else { + privsep_pw = pwcopy(privsep_pw); + freezero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd)); + privsep_pw->pw_passwd = xstrdup("*"); + } +#if !defined(ANDROID) + endpwent(); +#endif + + /* load host keys */ + sensitive_data.host_keys = xcalloc(options.num_host_key_files, + sizeof(struct sshkey *)); + sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files, + sizeof(struct sshkey *)); + + if (options.host_key_agent) { + if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME)) + setenv(SSH_AUTHSOCKET_ENV_NAME, + options.host_key_agent, 1); + if ((r = ssh_get_authentication_socket(NULL)) == 0) + have_agent = 1; + else + error_r(r, "Could not connect to agent \"%s\"", + options.host_key_agent); + } + + for (i = 0; i < options.num_host_key_files; i++) { + int ll = options.host_key_file_userprovided[i] ? + SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_DEBUG1; + + if (options.host_key_files[i] == NULL) + continue; + if ((r = sshkey_load_private(options.host_key_files[i], "", + &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) + do_log2_r(r, ll, "Unable to load host key \"%s\"", + options.host_key_files[i]); + if (sshkey_is_sk(key) && + key->sk_flags & SSH_SK_USER_PRESENCE_REQD) { + debug("host key %s requires user presence, ignoring", + options.host_key_files[i]); + key->sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; + } + if (r == 0 && key != NULL && + (r = sshkey_shield_private(key)) != 0) { + do_log2_r(r, ll, "Unable to shield host key \"%s\"", + options.host_key_files[i]); + sshkey_free(key); + key = NULL; + } + if ((r = sshkey_load_public(options.host_key_files[i], + &pubkey, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) + do_log2_r(r, ll, "Unable to load host key \"%s\"", + options.host_key_files[i]); + if (pubkey != NULL && key != NULL) { + if (!sshkey_equal(pubkey, key)) { + error("Public key for %s does not match " + "private key", options.host_key_files[i]); + sshkey_free(pubkey); + pubkey = NULL; + } + } + if (pubkey == NULL && key != NULL) { + if ((r = sshkey_from_private(key, &pubkey)) != 0) + fatal_r(r, "Could not demote key: \"%s\"", + options.host_key_files[i]); + } + sensitive_data.host_keys[i] = key; + sensitive_data.host_pubkeys[i] = pubkey; + + if (key == NULL && pubkey != NULL && have_agent) { + debug("will rely on agent for hostkey %s", + options.host_key_files[i]); + keytype = pubkey->type; + } else if (key != NULL) { + keytype = key->type; + accumulate_host_timing_secret(cfg, key); + } else { + do_log2(ll, "Unable to load host key: %s", + options.host_key_files[i]); + sensitive_data.host_keys[i] = NULL; + sensitive_data.host_pubkeys[i] = NULL; + continue; + } + + switch (keytype) { + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: + case KEY_ED25519: + case KEY_ECDSA_SK: + case KEY_ED25519_SK: + case KEY_XMSS: + if (have_agent || key != NULL) + sensitive_data.have_ssh2_key = 1; + break; + } + if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal("sshkey_fingerprint failed"); + debug("%s host key #%d: %s %s", + key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); + free(fp); + } + accumulate_host_timing_secret(cfg, NULL); + if (!sensitive_data.have_ssh2_key) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } + + /* + * Load certificates. They are stored in an array at identical + * indices to the public keys that they relate to. + */ + sensitive_data.host_certificates = xcalloc(options.num_host_key_files, + sizeof(struct sshkey *)); + for (i = 0; i < options.num_host_key_files; i++) + sensitive_data.host_certificates[i] = NULL; + + for (i = 0; i < options.num_host_cert_files; i++) { + if (options.host_cert_files[i] == NULL) + continue; + if ((r = sshkey_load_public(options.host_cert_files[i], + &key, NULL)) != 0) { + error_r(r, "Could not load host certificate \"%s\"", + options.host_cert_files[i]); + continue; + } + if (!sshkey_is_cert(key)) { + error("Certificate file is not a certificate: %s", + options.host_cert_files[i]); + sshkey_free(key); + continue; + } + /* Find matching private key */ + for (j = 0; j < options.num_host_key_files; j++) { + if (sshkey_equal_public(key, + sensitive_data.host_pubkeys[j])) { + sensitive_data.host_certificates[j] = key; + break; + } + } + if (j >= options.num_host_key_files) { + error("No matching private key for certificate: %s", + options.host_cert_files[i]); + sshkey_free(key); + continue; + } + sensitive_data.host_certificates[j] = key; + debug("host certificate: #%u type %d %s", j, key->type, + sshkey_type(key)); + } + + if (privsep_chroot) { + struct stat st; + + if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || + (S_ISDIR(st.st_mode) == 0)) + fatal("Missing privilege separation directory: %s", + _PATH_PRIVSEP_CHROOT_DIR); + +#ifdef HAVE_CYGWIN + if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && + (st.st_uid != getuid () || + (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) +#else + if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) +#endif + fatal("%s must be owned by root and not group or " + "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); + } + + if (test_flag > 1) { + /* + * If no connection info was provided by -C then use + * use a blank one that will cause no predicate to match. + */ + if (connection_info == NULL) + connection_info = get_connection_info(ssh, 0, 0); + connection_info->test = 1; + parse_server_match_config(&options, &includes, connection_info); + dump_config(&options); + } + + /* Configuration looks good, so exit if in test mode. */ + if (test_flag) + exit(0); + + /* + * Clear out any supplemental groups we may have inherited. This + * prevents inadvertent creation of files with bad modes (in the + * portable version at least, it's certainly possible for PAM + * to create a file, and we can't control the code in every + * module which might be used). + */ + if (setgroups(0, NULL) < 0) + debug("setgroups() failed: %.200s", strerror(errno)); + + if (rexec_flag) { + if (rexec_argc < 0) + fatal("rexec_argc %d < 0", rexec_argc); + rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); + for (i = 0; i < (u_int)rexec_argc; i++) { + debug("rexec_argv[%d]='%s'", i, saved_argv[i]); + rexec_argv[i] = saved_argv[i]; + } + rexec_argv[rexec_argc] = "-R"; + rexec_argv[rexec_argc + 1] = NULL; + } + listener_proctitle = prepare_proctitle(ac, av); + + /* Ensure that umask disallows at least group and world write */ + new_umask = umask(0077) | 0022; + (void) umask(new_umask); + + /* Initialize the log (it is reinitialized below in case we forked). */ + if (debug_flag && (!inetd_flag || rexeced_flag)) + log_stderr = 1; + log_init(__progname, options.log_level, + options.log_facility, log_stderr); + for (i = 0; i < options.num_log_verbose; i++) + log_verbose_add(options.log_verbose[i]); + + /* + * If not in debugging mode, not started from inetd and not already + * daemonized (eg re-exec via SIGHUP), disconnect from the controlling + * terminal, and fork. The original process exits. + */ + already_daemon = daemonized(); + if (!(debug_flag || inetd_flag || no_daemon_flag || already_daemon)) { + + if (daemon(0, 0) == -1) + fatal("daemon() failed: %.200s", strerror(errno)); + + disconnect_controlling_tty(); + } + /* Reinitialize the log (because of the fork above). */ + log_init(__progname, options.log_level, options.log_facility, log_stderr); + + /* + * Chdir to the root directory so that the current disk can be + * unmounted if desired. + */ + if (chdir("/") == -1) + error("chdir(\"/\"): %s", strerror(errno)); + + /* ignore SIGPIPE */ + ssh_signal(SIGPIPE, SIG_IGN); + + /* Get a connection, either from inetd or a listening TCP socket */ + if (inetd_flag) { + server_accept_inetd(&sock_in, &sock_out); + } else { + platform_pre_listen(); + server_listen(); + + ssh_signal(SIGHUP, sighup_handler); + ssh_signal(SIGCHLD, main_sigchld_handler); + ssh_signal(SIGTERM, sigterm_handler); + ssh_signal(SIGQUIT, sigterm_handler); + + /* + * Write out the pid file after the sigterm handler + * is setup and the listen sockets are bound + */ + if (options.pid_file != NULL && !debug_flag) { + FILE *f = fopen(options.pid_file, "w"); + + if (f == NULL) { + error("Couldn't create pid file \"%s\": %s", + options.pid_file, strerror(errno)); + } else { + fprintf(f, "%ld\n", (long) getpid()); + fclose(f); + } + } + + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); + } + + /* This is the child processing a new connection. */ + setproctitle("%s", "[accepted]"); + + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. We don't + * want the child to be able to affect the parent. + */ + if (!debug_flag && !inetd_flag && setsid() == -1) + error("setsid: %.100s", strerror(errno)); + + if (rexec_flag) { + debug("rexec start in %d out %d newsock %d pipe %d sock %d", + sock_in, sock_out, newsock, startup_pipe, config_s[0]); + dup2(newsock, STDIN_FILENO); + dup2(STDIN_FILENO, STDOUT_FILENO); + if (startup_pipe == -1) + close(REEXEC_STARTUP_PIPE_FD); + else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) { + dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD); + close(startup_pipe); + startup_pipe = REEXEC_STARTUP_PIPE_FD; + } + + dup2(config_s[1], REEXEC_CONFIG_PASS_FD); + close(config_s[1]); + + ssh_signal(SIGHUP, SIG_IGN); /* avoid reset to SIG_DFL */ + execv(rexec_argv[0], rexec_argv); + + /* Reexec has failed, fall back and continue */ + error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); + recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); + log_init(__progname, options.log_level, + options.log_facility, log_stderr); + + /* Clean up fds */ + close(REEXEC_CONFIG_PASS_FD); + newsock = sock_out = sock_in = dup(STDIN_FILENO); + if (stdfd_devnull(1, 1, 0) == -1) + error_f("stdfd_devnull failed"); + debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", + sock_in, sock_out, newsock, startup_pipe, config_s[0]); + } + + /* Executed child processes don't need these. */ + fcntl(sock_out, F_SETFD, FD_CLOEXEC); + fcntl(sock_in, F_SETFD, FD_CLOEXEC); + + /* We will not restart on SIGHUP since it no longer makes sense. */ + ssh_signal(SIGALRM, SIG_DFL); + ssh_signal(SIGHUP, SIG_DFL); + ssh_signal(SIGTERM, SIG_DFL); + ssh_signal(SIGQUIT, SIG_DFL); + ssh_signal(SIGCHLD, SIG_DFL); + ssh_signal(SIGINT, SIG_DFL); + + /* + * Register our connection. This turns encryption off because we do + * not have a key. + */ + if ((ssh = ssh_packet_set_connection(NULL, sock_in, sock_out)) == NULL) + fatal("Unable to create connection"); + the_active_state = ssh; + ssh_packet_set_server(ssh); + + check_ip_options(ssh); + + /* Prepare the channels layer */ + channel_init_channels(ssh); + channel_set_af(ssh, options.address_family); + process_permitopen(ssh, &options); + + /* Set SO_KEEPALIVE if requested. */ + if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) && + setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); + + if ((remote_port = ssh_remote_port(ssh)) < 0) { + debug("ssh_remote_port failed"); + cleanup_exit(255); + } + + if (options.routing_domain != NULL) + set_process_rdomain(ssh, options.routing_domain); + + /* + * The rest of the code depends on the fact that + * ssh_remote_ipaddr() caches the remote ip, even if + * the socket goes away. + */ + remote_ip = ssh_remote_ipaddr(ssh); + +#ifdef SSH_AUDIT_EVENTS + audit_connection_from(remote_ip, remote_port); +#endif + + rdomain = ssh_packet_rdomain_in(ssh); + + /* Log the connection. */ + laddr = get_local_ipaddr(sock_in); + verbose("Connection from %s port %d on %s port %d%s%s%s", + remote_ip, remote_port, laddr, ssh_local_port(ssh), + rdomain == NULL ? "" : " rdomain \"", + rdomain == NULL ? "" : rdomain, + rdomain == NULL ? "" : "\""); + free(laddr); + + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is + * cleared after successful authentication. A limit of zero + * indicates no limit. Note that we don't set the alarm in debugging + * mode; it is just annoying to have the server exit just when you + * are about to discover the bug. + */ + ssh_signal(SIGALRM, grace_alarm_handler); + if (!debug_flag) + alarm(options.login_grace_time); + + if ((r = kex_exchange_identification(ssh, -1, + options.version_addendum)) != 0) + sshpkt_fatal(ssh, r, "banner exchange"); + + ssh_packet_set_nonblocking(ssh); + + /* allocate authentication context */ + authctxt = xcalloc(1, sizeof(*authctxt)); + ssh->authctxt = authctxt; + + authctxt->loginmsg = loginmsg; + + /* XXX global for cleanup, access from other modules */ + the_authctxt = authctxt; + + /* Set default key authentication options */ + if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL) + fatal("allocation failed"); + + /* prepare buffer to collect messages to display to user after login */ + if ((loginmsg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + auth_debug_reset(); + + if (use_privsep) { + if (privsep_preauth(ssh) == 1) + goto authenticated; + } else if (have_agent) { + if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { + error_r(r, "Unable to get agent socket"); + have_agent = 0; + } + } + + /* perform the key exchange */ + /* authenticate user and start session */ + do_ssh2_kex(ssh); + do_authentication2(ssh); + + /* + * If we use privilege separation, the unprivileged child transfers + * the current keystate and exits + */ + if (use_privsep) { + mm_send_keystate(ssh, pmonitor); + ssh_packet_clear_keys(ssh); + exit(0); + } + + authenticated: + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + ssh_signal(SIGALRM, SIG_DFL); + authctxt->authenticated = 1; + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } + +#ifdef SSH_AUDIT_EVENTS + audit_event(ssh, SSH_AUTH_SUCCESS); +#endif + +#ifdef GSSAPI + if (options.gss_authentication) { + temporarily_use_uid(authctxt->pw); + ssh_gssapi_storecreds(); + restore_uid(); + } +#endif +#ifdef USE_PAM + if (options.use_pam) { + do_pam_setcred(1); + do_pam_session(ssh); + } +#endif + + /* + * In privilege separation, we fork another child and prepare + * file descriptor passing. + */ + if (use_privsep) { + privsep_postauth(ssh, authctxt); + /* the monitor process [priv] will not return */ + } + + ssh_packet_set_timeout(ssh, options.client_alive_interval, + options.client_alive_count_max); + + /* Try to send all our hostkeys to the client */ + notify_hostkeys(ssh); + + /* Start session. */ + do_authenticated(ssh, authctxt); + + /* The connection has been terminated. */ + ssh_packet_get_bytes(ssh, &ibytes, &obytes); + verbose("Transferred: sent %llu, received %llu bytes", + (unsigned long long)obytes, (unsigned long long)ibytes); + + verbose("Closing connection to %.500s port %d", remote_ip, remote_port); + +#ifdef USE_PAM + if (options.use_pam) + finish_pam(); +#endif /* USE_PAM */ + +#ifdef SSH_AUDIT_EVENTS + PRIVSEP(audit_event(ssh, SSH_CONNECTION_CLOSE)); +#endif + + ssh_packet_close(ssh); + + if (use_privsep) + mm_terminate(); + + exit(0); +} + +int +sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey, + struct sshkey *pubkey, u_char **signature, size_t *slenp, + const u_char *data, size_t dlen, const char *alg) +{ + int r; + + if (use_privsep) { + if (privkey) { + if (mm_sshkey_sign(ssh, privkey, signature, slenp, + data, dlen, alg, options.sk_provider, NULL, + ssh->compat) < 0) + fatal_f("privkey sign failed"); + } else { + if (mm_sshkey_sign(ssh, pubkey, signature, slenp, + data, dlen, alg, options.sk_provider, NULL, + ssh->compat) < 0) + fatal_f("pubkey sign failed"); + } + } else { + if (privkey) { + if (sshkey_sign(privkey, signature, slenp, data, dlen, + alg, options.sk_provider, NULL, ssh->compat) < 0) + fatal_f("privkey sign failed"); + } else { + if ((r = ssh_agent_sign(auth_sock, pubkey, + signature, slenp, data, dlen, alg, + ssh->compat)) != 0) { + fatal_fr(r, "agent sign failed"); + } + } + } + return 0; +} + +/* SSH2 key exchange */ +static void +do_ssh2_kex(struct ssh *ssh) +{ + char *myproposal[PROPOSAL_MAX] = { KEX_SERVER }; + struct kex *kex; + int r; + + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(ssh, + options.kex_algorithms); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(ssh, + options.ciphers); + myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(ssh, + options.ciphers); + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; + + if (options.compression == COMP_NONE) { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = + myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; + } + + if (options.rekey_limit || options.rekey_interval) + ssh_packet_set_rekey_limits(ssh, options.rekey_limit, + options.rekey_interval); + + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + ssh, list_hostkey_types()); + + /* start key exchange */ + if ((r = kex_setup(ssh, myproposal)) != 0) + fatal_r(r, "kex_setup"); + kex = ssh->kex; +#ifdef WITH_OPENSSL + kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; + kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; + kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; + kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; + kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; +# ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kex_gen_server; +# endif +#endif + kex->kex[KEX_C25519_SHA256] = kex_gen_server; + kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; + kex->host_key_index=&get_hostkey_index; + kex->sign = sshd_hostkey_sign; + + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &kex->done); + +#ifdef DEBUG_KEXDH + /* send 1st encrypted/maced/compressed message */ + packet_start(SSH2_MSG_IGNORE); + packet_put_cstring("markus"); + packet_send(); + packet_write_wait(); +#endif + debug("KEX done"); +} + +/* server specific fatal cleanup */ +void +cleanup_exit(int i) +{ + if (the_active_state != NULL && the_authctxt != NULL) { + do_cleanup(the_active_state, the_authctxt); + if (use_privsep && privsep_is_preauth && + pmonitor != NULL && pmonitor->m_pid > 1) { + debug("Killing privsep child %d", pmonitor->m_pid); + if (kill(pmonitor->m_pid, SIGKILL) != 0 && + errno != ESRCH) { + error_f("kill(%d): %s", pmonitor->m_pid, + strerror(errno)); + } + } + } +#ifdef SSH_AUDIT_EVENTS + /* done after do_cleanup so it can cancel the PAM auth 'thread' */ + if (the_active_state != NULL && (!use_privsep || mm_is_monitor())) + audit_event(the_active_state, SSH_CONNECTION_ABANDON); +#endif + _exit(i); +} diff --git a/aosp/external/openssh/sshd_config b/aosp/external/openssh/sshd_config new file mode 100755 index 0000000000000000000000000000000000000000..a69069fa772ba93c1f540294f9de6d3fe7c0f69c --- /dev/null +++ b/aosp/external/openssh/sshd_config @@ -0,0 +1,116 @@ +# $OpenBSD: sshd_config,v 1.104 2021/07/02 05:11:21 dtucker Exp $ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +#HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_ecdsa_key +#HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +PermitRootLogin without-password +StrictModes no +#MaxAuthTries 6 +#MaxSessions 10 + +#PubkeyAuthentication yes + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile /data/ssh/authorized_keys + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +PasswordAuthentication yes +PermitEmptyPasswords yes + +# Change to no to disable s/key passwords +#KbdInteractiveAuthentication yes + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the KbdInteractiveAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via KbdInteractiveAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and KbdInteractiveAuthentication to 'no'. +#UsePAM no + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +#X11Forwarding no +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +#PrintMotd yes +#PrintLastLog yes +#TCPKeepAlive yes +#PermitUserEnvironment no +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#UseDNS no +#PidFile /var/run/sshd.pid +#MaxStartups 10:30:100 +#PermitTunnel no +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# override default of no subsystems +Subsystem sftp /usr/libexec/sftp-server + +# Example of overriding settings on a per-user basis +#Match User anoncvs +# X11Forwarding no +# AllowTcpForwarding no +# PermitTTY no +# ForceCommand cvs server diff --git a/aosp/external/openssh/sshd_config.5 b/aosp/external/openssh/sshd_config.5 new file mode 100644 index 0000000000000000000000000000000000000000..3a4ffab7c5023a5e284c12a99ebf172af0c00868 --- /dev/null +++ b/aosp/external/openssh/sshd_config.5 @@ -0,0 +1,1974 @@ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: sshd_config.5,v 1.340 2022/03/31 17:58:44 naddy Exp $ +.Dd $Mdocdate: March 31 2022 $ +.Dt SSHD_CONFIG 5 +.Os +.Sh NAME +.Nm sshd_config +.Nd OpenSSH daemon configuration file +.Sh DESCRIPTION +.Xr sshd 8 +reads configuration data from +.Pa /etc/ssh/sshd_config +(or the file specified with +.Fl f +on the command line). +The file contains keyword-argument pairs, one per line. +For each keyword, the first obtained value will be used. +Lines starting with +.Ql # +and empty lines are interpreted as comments. +Arguments may optionally be enclosed in double quotes +.Pq \&" +in order to represent arguments containing spaces. +.Pp +The possible +keywords and their meanings are as follows (note that +keywords are case-insensitive and arguments are case-sensitive): +.Bl -tag -width Ds +.It Cm AcceptEnv +Specifies what environment variables sent by the client will be copied into +the session's +.Xr environ 7 . +See +.Cm SendEnv +and +.Cm SetEnv +in +.Xr ssh_config 5 +for how to configure the client. +The +.Ev TERM +environment variable is always accepted whenever the client +requests a pseudo-terminal as it is required by the protocol. +Variables are specified by name, which may contain the wildcard characters +.Ql * +and +.Ql \&? . +Multiple environment variables may be separated by whitespace or spread +across multiple +.Cm AcceptEnv +directives. +Be warned that some environment variables could be used to bypass restricted +user environments. +For this reason, care should be taken in the use of this directive. +The default is not to accept any environment variables. +.It Cm AddressFamily +Specifies which address family should be used by +.Xr sshd 8 . +Valid arguments are +.Cm any +(the default), +.Cm inet +(use IPv4 only), or +.Cm inet6 +(use IPv6 only). +.It Cm AllowAgentForwarding +Specifies whether +.Xr ssh-agent 1 +forwarding is permitted. +The default is +.Cm yes . +Note that disabling agent forwarding does not improve security +unless users are also denied shell access, as they can always install +their own forwarders. +.It Cm AllowGroups +This keyword can be followed by a list of group name patterns, separated +by spaces. +If specified, login is allowed only for users whose primary +group or supplementary group list matches one of the patterns. +Only group names are valid; a numerical group ID is not recognized. +By default, login is allowed for all groups. +The allow/deny groups directives are processed in the following order: +.Cm DenyGroups , +.Cm AllowGroups . +.Pp +See PATTERNS in +.Xr ssh_config 5 +for more information on patterns. +.It Cm AllowStreamLocalForwarding +Specifies whether StreamLocal (Unix-domain socket) forwarding is permitted. +The available options are +.Cm yes +(the default) +or +.Cm all +to allow StreamLocal forwarding, +.Cm no +to prevent all StreamLocal forwarding, +.Cm local +to allow local (from the perspective of +.Xr ssh 1 ) +forwarding only or +.Cm remote +to allow remote forwarding only. +Note that disabling StreamLocal forwarding does not improve security unless +users are also denied shell access, as they can always install their +own forwarders. +.It Cm AllowTcpForwarding +Specifies whether TCP forwarding is permitted. +The available options are +.Cm yes +(the default) +or +.Cm all +to allow TCP forwarding, +.Cm no +to prevent all TCP forwarding, +.Cm local +to allow local (from the perspective of +.Xr ssh 1 ) +forwarding only or +.Cm remote +to allow remote forwarding only. +Note that disabling TCP forwarding does not improve security unless +users are also denied shell access, as they can always install their +own forwarders. +.It Cm AllowUsers +This keyword can be followed by a list of user name patterns, separated +by spaces. +If specified, login is allowed only for user names that +match one of the patterns. +Only user names are valid; a numerical user ID is not recognized. +By default, login is allowed for all users. +If the pattern takes the form USER@HOST then USER and HOST +are separately checked, restricting logins to particular +users from particular hosts. +HOST criteria may additionally contain addresses to match in CIDR +address/masklen format. +The allow/deny users directives are processed in the following order: +.Cm DenyUsers , +.Cm AllowUsers . +.Pp +See PATTERNS in +.Xr ssh_config 5 +for more information on patterns. +.It Cm AuthenticationMethods +Specifies the authentication methods that must be successfully completed +for a user to be granted access. +This option must be followed by one or more lists of comma-separated +authentication method names, or by the single string +.Cm any +to indicate the default behaviour of accepting any single authentication +method. +If the default is overridden, then successful authentication requires +completion of every method in at least one of these lists. +.Pp +For example, +.Qq publickey,password publickey,keyboard-interactive +would require the user to complete public key authentication, followed by +either password or keyboard interactive authentication. +Only methods that are next in one or more lists are offered at each stage, +so for this example it would not be possible to attempt password or +keyboard-interactive authentication before public key. +.Pp +For keyboard interactive authentication it is also possible to +restrict authentication to a specific device by appending a +colon followed by the device identifier +.Cm bsdauth +or +.Cm pam . +depending on the server configuration. +For example, +.Qq keyboard-interactive:bsdauth +would restrict keyboard interactive authentication to the +.Cm bsdauth +device. +.Pp +If the publickey method is listed more than once, +.Xr sshd 8 +verifies that keys that have been used successfully are not reused for +subsequent authentications. +For example, +.Qq publickey,publickey +requires successful authentication using two different public keys. +.Pp +Note that each authentication method listed should also be explicitly enabled +in the configuration. +.Pp +The available authentication methods are: +.Qq gssapi-with-mic , +.Qq hostbased , +.Qq keyboard-interactive , +.Qq none +(used for access to password-less accounts when +.Cm PermitEmptyPasswords +is enabled), +.Qq password +and +.Qq publickey . +.It Cm AuthorizedKeysCommand +Specifies a program to be used to look up the user's public keys. +The program must be owned by root, not writable by group or others and +specified by an absolute path. +Arguments to +.Cm AuthorizedKeysCommand +accept the tokens described in the +.Sx TOKENS +section. +If no arguments are specified then the username of the target user is used. +.Pp +The program should produce on standard output zero or +more lines of authorized_keys output (see +.Sx AUTHORIZED_KEYS +in +.Xr sshd 8 ) . +.Cm AuthorizedKeysCommand +is tried after the usual +.Cm AuthorizedKeysFile +files and will not be executed if a matching key is found there. +By default, no +.Cm AuthorizedKeysCommand +is run. +.It Cm AuthorizedKeysCommandUser +Specifies the user under whose account the +.Cm AuthorizedKeysCommand +is run. +It is recommended to use a dedicated user that has no other role on the host +than running authorized keys commands. +If +.Cm AuthorizedKeysCommand +is specified but +.Cm AuthorizedKeysCommandUser +is not, then +.Xr sshd 8 +will refuse to start. +.It Cm AuthorizedKeysFile +Specifies the file that contains the public keys used for user authentication. +The format is described in the AUTHORIZED_KEYS FILE FORMAT section of +.Xr sshd 8 . +Arguments to +.Cm AuthorizedKeysFile +accept the tokens described in the +.Sx TOKENS +section. +After expansion, +.Cm AuthorizedKeysFile +is taken to be an absolute path or one relative to the user's home +directory. +Multiple files may be listed, separated by whitespace. +Alternately this option may be set to +.Cm none +to skip checking for user keys in files. +The default is +.Qq .ssh/authorized_keys .ssh/authorized_keys2 . +.It Cm AuthorizedPrincipalsCommand +Specifies a program to be used to generate the list of allowed +certificate principals as per +.Cm AuthorizedPrincipalsFile . +The program must be owned by root, not writable by group or others and +specified by an absolute path. +Arguments to +.Cm AuthorizedPrincipalsCommand +accept the tokens described in the +.Sx TOKENS +section. +If no arguments are specified then the username of the target user is used. +.Pp +The program should produce on standard output zero or +more lines of +.Cm AuthorizedPrincipalsFile +output. +If either +.Cm AuthorizedPrincipalsCommand +or +.Cm AuthorizedPrincipalsFile +is specified, then certificates offered by the client for authentication +must contain a principal that is listed. +By default, no +.Cm AuthorizedPrincipalsCommand +is run. +.It Cm AuthorizedPrincipalsCommandUser +Specifies the user under whose account the +.Cm AuthorizedPrincipalsCommand +is run. +It is recommended to use a dedicated user that has no other role on the host +than running authorized principals commands. +If +.Cm AuthorizedPrincipalsCommand +is specified but +.Cm AuthorizedPrincipalsCommandUser +is not, then +.Xr sshd 8 +will refuse to start. +.It Cm AuthorizedPrincipalsFile +Specifies a file that lists principal names that are accepted for +certificate authentication. +When using certificates signed by a key listed in +.Cm TrustedUserCAKeys , +this file lists names, one of which must appear in the certificate for it +to be accepted for authentication. +Names are listed one per line preceded by key options (as described in +.Sx AUTHORIZED_KEYS FILE FORMAT +in +.Xr sshd 8 ) . +Empty lines and comments starting with +.Ql # +are ignored. +.Pp +Arguments to +.Cm AuthorizedPrincipalsFile +accept the tokens described in the +.Sx TOKENS +section. +After expansion, +.Cm AuthorizedPrincipalsFile +is taken to be an absolute path or one relative to the user's home directory. +The default is +.Cm none , +i.e. not to use a principals file \(en in this case, the username +of the user must appear in a certificate's principals list for it to be +accepted. +.Pp +Note that +.Cm AuthorizedPrincipalsFile +is only used when authentication proceeds using a CA listed in +.Cm TrustedUserCAKeys +and is not consulted for certification authorities trusted via +.Pa ~/.ssh/authorized_keys , +though the +.Cm principals= +key option offers a similar facility (see +.Xr sshd 8 +for details). +.It Cm Banner +The contents of the specified file are sent to the remote user before +authentication is allowed. +If the argument is +.Cm none +then no banner is displayed. +By default, no banner is displayed. +.It Cm CASignatureAlgorithms +Specifies which algorithms are allowed for signing of certificates +by certificate authorities (CAs). +The default is: +.Bd -literal -offset indent +ssh-ed25519,ecdsa-sha2-nistp256, +ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +If the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +.Pp +Certificates signed using other algorithms will not be accepted for +public key or host-based authentication. +.It Cm ChrootDirectory +Specifies the pathname of a directory to +.Xr chroot 2 +to after authentication. +At session startup +.Xr sshd 8 +checks that all components of the pathname are root-owned directories +which are not writable by any other user or group. +After the chroot, +.Xr sshd 8 +changes the working directory to the user's home directory. +Arguments to +.Cm ChrootDirectory +accept the tokens described in the +.Sx TOKENS +section. +.Pp +The +.Cm ChrootDirectory +must contain the necessary files and directories to support the +user's session. +For an interactive session this requires at least a shell, typically +.Xr sh 1 , +and basic +.Pa /dev +nodes such as +.Xr null 4 , +.Xr zero 4 , +.Xr stdin 4 , +.Xr stdout 4 , +.Xr stderr 4 , +and +.Xr tty 4 +devices. +For file transfer sessions using SFTP +no additional configuration of the environment is necessary if the in-process +sftp-server is used, +though sessions which use logging may require +.Pa /dev/log +inside the chroot directory on some operating systems (see +.Xr sftp-server 8 +for details). +.Pp +For safety, it is very important that the directory hierarchy be +prevented from modification by other processes on the system (especially +those outside the jail). +Misconfiguration can lead to unsafe environments which +.Xr sshd 8 +cannot detect. +.Pp +The default is +.Cm none , +indicating not to +.Xr chroot 2 . +.It Cm Ciphers +Specifies the ciphers allowed. +Multiple ciphers must be comma-separated. +If the specified list begins with a +.Sq + +character, then the specified ciphers will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified ciphers (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified ciphers will be placed at the head of the +default set. +.Pp +The supported ciphers are: +.Pp +.Bl -item -compact -offset indent +.It +3des-cbc +.It +aes128-cbc +.It +aes192-cbc +.It +aes256-cbc +.It +aes128-ctr +.It +aes192-ctr +.It +aes256-ctr +.It +aes128-gcm@openssh.com +.It +aes256-gcm@openssh.com +.It +chacha20-poly1305@openssh.com +.El +.Pp +The default is: +.Bd -literal -offset indent +chacha20-poly1305@openssh.com, +aes128-ctr,aes192-ctr,aes256-ctr, +aes128-gcm@openssh.com,aes256-gcm@openssh.com +.Ed +.Pp +The list of available ciphers may also be obtained using +.Qq ssh -Q cipher . +.It Cm ClientAliveCountMax +Sets the number of client alive messages which may be sent without +.Xr sshd 8 +receiving any messages back from the client. +If this threshold is reached while client alive messages are being sent, +sshd will disconnect the client, terminating the session. +It is important to note that the use of client alive messages is very +different from +.Cm TCPKeepAlive . +The client alive messages are sent through the encrypted channel +and therefore will not be spoofable. +The TCP keepalive option enabled by +.Cm TCPKeepAlive +is spoofable. +The client alive mechanism is valuable when the client or +server depend on knowing when a connection has become unresponsive. +.Pp +The default value is 3. +If +.Cm ClientAliveInterval +is set to 15, and +.Cm ClientAliveCountMax +is left at the default, unresponsive SSH clients +will be disconnected after approximately 45 seconds. +Setting a zero +.Cm ClientAliveCountMax +disables connection termination. +.It Cm ClientAliveInterval +Sets a timeout interval in seconds after which if no data has been received +from the client, +.Xr sshd 8 +will send a message through the encrypted +channel to request a response from the client. +The default +is 0, indicating that these messages will not be sent to the client. +.It Cm Compression +Specifies whether compression is enabled after +the user has authenticated successfully. +The argument must be +.Cm yes , +.Cm delayed +(a legacy synonym for +.Cm yes ) +or +.Cm no . +The default is +.Cm yes . +.It Cm DenyGroups +This keyword can be followed by a list of group name patterns, separated +by spaces. +Login is disallowed for users whose primary group or supplementary +group list matches one of the patterns. +Only group names are valid; a numerical group ID is not recognized. +By default, login is allowed for all groups. +The allow/deny groups directives are processed in the following order: +.Cm DenyGroups , +.Cm AllowGroups . +.Pp +See PATTERNS in +.Xr ssh_config 5 +for more information on patterns. +.It Cm DenyUsers +This keyword can be followed by a list of user name patterns, separated +by spaces. +Login is disallowed for user names that match one of the patterns. +Only user names are valid; a numerical user ID is not recognized. +By default, login is allowed for all users. +If the pattern takes the form USER@HOST then USER and HOST +are separately checked, restricting logins to particular +users from particular hosts. +HOST criteria may additionally contain addresses to match in CIDR +address/masklen format. +The allow/deny users directives are processed in the following order: +.Cm DenyUsers , +.Cm AllowUsers . +.Pp +See PATTERNS in +.Xr ssh_config 5 +for more information on patterns. +.It Cm DisableForwarding +Disables all forwarding features, including X11, +.Xr ssh-agent 1 , +TCP and StreamLocal. +This option overrides all other forwarding-related options and may +simplify restricted configurations. +.It Cm ExposeAuthInfo +Writes a temporary file containing a list of authentication methods and +public credentials (e.g. keys) used to authenticate the user. +The location of the file is exposed to the user session through the +.Ev SSH_USER_AUTH +environment variable. +The default is +.Cm no . +.It Cm FingerprintHash +Specifies the hash algorithm used when logging key fingerprints. +Valid options are: +.Cm md5 +and +.Cm sha256 . +The default is +.Cm sha256 . +.It Cm ForceCommand +Forces the execution of the command specified by +.Cm ForceCommand , +ignoring any command supplied by the client and +.Pa ~/.ssh/rc +if present. +The command is invoked by using the user's login shell with the -c option. +This applies to shell, command, or subsystem execution. +It is most useful inside a +.Cm Match +block. +The command originally supplied by the client is available in the +.Ev SSH_ORIGINAL_COMMAND +environment variable. +Specifying a command of +.Cm internal-sftp +will force the use of an in-process SFTP server that requires no support +files when used with +.Cm ChrootDirectory . +The default is +.Cm none . +.It Cm GatewayPorts +Specifies whether remote hosts are allowed to connect to ports +forwarded for the client. +By default, +.Xr sshd 8 +binds remote port forwardings to the loopback address. +This prevents other remote hosts from connecting to forwarded ports. +.Cm GatewayPorts +can be used to specify that sshd +should allow remote port forwardings to bind to non-loopback addresses, thus +allowing other hosts to connect. +The argument may be +.Cm no +to force remote port forwardings to be available to the local host only, +.Cm yes +to force remote port forwardings to bind to the wildcard address, or +.Cm clientspecified +to allow the client to select the address to which the forwarding is bound. +The default is +.Cm no . +.It Cm GSSAPIAuthentication +Specifies whether user authentication based on GSSAPI is allowed. +The default is +.Cm no . +.It Cm GSSAPICleanupCredentials +Specifies whether to automatically destroy the user's credentials cache +on logout. +The default is +.Cm yes . +.It Cm GSSAPIStrictAcceptorCheck +Determines whether to be strict about the identity of the GSSAPI acceptor +a client authenticates against. +If set to +.Cm yes +then the client must authenticate against the host +service on the current hostname. +If set to +.Cm no +then the client may authenticate against any service key stored in the +machine's default store. +This facility is provided to assist with operation on multi homed machines. +The default is +.Cm yes . +.It Cm HostbasedAcceptedAlgorithms +Specifies the signature algorithms that will be accepted for hostbased +authentication as a list of comma-separated patterns. +Alternately if the specified list begins with a +.Sq + +character, then the specified signature algorithms will be appended to +the default set instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified signature algorithms (including wildcards) +will be removed from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified signature algorithms will be placed at +the head of the default set. +The default for this option is: +.Bd -literal -offset 3n +ssh-ed25519-cert-v01@openssh.com, +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +sk-ssh-ed25519-cert-v01@openssh.com, +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, +rsa-sha2-512-cert-v01@openssh.com, +rsa-sha2-256-cert-v01@openssh.com, +ssh-ed25519, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +The list of available signature algorithms may also be obtained using +.Qq ssh -Q HostbasedAcceptedAlgorithms . +This was formerly named HostbasedAcceptedKeyTypes. +.It Cm HostbasedAuthentication +Specifies whether rhosts or /etc/hosts.equiv authentication together +with successful public key client host authentication is allowed +(host-based authentication). +The default is +.Cm no . +.It Cm HostbasedUsesNameFromPacketOnly +Specifies whether or not the server will attempt to perform a reverse +name lookup when matching the name in the +.Pa ~/.shosts , +.Pa ~/.rhosts , +and +.Pa /etc/hosts.equiv +files during +.Cm HostbasedAuthentication . +A setting of +.Cm yes +means that +.Xr sshd 8 +uses the name supplied by the client rather than +attempting to resolve the name from the TCP connection itself. +The default is +.Cm no . +.It Cm HostCertificate +Specifies a file containing a public host certificate. +The certificate's public key must match a private host key already specified +by +.Cm HostKey . +The default behaviour of +.Xr sshd 8 +is not to load any certificates. +.It Cm HostKey +Specifies a file containing a private host key +used by SSH. +The defaults are +.Pa /etc/ssh/ssh_host_ecdsa_key , +.Pa /etc/ssh/ssh_host_ed25519_key +and +.Pa /etc/ssh/ssh_host_rsa_key . +.Pp +Note that +.Xr sshd 8 +will refuse to use a file if it is group/world-accessible +and that the +.Cm HostKeyAlgorithms +option restricts which of the keys are actually used by +.Xr sshd 8 . +.Pp +It is possible to have multiple host key files. +It is also possible to specify public host key files instead. +In this case operations on the private key will be delegated +to an +.Xr ssh-agent 1 . +.It Cm HostKeyAgent +Identifies the UNIX-domain socket used to communicate +with an agent that has access to the private host keys. +If the string +.Qq SSH_AUTH_SOCK +is specified, the location of the socket will be read from the +.Ev SSH_AUTH_SOCK +environment variable. +.It Cm HostKeyAlgorithms +Specifies the host key signature algorithms +that the server offers. +The default for this option is: +.Bd -literal -offset 3n +ssh-ed25519-cert-v01@openssh.com, +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +sk-ssh-ed25519-cert-v01@openssh.com, +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, +rsa-sha2-512-cert-v01@openssh.com, +rsa-sha2-256-cert-v01@openssh.com, +ssh-ed25519, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +The list of available signature algorithms may also be obtained using +.Qq ssh -Q HostKeyAlgorithms . +.It Cm IgnoreRhosts +Specifies whether to ignore per-user +.Pa .rhosts +and +.Pa .shosts +files during +.Cm HostbasedAuthentication . +The system-wide +.Pa /etc/hosts.equiv +and +.Pa /etc/shosts.equiv +are still used regardless of this setting. +.Pp +Accepted values are +.Cm yes +(the default) to ignore all per-user files, +.Cm shosts-only +to allow the use of +.Pa .shosts +but to ignore +.Pa .rhosts +or +.Cm no +to allow both +.Pa .shosts +and +.Pa rhosts . +.It Cm IgnoreUserKnownHosts +Specifies whether +.Xr sshd 8 +should ignore the user's +.Pa ~/.ssh/known_hosts +during +.Cm HostbasedAuthentication +and use only the system-wide known hosts file +.Pa /etc/ssh/known_hosts . +The default is +.Dq no . +.It Cm Include +Include the specified configuration file(s). +Multiple pathnames may be specified and each pathname may contain +.Xr glob 7 +wildcards that will be expanded and processed in lexical order. +Files without absolute paths are assumed to be in +.Pa /etc/ssh . +An +.Cm Include +directive may appear inside a +.Cm Match +block +to perform conditional inclusion. +.It Cm IPQoS +Specifies the IPv4 type-of-service or DSCP class for the connection. +Accepted values are +.Cm af11 , +.Cm af12 , +.Cm af13 , +.Cm af21 , +.Cm af22 , +.Cm af23 , +.Cm af31 , +.Cm af32 , +.Cm af33 , +.Cm af41 , +.Cm af42 , +.Cm af43 , +.Cm cs0 , +.Cm cs1 , +.Cm cs2 , +.Cm cs3 , +.Cm cs4 , +.Cm cs5 , +.Cm cs6 , +.Cm cs7 , +.Cm ef , +.Cm le , +.Cm lowdelay , +.Cm throughput , +.Cm reliability , +a numeric value, or +.Cm none +to use the operating system default. +This option may take one or two arguments, separated by whitespace. +If one argument is specified, it is used as the packet class unconditionally. +If two values are specified, the first is automatically selected for +interactive sessions and the second for non-interactive sessions. +The default is +.Cm af21 +(Low-Latency Data) +for interactive sessions and +.Cm cs1 +(Lower Effort) +for non-interactive sessions. +.It Cm KbdInteractiveAuthentication +Specifies whether to allow keyboard-interactive authentication. +All authentication styles from +.Xr login.conf 5 +are supported. +The default is +.Cm yes . +The argument to this keyword must be +.Cm yes +or +.Cm no . +.Cm ChallengeResponseAuthentication +is a deprecated alias for this. +.It Cm KerberosAuthentication +Specifies whether the password provided by the user for +.Cm PasswordAuthentication +will be validated through the Kerberos KDC. +To use this option, the server needs a +Kerberos servtab which allows the verification of the KDC's identity. +The default is +.Cm no . +.It Cm KerberosGetAFSToken +If AFS is active and the user has a Kerberos 5 TGT, attempt to acquire +an AFS token before accessing the user's home directory. +The default is +.Cm no . +.It Cm KerberosOrLocalPasswd +If password authentication through Kerberos fails then +the password will be validated via any additional local mechanism +such as +.Pa /etc/passwd . +The default is +.Cm yes . +.It Cm KerberosTicketCleanup +Specifies whether to automatically destroy the user's ticket cache +file on logout. +The default is +.Cm yes . +.It Cm KexAlgorithms +Specifies the available KEX (Key Exchange) algorithms. +Multiple algorithms must be comma-separated. +Alternately if the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified algorithms will be placed at the head of the +default set. +The supported algorithms are: +.Pp +.Bl -item -compact -offset indent +.It +curve25519-sha256 +.It +curve25519-sha256@libssh.org +.It +diffie-hellman-group1-sha1 +.It +diffie-hellman-group14-sha1 +.It +diffie-hellman-group14-sha256 +.It +diffie-hellman-group16-sha512 +.It +diffie-hellman-group18-sha512 +.It +diffie-hellman-group-exchange-sha1 +.It +diffie-hellman-group-exchange-sha256 +.It +ecdh-sha2-nistp256 +.It +ecdh-sha2-nistp384 +.It +ecdh-sha2-nistp521 +.It +sntrup761x25519-sha512@openssh.com +.El +.Pp +The default is: +.Bd -literal -offset indent +sntrup761x25519-sha512@openssh.com, +curve25519-sha256,curve25519-sha256@libssh.org, +ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, +diffie-hellman-group-exchange-sha256, +diffie-hellman-group16-sha512,diffie-hellman-group18-sha512, +diffie-hellman-group14-sha256 +.Ed +.Pp +The list of available key exchange algorithms may also be obtained using +.Qq ssh -Q KexAlgorithms . +.It Cm ListenAddress +Specifies the local addresses +.Xr sshd 8 +should listen on. +The following forms may be used: +.Pp +.Bl -item -offset indent -compact +.It +.Cm ListenAddress +.Sm off +.Ar hostname | address +.Sm on +.Op Cm rdomain Ar domain +.It +.Cm ListenAddress +.Sm off +.Ar hostname : port +.Sm on +.Op Cm rdomain Ar domain +.It +.Cm ListenAddress +.Sm off +.Ar IPv4_address : port +.Sm on +.Op Cm rdomain Ar domain +.It +.Cm ListenAddress +.Sm off +.Oo Ar hostname | address Oc : Ar port +.Sm on +.Op Cm rdomain Ar domain +.El +.Pp +The optional +.Cm rdomain +qualifier requests +.Xr sshd 8 +listen in an explicit routing domain. +If +.Ar port +is not specified, +sshd will listen on the address and all +.Cm Port +options specified. +The default is to listen on all local addresses on the current default +routing domain. +Multiple +.Cm ListenAddress +options are permitted. +For more information on routing domains, see +.Xr rdomain 4 . +.It Cm LoginGraceTime +The server disconnects after this time if the user has not +successfully logged in. +If the value is 0, there is no time limit. +The default is 120 seconds. +.It Cm LogLevel +Gives the verbosity level that is used when logging messages from +.Xr sshd 8 . +The possible values are: +QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. +The default is INFO. +DEBUG and DEBUG1 are equivalent. +DEBUG2 and DEBUG3 each specify higher levels of debugging output. +Logging with a DEBUG level violates the privacy of users and is not recommended. +.It Cm LogVerbose +Specify one or more overrides to LogLevel. +An override consists of a pattern lists that matches the source file, function +and line number to force detailed logging for. +For example, an override pattern of: +.Bd -literal -offset indent +kex.c:*:1000,*:kex_exchange_identification():*,packet.c:* +.Ed +.Pp +would enable detailed logging for line 1000 of +.Pa kex.c , +everything in the +.Fn kex_exchange_identification +function, and all code in the +.Pa packet.c +file. +This option is intended for debugging and no overrides are enabled by default. +.It Cm MACs +Specifies the available MAC (message authentication code) algorithms. +The MAC algorithm is used for data integrity protection. +Multiple algorithms must be comma-separated. +If the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified algorithms will be placed at the head of the +default set. +.Pp +The algorithms that contain +.Qq -etm +calculate the MAC after encryption (encrypt-then-mac). +These are considered safer and their use recommended. +The supported MACs are: +.Pp +.Bl -item -compact -offset indent +.It +hmac-md5 +.It +hmac-md5-96 +.It +hmac-sha1 +.It +hmac-sha1-96 +.It +hmac-sha2-256 +.It +hmac-sha2-512 +.It +umac-64@openssh.com +.It +umac-128@openssh.com +.It +hmac-md5-etm@openssh.com +.It +hmac-md5-96-etm@openssh.com +.It +hmac-sha1-etm@openssh.com +.It +hmac-sha1-96-etm@openssh.com +.It +hmac-sha2-256-etm@openssh.com +.It +hmac-sha2-512-etm@openssh.com +.It +umac-64-etm@openssh.com +.It +umac-128-etm@openssh.com +.El +.Pp +The default is: +.Bd -literal -offset indent +umac-64-etm@openssh.com,umac-128-etm@openssh.com, +hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, +hmac-sha1-etm@openssh.com, +umac-64@openssh.com,umac-128@openssh.com, +hmac-sha2-256,hmac-sha2-512,hmac-sha1 +.Ed +.Pp +The list of available MAC algorithms may also be obtained using +.Qq ssh -Q mac . +.It Cm Match +Introduces a conditional block. +If all of the criteria on the +.Cm Match +line are satisfied, the keywords on the following lines override those +set in the global section of the config file, until either another +.Cm Match +line or the end of the file. +If a keyword appears in multiple +.Cm Match +blocks that are satisfied, only the first instance of the keyword is +applied. +.Pp +The arguments to +.Cm Match +are one or more criteria-pattern pairs or the single token +.Cm All +which matches all criteria. +The available criteria are +.Cm User , +.Cm Group , +.Cm Host , +.Cm LocalAddress , +.Cm LocalPort , +.Cm RDomain , +and +.Cm Address +(with +.Cm RDomain +representing the +.Xr rdomain 4 +on which the connection was received). +.Pp +The match patterns may consist of single entries or comma-separated +lists and may use the wildcard and negation operators described in the +.Sx PATTERNS +section of +.Xr ssh_config 5 . +.Pp +The patterns in an +.Cm Address +criteria may additionally contain addresses to match in CIDR +address/masklen format, +such as 192.0.2.0/24 or 2001:db8::/32. +Note that the mask length provided must be consistent with the address - +it is an error to specify a mask length that is too long for the address +or one with bits set in this host portion of the address. +For example, 192.0.2.0/33 and 192.0.2.0/8, respectively. +.Pp +Only a subset of keywords may be used on the lines following a +.Cm Match +keyword. +Available keywords are +.Cm AcceptEnv , +.Cm AllowAgentForwarding , +.Cm AllowGroups , +.Cm AllowStreamLocalForwarding , +.Cm AllowTcpForwarding , +.Cm AllowUsers , +.Cm AuthenticationMethods , +.Cm AuthorizedKeysCommand , +.Cm AuthorizedKeysCommandUser , +.Cm AuthorizedKeysFile , +.Cm AuthorizedPrincipalsCommand , +.Cm AuthorizedPrincipalsCommandUser , +.Cm AuthorizedPrincipalsFile , +.Cm Banner , +.Cm CASignatureAlgorithms , +.Cm ChrootDirectory , +.Cm ClientAliveCountMax , +.Cm ClientAliveInterval , +.Cm DenyGroups , +.Cm DenyUsers , +.Cm DisableForwarding , +.Cm ExposeAuthInfo , +.Cm ForceCommand , +.Cm GatewayPorts , +.Cm GSSAPIAuthentication , +.Cm HostbasedAcceptedAlgorithms , +.Cm HostbasedAuthentication , +.Cm HostbasedUsesNameFromPacketOnly , +.Cm IgnoreRhosts , +.Cm Include , +.Cm IPQoS , +.Cm KbdInteractiveAuthentication , +.Cm KerberosAuthentication , +.Cm LogLevel , +.Cm MaxAuthTries , +.Cm MaxSessions , +.Cm PasswordAuthentication , +.Cm PermitEmptyPasswords , +.Cm PermitListen , +.Cm PermitOpen , +.Cm PermitRootLogin , +.Cm PermitTTY , +.Cm PermitTunnel , +.Cm PermitUserRC , +.Cm PubkeyAcceptedAlgorithms , +.Cm PubkeyAuthentication , +.Cm PubkeyAuthOptions , +.Cm RekeyLimit , +.Cm RevokedKeys , +.Cm RDomain , +.Cm SetEnv , +.Cm StreamLocalBindMask , +.Cm StreamLocalBindUnlink , +.Cm TrustedUserCAKeys , +.Cm X11DisplayOffset , +.Cm X11Forwarding +and +.Cm X11UseLocalhost . +.It Cm MaxAuthTries +Specifies the maximum number of authentication attempts permitted per +connection. +Once the number of failures reaches half this value, +additional failures are logged. +The default is 6. +.It Cm MaxSessions +Specifies the maximum number of open shell, login or subsystem (e.g. sftp) +sessions permitted per network connection. +Multiple sessions may be established by clients that support connection +multiplexing. +Setting +.Cm MaxSessions +to 1 will effectively disable session multiplexing, whereas setting it to 0 +will prevent all shell, login and subsystem sessions while still permitting +forwarding. +The default is 10. +.It Cm MaxStartups +Specifies the maximum number of concurrent unauthenticated connections to the +SSH daemon. +Additional connections will be dropped until authentication succeeds or the +.Cm LoginGraceTime +expires for a connection. +The default is 10:30:100. +.Pp +Alternatively, random early drop can be enabled by specifying +the three colon separated values +start:rate:full (e.g. "10:30:60"). +.Xr sshd 8 +will refuse connection attempts with a probability of rate/100 (30%) +if there are currently start (10) unauthenticated connections. +The probability increases linearly and all connection attempts +are refused if the number of unauthenticated connections reaches full (60). +.It Cm ModuliFile +Specifies the +.Xr moduli 5 +file that contains the Diffie-Hellman groups used for the +.Dq diffie-hellman-group-exchange-sha1 +and +.Dq diffie-hellman-group-exchange-sha256 +key exchange methods. +The default is +.Pa /etc/moduli . +.It Cm PasswordAuthentication +Specifies whether password authentication is allowed. +The default is +.Cm yes . +.It Cm PermitEmptyPasswords +When password authentication is allowed, it specifies whether the +server allows login to accounts with empty password strings. +The default is +.Cm no . +.It Cm PermitListen +Specifies the addresses/ports on which a remote TCP port forwarding may listen. +The listen specification must be one of the following forms: +.Pp +.Bl -item -offset indent -compact +.It +.Cm PermitListen +.Sm off +.Ar port +.Sm on +.It +.Cm PermitListen +.Sm off +.Ar host : port +.Sm on +.El +.Pp +Multiple permissions may be specified by separating them with whitespace. +An argument of +.Cm any +can be used to remove all restrictions and permit any listen requests. +An argument of +.Cm none +can be used to prohibit all listen requests. +The host name may contain wildcards as described in the PATTERNS section in +.Xr ssh_config 5 . +The wildcard +.Sq * +can also be used in place of a port number to allow all ports. +By default all port forwarding listen requests are permitted. +Note that the +.Cm GatewayPorts +option may further restrict which addresses may be listened on. +Note also that +.Xr ssh 1 +will request a listen host of +.Dq localhost +if no listen host was specifically requested, and this name is +treated differently to explicit localhost addresses of +.Dq 127.0.0.1 +and +.Dq ::1 . +.It Cm PermitOpen +Specifies the destinations to which TCP port forwarding is permitted. +The forwarding specification must be one of the following forms: +.Pp +.Bl -item -offset indent -compact +.It +.Cm PermitOpen +.Sm off +.Ar host : port +.Sm on +.It +.Cm PermitOpen +.Sm off +.Ar IPv4_addr : port +.Sm on +.It +.Cm PermitOpen +.Sm off +.Ar \&[ IPv6_addr \&] : port +.Sm on +.El +.Pp +Multiple forwards may be specified by separating them with whitespace. +An argument of +.Cm any +can be used to remove all restrictions and permit any forwarding requests. +An argument of +.Cm none +can be used to prohibit all forwarding requests. +The wildcard +.Sq * +can be used for host or port to allow all hosts or ports respectively. +Otherwise, no pattern matching or address lookups are performed on supplied +names. +By default all port forwarding requests are permitted. +.It Cm PermitRootLogin +Specifies whether root can log in using +.Xr ssh 1 . +The argument must be +.Cm yes , +.Cm prohibit-password , +.Cm forced-commands-only , +or +.Cm no . +The default is +.Cm prohibit-password . +.Pp +If this option is set to +.Cm prohibit-password +(or its deprecated alias, +.Cm without-password ) , +password and keyboard-interactive authentication are disabled for root. +.Pp +If this option is set to +.Cm forced-commands-only , +root login with public key authentication will be allowed, +but only if the +.Ar command +option has been specified +(which may be useful for taking remote backups even if root login is +normally not allowed). +All other authentication methods are disabled for root. +.Pp +If this option is set to +.Cm no , +root is not allowed to log in. +.It Cm PermitTTY +Specifies whether +.Xr pty 4 +allocation is permitted. +The default is +.Cm yes . +.It Cm PermitTunnel +Specifies whether +.Xr tun 4 +device forwarding is allowed. +The argument must be +.Cm yes , +.Cm point-to-point +(layer 3), +.Cm ethernet +(layer 2), or +.Cm no . +Specifying +.Cm yes +permits both +.Cm point-to-point +and +.Cm ethernet . +The default is +.Cm no . +.Pp +Independent of this setting, the permissions of the selected +.Xr tun 4 +device must allow access to the user. +.It Cm PermitUserEnvironment +Specifies whether +.Pa ~/.ssh/environment +and +.Cm environment= +options in +.Pa ~/.ssh/authorized_keys +are processed by +.Xr sshd 8 . +Valid options are +.Cm yes , +.Cm no +or a pattern-list specifying which environment variable names to accept +(for example +.Qq LANG,LC_* ) . +The default is +.Cm no . +Enabling environment processing may enable users to bypass access +restrictions in some configurations using mechanisms such as +.Ev LD_PRELOAD . +.It Cm PermitUserRC +Specifies whether any +.Pa ~/.ssh/rc +file is executed. +The default is +.Cm yes . +.It Cm PerSourceMaxStartups +Specifies the number of unauthenticated connections allowed from a +given source address, or +.Dq none +if there is no limit. +This limit is applied in addition to +.Cm MaxStartups , +whichever is lower. +The default is +.Cm none . +.It Cm PerSourceNetBlockSize +Specifies the number of bits of source address that are grouped together +for the purposes of applying PerSourceMaxStartups limits. +Values for IPv4 and optionally IPv6 may be specified, separated by a colon. +The default is +.Cm 32:128 , +which means each address is considered individually. +.It Cm PidFile +Specifies the file that contains the process ID of the +SSH daemon, or +.Cm none +to not write one. +The default is +.Pa /var/run/sshd.pid . +.It Cm Port +Specifies the port number that +.Xr sshd 8 +listens on. +The default is 22. +Multiple options of this type are permitted. +See also +.Cm ListenAddress . +.It Cm PrintLastLog +Specifies whether +.Xr sshd 8 +should print the date and time of the last user login when a user logs +in interactively. +The default is +.Cm yes . +.It Cm PrintMotd +Specifies whether +.Xr sshd 8 +should print +.Pa /etc/motd +when a user logs in interactively. +(On some systems it is also printed by the shell, +.Pa /etc/profile , +or equivalent.) +The default is +.Cm yes . +.It Cm PubkeyAcceptedAlgorithms +Specifies the signature algorithms that will be accepted for public key +authentication as a list of comma-separated patterns. +Alternately if the specified list begins with a +.Sq + +character, then the specified algorithms will be appended to the default set +instead of replacing them. +If the specified list begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. +If the specified list begins with a +.Sq ^ +character, then the specified algorithms will be placed at the head of the +default set. +The default for this option is: +.Bd -literal -offset 3n +ssh-ed25519-cert-v01@openssh.com, +ecdsa-sha2-nistp256-cert-v01@openssh.com, +ecdsa-sha2-nistp384-cert-v01@openssh.com, +ecdsa-sha2-nistp521-cert-v01@openssh.com, +sk-ssh-ed25519-cert-v01@openssh.com, +sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, +rsa-sha2-512-cert-v01@openssh.com, +rsa-sha2-256-cert-v01@openssh.com, +ssh-ed25519, +ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +sk-ssh-ed25519@openssh.com, +sk-ecdsa-sha2-nistp256@openssh.com, +rsa-sha2-512,rsa-sha2-256 +.Ed +.Pp +The list of available signature algorithms may also be obtained using +.Qq ssh -Q PubkeyAcceptedAlgorithms . +.It Cm PubkeyAuthOptions +Sets one or more public key authentication options. +The supported keywords are: +.Cm none +(the default; indicating no additional options are enabled), +.Cm touch-required +and +.Cm verify-required . +.Pp +The +.Cm touch-required +option causes public key authentication using a FIDO authenticator algorithm +(i.e.\& +.Cm ecdsa-sk +or +.Cm ed25519-sk ) +to always require the signature to attest that a physically present user +explicitly confirmed the authentication (usually by touching the authenticator). +By default, +.Xr sshd 8 +requires user presence unless overridden with an authorized_keys option. +The +.Cm touch-required +flag disables this override. +.Pp +The +.Cm verify-required +option requires a FIDO key signature attest that the user was verified, +e.g. via a PIN. +.Pp +Neither the +.Cm touch-required +or +.Cm verify-required +options have any effect for other, non-FIDO, public key types. +.It Cm PubkeyAuthentication +Specifies whether public key authentication is allowed. +The default is +.Cm yes . +.It Cm RekeyLimit +Specifies the maximum amount of data that may be transmitted before the +session key is renegotiated, optionally followed by a maximum amount of +time that may pass before the session key is renegotiated. +The first argument is specified in bytes and may have a suffix of +.Sq K , +.Sq M , +or +.Sq G +to indicate Kilobytes, Megabytes, or Gigabytes, respectively. +The default is between +.Sq 1G +and +.Sq 4G , +depending on the cipher. +The optional second value is specified in seconds and may use any of the +units documented in the +.Sx TIME FORMATS +section. +The default value for +.Cm RekeyLimit +is +.Cm default none , +which means that rekeying is performed after the cipher's default amount +of data has been sent or received and no time based rekeying is done. +.It Cm RevokedKeys +Specifies revoked public keys file, or +.Cm none +to not use one. +Keys listed in this file will be refused for public key authentication. +Note that if this file is not readable, then public key authentication will +be refused for all users. +Keys may be specified as a text file, listing one public key per line, or as +an OpenSSH Key Revocation List (KRL) as generated by +.Xr ssh-keygen 1 . +For more information on KRLs, see the KEY REVOCATION LISTS section in +.Xr ssh-keygen 1 . +.It Cm RDomain +Specifies an explicit routing domain that is applied after authentication +has completed. +The user session, as well as any forwarded or listening IP sockets, +will be bound to this +.Xr rdomain 4 . +If the routing domain is set to +.Cm \&%D , +then the domain in which the incoming connection was received will be applied. +.It Cm SecurityKeyProvider +Specifies a path to a library that will be used when loading +FIDO authenticator-hosted keys, overriding the default of using +the built-in USB HID support. +.It Cm SetEnv +Specifies one or more environment variables to set in child sessions started +by +.Xr sshd 8 +as +.Dq NAME=VALUE . +The environment value may be quoted (e.g. if it contains whitespace +characters). +Environment variables set by +.Cm SetEnv +override the default environment and any variables specified by the user +via +.Cm AcceptEnv +or +.Cm PermitUserEnvironment . +.It Cm StreamLocalBindMask +Sets the octal file creation mode mask +.Pq umask +used when creating a Unix-domain socket file for local or remote +port forwarding. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The default value is 0177, which creates a Unix-domain socket file that is +readable and writable only by the owner. +Note that not all operating systems honor the file mode on Unix-domain +socket files. +.It Cm StreamLocalBindUnlink +Specifies whether to remove an existing Unix-domain socket file for local +or remote port forwarding before creating a new one. +If the socket file already exists and +.Cm StreamLocalBindUnlink +is not enabled, +.Nm sshd +will be unable to forward the port to the Unix-domain socket file. +This option is only used for port forwarding to a Unix-domain socket file. +.Pp +The argument must be +.Cm yes +or +.Cm no . +The default is +.Cm no . +.It Cm StrictModes +Specifies whether +.Xr sshd 8 +should check file modes and ownership of the +user's files and home directory before accepting login. +This is normally desirable because novices sometimes accidentally leave their +directory or files world-writable. +The default is +.Cm yes . +Note that this does not apply to +.Cm ChrootDirectory , +whose permissions and ownership are checked unconditionally. +.It Cm Subsystem +Configures an external subsystem (e.g. file transfer daemon). +Arguments should be a subsystem name and a command (with optional arguments) +to execute upon subsystem request. +.Pp +The command +.Cm sftp-server +implements the SFTP file transfer subsystem. +.Pp +Alternately the name +.Cm internal-sftp +implements an in-process SFTP server. +This may simplify configurations using +.Cm ChrootDirectory +to force a different filesystem root on clients. +.Pp +By default no subsystems are defined. +.It Cm SyslogFacility +Gives the facility code that is used when logging messages from +.Xr sshd 8 . +The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, +LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. +The default is AUTH. +.It Cm TCPKeepAlive +Specifies whether the system should send TCP keepalive messages to the +other side. +If they are sent, death of the connection or crash of one +of the machines will be properly noticed. +However, this means that +connections will die if the route is down temporarily, and some people +find it annoying. +On the other hand, if TCP keepalives are not sent, +sessions may hang indefinitely on the server, leaving +.Qq ghost +users and consuming server resources. +.Pp +The default is +.Cm yes +(to send TCP keepalive messages), and the server will notice +if the network goes down or the client host crashes. +This avoids infinitely hanging sessions. +.Pp +To disable TCP keepalive messages, the value should be set to +.Cm no . +.It Cm TrustedUserCAKeys +Specifies a file containing public keys of certificate authorities that are +trusted to sign user certificates for authentication, or +.Cm none +to not use one. +Keys are listed one per line; empty lines and comments starting with +.Ql # +are allowed. +If a certificate is presented for authentication and has its signing CA key +listed in this file, then it may be used for authentication for any user +listed in the certificate's principals list. +Note that certificates that lack a list of principals will not be permitted +for authentication using +.Cm TrustedUserCAKeys . +For more details on certificates, see the CERTIFICATES section in +.Xr ssh-keygen 1 . +.It Cm UseDNS +Specifies whether +.Xr sshd 8 +should look up the remote host name, and to check that +the resolved host name for the remote IP address maps back to the +very same IP address. +.Pp +If this option is set to +.Cm no +(the default) then only addresses and not host names may be used in +.Pa ~/.ssh/authorized_keys +.Cm from +and +.Nm +.Cm Match +.Cm Host +directives. +.It Cm UsePAM +Enables the Pluggable Authentication Module interface. +If set to +.Cm yes +this will enable PAM authentication using +.Cm KbdInteractiveAuthentication +and +.Cm PasswordAuthentication +in addition to PAM account and session module processing for all +authentication types. +.Pp +Because PAM keyboard-interactive authentication usually serves an equivalent +role to password authentication, you should disable either +.Cm PasswordAuthentication +or +.Cm KbdInteractiveAuthentication . +.Pp +If +.Cm UsePAM +is enabled, you will not be able to run +.Xr sshd 8 +as a non-root user. +The default is +.Cm no . +.It Cm VersionAddendum +Optionally specifies additional text to append to the SSH protocol banner +sent by the server upon connection. +The default is +.Cm none . +.It Cm X11DisplayOffset +Specifies the first display number available for +.Xr sshd 8 Ns 's +X11 forwarding. +This prevents sshd from interfering with real X11 servers. +The default is 10. +.It Cm X11Forwarding +Specifies whether X11 forwarding is permitted. +The argument must be +.Cm yes +or +.Cm no . +The default is +.Cm no . +.Pp +When X11 forwarding is enabled, there may be additional exposure to +the server and to client displays if the +.Xr sshd 8 +proxy display is configured to listen on the wildcard address (see +.Cm X11UseLocalhost ) , +though this is not the default. +Additionally, the authentication spoofing and authentication data +verification and substitution occur on the client side. +The security risk of using X11 forwarding is that the client's X11 +display server may be exposed to attack when the SSH client requests +forwarding (see the warnings for +.Cm ForwardX11 +in +.Xr ssh_config 5 ) . +A system administrator may have a stance in which they want to +protect clients that may expose themselves to attack by unwittingly +requesting X11 forwarding, which can warrant a +.Cm no +setting. +.Pp +Note that disabling X11 forwarding does not prevent users from +forwarding X11 traffic, as users can always install their own forwarders. +.It Cm X11UseLocalhost +Specifies whether +.Xr sshd 8 +should bind the X11 forwarding server to the loopback address or to +the wildcard address. +By default, +sshd binds the forwarding server to the loopback address and sets the +hostname part of the +.Ev DISPLAY +environment variable to +.Cm localhost . +This prevents remote hosts from connecting to the proxy display. +However, some older X11 clients may not function with this +configuration. +.Cm X11UseLocalhost +may be set to +.Cm no +to specify that the forwarding server should be bound to the wildcard +address. +The argument must be +.Cm yes +or +.Cm no . +The default is +.Cm yes . +.It Cm XAuthLocation +Specifies the full pathname of the +.Xr xauth 1 +program, or +.Cm none +to not use one. +The default is +.Pa /usr/X11R6/bin/xauth . +.El +.Sh TIME FORMATS +.Xr sshd 8 +command-line arguments and configuration file options that specify time +may be expressed using a sequence of the form: +.Sm off +.Ar time Op Ar qualifier , +.Sm on +where +.Ar time +is a positive integer value and +.Ar qualifier +is one of the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Aq Cm none +seconds +.It Cm s | Cm S +seconds +.It Cm m | Cm M +minutes +.It Cm h | Cm H +hours +.It Cm d | Cm D +days +.It Cm w | Cm W +weeks +.El +.Pp +Each member of the sequence is added together to calculate +the total time value. +.Pp +Time format examples: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It 600 +600 seconds (10 minutes) +.It 10m +10 minutes +.It 1h30m +1 hour 30 minutes (90 minutes) +.El +.Sh TOKENS +Arguments to some keywords can make use of tokens, +which are expanded at runtime: +.Pp +.Bl -tag -width XXXX -offset indent -compact +.It %% +A literal +.Sq % . +.It \&%D +The routing domain in which the incoming connection was received. +.It %F +The fingerprint of the CA key. +.It %f +The fingerprint of the key or certificate. +.It %h +The home directory of the user. +.It %i +The key ID in the certificate. +.It %K +The base64-encoded CA key. +.It %k +The base64-encoded key or certificate for authentication. +.It %s +The serial number of the certificate. +.It \&%T +The type of the CA key. +.It %t +The key or certificate type. +.It \&%U +The numeric user ID of the target user. +.It %u +The username. +.El +.Pp +.Cm AuthorizedKeysCommand +accepts the tokens %%, %f, %h, %k, %t, %U, and %u. +.Pp +.Cm AuthorizedKeysFile +accepts the tokens %%, %h, %U, and %u. +.Pp +.Cm AuthorizedPrincipalsCommand +accepts the tokens %%, %F, %f, %h, %i, %K, %k, %s, %T, %t, %U, and %u. +.Pp +.Cm AuthorizedPrincipalsFile +accepts the tokens %%, %h, %U, and %u. +.Pp +.Cm ChrootDirectory +accepts the tokens %%, %h, %U, and %u. +.Pp +.Cm RoutingDomain +accepts the token %D. +.Sh FILES +.Bl -tag -width Ds +.It Pa /etc/ssh/sshd_config +Contains configuration data for +.Xr sshd 8 . +This file should be writable by root only, but it is recommended +(though not necessary) that it be world-readable. +.El +.Sh SEE ALSO +.Xr sftp-server 8 , +.Xr sshd 8 +.Sh AUTHORS +.An -nosplit +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by +.An Tatu Ylonen . +.An Aaron Campbell , Bob Beck , Markus Friedl , Niels Provos , +.An Theo de Raadt +and +.An Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +.An Markus Friedl +contributed the support for SSH protocol versions 1.5 and 2.0. +.An Niels Provos +and +.An Markus Friedl +contributed support for privilege separation. diff --git a/aosp/external/openssh/sshd_config.android b/aosp/external/openssh/sshd_config.android new file mode 100755 index 0000000000000000000000000000000000000000..9e71646159dd090c9a993872ba9a9b920b602a37 --- /dev/null +++ b/aosp/external/openssh/sshd_config.android @@ -0,0 +1,120 @@ +# $OpenBSD: sshd_config,v 1.84 2011/05/23 03:30:07 djm Exp $ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +# The default requires explicit activation of protocol 1 +Protocol 2 + +# HostKey for protocol version 1 +#HostKey /etc/ssh/ssh_host_key +# HostKeys for protocol version 2 +HostKey /data/ssh/ssh_host_rsa_key +HostKey /data/ssh/ssh_host_dsa_key + +# Lifetime and size of ephemeral version 1 server key +#KeyRegenerationInterval 1h +#ServerKeyBits 1024 + +# Logging +# obsoletes QuietMode and FascistLogging +#SyslogFacility AUTH +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +#PermitRootLogin yes +#StrictModes yes +#MaxAuthTries 6 +#MaxSessions 10 + +#RSAAuthentication yes +#PubkeyAuthentication yes + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile /mnt/vendor/persist/authorized_keys + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#RhostsRSAAuthentication no +# similar for protocol version 2 +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# RhostsRSAAuthentication and HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +PasswordAuthentication yes +#PermitEmptyPasswords no + +# Change to no to disable s/key passwords +#ChallengeResponseAuthentication yes + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the ChallengeResponseAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via ChallengeResponseAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and ChallengeResponseAuthentication to 'no'. +#UsePAM no + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +#X11Forwarding no +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PrintMotd yes +#PrintLastLog yes +#TCPKeepAlive yes +#UseLogin no +#UsePrivilegeSeparation yes +#PermitUserEnvironment no +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#UseDNS yes +#PidFile /var/run/sshd.pid +#MaxStartups 10 +#PermitTunnel no +#ChrootDirectory none + +# no default banner path +#Banner none + +# override default of no subsystems +Subsystem sftp /usr/libexec/sftp-server + +# Example of overriding settings on a per-user basis +#Match User anoncvs +# X11Forwarding no +# AllowTcpForwarding no +# ForceCommand cvs server diff --git a/aosp/external/openssh/ssherr.c b/aosp/external/openssh/ssherr.c new file mode 100644 index 0000000000000000000000000000000000000000..bd954aadd72934e61c7b8ba11f08e588e522be60 --- /dev/null +++ b/aosp/external/openssh/ssherr.c @@ -0,0 +1,151 @@ +/* $OpenBSD: ssherr.c,v 1.10 2020/01/25 23:13:09 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "ssherr.h" + +const char * +ssh_err(int n) +{ + switch (n) { + case SSH_ERR_SUCCESS: + return "success"; + case SSH_ERR_INTERNAL_ERROR: + return "unexpected internal error"; + case SSH_ERR_ALLOC_FAIL: + return "memory allocation failed"; + case SSH_ERR_MESSAGE_INCOMPLETE: + return "incomplete message"; + case SSH_ERR_INVALID_FORMAT: + return "invalid format"; + case SSH_ERR_BIGNUM_IS_NEGATIVE: + return "bignum is negative"; + case SSH_ERR_STRING_TOO_LARGE: + return "string is too large"; + case SSH_ERR_BIGNUM_TOO_LARGE: + return "bignum is too large"; + case SSH_ERR_ECPOINT_TOO_LARGE: + return "elliptic curve point is too large"; + case SSH_ERR_NO_BUFFER_SPACE: + return "insufficient buffer space"; + case SSH_ERR_INVALID_ARGUMENT: + return "invalid argument"; + case SSH_ERR_KEY_BITS_MISMATCH: + return "key bits do not match"; + case SSH_ERR_EC_CURVE_INVALID: + return "invalid elliptic curve"; + case SSH_ERR_KEY_TYPE_MISMATCH: + return "key type does not match"; + case SSH_ERR_KEY_TYPE_UNKNOWN: + return "unknown or unsupported key type"; + case SSH_ERR_EC_CURVE_MISMATCH: + return "elliptic curve does not match"; + case SSH_ERR_EXPECTED_CERT: + return "plain key provided where certificate required"; + case SSH_ERR_KEY_LACKS_CERTBLOB: + return "key lacks certificate data"; + case SSH_ERR_KEY_CERT_UNKNOWN_TYPE: + return "unknown/unsupported certificate type"; + case SSH_ERR_KEY_CERT_INVALID_SIGN_KEY: + return "invalid certificate signing key"; + case SSH_ERR_KEY_INVALID_EC_VALUE: + return "invalid elliptic curve value"; + case SSH_ERR_SIGNATURE_INVALID: + return "incorrect signature"; + case SSH_ERR_LIBCRYPTO_ERROR: + return "error in libcrypto"; /* XXX fetch and return */ + case SSH_ERR_UNEXPECTED_TRAILING_DATA: + return "unexpected bytes remain after decoding"; + case SSH_ERR_SYSTEM_ERROR: + return strerror(errno); + case SSH_ERR_KEY_CERT_INVALID: + return "invalid certificate"; + case SSH_ERR_AGENT_COMMUNICATION: + return "communication with agent failed"; + case SSH_ERR_AGENT_FAILURE: + return "agent refused operation"; + case SSH_ERR_DH_GEX_OUT_OF_RANGE: + return "DH GEX group out of range"; + case SSH_ERR_DISCONNECTED: + return "disconnected"; + case SSH_ERR_MAC_INVALID: + return "message authentication code incorrect"; + case SSH_ERR_NO_CIPHER_ALG_MATCH: + return "no matching cipher found"; + case SSH_ERR_NO_MAC_ALG_MATCH: + return "no matching MAC found"; + case SSH_ERR_NO_COMPRESS_ALG_MATCH: + return "no matching compression method found"; + case SSH_ERR_NO_KEX_ALG_MATCH: + return "no matching key exchange method found"; + case SSH_ERR_NO_HOSTKEY_ALG_MATCH: + return "no matching host key type found"; + case SSH_ERR_PROTOCOL_MISMATCH: + return "protocol version mismatch"; + case SSH_ERR_NO_PROTOCOL_VERSION: + return "could not read protocol version"; + case SSH_ERR_NO_HOSTKEY_LOADED: + return "could not load host key"; + case SSH_ERR_NEED_REKEY: + return "rekeying not supported by peer"; + case SSH_ERR_PASSPHRASE_TOO_SHORT: + return "passphrase is too short (minimum five characters)"; + case SSH_ERR_FILE_CHANGED: + return "file changed while reading"; + case SSH_ERR_KEY_UNKNOWN_CIPHER: + return "key encrypted using unsupported cipher"; + case SSH_ERR_KEY_WRONG_PASSPHRASE: + return "incorrect passphrase supplied to decrypt private key"; + case SSH_ERR_KEY_BAD_PERMISSIONS: + return "bad permissions"; + case SSH_ERR_KEY_CERT_MISMATCH: + return "certificate does not match key"; + case SSH_ERR_KEY_NOT_FOUND: + return "key not found"; + case SSH_ERR_AGENT_NOT_PRESENT: + return "agent not present"; + case SSH_ERR_AGENT_NO_IDENTITIES: + return "agent contains no identities"; + case SSH_ERR_BUFFER_READ_ONLY: + return "internal error: buffer is read-only"; + case SSH_ERR_KRL_BAD_MAGIC: + return "KRL file has invalid magic number"; + case SSH_ERR_KEY_REVOKED: + return "Key is revoked"; + case SSH_ERR_CONN_CLOSED: + return "Connection closed"; + case SSH_ERR_CONN_TIMEOUT: + return "Connection timed out"; + case SSH_ERR_CONN_CORRUPT: + return "Connection corrupted"; + case SSH_ERR_PROTOCOL_ERROR: + return "Protocol error"; + case SSH_ERR_KEY_LENGTH: + return "Invalid key length"; + case SSH_ERR_NUMBER_TOO_LARGE: + return "number is too large"; + case SSH_ERR_SIGN_ALG_UNSUPPORTED: + return "signature algorithm not supported"; + case SSH_ERR_FEATURE_UNSUPPORTED: + return "requested feature not supported"; + case SSH_ERR_DEVICE_NOT_FOUND: + return "device not found"; + default: + return "unknown error"; + } +} diff --git a/aosp/external/openssh/ssherr.h b/aosp/external/openssh/ssherr.h new file mode 100644 index 0000000000000000000000000000000000000000..085e752744d8f16cca2aad87c88bd84e6ba69319 --- /dev/null +++ b/aosp/external/openssh/ssherr.h @@ -0,0 +1,89 @@ +/* $OpenBSD: ssherr.h,v 1.8 2020/01/25 23:13:09 djm Exp $ */ +/* + * Copyright (c) 2011 Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SSHERR_H +#define _SSHERR_H + +/* XXX are these too granular? not granular enough? I can't decide - djm */ + +/* Error codes */ +#define SSH_ERR_SUCCESS 0 +#define SSH_ERR_INTERNAL_ERROR -1 +#define SSH_ERR_ALLOC_FAIL -2 +#define SSH_ERR_MESSAGE_INCOMPLETE -3 +#define SSH_ERR_INVALID_FORMAT -4 +#define SSH_ERR_BIGNUM_IS_NEGATIVE -5 +#define SSH_ERR_STRING_TOO_LARGE -6 +#define SSH_ERR_BIGNUM_TOO_LARGE -7 +#define SSH_ERR_ECPOINT_TOO_LARGE -8 +#define SSH_ERR_NO_BUFFER_SPACE -9 +#define SSH_ERR_INVALID_ARGUMENT -10 +#define SSH_ERR_KEY_BITS_MISMATCH -11 +#define SSH_ERR_EC_CURVE_INVALID -12 +#define SSH_ERR_KEY_TYPE_MISMATCH -13 +#define SSH_ERR_KEY_TYPE_UNKNOWN -14 /* XXX UNSUPPORTED? */ +#define SSH_ERR_EC_CURVE_MISMATCH -15 +#define SSH_ERR_EXPECTED_CERT -16 +#define SSH_ERR_KEY_LACKS_CERTBLOB -17 +#define SSH_ERR_KEY_CERT_UNKNOWN_TYPE -18 +#define SSH_ERR_KEY_CERT_INVALID_SIGN_KEY -19 +#define SSH_ERR_KEY_INVALID_EC_VALUE -20 +#define SSH_ERR_SIGNATURE_INVALID -21 +#define SSH_ERR_LIBCRYPTO_ERROR -22 +#define SSH_ERR_UNEXPECTED_TRAILING_DATA -23 +#define SSH_ERR_SYSTEM_ERROR -24 +#define SSH_ERR_KEY_CERT_INVALID -25 +#define SSH_ERR_AGENT_COMMUNICATION -26 +#define SSH_ERR_AGENT_FAILURE -27 +#define SSH_ERR_DH_GEX_OUT_OF_RANGE -28 +#define SSH_ERR_DISCONNECTED -29 +#define SSH_ERR_MAC_INVALID -30 +#define SSH_ERR_NO_CIPHER_ALG_MATCH -31 +#define SSH_ERR_NO_MAC_ALG_MATCH -32 +#define SSH_ERR_NO_COMPRESS_ALG_MATCH -33 +#define SSH_ERR_NO_KEX_ALG_MATCH -34 +#define SSH_ERR_NO_HOSTKEY_ALG_MATCH -35 +#define SSH_ERR_NO_HOSTKEY_LOADED -36 +#define SSH_ERR_PROTOCOL_MISMATCH -37 +#define SSH_ERR_NO_PROTOCOL_VERSION -38 +#define SSH_ERR_NEED_REKEY -39 +#define SSH_ERR_PASSPHRASE_TOO_SHORT -40 +#define SSH_ERR_FILE_CHANGED -41 +#define SSH_ERR_KEY_UNKNOWN_CIPHER -42 +#define SSH_ERR_KEY_WRONG_PASSPHRASE -43 +#define SSH_ERR_KEY_BAD_PERMISSIONS -44 +#define SSH_ERR_KEY_CERT_MISMATCH -45 +#define SSH_ERR_KEY_NOT_FOUND -46 +#define SSH_ERR_AGENT_NOT_PRESENT -47 +#define SSH_ERR_AGENT_NO_IDENTITIES -48 +#define SSH_ERR_BUFFER_READ_ONLY -49 +#define SSH_ERR_KRL_BAD_MAGIC -50 +#define SSH_ERR_KEY_REVOKED -51 +#define SSH_ERR_CONN_CLOSED -52 +#define SSH_ERR_CONN_TIMEOUT -53 +#define SSH_ERR_CONN_CORRUPT -54 +#define SSH_ERR_PROTOCOL_ERROR -55 +#define SSH_ERR_KEY_LENGTH -56 +#define SSH_ERR_NUMBER_TOO_LARGE -57 +#define SSH_ERR_SIGN_ALG_UNSUPPORTED -58 +#define SSH_ERR_FEATURE_UNSUPPORTED -59 +#define SSH_ERR_DEVICE_NOT_FOUND -60 + +/* Translate a numeric error code to a human-readable error string */ +const char *ssh_err(int n); + +#endif /* _SSHERR_H */ diff --git a/aosp/external/openssh/sshkey-xmss.c b/aosp/external/openssh/sshkey-xmss.c new file mode 100644 index 0000000000000000000000000000000000000000..f5235ef2fbd8acc8f145b4b6865af2f356f5ca3c --- /dev/null +++ b/aosp/external/openssh/sshkey-xmss.c @@ -0,0 +1,1113 @@ +/* $OpenBSD: sshkey-xmss.c,v 1.11 2021/04/03 06:18:41 djm Exp $ */ +/* + * Copyright (c) 2017 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +#ifdef WITH_XMSS + +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_FILE_H +# include +#endif + +#include "ssh2.h" +#include "ssherr.h" +#include "sshbuf.h" +#include "cipher.h" +#include "sshkey.h" +#include "sshkey-xmss.h" +#include "atomicio.h" +#include "log.h" + +#include "xmss_fast.h" + +/* opaque internal XMSS state */ +#define XMSS_MAGIC "xmss-state-v1" +#define XMSS_CIPHERNAME "aes256-gcm@openssh.com" +struct ssh_xmss_state { + xmss_params params; + u_int32_t n, w, h, k; + + bds_state bds; + u_char *stack; + u_int32_t stackoffset; + u_char *stacklevels; + u_char *auth; + u_char *keep; + u_char *th_nodes; + u_char *retain; + treehash_inst *treehash; + + u_int32_t idx; /* state read from file */ + u_int32_t maxidx; /* restricted # of signatures */ + int have_state; /* .state file exists */ + int lockfd; /* locked in sshkey_xmss_get_state() */ + u_char allow_update; /* allow sshkey_xmss_update_state() */ + char *enc_ciphername;/* encrypt state with cipher */ + u_char *enc_keyiv; /* encrypt state with key */ + u_int32_t enc_keyiv_len; /* length of enc_keyiv */ +}; + +int sshkey_xmss_init_bds_state(struct sshkey *); +int sshkey_xmss_init_enc_key(struct sshkey *, const char *); +void sshkey_xmss_free_bds(struct sshkey *); +int sshkey_xmss_get_state_from_file(struct sshkey *, const char *, + int *, int); +int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *, + struct sshbuf **); +int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *, + struct sshbuf **); +int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *); +int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *); + +#define PRINT(...) do { if (printerror) sshlog(__FILE__, __func__, __LINE__, \ + 0, SYSLOG_LEVEL_ERROR, NULL, __VA_ARGS__); } while (0) + +int +sshkey_xmss_init(struct sshkey *key, const char *name) +{ + struct ssh_xmss_state *state; + + if (key->xmss_state != NULL) + return SSH_ERR_INVALID_FORMAT; + if (name == NULL) + return SSH_ERR_INVALID_FORMAT; + state = calloc(sizeof(struct ssh_xmss_state), 1); + if (state == NULL) + return SSH_ERR_ALLOC_FAIL; + if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) { + state->n = 32; + state->w = 16; + state->h = 10; + } else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) { + state->n = 32; + state->w = 16; + state->h = 16; + } else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) { + state->n = 32; + state->w = 16; + state->h = 20; + } else { + free(state); + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + if ((key->xmss_name = strdup(name)) == NULL) { + free(state); + return SSH_ERR_ALLOC_FAIL; + } + state->k = 2; /* XXX hardcoded */ + state->lockfd = -1; + if (xmss_set_params(&state->params, state->n, state->h, state->w, + state->k) != 0) { + free(state); + return SSH_ERR_INVALID_FORMAT; + } + key->xmss_state = state; + return 0; +} + +void +sshkey_xmss_free_state(struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + sshkey_xmss_free_bds(key); + if (state) { + if (state->enc_keyiv) { + explicit_bzero(state->enc_keyiv, state->enc_keyiv_len); + free(state->enc_keyiv); + } + free(state->enc_ciphername); + free(state); + } + key->xmss_state = NULL; +} + +#define SSH_XMSS_K2_MAGIC "k=2" +#define num_stack(x) ((x->h+1)*(x->n)) +#define num_stacklevels(x) (x->h+1) +#define num_auth(x) ((x->h)*(x->n)) +#define num_keep(x) ((x->h >> 1)*(x->n)) +#define num_th_nodes(x) ((x->h - x->k)*(x->n)) +#define num_retain(x) (((1ULL << x->k) - x->k - 1) * (x->n)) +#define num_treehash(x) ((x->h) - (x->k)) + +int +sshkey_xmss_init_bds_state(struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + u_int32_t i; + + state->stackoffset = 0; + if ((state->stack = calloc(num_stack(state), 1)) == NULL || + (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL || + (state->auth = calloc(num_auth(state), 1)) == NULL || + (state->keep = calloc(num_keep(state), 1)) == NULL || + (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL || + (state->retain = calloc(num_retain(state), 1)) == NULL || + (state->treehash = calloc(num_treehash(state), + sizeof(treehash_inst))) == NULL) { + sshkey_xmss_free_bds(key); + return SSH_ERR_ALLOC_FAIL; + } + for (i = 0; i < state->h - state->k; i++) + state->treehash[i].node = &state->th_nodes[state->n*i]; + xmss_set_bds_state(&state->bds, state->stack, state->stackoffset, + state->stacklevels, state->auth, state->keep, state->treehash, + state->retain, 0); + return 0; +} + +void +sshkey_xmss_free_bds(struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return; + free(state->stack); + free(state->stacklevels); + free(state->auth); + free(state->keep); + free(state->th_nodes); + free(state->retain); + free(state->treehash); + state->stack = NULL; + state->stacklevels = NULL; + state->auth = NULL; + state->keep = NULL; + state->th_nodes = NULL; + state->retain = NULL; + state->treehash = NULL; +} + +void * +sshkey_xmss_params(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return NULL; + return &state->params; +} + +void * +sshkey_xmss_bds_state(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return NULL; + return &state->bds; +} + +int +sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (lenp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (state == NULL) + return SSH_ERR_INVALID_FORMAT; + *lenp = 4 + state->n + + state->params.wots_par.keysize + + state->h * state->n; + return 0; +} + +size_t +sshkey_xmss_pklen(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return 0; + return state->n * 2; +} + +size_t +sshkey_xmss_sklen(const struct sshkey *key) +{ + struct ssh_xmss_state *state = key->xmss_state; + + if (state == NULL) + return 0; + return state->n * 4 + 4; +} + +int +sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername) +{ + struct ssh_xmss_state *state = k->xmss_state; + const struct sshcipher *cipher; + size_t keylen = 0, ivlen = 0; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((cipher = cipher_by_name(ciphername)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((state->enc_ciphername = strdup(ciphername)) == NULL) + return SSH_ERR_ALLOC_FAIL; + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + state->enc_keyiv_len = keylen + ivlen; + if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) { + free(state->enc_ciphername); + state->enc_ciphername = NULL; + return SSH_ERR_ALLOC_FAIL; + } + arc4random_buf(state->enc_keyiv, state->enc_keyiv_len); + return 0; +} + +int +sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + int r; + + if (state == NULL || state->enc_keyiv == NULL || + state->enc_ciphername == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 || + (r = sshbuf_put_string(b, state->enc_keyiv, + state->enc_keyiv_len)) != 0) + return r; + return 0; +} + +int +sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + size_t len; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 || + (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0) + return r; + state->enc_keyiv_len = len; + return 0; +} + +int +sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b, + enum sshkey_serialize_rep opts) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_char have_info = 1; + u_int32_t idx; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (opts != SSHKEY_SERIALIZE_INFO) + return 0; + idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx; + if ((r = sshbuf_put_u8(b, have_info)) != 0 || + (r = sshbuf_put_u32(b, idx)) != 0 || + (r = sshbuf_put_u32(b, state->maxidx)) != 0) + return r; + return 0; +} + +int +sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_char have_info; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + /* optional */ + if (sshbuf_len(b) == 0) + return 0; + if ((r = sshbuf_get_u8(b, &have_info)) != 0) + return r; + if (have_info != 1) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_get_u32(b, &state->idx)) != 0 || + (r = sshbuf_get_u32(b, &state->maxidx)) != 0) + return r; + return 0; +} + +int +sshkey_xmss_generate_private_key(struct sshkey *k, u_int bits) +{ + int r; + const char *name; + + if (bits == 10) { + name = XMSS_SHA2_256_W16_H10_NAME; + } else if (bits == 16) { + name = XMSS_SHA2_256_W16_H16_NAME; + } else if (bits == 20) { + name = XMSS_SHA2_256_W16_H20_NAME; + } else { + name = XMSS_DEFAULT_NAME; + } + if ((r = sshkey_xmss_init(k, name)) != 0 || + (r = sshkey_xmss_init_bds_state(k)) != 0 || + (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0) + return r; + if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL || + (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } + xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k), + sshkey_xmss_params(k)); + return 0; +} + +int +sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename, + int *have_file, int printerror) +{ + struct sshbuf *b = NULL, *enc = NULL; + int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1; + u_int32_t len; + unsigned char buf[4], *data = NULL; + + *have_file = 0; + if ((fd = open(filename, O_RDONLY)) >= 0) { + *have_file = 1; + if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) { + PRINT("corrupt state file: %s", filename); + goto done; + } + len = PEEK_U32(buf); + if ((data = calloc(len, 1)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if (atomicio(read, fd, data, len) != len) { + PRINT("cannot read blob: %s", filename); + goto done; + } + if ((enc = sshbuf_from(data, len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + sshkey_xmss_free_bds(k); + if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) { + ret = r; + goto done; + } + if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) { + ret = r; + goto done; + } + ret = 0; + } +done: + if (fd != -1) + close(fd); + free(data); + sshbuf_free(enc); + sshbuf_free(b); + return ret; +} + +int +sshkey_xmss_get_state(const struct sshkey *k, int printerror) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_int32_t idx = 0; + char *filename = NULL; + char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL; + int lockfd = -1, have_state = 0, have_ostate, tries = 0; + int ret = SSH_ERR_INVALID_ARGUMENT, r; + + if (state == NULL) + goto done; + /* + * If maxidx is set, then we are allowed a limited number + * of signatures, but don't need to access the disk. + * Otherwise we need to deal with the on-disk state. + */ + if (state->maxidx) { + /* xmss_sk always contains the current state */ + idx = PEEK_U32(k->xmss_sk); + if (idx < state->maxidx) { + state->allow_update = 1; + return 0; + } + return SSH_ERR_INVALID_ARGUMENT; + } + if ((filename = k->xmss_filename) == NULL) + goto done; + if (asprintf(&lockfile, "%s.lock", filename) == -1 || + asprintf(&statefile, "%s.state", filename) == -1 || + asprintf(&ostatefile, "%s.ostate", filename) == -1) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) == -1) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("cannot open/create: %s", lockfile); + goto done; + } + while (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { + if (errno != EWOULDBLOCK) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("cannot lock: %s", lockfile); + goto done; + } + if (++tries > 10) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("giving up on: %s", lockfile); + goto done; + } + usleep(1000*100*tries); + } + /* XXX no longer const */ + if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k, + statefile, &have_state, printerror)) != 0) { + if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k, + ostatefile, &have_ostate, printerror)) == 0) { + state->allow_update = 1; + r = sshkey_xmss_forward_state(k, 1); + state->idx = PEEK_U32(k->xmss_sk); + state->allow_update = 0; + } + } + if (!have_state && !have_ostate) { + /* check that bds state is initialized */ + if (state->bds.auth == NULL) + goto done; + PRINT("start from scratch idx 0: %u", state->idx); + } else if (r != 0) { + ret = r; + goto done; + } + if (state->idx + 1 < state->idx) { + PRINT("state wrap: %u", state->idx); + goto done; + } + state->have_state = have_state; + state->lockfd = lockfd; + state->allow_update = 1; + lockfd = -1; + ret = 0; +done: + if (lockfd != -1) + close(lockfd); + free(lockfile); + free(statefile); + free(ostatefile); + return ret; +} + +int +sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_char *sig = NULL; + size_t required_siglen; + unsigned long long smlen; + u_char data; + int ret, r; + + if (state == NULL || !state->allow_update) + return SSH_ERR_INVALID_ARGUMENT; + if (reserve == 0) + return SSH_ERR_INVALID_ARGUMENT; + if (state->idx + reserve <= state->idx) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0) + return r; + if ((sig = malloc(required_siglen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + while (reserve-- > 0) { + state->idx = PEEK_U32(k->xmss_sk); + smlen = required_siglen; + if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k), + sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) { + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + } + free(sig); + return r; +} + +int +sshkey_xmss_update_state(const struct sshkey *k, int printerror) +{ + struct ssh_xmss_state *state = k->xmss_state; + struct sshbuf *b = NULL, *enc = NULL; + u_int32_t idx = 0; + unsigned char buf[4]; + char *filename = NULL; + char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL; + int fd = -1; + int ret = SSH_ERR_INVALID_ARGUMENT; + + if (state == NULL || !state->allow_update) + return ret; + if (state->maxidx) { + /* no update since the number of signatures is limited */ + ret = 0; + goto done; + } + idx = PEEK_U32(k->xmss_sk); + if (idx == state->idx) { + /* no signature happened, no need to update */ + ret = 0; + goto done; + } else if (idx != state->idx + 1) { + PRINT("more than one signature happened: idx %u state %u", + idx, state->idx); + goto done; + } + state->idx = idx; + if ((filename = k->xmss_filename) == NULL) + goto done; + if (asprintf(&statefile, "%s.state", filename) == -1 || + asprintf(&ostatefile, "%s.ostate", filename) == -1 || + asprintf(&nstatefile, "%s.nstate", filename) == -1) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + unlink(nstatefile); + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto done; + } + if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) { + PRINT("SERLIALIZE FAILED: %d", ret); + goto done; + } + if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) { + PRINT("ENCRYPT FAILED: %d", ret); + goto done; + } + if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) == -1) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("open new state file: %s", nstatefile); + goto done; + } + POKE_U32(buf, sshbuf_len(enc)); + if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("write new state file hdr: %s", nstatefile); + close(fd); + goto done; + } + if (atomicio(vwrite, fd, sshbuf_mutable_ptr(enc), sshbuf_len(enc)) != + sshbuf_len(enc)) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("write new state file data: %s", nstatefile); + close(fd); + goto done; + } + if (fsync(fd) == -1) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("sync new state file: %s", nstatefile); + close(fd); + goto done; + } + if (close(fd) == -1) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("close new state file: %s", nstatefile); + goto done; + } + if (state->have_state) { + unlink(ostatefile); + if (link(statefile, ostatefile)) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("backup state %s to %s", statefile, ostatefile); + goto done; + } + } + if (rename(nstatefile, statefile) == -1) { + ret = SSH_ERR_SYSTEM_ERROR; + PRINT("rename %s to %s", nstatefile, statefile); + goto done; + } + ret = 0; +done: + if (state->lockfd != -1) { + close(state->lockfd); + state->lockfd = -1; + } + if (nstatefile) + unlink(nstatefile); + free(statefile); + free(ostatefile); + free(nstatefile); + sshbuf_free(b); + sshbuf_free(enc); + return ret; +} + +int +sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + treehash_inst *th; + u_int32_t i, node; + int r; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (state->stack == NULL) + return SSH_ERR_INVALID_ARGUMENT; + state->stackoffset = state->bds.stackoffset; /* copy back */ + if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 || + (r = sshbuf_put_u32(b, state->idx)) != 0 || + (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 || + (r = sshbuf_put_u32(b, state->stackoffset)) != 0 || + (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 || + (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 || + (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 || + (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 || + (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 || + (r = sshbuf_put_u32(b, num_treehash(state))) != 0) + return r; + for (i = 0; i < num_treehash(state); i++) { + th = &state->treehash[i]; + node = th->node - state->th_nodes; + if ((r = sshbuf_put_u32(b, th->h)) != 0 || + (r = sshbuf_put_u32(b, th->next_idx)) != 0 || + (r = sshbuf_put_u32(b, th->stackusage)) != 0 || + (r = sshbuf_put_u8(b, th->completed)) != 0 || + (r = sshbuf_put_u32(b, node)) != 0) + return r; + } + return 0; +} + +int +sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b, + enum sshkey_serialize_rep opts) +{ + struct ssh_xmss_state *state = k->xmss_state; + int r = SSH_ERR_INVALID_ARGUMENT; + u_char have_stack, have_filename, have_enc; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshbuf_put_u8(b, opts)) != 0) + return r; + switch (opts) { + case SSHKEY_SERIALIZE_STATE: + r = sshkey_xmss_serialize_state(k, b); + break; + case SSHKEY_SERIALIZE_FULL: + if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0) + return r; + r = sshkey_xmss_serialize_state(k, b); + break; + case SSHKEY_SERIALIZE_SHIELD: + /* all of stack/filename/enc are optional */ + have_stack = state->stack != NULL; + if ((r = sshbuf_put_u8(b, have_stack)) != 0) + return r; + if (have_stack) { + state->idx = PEEK_U32(k->xmss_sk); /* update */ + if ((r = sshkey_xmss_serialize_state(k, b)) != 0) + return r; + } + have_filename = k->xmss_filename != NULL; + if ((r = sshbuf_put_u8(b, have_filename)) != 0) + return r; + if (have_filename && + (r = sshbuf_put_cstring(b, k->xmss_filename)) != 0) + return r; + have_enc = state->enc_keyiv != NULL; + if ((r = sshbuf_put_u8(b, have_enc)) != 0) + return r; + if (have_enc && + (r = sshkey_xmss_serialize_enc_key(k, b)) != 0) + return r; + if ((r = sshbuf_put_u32(b, state->maxidx)) != 0 || + (r = sshbuf_put_u8(b, state->allow_update)) != 0) + return r; + break; + case SSHKEY_SERIALIZE_DEFAULT: + r = 0; + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + break; + } + return r; +} + +int +sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + treehash_inst *th; + u_int32_t i, lh, node; + size_t ls, lsl, la, lk, ln, lr; + char *magic; + int r = SSH_ERR_INTERNAL_ERROR; + + if (state == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (k->xmss_sk == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((state->treehash = calloc(num_treehash(state), + sizeof(treehash_inst))) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 || + (r = sshbuf_get_u32(b, &state->idx)) != 0 || + (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 || + (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 || + (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 || + (r = sshbuf_get_string(b, &state->auth, &la)) != 0 || + (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 || + (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 || + (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 || + (r = sshbuf_get_u32(b, &lh)) != 0) + goto out; + if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + /* XXX check stackoffset */ + if (ls != num_stack(state) || + lsl != num_stacklevels(state) || + la != num_auth(state) || + lk != num_keep(state) || + ln != num_th_nodes(state) || + lr != num_retain(state) || + lh != num_treehash(state)) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + for (i = 0; i < num_treehash(state); i++) { + th = &state->treehash[i]; + if ((r = sshbuf_get_u32(b, &th->h)) != 0 || + (r = sshbuf_get_u32(b, &th->next_idx)) != 0 || + (r = sshbuf_get_u32(b, &th->stackusage)) != 0 || + (r = sshbuf_get_u8(b, &th->completed)) != 0 || + (r = sshbuf_get_u32(b, &node)) != 0) + goto out; + if (node < num_th_nodes(state)) + th->node = &state->th_nodes[node]; + } + POKE_U32(k->xmss_sk, state->idx); + xmss_set_bds_state(&state->bds, state->stack, state->stackoffset, + state->stacklevels, state->auth, state->keep, state->treehash, + state->retain, 0); + /* success */ + r = 0; + out: + free(magic); + return r; +} + +int +sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b) +{ + struct ssh_xmss_state *state = k->xmss_state; + enum sshkey_serialize_rep opts; + u_char have_state, have_stack, have_filename, have_enc; + int r; + + if ((r = sshbuf_get_u8(b, &have_state)) != 0) + return r; + + opts = have_state; + switch (opts) { + case SSHKEY_SERIALIZE_DEFAULT: + r = 0; + break; + case SSHKEY_SERIALIZE_SHIELD: + if ((r = sshbuf_get_u8(b, &have_stack)) != 0) + return r; + if (have_stack && + (r = sshkey_xmss_deserialize_state(k, b)) != 0) + return r; + if ((r = sshbuf_get_u8(b, &have_filename)) != 0) + return r; + if (have_filename && + (r = sshbuf_get_cstring(b, &k->xmss_filename, NULL)) != 0) + return r; + if ((r = sshbuf_get_u8(b, &have_enc)) != 0) + return r; + if (have_enc && + (r = sshkey_xmss_deserialize_enc_key(k, b)) != 0) + return r; + if ((r = sshbuf_get_u32(b, &state->maxidx)) != 0 || + (r = sshbuf_get_u8(b, &state->allow_update)) != 0) + return r; + break; + case SSHKEY_SERIALIZE_STATE: + if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) + return r; + break; + case SSHKEY_SERIALIZE_FULL: + if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 || + (r = sshkey_xmss_deserialize_state(k, b)) != 0) + return r; + break; + default: + r = SSH_ERR_INVALID_FORMAT; + break; + } + return r; +} + +int +sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b, + struct sshbuf **retp) +{ + struct ssh_xmss_state *state = k->xmss_state; + struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL; + struct sshcipher_ctx *ciphercontext = NULL; + const struct sshcipher *cipher; + u_char *cp, *key, *iv = NULL; + size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen; + int r = SSH_ERR_INTERNAL_ERROR; + + if (retp != NULL) + *retp = NULL; + if (state == NULL || + state->enc_keyiv == NULL || + state->enc_ciphername == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + blocksize = cipher_blocksize(cipher); + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if (state->enc_keyiv_len != keylen + ivlen) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + key = state->enc_keyiv; + if ((encrypted = sshbuf_new()) == NULL || + (encoded = sshbuf_new()) == NULL || + (padded = sshbuf_new()) == NULL || + (iv = malloc(ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* replace first 4 bytes of IV with index to ensure uniqueness */ + memcpy(iv, key + keylen, ivlen); + POKE_U32(iv, state->idx); + + if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 || + (r = sshbuf_put_u32(encoded, state->idx)) != 0) + goto out; + + /* padded state will be encrypted */ + if ((r = sshbuf_putb(padded, b)) != 0) + goto out; + i = 0; + while (sshbuf_len(padded) % blocksize) { + if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0) + goto out; + } + encrypted_len = sshbuf_len(padded); + + /* header including the length of state is used as AAD */ + if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0) + goto out; + aadlen = sshbuf_len(encoded); + + /* concat header and state */ + if ((r = sshbuf_putb(encoded, padded)) != 0) + goto out; + + /* reserve space for encryption of encoded data plus auth tag */ + /* encrypt at offset addlen */ + if ((r = sshbuf_reserve(encrypted, + encrypted_len + aadlen + authlen, &cp)) != 0 || + (r = cipher_init(&ciphercontext, cipher, key, keylen, + iv, ivlen, 1)) != 0 || + (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded), + encrypted_len, aadlen, authlen)) != 0) + goto out; + + /* success */ + r = 0; + out: + if (retp != NULL) { + *retp = encrypted; + encrypted = NULL; + } + sshbuf_free(padded); + sshbuf_free(encoded); + sshbuf_free(encrypted); + cipher_free(ciphercontext); + free(iv); + return r; +} + +int +sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded, + struct sshbuf **retp) +{ + struct ssh_xmss_state *state = k->xmss_state; + struct sshbuf *copy = NULL, *decrypted = NULL; + struct sshcipher_ctx *ciphercontext = NULL; + const struct sshcipher *cipher = NULL; + u_char *key, *iv = NULL, *dp; + size_t keylen, ivlen, authlen, aadlen; + u_int blocksize, encrypted_len, index; + int r = SSH_ERR_INTERNAL_ERROR; + + if (retp != NULL) + *retp = NULL; + if (state == NULL || + state->enc_keyiv == NULL || + state->enc_ciphername == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + blocksize = cipher_blocksize(cipher); + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if (state->enc_keyiv_len != keylen + ivlen) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + key = state->enc_keyiv; + + if ((copy = sshbuf_fromb(encoded)) == NULL || + (decrypted = sshbuf_new()) == NULL || + (iv = malloc(ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* check magic */ + if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) || + memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* parse public portion */ + if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 || + (r = sshbuf_get_u32(encoded, &index)) != 0 || + (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0) + goto out; + + /* check size of encrypted key blob */ + if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* check that an appropriate amount of auth data is present */ + if (sshbuf_len(encoded) < authlen || + sshbuf_len(encoded) - authlen < encrypted_len) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + aadlen = sshbuf_len(copy) - sshbuf_len(encoded); + + /* replace first 4 bytes of IV with index to ensure uniqueness */ + memcpy(iv, key + keylen, ivlen); + POKE_U32(iv, index); + + /* decrypt private state of key */ + if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 || + (r = cipher_init(&ciphercontext, cipher, key, keylen, + iv, ivlen, 0)) != 0 || + (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy), + encrypted_len, aadlen, authlen)) != 0) + goto out; + + /* there should be no trailing data */ + if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0) + goto out; + if (sshbuf_len(encoded) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* remove AAD */ + if ((r = sshbuf_consume(decrypted, aadlen)) != 0) + goto out; + /* XXX encrypted includes unchecked padding */ + + /* success */ + r = 0; + if (retp != NULL) { + *retp = decrypted; + decrypted = NULL; + } + out: + cipher_free(ciphercontext); + sshbuf_free(copy); + sshbuf_free(decrypted); + free(iv); + return r; +} + +u_int32_t +sshkey_xmss_signatures_left(const struct sshkey *k) +{ + struct ssh_xmss_state *state = k->xmss_state; + u_int32_t idx; + + if (sshkey_type_plain(k->type) == KEY_XMSS && state && + state->maxidx) { + idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx; + if (idx < state->maxidx) + return state->maxidx - idx; + } + return 0; +} + +int +sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign) +{ + struct ssh_xmss_state *state = k->xmss_state; + + if (sshkey_type_plain(k->type) != KEY_XMSS) + return SSH_ERR_INVALID_ARGUMENT; + if (maxsign == 0) + return 0; + if (state->idx + maxsign < state->idx) + return SSH_ERR_INVALID_ARGUMENT; + state->maxidx = state->idx + maxsign; + return 0; +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/sshkey-xmss.h b/aosp/external/openssh/sshkey-xmss.h new file mode 100644 index 0000000000000000000000000000000000000000..32a12be620b115a7f2b73a517d7f3c8e18734a8e --- /dev/null +++ b/aosp/external/openssh/sshkey-xmss.h @@ -0,0 +1,56 @@ +/* $OpenBSD: sshkey-xmss.h,v 1.3 2021/04/03 06:18:41 djm Exp $ */ +/* + * Copyright (c) 2017 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SSHKEY_XMSS_H +#define SSHKEY_XMSS_H + +#define XMSS_SHA2_256_W16_H10_NAME "XMSS_SHA2-256_W16_H10" +#define XMSS_SHA2_256_W16_H16_NAME "XMSS_SHA2-256_W16_H16" +#define XMSS_SHA2_256_W16_H20_NAME "XMSS_SHA2-256_W16_H20" +#define XMSS_DEFAULT_NAME XMSS_SHA2_256_W16_H10_NAME + +size_t sshkey_xmss_pklen(const struct sshkey *); +size_t sshkey_xmss_sklen(const struct sshkey *); +int sshkey_xmss_init(struct sshkey *, const char *); +void sshkey_xmss_free_state(struct sshkey *); +int sshkey_xmss_generate_private_key(struct sshkey *, u_int); +int sshkey_xmss_serialize_state(const struct sshkey *, struct sshbuf *); +int sshkey_xmss_serialize_state_opt(const struct sshkey *, struct sshbuf *, + enum sshkey_serialize_rep); +int sshkey_xmss_serialize_pk_info(const struct sshkey *, struct sshbuf *, + enum sshkey_serialize_rep); +int sshkey_xmss_deserialize_state(struct sshkey *, struct sshbuf *); +int sshkey_xmss_deserialize_state_opt(struct sshkey *, struct sshbuf *); +int sshkey_xmss_deserialize_pk_info(struct sshkey *, struct sshbuf *); + +int sshkey_xmss_siglen(const struct sshkey *, size_t *); +void *sshkey_xmss_params(const struct sshkey *); +void *sshkey_xmss_bds_state(const struct sshkey *); +int sshkey_xmss_get_state(const struct sshkey *, int); +int sshkey_xmss_enable_maxsign(struct sshkey *, u_int32_t); +int sshkey_xmss_forward_state(const struct sshkey *, u_int32_t); +int sshkey_xmss_update_state(const struct sshkey *, int); +u_int32_t sshkey_xmss_signatures_left(const struct sshkey *); + +#endif /* SSHKEY_XMSS_H */ diff --git a/aosp/external/openssh/sshkey.c b/aosp/external/openssh/sshkey.c new file mode 100644 index 0000000000000000000000000000000000000000..a9e834841940387b1e0655946f77742c1ce2d3e1 --- /dev/null +++ b/aosp/external/openssh/sshkey.c @@ -0,0 +1,4862 @@ +/* $OpenBSD: sshkey.c,v 1.120 2022/01/06 22:05:42 djm Exp $ */ +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2008 Alexander von Gernler. All rights reserved. + * Copyright (c) 2010,2011 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#endif + +#include "crypto_api.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UTIL_H +#include +#endif /* HAVE_UTIL_H */ + +#include "ssh2.h" +#include "ssherr.h" +#include "misc.h" +#include "sshbuf.h" +#include "cipher.h" +#include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" +#include "match.h" +#include "ssh-sk.h" + +#ifdef WITH_XMSS +#include "sshkey-xmss.h" +#include "xmss_fast.h" +#endif + +#include "openbsd-compat/openssl-compat.h" + +/* openssh private key file format */ +#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" +#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" +#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1) +#define MARK_END_LEN (sizeof(MARK_END) - 1) +#define KDFNAME "bcrypt" +#define AUTH_MAGIC "openssh-key-v1" +#define SALT_LEN 16 +#define DEFAULT_CIPHERNAME "aes256-ctr" +#define DEFAULT_ROUNDS 16 + +/* Version identification string for SSH v1 identity files. */ +#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" + +/* + * Constants relating to "shielding" support; protection of keys expected + * to remain in memory for long durations + */ +#define SSHKEY_SHIELD_PREKEY_LEN (16 * 1024) +#define SSHKEY_SHIELD_CIPHER "aes256-ctr" /* XXX want AES-EME* */ +#define SSHKEY_SHIELD_PREKEY_HASH SSH_DIGEST_SHA512 + +int sshkey_private_serialize_opt(struct sshkey *key, + struct sshbuf *buf, enum sshkey_serialize_rep); +static int sshkey_from_blob_internal(struct sshbuf *buf, + struct sshkey **keyp, int allow_cert); + +/* Supported key types */ +struct keytype { + const char *name; + const char *shortname; + const char *sigalg; + int type; + int nid; + int cert; + int sigonly; +}; +static const struct keytype keytypes[] = { + { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 }, + { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL, + KEY_ED25519_CERT, 0, 1, 0 }, +#ifdef ENABLE_SK + { "sk-ssh-ed25519@openssh.com", "ED25519-SK", NULL, + KEY_ED25519_SK, 0, 0, 0 }, + { "sk-ssh-ed25519-cert-v01@openssh.com", "ED25519-SK-CERT", NULL, + KEY_ED25519_SK_CERT, 0, 1, 0 }, +#endif +#ifdef WITH_XMSS + { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 }, + { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL, + KEY_XMSS_CERT, 0, 1, 0 }, +#endif /* WITH_XMSS */ +#ifdef WITH_OPENSSL + { "ssh-rsa", "RSA", NULL, KEY_RSA, 0, 0, 0 }, + { "rsa-sha2-256", "RSA", NULL, KEY_RSA, 0, 0, 1 }, + { "rsa-sha2-512", "RSA", NULL, KEY_RSA, 0, 0, 1 }, + { "ssh-dss", "DSA", NULL, KEY_DSA, 0, 0, 0 }, +# ifdef OPENSSL_HAS_ECC + { "ecdsa-sha2-nistp256", "ECDSA", NULL, + KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 }, + { "ecdsa-sha2-nistp384", "ECDSA", NULL, + KEY_ECDSA, NID_secp384r1, 0, 0 }, +# ifdef OPENSSL_HAS_NISTP521 + { "ecdsa-sha2-nistp521", "ECDSA", NULL, + KEY_ECDSA, NID_secp521r1, 0, 0 }, +# endif /* OPENSSL_HAS_NISTP521 */ +# ifdef ENABLE_SK + { "sk-ecdsa-sha2-nistp256@openssh.com", "ECDSA-SK", NULL, + KEY_ECDSA_SK, NID_X9_62_prime256v1, 0, 0 }, + { "webauthn-sk-ecdsa-sha2-nistp256@openssh.com", "ECDSA-SK", NULL, + KEY_ECDSA_SK, NID_X9_62_prime256v1, 0, 1 }, +# endif /* ENABLE_SK */ +# endif /* OPENSSL_HAS_ECC */ + { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL, + KEY_RSA_CERT, 0, 1, 0 }, + { "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT", + "rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 }, + { "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT", + "rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 }, + { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL, + KEY_DSA_CERT, 0, 1, 0 }, +# ifdef OPENSSL_HAS_ECC + { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", NULL, + KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 }, + { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", NULL, + KEY_ECDSA_CERT, NID_secp384r1, 1, 0 }, +# ifdef OPENSSL_HAS_NISTP521 + { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL, + KEY_ECDSA_CERT, NID_secp521r1, 1, 0 }, +# endif /* OPENSSL_HAS_NISTP521 */ +# ifdef ENABLE_SK + { "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-SK-CERT", NULL, + KEY_ECDSA_SK_CERT, NID_X9_62_prime256v1, 1, 0 }, +# endif /* ENABLE_SK */ +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + { NULL, NULL, NULL, -1, -1, 0, 0 } +}; + +const char * +sshkey_type(const struct sshkey *k) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type == k->type) + return kt->shortname; + } + return "unknown"; +} + +static const char * +sshkey_ssh_name_from_type_nid(int type, int nid) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type == type && (kt->nid == 0 || kt->nid == nid)) + return kt->name; + } + return "ssh-unknown"; +} + +int +sshkey_type_is_cert(int type) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->type == type) + return kt->cert; + } + return 0; +} + +const char * +sshkey_ssh_name(const struct sshkey *k) +{ + return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid); +} + +const char * +sshkey_ssh_name_plain(const struct sshkey *k) +{ + return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type), + k->ecdsa_nid); +} + +int +sshkey_type_from_name(const char *name) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + /* Only allow shortname matches for plain key types */ + if ((kt->name != NULL && strcmp(name, kt->name) == 0) || + (!kt->cert && strcasecmp(kt->shortname, name) == 0)) + return kt->type; + } + return KEY_UNSPEC; +} + +static int +key_type_is_ecdsa_variant(int type) +{ + switch (type) { + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + return 1; + } + return 0; +} + +int +sshkey_ecdsa_nid_from_name(const char *name) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (!key_type_is_ecdsa_variant(kt->type)) + continue; + if (kt->name != NULL && strcmp(name, kt->name) == 0) + return kt->nid; + } + return -1; +} + +int +sshkey_match_keyname_to_sigalgs(const char *keyname, const char *sigalgs) +{ + int ktype; + + if (sigalgs == NULL || *sigalgs == '\0' || + (ktype = sshkey_type_from_name(keyname)) == KEY_UNSPEC) + return 0; + else if (ktype == KEY_RSA) { + return match_pattern_list("ssh-rsa", sigalgs, 0) == 1 || + match_pattern_list("rsa-sha2-256", sigalgs, 0) == 1 || + match_pattern_list("rsa-sha2-512", sigalgs, 0) == 1; + } else if (ktype == KEY_RSA_CERT) { + return match_pattern_list("ssh-rsa-cert-v01@openssh.com", + sigalgs, 0) == 1 || + match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", + sigalgs, 0) == 1 || + match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", + sigalgs, 0) == 1; + } else + return match_pattern_list(keyname, sigalgs, 0) == 1; +} + +char * +sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) +{ + char *tmp, *ret = NULL; + size_t nlen, rlen = 0; + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->name == NULL) + continue; + if (!include_sigonly && kt->sigonly) + continue; + if ((certs_only && !kt->cert) || (plain_only && kt->cert)) + continue; + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(kt->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; + memcpy(ret + rlen, kt->name, nlen + 1); + rlen += nlen; + } + return ret; +} + +int +sshkey_names_valid2(const char *names, int allow_wildcard) +{ + char *s, *cp, *p; + const struct keytype *kt; + int type; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + if ((s = cp = strdup(names)) == NULL) + return 0; + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + type = sshkey_type_from_name(p); + if (type == KEY_UNSPEC) { + if (allow_wildcard) { + /* + * Try matching key types against the string. + * If any has a positive or negative match then + * the component is accepted. + */ + for (kt = keytypes; kt->type != -1; kt++) { + if (match_pattern_list(kt->name, + p, 0) != 0) + break; + } + if (kt->type != -1) + continue; + } + free(s); + return 0; + } + } + free(s); + return 1; +} + +u_int +sshkey_size(const struct sshkey *k) +{ +#ifdef WITH_OPENSSL + const BIGNUM *rsa_n, *dsa_p; +#endif /* WITH_OPENSSL */ + + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: + if (k->rsa == NULL) + return 0; + RSA_get0_key(k->rsa, &rsa_n, NULL, NULL); + return BN_num_bits(rsa_n); + case KEY_DSA: + case KEY_DSA_CERT: + if (k->dsa == NULL) + return 0; + DSA_get0_pqg(k->dsa, &dsa_p, NULL, NULL); + return BN_num_bits(dsa_p); + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + return sshkey_curve_nid_to_bits(k->ecdsa_nid); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: + return 256; /* XXX */ + } + return 0; +} + +static int +sshkey_type_is_valid_ca(int type) +{ + switch (type) { + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: + case KEY_ECDSA_SK: + case KEY_ED25519: + case KEY_ED25519_SK: + case KEY_XMSS: + return 1; + default: + return 0; + } +} + +int +sshkey_is_cert(const struct sshkey *k) +{ + if (k == NULL) + return 0; + return sshkey_type_is_cert(k->type); +} + +int +sshkey_is_sk(const struct sshkey *k) +{ + if (k == NULL) + return 0; + switch (sshkey_type_plain(k->type)) { + case KEY_ECDSA_SK: + case KEY_ED25519_SK: + return 1; + default: + return 0; + } +} + +/* Return the cert-less equivalent to a certified key type */ +int +sshkey_type_plain(int type) +{ + switch (type) { + case KEY_RSA_CERT: + return KEY_RSA; + case KEY_DSA_CERT: + return KEY_DSA; + case KEY_ECDSA_CERT: + return KEY_ECDSA; + case KEY_ECDSA_SK_CERT: + return KEY_ECDSA_SK; + case KEY_ED25519_CERT: + return KEY_ED25519; + case KEY_ED25519_SK_CERT: + return KEY_ED25519_SK; + case KEY_XMSS_CERT: + return KEY_XMSS; + default: + return type; + } +} + +#ifdef WITH_OPENSSL +/* XXX: these are really begging for a table-driven approach */ +int +sshkey_curve_name_to_nid(const char *name) +{ + if (strcmp(name, "nistp256") == 0) + return NID_X9_62_prime256v1; + else if (strcmp(name, "nistp384") == 0) + return NID_secp384r1; +# ifdef OPENSSL_HAS_NISTP521 + else if (strcmp(name, "nistp521") == 0) + return NID_secp521r1; +# endif /* OPENSSL_HAS_NISTP521 */ + else + return -1; +} + +u_int +sshkey_curve_nid_to_bits(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return 256; + case NID_secp384r1: + return 384; +# ifdef OPENSSL_HAS_NISTP521 + case NID_secp521r1: + return 521; +# endif /* OPENSSL_HAS_NISTP521 */ + default: + return 0; + } +} + +int +sshkey_ecdsa_bits_to_nid(int bits) +{ + switch (bits) { + case 256: + return NID_X9_62_prime256v1; + case 384: + return NID_secp384r1; +# ifdef OPENSSL_HAS_NISTP521 + case 521: + return NID_secp521r1; +# endif /* OPENSSL_HAS_NISTP521 */ + default: + return -1; + } +} + +const char * +sshkey_curve_nid_to_name(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return "nistp256"; + case NID_secp384r1: + return "nistp384"; +# ifdef OPENSSL_HAS_NISTP521 + case NID_secp521r1: + return "nistp521"; +# endif /* OPENSSL_HAS_NISTP521 */ + default: + return NULL; + } +} + +int +sshkey_ec_nid_to_hash_alg(int nid) +{ + int kbits = sshkey_curve_nid_to_bits(nid); + + if (kbits <= 0) + return -1; + + /* RFC5656 section 6.2.1 */ + if (kbits <= 256) + return SSH_DIGEST_SHA256; + else if (kbits <= 384) + return SSH_DIGEST_SHA384; + else + return SSH_DIGEST_SHA512; +} +#endif /* WITH_OPENSSL */ + +static void +cert_free(struct sshkey_cert *cert) +{ + u_int i; + + if (cert == NULL) + return; + sshbuf_free(cert->certblob); + sshbuf_free(cert->critical); + sshbuf_free(cert->extensions); + free(cert->key_id); + for (i = 0; i < cert->nprincipals; i++) + free(cert->principals[i]); + free(cert->principals); + sshkey_free(cert->signature_key); + free(cert->signature_type); + freezero(cert, sizeof(*cert)); +} + +static struct sshkey_cert * +cert_new(void) +{ + struct sshkey_cert *cert; + + if ((cert = calloc(1, sizeof(*cert))) == NULL) + return NULL; + if ((cert->certblob = sshbuf_new()) == NULL || + (cert->critical = sshbuf_new()) == NULL || + (cert->extensions = sshbuf_new()) == NULL) { + cert_free(cert); + return NULL; + } + cert->key_id = NULL; + cert->principals = NULL; + cert->signature_key = NULL; + cert->signature_type = NULL; + return cert; +} + +struct sshkey * +sshkey_new(int type) +{ + struct sshkey *k; +#ifdef WITH_OPENSSL + RSA *rsa; + DSA *dsa; +#endif /* WITH_OPENSSL */ + + if ((k = calloc(1, sizeof(*k))) == NULL) + return NULL; + k->type = type; + k->ecdsa = NULL; + k->ecdsa_nid = -1; + k->dsa = NULL; + k->rsa = NULL; + k->cert = NULL; + k->ed25519_sk = NULL; + k->ed25519_pk = NULL; + k->xmss_sk = NULL; + k->xmss_pk = NULL; + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: + if ((rsa = RSA_new()) == NULL) { + free(k); + return NULL; + } + k->rsa = rsa; + break; + case KEY_DSA: + case KEY_DSA_CERT: + if ((dsa = DSA_new()) == NULL) { + free(k); + return NULL; + } + k->dsa = dsa; + break; + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + /* Cannot do anything until we know the group */ + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + case KEY_XMSS: + case KEY_XMSS_CERT: + /* no need to prealloc */ + break; + case KEY_UNSPEC: + break; + default: + free(k); + return NULL; + } + + if (sshkey_is_cert(k)) { + if ((k->cert = cert_new()) == NULL) { + sshkey_free(k); + return NULL; + } + } + + return k; +} + +void +sshkey_free(struct sshkey *k) +{ + if (k == NULL) + return; + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: + RSA_free(k->rsa); + k->rsa = NULL; + break; + case KEY_DSA: + case KEY_DSA_CERT: + DSA_free(k->dsa); + k->dsa = NULL; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + free(k->sk_application); + sshbuf_free(k->sk_key_handle); + sshbuf_free(k->sk_reserved); + /* FALLTHROUGH */ + case KEY_ECDSA: + case KEY_ECDSA_CERT: + EC_KEY_free(k->ecdsa); + k->ecdsa = NULL; + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + free(k->sk_application); + sshbuf_free(k->sk_key_handle); + sshbuf_free(k->sk_reserved); + /* FALLTHROUGH */ + case KEY_ED25519: + case KEY_ED25519_CERT: + freezero(k->ed25519_pk, ED25519_PK_SZ); + k->ed25519_pk = NULL; + freezero(k->ed25519_sk, ED25519_SK_SZ); + k->ed25519_sk = NULL; + break; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + freezero(k->xmss_pk, sshkey_xmss_pklen(k)); + k->xmss_pk = NULL; + freezero(k->xmss_sk, sshkey_xmss_sklen(k)); + k->xmss_sk = NULL; + sshkey_xmss_free_state(k); + free(k->xmss_name); + k->xmss_name = NULL; + free(k->xmss_filename); + k->xmss_filename = NULL; + break; +#endif /* WITH_XMSS */ + case KEY_UNSPEC: + break; + default: + break; + } + if (sshkey_is_cert(k)) + cert_free(k->cert); + freezero(k->shielded_private, k->shielded_len); + freezero(k->shield_prekey, k->shield_prekey_len); + freezero(k, sizeof(*k)); +} + +static int +cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) +{ + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob)) + return 0; + if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob), + sshbuf_len(a->certblob)) != 0) + return 0; + return 1; +} + +/* + * Compare public portions of key only, allowing comparisons between + * certificates and plain keys too. + */ +int +sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) +{ +#if defined(WITH_OPENSSL) + const BIGNUM *rsa_e_a, *rsa_n_a; + const BIGNUM *rsa_e_b, *rsa_n_b; + const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a; + const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b; +#endif /* WITH_OPENSSL */ + + if (a == NULL || b == NULL || + sshkey_type_plain(a->type) != sshkey_type_plain(b->type)) + return 0; + + switch (a->type) { +#ifdef WITH_OPENSSL + case KEY_RSA_CERT: + case KEY_RSA: + if (a->rsa == NULL || b->rsa == NULL) + return 0; + RSA_get0_key(a->rsa, &rsa_n_a, &rsa_e_a, NULL); + RSA_get0_key(b->rsa, &rsa_n_b, &rsa_e_b, NULL); + return BN_cmp(rsa_e_a, rsa_e_b) == 0 && + BN_cmp(rsa_n_a, rsa_n_b) == 0; + case KEY_DSA_CERT: + case KEY_DSA: + if (a->dsa == NULL || b->dsa == NULL) + return 0; + DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a); + DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b); + DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL); + DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL); + return BN_cmp(dsa_p_a, dsa_p_b) == 0 && + BN_cmp(dsa_q_a, dsa_q_b) == 0 && + BN_cmp(dsa_g_a, dsa_g_b) == 0 && + BN_cmp(dsa_pub_key_a, dsa_pub_key_b) == 0; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + if (a->sk_application == NULL || b->sk_application == NULL) + return 0; + if (strcmp(a->sk_application, b->sk_application) != 0) + return 0; + /* FALLTHROUGH */ + case KEY_ECDSA_CERT: + case KEY_ECDSA: + if (a->ecdsa == NULL || b->ecdsa == NULL || + EC_KEY_get0_public_key(a->ecdsa) == NULL || + EC_KEY_get0_public_key(b->ecdsa) == NULL) + return 0; + if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa), + EC_KEY_get0_group(b->ecdsa), NULL) != 0 || + EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa), + EC_KEY_get0_public_key(a->ecdsa), + EC_KEY_get0_public_key(b->ecdsa), NULL) != 0) + return 0; + return 1; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + if (a->sk_application == NULL || b->sk_application == NULL) + return 0; + if (strcmp(a->sk_application, b->sk_application) != 0) + return 0; + /* FALLTHROUGH */ + case KEY_ED25519: + case KEY_ED25519_CERT: + return a->ed25519_pk != NULL && b->ed25519_pk != NULL && + memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + return a->xmss_pk != NULL && b->xmss_pk != NULL && + sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) && + memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0; +#endif /* WITH_XMSS */ + default: + return 0; + } + /* NOTREACHED */ +} + +int +sshkey_equal(const struct sshkey *a, const struct sshkey *b) +{ + if (a == NULL || b == NULL || a->type != b->type) + return 0; + if (sshkey_is_cert(a)) { + if (!cert_compare(a->cert, b->cert)) + return 0; + } + return sshkey_equal_public(a, b); +} + +static int +to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, + enum sshkey_serialize_rep opts) +{ + int type, ret = SSH_ERR_INTERNAL_ERROR; + const char *typename; +#ifdef WITH_OPENSSL + const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; +#endif /* WITH_OPENSSL */ + + if (key == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + if (sshkey_is_cert(key)) { + if (key->cert == NULL) + return SSH_ERR_EXPECTED_CERT; + if (sshbuf_len(key->cert->certblob) == 0) + return SSH_ERR_KEY_LACKS_CERTBLOB; + } + type = force_plain ? sshkey_type_plain(key->type) : key->type; + typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid); + + switch (type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_RSA_CERT: +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + case KEY_ED25519_SK_CERT: +#ifdef WITH_XMSS + case KEY_XMSS_CERT: +#endif /* WITH_XMSS */ + /* Use the existing blob */ + /* XXX modified flag? */ + if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) + return ret; + break; +#ifdef WITH_OPENSSL + case KEY_DSA: + if (key->dsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g); + DSA_get0_key(key->dsa, &dsa_pub_key, NULL); + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_bignum2(b, dsa_p)) != 0 || + (ret = sshbuf_put_bignum2(b, dsa_q)) != 0 || + (ret = sshbuf_put_bignum2(b, dsa_g)) != 0 || + (ret = sshbuf_put_bignum2(b, dsa_pub_key)) != 0) + return ret; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + case KEY_ECDSA_SK: + if (key->ecdsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0) + return ret; + if (type == KEY_ECDSA_SK) { + if ((ret = sshbuf_put_cstring(b, + key->sk_application)) != 0) + return ret; + } + break; +# endif + case KEY_RSA: + if (key->rsa == NULL) + return SSH_ERR_INVALID_ARGUMENT; + RSA_get0_key(key->rsa, &rsa_n, &rsa_e, NULL); + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_bignum2(b, rsa_e)) != 0 || + (ret = sshbuf_put_bignum2(b, rsa_n)) != 0) + return ret; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_SK: + if (key->ed25519_pk == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_string(b, + key->ed25519_pk, ED25519_PK_SZ)) != 0) + return ret; + if (type == KEY_ED25519_SK) { + if ((ret = sshbuf_put_cstring(b, + key->sk_application)) != 0) + return ret; + } + break; +#ifdef WITH_XMSS + case KEY_XMSS: + if (key->xmss_name == NULL || key->xmss_pk == NULL || + sshkey_xmss_pklen(key) == 0) + return SSH_ERR_INVALID_ARGUMENT; + if ((ret = sshbuf_put_cstring(b, typename)) != 0 || + (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 || + (ret = sshbuf_put_string(b, + key->xmss_pk, sshkey_xmss_pklen(key))) != 0 || + (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0) + return ret; + break; +#endif /* WITH_XMSS */ + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + return 0; +} + +int +sshkey_putb(const struct sshkey *key, struct sshbuf *b) +{ + return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT); +} + +int +sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b, + enum sshkey_serialize_rep opts) +{ + struct sshbuf *tmp; + int r; + + if ((tmp = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + r = to_blob_buf(key, tmp, 0, opts); + if (r == 0) + r = sshbuf_put_stringb(b, tmp); + sshbuf_free(tmp); + return r; +} + +int +sshkey_puts(const struct sshkey *key, struct sshbuf *b) +{ + return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT); +} + +int +sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) +{ + return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT); +} + +static int +to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain, + enum sshkey_serialize_rep opts) +{ + int ret = SSH_ERR_INTERNAL_ERROR; + size_t len; + struct sshbuf *b = NULL; + + if (lenp != NULL) + *lenp = 0; + if (blobp != NULL) + *blobp = NULL; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0) + goto out; + len = sshbuf_len(b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) { + if ((*blobp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*blobp, sshbuf_ptr(b), len); + } + ret = 0; + out: + sshbuf_free(b); + return ret; +} + +int +sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) +{ + return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT); +} + +int +sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) +{ + return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT); +} + +int +sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg, + u_char **retp, size_t *lenp) +{ + u_char *blob = NULL, *ret = NULL; + size_t blob_len = 0; + int r = SSH_ERR_INTERNAL_ERROR; + + if (retp != NULL) + *retp = NULL; + if (lenp != NULL) + *lenp = 0; + if (ssh_digest_bytes(dgst_alg) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT)) + != 0) + goto out; + if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = ssh_digest_memory(dgst_alg, blob, blob_len, + ret, SSH_DIGEST_MAX_LENGTH)) != 0) + goto out; + /* success */ + if (retp != NULL) { + *retp = ret; + ret = NULL; + } + if (lenp != NULL) + *lenp = ssh_digest_bytes(dgst_alg); + r = 0; + out: + free(ret); + if (blob != NULL) + freezero(blob, blob_len); + return r; +} + +static char * +fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) +{ + char *ret; + size_t plen = strlen(alg) + 1; + size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1; + + if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL) + return NULL; + strlcpy(ret, alg, rlen); + strlcat(ret, ":", rlen); + if (dgst_raw_len == 0) + return ret; + if (b64_ntop(dgst_raw, dgst_raw_len, ret + plen, rlen - plen) == -1) { + freezero(ret, rlen); + return NULL; + } + /* Trim padding characters from end */ + ret[strcspn(ret, "=")] = '\0'; + return ret; +} + +static char * +fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) +{ + char *retval, hex[5]; + size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2; + + if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL) + return NULL; + strlcpy(retval, alg, rlen); + strlcat(retval, ":", rlen); + for (i = 0; i < dgst_raw_len; i++) { + snprintf(hex, sizeof(hex), "%s%02x", + i > 0 ? ":" : "", dgst_raw[i]); + strlcat(retval, hex, rlen); + } + return retval; +} + +static char * +fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len) +{ + char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; + char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', + 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; + u_int i, j = 0, rounds, seed = 1; + char *retval; + + rounds = (dgst_raw_len / 2) + 1; + if ((retval = calloc(rounds, 6)) == NULL) + return NULL; + retval[j++] = 'x'; + for (i = 0; i < rounds; i++) { + u_int idx0, idx1, idx2, idx3, idx4; + if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { + idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + + seed) % 6; + idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; + idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + + (seed / 6)) % 6; + retval[j++] = vowels[idx0]; + retval[j++] = consonants[idx1]; + retval[j++] = vowels[idx2]; + if ((i + 1) < rounds) { + idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; + idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; + retval[j++] = consonants[idx3]; + retval[j++] = '-'; + retval[j++] = consonants[idx4]; + seed = ((seed * 5) + + ((((u_int)(dgst_raw[2 * i])) * 7) + + ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; + } + } else { + idx0 = seed % 6; + idx1 = 16; + idx2 = seed / 6; + retval[j++] = vowels[idx0]; + retval[j++] = consonants[idx1]; + retval[j++] = vowels[idx2]; + } + } + retval[j++] = 'x'; + retval[j++] = '\0'; + return retval; +} + +/* + * Draw an ASCII-Art representing the fingerprint so human brain can + * profit from its built-in pattern recognition ability. + * This technique is called "random art" and can be found in some + * scientific publications like this original paper: + * + * "Hash Visualization: a New Technique to improve Real-World Security", + * Perrig A. and Song D., 1999, International Workshop on Cryptographic + * Techniques and E-Commerce (CrypTEC '99) + * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf + * + * The subject came up in a talk by Dan Kaminsky, too. + * + * If you see the picture is different, the key is different. + * If the picture looks the same, you still know nothing. + * + * The algorithm used here is a worm crawling over a discrete plane, + * leaving a trace (augmenting the field) everywhere it goes. + * Movement is taken from dgst_raw 2bit-wise. Bumping into walls + * makes the respective movement vector be ignored for this turn. + * Graphs are not unambiguous, because circles in graphs can be + * walked in either direction. + */ + +/* + * Field sizes for the random art. Have to be odd, so the starting point + * can be in the exact middle of the picture, and FLDBASE should be >=8 . + * Else pictures would be too dense, and drawing the frame would + * fail, too, because the key type would not fit in anymore. + */ +#define FLDBASE 8 +#define FLDSIZE_Y (FLDBASE + 1) +#define FLDSIZE_X (FLDBASE * 2 + 1) +static char * +fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, + const struct sshkey *k) +{ + /* + * Chars to be used after each other every time the worm + * intersects with itself. Matter of taste. + */ + char *augmentation_string = " .o+=*BOX@%&#/^SE"; + char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; + u_char field[FLDSIZE_X][FLDSIZE_Y]; + size_t i, tlen, hlen; + u_int b; + int x, y, r; + size_t len = strlen(augmentation_string) - 1; + + if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) + return NULL; + + /* initialize field */ + memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); + x = FLDSIZE_X / 2; + y = FLDSIZE_Y / 2; + + /* process raw key */ + for (i = 0; i < dgst_raw_len; i++) { + int input; + /* each byte conveys four 2-bit move commands */ + input = dgst_raw[i]; + for (b = 0; b < 4; b++) { + /* evaluate 2 bit, rest is shifted later */ + x += (input & 0x1) ? 1 : -1; + y += (input & 0x2) ? 1 : -1; + + /* assure we are still in bounds */ + x = MAXIMUM(x, 0); + y = MAXIMUM(y, 0); + x = MINIMUM(x, FLDSIZE_X - 1); + y = MINIMUM(y, FLDSIZE_Y - 1); + + /* augment the field */ + if (field[x][y] < len - 2) + field[x][y]++; + input = input >> 2; + } + } + + /* mark starting point and end point*/ + field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; + field[x][y] = len; + + /* assemble title */ + r = snprintf(title, sizeof(title), "[%s %u]", + sshkey_type(k), sshkey_size(k)); + /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */ + if (r < 0 || r > (int)sizeof(title)) + r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); + tlen = (r <= 0) ? 0 : strlen(title); + + /* assemble hash ID. */ + r = snprintf(hash, sizeof(hash), "[%s]", alg); + hlen = (r <= 0) ? 0 : strlen(hash); + + /* output upper border */ + p = retval; + *p++ = '+'; + for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) + *p++ = '-'; + memcpy(p, title, tlen); + p += tlen; + for (i += tlen; i < FLDSIZE_X; i++) + *p++ = '-'; + *p++ = '+'; + *p++ = '\n'; + + /* output content */ + for (y = 0; y < FLDSIZE_Y; y++) { + *p++ = '|'; + for (x = 0; x < FLDSIZE_X; x++) + *p++ = augmentation_string[MINIMUM(field[x][y], len)]; + *p++ = '|'; + *p++ = '\n'; + } + + /* output lower border */ + *p++ = '+'; + for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) + *p++ = '-'; + memcpy(p, hash, hlen); + p += hlen; + for (i += hlen; i < FLDSIZE_X; i++) + *p++ = '-'; + *p++ = '+'; + + return retval; +} + +char * +sshkey_fingerprint(const struct sshkey *k, int dgst_alg, + enum sshkey_fp_rep dgst_rep) +{ + char *retval = NULL; + u_char *dgst_raw; + size_t dgst_raw_len; + + if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0) + return NULL; + switch (dgst_rep) { + case SSH_FP_DEFAULT: + if (dgst_alg == SSH_DIGEST_MD5) { + retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + } else { + retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + } + break; + case SSH_FP_HEX: + retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + break; + case SSH_FP_BASE64: + retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len); + break; + case SSH_FP_BUBBLEBABBLE: + retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); + break; + case SSH_FP_RANDOMART: + retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg), + dgst_raw, dgst_raw_len, k); + break; + default: + freezero(dgst_raw, dgst_raw_len); + return NULL; + } + freezero(dgst_raw, dgst_raw_len); + return retval; +} + +static int +peek_type_nid(const char *s, size_t l, int *nid) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (kt->name == NULL || strlen(kt->name) != l) + continue; + if (memcmp(s, kt->name, l) == 0) { + *nid = -1; + if (key_type_is_ecdsa_variant(kt->type)) + *nid = kt->nid; + return kt->type; + } + } + return KEY_UNSPEC; +} + +/* XXX this can now be made const char * */ +int +sshkey_read(struct sshkey *ret, char **cpp) +{ + struct sshkey *k; + char *cp, *blobcopy; + size_t space; + int r, type, curve_nid = -1; + struct sshbuf *blob; + + if (ret == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + switch (ret->type) { + case KEY_UNSPEC: + case KEY_RSA: + case KEY_DSA: + case KEY_ECDSA: + case KEY_ECDSA_SK: + case KEY_ED25519: + case KEY_ED25519_SK: + case KEY_DSA_CERT: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_RSA_CERT: + case KEY_ED25519_CERT: + case KEY_ED25519_SK_CERT: +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: +#endif /* WITH_XMSS */ + break; /* ok */ + default: + return SSH_ERR_INVALID_ARGUMENT; + } + + /* Decode type */ + cp = *cpp; + space = strcspn(cp, " \t"); + if (space == strlen(cp)) + return SSH_ERR_INVALID_FORMAT; + if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC) + return SSH_ERR_INVALID_FORMAT; + + /* skip whitespace */ + for (cp += space; *cp == ' ' || *cp == '\t'; cp++) + ; + if (*cp == '\0') + return SSH_ERR_INVALID_FORMAT; + if (ret->type != KEY_UNSPEC && ret->type != type) + return SSH_ERR_KEY_TYPE_MISMATCH; + if ((blob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + /* find end of keyblob and decode */ + space = strcspn(cp, " \t"); + if ((blobcopy = strndup(cp, space)) == NULL) { + sshbuf_free(blob); + return SSH_ERR_ALLOC_FAIL; + } + if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) { + free(blobcopy); + sshbuf_free(blob); + return r; + } + free(blobcopy); + if ((r = sshkey_fromb(blob, &k)) != 0) { + sshbuf_free(blob); + return r; + } + sshbuf_free(blob); + + /* skip whitespace and leave cp at start of comment */ + for (cp += space; *cp == ' ' || *cp == '\t'; cp++) + ; + + /* ensure type of blob matches type at start of line */ + if (k->type != type) { + sshkey_free(k); + return SSH_ERR_KEY_TYPE_MISMATCH; + } + if (key_type_is_ecdsa_variant(type) && curve_nid != k->ecdsa_nid) { + sshkey_free(k); + return SSH_ERR_EC_CURVE_MISMATCH; + } + + /* Fill in ret from parsed key */ + ret->type = type; + if (sshkey_is_cert(ret)) { + if (!sshkey_is_cert(k)) { + sshkey_free(k); + return SSH_ERR_EXPECTED_CERT; + } + if (ret->cert != NULL) + cert_free(ret->cert); + ret->cert = k->cert; + k->cert = NULL; + } + switch (sshkey_type_plain(ret->type)) { +#ifdef WITH_OPENSSL + case KEY_RSA: + RSA_free(ret->rsa); + ret->rsa = k->rsa; + k->rsa = NULL; +#ifdef DEBUG_PK + RSA_print_fp(stderr, ret->rsa, 8); +#endif + break; + case KEY_DSA: + DSA_free(ret->dsa); + ret->dsa = k->dsa; + k->dsa = NULL; +#ifdef DEBUG_PK + DSA_print_fp(stderr, ret->dsa, 8); +#endif + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + EC_KEY_free(ret->ecdsa); + ret->ecdsa = k->ecdsa; + ret->ecdsa_nid = k->ecdsa_nid; + k->ecdsa = NULL; + k->ecdsa_nid = -1; +#ifdef DEBUG_PK + sshkey_dump_ec_key(ret->ecdsa); +#endif + break; + case KEY_ECDSA_SK: + EC_KEY_free(ret->ecdsa); + ret->ecdsa = k->ecdsa; + ret->ecdsa_nid = k->ecdsa_nid; + ret->sk_application = k->sk_application; + k->ecdsa = NULL; + k->ecdsa_nid = -1; + k->sk_application = NULL; +#ifdef DEBUG_PK + sshkey_dump_ec_key(ret->ecdsa); + fprintf(stderr, "App: %s\n", ret->sk_application); +#endif + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + freezero(ret->ed25519_pk, ED25519_PK_SZ); + ret->ed25519_pk = k->ed25519_pk; + k->ed25519_pk = NULL; +#ifdef DEBUG_PK + /* XXX */ +#endif + break; + case KEY_ED25519_SK: + freezero(ret->ed25519_pk, ED25519_PK_SZ); + ret->ed25519_pk = k->ed25519_pk; + ret->sk_application = k->sk_application; + k->ed25519_pk = NULL; + k->sk_application = NULL; + break; +#ifdef WITH_XMSS + case KEY_XMSS: + free(ret->xmss_pk); + ret->xmss_pk = k->xmss_pk; + k->xmss_pk = NULL; + free(ret->xmss_state); + ret->xmss_state = k->xmss_state; + k->xmss_state = NULL; + free(ret->xmss_name); + ret->xmss_name = k->xmss_name; + k->xmss_name = NULL; + free(ret->xmss_filename); + ret->xmss_filename = k->xmss_filename; + k->xmss_filename = NULL; +#ifdef DEBUG_PK + /* XXX */ +#endif + break; +#endif /* WITH_XMSS */ + default: + sshkey_free(k); + return SSH_ERR_INTERNAL_ERROR; + } + sshkey_free(k); + + /* success */ + *cpp = cp; + return 0; +} + + +int +sshkey_to_base64(const struct sshkey *key, char **b64p) +{ + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + char *uu = NULL; + + if (b64p != NULL) + *b64p = NULL; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_putb(key, b)) != 0) + goto out; + if ((uu = sshbuf_dtob64_string(b, 0)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* Success */ + if (b64p != NULL) { + *b64p = uu; + uu = NULL; + } + r = 0; + out: + sshbuf_free(b); + free(uu); + return r; +} + +int +sshkey_format_text(const struct sshkey *key, struct sshbuf *b) +{ + int r = SSH_ERR_INTERNAL_ERROR; + char *uu = NULL; + + if ((r = sshkey_to_base64(key, &uu)) != 0) + goto out; + if ((r = sshbuf_putf(b, "%s %s", + sshkey_ssh_name(key), uu)) != 0) + goto out; + r = 0; + out: + free(uu); + return r; +} + +int +sshkey_write(const struct sshkey *key, FILE *f) +{ + struct sshbuf *b = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_format_text(key, b)) != 0) + goto out; + if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) { + if (feof(f)) + errno = EPIPE; + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } + /* Success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +const char * +sshkey_cert_type(const struct sshkey *k) +{ + switch (k->cert->type) { + case SSH2_CERT_TYPE_USER: + return "user"; + case SSH2_CERT_TYPE_HOST: + return "host"; + default: + return "unknown"; + } +} + +#ifdef WITH_OPENSSL +static int +rsa_generate_private_key(u_int bits, RSA **rsap) +{ + RSA *private = NULL; + BIGNUM *f4 = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (rsap == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE || + bits > SSHBUF_MAX_BIGNUM * 8) + return SSH_ERR_KEY_LENGTH; + *rsap = NULL; + if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!BN_set_word(f4, RSA_F4) || + !RSA_generate_key_ex(private, bits, f4, NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + *rsap = private; + private = NULL; + ret = 0; + out: + RSA_free(private); + BN_free(f4); + return ret; +} + +static int +dsa_generate_private_key(u_int bits, DSA **dsap) +{ + DSA *private; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (dsap == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (bits != 1024) + return SSH_ERR_KEY_LENGTH; + if ((private = DSA_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + *dsap = NULL; + if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, + NULL, NULL) || !DSA_generate_key(private)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + *dsap = private; + private = NULL; + ret = 0; + out: + DSA_free(private); + return ret; +} + +# ifdef OPENSSL_HAS_ECC +int +sshkey_ecdsa_key_to_nid(EC_KEY *k) +{ + EC_GROUP *eg; + int nids[] = { + NID_X9_62_prime256v1, + NID_secp384r1, +# ifdef OPENSSL_HAS_NISTP521 + NID_secp521r1, +# endif /* OPENSSL_HAS_NISTP521 */ + -1 + }; + int nid; + u_int i; + const EC_GROUP *g = EC_KEY_get0_group(k); + + /* + * The group may be stored in a ASN.1 encoded private key in one of two + * ways: as a "named group", which is reconstituted by ASN.1 object ID + * or explicit group parameters encoded into the key blob. Only the + * "named group" case sets the group NID for us, but we can figure + * it out for the other case by comparing against all the groups that + * are supported. + */ + if ((nid = EC_GROUP_get_curve_name(g)) > 0) + return nid; + for (i = 0; nids[i] != -1; i++) { + if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) + return -1; + if (EC_GROUP_cmp(g, eg, NULL) == 0) + break; + EC_GROUP_free(eg); + } + if (nids[i] != -1) { + /* Use the group with the NID attached */ + EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_set_group(k, eg) != 1) { + EC_GROUP_free(eg); + return -1; + } + } + return nids[i]; +} + +static int +ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap) +{ + EC_KEY *private; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (nid == NULL || ecdsap == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1) + return SSH_ERR_KEY_LENGTH; + *ecdsap = NULL; + if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_generate_key(private) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); + *ecdsap = private; + private = NULL; + ret = 0; + out: + EC_KEY_free(private); + return ret; +} +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + +int +sshkey_generate(int type, u_int bits, struct sshkey **keyp) +{ + struct sshkey *k; + int ret = SSH_ERR_INTERNAL_ERROR; + + if (keyp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + *keyp = NULL; + if ((k = sshkey_new(KEY_UNSPEC)) == NULL) + return SSH_ERR_ALLOC_FAIL; + switch (type) { + case KEY_ED25519: + if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL || + (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + break; + } + crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); + ret = 0; + break; +#ifdef WITH_XMSS + case KEY_XMSS: + ret = sshkey_xmss_generate_private_key(k, bits); + break; +#endif /* WITH_XMSS */ +#ifdef WITH_OPENSSL + case KEY_DSA: + ret = dsa_generate_private_key(bits, &k->dsa); + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid, + &k->ecdsa); + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: + ret = rsa_generate_private_key(bits, &k->rsa); + break; +#endif /* WITH_OPENSSL */ + default: + ret = SSH_ERR_INVALID_ARGUMENT; + } + if (ret == 0) { + k->type = type; + *keyp = k; + } else + sshkey_free(k); + return ret; +} + +int +sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key) +{ + u_int i; + const struct sshkey_cert *from; + struct sshkey_cert *to; + int r = SSH_ERR_INTERNAL_ERROR; + + if (to_key == NULL || (from = from_key->cert) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + if ((to = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + + if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 || + (r = sshbuf_putb(to->critical, from->critical)) != 0 || + (r = sshbuf_putb(to->extensions, from->extensions)) != 0) + goto out; + + to->serial = from->serial; + to->type = from->type; + if (from->key_id == NULL) + to->key_id = NULL; + else if ((to->key_id = strdup(from->key_id)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + to->valid_after = from->valid_after; + to->valid_before = from->valid_before; + if (from->signature_key == NULL) + to->signature_key = NULL; + else if ((r = sshkey_from_private(from->signature_key, + &to->signature_key)) != 0) + goto out; + if (from->signature_type != NULL && + (to->signature_type = strdup(from->signature_type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (from->nprincipals > 0) { + if ((to->principals = calloc(from->nprincipals, + sizeof(*to->principals))) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < from->nprincipals; i++) { + to->principals[i] = strdup(from->principals[i]); + if (to->principals[i] == NULL) { + to->nprincipals = i; + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + } + } + to->nprincipals = from->nprincipals; + + /* success */ + cert_free(to_key->cert); + to_key->cert = to; + to = NULL; + r = 0; + out: + cert_free(to); + return r; +} + +int +sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) +{ + struct sshkey *n = NULL; + int r = SSH_ERR_INTERNAL_ERROR; +#ifdef WITH_OPENSSL + const BIGNUM *rsa_n, *rsa_e; + BIGNUM *rsa_n_dup = NULL, *rsa_e_dup = NULL; + const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; + BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL; + BIGNUM *dsa_pub_key_dup = NULL; +#endif /* WITH_OPENSSL */ + + *pkp = NULL; + if ((n = sshkey_new(k->type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_DSA: + case KEY_DSA_CERT: + DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g); + DSA_get0_key(k->dsa, &dsa_pub_key, NULL); + if ((dsa_p_dup = BN_dup(dsa_p)) == NULL || + (dsa_q_dup = BN_dup(dsa_q)) == NULL || + (dsa_g_dup = BN_dup(dsa_g)) == NULL || + (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!DSA_set0_pqg(n->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */ + if (!DSA_set0_key(n->dsa, dsa_pub_key_dup, NULL)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_pub_key_dup = NULL; /* transferred */ + + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK: + case KEY_ECDSA_SK_CERT: + n->ecdsa_nid = k->ecdsa_nid; + n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (n->ecdsa == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_KEY_set_public_key(n->ecdsa, + EC_KEY_get0_public_key(k->ecdsa)) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (k->type != KEY_ECDSA_SK && k->type != KEY_ECDSA_SK_CERT) + break; + /* Append security-key application string */ + if ((n->sk_application = strdup(k->sk_application)) == NULL) + goto out; + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: + case KEY_RSA_CERT: + RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL); + if ((rsa_n_dup = BN_dup(rsa_n)) == NULL || + (rsa_e_dup = BN_dup(rsa_e)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (!RSA_set0_key(n->rsa, rsa_n_dup, rsa_e_dup, NULL)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + rsa_n_dup = rsa_e_dup = NULL; /* transferred */ + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + if (k->ed25519_pk != NULL) { + if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); + } + if (k->type != KEY_ED25519_SK && + k->type != KEY_ED25519_SK_CERT) + break; + /* Append security-key application string */ + if ((n->sk_application = strdup(k->sk_application)) == NULL) + goto out; + break; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + if ((r = sshkey_xmss_init(n, k->xmss_name)) != 0) + goto out; + if (k->xmss_pk != NULL) { + u_int32_t left; + size_t pklen = sshkey_xmss_pklen(k); + if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if ((n->xmss_pk = malloc(pklen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(n->xmss_pk, k->xmss_pk, pklen); + /* simulate number of signatures left on pubkey */ + left = sshkey_xmss_signatures_left(k); + if (left) + sshkey_xmss_enable_maxsign(n, left); + } + break; +#endif /* WITH_XMSS */ + default: + r = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; + } + if (sshkey_is_cert(k) && (r = sshkey_cert_copy(k, n)) != 0) + goto out; + /* success */ + *pkp = n; + n = NULL; + r = 0; + out: + sshkey_free(n); +#ifdef WITH_OPENSSL + BN_clear_free(rsa_n_dup); + BN_clear_free(rsa_e_dup); + BN_clear_free(dsa_p_dup); + BN_clear_free(dsa_q_dup); + BN_clear_free(dsa_g_dup); + BN_clear_free(dsa_pub_key_dup); +#endif + + return r; +} + +int +sshkey_is_shielded(struct sshkey *k) +{ + return k != NULL && k->shielded_private != NULL; +} + +int +sshkey_shield_private(struct sshkey *k) +{ + struct sshbuf *prvbuf = NULL; + u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH]; + struct sshcipher_ctx *cctx = NULL; + const struct sshcipher *cipher; + size_t i, enclen = 0; + struct sshkey *kswap = NULL, tmp; + int r = SSH_ERR_INTERNAL_ERROR; + +#ifdef DEBUG_PK + fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); +#endif + if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (cipher_keylen(cipher) + cipher_ivlen(cipher) > + ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + + /* Prepare a random pre-key, and from it an ephemeral key */ + if ((prekey = malloc(SSHKEY_SHIELD_PREKEY_LEN)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN); + if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, + prekey, SSHKEY_SHIELD_PREKEY_LEN, + keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: key+iv\n", __func__); + sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), + stderr); +#endif + if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), + keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0) + goto out; + + /* Serialise and encrypt the private key using the ephemeral key */ + if ((prvbuf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0) + goto out; + if ((r = sshkey_private_serialize_opt(k, prvbuf, + SSHKEY_SERIALIZE_SHIELD)) != 0) + goto out; + /* pad to cipher blocksize */ + i = 0; + while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) { + if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0) + goto out; + } +#ifdef DEBUG_PK + fprintf(stderr, "%s: serialised\n", __func__); + sshbuf_dump(prvbuf, stderr); +#endif + /* encrypt */ + enclen = sshbuf_len(prvbuf); + if ((enc = malloc(enclen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = cipher_crypt(cctx, 0, enc, + sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: encrypted\n", __func__); + sshbuf_dump_data(enc, enclen, stderr); +#endif + + /* Make a scrubbed, public-only copy of our private key argument */ + if ((r = sshkey_from_private(k, &kswap)) != 0) + goto out; + + /* Swap the private key out (it will be destroyed below) */ + tmp = *kswap; + *kswap = *k; + *k = tmp; + + /* Insert the shielded key into our argument */ + k->shielded_private = enc; + k->shielded_len = enclen; + k->shield_prekey = prekey; + k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN; + enc = prekey = NULL; /* transferred */ + enclen = 0; + + /* preserve key fields that are required for correct operation */ + k->sk_flags = kswap->sk_flags; + + /* success */ + r = 0; + + out: + /* XXX behaviour on error - invalidate original private key? */ + cipher_free(cctx); + explicit_bzero(keyiv, sizeof(keyiv)); + explicit_bzero(&tmp, sizeof(tmp)); + freezero(enc, enclen); + freezero(prekey, SSHKEY_SHIELD_PREKEY_LEN); + sshkey_free(kswap); + sshbuf_free(prvbuf); + return r; +} + +int +sshkey_unshield_private(struct sshkey *k) +{ + struct sshbuf *prvbuf = NULL; + u_char pad, *cp, keyiv[SSH_DIGEST_MAX_LENGTH]; + struct sshcipher_ctx *cctx = NULL; + const struct sshcipher *cipher; + size_t i; + struct sshkey *kswap = NULL, tmp; + int r = SSH_ERR_INTERNAL_ERROR; + +#ifdef DEBUG_PK + fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); +#endif + if (!sshkey_is_shielded(k)) + return 0; /* nothing to do */ + + if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if (cipher_keylen(cipher) + cipher_ivlen(cipher) > + ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + /* check size of shielded key blob */ + if (k->shielded_len < cipher_blocksize(cipher) || + (k->shielded_len % cipher_blocksize(cipher)) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* Calculate the ephemeral key from the prekey */ + if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, + k->shield_prekey, k->shield_prekey_len, + keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) + goto out; + if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), + keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: key+iv\n", __func__); + sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), + stderr); +#endif + + /* Decrypt and parse the shielded private key using the ephemeral key */ + if ((prvbuf = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0) + goto out; + /* decrypt */ +#ifdef DEBUG_PK + fprintf(stderr, "%s: encrypted\n", __func__); + sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr); +#endif + if ((r = cipher_crypt(cctx, 0, cp, + k->shielded_private, k->shielded_len, 0, 0)) != 0) + goto out; +#ifdef DEBUG_PK + fprintf(stderr, "%s: serialised\n", __func__); + sshbuf_dump(prvbuf, stderr); +#endif + /* Parse private key */ + if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0) + goto out; + /* Check deterministic padding */ + i = 0; + while (sshbuf_len(prvbuf)) { + if ((r = sshbuf_get_u8(prvbuf, &pad)) != 0) + goto out; + if (pad != (++i & 0xff)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* Swap the parsed key back into place */ + tmp = *kswap; + *kswap = *k; + *k = tmp; + + /* success */ + r = 0; + + out: + cipher_free(cctx); + explicit_bzero(keyiv, sizeof(keyiv)); + explicit_bzero(&tmp, sizeof(tmp)); + sshkey_free(kswap); + sshbuf_free(prvbuf); + return r; +} + +static int +cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf) +{ + struct sshbuf *principals = NULL, *crit = NULL; + struct sshbuf *exts = NULL, *ca = NULL; + u_char *sig = NULL; + size_t signed_len = 0, slen = 0, kidlen = 0; + int ret = SSH_ERR_INTERNAL_ERROR; + + /* Copy the entire key blob for verification and later serialisation */ + if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0) + return ret; + + /* Parse body of certificate up to signature */ + if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 || + (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 || + (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 || + (ret = sshbuf_froms(b, &principals)) != 0 || + (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 || + (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 || + (ret = sshbuf_froms(b, &crit)) != 0 || + (ret = sshbuf_froms(b, &exts)) != 0 || + (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || + (ret = sshbuf_froms(b, &ca)) != 0) { + /* XXX debug print error for ret */ + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* Signature is left in the buffer so we can calculate this length */ + signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b); + + if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + if (key->cert->type != SSH2_CERT_TYPE_USER && + key->cert->type != SSH2_CERT_TYPE_HOST) { + ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE; + goto out; + } + + /* Parse principals section */ + while (sshbuf_len(principals) > 0) { + char *principal = NULL; + char **oprincipals = NULL; + + if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((ret = sshbuf_get_cstring(principals, &principal, + NULL)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + oprincipals = key->cert->principals; + key->cert->principals = recallocarray(key->cert->principals, + key->cert->nprincipals, key->cert->nprincipals + 1, + sizeof(*key->cert->principals)); + if (key->cert->principals == NULL) { + free(principal); + key->cert->principals = oprincipals; + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + key->cert->principals[key->cert->nprincipals++] = principal; + } + + /* + * Stash a copies of the critical options and extensions sections + * for later use. + */ + if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 || + (exts != NULL && + (ret = sshbuf_putb(key->cert->extensions, exts)) != 0)) + goto out; + + /* + * Validate critical options and extensions sections format. + */ + while (sshbuf_len(crit) != 0) { + if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 || + (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) { + sshbuf_reset(key->cert->critical); + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + while (exts != NULL && sshbuf_len(exts) != 0) { + if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 || + (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) { + sshbuf_reset(key->cert->extensions); + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* Parse CA key and check signature */ + if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + goto out; + } + if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + goto out; + } + if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, + sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0, NULL)) != 0) + goto out; + if ((ret = sshkey_get_sigtype(sig, slen, + &key->cert->signature_type)) != 0) + goto out; + + /* Success */ + ret = 0; + out: + sshbuf_free(ca); + sshbuf_free(crit); + sshbuf_free(exts); + sshbuf_free(principals); + free(sig); + return ret; +} + +#ifdef WITH_OPENSSL +static int +check_rsa_length(const RSA *rsa) +{ + const BIGNUM *rsa_n; + + RSA_get0_key(rsa, &rsa_n, NULL, NULL); + if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + return 0; +} +#endif + +static int +sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, + int allow_cert) +{ + int type, ret = SSH_ERR_INTERNAL_ERROR; + char *ktype = NULL, *curve = NULL, *xmss_name = NULL; + struct sshkey *key = NULL; + size_t len; + u_char *pk = NULL; + struct sshbuf *copy; +#if defined(WITH_OPENSSL) + BIGNUM *rsa_n = NULL, *rsa_e = NULL; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL; +# if defined(OPENSSL_HAS_ECC) + EC_POINT *q = NULL; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + +#ifdef DEBUG_PK /* XXX */ + sshbuf_dump(b, stderr); +#endif + if (keyp != NULL) + *keyp = NULL; + if ((copy = sshbuf_fromb(b)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + + type = sshkey_type_from_name(ktype); + if (!allow_cert && sshkey_type_is_cert(type)) { + ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + goto out; + } + switch (type) { +#ifdef WITH_OPENSSL + case KEY_RSA_CERT: + /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_RSA: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_bignum2(b, &rsa_e) != 0 || + sshbuf_get_bignum2(b, &rsa_n) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + rsa_n = rsa_e = NULL; /* transferred */ + if ((ret = check_rsa_length(key->rsa)) != 0) + goto out; +#ifdef DEBUG_PK + RSA_print_fp(stderr, key->rsa, 8); +#endif + break; + case KEY_DSA_CERT: + /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_DSA: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_bignum2(b, &dsa_p) != 0 || + sshbuf_get_bignum2(b, &dsa_q) != 0 || + sshbuf_get_bignum2(b, &dsa_g) != 0 || + sshbuf_get_bignum2(b, &dsa_pub_key) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_p = dsa_q = dsa_g = NULL; /* transferred */ + if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_pub_key = NULL; /* transferred */ +#ifdef DEBUG_PK + DSA_print_fp(stderr, key->dsa, 8); +#endif + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: + /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_ECDSA: + case KEY_ECDSA_SK: + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype); + if (sshbuf_get_cstring(b, &curve, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + ret = SSH_ERR_EC_CURVE_MISMATCH; + goto out; + } + EC_KEY_free(key->ecdsa); + if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) + == NULL) { + ret = SSH_ERR_EC_CURVE_INVALID; + goto out; + } + if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), + q) != 0) { + ret = SSH_ERR_KEY_INVALID_EC_VALUE; + goto out; + } + if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { + /* XXX assume it is a allocation error */ + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +#ifdef DEBUG_PK + sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); +#endif + if (type == KEY_ECDSA_SK || type == KEY_ECDSA_SK_CERT) { + /* Parse additional security-key application string */ + if (sshbuf_get_cstring(b, &key->sk_application, + NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_PK + fprintf(stderr, "App: %s\n", key->sk_application); +#endif + } + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + case KEY_ED25519_SK_CERT: + /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_ED25519: + case KEY_ED25519_SK: + if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) + goto out; + if (len != ED25519_PK_SZ) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (type == KEY_ED25519_SK || type == KEY_ED25519_SK_CERT) { + /* Parse additional security-key application string */ + if (sshbuf_get_cstring(b, &key->sk_application, + NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } +#ifdef DEBUG_PK + fprintf(stderr, "App: %s\n", key->sk_application); +#endif + } + key->ed25519_pk = pk; + pk = NULL; + break; +#ifdef WITH_XMSS + case KEY_XMSS_CERT: + /* Skip nonce */ + if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* FALLTHROUGH */ + case KEY_XMSS: + if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0) + goto out; + if ((key = sshkey_new(type)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((ret = sshkey_xmss_init(key, xmss_name)) != 0) + goto out; + if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) + goto out; + if (len == 0 || len != sshkey_xmss_pklen(key)) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + key->xmss_pk = pk; + pk = NULL; + if (type != KEY_XMSS_CERT && + (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0) + goto out; + break; +#endif /* WITH_XMSS */ + case KEY_UNSPEC: + default: + ret = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; + } + + /* Parse certificate potion */ + if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0) + goto out; + + if (key != NULL && sshbuf_len(b) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + ret = 0; + if (keyp != NULL) { + *keyp = key; + key = NULL; + } + out: + sshbuf_free(copy); + sshkey_free(key); + free(xmss_name); + free(ktype); + free(curve); + free(pk); +#if defined(WITH_OPENSSL) + BN_clear_free(rsa_n); + BN_clear_free(rsa_e); + BN_clear_free(dsa_p); + BN_clear_free(dsa_q); + BN_clear_free(dsa_g); + BN_clear_free(dsa_pub_key); +# if defined(OPENSSL_HAS_ECC) + EC_POINT_free(q); +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + return ret; +} + +int +sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp) +{ + struct sshbuf *b; + int r; + + if ((b = sshbuf_from(blob, blen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + r = sshkey_from_blob_internal(b, keyp, 1); + sshbuf_free(b); + return r; +} + +int +sshkey_fromb(struct sshbuf *b, struct sshkey **keyp) +{ + return sshkey_from_blob_internal(b, keyp, 1); +} + +int +sshkey_froms(struct sshbuf *buf, struct sshkey **keyp) +{ + struct sshbuf *b; + int r; + + if ((r = sshbuf_froms(buf, &b)) != 0) + return r; + r = sshkey_from_blob_internal(b, keyp, 1); + sshbuf_free(b); + return r; +} + +int +sshkey_get_sigtype(const u_char *sig, size_t siglen, char **sigtypep) +{ + int r; + struct sshbuf *b = NULL; + char *sigtype = NULL; + + if (sigtypep != NULL) + *sigtypep = NULL; + if ((b = sshbuf_from(sig, siglen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0) + goto out; + /* success */ + if (sigtypep != NULL) { + *sigtypep = sigtype; + sigtype = NULL; + } + r = 0; + out: + free(sigtype); + sshbuf_free(b); + return r; +} + +/* + * + * Checks whether a certificate's signature type is allowed. + * Returns 0 (success) if the certificate signature type appears in the + * "allowed" pattern-list, or the key is not a certificate to begin with. + * Otherwise returns a ssherr.h code. + */ +int +sshkey_check_cert_sigtype(const struct sshkey *key, const char *allowed) +{ + if (key == NULL || allowed == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (!sshkey_type_is_cert(key->type)) + return 0; + if (key->cert == NULL || key->cert->signature_type == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (match_pattern_list(key->cert->signature_type, allowed, 0) != 1) + return SSH_ERR_SIGN_ALG_UNSUPPORTED; + return 0; +} + +/* + * Returns the expected signature algorithm for a given public key algorithm. + */ +const char * +sshkey_sigalg_by_name(const char *name) +{ + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { + if (strcmp(kt->name, name) != 0) + continue; + if (kt->sigalg != NULL) + return kt->sigalg; + if (!kt->cert) + return kt->name; + return sshkey_ssh_name_from_type_nid( + sshkey_type_plain(kt->type), kt->nid); + } + return NULL; +} + +/* + * Verifies that the signature algorithm appearing inside the signature blob + * matches that which was requested. + */ +int +sshkey_check_sigtype(const u_char *sig, size_t siglen, + const char *requested_alg) +{ + const char *expected_alg; + char *sigtype = NULL; + int r; + + if (requested_alg == NULL) + return 0; + if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) + return r; + r = strcmp(expected_alg, sigtype) == 0; + free(sigtype); + return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED; +} + +int +sshkey_sign(struct sshkey *key, + u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, + const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) +{ + int was_shielded = sshkey_is_shielded(key); + int r2, r = SSH_ERR_INTERNAL_ERROR; + + if (sigp != NULL) + *sigp = NULL; + if (lenp != NULL) + *lenp = 0; + if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) + return SSH_ERR_INVALID_ARGUMENT; + if ((r = sshkey_unshield_private(key)) != 0) + return r; + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT: + case KEY_DSA: + r = ssh_dss_sign(key, sigp, lenp, data, datalen, compat); + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + r = ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat); + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA_CERT: + case KEY_RSA: + r = ssh_rsa_sign(key, sigp, lenp, data, datalen, alg); + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + r = ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); + break; + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + case KEY_ECDSA_SK_CERT: + case KEY_ECDSA_SK: + r = sshsk_sign(sk_provider, key, sigp, lenp, data, + datalen, compat, sk_pin); + break; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + r = ssh_xmss_sign(key, sigp, lenp, data, datalen, compat); + break; +#endif /* WITH_XMSS */ + default: + r = SSH_ERR_KEY_TYPE_UNKNOWN; + break; + } + if (was_shielded && (r2 = sshkey_shield_private(key)) != 0) + return r2; + return r; +} + +/* + * ssh_key_verify returns 0 for a correct signature and < 0 on error. + * If "alg" specified, then the signature must use that algorithm. + */ +int +sshkey_verify(const struct sshkey *key, + const u_char *sig, size_t siglen, + const u_char *data, size_t dlen, const char *alg, u_int compat, + struct sshkey_sig_details **detailsp) +{ + if (detailsp != NULL) + *detailsp = NULL; + if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE) + return SSH_ERR_INVALID_ARGUMENT; + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT: + case KEY_DSA: + return ssh_dss_verify(key, sig, siglen, data, dlen, compat); +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: + return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat); + case KEY_ECDSA_SK_CERT: + case KEY_ECDSA_SK: + return ssh_ecdsa_sk_verify(key, sig, siglen, data, dlen, + compat, detailsp); +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA_CERT: + case KEY_RSA: + return ssh_rsa_verify(key, sig, siglen, data, dlen, alg); +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat); + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + return ssh_ed25519_sk_verify(key, sig, siglen, data, dlen, + compat, detailsp); +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + return ssh_xmss_verify(key, sig, siglen, data, dlen, compat); +#endif /* WITH_XMSS */ + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } +} + +/* Convert a plain key to their _CERT equivalent */ +int +sshkey_to_certified(struct sshkey *k) +{ + int newtype; + + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + newtype = KEY_RSA_CERT; + break; + case KEY_DSA: + newtype = KEY_DSA_CERT; + break; + case KEY_ECDSA: + newtype = KEY_ECDSA_CERT; + break; + case KEY_ECDSA_SK: + newtype = KEY_ECDSA_SK_CERT; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519_SK: + newtype = KEY_ED25519_SK_CERT; + break; + case KEY_ED25519: + newtype = KEY_ED25519_CERT; + break; +#ifdef WITH_XMSS + case KEY_XMSS: + newtype = KEY_XMSS_CERT; + break; +#endif /* WITH_XMSS */ + default: + return SSH_ERR_INVALID_ARGUMENT; + } + if ((k->cert = cert_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + k->type = newtype; + return 0; +} + +/* Convert a certificate to its raw key equivalent */ +int +sshkey_drop_cert(struct sshkey *k) +{ + if (!sshkey_type_is_cert(k->type)) + return SSH_ERR_KEY_TYPE_UNKNOWN; + cert_free(k->cert); + k->cert = NULL; + k->type = sshkey_type_plain(k->type); + return 0; +} + +/* Sign a certified key, (re-)generating the signed certblob. */ +int +sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, + const char *sk_provider, const char *sk_pin, + sshkey_certify_signer *signer, void *signer_ctx) +{ + struct sshbuf *principals = NULL; + u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; + size_t i, ca_len, sig_len; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *cert = NULL; + char *sigtype = NULL; +#ifdef WITH_OPENSSL + const BIGNUM *rsa_n, *rsa_e, *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; +#endif /* WITH_OPENSSL */ + + if (k == NULL || k->cert == NULL || + k->cert->certblob == NULL || ca == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (!sshkey_is_cert(k)) + return SSH_ERR_KEY_TYPE_UNKNOWN; + if (!sshkey_type_is_valid_ca(ca->type)) + return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + + /* + * If no alg specified as argument but a signature_type was set, + * then prefer that. If both were specified, then they must match. + */ + if (alg == NULL) + alg = k->cert->signature_type; + else if (k->cert->signature_type != NULL && + strcmp(alg, k->cert->signature_type) != 0) + return SSH_ERR_INVALID_ARGUMENT; + + /* + * If no signing algorithm or signature_type was specified and we're + * using a RSA key, then default to a good signature algorithm. + */ + if (alg == NULL && ca->type == KEY_RSA) + alg = "rsa-sha2-512"; + + if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0) + return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; + + cert = k->cert->certblob; /* for readability */ + sshbuf_reset(cert); + if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0) + goto out; + + /* -v01 certs put nonce first */ + arc4random_buf(&nonce, sizeof(nonce)); + if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) + goto out; + + /* XXX this substantially duplicates to_blob(); refactor */ + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_DSA_CERT: + DSA_get0_pqg(k->dsa, &dsa_p, &dsa_q, &dsa_g); + DSA_get0_key(k->dsa, &dsa_pub_key, NULL); + if ((ret = sshbuf_put_bignum2(cert, dsa_p)) != 0 || + (ret = sshbuf_put_bignum2(cert, dsa_q)) != 0 || + (ret = sshbuf_put_bignum2(cert, dsa_g)) != 0 || + (ret = sshbuf_put_bignum2(cert, dsa_pub_key)) != 0) + goto out; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA_SK_CERT: + if ((ret = sshbuf_put_cstring(cert, + sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 || + (ret = sshbuf_put_ec(cert, + EC_KEY_get0_public_key(k->ecdsa), + EC_KEY_get0_group(k->ecdsa))) != 0) + goto out; + if (k->type == KEY_ECDSA_SK_CERT) { + if ((ret = sshbuf_put_cstring(cert, + k->sk_application)) != 0) + goto out; + } + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA_CERT: + RSA_get0_key(k->rsa, &rsa_n, &rsa_e, NULL); + if ((ret = sshbuf_put_bignum2(cert, rsa_e)) != 0 || + (ret = sshbuf_put_bignum2(cert, rsa_n)) != 0) + goto out; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: + case KEY_ED25519_SK_CERT: + if ((ret = sshbuf_put_string(cert, + k->ed25519_pk, ED25519_PK_SZ)) != 0) + goto out; + if (k->type == KEY_ED25519_SK_CERT) { + if ((ret = sshbuf_put_cstring(cert, + k->sk_application)) != 0) + goto out; + } + break; +#ifdef WITH_XMSS + case KEY_XMSS_CERT: + if (k->xmss_name == NULL) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) || + (ret = sshbuf_put_string(cert, + k->xmss_pk, sshkey_xmss_pklen(k))) != 0) + goto out; + break; +#endif /* WITH_XMSS */ + default: + ret = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + + if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 || + (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 || + (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0) + goto out; + + if ((principals = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < k->cert->nprincipals; i++) { + if ((ret = sshbuf_put_cstring(principals, + k->cert->principals[i])) != 0) + goto out; + } + if ((ret = sshbuf_put_stringb(cert, principals)) != 0 || + (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 || + (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 || + (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 || + (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 || + (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ + (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) + goto out; + + /* Sign the whole mess */ + if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), + sshbuf_len(cert), alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) + goto out; + /* Check and update signature_type against what was actually used */ + if ((ret = sshkey_get_sigtype(sig_blob, sig_len, &sigtype)) != 0) + goto out; + if (alg != NULL && strcmp(alg, sigtype) != 0) { + ret = SSH_ERR_SIGN_ALG_UNSUPPORTED; + goto out; + } + if (k->cert->signature_type == NULL) { + k->cert->signature_type = sigtype; + sigtype = NULL; + } + /* Append signature and we are done */ + if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0) + goto out; + ret = 0; + out: + if (ret != 0) + sshbuf_reset(cert); + free(sig_blob); + free(ca_blob); + free(sigtype); + sshbuf_free(principals); + return ret; +} + +static int +default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, + const char *alg, const char *sk_provider, const char *sk_pin, + u_int compat, void *ctx) +{ + if (ctx != NULL) + return SSH_ERR_INVALID_ARGUMENT; + return sshkey_sign(key, sigp, lenp, data, datalen, alg, + sk_provider, sk_pin, compat); +} + +int +sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg, + const char *sk_provider, const char *sk_pin) +{ + return sshkey_certify_custom(k, ca, alg, sk_provider, sk_pin, + default_key_sign, NULL); +} + +int +sshkey_cert_check_authority(const struct sshkey *k, + int want_host, int require_principal, int wildcard_pattern, + uint64_t verify_time, const char *name, const char **reason) +{ + u_int i, principal_matches; + + if (reason == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (!sshkey_is_cert(k)) { + *reason = "Key is not a certificate"; + return SSH_ERR_KEY_CERT_INVALID; + } + if (want_host) { + if (k->cert->type != SSH2_CERT_TYPE_HOST) { + *reason = "Certificate invalid: not a host certificate"; + return SSH_ERR_KEY_CERT_INVALID; + } + } else { + if (k->cert->type != SSH2_CERT_TYPE_USER) { + *reason = "Certificate invalid: not a user certificate"; + return SSH_ERR_KEY_CERT_INVALID; + } + } + if (verify_time < k->cert->valid_after) { + *reason = "Certificate invalid: not yet valid"; + return SSH_ERR_KEY_CERT_INVALID; + } + if (verify_time >= k->cert->valid_before) { + *reason = "Certificate invalid: expired"; + return SSH_ERR_KEY_CERT_INVALID; + } + if (k->cert->nprincipals == 0) { + if (require_principal) { + *reason = "Certificate lacks principal list"; + return SSH_ERR_KEY_CERT_INVALID; + } + } else if (name != NULL) { + principal_matches = 0; + for (i = 0; i < k->cert->nprincipals; i++) { + if (wildcard_pattern) { + if (match_pattern(k->cert->principals[i], + name)) { + principal_matches = 1; + break; + } + } else if (strcmp(name, k->cert->principals[i]) == 0) { + principal_matches = 1; + break; + } + } + if (!principal_matches) { + *reason = "Certificate invalid: name is not a listed " + "principal"; + return SSH_ERR_KEY_CERT_INVALID; + } + } + return 0; +} + +int +sshkey_cert_check_authority_now(const struct sshkey *k, + int want_host, int require_principal, int wildcard_pattern, + const char *name, const char **reason) +{ + time_t now; + + if ((now = time(NULL)) < 0) { + /* yikes - system clock before epoch! */ + *reason = "Certificate invalid: not yet valid"; + return SSH_ERR_KEY_CERT_INVALID; + } + return sshkey_cert_check_authority(k, want_host, require_principal, + wildcard_pattern, (uint64_t)now, name, reason); +} + +int +sshkey_cert_check_host(const struct sshkey *key, const char *host, + int wildcard_principals, const char *ca_sign_algorithms, + const char **reason) +{ + int r; + + if ((r = sshkey_cert_check_authority_now(key, 1, 0, wildcard_principals, + host, reason)) != 0) + return r; + if (sshbuf_len(key->cert->critical) != 0) { + *reason = "Certificate contains unsupported critical options"; + return SSH_ERR_KEY_CERT_INVALID; + } + if (ca_sign_algorithms != NULL && + (r = sshkey_check_cert_sigtype(key, ca_sign_algorithms)) != 0) { + *reason = "Certificate signed with disallowed algorithm"; + return SSH_ERR_KEY_CERT_INVALID; + } + return 0; +} + +size_t +sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l) +{ + char from[32], to[32], ret[128]; + + *from = *to = '\0'; + if (cert->valid_after == 0 && + cert->valid_before == 0xffffffffffffffffULL) + return strlcpy(s, "forever", l); + + if (cert->valid_after != 0) + format_absolute_time(cert->valid_after, from, sizeof(from)); + if (cert->valid_before != 0xffffffffffffffffULL) + format_absolute_time(cert->valid_before, to, sizeof(to)); + + if (cert->valid_after == 0) + snprintf(ret, sizeof(ret), "before %s", to); + else if (cert->valid_before == 0xffffffffffffffffULL) + snprintf(ret, sizeof(ret), "after %s", from); + else + snprintf(ret, sizeof(ret), "from %s to %s", from, to); + + return strlcpy(s, ret, l); +} + +int +sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, + enum sshkey_serialize_rep opts) +{ + int r = SSH_ERR_INTERNAL_ERROR; + int was_shielded = sshkey_is_shielded(key); + struct sshbuf *b = NULL; +#ifdef WITH_OPENSSL + const BIGNUM *rsa_n, *rsa_e, *rsa_d, *rsa_iqmp, *rsa_p, *rsa_q; + const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key, *dsa_priv_key; +#endif /* WITH_OPENSSL */ + + if ((r = sshkey_unshield_private(key)) != 0) + return r; + if ((b = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0) + goto out; + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_RSA: + RSA_get0_key(key->rsa, &rsa_n, &rsa_e, &rsa_d); + RSA_get0_factors(key->rsa, &rsa_p, &rsa_q); + RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp); + if ((r = sshbuf_put_bignum2(b, rsa_n)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_e)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_d)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_iqmp)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_p)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_q)) != 0) + goto out; + break; + case KEY_RSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + RSA_get0_key(key->rsa, NULL, NULL, &rsa_d); + RSA_get0_factors(key->rsa, &rsa_p, &rsa_q); + RSA_get0_crt_params(key->rsa, NULL, NULL, &rsa_iqmp); + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_d)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_iqmp)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_p)) != 0 || + (r = sshbuf_put_bignum2(b, rsa_q)) != 0) + goto out; + break; + case KEY_DSA: + DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g); + DSA_get0_key(key->dsa, &dsa_pub_key, &dsa_priv_key); + if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 || + (r = sshbuf_put_bignum2(b, dsa_q)) != 0 || + (r = sshbuf_put_bignum2(b, dsa_g)) != 0 || + (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0 || + (r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0) + goto out; + break; + case KEY_DSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + DSA_get0_key(key->dsa, NULL, &dsa_priv_key); + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0) + goto out; + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if ((r = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 || + (r = sshbuf_put_bignum2(b, + EC_KEY_get0_private_key(key->ecdsa))) != 0) + goto out; + break; + case KEY_ECDSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_bignum2(b, + EC_KEY_get0_private_key(key->ecdsa))) != 0) + goto out; + break; + case KEY_ECDSA_SK: + if ((r = sshbuf_put_cstring(b, + sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || + (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; + case KEY_ECDSA_SK_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; +# endif /* OPENSSL_HAS_ECC */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + if ((r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_sk, + ED25519_SK_SZ)) != 0) + goto out; + break; + case KEY_ED25519_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_sk, + ED25519_SK_SZ)) != 0) + goto out; + break; + case KEY_ED25519_SK: + if ((r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; + case KEY_ED25519_SK_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_string(b, key->ed25519_pk, + ED25519_PK_SZ)) != 0 || + (r = sshbuf_put_cstring(b, key->sk_application)) != 0 || + (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || + (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) + goto out; + break; +#ifdef WITH_XMSS + case KEY_XMSS: + if (key->xmss_name == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 || + (r = sshbuf_put_string(b, key->xmss_pk, + sshkey_xmss_pklen(key))) != 0 || + (r = sshbuf_put_string(b, key->xmss_sk, + sshkey_xmss_sklen(key))) != 0 || + (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0) + goto out; + break; + case KEY_XMSS_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 || + key->xmss_name == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || + (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 || + (r = sshbuf_put_string(b, key->xmss_pk, + sshkey_xmss_pklen(key))) != 0 || + (r = sshbuf_put_string(b, key->xmss_sk, + sshkey_xmss_sklen(key))) != 0 || + (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0) + goto out; + break; +#endif /* WITH_XMSS */ + default: + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + /* + * success (but we still need to append the output to buf after + * possibly re-shielding the private key) + */ + r = 0; + out: + if (was_shielded) + r = sshkey_shield_private(key); + if (r == 0) + r = sshbuf_putb(buf, b); + sshbuf_free(b); + + return r; +} + +int +sshkey_private_serialize(struct sshkey *key, struct sshbuf *b) +{ + return sshkey_private_serialize_opt(key, b, + SSHKEY_SERIALIZE_DEFAULT); +} + +int +sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) +{ + char *tname = NULL, *curve = NULL, *xmss_name = NULL; + char *expect_sk_application = NULL; + struct sshkey *k = NULL; + size_t pklen = 0, sklen = 0; + int type, r = SSH_ERR_INTERNAL_ERROR; + u_char *ed25519_pk = NULL, *ed25519_sk = NULL; + u_char *expect_ed25519_pk = NULL; + u_char *xmss_pk = NULL, *xmss_sk = NULL; +#ifdef WITH_OPENSSL + BIGNUM *exponent = NULL; + BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; + BIGNUM *rsa_iqmp = NULL, *rsa_p = NULL, *rsa_q = NULL; + BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; + BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL; +#endif /* WITH_OPENSSL */ + + if (kp != NULL) + *kp = NULL; + if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) + goto out; + type = sshkey_type_from_name(tname); + if (sshkey_type_is_cert(type)) { + /* + * Certificate key private keys begin with the certificate + * itself. Make sure this matches the type of the enclosing + * private key. + */ + if ((r = sshkey_froms(buf, &k)) != 0) + goto out; + if (k->type != type) { + r = SSH_ERR_KEY_CERT_MISMATCH; + goto out; + } + /* For ECDSA keys, the group must match too */ + if (k->type == KEY_ECDSA && + k->ecdsa_nid != sshkey_ecdsa_nid_from_name(tname)) { + r = SSH_ERR_KEY_CERT_MISMATCH; + goto out; + } + /* + * Several fields are redundant between certificate and + * private key body, we require these to match. + */ + expect_sk_application = k->sk_application; + expect_ed25519_pk = k->ed25519_pk; + k->sk_application = NULL; + k->ed25519_pk = NULL; + } else { + if ((k = sshkey_new(type)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + } + switch (type) { +#ifdef WITH_OPENSSL + case KEY_DSA: + if ((r = sshbuf_get_bignum2(buf, &dsa_p)) != 0 || + (r = sshbuf_get_bignum2(buf, &dsa_q)) != 0 || + (r = sshbuf_get_bignum2(buf, &dsa_g)) != 0 || + (r = sshbuf_get_bignum2(buf, &dsa_pub_key)) != 0) + goto out; + if (!DSA_set0_pqg(k->dsa, dsa_p, dsa_q, dsa_g)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_p = dsa_q = dsa_g = NULL; /* transferred */ + if (!DSA_set0_key(k->dsa, dsa_pub_key, NULL)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_pub_key = NULL; /* transferred */ + /* FALLTHROUGH */ + case KEY_DSA_CERT: + if ((r = sshbuf_get_bignum2(buf, &dsa_priv_key)) != 0) + goto out; + if (!DSA_set0_key(k->dsa, NULL, dsa_priv_key)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + dsa_priv_key = NULL; /* transferred */ + break; +# ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0) + goto out; + if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + r = SSH_ERR_EC_CURVE_MISMATCH; + goto out; + } + k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (k->ecdsa == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0) + goto out; + /* FALLTHROUGH */ + case KEY_ECDSA_CERT: + if ((r = sshbuf_get_bignum2(buf, &exponent)) != 0) + goto out; + if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa))) != 0 || + (r = sshkey_ec_validate_private(k->ecdsa)) != 0) + goto out; + break; + case KEY_ECDSA_SK: + if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0) + goto out; + if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { + r = SSH_ERR_EC_CURVE_MISMATCH; + goto out; + } + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); + if (k->ecdsa == NULL) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 || + (r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa))) != 0) + goto out; + break; + case KEY_ECDSA_SK_CERT: + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), + EC_KEY_get0_public_key(k->ecdsa))) != 0) + goto out; + break; +# endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: + if ((r = sshbuf_get_bignum2(buf, &rsa_n)) != 0 || + (r = sshbuf_get_bignum2(buf, &rsa_e)) != 0) + goto out; + if (!RSA_set0_key(k->rsa, rsa_n, rsa_e, NULL)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + rsa_n = rsa_e = NULL; /* transferred */ + /* FALLTHROUGH */ + case KEY_RSA_CERT: + if ((r = sshbuf_get_bignum2(buf, &rsa_d)) != 0 || + (r = sshbuf_get_bignum2(buf, &rsa_iqmp)) != 0 || + (r = sshbuf_get_bignum2(buf, &rsa_p)) != 0 || + (r = sshbuf_get_bignum2(buf, &rsa_q)) != 0) + goto out; + if (!RSA_set0_key(k->rsa, NULL, NULL, rsa_d)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + rsa_d = NULL; /* transferred */ + if (!RSA_set0_factors(k->rsa, rsa_p, rsa_q)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + rsa_p = rsa_q = NULL; /* transferred */ + if ((r = check_rsa_length(k->rsa)) != 0) + goto out; + if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0) + goto out; + break; +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_CERT: + if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || + (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) + goto out; + if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + k->ed25519_pk = ed25519_pk; + k->ed25519_sk = ed25519_sk; + ed25519_pk = ed25519_sk = NULL; /* transferred */ + break; + case KEY_ED25519_SK: + case KEY_ED25519_SK_CERT: + if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0) + goto out; + if (pklen != ED25519_PK_SZ) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((k->sk_key_handle = sshbuf_new()) == NULL || + (k->sk_reserved = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_get_cstring(buf, &k->sk_application, + NULL)) != 0 || + (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || + (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) + goto out; + k->ed25519_pk = ed25519_pk; + ed25519_pk = NULL; /* transferred */ + break; +#ifdef WITH_XMSS + case KEY_XMSS: + case KEY_XMSS_CERT: + if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 || + (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 || + (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0) + goto out; + if (type == KEY_XMSS && + (r = sshkey_xmss_init(k, xmss_name)) != 0) + goto out; + if (pklen != sshkey_xmss_pklen(k) || + sklen != sshkey_xmss_sklen(k)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + k->xmss_pk = xmss_pk; + k->xmss_sk = xmss_sk; + xmss_pk = xmss_sk = NULL; + /* optional internal state */ + if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0) + goto out; + break; +#endif /* WITH_XMSS */ + default: + r = SSH_ERR_KEY_TYPE_UNKNOWN; + goto out; + } +#ifdef WITH_OPENSSL + /* enable blinding */ + switch (k->type) { + case KEY_RSA: + case KEY_RSA_CERT: + if (RSA_blinding_on(k->rsa, NULL) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + break; + } +#endif /* WITH_OPENSSL */ + if ((expect_sk_application != NULL && (k->sk_application == NULL || + strcmp(expect_sk_application, k->sk_application) != 0)) || + (expect_ed25519_pk != NULL && (k->ed25519_pk == NULL || + memcmp(expect_ed25519_pk, k->ed25519_pk, ED25519_PK_SZ) != 0))) { + r = SSH_ERR_KEY_CERT_MISMATCH; + goto out; + } + /* success */ + r = 0; + if (kp != NULL) { + *kp = k; + k = NULL; + } + out: + free(tname); + free(curve); +#ifdef WITH_OPENSSL + BN_clear_free(exponent); + BN_clear_free(dsa_p); + BN_clear_free(dsa_q); + BN_clear_free(dsa_g); + BN_clear_free(dsa_pub_key); + BN_clear_free(dsa_priv_key); + BN_clear_free(rsa_n); + BN_clear_free(rsa_e); + BN_clear_free(rsa_d); + BN_clear_free(rsa_p); + BN_clear_free(rsa_q); + BN_clear_free(rsa_iqmp); +#endif /* WITH_OPENSSL */ + sshkey_free(k); + freezero(ed25519_pk, pklen); + freezero(ed25519_sk, sklen); + free(xmss_name); + freezero(xmss_pk, pklen); + freezero(xmss_sk, sklen); + free(expect_sk_application); + free(expect_ed25519_pk); + return r; +} + +#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) +int +sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) +{ + EC_POINT *nq = NULL; + BIGNUM *order = NULL, *x = NULL, *y = NULL, *tmp = NULL; + int ret = SSH_ERR_KEY_INVALID_EC_VALUE; + + /* + * NB. This assumes OpenSSL has already verified that the public + * point lies on the curve. This is done by EC_POINT_oct2point() + * implicitly calling EC_POINT_is_on_curve(). If this code is ever + * reachable with public points not unmarshalled using + * EC_POINT_oct2point then the caller will need to explicitly check. + */ + + /* + * We shouldn't ever hit this case because bignum_get_ecpoint() + * refuses to load GF2m points. + */ + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != + NID_X9_62_prime_field) + goto out; + + /* Q != infinity */ + if (EC_POINT_is_at_infinity(group, public)) + goto out; + + if ((x = BN_new()) == NULL || + (y = BN_new()) == NULL || + (order = BN_new()) == NULL || + (tmp = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ + if (EC_GROUP_get_order(group, order, NULL) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, public, + x, y, NULL) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_num_bits(x) <= BN_num_bits(order) / 2 || + BN_num_bits(y) <= BN_num_bits(order) / 2) + goto out; + + /* nQ == infinity (n == order of subgroup) */ + if ((nq = EC_POINT_new(group)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (EC_POINT_mul(group, nq, NULL, public, order, NULL) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (EC_POINT_is_at_infinity(group, nq) != 1) + goto out; + + /* x < order - 1, y < order - 1 */ + if (!BN_sub(tmp, order, BN_value_one())) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0) + goto out; + ret = 0; + out: + BN_clear_free(x); + BN_clear_free(y); + BN_clear_free(order); + BN_clear_free(tmp); + EC_POINT_free(nq); + return ret; +} + +int +sshkey_ec_validate_private(const EC_KEY *key) +{ + BIGNUM *order = NULL, *tmp = NULL; + int ret = SSH_ERR_KEY_INVALID_EC_VALUE; + + if ((order = BN_new()) == NULL || (tmp = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* log2(private) > log2(order)/2 */ + if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, NULL) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_num_bits(EC_KEY_get0_private_key(key)) <= + BN_num_bits(order) / 2) + goto out; + + /* private < order - 1 */ + if (!BN_sub(tmp, order, BN_value_one())) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) + goto out; + ret = 0; + out: + BN_clear_free(order); + BN_clear_free(tmp); + return ret; +} + +void +sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) +{ + BIGNUM *x = NULL, *y = NULL; + + if (point == NULL) { + fputs("point=(NULL)\n", stderr); + return; + } + if ((x = BN_new()) == NULL || (y = BN_new()) == NULL) { + fprintf(stderr, "%s: BN_new failed\n", __func__); + goto out; + } + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != + NID_X9_62_prime_field) { + fprintf(stderr, "%s: group is not a prime field\n", __func__); + goto out; + } + if (EC_POINT_get_affine_coordinates_GFp(group, point, + x, y, NULL) != 1) { + fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n", + __func__); + goto out; + } + fputs("x=", stderr); + BN_print_fp(stderr, x); + fputs("\ny=", stderr); + BN_print_fp(stderr, y); + fputs("\n", stderr); + out: + BN_clear_free(x); + BN_clear_free(y); +} + +void +sshkey_dump_ec_key(const EC_KEY *key) +{ + const BIGNUM *exponent; + + sshkey_dump_ec_point(EC_KEY_get0_group(key), + EC_KEY_get0_public_key(key)); + fputs("exponent=", stderr); + if ((exponent = EC_KEY_get0_private_key(key)) == NULL) + fputs("(NULL)", stderr); + else + BN_print_fp(stderr, EC_KEY_get0_private_key(key)); + fputs("\n", stderr); +} +#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ + +static int +sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob, + const char *passphrase, const char *comment, const char *ciphername, + int rounds) +{ + u_char *cp, *key = NULL, *pubkeyblob = NULL; + u_char salt[SALT_LEN]; + char *b64 = NULL; + size_t i, pubkeylen, keylen, ivlen, blocksize, authlen; + u_int check; + int r = SSH_ERR_INTERNAL_ERROR; + struct sshcipher_ctx *ciphercontext = NULL; + const struct sshcipher *cipher; + const char *kdfname = KDFNAME; + struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL; + + if (rounds <= 0) + rounds = DEFAULT_ROUNDS; + if (passphrase == NULL || !strlen(passphrase)) { + ciphername = "none"; + kdfname = "none"; + } else if (ciphername == NULL) + ciphername = DEFAULT_CIPHERNAME; + if ((cipher = cipher_by_name(ciphername)) == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + + if ((kdf = sshbuf_new()) == NULL || + (encoded = sshbuf_new()) == NULL || + (encrypted = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + blocksize = cipher_blocksize(cipher); + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if ((key = calloc(1, keylen + ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (strcmp(kdfname, "bcrypt") == 0) { + arc4random_buf(salt, SALT_LEN); + if (bcrypt_pbkdf(passphrase, strlen(passphrase), + salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } + if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 || + (r = sshbuf_put_u32(kdf, rounds)) != 0) + goto out; + } else if (strcmp(kdfname, "none") != 0) { + /* Unsupported KDF type */ + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + if ((r = cipher_init(&ciphercontext, cipher, key, keylen, + key + keylen, ivlen, 1)) != 0) + goto out; + + if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 || + (r = sshbuf_put_cstring(encoded, ciphername)) != 0 || + (r = sshbuf_put_cstring(encoded, kdfname)) != 0 || + (r = sshbuf_put_stringb(encoded, kdf)) != 0 || + (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */ + (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 || + (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0) + goto out; + + /* set up the buffer that will be encrypted */ + + /* Random check bytes */ + check = arc4random(); + if ((r = sshbuf_put_u32(encrypted, check)) != 0 || + (r = sshbuf_put_u32(encrypted, check)) != 0) + goto out; + + /* append private key and comment*/ + if ((r = sshkey_private_serialize_opt(prv, encrypted, + SSHKEY_SERIALIZE_FULL)) != 0 || + (r = sshbuf_put_cstring(encrypted, comment)) != 0) + goto out; + + /* padding */ + i = 0; + while (sshbuf_len(encrypted) % blocksize) { + if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0) + goto out; + } + + /* length in destination buffer */ + if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0) + goto out; + + /* encrypt */ + if ((r = sshbuf_reserve(encoded, + sshbuf_len(encrypted) + authlen, &cp)) != 0) + goto out; + if ((r = cipher_crypt(ciphercontext, 0, cp, + sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0) + goto out; + + sshbuf_reset(blob); + + /* assemble uuencoded key */ + if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0 || + (r = sshbuf_dtob64(encoded, blob, 1)) != 0 || + (r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0) + goto out; + + /* success */ + r = 0; + + out: + sshbuf_free(kdf); + sshbuf_free(encoded); + sshbuf_free(encrypted); + cipher_free(ciphercontext); + explicit_bzero(salt, sizeof(salt)); + if (key != NULL) + freezero(key, keylen + ivlen); + if (pubkeyblob != NULL) + freezero(pubkeyblob, pubkeylen); + if (b64 != NULL) + freezero(b64, strlen(b64)); + return r; +} + +static int +private2_uudecode(struct sshbuf *blob, struct sshbuf **decodedp) +{ + const u_char *cp; + size_t encoded_len; + int r; + u_char last; + struct sshbuf *encoded = NULL, *decoded = NULL; + + if (blob == NULL || decodedp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + *decodedp = NULL; + + if ((encoded = sshbuf_new()) == NULL || + (decoded = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* check preamble */ + cp = sshbuf_ptr(blob); + encoded_len = sshbuf_len(blob); + if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) || + memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + cp += MARK_BEGIN_LEN; + encoded_len -= MARK_BEGIN_LEN; + + /* Look for end marker, removing whitespace as we go */ + while (encoded_len > 0) { + if (*cp != '\n' && *cp != '\r') { + if ((r = sshbuf_put_u8(encoded, *cp)) != 0) + goto out; + } + last = *cp; + encoded_len--; + cp++; + if (last == '\n') { + if (encoded_len >= MARK_END_LEN && + memcmp(cp, MARK_END, MARK_END_LEN) == 0) { + /* \0 terminate */ + if ((r = sshbuf_put_u8(encoded, 0)) != 0) + goto out; + break; + } + } + } + if (encoded_len == 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* decode base64 */ + if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0) + goto out; + + /* check magic */ + if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) || + memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + *decodedp = decoded; + decoded = NULL; + r = 0; + out: + sshbuf_free(encoded); + sshbuf_free(decoded); + return r; +} + +static int +private2_decrypt(struct sshbuf *decoded, const char *passphrase, + struct sshbuf **decryptedp, struct sshkey **pubkeyp) +{ + char *ciphername = NULL, *kdfname = NULL; + const struct sshcipher *cipher = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + size_t keylen = 0, ivlen = 0, authlen = 0, slen = 0; + struct sshbuf *kdf = NULL, *decrypted = NULL; + struct sshcipher_ctx *ciphercontext = NULL; + struct sshkey *pubkey = NULL; + u_char *key = NULL, *salt = NULL, *dp; + u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; + + if (decoded == NULL || decryptedp == NULL || pubkeyp == NULL) + return SSH_ERR_INVALID_ARGUMENT; + + *decryptedp = NULL; + *pubkeyp = NULL; + + if ((decrypted = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + /* parse public portion of key */ + if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || + (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 || + (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 || + (r = sshbuf_froms(decoded, &kdf)) != 0 || + (r = sshbuf_get_u32(decoded, &nkeys)) != 0) + goto out; + + if (nkeys != 1) { + /* XXX only one key supported at present */ + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + if ((r = sshkey_froms(decoded, &pubkey)) != 0 || + (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0) + goto out; + + if ((cipher = cipher_by_name(ciphername)) == NULL) { + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) { + r = SSH_ERR_KEY_UNKNOWN_CIPHER; + goto out; + } + if (strcmp(kdfname, "none") == 0 && strcmp(ciphername, "none") != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((passphrase == NULL || strlen(passphrase) == 0) && + strcmp(kdfname, "none") != 0) { + /* passphrase required */ + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + + /* check size of encrypted key blob */ + blocksize = cipher_blocksize(cipher); + if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* setup key */ + keylen = cipher_keylen(cipher); + ivlen = cipher_ivlen(cipher); + authlen = cipher_authlen(cipher); + if ((key = calloc(1, keylen + ivlen)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (strcmp(kdfname, "bcrypt") == 0) { + if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 || + (r = sshbuf_get_u32(kdf, &rounds)) != 0) + goto out; + if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, + key, keylen + ivlen, rounds) < 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + + /* check that an appropriate amount of auth data is present */ + if (sshbuf_len(decoded) < authlen || + sshbuf_len(decoded) - authlen < encrypted_len) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* decrypt private portion of key */ + if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 || + (r = cipher_init(&ciphercontext, cipher, key, keylen, + key + keylen, ivlen, 0)) != 0) + goto out; + if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded), + encrypted_len, 0, authlen)) != 0) { + /* an integrity error here indicates an incorrect passphrase */ + if (r == SSH_ERR_MAC_INVALID) + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0) + goto out; + /* there should be no trailing data */ + if (sshbuf_len(decoded) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* check check bytes */ + if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 || + (r = sshbuf_get_u32(decrypted, &check2)) != 0) + goto out; + if (check1 != check2) { + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + } + /* success */ + *decryptedp = decrypted; + decrypted = NULL; + *pubkeyp = pubkey; + pubkey = NULL; + r = 0; + out: + cipher_free(ciphercontext); + free(ciphername); + free(kdfname); + sshkey_free(pubkey); + if (salt != NULL) { + explicit_bzero(salt, slen); + free(salt); + } + if (key != NULL) { + explicit_bzero(key, keylen + ivlen); + free(key); + } + sshbuf_free(kdf); + sshbuf_free(decrypted); + return r; +} + +/* Check deterministic padding after private key */ +static int +private2_check_padding(struct sshbuf *decrypted) +{ + u_char pad; + size_t i; + int r = SSH_ERR_INTERNAL_ERROR; + + i = 0; + while (sshbuf_len(decrypted)) { + if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) + goto out; + if (pad != (++i & 0xff)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + /* success */ + r = 0; + out: + explicit_bzero(&pad, sizeof(pad)); + explicit_bzero(&i, sizeof(i)); + return r; +} + +static int +sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + char *comment = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *decoded = NULL, *decrypted = NULL; + struct sshkey *k = NULL, *pubkey = NULL; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + /* Undo base64 encoding and decrypt the private section */ + if ((r = private2_uudecode(blob, &decoded)) != 0 || + (r = private2_decrypt(decoded, passphrase, + &decrypted, &pubkey)) != 0) + goto out; + + if (type != KEY_UNSPEC && + sshkey_type_plain(type) != sshkey_type_plain(pubkey->type)) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + + /* Load the private key and comment */ + if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 || + (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0) + goto out; + + /* Check deterministic padding after private section */ + if ((r = private2_check_padding(decrypted)) != 0) + goto out; + + /* Check that the public key in the envelope matches the private key */ + if (!sshkey_equal(pubkey, k)) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* success */ + r = 0; + if (keyp != NULL) { + *keyp = k; + k = NULL; + } + if (commentp != NULL) { + *commentp = comment; + comment = NULL; + } + out: + free(comment); + sshbuf_free(decoded); + sshbuf_free(decrypted); + sshkey_free(k); + sshkey_free(pubkey); + return r; +} + +static int +sshkey_parse_private2_pubkey(struct sshbuf *blob, int type, + struct sshkey **keyp) +{ + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *decoded = NULL; + struct sshkey *pubkey = NULL; + u_int nkeys = 0; + + if (keyp != NULL) + *keyp = NULL; + + if ((r = private2_uudecode(blob, &decoded)) != 0) + goto out; + /* parse public key from unencrypted envelope */ + if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || + (r = sshbuf_skip_string(decoded)) != 0 || /* cipher */ + (r = sshbuf_skip_string(decoded)) != 0 || /* KDF alg */ + (r = sshbuf_skip_string(decoded)) != 0 || /* KDF hint */ + (r = sshbuf_get_u32(decoded, &nkeys)) != 0) + goto out; + + if (nkeys != 1) { + /* XXX only one key supported at present */ + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + + /* Parse the public key */ + if ((r = sshkey_froms(decoded, &pubkey)) != 0) + goto out; + + if (type != KEY_UNSPEC && + sshkey_type_plain(type) != sshkey_type_plain(pubkey->type)) { + r = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + + /* success */ + r = 0; + if (keyp != NULL) { + *keyp = pubkey; + pubkey = NULL; + } + out: + sshbuf_free(decoded); + sshkey_free(pubkey); + return r; +} + +#ifdef WITH_OPENSSL +/* convert SSH v2 key to PEM or PKCS#8 format */ +static int +sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf, + int format, const char *_passphrase, const char *comment) +{ + int was_shielded = sshkey_is_shielded(key); + int success, r; + int blen, len = strlen(_passphrase); + u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; + const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; + char *bptr; + BIO *bio = NULL; + struct sshbuf *blob; + EVP_PKEY *pkey = NULL; + + if (len > 0 && len <= 4) + return SSH_ERR_PASSPHRASE_TOO_SHORT; + if ((blob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((bio = BIO_new(BIO_s_mem())) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (format == SSHKEY_PRIVATE_PKCS8 && (pkey = EVP_PKEY_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshkey_unshield_private(key)) != 0) + goto out; + + switch (key->type) { + case KEY_DSA: + if (format == SSHKEY_PRIVATE_PEM) { + success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, + cipher, passphrase, len, NULL, NULL); + } else { + success = EVP_PKEY_set1_DSA(pkey, key->dsa); + } + break; +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: + if (format == SSHKEY_PRIVATE_PEM) { + success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, + cipher, passphrase, len, NULL, NULL); + } else { + success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa); + } + break; +#endif + case KEY_RSA: + if (format == SSHKEY_PRIVATE_PEM) { + success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, + cipher, passphrase, len, NULL, NULL); + } else { + success = EVP_PKEY_set1_RSA(pkey, key->rsa); + } + break; + default: + success = 0; + break; + } + if (success == 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (format == SSHKEY_PRIVATE_PKCS8) { + if ((success = PEM_write_bio_PrivateKey(bio, pkey, cipher, + passphrase, len, NULL, NULL)) == 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + } + if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) { + r = SSH_ERR_INTERNAL_ERROR; + goto out; + } + if ((r = sshbuf_put(blob, bptr, blen)) != 0) + goto out; + r = 0; + out: + if (was_shielded) + r = sshkey_shield_private(key); + if (r == 0) + r = sshbuf_putb(buf, blob); + + EVP_PKEY_free(pkey); + sshbuf_free(blob); + BIO_free(bio); + return r; +} +#endif /* WITH_OPENSSL */ + +/* Serialise "key" to buffer "blob" */ +int +sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, + int format, const char *openssh_format_cipher, int openssh_format_rounds) +{ + switch (key->type) { +#ifdef WITH_OPENSSL + case KEY_DSA: + case KEY_ECDSA: + case KEY_RSA: + break; /* see below */ +#endif /* WITH_OPENSSL */ + case KEY_ED25519: + case KEY_ED25519_SK: +#ifdef WITH_XMSS + case KEY_XMSS: +#endif /* WITH_XMSS */ +#ifdef WITH_OPENSSL + case KEY_ECDSA_SK: +#endif /* WITH_OPENSSL */ + return sshkey_private_to_blob2(key, blob, passphrase, + comment, openssh_format_cipher, openssh_format_rounds); + default: + return SSH_ERR_KEY_TYPE_UNKNOWN; + } + +#ifdef WITH_OPENSSL + switch (format) { + case SSHKEY_PRIVATE_OPENSSH: + return sshkey_private_to_blob2(key, blob, passphrase, + comment, openssh_format_cipher, openssh_format_rounds); + case SSHKEY_PRIVATE_PEM: + case SSHKEY_PRIVATE_PKCS8: + return sshkey_private_to_blob_pem_pkcs8(key, blob, + format, passphrase, comment); + default: + return SSH_ERR_INVALID_ARGUMENT; + } +#endif /* WITH_OPENSSL */ +} + +#ifdef WITH_OPENSSL +static int +translate_libcrypto_error(unsigned long pem_err) +{ + int pem_reason = ERR_GET_REASON(pem_err); + + switch (ERR_GET_LIB(pem_err)) { + case ERR_LIB_PEM: + switch (pem_reason) { + case PEM_R_BAD_PASSWORD_READ: +#ifdef PEM_R_PROBLEMS_GETTING_PASSWORD + case PEM_R_PROBLEMS_GETTING_PASSWORD: +#endif + case PEM_R_BAD_DECRYPT: + return SSH_ERR_KEY_WRONG_PASSPHRASE; + default: + return SSH_ERR_INVALID_FORMAT; + } + case ERR_LIB_EVP: + switch (pem_reason) { +#ifdef EVP_R_BAD_DECRYPT + case EVP_R_BAD_DECRYPT: + return SSH_ERR_KEY_WRONG_PASSPHRASE; +#endif +#ifdef EVP_R_BN_DECODE_ERROR + case EVP_R_BN_DECODE_ERROR: +#endif + case EVP_R_DECODE_ERROR: +#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR + case EVP_R_PRIVATE_KEY_DECODE_ERROR: +#endif + return SSH_ERR_INVALID_FORMAT; + default: + return SSH_ERR_LIBCRYPTO_ERROR; + } + case ERR_LIB_ASN1: + return SSH_ERR_INVALID_FORMAT; + } + return SSH_ERR_LIBCRYPTO_ERROR; +} + +static void +clear_libcrypto_errors(void) +{ + while (ERR_get_error() != 0) + ; +} + +/* + * Translate OpenSSL error codes to determine whether + * passphrase is required/incorrect. + */ +static int +convert_libcrypto_error(void) +{ + /* + * Some password errors are reported at the beginning + * of the error queue. + */ + if (translate_libcrypto_error(ERR_peek_error()) == + SSH_ERR_KEY_WRONG_PASSPHRASE) + return SSH_ERR_KEY_WRONG_PASSPHRASE; + return translate_libcrypto_error(ERR_peek_last_error()); +} + +static int +pem_passphrase_cb(char *buf, int size, int rwflag, void *u) +{ + char *p = (char *)u; + size_t len; + + if (p == NULL || (len = strlen(p)) == 0) + return -1; + if (size < 0 || len > (size_t)size) + return -1; + memcpy(buf, p, len); + return (int)len; +} + +static int +sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp) +{ + EVP_PKEY *pk = NULL; + struct sshkey *prv = NULL; + BIO *bio = NULL; + int r; + + if (keyp != NULL) + *keyp = NULL; + + if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX) + return SSH_ERR_ALLOC_FAIL; + if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) != + (int)sshbuf_len(blob)) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + clear_libcrypto_errors(); + if ((pk = PEM_read_bio_PrivateKey(bio, NULL, pem_passphrase_cb, + (char *)passphrase)) == NULL) { + /* + * libcrypto may return various ASN.1 errors when attempting + * to parse a key with an incorrect passphrase. + * Treat all format errors as "incorrect passphrase" if a + * passphrase was supplied. + */ + if (passphrase != NULL && *passphrase != '\0') + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + else + r = convert_libcrypto_error(); + goto out; + } + if (EVP_PKEY_base_id(pk) == EVP_PKEY_RSA && + (type == KEY_UNSPEC || type == KEY_RSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + prv->rsa = EVP_PKEY_get1_RSA(pk); + prv->type = KEY_RSA; +#ifdef DEBUG_PK + RSA_print_fp(stderr, prv->rsa, 8); +#endif + if (RSA_blinding_on(prv->rsa, NULL) != 1) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((r = check_rsa_length(prv->rsa)) != 0) + goto out; + } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA && + (type == KEY_UNSPEC || type == KEY_DSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + prv->dsa = EVP_PKEY_get1_DSA(pk); + prv->type = KEY_DSA; +#ifdef DEBUG_PK + DSA_print_fp(stderr, prv->dsa, 8); +#endif +#ifdef OPENSSL_HAS_ECC + } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_EC && + (type == KEY_UNSPEC || type == KEY_ECDSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); + prv->type = KEY_ECDSA; + prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa); + if (prv->ecdsa_nid == -1 || + sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL || + sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), + EC_KEY_get0_public_key(prv->ecdsa)) != 0 || + sshkey_ec_validate_private(prv->ecdsa) != 0) { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } +# ifdef DEBUG_PK + if (prv != NULL && prv->ecdsa != NULL) + sshkey_dump_ec_key(prv->ecdsa); +# endif +#endif /* OPENSSL_HAS_ECC */ + } else { + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + r = 0; + if (keyp != NULL) { + *keyp = prv; + prv = NULL; + } + out: + BIO_free(bio); + EVP_PKEY_free(pk); + sshkey_free(prv); + return r; +} +#endif /* WITH_OPENSSL */ + +int +sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp, char **commentp) +{ + int r = SSH_ERR_INTERNAL_ERROR; + + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + switch (type) { + case KEY_ED25519: + case KEY_XMSS: + /* No fallback for new-format-only keys */ + return sshkey_parse_private2(blob, type, passphrase, + keyp, commentp); + default: + r = sshkey_parse_private2(blob, type, passphrase, keyp, + commentp); + /* Only fallback to PEM parser if a format error occurred. */ + if (r != SSH_ERR_INVALID_FORMAT) + return r; +#ifdef WITH_OPENSSL + return sshkey_parse_private_pem_fileblob(blob, type, + passphrase, keyp); +#else + return SSH_ERR_INVALID_FORMAT; +#endif /* WITH_OPENSSL */ + } +} + +int +sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, + struct sshkey **keyp, char **commentp) +{ + if (keyp != NULL) + *keyp = NULL; + if (commentp != NULL) + *commentp = NULL; + + return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, + passphrase, keyp, commentp); +} + +void +sshkey_sig_details_free(struct sshkey_sig_details *details) +{ + freezero(details, sizeof(*details)); +} + +int +sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob, int type, + struct sshkey **pubkeyp) +{ + int r = SSH_ERR_INTERNAL_ERROR; + + if (pubkeyp != NULL) + *pubkeyp = NULL; + /* only new-format private keys bundle a public key inside */ + if ((r = sshkey_parse_private2_pubkey(blob, type, pubkeyp)) != 0) + return r; + return 0; +} + +#ifdef WITH_XMSS +/* + * serialize the key with the current state and forward the state + * maxsign times. + */ +int +sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, + u_int32_t maxsign, int printerror) +{ + int r, rupdate; + + if (maxsign == 0 || + sshkey_type_plain(k->type) != KEY_XMSS) + return sshkey_private_serialize_opt(k, b, + SSHKEY_SERIALIZE_DEFAULT); + if ((r = sshkey_xmss_get_state(k, printerror)) != 0 || + (r = sshkey_private_serialize_opt(k, b, + SSHKEY_SERIALIZE_STATE)) != 0 || + (r = sshkey_xmss_forward_state(k, maxsign)) != 0) + goto out; + r = 0; +out: + if ((rupdate = sshkey_xmss_update_state(k, printerror)) != 0) { + if (r == 0) + r = rupdate; + } + return r; +} + +u_int32_t +sshkey_signatures_left(const struct sshkey *k) +{ + if (sshkey_type_plain(k->type) == KEY_XMSS) + return sshkey_xmss_signatures_left(k); + return 0; +} + +int +sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) +{ + if (sshkey_type_plain(k->type) != KEY_XMSS) + return SSH_ERR_INVALID_ARGUMENT; + return sshkey_xmss_enable_maxsign(k, maxsign); +} + +int +sshkey_set_filename(struct sshkey *k, const char *filename) +{ + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if (sshkey_type_plain(k->type) != KEY_XMSS) + return 0; + if (filename == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((k->xmss_filename = strdup(filename)) == NULL) + return SSH_ERR_ALLOC_FAIL; + return 0; +} +#else +int +sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, + u_int32_t maxsign, int printerror) +{ + return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT); +} + +u_int32_t +sshkey_signatures_left(const struct sshkey *k) +{ + return 0; +} + +int +sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) +{ + return SSH_ERR_INVALID_ARGUMENT; +} + +int +sshkey_set_filename(struct sshkey *k, const char *filename) +{ + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + return 0; +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/sshkey.h b/aosp/external/openssh/sshkey.h new file mode 100644 index 0000000000000000000000000000000000000000..094815e00a2a000f3ea538823913f8e8e14ab28a --- /dev/null +++ b/aosp/external/openssh/sshkey.h @@ -0,0 +1,338 @@ +/* $OpenBSD: sshkey.h,v 1.51 2022/01/06 22:05:42 djm Exp $ */ + +/* + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SSHKEY_H +#define SSHKEY_H + +#include + +#ifdef WITH_OPENSSL +#include +#include +# ifdef OPENSSL_HAS_ECC +# include +# include +# else /* OPENSSL_HAS_ECC */ +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +# endif /* OPENSSL_HAS_ECC */ +#define SSH_OPENSSL_VERSION OpenSSL_version(OPENSSL_VERSION) +#else /* WITH_OPENSSL */ +# define BIGNUM void +# define RSA void +# define DSA void +# define EC_KEY void +# define EC_GROUP void +# define EC_POINT void +#define SSH_OPENSSL_VERSION "without OpenSSL" +#endif /* WITH_OPENSSL */ + +#define SSH_RSA_MINIMUM_MODULUS_SIZE 1024 +#define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20) + +struct sshbuf; + +/* Key types */ +enum sshkey_types { + KEY_RSA, + KEY_DSA, + KEY_ECDSA, + KEY_ED25519, + KEY_RSA_CERT, + KEY_DSA_CERT, + KEY_ECDSA_CERT, + KEY_ED25519_CERT, + KEY_XMSS, + KEY_XMSS_CERT, + KEY_ECDSA_SK, + KEY_ECDSA_SK_CERT, + KEY_ED25519_SK, + KEY_ED25519_SK_CERT, + KEY_UNSPEC +}; + +/* Default fingerprint hash */ +#define SSH_FP_HASH_DEFAULT SSH_DIGEST_SHA256 + +/* Fingerprint representation formats */ +enum sshkey_fp_rep { + SSH_FP_DEFAULT = 0, + SSH_FP_HEX, + SSH_FP_BASE64, + SSH_FP_BUBBLEBABBLE, + SSH_FP_RANDOMART +}; + +/* Private key serialisation formats, used on the wire */ +enum sshkey_serialize_rep { + SSHKEY_SERIALIZE_DEFAULT = 0, + SSHKEY_SERIALIZE_STATE = 1, /* only state is serialized */ + SSHKEY_SERIALIZE_FULL = 2, /* include keys for saving to disk */ + SSHKEY_SERIALIZE_SHIELD = 3, /* everything, for encrypting in ram */ + SSHKEY_SERIALIZE_INFO = 254, /* minimal information */ +}; + +/* Private key disk formats */ +enum sshkey_private_format { + SSHKEY_PRIVATE_OPENSSH = 0, + SSHKEY_PRIVATE_PEM = 1, + SSHKEY_PRIVATE_PKCS8 = 2, +}; + +/* key is stored in external hardware */ +#define SSHKEY_FLAG_EXT 0x0001 + +#define SSHKEY_CERT_MAX_PRINCIPALS 256 +/* XXX opaquify? */ +struct sshkey_cert { + struct sshbuf *certblob; /* Kept around for use on wire */ + u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ + u_int64_t serial; + char *key_id; + u_int nprincipals; + char **principals; + u_int64_t valid_after, valid_before; + struct sshbuf *critical; + struct sshbuf *extensions; + struct sshkey *signature_key; + char *signature_type; +}; + +/* XXX opaquify? */ +struct sshkey { + int type; + int flags; + /* KEY_RSA */ + RSA *rsa; + /* KEY_DSA */ + DSA *dsa; + /* KEY_ECDSA and KEY_ECDSA_SK */ + int ecdsa_nid; /* NID of curve */ + EC_KEY *ecdsa; + /* KEY_ED25519 and KEY_ED25519_SK */ + u_char *ed25519_sk; + u_char *ed25519_pk; + /* KEY_XMSS */ + char *xmss_name; + char *xmss_filename; /* for state file updates */ + void *xmss_state; /* depends on xmss_name, opaque */ + u_char *xmss_sk; + u_char *xmss_pk; + /* KEY_ECDSA_SK and KEY_ED25519_SK */ + char *sk_application; + uint8_t sk_flags; + struct sshbuf *sk_key_handle; + struct sshbuf *sk_reserved; + /* Certificates */ + struct sshkey_cert *cert; + /* Private key shielding */ + u_char *shielded_private; + size_t shielded_len; + u_char *shield_prekey; + size_t shield_prekey_len; +}; + +#define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES +#define ED25519_PK_SZ crypto_sign_ed25519_PUBLICKEYBYTES + +/* Additional fields contained in signature */ +struct sshkey_sig_details { + uint32_t sk_counter; /* U2F signature counter */ + uint8_t sk_flags; /* U2F signature flags; see ssh-sk.h */ +}; + +struct sshkey *sshkey_new(int); +void sshkey_free(struct sshkey *); +int sshkey_equal_public(const struct sshkey *, + const struct sshkey *); +int sshkey_equal(const struct sshkey *, const struct sshkey *); +char *sshkey_fingerprint(const struct sshkey *, + int, enum sshkey_fp_rep); +int sshkey_fingerprint_raw(const struct sshkey *k, + int, u_char **retp, size_t *lenp); +const char *sshkey_type(const struct sshkey *); +const char *sshkey_cert_type(const struct sshkey *); +int sshkey_format_text(const struct sshkey *, struct sshbuf *); +int sshkey_write(const struct sshkey *, FILE *); +int sshkey_read(struct sshkey *, char **); +u_int sshkey_size(const struct sshkey *); + +int sshkey_generate(int type, u_int bits, struct sshkey **keyp); +int sshkey_from_private(const struct sshkey *, struct sshkey **); + +int sshkey_is_shielded(struct sshkey *); +int sshkey_shield_private(struct sshkey *); +int sshkey_unshield_private(struct sshkey *); + +int sshkey_type_from_name(const char *); +int sshkey_is_cert(const struct sshkey *); +int sshkey_is_sk(const struct sshkey *); +int sshkey_type_is_cert(int); +int sshkey_type_plain(int); + +/* Returns non-zero if key name match sigalgs pattern list. (handles RSA) */ +int sshkey_match_keyname_to_sigalgs(const char *, const char *); + +int sshkey_to_certified(struct sshkey *); +int sshkey_drop_cert(struct sshkey *); +int sshkey_cert_copy(const struct sshkey *, struct sshkey *); +int sshkey_cert_check_authority(const struct sshkey *, int, int, int, + uint64_t, const char *, const char **); +int sshkey_cert_check_authority_now(const struct sshkey *, int, int, int, + const char *, const char **); +int sshkey_cert_check_host(const struct sshkey *, const char *, + int , const char *, const char **); +size_t sshkey_format_cert_validity(const struct sshkey_cert *, + char *, size_t) __attribute__((__bounded__(__string__, 2, 3))); +int sshkey_check_cert_sigtype(const struct sshkey *, const char *); + +int sshkey_certify(struct sshkey *, struct sshkey *, + const char *, const char *, const char *); +/* Variant allowing use of a custom signature function (e.g. for ssh-agent) */ +typedef int sshkey_certify_signer(struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, const char *, const char *, + u_int, void *); +int sshkey_certify_custom(struct sshkey *, struct sshkey *, const char *, + const char *, const char *, sshkey_certify_signer *, void *); + +int sshkey_ecdsa_nid_from_name(const char *); +int sshkey_curve_name_to_nid(const char *); +const char * sshkey_curve_nid_to_name(int); +u_int sshkey_curve_nid_to_bits(int); +int sshkey_ecdsa_bits_to_nid(int); +int sshkey_ecdsa_key_to_nid(EC_KEY *); +int sshkey_ec_nid_to_hash_alg(int nid); +int sshkey_ec_validate_public(const EC_GROUP *, const EC_POINT *); +int sshkey_ec_validate_private(const EC_KEY *); +const char *sshkey_ssh_name(const struct sshkey *); +const char *sshkey_ssh_name_plain(const struct sshkey *); +int sshkey_names_valid2(const char *, int); +char *sshkey_alg_list(int, int, int, char); + +int sshkey_from_blob(const u_char *, size_t, struct sshkey **); +int sshkey_fromb(struct sshbuf *, struct sshkey **); +int sshkey_froms(struct sshbuf *, struct sshkey **); +int sshkey_to_blob(const struct sshkey *, u_char **, size_t *); +int sshkey_to_base64(const struct sshkey *, char **); +int sshkey_putb(const struct sshkey *, struct sshbuf *); +int sshkey_puts(const struct sshkey *, struct sshbuf *); +int sshkey_puts_opts(const struct sshkey *, struct sshbuf *, + enum sshkey_serialize_rep); +int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); +int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); + +int sshkey_sign(struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, const char *, const char *, u_int); +int sshkey_verify(const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **); +int sshkey_check_sigtype(const u_char *, size_t, const char *); +const char *sshkey_sigalg_by_name(const char *); +int sshkey_get_sigtype(const u_char *, size_t, char **); + +/* for debug */ +void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); +void sshkey_dump_ec_key(const EC_KEY *); + +/* private key parsing and serialisation */ +int sshkey_private_serialize(struct sshkey *key, struct sshbuf *buf); +int sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, + enum sshkey_serialize_rep); +int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); + +/* private key file format parsing and serialisation */ +int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, + int format, const char *openssh_format_cipher, int openssh_format_rounds); +int sshkey_parse_private_fileblob(struct sshbuf *buffer, + const char *passphrase, struct sshkey **keyp, char **commentp); +int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, + const char *passphrase, struct sshkey **keyp, char **commentp); +int sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob, + int type, struct sshkey **pubkeyp); + +/* XXX should be internal, but used by ssh-keygen */ +int ssh_rsa_complete_crt_parameters(struct sshkey *, const BIGNUM *); + +/* stateful keys (e.g. XMSS) */ +int sshkey_set_filename(struct sshkey *, const char *); +int sshkey_enable_maxsign(struct sshkey *, u_int32_t); +u_int32_t sshkey_signatures_left(const struct sshkey *); +int sshkey_forward_state(const struct sshkey *, u_int32_t, int); +int sshkey_private_serialize_maxsign(struct sshkey *key, + struct sshbuf *buf, u_int32_t maxsign, int); + +void sshkey_sig_details_free(struct sshkey_sig_details *); + +#ifdef SSHKEY_INTERNAL +int ssh_rsa_sign(const struct sshkey *key, + u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, + const char *ident); +int ssh_rsa_verify(const struct sshkey *key, + const u_char *sig, size_t siglen, const u_char *data, size_t datalen, + const char *alg); +int ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_dss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +int ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +int ssh_ecdsa_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat, + struct sshkey_sig_details **detailsp); +int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_ed25519_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +int ssh_ed25519_sk_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat, + struct sshkey_sig_details **detailsp); +int ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat); +int ssh_xmss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat); +#endif + +#if !defined(WITH_OPENSSL) +# undef RSA +# undef DSA +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#elif !defined(OPENSSL_HAS_ECC) +# undef EC_KEY +# undef EC_GROUP +# undef EC_POINT +#endif + +#endif /* SSHKEY_H */ diff --git a/aosp/external/openssh/sshlogin.c b/aosp/external/openssh/sshlogin.c new file mode 100644 index 0000000000000000000000000000000000000000..82dd848191b1f158e679cc38bc381b58b78be06d --- /dev/null +++ b/aosp/external/openssh/sshlogin.c @@ -0,0 +1,173 @@ +/* $OpenBSD: sshlogin.c,v 1.35 2020/10/18 11:32:02 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file performs some of the things login(1) normally does. We cannot + * easily use something like login -p -h host -f user, because there are + * several different logins around, and it is hard to determined what kind of + * login the current system has. Also, we want to be able to execute commands + * on a tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * Copyright (c) 1999 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sshlogin.h" +#include "ssherr.h" +#include "loginrec.h" +#include "log.h" +#include "sshbuf.h" +#include "misc.h" +#include "servconf.h" + +extern struct sshbuf *loginmsg; +extern ServerOptions options; + +/* + * Returns the time when the user last logged in. Returns 0 if the + * information is not available. This must be called before record_login. + * The host the user logged in from will be returned in buf. + */ +time_t +get_last_login_time(uid_t uid, const char *logname, + char *buf, size_t bufsize) +{ + struct logininfo li; + + login_get_lastlog(&li, uid); + strlcpy(buf, li.hostname, bufsize); + return (time_t)li.tv_sec; +} + +/* + * Generate and store last login message. This must be done before + * login_login() is called and lastlog is updated. + */ +static void +store_lastlog_message(const char *user, uid_t uid) +{ +#ifndef NO_SSH_LASTLOG +# ifndef CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG + char hostname[HOST_NAME_MAX+1] = ""; + time_t last_login_time; +# endif + char *time_string; + int r; + + if (!options.print_lastlog) + return; + +# ifdef CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG + time_string = sys_auth_get_lastlogin_msg(user, uid); + if (time_string != NULL) { + if ((r = sshbuf_put(loginmsg, + time_string, strlen(time_string))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + free(time_string); + } +# else + last_login_time = get_last_login_time(uid, user, hostname, + sizeof(hostname)); + + if (last_login_time != 0) { + time_string = ctime(&last_login_time); + time_string[strcspn(time_string, "\n")] = '\0'; + if (strcmp(hostname, "") == 0) + r = sshbuf_putf(loginmsg, "Last login: %s\r\n", + time_string); + else + r = sshbuf_putf(loginmsg, "Last login: %s from %s\r\n", + time_string, hostname); + if (r != 0) + fatal_fr(r, "sshbuf_putf"); + } +# endif /* CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG */ +#endif /* NO_SSH_LASTLOG */ +} + +/* + * Records that the user has logged in. I wish these parts of operating + * systems were more standardized. + */ +void +record_login(pid_t pid, const char *tty, const char *user, uid_t uid, + const char *host, struct sockaddr *addr, socklen_t addrlen) +{ + struct logininfo *li; + + /* save previous login details before writing new */ + store_lastlog_message(user, uid); + + li = login_alloc_entry(pid, user, host, tty); + login_set_addr(li, addr, addrlen); + login_login(li); + login_free_entry(li); +} + +#ifdef LOGIN_NEEDS_UTMPX +void +record_utmp_only(pid_t pid, const char *ttyname, const char *user, + const char *host, struct sockaddr *addr, socklen_t addrlen) +{ + struct logininfo *li; + + li = login_alloc_entry(pid, user, host, ttyname); + login_set_addr(li, addr, addrlen); + login_utmp_only(li); + login_free_entry(li); +} +#endif + +/* Records that the user has logged out. */ +void +record_logout(pid_t pid, const char *tty, const char *user) +{ + struct logininfo *li; + + li = login_alloc_entry(pid, user, NULL, tty); + login_logout(li); + login_free_entry(li); +} diff --git a/aosp/external/openssh/sshlogin.h b/aosp/external/openssh/sshlogin.h new file mode 100644 index 0000000000000000000000000000000000000000..52119a979f3de3afae6fc7781ec98b9672329d47 --- /dev/null +++ b/aosp/external/openssh/sshlogin.h @@ -0,0 +1,23 @@ +/* $OpenBSD: sshlogin.h,v 1.8 2006/08/03 03:34:42 deraadt Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +void record_login(pid_t, const char *, const char *, uid_t, + const char *, struct sockaddr *, socklen_t); +void record_logout(pid_t, const char *, const char *); +time_t get_last_login_time(uid_t, const char *, char *, size_t); + +#ifdef LOGIN_NEEDS_UTMPX +void record_utmp_only(pid_t, const char *, const char *, const char *, + struct sockaddr *, socklen_t); +#endif diff --git a/aosp/external/openssh/sshpty.c b/aosp/external/openssh/sshpty.c new file mode 100644 index 0000000000000000000000000000000000000000..cae0b977a585379c5cc73ee322ba3f8f3e8fd387 --- /dev/null +++ b/aosp/external/openssh/sshpty.c @@ -0,0 +1,232 @@ +/* $OpenBSD: sshpty.c,v 1.34 2019/07/04 16:20:10 deraadt Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Allocating a pseudo-terminal, and making it the controlling tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UTIL_H +# include +#endif +#include + +#include "sshpty.h" +#include "log.h" +#include "misc.h" + +#ifdef HAVE_PTY_H +# include +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifdef __APPLE__ +# include +# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) +# define __APPLE_PRIVPTY__ +# endif +#endif + +/* + * Allocates and opens a pty. Returns 0 if no pty could be allocated, or + * nonzero if a pty was successfully allocated. On success, open file + * descriptors for the pty and tty sides and the name of the tty side are + * returned (the buffer must be able to hold at least 64 characters). + */ + +int +pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen) +{ + /* openpty(3) exists in OSF/1 and some other os'es */ + char *name; + int i; + + i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); + if (i == -1) { + error("openpty: %.100s", strerror(errno)); + return 0; + } + name = ttyname(*ttyfd); + if (!name) + fatal("openpty returns device for which ttyname fails."); + + strlcpy(namebuf, name, namebuflen); /* possible truncation */ + return 1; +} + +/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ + +void +pty_release(const char *tty) +{ +#if !defined(__APPLE_PRIVPTY__) && !defined(HAVE_OPENPTY) + if (chown(tty, (uid_t) 0, (gid_t) 0) == -1) + error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno)); + if (chmod(tty, (mode_t) 0666) == -1) + error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno)); +#endif /* !__APPLE_PRIVPTY__ && !HAVE_OPENPTY */ +} + +/* Makes the tty the process's controlling tty and sets it to sane modes. */ + +void +pty_make_controlling_tty(int *ttyfd, const char *tty) +{ + int fd; + + /* First disconnect from the old controlling tty. */ +#ifdef TIOCNOTTY + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } +#endif /* TIOCNOTTY */ + if (setsid() == -1) + error("setsid: %.100s", strerror(errno)); + + /* + * Verify that we are successfully disconnected from the controlling + * tty. + */ + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + error("Failed to disconnect from controlling tty."); + close(fd); + } + /* Make it our controlling tty. */ +#ifdef TIOCSCTTY + debug("Setting controlling tty using TIOCSCTTY."); + if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) + error("ioctl(TIOCSCTTY): %.100s", strerror(errno)); +#endif /* TIOCSCTTY */ +#ifdef NEED_SETPGRP + if (setpgrp(0,0) < 0) + error("SETPGRP %s",strerror(errno)); +#endif /* NEED_SETPGRP */ + fd = open(tty, O_RDWR); + if (fd == -1) + error("%.100s: %.100s", tty, strerror(errno)); + else + close(fd); + + /* Verify that we now have a controlling tty. */ + fd = open(_PATH_TTY, O_WRONLY); + if (fd == -1) + error("open /dev/tty failed - could not set controlling tty: %.100s", + strerror(errno)); + else + close(fd); +} + +/* Changes the window size associated with the pty. */ + +void +pty_change_window_size(int ptyfd, u_int row, u_int col, + u_int xpixel, u_int ypixel) +{ + struct winsize w; + + /* may truncate u_int -> u_short */ + w.ws_row = row; + w.ws_col = col; + w.ws_xpixel = xpixel; + w.ws_ypixel = ypixel; + (void) ioctl(ptyfd, TIOCSWINSZ, &w); +} + +void +pty_setowner(struct passwd *pw, const char *tty) +{ + struct group *grp; + gid_t gid; + mode_t mode; + struct stat st; + + /* Determine the group to make the owner of the tty. */ + grp = getgrnam("tty"); + if (grp == NULL) + debug("%s: no tty group", __func__); + gid = (grp != NULL) ? grp->gr_gid : pw->pw_gid; + mode = (grp != NULL) ? 0620 : 0600; + + /* + * Change owner and mode of the tty as required. + * Warn but continue if filesystem is read-only and the uids match/ + * tty is owned by root. + */ + if (stat(tty, &st) == -1) + fatal("stat(%.100s) failed: %.100s", tty, + strerror(errno)); + +#ifdef WITH_SELINUX + ssh_selinux_setup_pty(pw->pw_name, tty); +#endif + + if (st.st_uid != pw->pw_uid || st.st_gid != gid) { + if (chown(tty, pw->pw_uid, gid) == -1) { + if (errno == EROFS && + (st.st_uid == pw->pw_uid || st.st_uid == 0)) + debug("chown(%.100s, %u, %u) failed: %.100s", + tty, (u_int)pw->pw_uid, (u_int)gid, + strerror(errno)); + else + fatal("chown(%.100s, %u, %u) failed: %.100s", + tty, (u_int)pw->pw_uid, (u_int)gid, + strerror(errno)); + } + } + + if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { + if (chmod(tty, mode) == -1) { + if (errno == EROFS && + (st.st_mode & (S_IRGRP | S_IROTH)) == 0) + debug("chmod(%.100s, 0%o) failed: %.100s", + tty, (u_int)mode, strerror(errno)); + else + fatal("chmod(%.100s, 0%o) failed: %.100s", + tty, (u_int)mode, strerror(errno)); + } + } +} + +/* Disconnect from the controlling tty. */ +void +disconnect_controlling_tty(void) +{ +#ifdef TIOCNOTTY + int fd; + + if ((fd = open(_PATH_TTY, O_RDWR | O_NOCTTY)) >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } +#endif /* TIOCNOTTY */ +} diff --git a/aosp/external/openssh/sshpty.h b/aosp/external/openssh/sshpty.h new file mode 100644 index 0000000000000000000000000000000000000000..9ec7e9a15abd54792f3f36c7b7b0303a29e54662 --- /dev/null +++ b/aosp/external/openssh/sshpty.h @@ -0,0 +1,28 @@ +/* $OpenBSD: sshpty.h,v 1.13 2016/11/29 03:54:50 dtucker Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for allocating a pseudo-terminal and making it the controlling + * tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include + +struct termios *get_saved_tio(void); +void leave_raw_mode(int); +void enter_raw_mode(int); + +int pty_allocate(int *, int *, char *, size_t); +void pty_release(const char *); +void pty_make_controlling_tty(int *, const char *); +void pty_change_window_size(int, u_int, u_int, u_int, u_int); +void pty_setowner(struct passwd *, const char *); +void disconnect_controlling_tty(void); diff --git a/aosp/external/openssh/sshsig.c b/aosp/external/openssh/sshsig.c new file mode 100644 index 0000000000000000000000000000000000000000..1e3b63982ba886ed00bfeb2934989517cd6901de --- /dev/null +++ b/aosp/external/openssh/sshsig.c @@ -0,0 +1,1147 @@ +/* $OpenBSD: sshsig.c,v 1.29 2022/03/30 04:27:51 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include "authfd.h" +#include "authfile.h" +#include "log.h" +#include "misc.h" +#include "sshbuf.h" +#include "sshsig.h" +#include "ssherr.h" +#include "sshkey.h" +#include "match.h" +#include "digest.h" + +#define SIG_VERSION 0x01 +#define MAGIC_PREAMBLE "SSHSIG" +#define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1) +#define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n" +#define END_SIGNATURE "-----END SSH SIGNATURE-----" +#define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */ +#define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256" +#define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */ +#define HASHALG_ALLOWED "sha256,sha512" + +int +sshsig_armor(const struct sshbuf *blob, struct sshbuf **out) +{ + struct sshbuf *buf = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + *out = NULL; + + if ((buf = sshbuf_new()) == NULL) { + error_f("sshbuf_new failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((r = sshbuf_put(buf, BEGIN_SIGNATURE, + sizeof(BEGIN_SIGNATURE)-1)) != 0) { + error_fr(r, "sshbuf_putf"); + goto out; + } + + if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) { + error_fr(r, "base64 encode signature"); + goto out; + } + + if ((r = sshbuf_put(buf, END_SIGNATURE, + sizeof(END_SIGNATURE)-1)) != 0 || + (r = sshbuf_put_u8(buf, '\n')) != 0) { + error_fr(r, "sshbuf_put"); + goto out; + } + /* success */ + *out = buf; + buf = NULL; /* transferred */ + r = 0; + out: + sshbuf_free(buf); + return r; +} + +int +sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out) +{ + int r; + size_t eoffset = 0; + struct sshbuf *buf = NULL; + struct sshbuf *sbuf = NULL; + char *b64 = NULL; + + if ((sbuf = sshbuf_fromb(sig)) == NULL) { + error_f("sshbuf_fromb failed"); + return SSH_ERR_ALLOC_FAIL; + } + + if ((r = sshbuf_cmp(sbuf, 0, + BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) { + error("Couldn't parse signature: missing header"); + goto done; + } + + if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) { + error_fr(r, "consume"); + goto done; + } + + if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE, + sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) { + error("Couldn't parse signature: missing footer"); + goto done; + } + + if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) { + error_fr(r, "consume"); + goto done; + } + + if ((b64 = sshbuf_dup_string(sbuf)) == NULL) { + error_f("sshbuf_dup_string failed"); + r = SSH_ERR_ALLOC_FAIL; + goto done; + } + + if ((buf = sshbuf_new()) == NULL) { + error_f("sshbuf_new() failed"); + r = SSH_ERR_ALLOC_FAIL; + goto done; + } + + if ((r = sshbuf_b64tod(buf, b64)) != 0) { + error_fr(r, "decode base64"); + goto done; + } + + /* success */ + *out = buf; + r = 0; + buf = NULL; /* transferred */ +done: + sshbuf_free(buf); + sshbuf_free(sbuf); + free(b64); + return r; +} + +static int +sshsig_wrap_sign(struct sshkey *key, const char *hashalg, + const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message, + const char *sig_namespace, struct sshbuf **out, + sshsig_signer *signer, void *signer_ctx) +{ + int r; + size_t slen = 0; + u_char *sig = NULL; + struct sshbuf *blob = NULL; + struct sshbuf *tosign = NULL; + const char *sign_alg = NULL; + + if ((tosign = sshbuf_new()) == NULL || + (blob = sshbuf_new()) == NULL) { + error_f("sshbuf_new failed"); + r = SSH_ERR_ALLOC_FAIL; + goto done; + } + + if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || + (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 || + (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */ + (r = sshbuf_put_cstring(tosign, hashalg)) != 0 || + (r = sshbuf_put_stringb(tosign, h_message)) != 0) { + error_fr(r, "assemble message to sign"); + goto done; + } + + /* If using RSA keys then default to a good signature algorithm */ + if (sshkey_type_plain(key->type) == KEY_RSA) + sign_alg = RSA_SIGN_ALG; + + if (signer != NULL) { + if ((r = signer(key, &sig, &slen, + sshbuf_ptr(tosign), sshbuf_len(tosign), + sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) { + error_r(r, "Couldn't sign message (signer)"); + goto done; + } + } else { + if ((r = sshkey_sign(key, &sig, &slen, + sshbuf_ptr(tosign), sshbuf_len(tosign), + sign_alg, sk_provider, sk_pin, 0)) != 0) { + error_r(r, "Couldn't sign message"); + goto done; + } + } + + if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || + (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 || + (r = sshkey_puts(key, blob)) != 0 || + (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 || + (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */ + (r = sshbuf_put_cstring(blob, hashalg)) != 0 || + (r = sshbuf_put_string(blob, sig, slen)) != 0) { + error_fr(r, "assemble signature object"); + goto done; + } + + if (out != NULL) { + *out = blob; + blob = NULL; + } + r = 0; +done: + free(sig); + sshbuf_free(blob); + sshbuf_free(tosign); + return r; +} + +/* Check preamble and version. */ +static int +sshsig_parse_preamble(struct sshbuf *buf) +{ + int r = SSH_ERR_INTERNAL_ERROR; + uint32_t sversion; + + if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || + (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 || + (r = sshbuf_get_u32(buf, &sversion)) != 0) { + error("Couldn't verify signature: invalid format"); + return r; + } + + if (sversion > SIG_VERSION) { + error("Signature version %lu is larger than supported " + "version %u", (unsigned long)sversion, SIG_VERSION); + return SSH_ERR_INVALID_FORMAT; + } + return 0; +} + +static int +sshsig_check_hashalg(const char *hashalg) +{ + if (hashalg == NULL || + match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1) + return 0; + error_f("unsupported hash algorithm \"%.100s\"", hashalg); + return SSH_ERR_SIGN_ALG_UNSUPPORTED; +} + +static int +sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp) +{ + struct sshbuf *buf = NULL; + char *hashalg = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if (hashalgp != NULL) + *hashalgp = NULL; + if ((buf = sshbuf_fromb(signature)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshsig_parse_preamble(buf)) != 0) + goto done; + if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || + (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || + (r = sshbuf_get_string(buf, NULL, NULL)) != 0 || + (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 || + (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) { + error_fr(r, "parse signature object"); + goto done; + } + + /* success */ + r = 0; + *hashalgp = hashalg; + hashalg = NULL; + done: + free(hashalg); + sshbuf_free(buf); + return r; +} + +static int +sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg, + const struct sshbuf *h_message, const char *expect_namespace, + struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details) +{ + int r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *buf = NULL, *toverify = NULL; + struct sshkey *key = NULL; + const u_char *sig; + char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL; + size_t siglen; + + debug_f("verify message length %zu", sshbuf_len(h_message)); + if (sig_details != NULL) + *sig_details = NULL; + if (sign_keyp != NULL) + *sign_keyp = NULL; + + if ((toverify = sshbuf_new()) == NULL) { + error_f("sshbuf_new failed"); + r = SSH_ERR_ALLOC_FAIL; + goto done; + } + if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE, + MAGIC_PREAMBLE_LEN)) != 0 || + (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 || + (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */ + (r = sshbuf_put_cstring(toverify, hashalg)) != 0 || + (r = sshbuf_put_stringb(toverify, h_message)) != 0) { + error_fr(r, "assemble message to verify"); + goto done; + } + + if ((r = sshsig_parse_preamble(signature)) != 0) + goto done; + + if ((r = sshkey_froms(signature, &key)) != 0 || + (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 || + (r = sshbuf_get_string(signature, NULL, NULL)) != 0 || + (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 || + (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) { + error_fr(r, "parse signature object"); + goto done; + } + + if (sshbuf_len(signature) != 0) { + error("Signature contains trailing data"); + r = SSH_ERR_INVALID_FORMAT; + goto done; + } + + if (strcmp(expect_namespace, got_namespace) != 0) { + error("Couldn't verify signature: namespace does not match"); + debug_f("expected namespace \"%s\" received \"%s\"", + expect_namespace, got_namespace); + r = SSH_ERR_SIGNATURE_INVALID; + goto done; + } + if (strcmp(hashalg, sig_hashalg) != 0) { + error("Couldn't verify signature: hash algorithm mismatch"); + debug_f("expected algorithm \"%s\" received \"%s\"", + hashalg, sig_hashalg); + r = SSH_ERR_SIGNATURE_INVALID; + goto done; + } + /* Ensure that RSA keys use an acceptable signature algorithm */ + if (sshkey_type_plain(key->type) == KEY_RSA) { + if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) { + error_r(r, "Couldn't verify signature: unable to get " + "signature type"); + goto done; + } + if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) { + error("Couldn't verify signature: unsupported RSA " + "signature algorithm %s", sigtype); + r = SSH_ERR_SIGN_ALG_UNSUPPORTED; + goto done; + } + } + if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify), + sshbuf_len(toverify), NULL, 0, sig_details)) != 0) { + error_r(r, "Signature verification failed"); + goto done; + } + + /* success */ + r = 0; + if (sign_keyp != NULL) { + *sign_keyp = key; + key = NULL; /* transferred */ + } +done: + free(got_namespace); + free(sigtype); + free(sig_hashalg); + sshbuf_free(buf); + sshbuf_free(toverify); + sshkey_free(key); + return r; +} + +static int +hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp) +{ + char *hex, hash[SSH_DIGEST_MAX_LENGTH]; + int alg, r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + + *bp = NULL; + memset(hash, 0, sizeof(hash)); + + if ((r = sshsig_check_hashalg(hashalg)) != 0) + return r; + if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { + error_f("can't look up hash algorithm %s", hashalg); + return SSH_ERR_INTERNAL_ERROR; + } + if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) { + error_fr(r, "ssh_digest_buffer"); + return r; + } + if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { + debug3_f("final hash: %s", hex); + freezero(hex, strlen(hex)); + } + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { + error_fr(r, "sshbuf_put"); + goto out; + } + *bp = b; + b = NULL; /* transferred */ + /* success */ + r = 0; + out: + sshbuf_free(b); + explicit_bzero(hash, sizeof(hash)); + return r; +} + +int +sshsig_signb(struct sshkey *key, const char *hashalg, + const char *sk_provider, const char *sk_pin, + const struct sshbuf *message, const char *sig_namespace, + struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) +{ + struct sshbuf *b = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if (hashalg == NULL) + hashalg = HASHALG_DEFAULT; + if (out != NULL) + *out = NULL; + if ((r = hash_buffer(message, hashalg, &b)) != 0) { + error_fr(r, "hash buffer"); + goto out; + } + if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, + sig_namespace, out, signer, signer_ctx)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +int +sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message, + const char *expect_namespace, struct sshkey **sign_keyp, + struct sshkey_sig_details **sig_details) +{ + struct sshbuf *b = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + char *hashalg = NULL; + + if (sig_details != NULL) + *sig_details = NULL; + if (sign_keyp != NULL) + *sign_keyp = NULL; + if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) + return r; + debug_f("signature made with hash \"%s\"", hashalg); + if ((r = hash_buffer(message, hashalg, &b)) != 0) { + error_fr(r, "hash buffer"); + goto out; + } + if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, + sign_keyp, sig_details)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + free(hashalg); + return r; +} + +static int +hash_file(int fd, const char *hashalg, struct sshbuf **bp) +{ + char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH]; + ssize_t n, total = 0; + struct ssh_digest_ctx *ctx; + int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + + *bp = NULL; + memset(hash, 0, sizeof(hash)); + + if ((r = sshsig_check_hashalg(hashalg)) != 0) + return r; + if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { + error_f("can't look up hash algorithm %s", hashalg); + return SSH_ERR_INTERNAL_ERROR; + } + if ((ctx = ssh_digest_start(alg)) == NULL) { + error_f("ssh_digest_start failed"); + return SSH_ERR_INTERNAL_ERROR; + } + for (;;) { + if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + oerrno = errno; + error_f("read: %s", strerror(errno)); + ssh_digest_free(ctx); + errno = oerrno; + r = SSH_ERR_SYSTEM_ERROR; + goto out; + } else if (n == 0) { + debug2_f("hashed %zu bytes", total); + break; /* EOF */ + } + total += (size_t)n; + if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) { + error_fr(r, "ssh_digest_update"); + goto out; + } + } + if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) { + error_fr(r, "ssh_digest_final"); + goto out; + } + if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { + debug3_f("final hash: %s", hex); + freezero(hex, strlen(hex)); + } + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { + error_fr(r, "sshbuf_put"); + goto out; + } + *bp = b; + b = NULL; /* transferred */ + /* success */ + r = 0; + out: + sshbuf_free(b); + ssh_digest_free(ctx); + explicit_bzero(hash, sizeof(hash)); + return r; +} + +int +sshsig_sign_fd(struct sshkey *key, const char *hashalg, + const char *sk_provider, const char *sk_pin, + int fd, const char *sig_namespace, struct sshbuf **out, + sshsig_signer *signer, void *signer_ctx) +{ + struct sshbuf *b = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if (hashalg == NULL) + hashalg = HASHALG_DEFAULT; + if (out != NULL) + *out = NULL; + if ((r = hash_file(fd, hashalg, &b)) != 0) { + error_fr(r, "hash_file"); + return r; + } + if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, + sig_namespace, out, signer, signer_ctx)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + return r; +} + +int +sshsig_verify_fd(struct sshbuf *signature, int fd, + const char *expect_namespace, struct sshkey **sign_keyp, + struct sshkey_sig_details **sig_details) +{ + struct sshbuf *b = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + char *hashalg = NULL; + + if (sig_details != NULL) + *sig_details = NULL; + if (sign_keyp != NULL) + *sign_keyp = NULL; + if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) + return r; + debug_f("signature made with hash \"%s\"", hashalg); + if ((r = hash_file(fd, hashalg, &b)) != 0) { + error_fr(r, "hash_file"); + goto out; + } + if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, + sign_keyp, sig_details)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(b); + free(hashalg); + return r; +} + +struct sshsigopt { + int ca; + char *namespaces; + uint64_t valid_after, valid_before; +}; + +struct sshsigopt * +sshsigopt_parse(const char *opts, const char *path, u_long linenum, + const char **errstrp) +{ + struct sshsigopt *ret; + int r; + char *opt; + const char *errstr = NULL; + + if ((ret = calloc(1, sizeof(*ret))) == NULL) + return NULL; + if (opts == NULL || *opts == '\0') + return ret; /* Empty options yields empty options :) */ + + while (*opts && *opts != ' ' && *opts != '\t') { + /* flag options */ + if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { + ret->ca = 1; + } else if (opt_match(&opts, "namespaces")) { + if (ret->namespaces != NULL) { + errstr = "multiple \"namespaces\" clauses"; + goto fail; + } + ret->namespaces = opt_dequote(&opts, &errstr); + if (ret->namespaces == NULL) + goto fail; + } else if (opt_match(&opts, "valid-after")) { + if (ret->valid_after != 0) { + errstr = "multiple \"valid-after\" clauses"; + goto fail; + } + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + if (parse_absolute_time(opt, &ret->valid_after) != 0 || + ret->valid_after == 0) { + free(opt); + errstr = "invalid \"valid-after\" time"; + goto fail; + } + free(opt); + } else if (opt_match(&opts, "valid-before")) { + if (ret->valid_before != 0) { + errstr = "multiple \"valid-before\" clauses"; + goto fail; + } + if ((opt = opt_dequote(&opts, &errstr)) == NULL) + goto fail; + if (parse_absolute_time(opt, &ret->valid_before) != 0 || + ret->valid_before == 0) { + free(opt); + errstr = "invalid \"valid-before\" time"; + goto fail; + } + free(opt); + } + /* + * Skip the comma, and move to the next option + * (or break out if there are no more). + */ + if (*opts == '\0' || *opts == ' ' || *opts == '\t') + break; /* End of options. */ + /* Anything other than a comma is an unknown option */ + if (*opts != ',') { + errstr = "unknown key option"; + goto fail; + } + opts++; + if (*opts == '\0') { + errstr = "unexpected end-of-options"; + goto fail; + } + } + /* final consistency check */ + if (ret->valid_after != 0 && ret->valid_before != 0 && + ret->valid_before <= ret->valid_after) { + errstr = "\"valid-before\" time is before \"valid-after\""; + goto fail; + } + /* success */ + return ret; + fail: + if (errstrp != NULL) + *errstrp = errstr; + sshsigopt_free(ret); + return NULL; +} + +void +sshsigopt_free(struct sshsigopt *opts) +{ + if (opts == NULL) + return; + free(opts->namespaces); + free(opts); +} + +static int +parse_principals_key_and_options(const char *path, u_long linenum, char *line, + const char *required_principal, char **principalsp, struct sshkey **keyp, + struct sshsigopt **sigoptsp) +{ + char *opts = NULL, *tmp, *cp, *principals = NULL; + const char *reason = NULL; + struct sshsigopt *sigopts = NULL; + struct sshkey *key = NULL; + int r = SSH_ERR_INTERNAL_ERROR; + + if (principalsp != NULL) + *principalsp = NULL; + if (sigoptsp != NULL) + *sigoptsp = NULL; + if (keyp != NULL) + *keyp = NULL; + + cp = line; + cp = cp + strspn(cp, " \t"); /* skip leading whitespace */ + if (*cp == '#' || *cp == '\0') + return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */ + + /* format: identity[,identity...] [option[,option...]] key */ + if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) { + error("%s:%lu: invalid line", path, linenum); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if ((principals = strdup(tmp)) == NULL) { + error_f("strdup failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + /* + * Bail out early if we're looking for a particular principal and this + * line does not list it. + */ + if (required_principal != NULL) { + if (match_pattern_list(required_principal, + principals, 0) != 1) { + /* principal didn't match */ + r = SSH_ERR_KEY_NOT_FOUND; + goto out; + } + debug_f("%s:%lu: matched principal \"%s\"", + path, linenum, required_principal); + } + + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { + error_f("sshkey_new failed"); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshkey_read(key, &cp) != 0) { + /* no key? Check for options */ + opts = cp; + if (sshkey_advance_past_options(&cp) != 0) { + error("%s:%lu: invalid options", path, linenum); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (cp == NULL || *cp == '\0') { + error("%s:%lu: missing key", path, linenum); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + *cp++ = '\0'; + skip_space(&cp); + if (sshkey_read(key, &cp) != 0) { + error("%s:%lu: invalid key", path, linenum); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + } + debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts); + if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) { + error("%s:%lu: bad options: %s", path, linenum, reason); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + /* success */ + if (principalsp != NULL) { + *principalsp = principals; + principals = NULL; /* transferred */ + } + if (sigoptsp != NULL) { + *sigoptsp = sigopts; + sigopts = NULL; /* transferred */ + } + if (keyp != NULL) { + *keyp = key; + key = NULL; /* transferred */ + } + r = 0; + out: + free(principals); + sshsigopt_free(sigopts); + sshkey_free(key); + return r; +} + +static int +cert_filter_principals(const char *path, u_long linenum, + char **principalsp, const struct sshkey *cert, uint64_t verify_time) +{ + char *cp, *oprincipals, *principals; + const char *reason; + struct sshbuf *nprincipals; + int r = SSH_ERR_INTERNAL_ERROR, success = 0; + u_int i; + + oprincipals = principals = *principalsp; + *principalsp = NULL; + + if ((nprincipals = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + + while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { + /* Check certificate validity */ + if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, + verify_time, NULL, &reason)) != 0) { + debug("%s:%lu: principal \"%s\" not authorized: %s", + path, linenum, cp, reason); + continue; + } + /* Return all matching principal names from the cert */ + for (i = 0; i < cert->cert->nprincipals; i++) { + if (match_pattern(cert->cert->principals[i], cp)) { + if ((r = sshbuf_putf(nprincipals, "%s%s", + sshbuf_len(nprincipals) != 0 ? "," : "", + cert->cert->principals[i])) != 0) { + error_f("buffer error"); + goto out; + } + } + } + } + if (sshbuf_len(nprincipals) == 0) { + error("%s:%lu: no valid principals found", path, linenum); + r = SSH_ERR_KEY_CERT_INVALID; + goto out; + } + if ((principals = sshbuf_dup_string(nprincipals)) == NULL) { + error_f("buffer error"); + goto out; + } + /* success */ + success = 1; + *principalsp = principals; + out: + sshbuf_free(nprincipals); + free(oprincipals); + return success ? 0 : r; +} + +static int +check_allowed_keys_line(const char *path, u_long linenum, char *line, + const struct sshkey *sign_key, const char *principal, + const char *sig_namespace, uint64_t verify_time, char **principalsp) +{ + struct sshkey *found_key = NULL; + char *principals = NULL; + int r, success = 0; + const char *reason = NULL; + struct sshsigopt *sigopts = NULL; + char tvalid[64], tverify[64]; + + if (principalsp != NULL) + *principalsp = NULL; + + /* Parse the line */ + if ((r = parse_principals_key_and_options(path, linenum, line, + principal, &principals, &found_key, &sigopts)) != 0) { + /* error already logged */ + goto done; + } + + if (!sigopts->ca && sshkey_equal(found_key, sign_key)) { + /* Exact match of key */ + debug("%s:%lu: matched key", path, linenum); + } else if (sigopts->ca && sshkey_is_cert(sign_key) && + sshkey_equal_public(sign_key->cert->signature_key, found_key)) { + if (principal) { + /* Match certificate CA key with specified principal */ + if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0, + verify_time, principal, &reason)) != 0) { + error("%s:%lu: certificate not authorized: %s", + path, linenum, reason); + goto done; + } + debug("%s:%lu: matched certificate CA key", + path, linenum); + } else { + /* No principal specified - find all matching ones */ + if ((r = cert_filter_principals(path, linenum, + &principals, sign_key, verify_time)) != 0) { + /* error already displayed */ + debug_r(r, "%s:%lu: cert_filter_principals", + path, linenum); + goto done; + } + debug("%s:%lu: matched certificate CA key", + path, linenum); + } + } else { + /* Didn't match key */ + goto done; + } + + /* Check whether options preclude the use of this key */ + if (sigopts->namespaces != NULL && sig_namespace != NULL && + match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) { + error("%s:%lu: key is not permitted for use in signature " + "namespace \"%s\"", path, linenum, sig_namespace); + goto done; + } + + /* check key time validity */ + format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify)); + if (sigopts->valid_after != 0 && + (uint64_t)verify_time < sigopts->valid_after) { + format_absolute_time(sigopts->valid_after, + tvalid, sizeof(tvalid)); + error("%s:%lu: key is not yet valid: " + "verify time %s < valid-after %s", path, linenum, + tverify, tvalid); + goto done; + } + if (sigopts->valid_before != 0 && + (uint64_t)verify_time > sigopts->valid_before) { + format_absolute_time(sigopts->valid_before, + tvalid, sizeof(tvalid)); + error("%s:%lu: key has expired: " + "verify time %s > valid-before %s", path, linenum, + tverify, tvalid); + goto done; + } + success = 1; + + done: + if (success && principalsp != NULL) { + *principalsp = principals; + principals = NULL; /* transferred */ + } + free(principals); + sshkey_free(found_key); + sshsigopt_free(sigopts); + return success ? 0 : SSH_ERR_KEY_NOT_FOUND; +} + +int +sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, + const char *principal, const char *sig_namespace, uint64_t verify_time) +{ + FILE *f = NULL; + char *line = NULL; + size_t linesize = 0; + u_long linenum = 0; + int r = SSH_ERR_INTERNAL_ERROR, oerrno; + + /* Check key and principal against file */ + if ((f = fopen(path, "r")) == NULL) { + oerrno = errno; + error("Unable to open allowed keys file \"%s\": %s", + path, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + + while (getline(&line, &linesize, f) != -1) { + linenum++; + r = check_allowed_keys_line(path, linenum, line, sign_key, + principal, sig_namespace, verify_time, NULL); + free(line); + line = NULL; + linesize = 0; + if (r == SSH_ERR_KEY_NOT_FOUND) + continue; + else if (r == 0) { + /* success */ + fclose(f); + return 0; + } else + break; + } + /* Either we hit an error parsing or we simply didn't find the key */ + fclose(f); + free(line); + return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; +} + +int +sshsig_find_principals(const char *path, const struct sshkey *sign_key, + uint64_t verify_time, char **principals) +{ + FILE *f = NULL; + char *line = NULL; + size_t linesize = 0; + u_long linenum = 0; + int r = SSH_ERR_INTERNAL_ERROR, oerrno; + + if ((f = fopen(path, "r")) == NULL) { + oerrno = errno; + error("Unable to open allowed keys file \"%s\": %s", + path, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + + r = SSH_ERR_KEY_NOT_FOUND; + while (getline(&line, &linesize, f) != -1) { + linenum++; + r = check_allowed_keys_line(path, linenum, line, + sign_key, NULL, NULL, verify_time, principals); + free(line); + line = NULL; + linesize = 0; + if (r == SSH_ERR_KEY_NOT_FOUND) + continue; + else if (r == 0) { + /* success */ + fclose(f); + return 0; + } else + break; + } + free(line); + /* Either we hit an error parsing or we simply didn't find the key */ + if (ferror(f) != 0) { + oerrno = errno; + fclose(f); + error("Unable to read allowed keys file \"%s\": %s", + path, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + fclose(f); + return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; +} + +int +sshsig_match_principals(const char *path, const char *principal, + char ***principalsp, size_t *nprincipalsp) +{ + FILE *f = NULL; + char *found, *line = NULL, **principals = NULL, **tmp; + size_t i, nprincipals = 0, linesize = 0; + u_long linenum = 0; + int oerrno = 0, r, ret = 0; + + if (principalsp != NULL) + *principalsp = NULL; + if (nprincipalsp != NULL) + *nprincipalsp = 0; + + /* Check key and principal against file */ + if ((f = fopen(path, "r")) == NULL) { + oerrno = errno; + error("Unable to open allowed keys file \"%s\": %s", + path, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + + while (getline(&line, &linesize, f) != -1) { + linenum++; + /* Parse the line */ + if ((r = parse_principals_key_and_options(path, linenum, line, + principal, &found, NULL, NULL)) != 0) { + if (r == SSH_ERR_KEY_NOT_FOUND) + continue; + ret = r; + oerrno = errno; + break; /* unexpected error */ + } + if ((tmp = recallocarray(principals, nprincipals, + nprincipals + 1, sizeof(*principals))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + free(found); + break; + } + principals = tmp; + principals[nprincipals++] = found; /* transferred */ + free(line); + line = NULL; + linesize = 0; + } + fclose(f); + + if (ret == 0) { + if (nprincipals == 0) + ret = SSH_ERR_KEY_NOT_FOUND; + if (principalsp != NULL) { + *principalsp = principals; + principals = NULL; /* transferred */ + } + if (nprincipalsp != 0) { + *nprincipalsp = nprincipals; + nprincipals = 0; + } + } + + for (i = 0; i < nprincipals; i++) + free(principals[i]); + free(principals); + + errno = oerrno; + return ret; +} + +int +sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) +{ + struct sshkey *pk = NULL; + int r = SSH_ERR_SIGNATURE_INVALID; + + if (pubkey == NULL) + return SSH_ERR_INTERNAL_ERROR; + if ((r = sshsig_parse_preamble(signature)) != 0) + return r; + if ((r = sshkey_froms(signature, &pk)) != 0) + return r; + + *pubkey = pk; + pk = NULL; + return 0; +} diff --git a/aosp/external/openssh/sshsig.h b/aosp/external/openssh/sshsig.h new file mode 100644 index 0000000000000000000000000000000000000000..ac5577962fba743d1738784043f4af3ef51a3975 --- /dev/null +++ b/aosp/external/openssh/sshsig.h @@ -0,0 +1,111 @@ +/* $OpenBSD: sshsig.h,v 1.11 2021/11/27 07:14:46 djm Exp $ */ +/* + * Copyright (c) 2019 Google LLC + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SSHSIG_H +#define SSHSIG_H + +struct sshbuf; +struct sshkey; +struct sshsigopt; +struct sshkey_sig_details; + +typedef int sshsig_signer(struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, const char *, const char *, + u_int, void *); + +/* Buffer-oriented API */ + +/* + * Creates a detached SSH signature for a given buffer. + * Returns 0 on success or a negative SSH_ERR_* error code on failure. + * out is populated with the detached signature, or NULL on failure. + */ +int sshsig_signb(struct sshkey *key, const char *hashalg, + const char *sk_provider, const char *sk_pin, const struct sshbuf *message, + const char *sig_namespace, struct sshbuf **out, + sshsig_signer *signer, void *signer_ctx); + +/* + * Verifies that a detached signature is valid and optionally returns key + * used to sign via argument. + * Returns 0 on success or a negative SSH_ERR_* error code on failure. + */ +int sshsig_verifyb(struct sshbuf *signature, + const struct sshbuf *message, const char *sig_namespace, + struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details); + +/* File/FD-oriented API */ + +/* + * Creates a detached SSH signature for a given file. + * Returns 0 on success or a negative SSH_ERR_* error code on failure. + * out is populated with the detached signature, or NULL on failure. + */ +int sshsig_sign_fd(struct sshkey *key, const char *hashalg, + const char *sk_provider, const char *sk_pin, + int fd, const char *sig_namespace, + struct sshbuf **out, sshsig_signer *signer, void *signer_ctx); + +/* + * Verifies that a detached signature over a file is valid and optionally + * returns key used to sign via argument. + * Returns 0 on success or a negative SSH_ERR_* error code on failure. + */ +int sshsig_verify_fd(struct sshbuf *signature, int fd, + const char *sig_namespace, struct sshkey **sign_keyp, + struct sshkey_sig_details **sig_details); + +/* Utility functions */ + +/* + * Return a base64 encoded "ASCII armoured" version of a raw signature. + */ +int sshsig_armor(const struct sshbuf *blob, struct sshbuf **out); + +/* + * Decode a base64 encoded armoured signature to a raw signature. + */ +int sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out); + +/* + * Checks whether a particular key/principal/namespace is permitted by + * an allowed_keys file. Returns 0 on success. + */ +int sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, + const char *principal, const char *ns, uint64_t verify_time); + +/* Parse zero or more allowed_keys signature options */ +struct sshsigopt *sshsigopt_parse(const char *opts, + const char *path, u_long linenum, const char **errstrp); + +/* Free signature options */ +void sshsigopt_free(struct sshsigopt *opts); + +/* Get public key from signature */ +int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey); + +/* Find principal in allowed_keys file, given a sshkey. Returns + * 0 on success. + */ +int sshsig_find_principals(const char *path, const struct sshkey *sign_key, + uint64_t verify_time, char **principal); + +/* Find all principals in allowed_keys file matching *principal */ +int sshsig_match_principals(const char *path, + const char *principal, char ***principalsp, size_t *nprincipalsp); + +#endif /* SSHSIG_H */ diff --git a/aosp/external/openssh/sshtty.c b/aosp/external/openssh/sshtty.c new file mode 100644 index 0000000000000000000000000000000000000000..d214ce3bb1a2e0a1f6cc36891b8a0518177dbf87 --- /dev/null +++ b/aosp/external/openssh/sshtty.c @@ -0,0 +1,96 @@ +/* $OpenBSD: sshtty.c,v 1.14 2010/01/09 05:04:24 djm Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#include "sshpty.h" + +static struct termios _saved_tio; +static int _in_raw_mode = 0; + +struct termios * +get_saved_tio(void) +{ + return _in_raw_mode ? &_saved_tio : NULL; +} + +void +leave_raw_mode(int quiet) +{ + if (!_in_raw_mode) + return; + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) { + if (!quiet) + perror("tcsetattr"); + } else + _in_raw_mode = 0; +} + +void +enter_raw_mode(int quiet) +{ + struct termios tio; + + if (tcgetattr(fileno(stdin), &tio) == -1) { + if (!quiet) + perror("tcgetattr"); + return; + } + _saved_tio = tio; + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); +#ifdef IUCLC + tio.c_iflag &= ~IUCLC; +#endif + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); +#ifdef IEXTEN + tio.c_lflag &= ~IEXTEN; +#endif + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) { + if (!quiet) + perror("tcsetattr"); + } else + _in_raw_mode = 1; +} diff --git a/aosp/external/openssh/start-ssh b/aosp/external/openssh/start-ssh new file mode 100755 index 0000000000000000000000000000000000000000..ae3bc2528633dd30f5cd093011cbbe3cf78beccf --- /dev/null +++ b/aosp/external/openssh/start-ssh @@ -0,0 +1,51 @@ +#!/system/bin/sh + +umask 077 + +# DEBUG=1 + +RSA_KEY=/data/ssh/ssh_host_rsa_key +RSA_PUB_KEY=/data/ssh/ssh_host_rsa_key.pub +ED25519_KEY=/data/ssh/ssh_host_ed25519_key +ED25519_PUB_KEY=/data/ssh/ssh_host_ed25519_key.pub +ECDSA_KEY=/data/ssh/ssh_host_ecdsa_key +ECDSA_PUB_KEY=/data/ssh/ssh_host_ecdsa_key.pub +AUTHORIZED_KEYS=/data/ssh/authorized_keys +DEFAULT_AUTHORIZED_KEYS=/system/etc/security/authorized_keys.default +SSHD_CONFIG=/data/ssh/sshd_config +DEFAULT_SSHD_CONFIG=/system/etc/ssh/sshd_config + +if [ ! -f $RSA_KEY ]; then + /system/bin/ssh-keygen -t rsa -f $RSA_KEY -N "" + chmod 600 /$RSA_KEY + chmod 644 $RSA_PUB_KEY +fi + +if [ ! -f $ECDSA_KEY ]; then + /system/bin/ssh-keygen -t ecdsa -f $ECDSA_KEY -N "" + chmod 600 /$ECDSA_KEY + chmod 644 $ECDSA_PUB_KEY +fi + +if [ ! -f $ED25519_KEY ]; then + /system/bin/ssh-keygen -t ed25519 -f $ED25519_KEY -N "" + chmod 600 /$ED25519_KEY + chmod 644 $ED25519_PUB_KEY +fi + +if [[ ! -f $AUTHORIZED_KEYS && -f $DEFAULT_AUTHORIZED_KEYS ]]; then + cat $DEFAULT_AUTHORIZED_KEYS > $AUTHORIZED_KEYS +fi + +if [[ ! -f $SSHD_CONFIG && -f $DEFAULT_SSHD_CONFIG ]]; then + SSHD_CONFIG="$DEFAULT_SSHD_CONFIG" +fi + + +if [ "1" == "$DEBUG" ] ; then + # run sshd in debug mode and capture output to logcat + /system/bin/logwrapper /system/bin/sshd -f "$SSHD_CONFIG" -D -d +else + # don't daemonize - otherwise we can't stop the sshd service + /system/bin/sshd -f "$SSHD_CONFIG" -D +fi diff --git a/aosp/external/openssh/survey.sh.in b/aosp/external/openssh/survey.sh.in new file mode 100644 index 0000000000000000000000000000000000000000..d6075a6b3d70915bb53a7121eec77d08f00e73f2 --- /dev/null +++ b/aosp/external/openssh/survey.sh.in @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Copyright (c) 2004, 2005 Darren Tucker +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +host="@host@" +AWK="@AWK@" +CC="@CC@" +CPP="@CPP@" +CFLAGS="@CFLAGS@" +CPPFLAGS="@CPPFLAGS@" +LDFLAGS="@LDFLAGS@" +LIBS="@LIBS@" + +# Note format: +# identifier: [data] CRCR + +echo "openssh-survey-version: 1" +echo +echo "openssh-version: `./ssh -V 2>&1`" +echo +configinv=`$AWK '/^ \\\$.*configure/' config.log | sed 's/^ \\\$ //g'` +echo "configure-invocation: $configinv" +echo +echo "host: $host" +echo +echo "uname: `uname`" +echo +echo "uname-r: `uname -r`" +echo +echo "uname-m: `uname -m`" +echo +echo "uname-p: `uname -p`" +echo +echo "oslevel: `oslevel 2>/dev/null`" +echo +echo "oslevel-r: `oslevel -r 2>/dev/null`" +echo +echo "cc: $CC" +echo +echo "cflags: $CFLAGS" +echo +echo "cppflags: $CPPFLAGS" +echo +echo "ldflags: $LDFLAGS" +echo +echo "libs: $LIBS" +echo +echo "ccver-v: `$CC -v 2>&1 | sed '/^[ \t]*$/d'`" +echo +echo "ccver-V: `$CC -V 2>&1 | sed '/^[ \t]*$/d'`" +echo +echo "cppdefines:" +${CPP} -dM - + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * SSH2 tty modes support by Kevin Steves. + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Encoding and decoding of terminal modes in a portable way. + * Much of the format is defined in ttymodes.h; it is included multiple times + * into this file with the appropriate macro definitions to generate the + * suitable code. + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include + +#include "packet.h" +#include "log.h" +#include "compat.h" +#include "sshbuf.h" +#include "ssherr.h" + +#define TTY_OP_END 0 +/* + * uint32 (u_int) follows speed. + */ +#define TTY_OP_ISPEED 128 +#define TTY_OP_OSPEED 129 + +/* + * Converts POSIX speed_t to a baud rate. The values of the + * constants for speed_t are not themselves portable. + */ +static int +speed_to_baud(speed_t speed) +{ + switch (speed) { + case B0: + return 0; + case B50: + return 50; + case B75: + return 75; + case B110: + return 110; + case B134: + return 134; + case B150: + return 150; + case B200: + return 200; + case B300: + return 300; + case B600: + return 600; + case B1200: + return 1200; + case B1800: + return 1800; + case B2400: + return 2400; + case B4800: + return 4800; + case B9600: + return 9600; + +#ifdef B19200 + case B19200: + return 19200; +#else /* B19200 */ +#ifdef EXTA + case EXTA: + return 19200; +#endif /* EXTA */ +#endif /* B19200 */ + +#ifdef B38400 + case B38400: + return 38400; +#else /* B38400 */ +#ifdef EXTB + case EXTB: + return 38400; +#endif /* EXTB */ +#endif /* B38400 */ + +#ifdef B7200 + case B7200: + return 7200; +#endif /* B7200 */ +#ifdef B14400 + case B14400: + return 14400; +#endif /* B14400 */ +#ifdef B28800 + case B28800: + return 28800; +#endif /* B28800 */ +#ifdef B57600 + case B57600: + return 57600; +#endif /* B57600 */ +#ifdef B76800 + case B76800: + return 76800; +#endif /* B76800 */ +#ifdef B115200 + case B115200: + return 115200; +#endif /* B115200 */ +#ifdef B230400 + case B230400: + return 230400; +#endif /* B230400 */ + default: + return 9600; + } +} + +/* + * Converts a numeric baud rate to a POSIX speed_t. + */ +static speed_t +baud_to_speed(int baud) +{ + switch (baud) { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + +#ifdef B19200 + case 19200: + return B19200; +#else /* B19200 */ +#ifdef EXTA + case 19200: + return EXTA; +#endif /* EXTA */ +#endif /* B19200 */ + +#ifdef B38400 + case 38400: + return B38400; +#else /* B38400 */ +#ifdef EXTB + case 38400: + return EXTB; +#endif /* EXTB */ +#endif /* B38400 */ + +#ifdef B7200 + case 7200: + return B7200; +#endif /* B7200 */ +#ifdef B14400 + case 14400: + return B14400; +#endif /* B14400 */ +#ifdef B28800 + case 28800: + return B28800; +#endif /* B28800 */ +#ifdef B57600 + case 57600: + return B57600; +#endif /* B57600 */ +#ifdef B76800 + case 76800: + return B76800; +#endif /* B76800 */ +#ifdef B115200 + case 115200: + return B115200; +#endif /* B115200 */ +#ifdef B230400 + case 230400: + return B230400; +#endif /* B230400 */ + default: + return B9600; + } +} + +/* + * Encode a special character into SSH line format. + */ +static u_int +special_char_encode(cc_t c) +{ +#ifdef _POSIX_VDISABLE + if (c == _POSIX_VDISABLE) + return 255; +#endif /* _POSIX_VDISABLE */ + return c; +} + +/* + * Decode a special character from SSH line format. + */ +static cc_t +special_char_decode(u_int c) +{ +#ifdef _POSIX_VDISABLE + if (c == 255) + return _POSIX_VDISABLE; +#endif /* _POSIX_VDISABLE */ + return c; +} + +/* + * Encodes terminal modes for the terminal referenced by fd + * or tiop in a portable manner, and appends the modes to a packet + * being constructed. + */ +void +ssh_tty_make_modes(struct ssh *ssh, int fd, struct termios *tiop) +{ + struct termios tio; + struct sshbuf *buf; + int r, ibaud, obaud; + + if ((buf = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + + if (tiop == NULL) { + if (fd == -1) { + debug_f("no fd or tio"); + goto end; + } + if (tcgetattr(fd, &tio) == -1) { + logit("tcgetattr: %.100s", strerror(errno)); + goto end; + } + } else + tio = *tiop; + + /* Store input and output baud rates. */ + obaud = speed_to_baud(cfgetospeed(&tio)); + ibaud = speed_to_baud(cfgetispeed(&tio)); + if ((r = sshbuf_put_u8(buf, TTY_OP_OSPEED)) != 0 || + (r = sshbuf_put_u32(buf, obaud)) != 0 || + (r = sshbuf_put_u8(buf, TTY_OP_ISPEED)) != 0 || + (r = sshbuf_put_u32(buf, ibaud)) != 0) + fatal_fr(r, "compose"); + + /* Store values of mode flags. */ +#define TTYCHAR(NAME, OP) \ + if ((r = sshbuf_put_u8(buf, OP)) != 0 || \ + (r = sshbuf_put_u32(buf, \ + special_char_encode(tio.c_cc[NAME]))) != 0) \ + fatal_fr(r, "compose %s", #NAME); + +#define SSH_TTYMODE_IUTF8 42 /* for SSH_BUG_UTF8TTYMODE */ + +#define TTYMODE(NAME, FIELD, OP) \ + if (OP == SSH_TTYMODE_IUTF8 && (ssh->compat & SSH_BUG_UTF8TTYMODE)) { \ + debug3_f("SSH_BUG_UTF8TTYMODE"); \ + } else if ((r = sshbuf_put_u8(buf, OP)) != 0 || \ + (r = sshbuf_put_u32(buf, ((tio.FIELD & NAME) != 0))) != 0) \ + fatal_fr(r, "compose %s", #NAME); + +#include "ttymodes.h" + +#undef TTYCHAR +#undef TTYMODE + +end: + /* Mark end of mode data. */ + if ((r = sshbuf_put_u8(buf, TTY_OP_END)) != 0 || + (r = sshpkt_put_stringb(ssh, buf)) != 0) + fatal_fr(r, "compose end"); + sshbuf_free(buf); +} + +/* + * Decodes terminal modes for the terminal referenced by fd in a portable + * manner from a packet being read. + */ +void +ssh_tty_parse_modes(struct ssh *ssh, int fd) +{ + struct termios tio; + struct sshbuf *buf; + const u_char *data; + u_char opcode; + u_int baud, u; + int r, failure = 0; + size_t len; + + if ((r = sshpkt_get_string_direct(ssh, &data, &len)) != 0) + fatal_fr(r, "parse"); + if (len == 0) + return; + if ((buf = sshbuf_from(data, len)) == NULL) { + error_f("sshbuf_from failed"); + return; + } + + /* + * Get old attributes for the terminal. We will modify these + * flags. I am hoping that if there are any machine-specific + * modes, they will initially have reasonable values. + */ + if (tcgetattr(fd, &tio) == -1) { + logit("tcgetattr: %.100s", strerror(errno)); + failure = -1; + } + + while (sshbuf_len(buf) > 0) { + if ((r = sshbuf_get_u8(buf, &opcode)) != 0) + fatal_fr(r, "parse opcode"); + switch (opcode) { + case TTY_OP_END: + goto set; + + case TTY_OP_ISPEED: + if ((r = sshbuf_get_u32(buf, &baud)) != 0) + fatal_fr(r, "parse ispeed"); + if (failure != -1 && + cfsetispeed(&tio, baud_to_speed(baud)) == -1) + error("cfsetispeed failed for %d", baud); + break; + + case TTY_OP_OSPEED: + if ((r = sshbuf_get_u32(buf, &baud)) != 0) + fatal_fr(r, "parse ospeed"); + if (failure != -1 && + cfsetospeed(&tio, baud_to_speed(baud)) == -1) + error("cfsetospeed failed for %d", baud); + break; + +#define TTYCHAR(NAME, OP) \ + case OP: \ + if ((r = sshbuf_get_u32(buf, &u)) != 0) \ + fatal_fr(r, "parse %s", #NAME); \ + tio.c_cc[NAME] = special_char_decode(u); \ + break; +#define TTYMODE(NAME, FIELD, OP) \ + case OP: \ + if ((r = sshbuf_get_u32(buf, &u)) != 0) \ + fatal_fr(r, "parse %s", #NAME); \ + if (u) \ + tio.FIELD |= NAME; \ + else \ + tio.FIELD &= ~NAME; \ + break; + +#include "ttymodes.h" + +#undef TTYCHAR +#undef TTYMODE + + default: + debug("Ignoring unsupported tty mode opcode %d (0x%x)", + opcode, opcode); + /* + * SSH2: + * Opcodes 1 to 159 are defined to have a uint32 + * argument. + * Opcodes 160 to 255 are undefined and cause parsing + * to stop. + */ + if (opcode > 0 && opcode < 160) { + if ((r = sshbuf_get_u32(buf, NULL)) != 0) + fatal_fr(r, "parse arg"); + break; + } else { + logit_f("unknown opcode %d", opcode); + goto set; + } + } + } + +set: + len = sshbuf_len(buf); + sshbuf_free(buf); + if (len > 0) { + logit_f("%zu bytes left", len); + return; /* Don't process bytes passed */ + } + if (failure == -1) + return; /* Packet parsed ok but tcgetattr() failed */ + + /* Set the new modes for the terminal. */ + if (tcsetattr(fd, TCSANOW, &tio) == -1) + logit("Setting tty modes failed: %.100s", strerror(errno)); +} diff --git a/aosp/external/openssh/ttymodes.h b/aosp/external/openssh/ttymodes.h new file mode 100644 index 0000000000000000000000000000000000000000..24f07560c9fdcb5af02b4bc5d22ced253d414112 --- /dev/null +++ b/aosp/external/openssh/ttymodes.h @@ -0,0 +1,169 @@ +/* $OpenBSD: ttymodes.h,v 1.16 2017/04/30 23:26:54 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * SSH2 tty modes support by Kevin Steves. + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The tty mode description is a string, consisting of + * opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). + * Opcodes 1-159 have uint32 arguments. + * Opcodes 160-255 are not yet defined and cause parsing to stop (they + * should only be used after any other data). + * + * The client puts in the string any modes it knows about, and the + * server ignores any modes it does not know about. This allows some degree + * of machine-independence, at least between systems that use a posix-like + * tty interface. The protocol can support other systems as well, but might + * require reimplementing as mode names would likely be different. + */ + +/* + * Some constants and prototypes are defined in packet.h; this file + * is only intended for including from ttymodes.c. + */ + +/* termios macro */ +/* name, op */ +TTYCHAR(VINTR, 1) +TTYCHAR(VQUIT, 2) +TTYCHAR(VERASE, 3) +#if defined(VKILL) +TTYCHAR(VKILL, 4) +#endif /* VKILL */ +TTYCHAR(VEOF, 5) +#if defined(VEOL) +TTYCHAR(VEOL, 6) +#endif /* VEOL */ +#ifdef VEOL2 +TTYCHAR(VEOL2, 7) +#endif /* VEOL2 */ +TTYCHAR(VSTART, 8) +TTYCHAR(VSTOP, 9) +#if defined(VSUSP) +TTYCHAR(VSUSP, 10) +#endif /* VSUSP */ +#if defined(VDSUSP) +TTYCHAR(VDSUSP, 11) +#endif /* VDSUSP */ +#if defined(VREPRINT) +TTYCHAR(VREPRINT, 12) +#endif /* VREPRINT */ +#if defined(VWERASE) +TTYCHAR(VWERASE, 13) +#endif /* VWERASE */ +#if defined(VLNEXT) +TTYCHAR(VLNEXT, 14) +#endif /* VLNEXT */ +#if defined(VFLUSH) +TTYCHAR(VFLUSH, 15) +#endif /* VFLUSH */ +#ifdef VSWTCH +TTYCHAR(VSWTCH, 16) +#endif /* VSWTCH */ +#if defined(VSTATUS) +TTYCHAR(VSTATUS, 17) +#endif /* VSTATUS */ +#ifdef VDISCARD +TTYCHAR(VDISCARD, 18) +#endif /* VDISCARD */ + +/* name, field, op */ +TTYMODE(IGNPAR, c_iflag, 30) +TTYMODE(PARMRK, c_iflag, 31) +TTYMODE(INPCK, c_iflag, 32) +TTYMODE(ISTRIP, c_iflag, 33) +TTYMODE(INLCR, c_iflag, 34) +TTYMODE(IGNCR, c_iflag, 35) +TTYMODE(ICRNL, c_iflag, 36) +#if defined(IUCLC) +TTYMODE(IUCLC, c_iflag, 37) +#endif +TTYMODE(IXON, c_iflag, 38) +TTYMODE(IXANY, c_iflag, 39) +TTYMODE(IXOFF, c_iflag, 40) +#ifdef IMAXBEL +TTYMODE(IMAXBEL,c_iflag, 41) +#endif /* IMAXBEL */ +#ifdef IUTF8 +TTYMODE(IUTF8, c_iflag, 42) +#endif /* IUTF8 */ + +TTYMODE(ISIG, c_lflag, 50) +TTYMODE(ICANON, c_lflag, 51) +#ifdef XCASE +TTYMODE(XCASE, c_lflag, 52) +#endif +TTYMODE(ECHO, c_lflag, 53) +TTYMODE(ECHOE, c_lflag, 54) +TTYMODE(ECHOK, c_lflag, 55) +TTYMODE(ECHONL, c_lflag, 56) +TTYMODE(NOFLSH, c_lflag, 57) +TTYMODE(TOSTOP, c_lflag, 58) +#ifdef IEXTEN +TTYMODE(IEXTEN, c_lflag, 59) +#endif /* IEXTEN */ +#if defined(ECHOCTL) +TTYMODE(ECHOCTL,c_lflag, 60) +#endif /* ECHOCTL */ +#ifdef ECHOKE +TTYMODE(ECHOKE, c_lflag, 61) +#endif /* ECHOKE */ +#if defined(PENDIN) +TTYMODE(PENDIN, c_lflag, 62) +#endif /* PENDIN */ + +TTYMODE(OPOST, c_oflag, 70) +#if defined(OLCUC) +TTYMODE(OLCUC, c_oflag, 71) +#endif +#ifdef ONLCR +TTYMODE(ONLCR, c_oflag, 72) +#endif +#ifdef OCRNL +TTYMODE(OCRNL, c_oflag, 73) +#endif +#ifdef ONOCR +TTYMODE(ONOCR, c_oflag, 74) +#endif +#ifdef ONLRET +TTYMODE(ONLRET, c_oflag, 75) +#endif + +TTYMODE(CS7, c_cflag, 90) +TTYMODE(CS8, c_cflag, 91) +TTYMODE(PARENB, c_cflag, 92) +TTYMODE(PARODD, c_cflag, 93) diff --git a/aosp/external/openssh/uidswap.c b/aosp/external/openssh/uidswap.c new file mode 100644 index 0000000000000000000000000000000000000000..d373859ff3c9da2310893091bdc4b2e0bb81076d --- /dev/null +++ b/aosp/external/openssh/uidswap.c @@ -0,0 +1,289 @@ +/* $OpenBSD: uidswap.c,v 1.42 2019/06/28 13:35:04 deraadt Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Code for uid-swapping. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" +#include "uidswap.h" +#include "xmalloc.h" + +#if defined(ANDROID) +#define AID_GRAPHICS 1003 +#define AID_INPUT 1004 +#define AID_LOG 1007 +#define AID_MOUNT 1009 +#define AID_SDCARD_RW 1015 +#define AID_SHELL 2000 +#define AID_NET_BT_ADMIN 3001 +#define AID_NET_BT 3002 +#define AID_INET 3003 +#define AID_NET_BW_STATS 3006 +#include +#include +#endif + +/* + * Note: all these functions must work in all of the following cases: + * 1. euid=0, ruid=0 + * 2. euid=0, ruid!=0 + * 3. euid!=0, ruid!=0 + * Additionally, they must work regardless of whether the system has + * POSIX saved uids or not. + */ + +#if defined(_POSIX_SAVED_IDS) && !defined(BROKEN_SAVED_UIDS) +/* Lets assume that posix saved ids also work with seteuid, even though that + is not part of the posix specification. */ +#define SAVED_IDS_WORK_WITH_SETEUID +/* Saved effective uid. */ +static uid_t saved_euid = 0; +static gid_t saved_egid = 0; +#endif + +/* Saved effective uid. */ +static int privileged = 0; +static int temporarily_use_uid_effective = 0; +static uid_t user_groups_uid; +static gid_t *saved_egroups = NULL, *user_groups = NULL; +static int saved_egroupslen = -1, user_groupslen = -1; + +/* + * Temporarily changes to the given uid. If the effective user + * id is not root, this does nothing. This call cannot be nested. + */ +void +temporarily_use_uid(struct passwd *pw) +{ + /* Save the current euid, and egroups. */ +#ifdef SAVED_IDS_WORK_WITH_SETEUID + saved_euid = geteuid(); + saved_egid = getegid(); + debug("temporarily_use_uid: %u/%u (e=%u/%u)", + (u_int)pw->pw_uid, (u_int)pw->pw_gid, + (u_int)saved_euid, (u_int)saved_egid); +#ifndef HAVE_CYGWIN + if (saved_euid != 0) { + privileged = 0; + return; + } +#endif +#else + if (geteuid() != 0) { + privileged = 0; + return; + } +#endif /* SAVED_IDS_WORK_WITH_SETEUID */ + + privileged = 1; + temporarily_use_uid_effective = 1; + + saved_egroupslen = getgroups(0, NULL); + if (saved_egroupslen == -1) + fatal("getgroups: %.100s", strerror(errno)); + if (saved_egroupslen > 0) { + saved_egroups = xreallocarray(saved_egroups, + saved_egroupslen, sizeof(gid_t)); + if (getgroups(saved_egroupslen, saved_egroups) == -1) + fatal("getgroups: %.100s", strerror(errno)); + } else { /* saved_egroupslen == 0 */ + free(saved_egroups); + saved_egroups = NULL; + } + + /* set and save the user's groups */ + if (user_groupslen == -1 || user_groups_uid != pw->pw_uid) { + if (initgroups(pw->pw_name, pw->pw_gid) == -1) + fatal("initgroups: %s: %.100s", pw->pw_name, + strerror(errno)); + + user_groupslen = getgroups(0, NULL); + if (user_groupslen == -1) + fatal("getgroups: %.100s", strerror(errno)); + if (user_groupslen > 0) { + user_groups = xreallocarray(user_groups, + user_groupslen, sizeof(gid_t)); + if (getgroups(user_groupslen, user_groups) == -1) + fatal("getgroups: %.100s", strerror(errno)); + } else { /* user_groupslen == 0 */ + free(user_groups); + user_groups = NULL; + } + user_groups_uid = pw->pw_uid; + } + /* Set the effective uid to the given (unprivileged) uid. */ + if (setgroups(user_groupslen, user_groups) == -1) + fatal("setgroups: %.100s", strerror(errno)); +#ifndef SAVED_IDS_WORK_WITH_SETEUID + /* Propagate the privileged gid to all of our gids. */ + if (setgid(getegid()) == -1) + debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno)); + /* Propagate the privileged uid to all of our uids. */ + if (setuid(geteuid()) == -1) + debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno)); +#endif /* SAVED_IDS_WORK_WITH_SETEUID */ + if (setegid(pw->pw_gid) == -1) + fatal("setegid %u: %.100s", (u_int)pw->pw_gid, + strerror(errno)); + if (seteuid(pw->pw_uid) == -1) + fatal("seteuid %u: %.100s", (u_int)pw->pw_uid, + strerror(errno)); +} + +/* + * Restores to the original (privileged) uid. + */ +void +restore_uid(void) +{ + /* it's a no-op unless privileged */ + if (!privileged) { + debug("restore_uid: (unprivileged)"); + return; + } + if (!temporarily_use_uid_effective) + fatal("restore_uid: temporarily_use_uid not effective"); + +#ifdef SAVED_IDS_WORK_WITH_SETEUID + debug("restore_uid: %u/%u", (u_int)saved_euid, (u_int)saved_egid); + /* Set the effective uid back to the saved privileged uid. */ + if (seteuid(saved_euid) == -1) + fatal("seteuid %u: %.100s", (u_int)saved_euid, strerror(errno)); + if (setegid(saved_egid) == -1) + fatal("setegid %u: %.100s", (u_int)saved_egid, strerror(errno)); +#else /* SAVED_IDS_WORK_WITH_SETEUID */ + /* + * We are unable to restore the real uid to its unprivileged value. + * Propagate the real uid (usually more privileged) to effective uid + * as well. + */ + if (setuid(getuid()) == -1) + fatal("%s: setuid failed: %s", __func__, strerror(errno)); + if (setgid(getgid()) == -1) + fatal("%s: setgid failed: %s", __func__, strerror(errno)); +#endif /* SAVED_IDS_WORK_WITH_SETEUID */ + + if (setgroups(saved_egroupslen, saved_egroups) == -1) + fatal("setgroups: %.100s", strerror(errno)); + temporarily_use_uid_effective = 0; +} + +/* + * Permanently sets all uids to the given uid. This cannot be + * called while temporarily_use_uid is effective. + */ +void +permanently_set_uid(struct passwd *pw) +{ +#ifndef NO_UID_RESTORATION_TEST + uid_t old_uid = getuid(); + gid_t old_gid = getgid(); +#endif +#if defined(ANDROID) + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; +#endif + + if (pw == NULL) + fatal("permanently_set_uid: no user given"); + if (temporarily_use_uid_effective) + fatal("permanently_set_uid: temporarily_use_uid effective"); + debug("permanently_set_uid: %u/%u", (u_int)pw->pw_uid, + (u_int)pw->pw_gid); + +#if defined(ANDROID) + if (pw->pw_uid == AID_SHELL) { + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + /* add extra groups needed for shell user: + * - AID_LOG to read system logs (adb logcat) + * - AID_INPUT to diagnose input issues (getevent) + * - AID_INET to diagnose network issues (netcfg, ping) + * - AID_GRAPHICS to access the frame buffer + * - AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump) + * - AID_SDCARD_RW to allow writing to the SD card + * - AID_MOUNT to allow unmounting the SD card before rebooting + * - AID_NET_BW_STATS to read out qtaguid statistics. */ + gid_t groups[] = {AID_LOG, AID_INPUT, AID_INET, + AID_GRAPHICS, AID_NET_BT, AID_NET_BT_ADMIN, + AID_SDCARD_RW, AID_MOUNT, AID_NET_BW_STATS}; + setgroups(sizeof(groups)/sizeof(groups[0]), groups); + } +#endif + + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) + fatal("setresgid %u: %.100s", (u_int)pw->pw_gid, strerror(errno)); + +#ifdef __APPLE__ + /* + * OS X requires initgroups after setgid to opt back into + * memberd support for >16 supplemental groups. + */ + if (initgroups(pw->pw_name, pw->pw_gid) == -1) + fatal("initgroups %.100s %u: %.100s", + pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); +#endif + + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + fatal("setresuid %u: %.100s", (u_int)pw->pw_uid, strerror(errno)); + +#ifndef NO_UID_RESTORATION_TEST + /* Try restoration of GID if changed (test clearing of saved gid) */ + if (old_gid != pw->pw_gid && pw->pw_uid != 0 && + (setgid(old_gid) != -1 || setegid(old_gid) != -1)) + fatal("%s: was able to restore old [e]gid", __func__); +#endif + + /* Verify GID drop was successful */ + if (getgid() != pw->pw_gid || getegid() != pw->pw_gid) { + fatal("%s: egid incorrect gid:%u egid:%u (should be %u)", + __func__, (u_int)getgid(), (u_int)getegid(), + (u_int)pw->pw_gid); + } + +#ifndef NO_UID_RESTORATION_TEST + /* Try restoration of UID if changed (test clearing of saved uid) */ + if (old_uid != pw->pw_uid && + (setuid(old_uid) != -1 || seteuid(old_uid) != -1)) + fatal("%s: was able to restore old [e]uid", __func__); +#endif + + /* Verify UID drop was successful */ + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) { + fatal("%s: euid incorrect uid:%u euid:%u (should be %u)", + __func__, (u_int)getuid(), (u_int)geteuid(), + (u_int)pw->pw_uid); + } + +#if defined(ANDROID) + if (pw->pw_uid == AID_SHELL) { + /* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */ + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + cap.effective = cap.permitted = (1 << CAP_SYS_BOOT); + cap.inheritable = 0; + capset(&header, &cap); + } +#endif + +} diff --git a/aosp/external/openssh/uidswap.h b/aosp/external/openssh/uidswap.h new file mode 100644 index 0000000000000000000000000000000000000000..4ac91aa0471acd6028c84b5cc4528317f85131c7 --- /dev/null +++ b/aosp/external/openssh/uidswap.h @@ -0,0 +1,17 @@ +/* $OpenBSD: uidswap.h,v 1.14 2018/07/18 11:34:05 dtucker Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +void temporarily_use_uid(struct passwd *); +void restore_uid(void); +void permanently_set_uid(struct passwd *); diff --git a/aosp/external/openssh/umac.c b/aosp/external/openssh/umac.c new file mode 100644 index 0000000000000000000000000000000000000000..a710424ce0fd8fc8ef357c9778c98a7fe9f1c713 --- /dev/null +++ b/aosp/external/openssh/umac.c @@ -0,0 +1,1282 @@ +/* $OpenBSD: umac.c,v 1.22 2022/01/01 05:55:06 jsg Exp $ */ +/* ----------------------------------------------------------------------- + * + * umac.c -- C Implementation UMAC Message Authentication + * + * Version 0.93b of rfc4418.txt -- 2006 July 18 + * + * For a full description of UMAC message authentication see the UMAC + * world-wide-web page at http://www.cs.ucdavis.edu/~rogaway/umac + * Please report bugs and suggestions to the UMAC webpage. + * + * Copyright (c) 1999-2006 Ted Krovetz + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and with or without fee, is hereby + * granted provided that the above copyright notice appears in all copies + * and in supporting documentation, and that the name of the copyright + * holder not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * + * Comments should be directed to Ted Krovetz (tdk@acm.org) + * + * ---------------------------------------------------------------------- */ + + /* ////////////////////// IMPORTANT NOTES ///////////////////////////////// + * + * 1) This version does not work properly on messages larger than 16MB + * + * 2) If you set the switch to use SSE2, then all data must be 16-byte + * aligned + * + * 3) When calling the function umac(), it is assumed that msg is in + * a writable buffer of length divisible by 32 bytes. The message itself + * does not have to fill the entire buffer, but bytes beyond msg may be + * zeroed. + * + * 4) Three free AES implementations are supported by this implementation of + * UMAC. Paulo Barreto's version is in the public domain and can be found + * at http://www.esat.kuleuven.ac.be/~rijmen/rijndael/ (search for + * "Barreto"). The only two files needed are rijndael-alg-fst.c and + * rijndael-alg-fst.h. Brian Gladman's version is distributed with the GNU + * Public license at http://fp.gladman.plus.com/AES/index.htm. It + * includes a fast IA-32 assembly version. The OpenSSL crypo library is + * the third. + * + * 5) With FORCE_C_ONLY flags set to 0, incorrect results are sometimes + * produced under gcc with optimizations set -O3 or higher. Dunno why. + * + /////////////////////////////////////////////////////////////////////// */ + +/* ---------------------------------------------------------------------- */ +/* --- User Switches ---------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +#ifndef UMAC_OUTPUT_LEN +#define UMAC_OUTPUT_LEN 8 /* Alowable: 4, 8, 12, 16 */ +#endif + +#if UMAC_OUTPUT_LEN != 4 && UMAC_OUTPUT_LEN != 8 && \ + UMAC_OUTPUT_LEN != 12 && UMAC_OUTPUT_LEN != 16 +# error UMAC_OUTPUT_LEN must be defined to 4, 8, 12 or 16 +#endif + +/* #define FORCE_C_ONLY 1 ANSI C and 64-bit integers req'd */ +/* #define AES_IMPLEMENTAION 1 1 = OpenSSL, 2 = Barreto, 3 = Gladman */ +/* #define SSE2 0 Is SSE2 is available? */ +/* #define RUN_TESTS 0 Run basic correctness/speed tests */ +/* #define UMAC_AE_SUPPORT 0 Enable authenticated encryption */ + +/* ---------------------------------------------------------------------- */ +/* -- Global Includes --------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +#include "includes.h" +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "umac.h" +#include "misc.h" + +/* ---------------------------------------------------------------------- */ +/* --- Primitive Data Types --- */ +/* ---------------------------------------------------------------------- */ + +/* The following assumptions may need change on your system */ +typedef u_int8_t UINT8; /* 1 byte */ +typedef u_int16_t UINT16; /* 2 byte */ +typedef u_int32_t UINT32; /* 4 byte */ +typedef u_int64_t UINT64; /* 8 bytes */ +typedef unsigned int UWORD; /* Register */ + +/* ---------------------------------------------------------------------- */ +/* --- Constants -------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +#define UMAC_KEY_LEN 16 /* UMAC takes 16 bytes of external key */ + +/* Message "words" are read from memory in an endian-specific manner. */ +/* For this implementation to behave correctly, __LITTLE_ENDIAN__ must */ +/* be set true if the host computer is little-endian. */ + +#if BYTE_ORDER == LITTLE_ENDIAN +#define __LITTLE_ENDIAN__ 1 +#else +#define __LITTLE_ENDIAN__ 0 +#endif + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- Architecture Specific ------------------------------------------ */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- Primitive Routines --------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------- */ +/* --- 32-bit by 32-bit to 64-bit Multiplication ------------------------ */ +/* ---------------------------------------------------------------------- */ + +#define MUL64(a,b) ((UINT64)((UINT64)(UINT32)(a) * (UINT64)(UINT32)(b))) + +/* ---------------------------------------------------------------------- */ +/* --- Endian Conversion --- Forcing assembly on some platforms */ +/* ---------------------------------------------------------------------- */ + +#if (__LITTLE_ENDIAN__) +#define LOAD_UINT32_REVERSED(p) get_u32(p) +#define STORE_UINT32_REVERSED(p,v) put_u32(p,v) +#else +#define LOAD_UINT32_REVERSED(p) get_u32_le(p) +#define STORE_UINT32_REVERSED(p,v) put_u32_le(p,v) +#endif + +#define LOAD_UINT32_LITTLE(p) (get_u32_le(p)) +#define STORE_UINT32_BIG(p,v) put_u32(p, v) + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- Begin KDF & PDF Section ---------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* UMAC uses AES with 16 byte block and key lengths */ +#define AES_BLOCK_LEN 16 + +/* OpenSSL's AES */ +#ifdef WITH_OPENSSL +#include "openbsd-compat/openssl-compat.h" +#ifndef USE_BUILTIN_RIJNDAEL +# include +#endif +typedef AES_KEY aes_int_key[1]; +#define aes_encryption(in,out,int_key) \ + AES_encrypt((u_char *)(in),(u_char *)(out),(AES_KEY *)int_key) +#define aes_key_setup(key,int_key) \ + AES_set_encrypt_key((const u_char *)(key),UMAC_KEY_LEN*8,int_key) +#else +#include "rijndael.h" +#define AES_ROUNDS ((UMAC_KEY_LEN / 4) + 6) +typedef UINT8 aes_int_key[AES_ROUNDS+1][4][4]; /* AES internal */ +#define aes_encryption(in,out,int_key) \ + rijndaelEncrypt((u32 *)(int_key), AES_ROUNDS, (u8 *)(in), (u8 *)(out)) +#define aes_key_setup(key,int_key) \ + rijndaelKeySetupEnc((u32 *)(int_key), (const unsigned char *)(key), \ + UMAC_KEY_LEN*8) +#endif + +/* The user-supplied UMAC key is stretched using AES in a counter + * mode to supply all random bits needed by UMAC. The kdf function takes + * an AES internal key representation 'key' and writes a stream of + * 'nbytes' bytes to the memory pointed at by 'bufp'. Each distinct + * 'ndx' causes a distinct byte stream. + */ +static void kdf(void *bufp, aes_int_key key, UINT8 ndx, int nbytes) +{ + UINT8 in_buf[AES_BLOCK_LEN] = {0}; + UINT8 out_buf[AES_BLOCK_LEN]; + UINT8 *dst_buf = (UINT8 *)bufp; + int i; + + /* Setup the initial value */ + in_buf[AES_BLOCK_LEN-9] = ndx; + in_buf[AES_BLOCK_LEN-1] = i = 1; + + while (nbytes >= AES_BLOCK_LEN) { + aes_encryption(in_buf, out_buf, key); + memcpy(dst_buf,out_buf,AES_BLOCK_LEN); + in_buf[AES_BLOCK_LEN-1] = ++i; + nbytes -= AES_BLOCK_LEN; + dst_buf += AES_BLOCK_LEN; + } + if (nbytes) { + aes_encryption(in_buf, out_buf, key); + memcpy(dst_buf,out_buf,nbytes); + } + explicit_bzero(in_buf, sizeof(in_buf)); + explicit_bzero(out_buf, sizeof(out_buf)); +} + +/* The final UHASH result is XOR'd with the output of a pseudorandom + * function. Here, we use AES to generate random output and + * xor the appropriate bytes depending on the last bits of nonce. + * This scheme is optimized for sequential, increasing big-endian nonces. + */ + +typedef struct { + UINT8 cache[AES_BLOCK_LEN]; /* Previous AES output is saved */ + UINT8 nonce[AES_BLOCK_LEN]; /* The AES input making above cache */ + aes_int_key prf_key; /* Expanded AES key for PDF */ +} pdf_ctx; + +static void pdf_init(pdf_ctx *pc, aes_int_key prf_key) +{ + UINT8 buf[UMAC_KEY_LEN]; + + kdf(buf, prf_key, 0, UMAC_KEY_LEN); + aes_key_setup(buf, pc->prf_key); + + /* Initialize pdf and cache */ + memset(pc->nonce, 0, sizeof(pc->nonce)); + aes_encryption(pc->nonce, pc->cache, pc->prf_key); + explicit_bzero(buf, sizeof(buf)); +} + +static void pdf_gen_xor(pdf_ctx *pc, const UINT8 nonce[8], UINT8 buf[8]) +{ + /* 'ndx' indicates that we'll be using the 0th or 1st eight bytes + * of the AES output. If last time around we returned the ndx-1st + * element, then we may have the result in the cache already. + */ + +#if (UMAC_OUTPUT_LEN == 4) +#define LOW_BIT_MASK 3 +#elif (UMAC_OUTPUT_LEN == 8) +#define LOW_BIT_MASK 1 +#elif (UMAC_OUTPUT_LEN > 8) +#define LOW_BIT_MASK 0 +#endif + union { + UINT8 tmp_nonce_lo[4]; + UINT32 align; + } t; +#if LOW_BIT_MASK != 0 + int ndx = nonce[7] & LOW_BIT_MASK; +#endif + *(UINT32 *)t.tmp_nonce_lo = ((const UINT32 *)nonce)[1]; + t.tmp_nonce_lo[3] &= ~LOW_BIT_MASK; /* zero last bit */ + + if ( (((UINT32 *)t.tmp_nonce_lo)[0] != ((UINT32 *)pc->nonce)[1]) || + (((const UINT32 *)nonce)[0] != ((UINT32 *)pc->nonce)[0]) ) + { + ((UINT32 *)pc->nonce)[0] = ((const UINT32 *)nonce)[0]; + ((UINT32 *)pc->nonce)[1] = ((UINT32 *)t.tmp_nonce_lo)[0]; + aes_encryption(pc->nonce, pc->cache, pc->prf_key); + } + +#if (UMAC_OUTPUT_LEN == 4) + *((UINT32 *)buf) ^= ((UINT32 *)pc->cache)[ndx]; +#elif (UMAC_OUTPUT_LEN == 8) + *((UINT64 *)buf) ^= ((UINT64 *)pc->cache)[ndx]; +#elif (UMAC_OUTPUT_LEN == 12) + ((UINT64 *)buf)[0] ^= ((UINT64 *)pc->cache)[0]; + ((UINT32 *)buf)[2] ^= ((UINT32 *)pc->cache)[2]; +#elif (UMAC_OUTPUT_LEN == 16) + ((UINT64 *)buf)[0] ^= ((UINT64 *)pc->cache)[0]; + ((UINT64 *)buf)[1] ^= ((UINT64 *)pc->cache)[1]; +#endif +} + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- Begin NH Hash Section ------------------------------------------ */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* The NH-based hash functions used in UMAC are described in the UMAC paper + * and specification, both of which can be found at the UMAC website. + * The interface to this implementation has two + * versions, one expects the entire message being hashed to be passed + * in a single buffer and returns the hash result immediately. The second + * allows the message to be passed in a sequence of buffers. In the + * multiple-buffer interface, the client calls the routine nh_update() as + * many times as necessary. When there is no more data to be fed to the + * hash, the client calls nh_final() which calculates the hash output. + * Before beginning another hash calculation the nh_reset() routine + * must be called. The single-buffer routine, nh(), is equivalent to + * the sequence of calls nh_update() and nh_final(); however it is + * optimized and should be preferred whenever the multiple-buffer interface + * is not necessary. When using either interface, it is the client's + * responsibility to pass no more than L1_KEY_LEN bytes per hash result. + * + * The routine nh_init() initializes the nh_ctx data structure and + * must be called once, before any other PDF routine. + */ + + /* The "nh_aux" routines do the actual NH hashing work. They + * expect buffers to be multiples of L1_PAD_BOUNDARY. These routines + * produce output for all STREAMS NH iterations in one call, + * allowing the parallel implementation of the streams. + */ + +#define STREAMS (UMAC_OUTPUT_LEN / 4) /* Number of times hash is applied */ +#define L1_KEY_LEN 1024 /* Internal key bytes */ +#define L1_KEY_SHIFT 16 /* Toeplitz key shift between streams */ +#define L1_PAD_BOUNDARY 32 /* pad message to boundary multiple */ +#define ALLOC_BOUNDARY 16 /* Keep buffers aligned to this */ +#define HASH_BUF_BYTES 64 /* nh_aux_hb buffer multiple */ + +typedef struct { + UINT8 nh_key [L1_KEY_LEN + L1_KEY_SHIFT * (STREAMS - 1)]; /* NH Key */ + UINT8 data [HASH_BUF_BYTES]; /* Incoming data buffer */ + int next_data_empty; /* Bookkeeping variable for data buffer. */ + int bytes_hashed; /* Bytes (out of L1_KEY_LEN) incorporated. */ + UINT64 state[STREAMS]; /* on-line state */ +} nh_ctx; + + +#if (UMAC_OUTPUT_LEN == 4) + +static void nh_aux(void *kp, const void *dp, void *hp, UINT32 dlen) +/* NH hashing primitive. Previous (partial) hash result is loaded and +* then stored via hp pointer. The length of the data pointed at by "dp", +* "dlen", is guaranteed to be divisible by L1_PAD_BOUNDARY (32). Key +* is expected to be endian compensated in memory at key setup. +*/ +{ + UINT64 h; + UWORD c = dlen / 32; + UINT32 *k = (UINT32 *)kp; + const UINT32 *d = (const UINT32 *)dp; + UINT32 d0,d1,d2,d3,d4,d5,d6,d7; + UINT32 k0,k1,k2,k3,k4,k5,k6,k7; + + h = *((UINT64 *)hp); + do { + d0 = LOAD_UINT32_LITTLE(d+0); d1 = LOAD_UINT32_LITTLE(d+1); + d2 = LOAD_UINT32_LITTLE(d+2); d3 = LOAD_UINT32_LITTLE(d+3); + d4 = LOAD_UINT32_LITTLE(d+4); d5 = LOAD_UINT32_LITTLE(d+5); + d6 = LOAD_UINT32_LITTLE(d+6); d7 = LOAD_UINT32_LITTLE(d+7); + k0 = *(k+0); k1 = *(k+1); k2 = *(k+2); k3 = *(k+3); + k4 = *(k+4); k5 = *(k+5); k6 = *(k+6); k7 = *(k+7); + h += MUL64((k0 + d0), (k4 + d4)); + h += MUL64((k1 + d1), (k5 + d5)); + h += MUL64((k2 + d2), (k6 + d6)); + h += MUL64((k3 + d3), (k7 + d7)); + + d += 8; + k += 8; + } while (--c); + *((UINT64 *)hp) = h; +} + +#elif (UMAC_OUTPUT_LEN == 8) + +static void nh_aux(void *kp, const void *dp, void *hp, UINT32 dlen) +/* Same as previous nh_aux, but two streams are handled in one pass, + * reading and writing 16 bytes of hash-state per call. + */ +{ + UINT64 h1,h2; + UWORD c = dlen / 32; + UINT32 *k = (UINT32 *)kp; + const UINT32 *d = (const UINT32 *)dp; + UINT32 d0,d1,d2,d3,d4,d5,d6,d7; + UINT32 k0,k1,k2,k3,k4,k5,k6,k7, + k8,k9,k10,k11; + + h1 = *((UINT64 *)hp); + h2 = *((UINT64 *)hp + 1); + k0 = *(k+0); k1 = *(k+1); k2 = *(k+2); k3 = *(k+3); + do { + d0 = LOAD_UINT32_LITTLE(d+0); d1 = LOAD_UINT32_LITTLE(d+1); + d2 = LOAD_UINT32_LITTLE(d+2); d3 = LOAD_UINT32_LITTLE(d+3); + d4 = LOAD_UINT32_LITTLE(d+4); d5 = LOAD_UINT32_LITTLE(d+5); + d6 = LOAD_UINT32_LITTLE(d+6); d7 = LOAD_UINT32_LITTLE(d+7); + k4 = *(k+4); k5 = *(k+5); k6 = *(k+6); k7 = *(k+7); + k8 = *(k+8); k9 = *(k+9); k10 = *(k+10); k11 = *(k+11); + + h1 += MUL64((k0 + d0), (k4 + d4)); + h2 += MUL64((k4 + d0), (k8 + d4)); + + h1 += MUL64((k1 + d1), (k5 + d5)); + h2 += MUL64((k5 + d1), (k9 + d5)); + + h1 += MUL64((k2 + d2), (k6 + d6)); + h2 += MUL64((k6 + d2), (k10 + d6)); + + h1 += MUL64((k3 + d3), (k7 + d7)); + h2 += MUL64((k7 + d3), (k11 + d7)); + + k0 = k8; k1 = k9; k2 = k10; k3 = k11; + + d += 8; + k += 8; + } while (--c); + ((UINT64 *)hp)[0] = h1; + ((UINT64 *)hp)[1] = h2; +} + +#elif (UMAC_OUTPUT_LEN == 12) + +static void nh_aux(void *kp, const void *dp, void *hp, UINT32 dlen) +/* Same as previous nh_aux, but two streams are handled in one pass, + * reading and writing 24 bytes of hash-state per call. +*/ +{ + UINT64 h1,h2,h3; + UWORD c = dlen / 32; + UINT32 *k = (UINT32 *)kp; + const UINT32 *d = (const UINT32 *)dp; + UINT32 d0,d1,d2,d3,d4,d5,d6,d7; + UINT32 k0,k1,k2,k3,k4,k5,k6,k7, + k8,k9,k10,k11,k12,k13,k14,k15; + + h1 = *((UINT64 *)hp); + h2 = *((UINT64 *)hp + 1); + h3 = *((UINT64 *)hp + 2); + k0 = *(k+0); k1 = *(k+1); k2 = *(k+2); k3 = *(k+3); + k4 = *(k+4); k5 = *(k+5); k6 = *(k+6); k7 = *(k+7); + do { + d0 = LOAD_UINT32_LITTLE(d+0); d1 = LOAD_UINT32_LITTLE(d+1); + d2 = LOAD_UINT32_LITTLE(d+2); d3 = LOAD_UINT32_LITTLE(d+3); + d4 = LOAD_UINT32_LITTLE(d+4); d5 = LOAD_UINT32_LITTLE(d+5); + d6 = LOAD_UINT32_LITTLE(d+6); d7 = LOAD_UINT32_LITTLE(d+7); + k8 = *(k+8); k9 = *(k+9); k10 = *(k+10); k11 = *(k+11); + k12 = *(k+12); k13 = *(k+13); k14 = *(k+14); k15 = *(k+15); + + h1 += MUL64((k0 + d0), (k4 + d4)); + h2 += MUL64((k4 + d0), (k8 + d4)); + h3 += MUL64((k8 + d0), (k12 + d4)); + + h1 += MUL64((k1 + d1), (k5 + d5)); + h2 += MUL64((k5 + d1), (k9 + d5)); + h3 += MUL64((k9 + d1), (k13 + d5)); + + h1 += MUL64((k2 + d2), (k6 + d6)); + h2 += MUL64((k6 + d2), (k10 + d6)); + h3 += MUL64((k10 + d2), (k14 + d6)); + + h1 += MUL64((k3 + d3), (k7 + d7)); + h2 += MUL64((k7 + d3), (k11 + d7)); + h3 += MUL64((k11 + d3), (k15 + d7)); + + k0 = k8; k1 = k9; k2 = k10; k3 = k11; + k4 = k12; k5 = k13; k6 = k14; k7 = k15; + + d += 8; + k += 8; + } while (--c); + ((UINT64 *)hp)[0] = h1; + ((UINT64 *)hp)[1] = h2; + ((UINT64 *)hp)[2] = h3; +} + +#elif (UMAC_OUTPUT_LEN == 16) + +static void nh_aux(void *kp, const void *dp, void *hp, UINT32 dlen) +/* Same as previous nh_aux, but two streams are handled in one pass, + * reading and writing 24 bytes of hash-state per call. +*/ +{ + UINT64 h1,h2,h3,h4; + UWORD c = dlen / 32; + UINT32 *k = (UINT32 *)kp; + const UINT32 *d = (const UINT32 *)dp; + UINT32 d0,d1,d2,d3,d4,d5,d6,d7; + UINT32 k0,k1,k2,k3,k4,k5,k6,k7, + k8,k9,k10,k11,k12,k13,k14,k15, + k16,k17,k18,k19; + + h1 = *((UINT64 *)hp); + h2 = *((UINT64 *)hp + 1); + h3 = *((UINT64 *)hp + 2); + h4 = *((UINT64 *)hp + 3); + k0 = *(k+0); k1 = *(k+1); k2 = *(k+2); k3 = *(k+3); + k4 = *(k+4); k5 = *(k+5); k6 = *(k+6); k7 = *(k+7); + do { + d0 = LOAD_UINT32_LITTLE(d+0); d1 = LOAD_UINT32_LITTLE(d+1); + d2 = LOAD_UINT32_LITTLE(d+2); d3 = LOAD_UINT32_LITTLE(d+3); + d4 = LOAD_UINT32_LITTLE(d+4); d5 = LOAD_UINT32_LITTLE(d+5); + d6 = LOAD_UINT32_LITTLE(d+6); d7 = LOAD_UINT32_LITTLE(d+7); + k8 = *(k+8); k9 = *(k+9); k10 = *(k+10); k11 = *(k+11); + k12 = *(k+12); k13 = *(k+13); k14 = *(k+14); k15 = *(k+15); + k16 = *(k+16); k17 = *(k+17); k18 = *(k+18); k19 = *(k+19); + + h1 += MUL64((k0 + d0), (k4 + d4)); + h2 += MUL64((k4 + d0), (k8 + d4)); + h3 += MUL64((k8 + d0), (k12 + d4)); + h4 += MUL64((k12 + d0), (k16 + d4)); + + h1 += MUL64((k1 + d1), (k5 + d5)); + h2 += MUL64((k5 + d1), (k9 + d5)); + h3 += MUL64((k9 + d1), (k13 + d5)); + h4 += MUL64((k13 + d1), (k17 + d5)); + + h1 += MUL64((k2 + d2), (k6 + d6)); + h2 += MUL64((k6 + d2), (k10 + d6)); + h3 += MUL64((k10 + d2), (k14 + d6)); + h4 += MUL64((k14 + d2), (k18 + d6)); + + h1 += MUL64((k3 + d3), (k7 + d7)); + h2 += MUL64((k7 + d3), (k11 + d7)); + h3 += MUL64((k11 + d3), (k15 + d7)); + h4 += MUL64((k15 + d3), (k19 + d7)); + + k0 = k8; k1 = k9; k2 = k10; k3 = k11; + k4 = k12; k5 = k13; k6 = k14; k7 = k15; + k8 = k16; k9 = k17; k10 = k18; k11 = k19; + + d += 8; + k += 8; + } while (--c); + ((UINT64 *)hp)[0] = h1; + ((UINT64 *)hp)[1] = h2; + ((UINT64 *)hp)[2] = h3; + ((UINT64 *)hp)[3] = h4; +} + +/* ---------------------------------------------------------------------- */ +#endif /* UMAC_OUTPUT_LENGTH */ +/* ---------------------------------------------------------------------- */ + + +/* ---------------------------------------------------------------------- */ + +static void nh_transform(nh_ctx *hc, const UINT8 *buf, UINT32 nbytes) +/* This function is a wrapper for the primitive NH hash functions. It takes + * as argument "hc" the current hash context and a buffer which must be a + * multiple of L1_PAD_BOUNDARY. The key passed to nh_aux is offset + * appropriately according to how much message has been hashed already. + */ +{ + UINT8 *key; + + key = hc->nh_key + hc->bytes_hashed; + nh_aux(key, buf, hc->state, nbytes); +} + +/* ---------------------------------------------------------------------- */ + +#if (__LITTLE_ENDIAN__) +static void endian_convert(void *buf, UWORD bpw, UINT32 num_bytes) +/* We endian convert the keys on little-endian computers to */ +/* compensate for the lack of big-endian memory reads during hashing. */ +{ + UWORD iters = num_bytes / bpw; + if (bpw == 4) { + UINT32 *p = (UINT32 *)buf; + do { + *p = LOAD_UINT32_REVERSED(p); + p++; + } while (--iters); + } else if (bpw == 8) { + UINT32 *p = (UINT32 *)buf; + UINT32 t; + do { + t = LOAD_UINT32_REVERSED(p+1); + p[1] = LOAD_UINT32_REVERSED(p); + p[0] = t; + p += 2; + } while (--iters); + } +} +#define endian_convert_if_le(x,y,z) endian_convert((x),(y),(z)) +#else +#define endian_convert_if_le(x,y,z) do{}while(0) /* Do nothing */ +#endif + +/* ---------------------------------------------------------------------- */ + +static void nh_reset(nh_ctx *hc) +/* Reset nh_ctx to ready for hashing of new data */ +{ + hc->bytes_hashed = 0; + hc->next_data_empty = 0; + hc->state[0] = 0; +#if (UMAC_OUTPUT_LEN >= 8) + hc->state[1] = 0; +#endif +#if (UMAC_OUTPUT_LEN >= 12) + hc->state[2] = 0; +#endif +#if (UMAC_OUTPUT_LEN == 16) + hc->state[3] = 0; +#endif + +} + +/* ---------------------------------------------------------------------- */ + +static void nh_init(nh_ctx *hc, aes_int_key prf_key) +/* Generate nh_key, endian convert and reset to be ready for hashing. */ +{ + kdf(hc->nh_key, prf_key, 1, sizeof(hc->nh_key)); + endian_convert_if_le(hc->nh_key, 4, sizeof(hc->nh_key)); + nh_reset(hc); +} + +/* ---------------------------------------------------------------------- */ + +static void nh_update(nh_ctx *hc, const UINT8 *buf, UINT32 nbytes) +/* Incorporate nbytes of data into a nh_ctx, buffer whatever is not an */ +/* even multiple of HASH_BUF_BYTES. */ +{ + UINT32 i,j; + + j = hc->next_data_empty; + if ((j + nbytes) >= HASH_BUF_BYTES) { + if (j) { + i = HASH_BUF_BYTES - j; + memcpy(hc->data+j, buf, i); + nh_transform(hc,hc->data,HASH_BUF_BYTES); + nbytes -= i; + buf += i; + hc->bytes_hashed += HASH_BUF_BYTES; + } + if (nbytes >= HASH_BUF_BYTES) { + i = nbytes & ~(HASH_BUF_BYTES - 1); + nh_transform(hc, buf, i); + nbytes -= i; + buf += i; + hc->bytes_hashed += i; + } + j = 0; + } + memcpy(hc->data + j, buf, nbytes); + hc->next_data_empty = j + nbytes; +} + +/* ---------------------------------------------------------------------- */ + +static void zero_pad(UINT8 *p, int nbytes) +{ +/* Write "nbytes" of zeroes, beginning at "p" */ + if (nbytes >= (int)sizeof(UWORD)) { + while ((ptrdiff_t)p % sizeof(UWORD)) { + *p = 0; + nbytes--; + p++; + } + while (nbytes >= (int)sizeof(UWORD)) { + *(UWORD *)p = 0; + nbytes -= sizeof(UWORD); + p += sizeof(UWORD); + } + } + while (nbytes) { + *p = 0; + nbytes--; + p++; + } +} + +/* ---------------------------------------------------------------------- */ + +static void nh_final(nh_ctx *hc, UINT8 *result) +/* After passing some number of data buffers to nh_update() for integration + * into an NH context, nh_final is called to produce a hash result. If any + * bytes are in the buffer hc->data, incorporate them into the + * NH context. Finally, add into the NH accumulation "state" the total number + * of bits hashed. The resulting numbers are written to the buffer "result". + * If nh_update was never called, L1_PAD_BOUNDARY zeroes are incorporated. + */ +{ + int nh_len, nbits; + + if (hc->next_data_empty != 0) { + nh_len = ((hc->next_data_empty + (L1_PAD_BOUNDARY - 1)) & + ~(L1_PAD_BOUNDARY - 1)); + zero_pad(hc->data + hc->next_data_empty, + nh_len - hc->next_data_empty); + nh_transform(hc, hc->data, nh_len); + hc->bytes_hashed += hc->next_data_empty; + } else if (hc->bytes_hashed == 0) { + nh_len = L1_PAD_BOUNDARY; + zero_pad(hc->data, L1_PAD_BOUNDARY); + nh_transform(hc, hc->data, nh_len); + } + + nbits = (hc->bytes_hashed << 3); + ((UINT64 *)result)[0] = ((UINT64 *)hc->state)[0] + nbits; +#if (UMAC_OUTPUT_LEN >= 8) + ((UINT64 *)result)[1] = ((UINT64 *)hc->state)[1] + nbits; +#endif +#if (UMAC_OUTPUT_LEN >= 12) + ((UINT64 *)result)[2] = ((UINT64 *)hc->state)[2] + nbits; +#endif +#if (UMAC_OUTPUT_LEN == 16) + ((UINT64 *)result)[3] = ((UINT64 *)hc->state)[3] + nbits; +#endif + nh_reset(hc); +} + +/* ---------------------------------------------------------------------- */ + +static void nh(nh_ctx *hc, const UINT8 *buf, UINT32 padded_len, + UINT32 unpadded_len, UINT8 *result) +/* All-in-one nh_update() and nh_final() equivalent. + * Assumes that padded_len is divisible by L1_PAD_BOUNDARY and result is + * well aligned + */ +{ + UINT32 nbits; + + /* Initialize the hash state */ + nbits = (unpadded_len << 3); + + ((UINT64 *)result)[0] = nbits; +#if (UMAC_OUTPUT_LEN >= 8) + ((UINT64 *)result)[1] = nbits; +#endif +#if (UMAC_OUTPUT_LEN >= 12) + ((UINT64 *)result)[2] = nbits; +#endif +#if (UMAC_OUTPUT_LEN == 16) + ((UINT64 *)result)[3] = nbits; +#endif + + nh_aux(hc->nh_key, buf, result, padded_len); +} + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- Begin UHASH Section -------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* UHASH is a multi-layered algorithm. Data presented to UHASH is first + * hashed by NH. The NH output is then hashed by a polynomial-hash layer + * unless the initial data to be hashed is short. After the polynomial- + * layer, an inner-product hash is used to produce the final UHASH output. + * + * UHASH provides two interfaces, one all-at-once and another where data + * buffers are presented sequentially. In the sequential interface, the + * UHASH client calls the routine uhash_update() as many times as necessary. + * When there is no more data to be fed to UHASH, the client calls + * uhash_final() which + * calculates the UHASH output. Before beginning another UHASH calculation + * the uhash_reset() routine must be called. The all-at-once UHASH routine, + * uhash(), is equivalent to the sequence of calls uhash_update() and + * uhash_final(); however it is optimized and should be + * used whenever the sequential interface is not necessary. + * + * The routine uhash_init() initializes the uhash_ctx data structure and + * must be called once, before any other UHASH routine. + */ + +/* ---------------------------------------------------------------------- */ +/* ----- Constants and uhash_ctx ---------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +/* ----- Poly hash and Inner-Product hash Constants --------------------- */ +/* ---------------------------------------------------------------------- */ + +/* Primes and masks */ +#define p36 ((UINT64)0x0000000FFFFFFFFBull) /* 2^36 - 5 */ +#define p64 ((UINT64)0xFFFFFFFFFFFFFFC5ull) /* 2^64 - 59 */ +#define m36 ((UINT64)0x0000000FFFFFFFFFull) /* The low 36 of 64 bits */ + + +/* ---------------------------------------------------------------------- */ + +typedef struct uhash_ctx { + nh_ctx hash; /* Hash context for L1 NH hash */ + UINT64 poly_key_8[STREAMS]; /* p64 poly keys */ + UINT64 poly_accum[STREAMS]; /* poly hash result */ + UINT64 ip_keys[STREAMS*4]; /* Inner-product keys */ + UINT32 ip_trans[STREAMS]; /* Inner-product translation */ + UINT32 msg_len; /* Total length of data passed */ + /* to uhash */ +} uhash_ctx; +typedef struct uhash_ctx *uhash_ctx_t; + +/* ---------------------------------------------------------------------- */ + + +/* The polynomial hashes use Horner's rule to evaluate a polynomial one + * word at a time. As described in the specification, poly32 and poly64 + * require keys from special domains. The following implementations exploit + * the special domains to avoid overflow. The results are not guaranteed to + * be within Z_p32 and Z_p64, but the Inner-Product hash implementation + * patches any errant values. + */ + +static UINT64 poly64(UINT64 cur, UINT64 key, UINT64 data) +{ + UINT32 key_hi = (UINT32)(key >> 32), + key_lo = (UINT32)key, + cur_hi = (UINT32)(cur >> 32), + cur_lo = (UINT32)cur, + x_lo, + x_hi; + UINT64 X,T,res; + + X = MUL64(key_hi, cur_lo) + MUL64(cur_hi, key_lo); + x_lo = (UINT32)X; + x_hi = (UINT32)(X >> 32); + + res = (MUL64(key_hi, cur_hi) + x_hi) * 59 + MUL64(key_lo, cur_lo); + + T = ((UINT64)x_lo << 32); + res += T; + if (res < T) + res += 59; + + res += data; + if (res < data) + res += 59; + + return res; +} + + +/* Although UMAC is specified to use a ramped polynomial hash scheme, this + * implementation does not handle all ramp levels. Because we don't handle + * the ramp up to p128 modulus in this implementation, we are limited to + * 2^14 poly_hash() invocations per stream (for a total capacity of 2^24 + * bytes input to UMAC per tag, ie. 16MB). + */ +static void poly_hash(uhash_ctx_t hc, UINT32 data_in[]) +{ + int i; + UINT64 *data=(UINT64*)data_in; + + for (i = 0; i < STREAMS; i++) { + if ((UINT32)(data[i] >> 32) == 0xfffffffful) { + hc->poly_accum[i] = poly64(hc->poly_accum[i], + hc->poly_key_8[i], p64 - 1); + hc->poly_accum[i] = poly64(hc->poly_accum[i], + hc->poly_key_8[i], (data[i] - 59)); + } else { + hc->poly_accum[i] = poly64(hc->poly_accum[i], + hc->poly_key_8[i], data[i]); + } + } +} + + +/* ---------------------------------------------------------------------- */ + + +/* The final step in UHASH is an inner-product hash. The poly hash + * produces a result not necessarily WORD_LEN bytes long. The inner- + * product hash breaks the polyhash output into 16-bit chunks and + * multiplies each with a 36 bit key. + */ + +static UINT64 ip_aux(UINT64 t, UINT64 *ipkp, UINT64 data) +{ + t = t + ipkp[0] * (UINT64)(UINT16)(data >> 48); + t = t + ipkp[1] * (UINT64)(UINT16)(data >> 32); + t = t + ipkp[2] * (UINT64)(UINT16)(data >> 16); + t = t + ipkp[3] * (UINT64)(UINT16)(data); + + return t; +} + +static UINT32 ip_reduce_p36(UINT64 t) +{ +/* Divisionless modular reduction */ + UINT64 ret; + + ret = (t & m36) + 5 * (t >> 36); + if (ret >= p36) + ret -= p36; + + /* return least significant 32 bits */ + return (UINT32)(ret); +} + + +/* If the data being hashed by UHASH is no longer than L1_KEY_LEN, then + * the polyhash stage is skipped and ip_short is applied directly to the + * NH output. + */ +static void ip_short(uhash_ctx_t ahc, UINT8 *nh_res, u_char *res) +{ + UINT64 t; + UINT64 *nhp = (UINT64 *)nh_res; + + t = ip_aux(0,ahc->ip_keys, nhp[0]); + STORE_UINT32_BIG((UINT32 *)res+0, ip_reduce_p36(t) ^ ahc->ip_trans[0]); +#if (UMAC_OUTPUT_LEN >= 8) + t = ip_aux(0,ahc->ip_keys+4, nhp[1]); + STORE_UINT32_BIG((UINT32 *)res+1, ip_reduce_p36(t) ^ ahc->ip_trans[1]); +#endif +#if (UMAC_OUTPUT_LEN >= 12) + t = ip_aux(0,ahc->ip_keys+8, nhp[2]); + STORE_UINT32_BIG((UINT32 *)res+2, ip_reduce_p36(t) ^ ahc->ip_trans[2]); +#endif +#if (UMAC_OUTPUT_LEN == 16) + t = ip_aux(0,ahc->ip_keys+12, nhp[3]); + STORE_UINT32_BIG((UINT32 *)res+3, ip_reduce_p36(t) ^ ahc->ip_trans[3]); +#endif +} + +/* If the data being hashed by UHASH is longer than L1_KEY_LEN, then + * the polyhash stage is not skipped and ip_long is applied to the + * polyhash output. + */ +static void ip_long(uhash_ctx_t ahc, u_char *res) +{ + int i; + UINT64 t; + + for (i = 0; i < STREAMS; i++) { + /* fix polyhash output not in Z_p64 */ + if (ahc->poly_accum[i] >= p64) + ahc->poly_accum[i] -= p64; + t = ip_aux(0,ahc->ip_keys+(i*4), ahc->poly_accum[i]); + STORE_UINT32_BIG((UINT32 *)res+i, + ip_reduce_p36(t) ^ ahc->ip_trans[i]); + } +} + + +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ + +/* Reset uhash context for next hash session */ +static int uhash_reset(uhash_ctx_t pc) +{ + nh_reset(&pc->hash); + pc->msg_len = 0; + pc->poly_accum[0] = 1; +#if (UMAC_OUTPUT_LEN >= 8) + pc->poly_accum[1] = 1; +#endif +#if (UMAC_OUTPUT_LEN >= 12) + pc->poly_accum[2] = 1; +#endif +#if (UMAC_OUTPUT_LEN == 16) + pc->poly_accum[3] = 1; +#endif + return 1; +} + +/* ---------------------------------------------------------------------- */ + +/* Given a pointer to the internal key needed by kdf() and a uhash context, + * initialize the NH context and generate keys needed for poly and inner- + * product hashing. All keys are endian adjusted in memory so that native + * loads cause correct keys to be in registers during calculation. + */ +static void uhash_init(uhash_ctx_t ahc, aes_int_key prf_key) +{ + int i; + UINT8 buf[(8*STREAMS+4)*sizeof(UINT64)]; + + /* Zero the entire uhash context */ + memset(ahc, 0, sizeof(uhash_ctx)); + + /* Initialize the L1 hash */ + nh_init(&ahc->hash, prf_key); + + /* Setup L2 hash variables */ + kdf(buf, prf_key, 2, sizeof(buf)); /* Fill buffer with index 1 key */ + for (i = 0; i < STREAMS; i++) { + /* Fill keys from the buffer, skipping bytes in the buffer not + * used by this implementation. Endian reverse the keys if on a + * little-endian computer. + */ + memcpy(ahc->poly_key_8+i, buf+24*i, 8); + endian_convert_if_le(ahc->poly_key_8+i, 8, 8); + /* Mask the 64-bit keys to their special domain */ + ahc->poly_key_8[i] &= ((UINT64)0x01ffffffu << 32) + 0x01ffffffu; + ahc->poly_accum[i] = 1; /* Our polyhash prepends a non-zero word */ + } + + /* Setup L3-1 hash variables */ + kdf(buf, prf_key, 3, sizeof(buf)); /* Fill buffer with index 2 key */ + for (i = 0; i < STREAMS; i++) + memcpy(ahc->ip_keys+4*i, buf+(8*i+4)*sizeof(UINT64), + 4*sizeof(UINT64)); + endian_convert_if_le(ahc->ip_keys, sizeof(UINT64), + sizeof(ahc->ip_keys)); + for (i = 0; i < STREAMS*4; i++) + ahc->ip_keys[i] %= p36; /* Bring into Z_p36 */ + + /* Setup L3-2 hash variables */ + /* Fill buffer with index 4 key */ + kdf(ahc->ip_trans, prf_key, 4, STREAMS * sizeof(UINT32)); + endian_convert_if_le(ahc->ip_trans, sizeof(UINT32), + STREAMS * sizeof(UINT32)); + explicit_bzero(buf, sizeof(buf)); +} + +/* ---------------------------------------------------------------------- */ + +#if 0 +static uhash_ctx_t uhash_alloc(u_char key[]) +{ +/* Allocate memory and force to a 16-byte boundary. */ + uhash_ctx_t ctx; + u_char bytes_to_add; + aes_int_key prf_key; + + ctx = (uhash_ctx_t)malloc(sizeof(uhash_ctx)+ALLOC_BOUNDARY); + if (ctx) { + if (ALLOC_BOUNDARY) { + bytes_to_add = ALLOC_BOUNDARY - + ((ptrdiff_t)ctx & (ALLOC_BOUNDARY -1)); + ctx = (uhash_ctx_t)((u_char *)ctx + bytes_to_add); + *((u_char *)ctx - 1) = bytes_to_add; + } + aes_key_setup(key,prf_key); + uhash_init(ctx, prf_key); + } + return (ctx); +} +#endif + +/* ---------------------------------------------------------------------- */ + +#if 0 +static int uhash_free(uhash_ctx_t ctx) +{ +/* Free memory allocated by uhash_alloc */ + u_char bytes_to_sub; + + if (ctx) { + if (ALLOC_BOUNDARY) { + bytes_to_sub = *((u_char *)ctx - 1); + ctx = (uhash_ctx_t)((u_char *)ctx - bytes_to_sub); + } + free(ctx); + } + return (1); +} +#endif +/* ---------------------------------------------------------------------- */ + +static int uhash_update(uhash_ctx_t ctx, const u_char *input, long len) +/* Given len bytes of data, we parse it into L1_KEY_LEN chunks and + * hash each one with NH, calling the polyhash on each NH output. + */ +{ + UWORD bytes_hashed, bytes_remaining; + UINT64 result_buf[STREAMS]; + UINT8 *nh_result = (UINT8 *)&result_buf; + + if (ctx->msg_len + len <= L1_KEY_LEN) { + nh_update(&ctx->hash, (const UINT8 *)input, len); + ctx->msg_len += len; + } else { + + bytes_hashed = ctx->msg_len % L1_KEY_LEN; + if (ctx->msg_len == L1_KEY_LEN) + bytes_hashed = L1_KEY_LEN; + + if (bytes_hashed + len >= L1_KEY_LEN) { + + /* If some bytes have been passed to the hash function */ + /* then we want to pass at most (L1_KEY_LEN - bytes_hashed) */ + /* bytes to complete the current nh_block. */ + if (bytes_hashed) { + bytes_remaining = (L1_KEY_LEN - bytes_hashed); + nh_update(&ctx->hash, (const UINT8 *)input, bytes_remaining); + nh_final(&ctx->hash, nh_result); + ctx->msg_len += bytes_remaining; + poly_hash(ctx,(UINT32 *)nh_result); + len -= bytes_remaining; + input += bytes_remaining; + } + + /* Hash directly from input stream if enough bytes */ + while (len >= L1_KEY_LEN) { + nh(&ctx->hash, (const UINT8 *)input, L1_KEY_LEN, + L1_KEY_LEN, nh_result); + ctx->msg_len += L1_KEY_LEN; + len -= L1_KEY_LEN; + input += L1_KEY_LEN; + poly_hash(ctx,(UINT32 *)nh_result); + } + } + + /* pass remaining < L1_KEY_LEN bytes of input data to NH */ + if (len) { + nh_update(&ctx->hash, (const UINT8 *)input, len); + ctx->msg_len += len; + } + } + + return (1); +} + +/* ---------------------------------------------------------------------- */ + +static int uhash_final(uhash_ctx_t ctx, u_char *res) +/* Incorporate any pending data, pad, and generate tag */ +{ + UINT64 result_buf[STREAMS]; + UINT8 *nh_result = (UINT8 *)&result_buf; + + if (ctx->msg_len > L1_KEY_LEN) { + if (ctx->msg_len % L1_KEY_LEN) { + nh_final(&ctx->hash, nh_result); + poly_hash(ctx,(UINT32 *)nh_result); + } + ip_long(ctx, res); + } else { + nh_final(&ctx->hash, nh_result); + ip_short(ctx,nh_result, res); + } + uhash_reset(ctx); + return (1); +} + +/* ---------------------------------------------------------------------- */ + +#if 0 +static int uhash(uhash_ctx_t ahc, u_char *msg, long len, u_char *res) +/* assumes that msg is in a writable buffer of length divisible by */ +/* L1_PAD_BOUNDARY. Bytes beyond msg[len] may be zeroed. */ +{ + UINT8 nh_result[STREAMS*sizeof(UINT64)]; + UINT32 nh_len; + int extra_zeroes_needed; + + /* If the message to be hashed is no longer than L1_HASH_LEN, we skip + * the polyhash. + */ + if (len <= L1_KEY_LEN) { + if (len == 0) /* If zero length messages will not */ + nh_len = L1_PAD_BOUNDARY; /* be seen, comment out this case */ + else + nh_len = ((len + (L1_PAD_BOUNDARY - 1)) & ~(L1_PAD_BOUNDARY - 1)); + extra_zeroes_needed = nh_len - len; + zero_pad((UINT8 *)msg + len, extra_zeroes_needed); + nh(&ahc->hash, (UINT8 *)msg, nh_len, len, nh_result); + ip_short(ahc,nh_result, res); + } else { + /* Otherwise, we hash each L1_KEY_LEN chunk with NH, passing the NH + * output to poly_hash(). + */ + do { + nh(&ahc->hash, (UINT8 *)msg, L1_KEY_LEN, L1_KEY_LEN, nh_result); + poly_hash(ahc,(UINT32 *)nh_result); + len -= L1_KEY_LEN; + msg += L1_KEY_LEN; + } while (len >= L1_KEY_LEN); + if (len) { + nh_len = ((len + (L1_PAD_BOUNDARY - 1)) & ~(L1_PAD_BOUNDARY - 1)); + extra_zeroes_needed = nh_len - len; + zero_pad((UINT8 *)msg + len, extra_zeroes_needed); + nh(&ahc->hash, (UINT8 *)msg, nh_len, len, nh_result); + poly_hash(ahc,(UINT32 *)nh_result); + } + + ip_long(ahc, res); + } + + uhash_reset(ahc); + return 1; +} +#endif + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- Begin UMAC Section --------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ + +/* The UMAC interface has two interfaces, an all-at-once interface where + * the entire message to be authenticated is passed to UMAC in one buffer, + * and a sequential interface where the message is presented a little at a + * time. The all-at-once is more optimized than the sequential version and + * should be preferred when the sequential interface is not required. + */ +struct umac_ctx { + uhash_ctx hash; /* Hash function for message compression */ + pdf_ctx pdf; /* PDF for hashed output */ + void *free_ptr; /* Address to free this struct via */ +} umac_ctx; + +/* ---------------------------------------------------------------------- */ + +#if 0 +int umac_reset(struct umac_ctx *ctx) +/* Reset the hash function to begin a new authentication. */ +{ + uhash_reset(&ctx->hash); + return (1); +} +#endif + +/* ---------------------------------------------------------------------- */ + +int umac_delete(struct umac_ctx *ctx) +/* Deallocate the ctx structure */ +{ + if (ctx) { + if (ALLOC_BOUNDARY) + ctx = (struct umac_ctx *)ctx->free_ptr; + freezero(ctx, sizeof(*ctx) + ALLOC_BOUNDARY); + } + return (1); +} + +/* ---------------------------------------------------------------------- */ + +struct umac_ctx *umac_new(const u_char key[]) +/* Dynamically allocate a umac_ctx struct, initialize variables, + * generate subkeys from key. Align to 16-byte boundary. + */ +{ + struct umac_ctx *ctx, *octx; + size_t bytes_to_add; + aes_int_key prf_key; + + octx = ctx = xcalloc(1, sizeof(*ctx) + ALLOC_BOUNDARY); + if (ctx) { + if (ALLOC_BOUNDARY) { + bytes_to_add = ALLOC_BOUNDARY - + ((ptrdiff_t)ctx & (ALLOC_BOUNDARY - 1)); + ctx = (struct umac_ctx *)((u_char *)ctx + bytes_to_add); + } + ctx->free_ptr = octx; + aes_key_setup(key, prf_key); + pdf_init(&ctx->pdf, prf_key); + uhash_init(&ctx->hash, prf_key); + explicit_bzero(prf_key, sizeof(prf_key)); + } + + return (ctx); +} + +/* ---------------------------------------------------------------------- */ + +int umac_final(struct umac_ctx *ctx, u_char tag[], const u_char nonce[8]) +/* Incorporate any pending data, pad, and generate tag */ +{ + uhash_final(&ctx->hash, (u_char *)tag); + pdf_gen_xor(&ctx->pdf, (const UINT8 *)nonce, (UINT8 *)tag); + + return (1); +} + +/* ---------------------------------------------------------------------- */ + +int umac_update(struct umac_ctx *ctx, const u_char *input, long len) +/* Given len bytes of data, we parse it into L1_KEY_LEN chunks and */ +/* hash each one, calling the PDF on the hashed output whenever the hash- */ +/* output buffer is full. */ +{ + uhash_update(&ctx->hash, input, len); + return (1); +} + +/* ---------------------------------------------------------------------- */ + +#if 0 +int umac(struct umac_ctx *ctx, u_char *input, + long len, u_char tag[], + u_char nonce[8]) +/* All-in-one version simply calls umac_update() and umac_final(). */ +{ + uhash(&ctx->hash, input, len, (u_char *)tag); + pdf_gen_xor(&ctx->pdf, (UINT8 *)nonce, (UINT8 *)tag); + + return (1); +} +#endif + +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ----- End UMAC Section ----------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ diff --git a/aosp/external/openssh/umac.h b/aosp/external/openssh/umac.h new file mode 100644 index 0000000000000000000000000000000000000000..f21398a8d73390cab19699b3c2cd45298973331d --- /dev/null +++ b/aosp/external/openssh/umac.h @@ -0,0 +1,129 @@ +/* $OpenBSD: umac.h,v 1.5 2022/01/01 01:55:30 jsg Exp $ */ +/* ----------------------------------------------------------------------- + * + * umac.h -- C Implementation UMAC Message Authentication + * + * Version 0.93a of rfc4418.txt -- 2006 July 14 + * + * For a full description of UMAC message authentication see the UMAC + * world-wide-web page at http://www.cs.ucdavis.edu/~rogaway/umac + * Please report bugs and suggestions to the UMAC webpage. + * + * Copyright (c) 1999-2004 Ted Krovetz + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and with or without fee, is hereby + * granted provided that the above copyright notice appears in all copies + * and in supporting documentation, and that the name of the copyright + * holder not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * + * Comments should be directed to Ted Krovetz (tdk@acm.org) + * + * ---------------------------------------------------------------------- */ + + /* ////////////////////// IMPORTANT NOTES ///////////////////////////////// + * + * 1) This version does not work properly on messages larger than 16MB + * + * 2) If you set the switch to use SSE2, then all data must be 16-byte + * aligned + * + * 3) When calling the function umac(), it is assumed that msg is in + * a writable buffer of length divisible by 32 bytes. The message itself + * does not have to fill the entire buffer, but bytes beyond msg may be + * zeroed. + * + * 4) Two free AES implementations are supported by this implementation of + * UMAC. Paulo Barreto's version is in the public domain and can be found + * at http://www.esat.kuleuven.ac.be/~rijmen/rijndael/ (search for + * "Barreto"). The only two files needed are rijndael-alg-fst.c and + * rijndael-alg-fst.h. + * Brian Gladman's version is distributed with GNU Public license + * and can be found at http://fp.gladman.plus.com/AES/index.htm. It + * includes a fast IA-32 assembly version. + * + /////////////////////////////////////////////////////////////////////// */ +#ifndef HEADER_UMAC_H +#define HEADER_UMAC_H + + +#ifdef __cplusplus + extern "C" { +#endif + +struct umac_ctx *umac_new(const u_char key[]); +/* Dynamically allocate a umac_ctx struct, initialize variables, + * generate subkeys from key. + */ + +#if 0 +int umac_reset(struct umac_ctx *ctx); +/* Reset a umac_ctx to begin authenticating a new message */ +#endif + +int umac_update(struct umac_ctx *ctx, const u_char *input, long len); +/* Incorporate len bytes pointed to by input into context ctx */ + +int umac_final(struct umac_ctx *ctx, u_char tag[], const u_char nonce[8]); +/* Incorporate any pending data and the ctr value, and return tag. + * This function returns error code if ctr < 0. + */ + +int umac_delete(struct umac_ctx *ctx); +/* Deallocate the context structure */ + +#if 0 +int umac(struct umac_ctx *ctx, u_char *input, + long len, u_char tag[], + u_char nonce[8]); +/* All-in-one implementation of the functions Reset, Update and Final */ +#endif + +/* uhash.h */ + + +#if 0 +typedef struct uhash_ctx *uhash_ctx_t; + /* The uhash_ctx structure is defined by the implementation of the */ + /* UHASH functions. */ + +uhash_ctx_t uhash_alloc(u_char key[16]); + /* Dynamically allocate a uhash_ctx struct and generate subkeys using */ + /* the kdf and kdf_key passed in. If kdf_key_len is 0 then RC6 is */ + /* used to generate key with a fixed key. If kdf_key_len > 0 but kdf */ + /* is NULL then the first 16 bytes pointed at by kdf_key is used as a */ + /* key for an RC6 based KDF. */ + +int uhash_free(uhash_ctx_t ctx); + +int uhash_set_params(uhash_ctx_t ctx, + void *params); + +int uhash_reset(uhash_ctx_t ctx); + +int uhash_update(uhash_ctx_t ctx, + u_char *input, + long len); + +int uhash_final(uhash_ctx_t ctx, + u_char output[]); + +int uhash(uhash_ctx_t ctx, + u_char *input, + long len, + u_char output[]); + +#endif + +/* matching umac-128 API, we reuse umac_ctx, since it's opaque */ +struct umac_ctx *umac128_new(const u_char key[]); +int umac128_update(struct umac_ctx *ctx, const u_char *input, long len); +int umac128_final(struct umac_ctx *ctx, u_char tag[], const u_char nonce[8]); +int umac128_delete(struct umac_ctx *ctx); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_UMAC_H */ diff --git a/aosp/external/openssh/umac128.c b/aosp/external/openssh/umac128.c new file mode 100644 index 0000000000000000000000000000000000000000..f71792506264be065f5bf22774ae220661fe42a5 --- /dev/null +++ b/aosp/external/openssh/umac128.c @@ -0,0 +1,10 @@ +/* $OpenBSD: umac128.c,v 1.2 2018/02/08 04:12:32 dtucker Exp $ */ + +#define UMAC_OUTPUT_LEN 16 +#define umac_new umac128_new +#define umac_update umac128_update +#define umac_final umac128_final +#define umac_delete umac128_delete +#define umac_ctx umac128_ctx + +#include "umac.c" diff --git a/aosp/external/openssh/utf8.c b/aosp/external/openssh/utf8.c new file mode 100644 index 0000000000000000000000000000000000000000..7f63b25aeefccb30d051722e4892eb3a95720108 --- /dev/null +++ b/aosp/external/openssh/utf8.c @@ -0,0 +1,355 @@ +/* $OpenBSD: utf8.c,v 1.11 2020/05/01 06:28:52 djm Exp $ */ +/* + * Copyright (c) 2016 Ingo Schwarze + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Utility functions for multibyte-character handling, + * in particular to sanitize untrusted strings for terminal output. + */ + +#include "includes.h" + +#include +#ifdef HAVE_LANGINFO_H +# include +#endif +#include +#include +#include +#include +#include +#include +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +# include +#endif +#ifdef HAVE_WCHAR_H +# include +#endif + +#include "utf8.h" + +static int dangerous_locale(void); +static int grow_dst(char **, size_t *, size_t, char **, size_t); + + +/* + * For US-ASCII and UTF-8 encodings, we can safely recover from + * encoding errors and from non-printable characters. For any + * other encodings, err to the side of caution and abort parsing: + * For state-dependent encodings, recovery is impossible. + * For arbitrary encodings, replacement of non-printable + * characters would be non-trivial and too fragile. + * The comments indicate what nl_langinfo(CODESET) + * returns for US-ASCII on various operating systems. + */ + +static int +dangerous_locale(void) { + char *loc; + + loc = nl_langinfo(CODESET); + return strcmp(loc, "UTF-8") != 0 && + strcmp(loc, "US-ASCII") != 0 && /* OpenBSD */ + strcmp(loc, "ANSI_X3.4-1968") != 0 && /* Linux */ + strcmp(loc, "ISO8859-1") != 0 && /* AIX */ + strcmp(loc, "646") != 0 && /* Solaris, NetBSD */ + strcmp(loc, "") != 0; /* Solaris 6 */ +} + +static int +grow_dst(char **dst, size_t *sz, size_t maxsz, char **dp, size_t need) +{ + char *tp; + size_t tsz; + + if (*dp + need < *dst + *sz) + return 0; + tsz = *sz + 128; + if (tsz > maxsz) + tsz = maxsz; + if ((tp = recallocarray(*dst, *sz, tsz, 1)) == NULL) + return -1; + *dp = tp + (*dp - *dst); + *dst = tp; + *sz = tsz; + return 0; +} + +/* + * The following two functions limit the number of bytes written, + * including the terminating '\0', to sz. Unless wp is NULL, + * they limit the number of display columns occupied to *wp. + * Whichever is reached first terminates the output string. + * To stay close to the standard interfaces, they return the number of + * non-NUL bytes that would have been written if both were unlimited. + * If wp is NULL, newline, carriage return, and tab are allowed; + * otherwise, the actual number of columns occupied by what was + * written is returned in *wp. + */ + +int +vasnmprintf(char **str, size_t maxsz, int *wp, const char *fmt, va_list ap) +{ + char *src; /* Source string returned from vasprintf. */ + char *sp; /* Pointer into src. */ + char *dst; /* Destination string to be returned. */ + char *dp; /* Pointer into dst. */ + char *tp; /* Temporary pointer for dst. */ + size_t sz; /* Number of bytes allocated for dst. */ + wchar_t wc; /* Wide character at sp. */ + int len; /* Number of bytes in the character at sp. */ + int ret; /* Number of bytes needed to format src. */ + int width; /* Display width of the character wc. */ + int total_width, max_width, print; + + src = NULL; + if ((ret = vasprintf(&src, fmt, ap)) <= 0) + goto fail; + + sz = strlen(src) + 1; + if ((dst = malloc(sz)) == NULL) { + free(src); + ret = -1; + goto fail; + } + + if (maxsz > INT_MAX) + maxsz = INT_MAX; + + sp = src; + dp = dst; + ret = 0; + print = 1; + total_width = 0; + max_width = wp == NULL ? INT_MAX : *wp; + while (*sp != '\0') { + if ((len = mbtowc(&wc, sp, MB_CUR_MAX)) == -1) { + (void)mbtowc(NULL, NULL, MB_CUR_MAX); + if (dangerous_locale()) { + ret = -1; + break; + } + len = 1; + width = -1; + } else if (wp == NULL && + (wc == L'\n' || wc == L'\r' || wc == L'\t')) { + /* + * Don't use width uninitialized; the actual + * value doesn't matter because total_width + * is only returned for wp != NULL. + */ + width = 0; + } else if ((width = wcwidth(wc)) == -1 && + dangerous_locale()) { + ret = -1; + break; + } + + /* Valid, printable character. */ + + if (width >= 0) { + if (print && (dp - dst >= (int)maxsz - len || + total_width > max_width - width)) + print = 0; + if (print) { + if (grow_dst(&dst, &sz, maxsz, + &dp, len) == -1) { + ret = -1; + break; + } + total_width += width; + memcpy(dp, sp, len); + dp += len; + } + sp += len; + if (ret >= 0) + ret += len; + continue; + } + + /* Escaping required. */ + + while (len > 0) { + if (print && (dp - dst >= (int)maxsz - 4 || + total_width > max_width - 4)) + print = 0; + if (print) { + if (grow_dst(&dst, &sz, maxsz, + &dp, 4) == -1) { + ret = -1; + break; + } + tp = vis(dp, *sp, VIS_OCTAL | VIS_ALL, 0); + width = tp - dp; + total_width += width; + dp = tp; + } else + width = 4; + len--; + sp++; + if (ret >= 0) + ret += width; + } + if (len > 0) + break; + } + free(src); + *dp = '\0'; + *str = dst; + if (wp != NULL) + *wp = total_width; + + /* + * If the string was truncated by the width limit but + * would have fit into the size limit, the only sane way + * to report the problem is using the return value, such + * that the usual idiom "if (ret < 0 || ret >= sz) error" + * works as expected. + */ + + if (ret < (int)maxsz && !print) + ret = -1; + return ret; + +fail: + if (wp != NULL) + *wp = 0; + if (ret == 0) { + *str = src; + return 0; + } else { + *str = NULL; + return -1; + } +} + +int +snmprintf(char *str, size_t sz, int *wp, const char *fmt, ...) +{ + va_list ap; + char *cp = NULL; + int ret; + + va_start(ap, fmt); + ret = vasnmprintf(&cp, sz, wp, fmt, ap); + va_end(ap); + if (cp != NULL) { + (void)strlcpy(str, cp, sz); + free(cp); + } else + *str = '\0'; + return ret; +} + +int +asmprintf(char **outp, size_t sz, int *wp, const char *fmt, ...) +{ + va_list ap; + int ret; + + *outp = NULL; + va_start(ap, fmt); + ret = vasnmprintf(outp, sz, wp, fmt, ap); + va_end(ap); + + return ret; +} + +/* + * To stay close to the standard interfaces, the following functions + * return the number of non-NUL bytes written. + */ + +int +vfmprintf(FILE *stream, const char *fmt, va_list ap) +{ + char *str = NULL; + int ret; + + if ((ret = vasnmprintf(&str, INT_MAX, NULL, fmt, ap)) < 0) { + free(str); + return -1; + } + if (fputs(str, stream) == EOF) + ret = -1; + free(str); + return ret; +} + +int +fmprintf(FILE *stream, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfmprintf(stream, fmt, ap); + va_end(ap); + return ret; +} + +int +mprintf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfmprintf(stdout, fmt, ap); + va_end(ap); + return ret; +} + +/* + * Set up libc for multibyte output in the user's chosen locale. + * + * XXX: we are known to have problems with Turkish (i/I confusion) so we + * deliberately fall back to the C locale for now. Longer term we should + * always prefer to select C.[encoding] if possible, but there's no + * standardisation in locales between systems, so we'll need to survey + * what's out there first. + */ +void +msetlocale(void) +{ + const char *vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL }; + char *cp; + int i; + + /* + * We can't yet cope with dotless/dotted I in Turkish locales, + * so fall back to the C locale for these. + */ + for (i = 0; vars[i] != NULL; i++) { + if ((cp = getenv(vars[i])) == NULL) + continue; + if (strncasecmp(cp, "TR", 2) != 0) + break; + /* + * If we're in a UTF-8 locale then prefer to use + * the C.UTF-8 locale (or equivalent) if it exists. + */ + if ((strcasestr(cp, "UTF-8") != NULL || + strcasestr(cp, "UTF8") != NULL) && + (setlocale(LC_CTYPE, "C.UTF-8") != NULL || + setlocale(LC_CTYPE, "POSIX.UTF-8") != NULL)) + return; + setlocale(LC_CTYPE, "C"); + return; + } + /* We can handle this locale */ + setlocale(LC_CTYPE, ""); +} diff --git a/aosp/external/openssh/utf8.h b/aosp/external/openssh/utf8.h new file mode 100644 index 0000000000000000000000000000000000000000..09941d47180a7ae06d0ef4c76040fcc7f51786ad --- /dev/null +++ b/aosp/external/openssh/utf8.h @@ -0,0 +1,28 @@ +/* $OpenBSD: utf8.h,v 1.4 2021/04/03 06:18:41 djm Exp $ */ +/* + * Copyright (c) 2016 Ingo Schwarze + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int vasnmprintf(char **, size_t, int *, const char *, va_list); +int mprintf(const char *, ...) + __attribute__((format(printf, 1, 2))); +int fmprintf(FILE *, const char *, ...) + __attribute__((format(printf, 2, 3))); +int vfmprintf(FILE *, const char *, va_list); +int snmprintf(char *, size_t, int *, const char *, ...) + __attribute__((format(printf, 4, 5))); +int asmprintf(char **, size_t, int *, const char *, ...) + __attribute__((format(printf, 4, 5))); +void msetlocale(void); diff --git a/aosp/external/openssh/verify.c b/aosp/external/openssh/verify.c new file mode 100644 index 0000000000000000000000000000000000000000..1671a4132c9e252fbc64a20ecded833067d515c9 --- /dev/null +++ b/aosp/external/openssh/verify.c @@ -0,0 +1,49 @@ +/* $OpenBSD: verify.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Author: Daniel J. Bernstein + * Copied from nacl-20110221/crypto_verify/32/ref/verify.c + */ + +#include "includes.h" + +#include "crypto_api.h" + +int crypto_verify_32(const unsigned char *x,const unsigned char *y) +{ + unsigned int differentbits = 0; +#define F(i) differentbits |= x[i] ^ y[i]; + F(0) + F(1) + F(2) + F(3) + F(4) + F(5) + F(6) + F(7) + F(8) + F(9) + F(10) + F(11) + F(12) + F(13) + F(14) + F(15) + F(16) + F(17) + F(18) + F(19) + F(20) + F(21) + F(22) + F(23) + F(24) + F(25) + F(26) + F(27) + F(28) + F(29) + F(30) + F(31) + return (1 & ((differentbits - 1) >> 8)) - 1; +} diff --git a/aosp/external/openssh/version.h b/aosp/external/openssh/version.h new file mode 100644 index 0000000000000000000000000000000000000000..e600fe4c34c9c2a766345721bcd5a2e6ad1d07e3 --- /dev/null +++ b/aosp/external/openssh/version.h @@ -0,0 +1,6 @@ +/* $OpenBSD: version.h,v 1.94 2022/04/04 22:45:25 djm Exp $ */ + +#define SSH_VERSION "OpenSSH_9.0" + +#define SSH_PORTABLE "p1" +#define SSH_RELEASE SSH_VERSION SSH_PORTABLE diff --git a/aosp/external/openssh/xmalloc.c b/aosp/external/openssh/xmalloc.c new file mode 100644 index 0000000000000000000000000000000000000000..67191e3f214ddfb15f9bb72a49e50e4c8c00fc8f --- /dev/null +++ b/aosp/external/openssh/xmalloc.c @@ -0,0 +1,118 @@ +/* $OpenBSD: xmalloc.c,v 1.37 2022/03/13 23:27:54 cheloha Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include + +#include "xmalloc.h" +#include "log.h" + +#if defined(__OpenBSD__) +char *malloc_options = "S"; +#endif /* __OpenBSD__ */ + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: out of memory (allocating %zu bytes)", size); + return ptr; +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (size == 0 || nmemb == 0) + fatal("xcalloc: zero size"); + if (SIZE_MAX / nmemb < size) + fatal("xcalloc: nmemb * size > SIZE_MAX"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + fatal("xcalloc: out of memory (allocating %zu bytes)", + size * nmemb); + return ptr; +} + +void * +xreallocarray(void *ptr, size_t nmemb, size_t size) +{ + void *new_ptr; + + new_ptr = reallocarray(ptr, nmemb, size); + if (new_ptr == NULL) + fatal("xreallocarray: out of memory (%zu elements of %zu bytes)", + nmemb, size); + return new_ptr; +} + +void * +xrecallocarray(void *ptr, size_t onmemb, size_t nmemb, size_t size) +{ + void *new_ptr; + + new_ptr = recallocarray(ptr, onmemb, nmemb, size); + if (new_ptr == NULL) + fatal("xrecallocarray: out of memory (%zu elements of %zu bytes)", + nmemb, size); + return new_ptr; +} + +char * +xstrdup(const char *str) +{ + size_t len; + char *cp; + + len = strlen(str) + 1; + cp = xmalloc(len); + return memcpy(cp, str, len); +} + +int +xvasprintf(char **ret, const char *fmt, va_list ap) +{ + int i; + + i = vasprintf(ret, fmt, ap); + if (i < 0 || *ret == NULL) + fatal("xvasprintf: could not allocate memory"); + return i; +} + +int +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvasprintf(ret, fmt, ap); + va_end(ap); + return i; +} diff --git a/aosp/external/openssh/xmalloc.h b/aosp/external/openssh/xmalloc.h new file mode 100644 index 0000000000000000000000000000000000000000..a6b8d23bde8e1879f5a8d74e06c67f6a2a4a3d9b --- /dev/null +++ b/aosp/external/openssh/xmalloc.h @@ -0,0 +1,27 @@ +/* $OpenBSD: xmalloc.h,v 1.20 2021/04/03 06:18:41 djm Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xreallocarray(void *, size_t, size_t); +void *xrecallocarray(void *, size_t, size_t, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); +int xvasprintf(char **, const char *, va_list) + __attribute__((__nonnull__ (2))); diff --git a/aosp/external/openssh/xmss_commons.c b/aosp/external/openssh/xmss_commons.c new file mode 100644 index 0000000000000000000000000000000000000000..8d6b80b6eb799cf124ab097d2c9523339d43e630 --- /dev/null +++ b/aosp/external/openssh/xmss_commons.c @@ -0,0 +1,36 @@ +/* $OpenBSD: xmss_commons.c,v 1.2 2018/02/26 03:56:44 dtucker Exp $ */ +/* +xmss_commons.c 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "includes.h" +#ifdef WITH_XMSS + +#include "xmss_commons.h" +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif + +void to_byte(unsigned char *out, unsigned long long in, uint32_t bytes) +{ + int32_t i; + for (i = bytes-1; i >= 0; i--) { + out[i] = in & 0xff; + in = in >> 8; + } +} + +#if 0 +void hexdump(const unsigned char *a, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf("%02x", a[i]); +} +#endif +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_commons.h b/aosp/external/openssh/xmss_commons.h new file mode 100644 index 0000000000000000000000000000000000000000..a98e4799c4256f4495b850fcaf6072cf99d64fc4 --- /dev/null +++ b/aosp/external/openssh/xmss_commons.h @@ -0,0 +1,21 @@ +#ifdef WITH_XMSS +/* $OpenBSD: xmss_commons.h,v 1.3 2018/02/26 03:56:44 dtucker Exp $ */ +/* +xmss_commons.h 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ +#ifndef XMSS_COMMONS_H +#define XMSS_COMMONS_H + +#include +#ifdef HAVE_STDINT_H +#include +#endif +#endif +void to_byte(unsigned char *output, unsigned long long in, uint32_t bytes); +#if 0 +void hexdump(const unsigned char *a, size_t len); +#endif +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_fast.c b/aosp/external/openssh/xmss_fast.c new file mode 100644 index 0000000000000000000000000000000000000000..421b39a37a9e4a8071989d3dafe90a95eab71982 --- /dev/null +++ b/aosp/external/openssh/xmss_fast.c @@ -0,0 +1,1106 @@ +/* $OpenBSD: xmss_fast.c,v 1.3 2018/03/22 07:06:11 markus Exp $ */ +/* +xmss_fast.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "includes.h" +#ifdef WITH_XMSS + +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif + +#include "xmss_fast.h" +#include "crypto_api.h" +#include "xmss_wots.h" +#include "xmss_hash.h" + +#include "xmss_commons.h" +#include "xmss_hash_address.h" +// For testing +#include "stdio.h" + + + +/** + * Used for pseudorandom keygeneration, + * generates the seed for the WOTS keypair at address addr + * + * takes n byte sk_seed and returns n byte seed using 32 byte address addr. + */ +static void get_seed(unsigned char *seed, const unsigned char *sk_seed, int n, uint32_t addr[8]) +{ + unsigned char bytes[32]; + // Make sure that chain addr, hash addr, and key bit are 0! + setChainADRS(addr,0); + setHashADRS(addr,0); + setKeyAndMask(addr,0); + // Generate pseudorandom value + addr_to_byte(bytes, addr); + prf(seed, bytes, sk_seed, n); +} + +/** + * Initialize xmss params struct + * parameter names are the same as in the draft + * parameter k is K as used in the BDS algorithm + */ +int xmss_set_params(xmss_params *params, int n, int h, int w, int k) +{ + if (k >= h || k < 2 || (h - k) % 2) { + fprintf(stderr, "For BDS traversal, H - K must be even, with H > K >= 2!\n"); + return 1; + } + params->h = h; + params->n = n; + params->k = k; + wots_params wots_par; + wots_set_params(&wots_par, n, w); + params->wots_par = wots_par; + return 0; +} + +/** + * Initialize BDS state struct + * parameter names are the same as used in the description of the BDS traversal + */ +void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf) +{ + state->stack = stack; + state->stackoffset = stackoffset; + state->stacklevels = stacklevels; + state->auth = auth; + state->keep = keep; + state->treehash = treehash; + state->retain = retain; + state->next_leaf = next_leaf; +} + +/** + * Initialize xmssmt_params struct + * parameter names are the same as in the draft + * + * Especially h is the total tree height, i.e. the XMSS trees have height h/d + */ +int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k) +{ + if (h % d) { + fprintf(stderr, "d must divide h without remainder!\n"); + return 1; + } + params->h = h; + params->d = d; + params->n = n; + params->index_len = (h + 7) / 8; + xmss_params xmss_par; + if (xmss_set_params(&xmss_par, n, (h/d), w, k)) { + return 1; + } + params->xmss_par = xmss_par; + return 0; +} + +/** + * Computes a leaf from a WOTS public key using an L-tree. + */ +static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + unsigned int l = params->wots_par.len; + unsigned int n = params->n; + uint32_t i = 0; + uint32_t height = 0; + uint32_t bound; + + //ADRS.setTreeHeight(0); + setTreeHeight(addr, height); + + while (l > 1) { + bound = l >> 1; //floor(l / 2); + for (i = 0; i < bound; i++) { + //ADRS.setTreeIndex(i); + setTreeIndex(addr, i); + //wots_pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS); + hash_h(wots_pk+i*n, wots_pk+i*2*n, pub_seed, addr, n); + } + //if ( l % 2 == 1 ) { + if (l & 1) { + //pk[floor(l / 2) + 1] = pk[l]; + memcpy(wots_pk+(l>>1)*n, wots_pk+(l-1)*n, n); + //l = ceil(l / 2); + l=(l>>1)+1; + } + else { + //l = ceil(l / 2); + l=(l>>1); + } + //ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + height++; + setTreeHeight(addr, height); + } + //return pk[0]; + memcpy(leaf, wots_pk, n); +} + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, then computes leaf using l_tree. As this happens position independent, we only require that addr encodes the right ltree-address. + */ +static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8]) +{ + unsigned char seed[params->n]; + unsigned char pk[params->wots_par.keysize]; + + get_seed(seed, sk_seed, params->n, ots_addr); + wots_pkgen(pk, seed, &(params->wots_par), pub_seed, ots_addr); + + l_tree(leaf, pk, params, pub_seed, ltree_addr); +} + +static int treehash_minheight_on_stack(bds_state* state, const xmss_params *params, const treehash_inst *treehash) { + unsigned int r = params->h, i; + for (i = 0; i < treehash->stackusage; i++) { + if (state->stacklevels[state->stackoffset - i - 1] < r) { + r = state->stacklevels[state->stackoffset - i - 1]; + } + } + return r; +} + +/** + * Merkle's TreeHash algorithm. The address only needs to initialize the first 78 bits of addr. Everything else will be set by treehash. + * Currently only used for key generation. + * + */ +static void treehash_setup(unsigned char *node, int height, int index, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8]) +{ + unsigned int idx = index; + unsigned int n = params->n; + unsigned int h = params->h; + unsigned int k = params->k; + // use three different addresses because at this point we use all three formats in parallel + uint32_t ots_addr[8]; + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + uint32_t lastnode, i; + unsigned char stack[(height+1)*n]; + unsigned int stacklevels[height+1]; + unsigned int stackoffset=0; + unsigned int nodeh; + + lastnode = idx+(1<treehash[i].h = i; + state->treehash[i].completed = 1; + state->treehash[i].stackusage = 0; + } + + i = 0; + for (; idx < lastnode; idx++) { + setLtreeADRS(ltree_addr, idx); + setOTSADRS(ots_addr, idx); + gen_leaf_wots(stack+stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr); + stacklevels[stackoffset] = 0; + stackoffset++; + if (h - k > 0 && i == 3) { + memcpy(state->treehash[0].node, stack+stackoffset*n, n); + } + while (stackoffset>1 && stacklevels[stackoffset-1] == stacklevels[stackoffset-2]) + { + nodeh = stacklevels[stackoffset-1]; + if (i >> nodeh == 1) { + memcpy(state->auth + nodeh*n, stack+(stackoffset-1)*n, n); + } + else { + if (nodeh < h - k && i >> nodeh == 3) { + memcpy(state->treehash[nodeh].node, stack+(stackoffset-1)*n, n); + } + else if (nodeh >= h - k) { + memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((i >> nodeh) - 3) >> 1)) * n, stack+(stackoffset-1)*n, n); + } + } + setTreeHeight(node_addr, stacklevels[stackoffset-1]); + setTreeIndex(node_addr, (idx >> (stacklevels[stackoffset-1]+1))); + hash_h(stack+(stackoffset-2)*n, stack+(stackoffset-2)*n, pub_seed, + node_addr, n); + stacklevels[stackoffset-2]++; + stackoffset--; + } + i++; + } + + for (i = 0; i < n; i++) + node[i] = stack[i]; +} + +static void treehash_update(treehash_inst *treehash, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8]) { + int n = params->n; + + uint32_t ots_addr[8]; + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + setLtreeADRS(ltree_addr, treehash->next_idx); + setOTSADRS(ots_addr, treehash->next_idx); + + unsigned char nodebuffer[2 * n]; + unsigned int nodeheight = 0; + gen_leaf_wots(nodebuffer, sk_seed, params, pub_seed, ltree_addr, ots_addr); + while (treehash->stackusage > 0 && state->stacklevels[state->stackoffset-1] == nodeheight) { + memcpy(nodebuffer + n, nodebuffer, n); + memcpy(nodebuffer, state->stack + (state->stackoffset-1)*n, n); + setTreeHeight(node_addr, nodeheight); + setTreeIndex(node_addr, (treehash->next_idx >> (nodeheight+1))); + hash_h(nodebuffer, nodebuffer, pub_seed, node_addr, n); + nodeheight++; + treehash->stackusage--; + state->stackoffset--; + } + if (nodeheight == treehash->h) { // this also implies stackusage == 0 + memcpy(treehash->node, nodebuffer, n); + treehash->completed = 1; + } + else { + memcpy(state->stack + state->stackoffset*n, nodebuffer, n); + treehash->stackusage++; + state->stacklevels[state->stackoffset] = nodeheight; + state->stackoffset++; + treehash->next_idx++; + } +} + +/** + * Computes a root node given a leaf and an authapth + */ +static void validate_authpath(unsigned char *root, const unsigned char *leaf, unsigned long leafidx, const unsigned char *authpath, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + unsigned int n = params->n; + + uint32_t i, j; + unsigned char buffer[2*n]; + + // If leafidx is odd (last bit = 1), current path element is a right child and authpath has to go to the left. + // Otherwise, it is the other way around + if (leafidx & 1) { + for (j = 0; j < n; j++) + buffer[n+j] = leaf[j]; + for (j = 0; j < n; j++) + buffer[j] = authpath[j]; + } + else { + for (j = 0; j < n; j++) + buffer[j] = leaf[j]; + for (j = 0; j < n; j++) + buffer[n+j] = authpath[j]; + } + authpath += n; + + for (i=0; i < params->h-1; i++) { + setTreeHeight(addr, i); + leafidx >>= 1; + setTreeIndex(addr, leafidx); + if (leafidx&1) { + hash_h(buffer+n, buffer, pub_seed, addr, n); + for (j = 0; j < n; j++) + buffer[j] = authpath[j]; + } + else { + hash_h(buffer, buffer, pub_seed, addr, n); + for (j = 0; j < n; j++) + buffer[j+n] = authpath[j]; + } + authpath += n; + } + setTreeHeight(addr, (params->h-1)); + leafidx >>= 1; + setTreeIndex(addr, leafidx); + hash_h(root, buffer, pub_seed, addr, n); +} + +/** + * Performs one treehash update on the instance that needs it the most. + * Returns 1 if such an instance was not found + **/ +static char bds_treehash_update(bds_state *state, unsigned int updates, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) { + uint32_t i, j; + unsigned int level, l_min, low; + unsigned int h = params->h; + unsigned int k = params->k; + unsigned int used = 0; + + for (j = 0; j < updates; j++) { + l_min = h; + level = h - k; + for (i = 0; i < h - k; i++) { + if (state->treehash[i].completed) { + low = h; + } + else if (state->treehash[i].stackusage == 0) { + low = i; + } + else { + low = treehash_minheight_on_stack(state, params, &(state->treehash[i])); + } + if (low < l_min) { + level = i; + l_min = low; + } + } + if (level == h - k) { + break; + } + treehash_update(&(state->treehash[level]), state, sk_seed, params, pub_seed, addr); + used++; + } + return updates - used; +} + +/** + * Updates the state (typically NEXT_i) by adding a leaf and updating the stack + * Returns 1 if all leaf nodes have already been processed + **/ +static char bds_state_update(bds_state *state, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) { + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + uint32_t ots_addr[8]; + + int n = params->n; + int h = params->h; + int k = params->k; + + int nodeh; + int idx = state->next_leaf; + if (idx == 1 << h) { + return 1; + } + + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + setOTSADRS(ots_addr, idx); + setLtreeADRS(ltree_addr, idx); + + gen_leaf_wots(state->stack+state->stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr); + + state->stacklevels[state->stackoffset] = 0; + state->stackoffset++; + if (h - k > 0 && idx == 3) { + memcpy(state->treehash[0].node, state->stack+state->stackoffset*n, n); + } + while (state->stackoffset>1 && state->stacklevels[state->stackoffset-1] == state->stacklevels[state->stackoffset-2]) { + nodeh = state->stacklevels[state->stackoffset-1]; + if (idx >> nodeh == 1) { + memcpy(state->auth + nodeh*n, state->stack+(state->stackoffset-1)*n, n); + } + else { + if (nodeh < h - k && idx >> nodeh == 3) { + memcpy(state->treehash[nodeh].node, state->stack+(state->stackoffset-1)*n, n); + } + else if (nodeh >= h - k) { + memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((idx >> nodeh) - 3) >> 1)) * n, state->stack+(state->stackoffset-1)*n, n); + } + } + setTreeHeight(node_addr, state->stacklevels[state->stackoffset-1]); + setTreeIndex(node_addr, (idx >> (state->stacklevels[state->stackoffset-1]+1))); + hash_h(state->stack+(state->stackoffset-2)*n, state->stack+(state->stackoffset-2)*n, pub_seed, node_addr, n); + + state->stacklevels[state->stackoffset-2]++; + state->stackoffset--; + } + state->next_leaf++; + return 0; +} + +/** + * Returns the auth path for node leaf_idx and computes the auth path for the + * next leaf node, using the algorithm described by Buchmann, Dahmen and Szydlo + * in "Post Quantum Cryptography", Springer 2009. + */ +static void bds_round(bds_state *state, const unsigned long leaf_idx, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, uint32_t addr[8]) +{ + unsigned int i; + unsigned int n = params->n; + unsigned int h = params->h; + unsigned int k = params->k; + + unsigned int tau = h; + unsigned int startidx; + unsigned int offset, rowidx; + unsigned char buf[2 * n]; + + uint32_t ots_addr[8]; + uint32_t ltree_addr[8]; + uint32_t node_addr[8]; + // only copy layer and tree address parts + memcpy(ots_addr, addr, 12); + // type = ots + setType(ots_addr, 0); + memcpy(ltree_addr, addr, 12); + setType(ltree_addr, 1); + memcpy(node_addr, addr, 12); + setType(node_addr, 2); + + for (i = 0; i < h; i++) { + if (! ((leaf_idx >> i) & 1)) { + tau = i; + break; + } + } + + if (tau > 0) { + memcpy(buf, state->auth + (tau-1) * n, n); + // we need to do this before refreshing state->keep to prevent overwriting + memcpy(buf + n, state->keep + ((tau-1) >> 1) * n, n); + } + if (!((leaf_idx >> (tau + 1)) & 1) && (tau < h - 1)) { + memcpy(state->keep + (tau >> 1)*n, state->auth + tau*n, n); + } + if (tau == 0) { + setLtreeADRS(ltree_addr, leaf_idx); + setOTSADRS(ots_addr, leaf_idx); + gen_leaf_wots(state->auth, sk_seed, params, pub_seed, ltree_addr, ots_addr); + } + else { + setTreeHeight(node_addr, (tau-1)); + setTreeIndex(node_addr, leaf_idx >> tau); + hash_h(state->auth + tau * n, buf, pub_seed, node_addr, n); + for (i = 0; i < tau; i++) { + if (i < h - k) { + memcpy(state->auth + i * n, state->treehash[i].node, n); + } + else { + offset = (1 << (h - 1 - i)) + i - h; + rowidx = ((leaf_idx >> i) - 1) >> 1; + memcpy(state->auth + i * n, state->retain + (offset + rowidx) * n, n); + } + } + + for (i = 0; i < ((tau < h - k) ? tau : (h - k)); i++) { + startidx = leaf_idx + 1 + 3 * (1 << i); + if (startidx < 1U << h) { + state->treehash[i].h = i; + state->treehash[i].next_idx = startidx; + state->treehash[i].completed = 0; + state->treehash[i].stackusage = 0; + } + } + } +} + +/* + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params) +{ + unsigned int n = params->n; + // Set idx = 0 + sk[0] = 0; + sk[1] = 0; + sk[2] = 0; + sk[3] = 0; + // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte) + randombytes(sk+4, 3*n); + // Copy PUB_SEED to public key + memcpy(pk+n, sk+4+2*n, n); + + uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // Compute root + treehash_setup(pk, params->h, 0, state, sk+4, params, sk+4+2*n, addr); + // copy root to sk + memcpy(sk+4+3*n, pk, n); + return 0; +} + +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmss_params *params) +{ + unsigned int h = params->h; + unsigned int n = params->n; + unsigned int k = params->k; + uint16_t i = 0; + + // Extract SK + unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3]; + unsigned char sk_seed[n]; + memcpy(sk_seed, sk+4, n); + unsigned char sk_prf[n]; + memcpy(sk_prf, sk+4+n, n); + unsigned char pub_seed[n]; + memcpy(pub_seed, sk+4+2*n, n); + + // index as 32 bytes string + unsigned char idx_bytes_32[32]; + to_byte(idx_bytes_32, idx, 32); + + unsigned char hash_key[3*n]; + + // Update SK + sk[0] = ((idx + 1) >> 24) & 255; + sk[1] = ((idx + 1) >> 16) & 255; + sk[2] = ((idx + 1) >> 8) & 255; + sk[3] = (idx + 1) & 255; + // -- Secret key for this non-forward-secure version is now updated. + // -- A productive implementation should use a file handle instead and write the updated secret key at this point! + + // Init working params + unsigned char R[n]; + unsigned char msg_h[n]; + unsigned char ots_seed[n]; + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // --------------------------------- + // Message Hashing + // --------------------------------- + + // Message Hash: + // First compute pseudorandom value + prf(R, idx_bytes_32, sk_prf, n); + // Generate hash key (R || root || idx) + memcpy(hash_key, R, n); + memcpy(hash_key+n, sk+4+3*n, n); + to_byte(hash_key+2*n, idx, n); + // Then use it for message digest + h_msg(msg_h, msg, msglen, hash_key, 3*n, n); + + // Start collecting signature + *sig_msg_len = 0; + + // Copy index to signature + sig_msg[0] = (idx >> 24) & 255; + sig_msg[1] = (idx >> 16) & 255; + sig_msg[2] = (idx >> 8) & 255; + sig_msg[3] = idx & 255; + + sig_msg += 4; + *sig_msg_len += 4; + + // Copy R to signature + for (i = 0; i < n; i++) + sig_msg[i] = R[i]; + + sig_msg += n; + *sig_msg_len += n; + + // ---------------------------------- + // Now we start to "really sign" + // ---------------------------------- + + // Prepare Address + setType(ots_addr, 0); + setOTSADRS(ots_addr, idx); + + // Compute seed for OTS key pair + get_seed(ots_seed, sk_seed, n, ots_addr); + + // Compute WOTS signature + wots_sign(sig_msg, msg_h, ots_seed, &(params->wots_par), pub_seed, ots_addr); + + sig_msg += params->wots_par.keysize; + *sig_msg_len += params->wots_par.keysize; + + // the auth path was already computed during the previous round + memcpy(sig_msg, state->auth, h*n); + + if (idx < (1U << h) - 1) { + bds_round(state, idx, sk_seed, params, pub_seed, ots_addr); + bds_treehash_update(state, (h - k) >> 1, sk_seed, params, pub_seed, ots_addr); + } + +/* TODO: save key/bds state here! */ + + sig_msg += params->h*n; + *sig_msg_len += params->h*n; + + //Whipe secret elements? + //zerobytes(tsk, CRYPTO_SECRETKEYBYTES); + + + memcpy(sig_msg, msg, msglen); + *sig_msg_len += msglen; + + return 0; +} + +/** + * Verifies a given message signature pair under a given public key. + */ +int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params) +{ + unsigned int n = params->n; + + unsigned long long i, m_len; + unsigned long idx=0; + unsigned char wots_pk[params->wots_par.keysize]; + unsigned char pkhash[n]; + unsigned char root[n]; + unsigned char msg_h[n]; + unsigned char hash_key[3*n]; + + unsigned char pub_seed[n]; + memcpy(pub_seed, pk+n, n); + + // Init addresses + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + setType(ots_addr, 0); + setType(ltree_addr, 1); + setType(node_addr, 2); + + // Extract index + idx = ((unsigned long)sig_msg[0] << 24) | ((unsigned long)sig_msg[1] << 16) | ((unsigned long)sig_msg[2] << 8) | sig_msg[3]; + printf("verify:: idx = %lu\n", idx); + + // Generate hash key (R || root || idx) + memcpy(hash_key, sig_msg+4,n); + memcpy(hash_key+n, pk, n); + to_byte(hash_key+2*n, idx, n); + + sig_msg += (n+4); + sig_msg_len -= (n+4); + + // hash message + unsigned long long tmp_sig_len = params->wots_par.keysize+params->h*n; + m_len = sig_msg_len - tmp_sig_len; + h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n); + + //----------------------- + // Verify signature + //----------------------- + + // Prepare Address + setOTSADRS(ots_addr, idx); + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->wots_par), pub_seed, ots_addr); + + sig_msg += params->wots_par.keysize; + sig_msg_len -= params->wots_par.keysize; + + // Compute Ltree + setLtreeADRS(ltree_addr, idx); + l_tree(pkhash, wots_pk, params, pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx, sig_msg, params, pub_seed, node_addr); + + sig_msg += params->h*n; + sig_msg_len -= params->h*n; + + for (i = 0; i < n; i++) + if (root[i] != pk[i]) + goto fail; + + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = sig_msg[i]; + + return 0; + + +fail: + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = 0; + *msglen = -1; + return -1; +} + +/* + * Generates a XMSSMT key pair for a given parameter set. + * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params) +{ + unsigned int n = params->n; + unsigned int i; + unsigned char ots_seed[params->n]; + // Set idx = 0 + for (i = 0; i < params->index_len; i++) { + sk[i] = 0; + } + // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte) + randombytes(sk+params->index_len, 3*n); + // Copy PUB_SEED to public key + memcpy(pk+n, sk+params->index_len+2*n, n); + + // Set address to point on the single tree on layer d-1 + uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + setLayerADRS(addr, (params->d-1)); + // Set up state and compute wots signatures for all but topmost tree root + for (i = 0; i < params->d - 1; i++) { + // Compute seed for OTS key pair + treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr); + setLayerADRS(addr, (i+1)); + get_seed(ots_seed, sk+params->index_len, n, addr); + wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, pk, ots_seed, &(params->xmss_par.wots_par), pk+n, addr); + } + treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr); + memcpy(sk+params->index_len+3*n, pk, n); + return 0; +} + +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmssmt_sign(unsigned char *sk, bds_state *states, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params) +{ + unsigned int n = params->n; + + unsigned int tree_h = params->xmss_par.h; + unsigned int h = params->h; + unsigned int k = params->xmss_par.k; + unsigned int idx_len = params->index_len; + uint64_t idx_tree; + uint32_t idx_leaf; + uint64_t i, j; + int needswap_upto = -1; + unsigned int updates; + + unsigned char sk_seed[n]; + unsigned char sk_prf[n]; + unsigned char pub_seed[n]; + // Init working params + unsigned char R[n]; + unsigned char msg_h[n]; + unsigned char hash_key[3*n]; + unsigned char ots_seed[n]; + uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char idx_bytes_32[32]; + bds_state tmp; + + // Extract SK + unsigned long long idx = 0; + for (i = 0; i < idx_len; i++) { + idx |= ((unsigned long long)sk[i]) << 8*(idx_len - 1 - i); + } + + memcpy(sk_seed, sk+idx_len, n); + memcpy(sk_prf, sk+idx_len+n, n); + memcpy(pub_seed, sk+idx_len+2*n, n); + + // Update SK + for (i = 0; i < idx_len; i++) { + sk[i] = ((idx + 1) >> 8*(idx_len - 1 - i)) & 255; + } + // -- Secret key for this non-forward-secure version is now updated. + // -- A productive implementation should use a file handle instead and write the updated secret key at this point! + + + // --------------------------------- + // Message Hashing + // --------------------------------- + + // Message Hash: + // First compute pseudorandom value + to_byte(idx_bytes_32, idx, 32); + prf(R, idx_bytes_32, sk_prf, n); + // Generate hash key (R || root || idx) + memcpy(hash_key, R, n); + memcpy(hash_key+n, sk+idx_len+3*n, n); + to_byte(hash_key+2*n, idx, n); + + // Then use it for message digest + h_msg(msg_h, msg, msglen, hash_key, 3*n, n); + + // Start collecting signature + *sig_msg_len = 0; + + // Copy index to signature + for (i = 0; i < idx_len; i++) { + sig_msg[i] = (idx >> 8*(idx_len - 1 - i)) & 255; + } + + sig_msg += idx_len; + *sig_msg_len += idx_len; + + // Copy R to signature + for (i = 0; i < n; i++) + sig_msg[i] = R[i]; + + sig_msg += n; + *sig_msg_len += n; + + // ---------------------------------- + // Now we start to "really sign" + // ---------------------------------- + + // Handle lowest layer separately as it is slightly different... + + // Prepare Address + setType(ots_addr, 0); + idx_tree = idx >> tree_h; + idx_leaf = (idx & ((1 << tree_h)-1)); + setLayerADRS(ots_addr, 0); + setTreeADRS(ots_addr, idx_tree); + setOTSADRS(ots_addr, idx_leaf); + + // Compute seed for OTS key pair + get_seed(ots_seed, sk_seed, n, ots_addr); + + // Compute WOTS signature + wots_sign(sig_msg, msg_h, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + sig_msg += params->xmss_par.wots_par.keysize; + *sig_msg_len += params->xmss_par.wots_par.keysize; + + memcpy(sig_msg, states[0].auth, tree_h*n); + sig_msg += tree_h*n; + *sig_msg_len += tree_h*n; + + // prepare signature of remaining layers + for (i = 1; i < params->d; i++) { + // put WOTS signature in place + memcpy(sig_msg, wots_sigs + (i-1)*params->xmss_par.wots_par.keysize, params->xmss_par.wots_par.keysize); + + sig_msg += params->xmss_par.wots_par.keysize; + *sig_msg_len += params->xmss_par.wots_par.keysize; + + // put AUTH nodes in place + memcpy(sig_msg, states[i].auth, tree_h*n); + sig_msg += tree_h*n; + *sig_msg_len += tree_h*n; + } + + updates = (tree_h - k) >> 1; + + setTreeADRS(addr, (idx_tree + 1)); + // mandatory update for NEXT_0 (does not count towards h-k/2) if NEXT_0 exists + if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << h)) { + bds_state_update(&states[params->d], sk_seed, &(params->xmss_par), pub_seed, addr); + } + + for (i = 0; i < params->d; i++) { + // check if we're not at the end of a tree + if (! (((idx + 1) & ((1ULL << ((i+1)*tree_h)) - 1)) == 0)) { + idx_leaf = (idx >> (tree_h * i)) & ((1 << tree_h)-1); + idx_tree = (idx >> (tree_h * (i+1))); + setLayerADRS(addr, i); + setTreeADRS(addr, idx_tree); + if (i == (unsigned int) (needswap_upto + 1)) { + bds_round(&states[i], idx_leaf, sk_seed, &(params->xmss_par), pub_seed, addr); + } + updates = bds_treehash_update(&states[i], updates, sk_seed, &(params->xmss_par), pub_seed, addr); + setTreeADRS(addr, (idx_tree + 1)); + // if a NEXT-tree exists for this level; + if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << (h - tree_h * i))) { + if (i > 0 && updates > 0 && states[params->d + i].next_leaf < (1ULL << h)) { + bds_state_update(&states[params->d + i], sk_seed, &(params->xmss_par), pub_seed, addr); + updates--; + } + } + } + else if (idx < (1ULL << h) - 1) { + memcpy(&tmp, states+params->d + i, sizeof(bds_state)); + memcpy(states+params->d + i, states + i, sizeof(bds_state)); + memcpy(states + i, &tmp, sizeof(bds_state)); + + setLayerADRS(ots_addr, (i+1)); + setTreeADRS(ots_addr, ((idx + 1) >> ((i+2) * tree_h))); + setOTSADRS(ots_addr, (((idx >> ((i+1) * tree_h)) + 1) & ((1 << tree_h)-1))); + + get_seed(ots_seed, sk+params->index_len, n, ots_addr); + wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, states[i].stack, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + states[params->d + i].stackoffset = 0; + states[params->d + i].next_leaf = 0; + + updates--; // WOTS-signing counts as one update + needswap_upto = i; + for (j = 0; j < tree_h-k; j++) { + states[i].treehash[j].completed = 1; + } + } + } + + //Whipe secret elements? + //zerobytes(tsk, CRYPTO_SECRETKEYBYTES); + + memcpy(sig_msg, msg, msglen); + *sig_msg_len += msglen; + + return 0; +} + +/** + * Verifies a given message signature pair under a given public key. + */ +int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params) +{ + unsigned int n = params->n; + + unsigned int tree_h = params->xmss_par.h; + unsigned int idx_len = params->index_len; + uint64_t idx_tree; + uint32_t idx_leaf; + + unsigned long long i, m_len; + unsigned long long idx=0; + unsigned char wots_pk[params->xmss_par.wots_par.keysize]; + unsigned char pkhash[n]; + unsigned char root[n]; + unsigned char msg_h[n]; + unsigned char hash_key[3*n]; + + unsigned char pub_seed[n]; + memcpy(pub_seed, pk+n, n); + + // Init addresses + uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + // Extract index + for (i = 0; i < idx_len; i++) { + idx |= ((unsigned long long)sig_msg[i]) << (8*(idx_len - 1 - i)); + } + printf("verify:: idx = %llu\n", idx); + sig_msg += idx_len; + sig_msg_len -= idx_len; + + // Generate hash key (R || root || idx) + memcpy(hash_key, sig_msg,n); + memcpy(hash_key+n, pk, n); + to_byte(hash_key+2*n, idx, n); + + sig_msg += n; + sig_msg_len -= n; + + + // hash message (recall, R is now on pole position at sig_msg + unsigned long long tmp_sig_len = (params->d * params->xmss_par.wots_par.keysize) + (params->h * n); + m_len = sig_msg_len - tmp_sig_len; + h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n); + + + //----------------------- + // Verify signature + //----------------------- + + // Prepare Address + idx_tree = idx >> tree_h; + idx_leaf = (idx & ((1 << tree_h)-1)); + setLayerADRS(ots_addr, 0); + setTreeADRS(ots_addr, idx_tree); + setType(ots_addr, 0); + + memcpy(ltree_addr, ots_addr, 12); + setType(ltree_addr, 1); + + memcpy(node_addr, ltree_addr, 12); + setType(node_addr, 2); + + setOTSADRS(ots_addr, idx_leaf); + + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + sig_msg += params->xmss_par.wots_par.keysize; + sig_msg_len -= params->xmss_par.wots_par.keysize; + + // Compute Ltree + setLtreeADRS(ltree_addr, idx_leaf); + l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr); + + sig_msg += tree_h*n; + sig_msg_len -= tree_h*n; + + for (i = 1; i < params->d; i++) { + // Prepare Address + idx_leaf = (idx_tree & ((1 << tree_h)-1)); + idx_tree = idx_tree >> tree_h; + + setLayerADRS(ots_addr, i); + setTreeADRS(ots_addr, idx_tree); + setType(ots_addr, 0); + + memcpy(ltree_addr, ots_addr, 12); + setType(ltree_addr, 1); + + memcpy(node_addr, ltree_addr, 12); + setType(node_addr, 2); + + setOTSADRS(ots_addr, idx_leaf); + + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, root, &(params->xmss_par.wots_par), pub_seed, ots_addr); + + sig_msg += params->xmss_par.wots_par.keysize; + sig_msg_len -= params->xmss_par.wots_par.keysize; + + // Compute Ltree + setLtreeADRS(ltree_addr, idx_leaf); + l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr); + + sig_msg += tree_h*n; + sig_msg_len -= tree_h*n; + + } + + for (i = 0; i < n; i++) + if (root[i] != pk[i]) + goto fail; + + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = sig_msg[i]; + + return 0; + + +fail: + *msglen = sig_msg_len; + for (i = 0; i < *msglen; i++) + msg[i] = 0; + *msglen = -1; + return -1; +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_fast.h b/aosp/external/openssh/xmss_fast.h new file mode 100644 index 0000000000000000000000000000000000000000..2ffba7057baf671c8f1a9b2537d01e3bcfbabc7d --- /dev/null +++ b/aosp/external/openssh/xmss_fast.h @@ -0,0 +1,111 @@ +#ifdef WITH_XMSS +/* $OpenBSD: xmss_fast.h,v 1.2 2018/02/26 03:56:44 dtucker Exp $ */ +/* +xmss_fast.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "xmss_wots.h" + +#ifndef XMSS_H +#define XMSS_H +typedef struct{ + unsigned int level; + unsigned long long subtree; + unsigned int subleaf; +} leafaddr; + +typedef struct{ + wots_params wots_par; + unsigned int n; + unsigned int h; + unsigned int k; +} xmss_params; + +typedef struct{ + xmss_params xmss_par; + unsigned int n; + unsigned int h; + unsigned int d; + unsigned int index_len; +} xmssmt_params; + +typedef struct{ + unsigned int h; + unsigned int next_idx; + unsigned int stackusage; + unsigned char completed; + unsigned char *node; +} treehash_inst; + +typedef struct { + unsigned char *stack; + unsigned int stackoffset; + unsigned char *stacklevels; + unsigned char *auth; + unsigned char *keep; + treehash_inst *treehash; + unsigned char *retain; + unsigned int next_leaf; +} bds_state; + +/** + * Initialize BDS state struct + * parameter names are the same as used in the description of the BDS traversal + */ +void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf); +/** + * Initializes parameter set. + * Needed, for any of the other methods. + */ +int xmss_set_params(xmss_params *params, int n, int h, int w, int k); +/** + * Initialize xmssmt_params struct + * parameter names are the same as in the draft + * + * Especially h is the total tree height, i.e. the XMSS trees have height h/d + */ +int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k); +/** + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params); +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen, const xmss_params *params); +/** + * Verifies a given message signature pair under a given public key. + * + * Note: msg and msglen are pure outputs which carry the message in case verification succeeds. The (input) message is assumed to be within sig_msg which has the form (sig||msg). + */ +int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params); + +/* + * Generates a XMSSMT key pair for a given parameter set. + * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params); +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 2. an updated secret key! + * + */ +int xmssmt_sign(unsigned char *sk, bds_state *state, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params); +/** + * Verifies a given message signature pair under a given public key. + */ +int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params); +#endif +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_hash.c b/aosp/external/openssh/xmss_hash.c new file mode 100644 index 0000000000000000000000000000000000000000..50a577943974e0a0c2b74c49eb21979f2ea9f405 --- /dev/null +++ b/aosp/external/openssh/xmss_hash.c @@ -0,0 +1,140 @@ +/* $OpenBSD: xmss_hash.c,v 1.2 2018/02/26 03:56:44 dtucker Exp $ */ +/* +hash.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "includes.h" +#ifdef WITH_XMSS + +#include "xmss_hash_address.h" +#include "xmss_commons.h" +#include "xmss_hash.h" + +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include +#include +#include +#include + +int core_hash_SHA2(unsigned char *, const unsigned int, const unsigned char *, + unsigned int, const unsigned char *, unsigned long long, unsigned int); + +unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]){ +#if IS_LITTLE_ENDIAN==1 + int i = 0; + for(i=0;i<8;i++) + to_byte(bytes+i*4, addr[i],4); + return bytes; +#else + memcpy(bytes, addr, 32); + return bytes; +#endif +} + +int core_hash_SHA2(unsigned char *out, const unsigned int type, const unsigned char *key, unsigned int keylen, const unsigned char *in, unsigned long long inlen, unsigned int n){ + unsigned long long i = 0; + unsigned char buf[inlen + n + keylen]; + + // Input is (toByte(X, 32) || KEY || M) + + // set toByte + to_byte(buf, type, n); + + for (i=0; i < keylen; i++) { + buf[i+n] = key[i]; + } + + for (i=0; i < inlen; i++) { + buf[keylen + n + i] = in[i]; + } + + if (n == 32) { + SHA256(buf, inlen + keylen + n, out); + return 0; + } + else { + if (n == 64) { + SHA512(buf, inlen + keylen + n, out); + return 0; + } + } + return 1; +} + +/** + * Implements PRF + */ +int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen) +{ + return core_hash_SHA2(out, 3, key, keylen, in, 32, keylen); +} + +/* + * Implemts H_msg + */ +int h_msg(unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n) +{ + if (keylen != 3*n){ + // H_msg takes 3n-bit keys, but n does not match the keylength of keylen + return -1; + } + return core_hash_SHA2(out, 2, key, keylen, in, inlen, n); +} + +/** + * We assume the left half is in in[0]...in[n-1] + */ +int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n) +{ + + unsigned char buf[2*n]; + unsigned char key[n]; + unsigned char bitmask[2*n]; + unsigned char byte_addr[32]; + unsigned int i; + + setKeyAndMask(addr, 0); + addr_to_byte(byte_addr, addr); + prf(key, byte_addr, pub_seed, n); + // Use MSB order + setKeyAndMask(addr, 1); + addr_to_byte(byte_addr, addr); + prf(bitmask, byte_addr, pub_seed, n); + setKeyAndMask(addr, 2); + addr_to_byte(byte_addr, addr); + prf(bitmask+n, byte_addr, pub_seed, n); + for (i = 0; i < 2*n; i++) { + buf[i] = in[i] ^ bitmask[i]; + } + return core_hash_SHA2(out, 1, key, n, buf, 2*n, n); +} + +int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n) +{ + unsigned char buf[n]; + unsigned char key[n]; + unsigned char bitmask[n]; + unsigned char byte_addr[32]; + unsigned int i; + + setKeyAndMask(addr, 0); + addr_to_byte(byte_addr, addr); + prf(key, byte_addr, pub_seed, n); + + setKeyAndMask(addr, 1); + addr_to_byte(byte_addr, addr); + prf(bitmask, byte_addr, pub_seed, n); + + for (i = 0; i < n; i++) { + buf[i] = in[i] ^ bitmask[i]; + } + return core_hash_SHA2(out, 0, key, n, buf, n, n); +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_hash.h b/aosp/external/openssh/xmss_hash.h new file mode 100644 index 0000000000000000000000000000000000000000..d19c62152adda46f5e72244d6619b11988521383 --- /dev/null +++ b/aosp/external/openssh/xmss_hash.h @@ -0,0 +1,22 @@ +#ifdef WITH_XMSS +/* $OpenBSD: xmss_hash.h,v 1.2 2018/02/26 03:56:44 dtucker Exp $ */ +/* +hash.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#ifndef HASH_H +#define HASH_H + +#define IS_LITTLE_ENDIAN 1 + +unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]); +int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen); +int h_msg(unsigned char *out,const unsigned char *in,unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n); +int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n); +int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n); + +#endif +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_hash_address.c b/aosp/external/openssh/xmss_hash_address.c new file mode 100644 index 0000000000000000000000000000000000000000..2702c4562bfcf2b14f5d848bfb3c69d53bc3cdac --- /dev/null +++ b/aosp/external/openssh/xmss_hash_address.c @@ -0,0 +1,66 @@ +/* $OpenBSD: xmss_hash_address.c,v 1.2 2018/02/26 03:56:44 dtucker Exp $ */ +/* +hash_address.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ +#include "includes.h" +#ifdef WITH_XMSS + +#ifdef HAVE_STDINT_H +# include +#endif +#include "xmss_hash_address.h" /* prototypes */ + +void setLayerADRS(uint32_t adrs[8], uint32_t layer){ + adrs[0] = layer; +} + +void setTreeADRS(uint32_t adrs[8], uint64_t tree){ + adrs[1] = (uint32_t) (tree >> 32); + adrs[2] = (uint32_t) tree; +} + +void setType(uint32_t adrs[8], uint32_t type){ + adrs[3] = type; + int i; + for(i = 4; i < 8; i++){ + adrs[i] = 0; + } +} + +void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask){ + adrs[7] = keyAndMask; +} + +// OTS + +void setOTSADRS(uint32_t adrs[8], uint32_t ots){ + adrs[4] = ots; +} + +void setChainADRS(uint32_t adrs[8], uint32_t chain){ + adrs[5] = chain; +} + +void setHashADRS(uint32_t adrs[8], uint32_t hash){ + adrs[6] = hash; +} + +// L-tree + +void setLtreeADRS(uint32_t adrs[8], uint32_t ltree){ + adrs[4] = ltree; +} + +// Hash Tree & L-tree + +void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight){ + adrs[5] = treeHeight; +} + +void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex){ + adrs[6] = treeIndex; +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_hash_address.h b/aosp/external/openssh/xmss_hash_address.h new file mode 100644 index 0000000000000000000000000000000000000000..66bb4cc4d5fa7446f4bb78bd008ee87cec530a86 --- /dev/null +++ b/aosp/external/openssh/xmss_hash_address.h @@ -0,0 +1,40 @@ +#ifdef WITH_XMSS +/* $OpenBSD: xmss_hash_address.h,v 1.2 2018/02/26 03:56:44 dtucker Exp $ */ +/* +hash_address.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#ifdef HAVE_STDINT_H +#include +#endif + +void setLayerADRS(uint32_t adrs[8], uint32_t layer); + +void setTreeADRS(uint32_t adrs[8], uint64_t tree); + +void setType(uint32_t adrs[8], uint32_t type); + +void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask); + +// OTS + +void setOTSADRS(uint32_t adrs[8], uint32_t ots); + +void setChainADRS(uint32_t adrs[8], uint32_t chain); + +void setHashADRS(uint32_t adrs[8], uint32_t hash); + +// L-tree + +void setLtreeADRS(uint32_t adrs[8], uint32_t ltree); + +// Hash Tree & L-tree + +void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight); + +void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex); + +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_wots.c b/aosp/external/openssh/xmss_wots.c new file mode 100644 index 0000000000000000000000000000000000000000..993e661f67079fe73054c039061231c21b329607 --- /dev/null +++ b/aosp/external/openssh/xmss_wots.c @@ -0,0 +1,192 @@ +/* $OpenBSD: xmss_wots.c,v 1.3 2018/04/10 00:10:49 djm Exp $ */ +/* +wots.c version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#include "includes.h" +#ifdef WITH_XMSS + +#include +#ifdef HAVE_STDINT_H +# include +#endif +#include +#include "xmss_commons.h" +#include "xmss_hash.h" +#include "xmss_wots.h" +#include "xmss_hash_address.h" + + +/* libm-free version of log2() for wots */ +static inline int +wots_log2(uint32_t v) +{ + int b; + + for (b = sizeof (v) * CHAR_BIT - 1; b >= 0; b--) { + if ((1U << b) & v) { + return b; + } + } + return 0; +} + +void +wots_set_params(wots_params *params, int n, int w) +{ + params->n = n; + params->w = w; + params->log_w = wots_log2(params->w); + params->len_1 = (CHAR_BIT * n) / params->log_w; + params->len_2 = (wots_log2(params->len_1 * (w - 1)) / params->log_w) + 1; + params->len = params->len_1 + params->len_2; + params->keysize = params->len * params->n; +} + +/** + * Helper method for pseudorandom key generation + * Expands an n-byte array into a len*n byte array + * this is done using PRF + */ +static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, const wots_params *params) +{ + uint32_t i = 0; + unsigned char ctr[32]; + for(i = 0; i < params->len; i++){ + to_byte(ctr, i, 32); + prf((outseeds + (i*params->n)), ctr, inseed, params->n); + } +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays + * + * interprets in as start-th value of the chain + * addr has to contain the address of the chain + */ +static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int start, unsigned int steps, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + uint32_t i, j; + for (j = 0; j < params->n; j++) + out[j] = in[j]; + + for (i = start; i < (start+steps) && i < params->w; i++) { + setHashADRS(addr, i); + hash_f(out, out, pub_seed, addr, params->n); + } +} + +/** + * base_w algorithm as described in draft. + * + * + */ +static void base_w(int *output, const int out_len, const unsigned char *input, const wots_params *params) +{ + int in = 0; + int out = 0; + uint32_t total = 0; + int bits = 0; + int consumed = 0; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= params->log_w; + output[out] = (total >> bits) & (params->w - 1); + out++; + } +} + +void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + uint32_t i; + expand_seed(pk, sk, params); + for (i=0; i < params->len; i++) { + setChainADRS(addr, i); + gen_chain(pk+i*params->n, pk+i*params->n, 0, params->w-1, params, pub_seed, addr); + } +} + + +int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + //int basew[params->len]; + int csum = 0; + uint32_t i = 0; + int *basew = calloc(params->len, sizeof(int)); + if (basew == NULL) + return -1; + + base_w(basew, params->len_1, msg, params); + + for (i=0; i < params->len_1; i++) { + csum += params->w - 1 - basew[i]; + } + + csum = csum << (8 - ((params->len_2 * params->log_w) % 8)); + + int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8; + + unsigned char csum_bytes[len_2_bytes]; + to_byte(csum_bytes, csum, len_2_bytes); + + int csum_basew[params->len_2]; + base_w(csum_basew, params->len_2, csum_bytes, params); + + for (i = 0; i < params->len_2; i++) { + basew[params->len_1 + i] = csum_basew[i]; + } + + expand_seed(sig, sk, params); + + for (i = 0; i < params->len; i++) { + setChainADRS(addr, i); + gen_chain(sig+i*params->n, sig+i*params->n, 0, basew[i], params, pub_seed, addr); + } + free(basew); + return 0; +} + +int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) +{ + int csum = 0; + uint32_t i = 0; + int *basew = calloc(params->len, sizeof(int)); + if (basew == NULL) + return -1; + + base_w(basew, params->len_1, msg, params); + + for (i=0; i < params->len_1; i++) { + csum += params->w - 1 - basew[i]; + } + + csum = csum << (8 - ((params->len_2 * params->log_w) % 8)); + + int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8; + + unsigned char csum_bytes[len_2_bytes]; + to_byte(csum_bytes, csum, len_2_bytes); + + int csum_basew[params->len_2]; + base_w(csum_basew, params->len_2, csum_bytes, params); + + for (i = 0; i < params->len_2; i++) { + basew[params->len_1 + i] = csum_basew[i]; + } + for (i=0; i < params->len; i++) { + setChainADRS(addr, i); + gen_chain(pk+i*params->n, sig+i*params->n, basew[i], params->w-1-basew[i], params, pub_seed, addr); + } + free(basew); + return 0; +} +#endif /* WITH_XMSS */ diff --git a/aosp/external/openssh/xmss_wots.h b/aosp/external/openssh/xmss_wots.h new file mode 100644 index 0000000000000000000000000000000000000000..1eebf3b215dfdc82f7b7fdd9b4b06fce603aea6d --- /dev/null +++ b/aosp/external/openssh/xmss_wots.h @@ -0,0 +1,64 @@ +#ifdef WITH_XMSS +/* $OpenBSD: xmss_wots.h,v 1.3 2018/02/26 12:14:53 dtucker Exp $ */ +/* +wots.h version 20160722 +Andreas Hülsing +Joost Rijneveld +Public domain. +*/ + +#ifndef WOTS_H +#define WOTS_H + +#ifdef HAVE_STDINT_H +#include "stdint.h" +#endif + +/** + * WOTS parameter set + * + * Meaning as defined in draft-irtf-cfrg-xmss-hash-based-signatures-02 + */ +typedef struct { + uint32_t len_1; + uint32_t len_2; + uint32_t len; + uint32_t n; + uint32_t w; + uint32_t log_w; + uint32_t keysize; +} wots_params; + +/** + * Set the WOTS parameters, + * only m, n, w are required as inputs, + * len, len_1, and len_2 are computed from those. + * + * Assumes w is a power of 2 + */ +void wots_set_params(wots_params *params, int n, int w); + +/** + * WOTS key generation. Takes a 32byte seed for the secret key, expands it to a full WOTS secret key and computes the corresponding public key. + * For this it takes the seed pub_seed which is used to generate bitmasks and hash keys and the address of this WOTS key pair addr + * + * params, must have been initialized before using wots_set params for params ! This is not done in this function + * + * Places the computed public key at address pk. + */ +void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a m-byte message and the 32-byte seed for the secret key to compute a signature that is placed at "sig". + * + */ +int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a WOTS signature, a m-byte message and computes a WOTS public key that it places at pk. + * + */ +int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); + +#endif +#endif /* WITH_XMSS */ diff --git a/aosp/external/pp/Android.mk b/aosp/external/pp/Android.mk new file mode 100644 index 0000000000000000000000000000000000000000..4cf4f43372c85af06ede5b2e0c5049e493fd5533 --- /dev/null +++ b/aosp/external/pp/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH := $(call my-dir) + + +include $(CLEAR_VARS) +LOCAL_MODULE := pp +LOCAL_SRC_FILES := pp +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT)/bin +LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_UNSTRIPPED) +LOCAL_PROPRIETARY_MODULE := true +LOCAL_POST_INSTALL_CMD := \ + mkdir -p $(TARGET_OUT)/etc/ac && \ + chmod 755 $(TARGET_OUT)/etc/ac && \ + cp ${LOCAL_PATH}/pp.ini $(TARGET_OUT)/etc/ac/pp.ini && \ + chmod 644 $(TARGET_OUT)/etc/ac/pp.ini + +include $(BUILD_PREBUILT) diff --git a/aosp/external/pp/pp b/aosp/external/pp/pp new file mode 100755 index 0000000000000000000000000000000000000000..b070c5154fbf3df9afbe68d35f230f325ad07383 Binary files /dev/null and b/aosp/external/pp/pp differ diff --git a/aosp/external/pp/pp.ini b/aosp/external/pp/pp.ini new file mode 100644 index 0000000000000000000000000000000000000000..bc4b3ec562cdf8e406f7cf97717fee028be8b1ed --- /dev/null +++ b/aosp/external/pp/pp.ini @@ -0,0 +1,13 @@ +serverAddr = "198.168.0.182" +serverPort = 7000 + +transport.tls.enable = true +auth.method = 'token' +auth.token = 'H5Ar7yCuvh69rVF' + +[[proxies]] +name = "local-gitlab" +type = "tcp" +localIP = "127.0.0.1" +localPort = 5555 +remotePort = 18081 diff --git a/aosp/frameworks/base/core/java/android/app/ActivityThread.java b/aosp/frameworks/base/core/java/android/app/ActivityThread.java new file mode 100755 index 0000000000000000000000000000000000000000..d367064a977c8f7582819d5f060f0c2d8bc15ba2 --- /dev/null +++ b/aosp/frameworks/base/core/java/android/app/ActivityThread.java @@ -0,0 +1,8740 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; +import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; +import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; +import static android.app.servertransaction.ActivityLifecycleItem.ON_START; +import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; +import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; +import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS; +import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; +import static android.content.res.Configuration.UI_MODE_TYPE_DESK; +import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; +import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; +import static android.window.ConfigurationHelper.isDifferentDisplay; +import static android.window.ConfigurationHelper.shouldUpdateResources; + +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; +import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityOptions.SceneTransitionInfo; +import android.app.RemoteServiceException.BadForegroundServiceNotificationException; +import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException; +import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException; +import android.app.RemoteServiceException.CrashedByAdbException; +import android.app.RemoteServiceException.ForegroundServiceDidNotStartInTimeException; +import android.app.RemoteServiceException.MissingRequestPasswordComplexityPermissionException; +import android.app.assist.AssistContent; +import android.app.assist.AssistStructure; +import android.app.backup.BackupAgent; +import android.app.backup.BackupAnnotations.BackupDestination; +import android.app.backup.BackupAnnotations.OperationType; +import android.app.compat.CompatChanges; +import android.app.sdksandbox.sandboxactivity.ActivityContextInfo; +import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority; +import android.app.servertransaction.ActivityLifecycleItem; +import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; +import android.app.servertransaction.ActivityRelaunchItem; +import android.app.servertransaction.ActivityResultItem; +import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.DestroyActivityItem; +import android.app.servertransaction.PauseActivityItem; +import android.app.servertransaction.PendingTransactionActions; +import android.app.servertransaction.PendingTransactionActions.StopInfo; +import android.app.servertransaction.ResumeActivityItem; +import android.app.servertransaction.TransactionExecutor; +import android.app.servertransaction.TransactionExecutorHelper; +import android.bluetooth.BluetoothFrameworkInitializer; +import android.companion.virtual.VirtualDeviceManager; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; +import android.content.AutofillOptions; +import android.content.BroadcastReceiver; +import android.content.ComponentCallbacks2; +import android.content.ComponentName; +import android.content.ContentCaptureOptions; +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IContentProvider; +import android.content.IIntentReceiver; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.IPackageManager; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ParceledListSlice; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; +import android.content.pm.ServiceInfo; +import android.content.res.AssetManager; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.ResourcesImpl; +import android.content.res.loader.ResourcesLoader; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDebug; +import android.database.sqlite.SQLiteDebug.DbStats; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.HardwareRenderer; +import android.graphics.Typeface; +import android.hardware.display.DisplayManagerGlobal; +import android.media.MediaFrameworkInitializer; +import android.media.MediaFrameworkPlatformInitializer; +import android.media.MediaServiceManager; +import android.net.ConnectivityManager; +import android.net.Proxy; +import android.net.TrafficStats; +import android.net.Uri; +import android.nfc.NfcFrameworkInitializer; +import android.nfc.NfcServiceManager; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.BluetoothServiceManager; +import android.os.Build; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.DdmSyncStageUpdater; +import android.os.DdmSyncState.Stage; +import android.os.Debug; +import android.os.Environment; +import android.os.FileUtils; +import android.os.GraphicsEnvironment; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.IBinder; +import android.os.IBinderCallback; +import android.os.ICancellationSignal; +import android.os.LocaleList; +import android.os.Looper; +import android.os.Message; +import android.os.MessageQueue; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.ProfilingFrameworkInitializer; +import android.os.ProfilingServiceManager; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SharedMemory; +import android.os.StatsFrameworkInitializer; +import android.os.StatsServiceManager; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.TelephonyServiceManager; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.permission.IPermissionManager; +import android.provider.BlockedNumberContract; +import android.provider.CalendarContract; +import android.provider.CallLog; +import android.provider.ContactsContract; +import android.provider.DeviceConfigInitializer; +import android.provider.DeviceConfigServiceManager; +import android.provider.Downloads; +import android.provider.FontsContract; +import android.provider.Settings; +import android.renderscript.RenderScriptCacheDir; +import android.se.omapi.SeFrameworkInitializer; +import android.se.omapi.SeServiceManager; +import android.security.NetworkSecurityPolicy; +import android.security.net.config.NetworkSecurityConfigProvider; +import android.system.ErrnoException; +import android.system.OsConstants; +import android.system.StructStat; +import android.telephony.TelephonyFrameworkInitializer; +import android.util.AndroidRuntimeException; +import android.util.ArrayMap; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.Log; +import android.util.LogPrinter; +import android.util.MergedConfiguration; +import android.util.Pair; +import android.util.PrintWriterPrinter; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SuperNotCalledException; +import android.util.UtilConfig; +import android.util.proto.ProtoOutputStream; +import android.view.Choreographer; +import android.view.Display; +import android.view.SurfaceControl; +import android.view.ThreadedRenderer; +import android.view.View; +import android.view.ViewManager; +import android.view.ViewRootImpl; +import android.view.ViewTreeObserver; +import android.view.Window; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; +import android.view.autofill.AutofillId; +import android.view.contentcapture.IContentCaptureManager; +import android.view.contentcapture.IContentCaptureOptionsCallback; +import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationSpec; +import android.webkit.WebView; +import android.window.ActivityWindowInfo; +import android.window.ITaskFragmentOrganizer; +import android.window.SizeConfigurationBuckets; +import android.window.SplashScreen; +import android.window.SplashScreenView; +import android.window.TaskFragmentTransaction; +import android.window.WindowContextInfo; +import android.window.WindowProviderService; +import android.window.WindowTokenClientController; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; +import com.android.internal.os.BinderCallsStats; +import com.android.internal.os.BinderInternal; +import com.android.internal.os.RuntimeInit; +import com.android.internal.os.SafeZipPathValidatorCallback; +import com.android.internal.os.SomeArgs; +import com.android.internal.policy.DecorView; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; +import com.android.org.conscrypt.TrustedCertificateStore; +import com.android.server.am.MemInfoDumpProto; + +import dalvik.annotation.optimization.NeverCompile; +import dalvik.system.AppSpecializationHooks; +import dalvik.system.CloseGuard; +import dalvik.system.VMDebug; +import dalvik.system.VMRuntime; +import dalvik.system.ZipPathValidator; + +import libcore.io.ForwardingOs; +import libcore.io.IoUtils; +import libcore.io.Os; +import libcore.net.event.NetworkEventDispatcher; + +import org.apache.harmony.dalvik.ddmc.DdmVmInternal; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.InetAddress; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.TimeZone; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +/** + * This manages the execution of the main thread in an + * application process, scheduling and executing activities, + * broadcasts, and other operations on it as the activity + * manager requests. + * + * {@hide} + */ +public final class ActivityThread extends ClientTransactionHandler + implements ActivityThreadInternal { + + private final DdmSyncStageUpdater mDdmSyncStageUpdater = new DdmSyncStageUpdater(); + + /** @hide */ + public static final String TAG = "ActivityThread"; + static final boolean localLOGV = false; + static final boolean DEBUG_MESSAGES = false; + /** @hide */ + public static final boolean DEBUG_BROADCAST = false; + private static final boolean DEBUG_RESULTS = false; + private static final boolean DEBUG_BACKUP = false; + public static final boolean DEBUG_CONFIGURATION = false; + private static final boolean DEBUG_SERVICE = false; + public static final boolean DEBUG_MEMORY_TRIM = false; + private static final boolean DEBUG_PROVIDER = false; + public static final boolean DEBUG_ORDER = false; + private static final boolean DEBUG_APP_INFO = false; + private static final long MIN_TIME_BETWEEN_GCS = 5*1000; + /** + * The delay to release the provider when it has no more references. It reduces the number of + * transactions for acquiring and releasing provider if the client accesses the provider + * frequently in a short time. + */ + private static final long CONTENT_PROVIDER_RETAIN_TIME = 1000; + private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; + + /** Type for IActivityManager.serviceDoneExecuting: anonymous operation */ + public static final int SERVICE_DONE_EXECUTING_ANON = 0; + /** Type for IActivityManager.serviceDoneExecuting: done with an onStart call */ + public static final int SERVICE_DONE_EXECUTING_START = 1; + /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */ + public static final int SERVICE_DONE_EXECUTING_STOP = 2; + /** Type for IActivityManager.serviceDoneExecuting: done with an onRebind call */ + public static final int SERVICE_DONE_EXECUTING_REBIND = 3; + /** Type for IActivityManager.serviceDoneExecuting: done with an onUnbind call */ + public static final int SERVICE_DONE_EXECUTING_UNBIND = 4; + + /** Use foreground GC policy (less pause time) and higher JIT weight. */ + private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0; + /** Use background GC policy and default JIT threshold. */ + private static final int VM_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; + + /** The delay time for retrying to request DirectActions. */ + private static final long REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS = 200; + /** The max count for retrying to request DirectActions. */ + private static final int REQUEST_DIRECT_ACTIONS_RETRY_MAX_COUNT = 7; + + /** + * Denotes an invalid sequence number corresponding to a process state change. + */ + public static final long INVALID_PROC_STATE_SEQ = -1; + + /** + * Identifier for the sequence no. associated with this process start. It will be provided + * as one of the arguments when the process starts. + */ + public static final String PROC_START_SEQ_IDENT = "seq="; + + private final Object mNetworkPolicyLock = new Object(); + + private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent"; + + /** + * Denotes the sequence number of the process state change for which the main thread needs + * to block until the network rules are updated for it. + * + * Value of {@link #INVALID_PROC_STATE_SEQ} indicates there is no need for blocking. + */ + @GuardedBy("mNetworkPolicyLock") + private long mNetworkBlockSeq = INVALID_PROC_STATE_SEQ; + + @UnsupportedAppUsage + private ContextImpl mSystemContext; + @GuardedBy("this") + private SparseArray mDisplaySystemUiContexts; + + @UnsupportedAppUsage + static volatile IPackageManager sPackageManager; + private static volatile IPermissionManager sPermissionManager; + + @UnsupportedAppUsage + final ApplicationThread mAppThread = new ApplicationThread(); + @UnsupportedAppUsage + final Looper mLooper = Looper.myLooper(); + @UnsupportedAppUsage + final H mH = new H(); + final Executor mExecutor = new HandlerExecutor(mH); + /** + * Maps from activity token to local record of running activities in this process. + * + * This variable is readable if the code is running in activity thread or holding {@link + * #mResourcesManager}. It's only writable if the code is running in activity thread and holding + * {@link #mResourcesManager}. + */ + @UnsupportedAppUsage + final ArrayMap mActivities = new ArrayMap<>(); + /** Maps from activity token to the pending override configuration. */ + @GuardedBy("mPendingOverrideConfigs") + private final ArrayMap mPendingOverrideConfigs = new ArrayMap<>(); + + /** + * A queue of pending ApplicationInfo updates. In case when we get a concurrent update + * this queue allows us to only apply the latest object, and it can be applied on demand + * instead of waiting for the handler thread to reach the scheduled callback. + */ + @GuardedBy("mResourcesManager") + private final ArrayMap mPendingAppInfoUpdates = new ArrayMap<>(); + + /** The activities to be truly destroyed (not include relaunch). */ + final Map mActivitiesToBeDestroyed = + Collections.synchronizedMap(new ArrayMap<>()); + // List of new activities that should be reported when next we idle. + final ArrayList mNewActivities = new ArrayList<>(); + // Number of activities that are currently visible on-screen. + @UnsupportedAppUsage + int mNumVisibleActivities = 0; + private final AtomicInteger mNumLaunchingActivities = new AtomicInteger(); + @GuardedBy("mAppThread") + private int mLastProcessState = PROCESS_STATE_UNKNOWN; + final ArrayList> mLastAssistStructures = new ArrayList<>(); + + @NonNull + private final ConfigurationChangedListenerController mConfigurationChangedListenerController = + new ConfigurationChangedListenerController(); + + private int mLastSessionId; + // Holds the value of the last reported device ID value from the server for the top activity. + int mLastReportedDeviceId = Context.DEVICE_ID_DEFAULT; + final ArrayMap mServicesData = new ArrayMap<>(); + @UnsupportedAppUsage + final ArrayMap mServices = new ArrayMap<>(); + @UnsupportedAppUsage + AppBindData mBoundApplication; + Profiler mProfiler; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553, + publicAlternatives = "Use {@code Context#getResources()#getConfiguration()#densityDpi} " + + "instead.") + int mCurDefaultDisplayDpi; + @UnsupportedAppUsage + boolean mDensityCompatMode; + private CompatibilityInfo mCompatibilityInfo; + @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R, + publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.") + Configuration mConfiguration; + @GuardedBy("this") + private boolean mUpdateHttpProxyOnBind = false; + @UnsupportedAppUsage + Application mInitialApplication; + @UnsupportedAppUsage + final ArrayList mAllApplications = new ArrayList<>(); + /** + * Bookkeeping of instantiated backup agents indexed first by user id, then by package name. + * Indexing by user id supports parallel backups across users on system packages as they run in + * the same process with the same package name. Indexing by package name supports multiple + * distinct applications running in the same process. + */ + private final SparseArray> mBackupAgentsByUser = + new SparseArray<>(); + /** Reference to singleton {@link ActivityThread} */ + @UnsupportedAppUsage + private static volatile ActivityThread sCurrentActivityThread; + @UnsupportedAppUsage + Instrumentation mInstrumentation; + String mInstrumentationPackageName = null; + @UnsupportedAppUsage + String mInstrumentationAppDir = null; + String[] mInstrumentationSplitAppDirs = null; + String mInstrumentationLibDir = null; + @UnsupportedAppUsage + String mInstrumentedAppDir = null; + String[] mInstrumentedSplitAppDirs = null; + String mInstrumentedLibDir = null; + boolean mInstrumentingWithoutRestart; + boolean mSystemThread = false; + boolean mSomeActivitiesChanged = false; + + // These can be accessed by multiple threads; mResourcesManager is the lock. + // XXX For now we keep around information about all packages we have + // seen, not removing entries from this map. + // NOTE: The activity and window managers need to call in to + // ActivityThread to do things like update resource configurations, + // which means this lock gets held while the activity and window managers + // holds their own lock. Thus you MUST NEVER call back into the activity manager + // or window manager or anything that depends on them while holding this lock. + // These LoadedApk are only valid for the userId that we're running as. + @GuardedBy("mResourcesManager") + @UnsupportedAppUsage + final ArrayMap> mPackages = new ArrayMap<>(); + @GuardedBy("mResourcesManager") + @UnsupportedAppUsage + final ArrayMap> mResourcePackages = new ArrayMap<>(); + @GuardedBy("mResourcesManager") + final ArrayList mRelaunchingActivities = new ArrayList<>(); + @GuardedBy("mResourcesManager") + @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R, + publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.") + Configuration mPendingConfiguration = null; + // An executor that performs multi-step transactions. + private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this); + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + private final ResourcesManager mResourcesManager; + + // Registry of remote cancellation transports pending a reply with reply handles. + @GuardedBy("this") + private @Nullable Map mRemoteCancellations; + + private static final class ProviderKey { + final String authority; + final int userId; + + @GuardedBy("mLock") + ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter + final Object mLock; // The lock to be used to get notified when the provider is ready + + public ProviderKey(String authority, int userId) { + this.authority = authority; + this.userId = userId; + this.mLock = new Object(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof ProviderKey) { + final ProviderKey other = (ProviderKey) o; + return Objects.equals(authority, other.authority) && userId == other.userId; + } + return false; + } + + @Override + public int hashCode() { + return ((authority != null) ? authority.hashCode() : 0) ^ userId; + } + } + + // The lock of mProviderMap protects the following variables. + @UnsupportedAppUsage + final ArrayMap mProviderMap + = new ArrayMap(); + @UnsupportedAppUsage + final ArrayMap mProviderRefCountMap + = new ArrayMap(); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + final ArrayMap mLocalProviders + = new ArrayMap(); + @UnsupportedAppUsage + final ArrayMap mLocalProvidersByName + = new ArrayMap(); + + // Mitigation for b/74523247: Used to serialize calls to AM.getContentProvider(). + // Note we never removes items from this map but that's okay because there are only so many + // users and so many authorities. + @GuardedBy("mGetProviderKeys") + final ArrayMap mGetProviderKeys = new ArrayMap<>(); + + final ArrayMap> mOnPauseListeners + = new ArrayMap>(); + + private SplashScreen.SplashScreenManagerGlobal mSplashScreenGlobal; + + final GcIdler mGcIdler = new GcIdler(); + final PurgeIdler mPurgeIdler = new PurgeIdler(); + + boolean mPurgeIdlerScheduled = false; + boolean mGcIdlerScheduled = false; + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + static volatile Handler sMainThreadHandler; // set once in main() + private long mStartSeq; // Only accesssed from the main thread + + Bundle mCoreSettings = null; + + /** + * The lock word for the {@link #mCoreSettings}. + */ + private final Object mCoreSettingsLock = new Object(); + + private IContentCaptureOptionsCallback.Stub mContentCaptureOptionsCallback = null; + + /** A client side controller to handle process level configuration changes. */ + private ConfigurationController mConfigurationController; + + /** Activity client record, used for bookkeeping for the real {@link Activity} instance. */ + public static final class ActivityClientRecord { + @UnsupportedAppUsage + public IBinder token; + public IBinder assistToken; + // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be + // used without security checks + public IBinder shareableActivityToken; + // The token of the TaskFragment that embedded this activity. + @Nullable public IBinder mTaskFragmentToken; + public IBinder initialCallerInfoAccessToken; + int ident; + @UnsupportedAppUsage + Intent intent; + String referrer; + IVoiceInteractor voiceInteractor; + Bundle state; + PersistableBundle persistentState; + @UnsupportedAppUsage + Activity activity; + Window window; + Activity parent; + String embeddedID; + Activity.NonConfigurationInstances lastNonConfigurationInstances; + // TODO(lifecycler): Use mLifecycleState instead. + @UnsupportedAppUsage + boolean paused; + @UnsupportedAppUsage + boolean stopped; + boolean hideForNow; + Configuration createdConfig; + Configuration overrideConfig; + @NonNull + private ActivityWindowInfo mActivityWindowInfo; + + // Used for consolidating configs before sending on to Activity. + private final Configuration tmpConfig = new Configuration(); + // Callback used for updating activity override config and camera compat control state. + ViewRootImpl.ActivityConfigCallback activityConfigCallback; + + // Indicates whether this activity is currently the topmost resumed one in the system. + // This holds the last reported value from server. + boolean isTopResumedActivity; + // This holds the value last sent to the activity. This is needed, because an update from + // server may come at random time, but we always need to report changes between ON_RESUME + // and ON_PAUSE to the app. + boolean lastReportedTopResumedState; + + ProfilerInfo profilerInfo; + + @UnsupportedAppUsage + ActivityInfo activityInfo; + @UnsupportedAppUsage + CompatibilityInfo compatInfo; + @UnsupportedAppUsage + public LoadedApk packageInfo; + + List pendingResults; + List pendingIntents; + + boolean startsNotResumed; + public final boolean isForward; + int pendingConfigChanges; + // Whether we are in the process of performing on user leaving. + boolean mIsUserLeaving; + + Window mPendingRemoveWindow; + WindowManager mPendingRemoveWindowManager; + @UnsupportedAppUsage + boolean mPreserveWindow; + + /** The scene transition info. */ + SceneTransitionInfo mSceneTransitionInfo; + + /** Whether this activiy was launched from a bubble. */ + boolean mLaunchedFromBubble; + + /** + * This can be different from the current configuration because a new configuration may not + * always update to activity, e.g. windowing mode change without size change. + */ + int mLastReportedWindowingMode = WINDOWING_MODE_UNDEFINED; + + @LifecycleState + private int mLifecycleState = PRE_ON_CREATE; + + private SizeConfigurationBuckets mSizeConfigurations; + + @VisibleForTesting + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public ActivityClientRecord() { + this.isForward = false; + init(); + } + + public ActivityClientRecord(IBinder token, Intent intent, int ident, + ActivityInfo info, Configuration overrideConfig, + String referrer, IVoiceInteractor voiceInteractor, Bundle state, + PersistableBundle persistentState, List pendingResults, + List pendingNewIntents, SceneTransitionInfo sceneTransitionInfo, + boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client, + IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble, + IBinder taskFragmentToken, IBinder initialCallerInfoAccessToken, + ActivityWindowInfo activityWindowInfo) { + this.token = token; + this.assistToken = assistToken; + this.shareableActivityToken = shareableActivityToken; + this.ident = ident; + this.intent = intent; + this.referrer = referrer; + this.voiceInteractor = voiceInteractor; + this.activityInfo = info; + this.state = state; + this.persistentState = persistentState; + this.pendingResults = pendingResults; + this.pendingIntents = pendingNewIntents; + this.isForward = isForward; + this.profilerInfo = profilerInfo; + this.overrideConfig = overrideConfig; + this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo); + this.initialCallerInfoAccessToken = initialCallerInfoAccessToken; + mSceneTransitionInfo = sceneTransitionInfo; + mLaunchedFromBubble = launchedFromBubble; + mTaskFragmentToken = taskFragmentToken; + mActivityWindowInfo = activityWindowInfo; + init(); + } + + /** Common initializer for all constructors. */ + private void init() { + parent = null; + embeddedID = null; + paused = false; + stopped = false; + hideForNow = false; + activityConfigCallback = new ViewRootImpl.ActivityConfigCallback() { + @Override + public void onConfigurationChanged(Configuration overrideConfig, + int newDisplayId) { + if (activity == null) { + throw new IllegalStateException( + "Received config update for non-existing activity"); + } + activity.mMainThread.handleActivityConfigurationChanged( + ActivityClientRecord.this, overrideConfig, newDisplayId, + mActivityWindowInfo, false /* alwaysReportChange */); + } + + @Override + public void requestCompatCameraControl(boolean showControl, + boolean transformationApplied, ICompatCameraControlCallback callback) { + if (activity == null) { + throw new IllegalStateException( + "Received camera compat control update for non-existing activity"); + } + ActivityClient.getInstance().requestCompatCameraControl( + activity.getResources(), token, showControl, transformationApplied, + callback); + } + + }; + } + + /** Get the current lifecycle state. */ + public int getLifecycleState() { + return mLifecycleState; + } + + /** Update the current lifecycle state for internal bookkeeping. */ + public void setState(@LifecycleState int newLifecycleState) { + mLifecycleState = newLifecycleState; + switch (mLifecycleState) { + case ON_CREATE: + paused = true; + stopped = true; + break; + case ON_START: + paused = true; + stopped = false; + break; + case ON_RESUME: + paused = false; + stopped = false; + break; + case ON_PAUSE: + paused = true; + stopped = false; + break; + case ON_STOP: + paused = true; + stopped = true; + break; + } + } + + private boolean isPreHoneycomb() { + return activity != null && activity.getApplicationInfo().targetSdkVersion + < android.os.Build.VERSION_CODES.HONEYCOMB; + } + + private boolean isPreP() { + return activity != null && activity.getApplicationInfo().targetSdkVersion + < android.os.Build.VERSION_CODES.P; + } + + public boolean isPersistable() { + return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; + } + + public boolean isVisibleFromServer() { + return activity != null && activity.mVisibleFromServer; + } + + @NonNull + public ActivityWindowInfo getActivityWindowInfo() { + return mActivityWindowInfo; + } + + public String toString() { + ComponentName componentName = intent != null ? intent.getComponent() : null; + return "ActivityRecord{" + + Integer.toHexString(System.identityHashCode(this)) + + " token=" + token + " " + (componentName == null + ? "no component name" : componentName.toShortString()) + + "}"; + } + + public String getStateString() { + StringBuilder sb = new StringBuilder(); + sb.append("ActivityClientRecord{"); + sb.append("paused=").append(paused); + sb.append(", stopped=").append(stopped); + sb.append(", hideForNow=").append(hideForNow); + sb.append(", startsNotResumed=").append(startsNotResumed); + sb.append(", isForward=").append(isForward); + sb.append(", pendingConfigChanges=").append(pendingConfigChanges); + sb.append(", preserveWindow=").append(mPreserveWindow); + if (activity != null) { + sb.append(", Activity{"); + sb.append("resumed=").append(activity.mResumed); + sb.append(", stopped=").append(activity.mStopped); + sb.append(", finished=").append(activity.isFinishing()); + sb.append(", destroyed=").append(activity.isDestroyed()); + sb.append(", startedActivity=").append(activity.mStartedActivity); + sb.append(", changingConfigurations=").append(activity.mChangingConfigurations); + sb.append("}"); + } + sb.append("}"); + return sb.toString(); + } + } + + static final class ProviderClientRecord { + final String[] mNames; + @UnsupportedAppUsage + final IContentProvider mProvider; + @UnsupportedAppUsage + final ContentProvider mLocalProvider; + @UnsupportedAppUsage + final ContentProviderHolder mHolder; + + ProviderClientRecord(String[] names, IContentProvider provider, + ContentProvider localProvider, ContentProviderHolder holder) { + mNames = names; + mProvider = provider; + mLocalProvider = localProvider; + mHolder = holder; + } + } + + static final class ReceiverData extends BroadcastReceiver.PendingResult { + public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras, + boolean ordered, boolean sticky, boolean assumeDelivered, IBinder token, + int sendingUser, int sendingUid, String sendingPackage) { + super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, + assumeDelivered, token, sendingUser, intent.getFlags(), sendingUid, + sendingPackage); + this.intent = intent; + } + + @UnsupportedAppUsage + final Intent intent; + @UnsupportedAppUsage + ActivityInfo info; + @UnsupportedAppUsage + CompatibilityInfo compatInfo; + public String toString() { + return "ReceiverData{intent=" + intent + " packageName=" + + info.packageName + " resultCode=" + getResultCode() + + " resultData=" + getResultData() + " resultExtras=" + + getResultExtras(false) + " sentFromUid=" + + getSentFromUid() + " sentFromPackage=" + getSentFromPackage() + "}"; + } + } + + static final class CreateBackupAgentData { + ApplicationInfo appInfo; + int backupMode; + int userId; + @BackupDestination int backupDestination; + public String toString() { + return "CreateBackupAgentData{appInfo=" + appInfo + + " backupAgent=" + appInfo.backupAgentName + + " mode=" + backupMode + " userId=" + userId + "}"; + } + } + + static final class CreateServiceData { + @UnsupportedAppUsage + CreateServiceData() { + } + @UnsupportedAppUsage + IBinder token; + @UnsupportedAppUsage + ServiceInfo info; + @UnsupportedAppUsage + CompatibilityInfo compatInfo; + @UnsupportedAppUsage + Intent intent; + public String toString() { + return "CreateServiceData{token=" + token + " className=" + + info.name + " packageName=" + info.packageName + + " intent=" + intent + "}"; + } + } + + static final class BindServiceData { + @UnsupportedAppUsage + IBinder token; + @UnsupportedAppUsage + Intent intent; + boolean rebind; + long bindSeq; + public String toString() { + return "BindServiceData{token=" + token + " intent=" + intent + + " bindSeq=" + bindSeq + "}"; + } + } + + static final class ServiceArgsData { + @UnsupportedAppUsage + IBinder token; + boolean taskRemoved; + int startId; + int flags; + @UnsupportedAppUsage + Intent args; + public String toString() { + return "ServiceArgsData{token=" + token + " startId=" + startId + + " args=" + args + "}"; + } + } + + static final class AppBindData { + @UnsupportedAppUsage + AppBindData() { + } + @UnsupportedAppUsage + LoadedApk info; + @UnsupportedAppUsage + String processName; + @UnsupportedAppUsage + ApplicationInfo appInfo; + String sdkSandboxClientAppVolumeUuid; + String sdkSandboxClientAppPackage; + boolean isSdkInSandbox; + @UnsupportedAppUsage + List providers; + ComponentName instrumentationName; + @UnsupportedAppUsage + Bundle instrumentationArgs; + IInstrumentationWatcher instrumentationWatcher; + IUiAutomationConnection instrumentationUiAutomationConnection; + int debugMode; + boolean enableBinderTracking; + boolean trackAllocation; + @UnsupportedAppUsage + boolean restrictedBackupMode; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + boolean persistent; + Configuration config; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + CompatibilityInfo compatInfo; + String buildSerial; + + /** Initial values for {@link Profiler}. */ + ProfilerInfo initProfilerInfo; + + AutofillOptions autofillOptions; + + /** + * Content capture options for the application - when null, it means ContentCapture is not + * enabled for the package. + */ + @Nullable + ContentCaptureOptions contentCaptureOptions; + + long[] disabledCompatChanges; + + SharedMemory mSerializedSystemFontMap; + + long startRequestedElapsedTime; + long startRequestedUptime; + + @Override + public String toString() { + return "AppBindData{appInfo=" + appInfo + "}"; + } + } + + static final class Profiler { + String profileFile; + ParcelFileDescriptor profileFd; + int samplingInterval; + boolean autoStopProfiler; + boolean streamingOutput; + int mClockType; + boolean profiling; + boolean handlingProfiling; + public void setProfiler(ProfilerInfo profilerInfo) { + ParcelFileDescriptor fd = profilerInfo.profileFd; + if (profiling) { + if (fd != null) { + try { + fd.close(); + } catch (IOException e) { + // Ignore + } + } + return; + } + if (profileFd != null) { + try { + profileFd.close(); + } catch (IOException e) { + // Ignore + } + } + profileFile = profilerInfo.profileFile; + profileFd = fd; + samplingInterval = profilerInfo.samplingInterval; + autoStopProfiler = profilerInfo.autoStopProfiler; + streamingOutput = profilerInfo.streamingOutput; + mClockType = profilerInfo.clockType; + } + public void startProfiling() { + if (profileFd == null || profiling) { + return; + } + try { + int bufferSize = SystemProperties.getInt("debug.traceview-buffer-size-mb", 8); + VMDebug.startMethodTracing(profileFile, profileFd.getFileDescriptor(), + bufferSize * 1024 * 1024, mClockType, samplingInterval != 0, + samplingInterval, streamingOutput); + profiling = true; + } catch (RuntimeException e) { + Slog.w(TAG, "Profiling failed on path " + profileFile, e); + try { + profileFd.close(); + profileFd = null; + } catch (IOException e2) { + Slog.w(TAG, "Failure closing profile fd", e2); + } + } + } + public void stopProfiling() { + if (profiling) { + profiling = false; + Debug.stopMethodTracing(); + if (profileFd != null) { + try { + profileFd.close(); + } catch (IOException e) { + } + } + profileFd = null; + profileFile = null; + } + } + } + + static final class DumpComponentInfo { + ParcelFileDescriptor fd; + IBinder token; + String prefix; + String[] args; + } + + static final class ContextCleanupInfo { + ContextImpl context; + String what; + String who; + } + + static final class DumpHeapData { + // Whether to dump the native or managed heap. + public boolean managed; + public boolean mallocInfo; + public boolean runGc; + String path; + ParcelFileDescriptor fd; + RemoteCallback finishCallback; + } + + static final class DumpResourcesData { + public ParcelFileDescriptor fd; + public RemoteCallback finishCallback; + } + + static final class UpdateCompatibilityData { + String pkg; + CompatibilityInfo info; + } + + static final class RequestAssistContextExtras { + IBinder activityToken; + IBinder requestToken; + int requestType; + int sessionId; + int flags; + } + + // A list of receivers and an index into the receiver to be processed next. + static final class ReceiverList { + List receivers; + int index; + } + + private class ApplicationThread extends IApplicationThread.Stub { + private static final String DB_CONNECTION_INFO_HEADER = " %8s %8s %14s %5s %5s %5s %s"; + private static final String DB_CONNECTION_INFO_FORMAT = " %8s %8s %14s %5d %5d %5d %s"; + private static final String DB_POOL_INFO_HEADER = " %13s %13s %13s %s"; + private static final String DB_POOL_INFO_FORMAT = " %13d %13d %13d %s"; + + public final void scheduleReceiver(Intent intent, ActivityInfo info, + CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras, + boolean ordered, boolean assumeDelivered, int sendingUser, int processState, + int sendingUid, String sendingPackage) { + updateProcessState(processState, false); + ReceiverData r = new ReceiverData(intent, resultCode, data, extras, + ordered, false, assumeDelivered, mAppThread.asBinder(), sendingUser, + sendingUid, sendingPackage); + r.info = info; + sendMessage(H.RECEIVER, r); + } + + public final void scheduleReceiverList(List info) throws RemoteException { + for (int i = 0; i < info.size(); i++) { + ReceiverInfo r = info.get(i); + if (r.registered) { + scheduleRegisteredReceiver(r.receiver, r.intent, + r.resultCode, r.data, r.extras, r.ordered, r.sticky, + r.assumeDelivered, r.sendingUser, r.processState, + r.sendingUid, r.sendingPackage); + } else { + scheduleReceiver(r.intent, r.activityInfo, r.compatInfo, + r.resultCode, r.data, r.extras, r.sync, + r.assumeDelivered, r.sendingUser, r.processState, + r.sendingUid, r.sendingPackage); + } + } + } + + public final void scheduleCreateBackupAgent(ApplicationInfo app, + int backupMode, int userId, @BackupDestination int backupDestination) { + CreateBackupAgentData d = new CreateBackupAgentData(); + d.appInfo = app; + d.backupMode = backupMode; + d.userId = userId; + d.backupDestination = backupDestination; + + sendMessage(H.CREATE_BACKUP_AGENT, d); + } + + public final void scheduleDestroyBackupAgent(ApplicationInfo app, int userId) { + CreateBackupAgentData d = new CreateBackupAgentData(); + d.appInfo = app; + d.userId = userId; + + sendMessage(H.DESTROY_BACKUP_AGENT, d); + } + + public final void scheduleCreateService(IBinder token, + ServiceInfo info, CompatibilityInfo compatInfo, int processState) { + updateProcessState(processState, false); + CreateServiceData s = new CreateServiceData(); + s.token = token; + s.info = info; + + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleCreateService. token=" + + token); + } + sendMessage(H.CREATE_SERVICE, s); + } + + public final void scheduleBindService(IBinder token, Intent intent, + boolean rebind, int processState, long bindSeq) { + updateProcessState(processState, false); + BindServiceData s = new BindServiceData(); + s.token = token; + s.intent = intent; + s.rebind = rebind; + s.bindSeq = bindSeq; + + if (DEBUG_SERVICE) + Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid=" + + Binder.getCallingUid() + " pid=" + Binder.getCallingPid()); + + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleBindService. token=" + + token + " bindSeq=" + bindSeq); + } + sendMessage(H.BIND_SERVICE, s); + } + + public final void scheduleUnbindService(IBinder token, Intent intent) { + BindServiceData s = new BindServiceData(); + s.token = token; + s.intent = intent; + s.bindSeq = -1; + + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleUnbindService. token=" + + token); + } + sendMessage(H.UNBIND_SERVICE, s); + } + + public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) { + List list = args.getList(); + + for (int i = 0; i < list.size(); i++) { + ServiceStartArgs ssa = list.get(i); + ServiceArgsData s = new ServiceArgsData(); + s.token = token; + s.taskRemoved = ssa.taskRemoved; + s.startId = ssa.startId; + s.flags = ssa.flags; + s.args = ssa.args; + + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleServiceArgs. token=" + + token + " startId=" + s.startId); + } + sendMessage(H.SERVICE_ARGS, s); + } + } + + public final void scheduleStopService(IBinder token) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleStopService. token=" + + token); + } + sendMessage(H.STOP_SERVICE, token); + } + + @Override + public final void scheduleTimeoutService(IBinder token, int startId) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleTimeoutService. token=" + + token); + } + sendMessage(H.TIMEOUT_SERVICE, token, startId); + } + + @Override + public final void schedulePing(RemoteCallback pong) { + sendMessage(H.PING, pong); + } + + @Override + public final void scheduleTimeoutServiceForType(IBinder token, int startId, + @ServiceInfo.ForegroundServiceType int fgsType) { + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "scheduleTimeoutServiceForType. token=" + token); + } + sendMessage(H.TIMEOUT_SERVICE_FOR_TYPE, token, startId, fgsType); + } + + @Override + public final void bindApplication( + String processName, + ApplicationInfo appInfo, + String sdkSandboxClientAppVolumeUuid, + String sdkSandboxClientAppPackage, + boolean isSdkInSandbox, + ProviderInfoList providerList, + ComponentName instrumentationName, + ProfilerInfo profilerInfo, + Bundle instrumentationArgs, + IInstrumentationWatcher instrumentationWatcher, + IUiAutomationConnection instrumentationUiConnection, + int debugMode, + boolean enableBinderTracking, + boolean trackAllocation, + boolean isRestrictedBackupMode, + boolean persistent, + Configuration config, + CompatibilityInfo compatInfo, + Map services, + Bundle coreSettings, + String buildSerial, + AutofillOptions autofillOptions, + ContentCaptureOptions contentCaptureOptions, + long[] disabledCompatChanges, + SharedMemory serializedSystemFontMap, + long startRequestedElapsedTime, + long startRequestedUptime) { + if (services != null) { + if (false) { + // Test code to make sure the app could see the passed-in services. + for (Object oname : services.keySet()) { + if (services.get(oname) == null) { + continue; // AM just passed in a null service. + } + String name = (String) oname; + + // See b/79378449 about the following exemption. + switch (name) { + case "package": + case Context.WINDOW_SERVICE: + continue; + } + + if (ServiceManager.getService(name) == null) { + Log.wtf(TAG, "Service " + name + " should be accessible by this app"); + } + } + } + + // Setup the service cache in the ServiceManager + ServiceManager.initServiceCache(services); + } + + setCoreSettings(coreSettings); + + AppBindData data = new AppBindData(); + data.processName = processName; + data.appInfo = appInfo; + data.sdkSandboxClientAppVolumeUuid = sdkSandboxClientAppVolumeUuid; + data.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage; + data.isSdkInSandbox = isSdkInSandbox; + data.providers = providerList.getList(); + data.instrumentationName = instrumentationName; + data.instrumentationArgs = instrumentationArgs; + data.instrumentationWatcher = instrumentationWatcher; + data.instrumentationUiAutomationConnection = instrumentationUiConnection; + data.debugMode = debugMode; + data.enableBinderTracking = enableBinderTracking; + data.trackAllocation = trackAllocation; + data.restrictedBackupMode = isRestrictedBackupMode; + data.persistent = persistent; + data.config = config; + data.compatInfo = compatInfo; + data.initProfilerInfo = profilerInfo; + data.buildSerial = buildSerial; + data.autofillOptions = autofillOptions; + data.contentCaptureOptions = contentCaptureOptions; + data.disabledCompatChanges = disabledCompatChanges; + data.mSerializedSystemFontMap = serializedSystemFontMap; + data.startRequestedElapsedTime = startRequestedElapsedTime; + data.startRequestedUptime = startRequestedUptime; + updateCompatOverrideScale(compatInfo); + CompatibilityInfo.applyOverrideScaleIfNeeded(config); + sendMessage(H.BIND_APPLICATION, data); + } + + private void updateCompatOverrideScale(CompatibilityInfo info) { + if (info.hasOverrideScaling()) { + CompatibilityInfo.setOverrideInvertedScale(info.applicationInvertedScale, + info.applicationDensityInvertedScale); + } else { + CompatibilityInfo.setOverrideInvertedScale(/* invertScale */ 1f, + /* densityInvertScale */1f); + } + } + + public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = entryPoint; + args.arg2 = entryPointArgs; + sendMessage(H.RUN_ISOLATED_ENTRY_POINT, args); + } + + public final void scheduleExit() { + sendMessage(H.EXIT_APPLICATION, null); + } + + public final void scheduleSuicide() { + sendMessage(H.SUICIDE, null); + } + + public void scheduleApplicationInfoChanged(ApplicationInfo ai) { + synchronized (mResourcesManager) { + var oldAi = mPendingAppInfoUpdates.put(ai.packageName, ai); + if (oldAi != null && oldAi.createTimestamp > ai.createTimestamp) { + Slog.w(TAG, "Skipping application info changed for obsolete AI with TS " + + ai.createTimestamp + " < already pending TS " + + oldAi.createTimestamp); + mPendingAppInfoUpdates.put(ai.packageName, oldAi); + return; + } + } + mResourcesManager.appendPendingAppInfoUpdate(new String[]{ai.sourceDir}, ai); + mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai.packageName); + sendMessage(H.APPLICATION_INFO_CHANGED, ai.packageName); + } + + public void updateTimeZone() { + TimeZone.setDefault(null); + } + + public void clearDnsCache() { + // a non-standard API to get this to libcore + InetAddress.clearDnsCache(); + // Allow libcore to perform the necessary actions as it sees fit upon a network + // configuration change. + NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange(); + } + + public void updateHttpProxy() { + final Application app; + synchronized (ActivityThread.this) { + app = getApplication(); + if (null == app) { + // The app is not bound yet. Make a note to update the HTTP proxy when the + // app is bound. + mUpdateHttpProxyOnBind = true; + return; + } + } + // App is present, update the proxy inline. + ActivityThread.updateHttpProxy(app); + } + + public void processInBackground() { + mH.removeMessages(H.GC_WHEN_IDLE); + mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE)); + } + + public void dumpService(ParcelFileDescriptor pfd, IBinder servicetoken, String[] args) { + DumpComponentInfo data = new DumpComponentInfo(); + try { + data.fd = pfd.dup(); + data.token = servicetoken; + data.args = args; + sendMessage(H.DUMP_SERVICE, data, 0, 0, true /*async*/); + } catch (IOException e) { + Slog.w(TAG, "dumpService failed", e); + } finally { + IoUtils.closeQuietly(pfd); + } + } + + // This function exists to make sure all receiver dispatching is + // correctly ordered, since these are one-way calls and the binder driver + // applies transaction ordering per object for such calls. + public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, + int resultCode, String dataStr, Bundle extras, boolean ordered, + boolean sticky, boolean assumeDelivered, int sendingUser, int processState, + int sendingUid, String sendingPackage) + throws RemoteException { + updateProcessState(processState, false); + + // We can't modify IIntentReceiver due to UnsupportedAppUsage, so + // try our best to shortcut to known subclasses, and alert if + // registered using a custom IIntentReceiver that isn't able to + // report an expected delivery event + if (receiver instanceof LoadedApk.ReceiverDispatcher.InnerReceiver) { + ((LoadedApk.ReceiverDispatcher.InnerReceiver) receiver).performReceive(intent, + resultCode, dataStr, extras, ordered, sticky, assumeDelivered, sendingUser, + sendingUid, sendingPackage); + } else { + if (!assumeDelivered) { + Log.wtf(TAG, "scheduleRegisteredReceiver() called for " + receiver + + " and " + intent + " without mechanism to finish delivery"); + } + if (sendingUid != Process.INVALID_UID || sendingPackage != null) { + Log.wtf(TAG, + "scheduleRegisteredReceiver() called for " + receiver + " and " + intent + + " from " + sendingPackage + " (UID: " + sendingUid + + ") without mechanism to propagate the sender's identity"); + } + receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky, + sendingUser); + } + } + + @Override + public void scheduleLowMemory() { + sendMessage(H.LOW_MEMORY, null); + } + + @Override + public void profilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { + sendMessage(H.PROFILER_CONTROL, profilerInfo, start ? 1 : 0, profileType); + } + + @Override + public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path, + ParcelFileDescriptor fd, RemoteCallback finishCallback) { + DumpHeapData dhd = new DumpHeapData(); + dhd.managed = managed; + dhd.mallocInfo = mallocInfo; + dhd.runGc = runGc; + dhd.path = path; + try { + // Since we're going to dump the heap asynchronously, dup the file descriptor before + // it's closed on returning from the IPC call. + dhd.fd = fd.dup(); + } catch (IOException e) { + Slog.e(TAG, "Failed to duplicate heap dump file descriptor", e); + return; + } finally { + IoUtils.closeQuietly(fd); + } + dhd.finishCallback = finishCallback; + sendMessage(H.DUMP_HEAP, dhd, 0, 0, true /*async*/); + } + + public void attachAgent(String agent) { + sendMessage(H.ATTACH_AGENT, agent); + } + + public void attachStartupAgents(String dataDir) { + sendMessage(H.ATTACH_STARTUP_AGENTS, dataDir); + } + + public void setSchedulingGroup(int group) { + // Note: do this immediately, since going into the foreground + // should happen regardless of what pending work we have to do + // and the activity manager will wait for us to report back that + // we are done before sending us to the background. + try { + Process.setProcessGroup(Process.myPid(), group); + } catch (Exception e) { + Slog.w(TAG, "Failed setting process group to " + group, e); + } + } + + public void dispatchPackageBroadcast(int cmd, String[] packages) { + sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd); + } + + @Override + public void scheduleCrash(String msg, int typeId, @Nullable Bundle extras) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = msg; + args.arg2 = extras; + sendMessage(H.SCHEDULE_CRASH, args, typeId); + } + + @Override + public void dumpResources(ParcelFileDescriptor fd, RemoteCallback callback) { + DumpResourcesData data = new DumpResourcesData(); + try { + data.fd = fd.dup(); + data.finishCallback = callback; + sendMessage(H.DUMP_RESOURCES, data, 0, 0, false /*async*/); + } catch (IOException e) { + Slog.w(TAG, "dumpResources failed", e); + } finally { + IoUtils.closeQuietly(fd); + } + } + + public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken, + String prefix, String[] args) { + DumpComponentInfo data = new DumpComponentInfo(); + try { + data.fd = pfd.dup(); + data.token = activitytoken; + data.prefix = prefix; + data.args = args; + sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/); + } catch (IOException e) { + Slog.w(TAG, "dumpActivity failed", e); + } finally { + IoUtils.closeQuietly(pfd); + } + } + + public void dumpProvider(ParcelFileDescriptor pfd, IBinder providertoken, + String[] args) { + DumpComponentInfo data = new DumpComponentInfo(); + try { + data.fd = pfd.dup(); + data.token = providertoken; + data.args = args; + sendMessage(H.DUMP_PROVIDER, data, 0, 0, true /*async*/); + } catch (IOException e) { + Slog.w(TAG, "dumpProvider failed", e); + } finally { + IoUtils.closeQuietly(pfd); + } + } + + @NeverCompile + @Override + public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin, + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + boolean dumpUnreachable, boolean dumpAllocatorStats, String[] args) { + FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor()); + PrintWriter pw = new FastPrintWriter(fout); + try { + dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, + dumpUnreachable, dumpAllocatorStats); + } finally { + pw.flush(); + IoUtils.closeQuietly(pfd); + } + } + + @NeverCompile + private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + boolean dumpUnreachable, boolean dumpAllocatorStats) { + long nativeMax = Debug.getNativeHeapSize() / 1024; + long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; + long nativeFree = Debug.getNativeHeapFreeSize() / 1024; + + Runtime runtime = Runtime.getRuntime(); + runtime.gc(); // Do GC since countInstancesOfClass counts unreachable objects. + long dalvikMax = runtime.totalMemory() / 1024; + long dalvikFree = runtime.freeMemory() / 1024; + long dalvikAllocated = dalvikMax - dalvikFree; + + Class[] classesToCount = new Class[] { + ContextImpl.class, + Activity.class, + WebView.class, + View.class, + ViewRootImpl.class + }; + long[] instanceCounts = VMDebug.countInstancesOfClasses(classesToCount, true); + long appContextInstanceCount = instanceCounts[0]; + long activityInstanceCount = instanceCounts[1]; + long webviewInstanceCount = instanceCounts[2]; + long viewInstanceCount = instanceCounts[3]; + long viewRootInstanceCount = instanceCounts[4]; + + int globalAssetCount = AssetManager.getGlobalAssetCount(); + int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); + int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); + int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); + int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); + long parcelSize = Parcel.getGlobalAllocSize(); + long parcelCount = Parcel.getGlobalAllocCount(); + SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); + + dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, + Process.myPid(), + (mBoundApplication != null) ? mBoundApplication.processName : "unknown", + nativeMax, nativeAllocated, nativeFree, + dalvikMax, dalvikAllocated, dalvikFree); + + if (checkin) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + + // Object counts + pw.print(viewInstanceCount); pw.print(','); + pw.print(viewRootInstanceCount); pw.print(','); + pw.print(appContextInstanceCount); pw.print(','); + pw.print(activityInstanceCount); pw.print(','); + + pw.print(globalAssetCount); pw.print(','); + pw.print(globalAssetManagerCount); pw.print(','); + pw.print(binderLocalObjectCount); pw.print(','); + pw.print(binderProxyObjectCount); pw.print(','); + + pw.print(binderDeathObjectCount); pw.print(','); + + // SQL + pw.print(stats.memoryUsed / 1024); pw.print(','); + pw.print(stats.memoryUsed / 1024); pw.print(','); + pw.print(stats.pageCacheOverflow / 1024); pw.print(','); + pw.print(stats.largestMemAlloc / 1024); + for (int i = 0; i < stats.dbStats.size(); i++) { + DbStats dbStats = stats.dbStats.get(i); + pw.print(','); pw.print(dbStats.dbName); + pw.print(','); pw.print(dbStats.pageSize); + pw.print(','); pw.print(dbStats.dbSize); + pw.print(','); pw.print(dbStats.lookaside); + pw.print(','); pw.print(dbStats.cacheHits); + pw.print(','); pw.print(dbStats.cacheMisses); + pw.print(','); pw.print(dbStats.cacheSize); + } + pw.println(); + + return; + } + + pw.println(" "); + pw.println(" Objects"); + printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRootImpl:", + viewRootInstanceCount); + + printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount, + "Activities:", activityInstanceCount); + + printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount, + "AssetManagers:", globalAssetManagerCount); + + printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount, + "Proxy Binders:", binderProxyObjectCount); + printRow(pw, TWO_COUNT_COLUMNS, "Parcel memory:", parcelSize/1024, + "Parcel count:", parcelCount); + printRow(pw, TWO_COUNT_COLUMNS, "Death Recipients:", binderDeathObjectCount, + "WebViews:", webviewInstanceCount); + + // SQLite mem info + pw.println(" "); + pw.println(" SQL"); + printRow(pw, ONE_COUNT_COLUMN, "MEMORY_USED:", stats.memoryUsed / 1024); + printRow(pw, TWO_COUNT_COLUMNS, "PAGECACHE_OVERFLOW:", + stats.pageCacheOverflow / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024); + pw.println(" "); + int N = stats.dbStats.size(); + if (N > 0) { + pw.println(" DATABASES"); + printRow(pw, DB_CONNECTION_INFO_HEADER, "pgsz", "dbsz", "Lookaside(b)", + "cache hits", "cache misses", "cache size", "Dbname"); + pw.println("PER CONNECTION STATS"); + for (int i = 0; i < N; i++) { + DbStats dbStats = stats.dbStats.get(i); + if (dbStats.arePoolStats) { + // these will be printed after + continue; + } + printRow(pw, DB_CONNECTION_INFO_FORMAT, + (dbStats.pageSize > 0) ? String.valueOf(dbStats.pageSize) : " ", + (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ", + (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ", + dbStats.cacheHits, dbStats.cacheMisses, dbStats.cacheSize, + dbStats.dbName); + } + // Print stats accumulated through all the connections that have existed in the + // pool since it was opened. + pw.println("POOL STATS"); + printRow(pw, DB_POOL_INFO_HEADER, "cache hits", "cache misses", "cache size", + "Dbname"); + for (int i = 0; i < N; i++) { + DbStats dbStats = stats.dbStats.get(i); + if (!dbStats.arePoolStats) { + continue; + } + printRow(pw, DB_POOL_INFO_FORMAT, dbStats.cacheHits, dbStats.cacheMisses, + dbStats.cacheSize, dbStats.dbName); + } + } + + // Asset details. + String assetAlloc = AssetManager.getAssetAllocations(); + if (assetAlloc != null) { + pw.println(" "); + pw.println(" Asset Allocations"); + pw.print(assetAlloc); + } + + // Unreachable native memory + if (dumpUnreachable) { + boolean showContents = ((mBoundApplication != null) + && ((mBoundApplication.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0)) + || android.os.Build.IS_DEBUGGABLE; + pw.println(" "); + pw.println(" Unreachable memory"); + pw.print(Debug.getUnreachableMemory(100, showContents)); + } + if (dumpAllocatorStats) { + Debug.logAllocatorStats(); + } + } + + @NeverCompile + @Override + public void dumpMemInfoProto(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + boolean dumpUnreachable, String[] args) { + ProtoOutputStream proto = new ProtoOutputStream(pfd.getFileDescriptor()); + try { + dumpMemInfo(proto, mem, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable); + } finally { + proto.flush(); + IoUtils.closeQuietly(pfd); + } + } + + @NeverCompile + private void dumpMemInfo(ProtoOutputStream proto, Debug.MemoryInfo memInfo, + boolean dumpFullInfo, boolean dumpDalvik, + boolean dumpSummaryOnly, boolean dumpUnreachable) { + long nativeMax = Debug.getNativeHeapSize() / 1024; + long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; + long nativeFree = Debug.getNativeHeapFreeSize() / 1024; + + Runtime runtime = Runtime.getRuntime(); + runtime.gc(); // Do GC since countInstancesOfClass counts unreachable objects. + long dalvikMax = runtime.totalMemory() / 1024; + long dalvikFree = runtime.freeMemory() / 1024; + long dalvikAllocated = dalvikMax - dalvikFree; + + Class[] classesToCount = new Class[] { + ContextImpl.class, + Activity.class, + WebView.class, + View.class, + ViewRootImpl.class + }; + long[] instanceCounts = VMDebug.countInstancesOfClasses(classesToCount, true); + long appContextInstanceCount = instanceCounts[0]; + long activityInstanceCount = instanceCounts[1]; + long webviewInstanceCount = instanceCounts[2]; + long viewInstanceCount = instanceCounts[3]; + long viewRootInstanceCount = instanceCounts[4]; + + int globalAssetCount = AssetManager.getGlobalAssetCount(); + int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); + int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); + int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); + int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); + long parcelSize = Parcel.getGlobalAllocSize(); + long parcelCount = Parcel.getGlobalAllocCount(); + SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); + + final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY); + proto.write(MemInfoDumpProto.ProcessMemory.PID, Process.myPid()); + proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, + (mBoundApplication != null) ? mBoundApplication.processName : "unknown"); + dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly, + nativeMax, nativeAllocated, nativeFree, + dalvikMax, dalvikAllocated, dalvikFree); + proto.end(mToken); + + final long oToken = proto.start(MemInfoDumpProto.AppData.OBJECTS); + proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, + viewInstanceCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT, + viewRootInstanceCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT, + appContextInstanceCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT, + activityInstanceCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, + globalAssetCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT, + globalAssetManagerCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT, + binderLocalObjectCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT, + binderProxyObjectCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_MEMORY_KB, + parcelSize / 1024); + proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT, + binderDeathObjectCount); + proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT, + webviewInstanceCount); + proto.end(oToken); + + // SQLite mem info + final long sToken = proto.start(MemInfoDumpProto.AppData.SQL); + proto.write(MemInfoDumpProto.AppData.SqlStats.MEMORY_USED_KB, + stats.memoryUsed / 1024); + proto.write(MemInfoDumpProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB, + stats.pageCacheOverflow / 1024); + proto.write(MemInfoDumpProto.AppData.SqlStats.MALLOC_SIZE_KB, + stats.largestMemAlloc / 1024); + int n = stats.dbStats.size(); + for (int i = 0; i < n; i++) { + DbStats dbStats = stats.dbStats.get(i); + + final long dToken = proto.start(MemInfoDumpProto.AppData.SqlStats.DATABASES); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.NAME, dbStats.dbName); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B, + dbStats.lookaside); + proto.write( + MemInfoDumpProto.AppData.SqlStats.Database.CACHE_HITS, dbStats.cacheHits); + proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE_MISSES, + dbStats.cacheMisses); + proto.write( + MemInfoDumpProto.AppData.SqlStats.Database.CACHE_SIZE, dbStats.cacheSize); + proto.end(dToken); + } + proto.end(sToken); + + // Asset details. + String assetAlloc = AssetManager.getAssetAllocations(); + if (assetAlloc != null) { + proto.write(MemInfoDumpProto.AppData.ASSET_ALLOCATIONS, assetAlloc); + } + + // Unreachable native memory + if (dumpUnreachable) { + int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags; + boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 + || android.os.Build.IS_DEBUGGABLE; + proto.write(MemInfoDumpProto.AppData.UNREACHABLE_MEMORY, + Debug.getUnreachableMemory(100, showContents)); + } + } + + @Override + public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) { + DumpComponentInfo data = new DumpComponentInfo(); + try { + data.fd = pfd.dup(); + data.token = null; + data.args = args; + sendMessage(H.DUMP_GFXINFO, data, 0, 0, true /*async*/); + } catch (IOException e) { + Slog.w(TAG, "dumpGfxInfo failed", e); + } finally { + IoUtils.closeQuietly(pfd); + } + } + + @Override + public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) { + PropertyInvalidatedCache.dumpCacheInfo(pfd, args); + IoUtils.closeQuietly(pfd); + } + + private File getDatabasesDir(Context context) { + // There's no simple way to get the databases/ path, so do it this way. + return context.getDatabasePath("a").getParentFile(); + } + + private void dumpDatabaseInfo(ParcelFileDescriptor pfd, String[] args, boolean isSystem) { + PrintWriter pw = new FastPrintWriter( + new FileOutputStream(pfd.getFileDescriptor())); + PrintWriterPrinter printer = new PrintWriterPrinter(pw); + SQLiteDebug.dump(printer, args, isSystem); + pw.flush(); + } + + @Override + public void dumpDbInfo(final ParcelFileDescriptor pfd, final String[] args) { + if (mSystemThread) { + // Ensure this invocation is asynchronous to prevent writer waiting if buffer cannot + // be consumed. But it must duplicate the file descriptor first, since caller might + // be closing it. + final ParcelFileDescriptor dup; + try { + dup = pfd.dup(); + } catch (IOException e) { + Log.w(TAG, "Could not dup FD " + pfd.getFileDescriptor().getInt$()); + return; + } finally { + IoUtils.closeQuietly(pfd); + } + + AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { + @Override + public void run() { + try { + dumpDatabaseInfo(dup, args, true); + } finally { + IoUtils.closeQuietly(dup); + } + } + }); + } else { + dumpDatabaseInfo(pfd, args, false); + IoUtils.closeQuietly(pfd); + } + } + + @Override + public void unstableProviderDied(IBinder provider) { + sendMessage(H.UNSTABLE_PROVIDER_DIED, provider); + } + + @Override + public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken, + int requestType, int sessionId, int flags) { + RequestAssistContextExtras cmd = new RequestAssistContextExtras(); + cmd.activityToken = activityToken; + cmd.requestToken = requestToken; + cmd.requestType = requestType; + cmd.sessionId = sessionId; + cmd.flags = flags; + sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd); + } + + public void setCoreSettings(Bundle coreSettings) { + sendMessage(H.SET_CORE_SETTINGS, coreSettings); + } + + public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) { + UpdateCompatibilityData ucd = new UpdateCompatibilityData(); + ucd.pkg = pkg; + ucd.info = info; + updateCompatOverrideScale(info); + sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd); + } + + public void scheduleTrimMemory(int level) { + final Runnable r = PooledLambda.obtainRunnable(ActivityThread::handleTrimMemory, + ActivityThread.this, level).recycleOnUse(); + // Schedule trimming memory after drawing the frame to minimize jank-risk. + Choreographer choreographer = Choreographer.getMainThreadInstance(); + if (choreographer != null) { + choreographer.postCallback(Choreographer.CALLBACK_COMMIT, r, null); + } else { + mH.post(r); + } + } + + public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) { + sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0); + } + + public void scheduleOnNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) { + sendMessage(H.ON_NEW_SCENE_TRANSITION_INFO, + new Pair(token, info)); + } + + public void setProcessState(int state) { + updateProcessState(state, true); + } + + /** + * Updates {@link #mNetworkBlockSeq}. This is used by ActivityManagerService to inform + * the main thread that it needs to wait for the network rules to get updated before + * launching an activity. + */ + @Override + public void setNetworkBlockSeq(long procStateSeq) { + synchronized (mNetworkPolicyLock) { + mNetworkBlockSeq = procStateSeq; + } + } + + @Override + public void scheduleInstallProvider(ProviderInfo provider) { + sendMessage(H.INSTALL_PROVIDER, provider); + } + + @Override + public final void updateTimePrefs(int timeFormatPreference) { + final Boolean timeFormatPreferenceBool; + // For convenience we are using the Intent extra values. + if (timeFormatPreference == Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR) { + timeFormatPreferenceBool = Boolean.FALSE; + } else if (timeFormatPreference == Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR) { + timeFormatPreferenceBool = Boolean.TRUE; + } else { + // timeFormatPreference == Intent.EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT + // (or unknown). + timeFormatPreferenceBool = null; + } + DateFormat.set24HourTimePref(timeFormatPreferenceBool); + } + + @Override + public void scheduleEnterAnimationComplete(IBinder token) { + sendMessage(H.ENTER_ANIMATION_COMPLETE, token); + } + + @Override + public void notifyCleartextNetwork(byte[] firstPacket) { + if (StrictMode.vmCleartextNetworkEnabled()) { + StrictMode.onCleartextNetworkDetected(firstPacket); + } + } + + @Override + public void startBinderTracking() { + sendMessage(H.START_BINDER_TRACKING, null); + } + + @Override + public void stopBinderTrackingAndDump(ParcelFileDescriptor pfd) { + try { + sendMessage(H.STOP_BINDER_TRACKING_AND_DUMP, pfd.dup()); + } catch (IOException e) { + } finally { + IoUtils.closeQuietly(pfd); + } + } + + @Override + public void scheduleLocalVoiceInteractionStarted(IBinder token, + IVoiceInteractor voiceInteractor) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = token; + args.arg2 = voiceInteractor; + sendMessage(H.LOCAL_VOICE_INTERACTION_STARTED, args); + } + + @Override + public void handleTrustStorageUpdate() { + NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate(); + } + + @Override + public void scheduleTransaction(ClientTransaction transaction) throws RemoteException { + ActivityThread.this.scheduleTransaction(transaction); + } + + @Override + public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer, + @NonNull TaskFragmentTransaction transaction) throws RemoteException { + // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token + // after flag removal. + organizer.onTransactionReady(transaction); + } + + @Override + public void requestDirectActions(@NonNull IBinder activityToken, + @NonNull IVoiceInteractor interactor, @Nullable RemoteCallback cancellationCallback, + @NonNull RemoteCallback callback) { + final CancellationSignal cancellationSignal = new CancellationSignal(); + if (cancellationCallback != null) { + final ICancellationSignal transport = createSafeCancellationTransport( + cancellationSignal); + final Bundle cancellationResult = new Bundle(); + cancellationResult.putBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL, + transport.asBinder()); + cancellationCallback.sendResult(cancellationResult); + } + mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handleRequestDirectActions, + ActivityThread.this, activityToken, interactor, cancellationSignal, callback, + REQUEST_DIRECT_ACTIONS_RETRY_MAX_COUNT)); + } + + @Override + public void performDirectAction(@NonNull IBinder activityToken, @NonNull String actionId, + @Nullable Bundle arguments, @Nullable RemoteCallback cancellationCallback, + @NonNull RemoteCallback resultCallback) { + final CancellationSignal cancellationSignal = new CancellationSignal(); + if (cancellationCallback != null) { + final ICancellationSignal transport = createSafeCancellationTransport( + cancellationSignal); + final Bundle cancellationResult = new Bundle(); + cancellationResult.putBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL, + transport.asBinder()); + cancellationCallback.sendResult(cancellationResult); + } + mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handlePerformDirectAction, + ActivityThread.this, activityToken, actionId, arguments, + cancellationSignal, resultCallback)); + } + + @Override + public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, + @NonNull String authorities, int userId, boolean published) { + final String auths[] = authorities.split(";"); + for (String auth: auths) { + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key.mLock) { + key.mHolder = holder; + key.mLock.notifyAll(); + } + } + } + + @Override + public void instrumentWithoutRestart(ComponentName instrumentationName, + Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, + IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) { + AppBindData data = new AppBindData(); + data.instrumentationName = instrumentationName; + data.instrumentationArgs = instrumentationArgs; + data.instrumentationWatcher = instrumentationWatcher; + data.instrumentationUiAutomationConnection = instrumentationUiConnection; + data.appInfo = targetInfo; + sendMessage(H.INSTRUMENT_WITHOUT_RESTART, data); + } + + @Override + public void updateUiTranslationState(IBinder activityToken, int state, + TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds, + UiTranslationSpec uiTranslationSpec) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = activityToken; + args.arg2 = state; + args.arg3 = sourceSpec; + args.arg4 = targetSpec; + args.arg5 = viewIds; + args.arg6 = uiTranslationSpec; + sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args); + } + } + + private @NonNull SafeCancellationTransport createSafeCancellationTransport( + @NonNull CancellationSignal cancellationSignal) { + synchronized (ActivityThread.this) { + if (mRemoteCancellations == null) { + mRemoteCancellations = new ArrayMap<>(); + } + final SafeCancellationTransport transport = new SafeCancellationTransport( + this, cancellationSignal); + mRemoteCancellations.put(transport, cancellationSignal); + return transport; + } + } + + private @NonNull CancellationSignal removeSafeCancellationTransport( + @NonNull SafeCancellationTransport transport) { + synchronized (ActivityThread.this) { + final CancellationSignal cancellation = mRemoteCancellations.remove(transport); + if (mRemoteCancellations.isEmpty()) { + mRemoteCancellations = null; + } + return cancellation; + } + } + + private static final class SafeCancellationTransport extends ICancellationSignal.Stub { + private final @NonNull WeakReference mWeakActivityThread; + + SafeCancellationTransport(@NonNull ActivityThread activityThread, + @NonNull CancellationSignal cancellation) { + mWeakActivityThread = new WeakReference<>(activityThread); + } + + @Override + public void cancel() { + final ActivityThread activityThread = mWeakActivityThread.get(); + if (activityThread != null) { + final CancellationSignal cancellation = activityThread + .removeSafeCancellationTransport(this); + if (cancellation != null) { + cancellation.cancel(); + } + } + } + } + + private void throwRemoteServiceException(String message, int typeId, @Nullable Bundle extras) { + // Use a switch to ensure all the type IDs are unique. + switch (typeId) { + case ForegroundServiceDidNotStartInTimeException.TYPE_ID: + throw generateForegroundServiceDidNotStartInTimeException(message, extras); + + case CannotPostForegroundServiceNotificationException.TYPE_ID: + throw new CannotPostForegroundServiceNotificationException(message); + + case BadForegroundServiceNotificationException.TYPE_ID: + throw new BadForegroundServiceNotificationException(message); + + case BadUserInitiatedJobNotificationException.TYPE_ID: + throw new BadUserInitiatedJobNotificationException(message); + + case MissingRequestPasswordComplexityPermissionException.TYPE_ID: + throw new MissingRequestPasswordComplexityPermissionException(message); + + case CrashedByAdbException.TYPE_ID: + throw new CrashedByAdbException(message); + + default: + throw new RemoteServiceException(message + + " (with unwknown typeId:" + typeId + ")"); + } + } + + private ForegroundServiceDidNotStartInTimeException + generateForegroundServiceDidNotStartInTimeException(String message, Bundle extras) { + final String serviceClassName = + ForegroundServiceDidNotStartInTimeException.getServiceClassNameFromExtras(extras); + final Exception inner = (serviceClassName == null) ? null + : Service.getStartForegroundServiceStackTrace(serviceClassName); + throw new ForegroundServiceDidNotStartInTimeException(message, inner); + } + + class H extends Handler { + public static final int BIND_APPLICATION = 110; + @UnsupportedAppUsage + public static final int EXIT_APPLICATION = 111; + @UnsupportedAppUsage + public static final int RECEIVER = 113; + @UnsupportedAppUsage + public static final int CREATE_SERVICE = 114; + @UnsupportedAppUsage + public static final int SERVICE_ARGS = 115; + @UnsupportedAppUsage + public static final int STOP_SERVICE = 116; + + public static final int CONFIGURATION_CHANGED = 118; + public static final int CLEAN_UP_CONTEXT = 119; + @UnsupportedAppUsage + public static final int GC_WHEN_IDLE = 120; + @UnsupportedAppUsage + public static final int BIND_SERVICE = 121; + @UnsupportedAppUsage + public static final int UNBIND_SERVICE = 122; + public static final int DUMP_SERVICE = 123; + public static final int LOW_MEMORY = 124; + public static final int PROFILER_CONTROL = 127; + public static final int CREATE_BACKUP_AGENT = 128; + public static final int DESTROY_BACKUP_AGENT = 129; + public static final int SUICIDE = 130; + @UnsupportedAppUsage + public static final int REMOVE_PROVIDER = 131; + public static final int DISPATCH_PACKAGE_BROADCAST = 133; + @UnsupportedAppUsage + public static final int SCHEDULE_CRASH = 134; + public static final int DUMP_HEAP = 135; + public static final int DUMP_ACTIVITY = 136; + public static final int SLEEPING = 137; + public static final int SET_CORE_SETTINGS = 138; + public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139; + @UnsupportedAppUsage + public static final int DUMP_PROVIDER = 141; + public static final int UNSTABLE_PROVIDER_DIED = 142; + public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143; + public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144; + @UnsupportedAppUsage + public static final int INSTALL_PROVIDER = 145; + public static final int ON_NEW_SCENE_TRANSITION_INFO = 146; + @UnsupportedAppUsage + public static final int ENTER_ANIMATION_COMPLETE = 149; + public static final int START_BINDER_TRACKING = 150; + public static final int STOP_BINDER_TRACKING_AND_DUMP = 151; + public static final int LOCAL_VOICE_INTERACTION_STARTED = 154; + public static final int ATTACH_AGENT = 155; + public static final int APPLICATION_INFO_CHANGED = 156; + public static final int RUN_ISOLATED_ENTRY_POINT = 158; + public static final int EXECUTE_TRANSACTION = 159; + public static final int RELAUNCH_ACTIVITY = 160; + public static final int PURGE_RESOURCES = 161; + public static final int ATTACH_STARTUP_AGENTS = 162; + public static final int UPDATE_UI_TRANSLATION_STATE = 163; + public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164; + public static final int DUMP_GFXINFO = 165; + public static final int DUMP_RESOURCES = 166; + public static final int TIMEOUT_SERVICE = 167; + public static final int PING = 168; + + public static final int INSTRUMENT_WITHOUT_RESTART = 170; + public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171; + + public static final int TIMEOUT_SERVICE_FOR_TYPE = 172; + + String codeToString(int code) { + if (DEBUG_MESSAGES) { + switch (code) { + case BIND_APPLICATION: return "BIND_APPLICATION"; + case EXIT_APPLICATION: return "EXIT_APPLICATION"; + case RECEIVER: return "RECEIVER"; + case CREATE_SERVICE: return "CREATE_SERVICE"; + case SERVICE_ARGS: return "SERVICE_ARGS"; + case STOP_SERVICE: return "STOP_SERVICE"; + case CONFIGURATION_CHANGED: return "CONFIGURATION_CHANGED"; + case CLEAN_UP_CONTEXT: return "CLEAN_UP_CONTEXT"; + case GC_WHEN_IDLE: return "GC_WHEN_IDLE"; + case BIND_SERVICE: return "BIND_SERVICE"; + case UNBIND_SERVICE: return "UNBIND_SERVICE"; + case DUMP_SERVICE: return "DUMP_SERVICE"; + case LOW_MEMORY: return "LOW_MEMORY"; + case PROFILER_CONTROL: return "PROFILER_CONTROL"; + case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; + case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; + case SUICIDE: return "SUICIDE"; + case REMOVE_PROVIDER: return "REMOVE_PROVIDER"; + case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST"; + case SCHEDULE_CRASH: return "SCHEDULE_CRASH"; + case DUMP_HEAP: return "DUMP_HEAP"; + case DUMP_ACTIVITY: return "DUMP_ACTIVITY"; + case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS"; + case UPDATE_PACKAGE_COMPATIBILITY_INFO: + return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; + case DUMP_PROVIDER: return "DUMP_PROVIDER"; + case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; + case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS"; + case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE"; + case INSTALL_PROVIDER: return "INSTALL_PROVIDER"; + case ON_NEW_SCENE_TRANSITION_INFO: return "ON_NEW_SCENE_TRANSITION_INFO"; + case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE"; + case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED"; + case ATTACH_AGENT: return "ATTACH_AGENT"; + case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED"; + case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT"; + case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION"; + case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; + case PURGE_RESOURCES: return "PURGE_RESOURCES"; + case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS"; + case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE"; + case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK: + return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK"; + case DUMP_GFXINFO: return "DUMP GFXINFO"; + case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART"; + case FINISH_INSTRUMENTATION_WITHOUT_RESTART: + return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; + case DUMP_RESOURCES: return "DUMP_RESOURCES"; + case TIMEOUT_SERVICE: return "TIMEOUT_SERVICE"; + case PING: return "PING"; + case TIMEOUT_SERVICE_FOR_TYPE: return "TIMEOUT_SERVICE_FOR_TYPE"; + } + } + return Integer.toString(code); + } + public void handleMessage(Message msg) { + if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); + switch (msg.what) { + case BIND_APPLICATION: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); + AppBindData data = (AppBindData)msg.obj; + handleBindApplication(data); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case EXIT_APPLICATION: + if (mInitialApplication != null) { + mInitialApplication.onTerminate(); + } + Looper.myLooper().quit(); + break; + case RECEIVER: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + ReceiverData rec = (ReceiverData) msg.obj; + if (rec.intent != null) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "broadcastReceiveComp: " + rec.intent.getAction()); + } else { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "broadcastReceiveComp"); + } + } + handleReceiver((ReceiverData)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case CREATE_SERVICE: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + ("serviceCreate: " + String.valueOf(msg.obj))); + } + handleCreateService((CreateServiceData)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case BIND_SERVICE: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: " + + String.valueOf(msg.obj)); + } + handleBindService((BindServiceData)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case UNBIND_SERVICE: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind: " + + String.valueOf(msg.obj)); + } + handleUnbindService((BindServiceData)msg.obj); + schedulePurgeIdler(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case SERVICE_ARGS: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + ("serviceStart: " + String.valueOf(msg.obj))); + } + handleServiceArgs((ServiceArgsData)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case STOP_SERVICE: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop: " + + String.valueOf(msg.obj)); + } + handleStopService((IBinder)msg.obj); + schedulePurgeIdler(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case TIMEOUT_SERVICE: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout: " + + String.valueOf(msg.obj)); + } + handleTimeoutService((IBinder) msg.obj, msg.arg1); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case PING: + ((RemoteCallback) msg.obj).sendResult(null); + break; + case TIMEOUT_SERVICE_FOR_TYPE: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "serviceTimeoutForType: " + msg.obj); + } + handleTimeoutServiceForType((IBinder) msg.obj, msg.arg1, msg.arg2); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case CONFIGURATION_CHANGED: + mConfigurationController.handleConfigurationChanged((Configuration) msg.obj); + break; + case CLEAN_UP_CONTEXT: + ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj; + cci.context.performFinalCleanup(cci.who, cci.what); + break; + case GC_WHEN_IDLE: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "gcWhenIdle"); + try { + scheduleGcIdler(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + break; + case DUMP_SERVICE: + handleDumpService((DumpComponentInfo)msg.obj); + break; + case DUMP_GFXINFO: + handleDumpGfxInfo((DumpComponentInfo) msg.obj); + break; + case LOW_MEMORY: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "lowMemory"); + handleLowMemory(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case PROFILER_CONTROL: + handleProfilerControl(msg.arg1 != 0, (ProfilerInfo)msg.obj, msg.arg2); + break; + case CREATE_BACKUP_AGENT: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupCreateAgent"); + handleCreateBackupAgent((CreateBackupAgentData)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case DESTROY_BACKUP_AGENT: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupDestroyAgent"); + handleDestroyBackupAgent((CreateBackupAgentData)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case SUICIDE: + Process.killProcess(Process.myPid()); + break; + case REMOVE_PROVIDER: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove"); + completeRemoveProvider((ProviderRefCount)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case DISPATCH_PACKAGE_BROADCAST: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastPackage"); + handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case SCHEDULE_CRASH: { + SomeArgs args = (SomeArgs) msg.obj; + String message = (String) args.arg1; + Bundle extras = (Bundle) args.arg2; + args.recycle(); + throwRemoteServiceException(message, msg.arg1, extras); + break; + } + case DUMP_HEAP: + handleDumpHeap((DumpHeapData) msg.obj); + break; + case DUMP_RESOURCES: + handleDumpResources((DumpResourcesData) msg.obj); + break; + case DUMP_ACTIVITY: + handleDumpActivity((DumpComponentInfo)msg.obj); + break; + case DUMP_PROVIDER: + handleDumpProvider((DumpComponentInfo)msg.obj); + break; + case SET_CORE_SETTINGS: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setCoreSettings"); + handleSetCoreSettings((Bundle) msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; + case UPDATE_PACKAGE_COMPATIBILITY_INFO: + handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj); + break; + case UNSTABLE_PROVIDER_DIED: + handleUnstableProviderDied((IBinder)msg.obj, false); + break; + case REQUEST_ASSIST_CONTEXT_EXTRAS: + handleRequestAssistContextExtras((RequestAssistContextExtras)msg.obj); + break; + case TRANSLUCENT_CONVERSION_COMPLETE: + handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1); + break; + case INSTALL_PROVIDER: + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerInstall: " + + String.valueOf(msg.obj)); + } + try { + handleInstallProvider((ProviderInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + break; + case ON_NEW_SCENE_TRANSITION_INFO: + Pair pair = + (Pair) msg.obj; + onNewSceneTransitionInfo(pair.first, pair.second); + break; + case ENTER_ANIMATION_COMPLETE: + handleEnterAnimationComplete((IBinder) msg.obj); + break; + case START_BINDER_TRACKING: + handleStartBinderTracking(); + break; + case STOP_BINDER_TRACKING_AND_DUMP: + handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj); + break; + case LOCAL_VOICE_INTERACTION_STARTED: + handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1, + (IVoiceInteractor) ((SomeArgs) msg.obj).arg2); + break; + case ATTACH_AGENT: { + Application app = getApplication(); + handleAttachAgent((String) msg.obj, app != null ? app.mLoadedApk : null); + break; + } + case APPLICATION_INFO_CHANGED: + applyPendingApplicationInfoChanges((String) msg.obj); + break; + case RUN_ISOLATED_ENTRY_POINT: + handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1, + (String[]) ((SomeArgs) msg.obj).arg2); + break; + case EXECUTE_TRANSACTION: + final ClientTransaction transaction = (ClientTransaction) msg.obj; + mTransactionExecutor.execute(transaction); + if (isSystem()) { + // Client transactions inside system process are recycled on the client side + // instead of ClientLifecycleManager to avoid being cleared before this + // message is handled. + transaction.recycle(); + } + // TODO(lifecycler): Recycle locally scheduled transactions. + break; + case RELAUNCH_ACTIVITY: + handleRelaunchActivityLocally((IBinder) msg.obj); + break; + case PURGE_RESOURCES: + schedulePurgeIdler(); + break; + case ATTACH_STARTUP_AGENTS: + handleAttachStartupAgents((String) msg.obj); + break; + case UPDATE_UI_TRANSLATION_STATE: + final SomeArgs args = (SomeArgs) msg.obj; + updateUiTranslationState((IBinder) args.arg1, (int) args.arg2, + (TranslationSpec) args.arg3, (TranslationSpec) args.arg4, + (List) args.arg5, (UiTranslationSpec) args.arg6); + break; + case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK: + handleSetContentCaptureOptionsCallback((String) msg.obj); + break; + case INSTRUMENT_WITHOUT_RESTART: + handleInstrumentWithoutRestart((AppBindData) msg.obj); + break; + case FINISH_INSTRUMENTATION_WITHOUT_RESTART: + handleFinishInstrumentationWithoutRestart(); + break; + } + Object obj = msg.obj; + if (obj instanceof SomeArgs) { + ((SomeArgs) obj).recycle(); + } + if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); + } + } + + private class Idler implements MessageQueue.IdleHandler { + @Override + public final boolean queueIdle() { + boolean stopProfiling = false; + if (mBoundApplication != null && mProfiler.profileFd != null + && mProfiler.autoStopProfiler) { + stopProfiling = true; + } + final ActivityClient ac = ActivityClient.getInstance(); + while (mNewActivities.size() > 0) { + final ActivityClientRecord a = mNewActivities.remove(0); + if (localLOGV) { + Slog.v(TAG, "Reporting idle of " + a + " finished=" + + (a.activity != null && a.activity.mFinished)); + } + if (a.activity != null && !a.activity.mFinished) { + ac.activityIdle(a.token, a.createdConfig, stopProfiling); + a.createdConfig = null; + } + } + if (stopProfiling) { + mProfiler.stopProfiling(); + } + return false; + } + } + + final class GcIdler implements MessageQueue.IdleHandler { + @Override + public final boolean queueIdle() { + doGcIfNeeded(); + purgePendingResources(); + return false; + } + } + + final class PurgeIdler implements MessageQueue.IdleHandler { + @Override + public boolean queueIdle() { + purgePendingResources(); + return false; + } + } + + @UnsupportedAppUsage + public static ActivityThread currentActivityThread() { + return sCurrentActivityThread; + } + + public static boolean isSystem() { + return (sCurrentActivityThread != null) ? sCurrentActivityThread.mSystemThread : false; + } + + public static String currentOpPackageName() { + ActivityThread am = currentActivityThread(); + return (am != null && am.getApplication() != null) + ? am.getApplication().getOpPackageName() : null; + } + + public static AttributionSource currentAttributionSource() { + ActivityThread am = currentActivityThread(); + return (am != null && am.getApplication() != null) + ? am.getApplication().getAttributionSource() : null; + } + + @UnsupportedAppUsage + public static String currentPackageName() { + ActivityThread am = currentActivityThread(); + return (am != null && am.mBoundApplication != null) + ? am.mBoundApplication.appInfo.packageName : null; + } + + @UnsupportedAppUsage + public static String currentProcessName() { + ActivityThread am = currentActivityThread(); + return (am != null && am.mBoundApplication != null) + ? am.mBoundApplication.processName : null; + } + + @UnsupportedAppUsage + public static Application currentApplication() { + ActivityThread am = currentActivityThread(); + return am != null ? am.mInitialApplication : null; + } + + @UnsupportedAppUsage + public static IPackageManager getPackageManager() { + if (sPackageManager != null) { + return sPackageManager; + } + final IBinder b = ServiceManager.getService("package"); + sPackageManager = IPackageManager.Stub.asInterface(b); + return sPackageManager; + } + + /** Returns the permission manager */ + public static IPermissionManager getPermissionManager() { + if (sPermissionManager != null) { + return sPermissionManager; + } + final IBinder b = ServiceManager.getService("permissionmgr"); + sPermissionManager = IPermissionManager.Stub.asInterface(b); + return sPermissionManager; + } + + /** + * Creates the top level resources for the given package. Will return an existing + * Resources if one has already been created. + */ + Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs, + String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo, + Configuration overrideConfig) { + return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs, + overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(), + pkgInfo.getClassLoader(), null); + } + + @UnsupportedAppUsage + public Handler getHandler() { + return mH; + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, + int flags) { + return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); + } + + public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, + int flags, int userId) { + final boolean differentUser = (UserHandle.myUserId() != userId); + ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached( + packageName, + PackageManager.GET_SHARED_LIBRARY_FILES + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + (userId < 0) ? UserHandle.myUserId() : userId); + synchronized (mResourcesManager) { + WeakReference ref; + if (differentUser) { + // Caching not supported across users + ref = null; + } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) { + ref = mPackages.get(packageName); + } else { + ref = mResourcePackages.get(packageName); + } + + LoadedApk packageInfo = ref != null ? ref.get() : null; + if (ai != null && packageInfo != null) { + if (!isLoadedApkResourceDirsUpToDate(packageInfo, ai)) { + List oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, ai, oldPaths); + packageInfo.updateApplicationInfo(ai, oldPaths); + } + + if (packageInfo.isSecurityViolation() + && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) { + throw new SecurityException( + "Requesting code from " + packageName + + " to be run in process " + + mBoundApplication.processName + + "/" + mBoundApplication.appInfo.uid); + } + return packageInfo; + } + } + + if (ai != null) { + return getPackageInfo(ai, compatInfo, flags); + } + + return null; + } + + @UnsupportedAppUsage(trackingBug = 171933273) + public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, + int flags) { + boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; + boolean securityViolation = includeCode && ai.uid != 0 + && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null + ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid) + : true); + boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0; + if ((flags&(Context.CONTEXT_INCLUDE_CODE + |Context.CONTEXT_IGNORE_SECURITY)) + == Context.CONTEXT_INCLUDE_CODE) { + if (securityViolation) { + String msg = "Requesting code from " + ai.packageName + + " (with uid " + ai.uid + ")"; + if (mBoundApplication != null) { + msg = msg + " to be run in process " + + mBoundApplication.processName + " (with uid " + + mBoundApplication.appInfo.uid + ")"; + } + throw new SecurityException(msg); + } + } + return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode, + registerPackage); + } + + @UnsupportedAppUsage + public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, + CompatibilityInfo compatInfo) { + return getPackageInfo(ai, compatInfo, null, false, true, false); + } + + @Override + public LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) { + return getPackageInfo(ai, mCompatibilityInfo, null /* baseLoader */, + false /* securityViolation */, true /* includeCode */, false /* registerPackage */); + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) { + synchronized (mResourcesManager) { + WeakReference ref; + if (includeCode) { + ref = mPackages.get(packageName); + } else { + ref = mResourcePackages.get(packageName); + } + return ref != null ? ref.get() : null; + } + } + + private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, + ClassLoader baseLoader, boolean securityViolation, boolean includeCode, + boolean registerPackage) { + return getPackageInfo(aInfo, compatInfo, baseLoader, securityViolation, includeCode, + registerPackage, Process.isSdkSandbox()); + } + + private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, + ClassLoader baseLoader, boolean securityViolation, boolean includeCode, + boolean registerPackage, boolean isSdkSandbox) { + final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); + synchronized (mResourcesManager) { + WeakReference ref; + if (differentUser || isSdkSandbox) { + // Caching not supported across users and for sdk sandboxes + ref = null; + } else if (includeCode) { + ref = mPackages.get(aInfo.packageName); + } else { + ref = mResourcePackages.get(aInfo.packageName); + } + + LoadedApk packageInfo = ref != null ? ref.get() : null; + + if (packageInfo != null) { + if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) { + if (packageInfo.getApplicationInfo().createTimestamp > aInfo.createTimestamp) { + // The cached loaded apk is newer than the one passed in, we should not + // update the cached version + Slog.w(TAG, "getPackageInfo() called with an older ApplicationInfo " + + "than the cached version for package " + aInfo.packageName); + } else { + Slog.v(TAG, "getPackageInfo() caused update to cached ApplicationInfo " + + "for package " + aInfo.packageName); + List oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, aInfo, oldPaths); + packageInfo.updateApplicationInfo(aInfo, oldPaths); + } + } + + return packageInfo; + } + + if (localLOGV) { + Slog.v(TAG, (includeCode ? "Loading code package " + : "Loading resource-only package ") + aInfo.packageName + + " (in " + (mBoundApplication != null + ? mBoundApplication.processName : null) + + ")"); + } + + packageInfo = + new LoadedApk(this, aInfo, compatInfo, baseLoader, + securityViolation, includeCode + && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); + + if (mSystemThread && "android".equals(aInfo.packageName)) { + packageInfo.installSystemApplicationInfo(aInfo, + getSystemContext().mPackageInfo.getClassLoader()); + } + + if (differentUser || isSdkSandbox) { + // Caching not supported across users and for sdk sandboxes + } else if (includeCode) { + mPackages.put(aInfo.packageName, + new WeakReference(packageInfo)); + } else { + mResourcePackages.put(aInfo.packageName, + new WeakReference(packageInfo)); + } + + return packageInfo; + } + } + + private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk, + ApplicationInfo appInfo) { + Resources packageResources = loadedApk.mResources; + boolean resourceDirsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.resourceDirs), + ArrayUtils.defeatNullable(loadedApk.getOverlayDirs())); + boolean overlayPathsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.overlayPaths), + ArrayUtils.defeatNullable(loadedApk.getOverlayPaths())); + + return (packageResources == null || packageResources.getAssets().isUpToDate()) + && resourceDirsUpToDate && overlayPathsUpToDate; + } + + @UnsupportedAppUsage + ActivityThread() { + mResourcesManager = ResourcesManager.getInstance(); + } + + @UnsupportedAppUsage + public ApplicationThread getApplicationThread() + { + return mAppThread; + } + + @UnsupportedAppUsage + public Instrumentation getInstrumentation() + { + return mInstrumentation; + } + + public boolean isProfiling() { + return mProfiler != null && mProfiler.profileFile != null + && mProfiler.profileFd == null; + } + + public String getProfileFilePath() { + return mProfiler.profileFile; + } + + @UnsupportedAppUsage + public Looper getLooper() { + return mLooper; + } + + public Executor getExecutor() { + return mExecutor; + } + + @Override + @UnsupportedAppUsage + public Application getApplication() { + return mInitialApplication; + } + + @UnsupportedAppUsage + public String getProcessName() { + return mBoundApplication.processName; + } + + @Override + @UnsupportedAppUsage + public ContextImpl getSystemContext() { + synchronized (this) { + if (mSystemContext == null) { + mSystemContext = ContextImpl.createSystemContext(this); + } + return mSystemContext; + } + } + + @NonNull + public ContextImpl getSystemUiContext() { + return getSystemUiContext(DEFAULT_DISPLAY); + } + + /** + * Gets the context instance base on system resources & display information which used for UI. + * @param displayId The ID of the display where the UI is shown. + * @see ContextImpl#createSystemUiContext(ContextImpl, int) + */ + @NonNull + public ContextImpl getSystemUiContext(int displayId) { + synchronized (this) { + if (mDisplaySystemUiContexts == null) { + mDisplaySystemUiContexts = new SparseArray<>(); + } + ContextImpl systemUiContext = mDisplaySystemUiContexts.get(displayId); + if (systemUiContext == null) { + systemUiContext = ContextImpl.createSystemUiContext(getSystemContext(), displayId); + mDisplaySystemUiContexts.put(displayId, systemUiContext); + } + return systemUiContext; + } + } + + @Nullable + @Override + public ContextImpl getSystemUiContextNoCreate() { + synchronized (this) { + if (mDisplaySystemUiContexts == null) return null; + return mDisplaySystemUiContexts.get(DEFAULT_DISPLAY); + } + } + + void onSystemUiContextCleanup(ContextImpl context) { + synchronized (this) { + if (mDisplaySystemUiContexts == null) return; + final int index = mDisplaySystemUiContexts.indexOfValue(context); + if (index >= 0) { + mDisplaySystemUiContexts.removeAt(index); + } + } + } + + public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { + synchronized (this) { + getSystemContext().installSystemApplicationInfo(info, classLoader); + getSystemUiContext().installSystemApplicationInfo(info, classLoader); + + // give ourselves a default profiler + mProfiler = new Profiler(); + } + } + + @UnsupportedAppUsage + void scheduleGcIdler() { + if (!mGcIdlerScheduled) { + mGcIdlerScheduled = true; + Looper.myQueue().addIdleHandler(mGcIdler); + } + mH.removeMessages(H.GC_WHEN_IDLE); + } + + void unscheduleGcIdler() { + if (mGcIdlerScheduled) { + mGcIdlerScheduled = false; + Looper.myQueue().removeIdleHandler(mGcIdler); + } + mH.removeMessages(H.GC_WHEN_IDLE); + } + + void schedulePurgeIdler() { + if (!mPurgeIdlerScheduled) { + mPurgeIdlerScheduled = true; + Looper.myQueue().addIdleHandler(mPurgeIdler); + } + mH.removeMessages(H.PURGE_RESOURCES); + } + + void unschedulePurgeIdler() { + if (mPurgeIdlerScheduled) { + mPurgeIdlerScheduled = false; + Looper.myQueue().removeIdleHandler(mPurgeIdler); + } + mH.removeMessages(H.PURGE_RESOURCES); + } + + void doGcIfNeeded() { + doGcIfNeeded("bg"); + } + + void doGcIfNeeded(String reason) { + mGcIdlerScheduled = false; + final long now = SystemClock.uptimeMillis(); + //Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime() + // + "m now=" + now); + if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) { + //Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!"); + BinderInternal.forceGc(reason); + } + } + + private static final String HEAP_FULL_COLUMN = + "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; + private static final String HEAP_COLUMN = + "%13s %8s %8s %8s %8s %8s %8s %8s %8s"; + private static final String ONE_COUNT_COLUMN = "%21s %8d"; + private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; + private static final String THREE_COUNT_COLUMNS = "%21s %8d %21s %8d %21s %8d"; + private static final String TWO_COUNT_COLUMN_HEADER = "%21s %8s %21s %8s"; + private static final String ONE_ALT_COUNT_COLUMN = "%21s %8s %21s %8d"; + + // Formatting for checkin service - update version if row format changes + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4; + + static void printRow(PrintWriter pw, String format, Object...objs) { + pw.println(String.format(Locale.US, format, objs)); + } + + @NeverCompile + public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + int pid, String processName, + long nativeMax, long nativeAllocated, long nativeFree, + long dalvikMax, long dalvikAllocated, long dalvikFree) { + + // For checkin, we print one long comma-separated list of values + if (checkin) { + // NOTE: if you change anything significant below, also consider changing + // ACTIVITY_THREAD_CHECKIN_VERSION. + + // Header + pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); + pw.print(pid); pw.print(','); + pw.print(processName); pw.print(','); + + // Heap info - max + pw.print(nativeMax); pw.print(','); + pw.print(dalvikMax); pw.print(','); + pw.print("N/A,"); + pw.print(nativeMax + dalvikMax); pw.print(','); + + // Heap info - allocated + pw.print(nativeAllocated); pw.print(','); + pw.print(dalvikAllocated); pw.print(','); + pw.print("N/A,"); + pw.print(nativeAllocated + dalvikAllocated); pw.print(','); + + // Heap info - free + pw.print(nativeFree); pw.print(','); + pw.print(dalvikFree); pw.print(','); + pw.print("N/A,"); + pw.print(nativeFree + dalvikFree); pw.print(','); + + // Heap info - proportional set size + pw.print(memInfo.nativePss); pw.print(','); + pw.print(memInfo.dalvikPss); pw.print(','); + pw.print(memInfo.otherPss); pw.print(','); + pw.print(memInfo.getTotalPss()); pw.print(','); + + // Heap info - swappable set size + pw.print(memInfo.nativeSwappablePss); pw.print(','); + pw.print(memInfo.dalvikSwappablePss); pw.print(','); + pw.print(memInfo.otherSwappablePss); pw.print(','); + pw.print(memInfo.getTotalSwappablePss()); pw.print(','); + + // Heap info - shared dirty + pw.print(memInfo.nativeSharedDirty); pw.print(','); + pw.print(memInfo.dalvikSharedDirty); pw.print(','); + pw.print(memInfo.otherSharedDirty); pw.print(','); + pw.print(memInfo.getTotalSharedDirty()); pw.print(','); + + // Heap info - shared clean + pw.print(memInfo.nativeSharedClean); pw.print(','); + pw.print(memInfo.dalvikSharedClean); pw.print(','); + pw.print(memInfo.otherSharedClean); pw.print(','); + pw.print(memInfo.getTotalSharedClean()); pw.print(','); + + // Heap info - private Dirty + pw.print(memInfo.nativePrivateDirty); pw.print(','); + pw.print(memInfo.dalvikPrivateDirty); pw.print(','); + pw.print(memInfo.otherPrivateDirty); pw.print(','); + pw.print(memInfo.getTotalPrivateDirty()); pw.print(','); + + // Heap info - private Clean + pw.print(memInfo.nativePrivateClean); pw.print(','); + pw.print(memInfo.dalvikPrivateClean); pw.print(','); + pw.print(memInfo.otherPrivateClean); pw.print(','); + pw.print(memInfo.getTotalPrivateClean()); pw.print(','); + + // Heap info - swapped out + pw.print(memInfo.nativeSwappedOut); pw.print(','); + pw.print(memInfo.dalvikSwappedOut); pw.print(','); + pw.print(memInfo.otherSwappedOut); pw.print(','); + pw.print(memInfo.getTotalSwappedOut()); pw.print(','); + + // Heap info - swapped out pss + if (memInfo.hasSwappedOutPss) { + pw.print(memInfo.nativeSwappedOutPss); pw.print(','); + pw.print(memInfo.dalvikSwappedOutPss); pw.print(','); + pw.print(memInfo.otherSwappedOutPss); pw.print(','); + pw.print(memInfo.getTotalSwappedOutPss()); pw.print(','); + } else { + pw.print("N/A,"); + pw.print("N/A,"); + pw.print("N/A,"); + pw.print("N/A,"); + } + + // Heap info - other areas + for (int i=0; i list = + mOnPauseListeners.computeIfAbsent(activity, k -> new ArrayList<>()); + list.add(listener); + } + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void unregisterOnActivityPausedListener(Activity activity, + OnActivityPausedListener listener) { + synchronized (mOnPauseListeners) { + ArrayList list = mOnPauseListeners.get(activity); + if (list != null) { + list.remove(listener); + } + } + } + + public final ActivityInfo resolveActivityInfo(Intent intent) { + ActivityInfo aInfo = intent.resolveActivityInfo( + mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES); + if (aInfo == null) { + // Throw an exception. + Instrumentation.checkStartActivityResult( + ActivityManager.START_CLASS_NOT_FOUND, intent); + } + return aInfo; + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public final Activity startActivityNow(Activity parent, String id, + Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, + Activity.NonConfigurationInstances lastNonConfigurationInstances, IBinder assistToken, + IBinder shareableActivityToken) { + ActivityClientRecord r = new ActivityClientRecord(); + r.token = token; + r.assistToken = assistToken; + r.shareableActivityToken = shareableActivityToken; + r.ident = 0; + r.intent = intent; + r.state = state; + r.parent = parent; + r.embeddedID = id; + r.activityInfo = activityInfo; + r.lastNonConfigurationInstances = lastNonConfigurationInstances; + if (localLOGV) { + ComponentName compname = intent.getComponent(); + String name; + if (compname != null) { + name = compname.toShortString(); + } else { + name = "(Intent " + intent + ").getComponent() returned null"; + } + Slog.v(TAG, "Performing launch: action=" + intent.getAction() + + ", comp=" + name + + ", token=" + token); + } + // TODO(lifecycler): Can't switch to use #handleLaunchActivity() because it will try to + // call #reportSizeConfigurations(), but the server might not know anything about the + // activity if it was launched from LocalAcvitivyManager. + return performLaunchActivity(r, null /* customIntent */); + } + + @UnsupportedAppUsage + public final Activity getActivity(IBinder token) { + final ActivityClientRecord activityRecord = mActivities.get(token); + return activityRecord != null ? activityRecord.activity : null; + } + + @Override + public ActivityClientRecord getActivityClient(IBinder token) { + return mActivities.get(token); + } + + @Nullable + @Override + public Context getWindowContext(@NonNull IBinder clientToken) { + return WindowTokenClientController.getInstance().getWindowContext(clientToken); + } + + @VisibleForTesting(visibility = PACKAGE) + public Configuration getConfiguration() { + return mConfigurationController.getConfiguration(); + } + + /** + * @hide + */ + public void addConfigurationChangedListener(Executor executor, + Consumer consumer) { + mConfigurationChangedListenerController.addListener(executor, consumer); + } + + /** + * @hide + */ + public void removeConfigurationChangedListener(Consumer consumer) { + mConfigurationChangedListenerController.removeListener(consumer); + } + + @Override + public void updatePendingConfiguration(Configuration config) { + final Configuration updatedConfig = + mConfigurationController.updatePendingConfiguration(config); + // This is only done to maintain @UnsupportedAppUsage and should be removed someday. + if (updatedConfig != null) { + mPendingConfiguration = updatedConfig; + } + } + + @Override + public void updateProcessState(int processState, boolean fromIpc) { + synchronized (mAppThread) { + if (mLastProcessState == processState) { + return; + } + // Do not issue a transitional GC if we are transitioning between 2 cached states. + // Only update if the state flips between cached and uncached or vice versa + if (ActivityManager.isProcStateCached(mLastProcessState) + != ActivityManager.isProcStateCached(processState)) { + updateVmProcessState(processState); + } + mLastProcessState = processState; + if (localLOGV) { + Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState + + (fromIpc ? " (from ipc" : "")); + } + } + } + + /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ + // Currently ART VM only uses state updates for Transitional GC, and thus + // this function initiates a Transitional GC for transitions into Cached apps states. + private void updateVmProcessState(int processState) { + // Only a transition into Cached state should result in a Transitional GC request + // to the ART runtime. Update VM state to JANK_IMPERCEPTIBLE in that case. + // Note that there are 4 possible cached states currently, all of which are + // JANK_IMPERCEPTIBLE from GC point of view. + final int state = ActivityManager.isProcStateCached(processState) + ? VM_PROCESS_STATE_JANK_IMPERCEPTIBLE + : VM_PROCESS_STATE_JANK_PERCEPTIBLE; + VMRuntime.getRuntime().updateProcessState(state); + } + + @Override + public void countLaunchingActivities(int num) { + mNumLaunchingActivities.getAndAdd(num); + } + + @UnsupportedAppUsage + public void sendActivityResult( + IBinder activityToken, String id, int requestCode, + int resultCode, Intent data) { + if (DEBUG_RESULTS) Slog.v(TAG, "sendActivityResult: id=" + id + + " req=" + requestCode + " res=" + resultCode + " data=" + data); + final ArrayList list = new ArrayList<>(); + list.add(new ResultInfo(id, requestCode, resultCode, data, activityToken)); + final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread); + final ActivityResultItem activityResultItem = ActivityResultItem.obtain( + activityToken, list); + clientTransaction.addTransactionItem(activityResultItem); + try { + mAppThread.scheduleTransaction(clientTransaction); + } catch (RemoteException e) { + // Local scheduling + } + } + + @Override + TransactionExecutor getTransactionExecutor() { + return mTransactionExecutor; + } + + void sendMessage(int what, Object obj) { + sendMessage(what, obj, 0, 0, false); + } + + private void sendMessage(int what, Object obj, int arg1) { + sendMessage(what, obj, arg1, 0, false); + } + + private void sendMessage(int what, Object obj, int arg1, int arg2) { + sendMessage(what, obj, arg1, arg2, false); + } + + private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { + if (DEBUG_MESSAGES) { + Slog.v(TAG, + "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj); + } + Message msg = Message.obtain(); + msg.what = what; + msg.obj = obj; + msg.arg1 = arg1; + msg.arg2 = arg2; + if (async) { + msg.setAsynchronous(true); + } + mH.sendMessage(msg); + } + + final void scheduleContextCleanup(ContextImpl context, String who, + String what) { + ContextCleanupInfo cci = new ContextCleanupInfo(); + cci.context = context; + cci.who = who; + cci.what = what; + sendMessage(H.CLEAN_UP_CONTEXT, cci); + } + + /** Core implementation of activity launch. */ + private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { + ActivityInfo aInfo = r.activityInfo; + + if (getInstrumentation() != null + && getInstrumentation().getContext() != null + && getInstrumentation().getContext().getApplicationInfo() != null + && getInstrumentation().isSdkSandboxAllowedToStartActivities()) { + // Activities launched from CTS-in-sandbox tests use a customized ApplicationInfo. See + // also {@link SdkSandboxManagerLocal#getSdkSandboxApplicationInfoForInstrumentation}. + r.packageInfo = + getPackageInfo( + getInstrumentation().getContext().getApplicationInfo(), + mCompatibilityInfo, + Context.CONTEXT_INCLUDE_CODE); + } else if (r.packageInfo == null) { + r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo, + Context.CONTEXT_INCLUDE_CODE); + } + + ComponentName component = r.intent.getComponent(); + if (component == null) { + component = r.intent.resolveActivity( + mInitialApplication.getPackageManager()); + r.intent.setComponent(component); + } + + if (r.activityInfo.targetActivity != null) { + component = new ComponentName(r.activityInfo.packageName, + r.activityInfo.targetActivity); + } + + boolean isSandboxActivityContext = + sandboxActivitySdkBasedContext() + && SdkSandboxActivityAuthority.isSdkSandboxActivityIntent( + mSystemContext, r.intent); + boolean isSandboxedSdkContextUsed = false; + ContextImpl activityBaseContext; + if (isSandboxActivityContext) { + activityBaseContext = createBaseContextForSandboxActivity(r); + if (activityBaseContext == null) { + // Failed to retrieve the SDK based sandbox activity context, falling back to the + // app based context. + activityBaseContext = createBaseContextForActivity(r); + } else { + isSandboxedSdkContextUsed = true; + } + } else { + activityBaseContext = createBaseContextForActivity(r); + } + Activity activity = null; + try { + java.lang.ClassLoader cl; + if (isSandboxedSdkContextUsed) { + // In case of sandbox activity, the context refers to the an SDK with no visibility + // on the SandboxedActivity java class, the App context should be used instead. + cl = activityBaseContext.getApplicationContext().getClassLoader(); + } else { + cl = activityBaseContext.getClassLoader(); + } + activity = mInstrumentation.newActivity( + cl, component.getClassName(), r.intent); + StrictMode.incrementExpectedActivityCount(activity.getClass()); + r.intent.setExtrasClassLoader(cl); + r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo), + activityBaseContext.getAttributionSource()); + if (r.state != null) { + r.state.setClassLoader(cl); + } + } catch (Exception e) { + if (!mInstrumentation.onException(activity, e)) { + throw new RuntimeException( + "Unable to instantiate activity " + component + + ": " + e.toString(), e); + } + } + + try { + Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation); + + if (localLOGV) Slog.v(TAG, "Performing launch of " + r); + if (localLOGV) Slog.v( + TAG, r + ": app=" + app + + ", appName=" + app.getPackageName() + + ", pkg=" + r.packageInfo.getPackageName() + + ", comp=" + r.intent.getComponent().toShortString() + + ", dir=" + r.packageInfo.getAppDir()); + + // updatePendingActivityConfiguration() reads from mActivities to update + // ActivityClientRecord which runs in a different thread. Protect modifications to + // mActivities to avoid race. + synchronized (mResourcesManager) { + mActivities.put(r.token, r); + } + + if (activity != null) { + CharSequence title = + r.activityInfo.loadLabel(activityBaseContext.getPackageManager()); + Configuration config = + new Configuration(mConfigurationController.getCompatConfiguration()); + if (r.overrideConfig != null) { + config.updateFrom(r.overrideConfig); + } + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + + r.activityInfo.name + " with config " + config); + Window window = null; + if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { + window = r.mPendingRemoveWindow; + r.mPendingRemoveWindow = null; + r.mPendingRemoveWindowManager = null; + } + + // Activity resources must be initialized with the same loaders as the + // application context. + activityBaseContext.getResources().addLoaders( + app.getResources().getLoaders().toArray(new ResourcesLoader[0])); + + activityBaseContext.setOuterContext(activity); + activity.attach(activityBaseContext, this, getInstrumentation(), r.token, + r.ident, app, r.intent, r.activityInfo, title, r.parent, + r.embeddedID, r.lastNonConfigurationInstances, config, + r.referrer, r.voiceInteractor, window, r.activityConfigCallback, + r.assistToken, r.shareableActivityToken, r.initialCallerInfoAccessToken); + + if (customIntent != null) { + activity.mIntent = customIntent; + } + r.lastNonConfigurationInstances = null; + checkAndBlockForNetworkAccess(); + activity.mStartedActivity = false; + int theme = r.activityInfo.getThemeResource(); + if (theme != 0) { + activity.setTheme(theme); + } + + if (r.mSceneTransitionInfo != null) { + activity.mSceneTransitionInfo = r.mSceneTransitionInfo; + r.mSceneTransitionInfo = null; + } + activity.mLaunchedFromBubble = r.mLaunchedFromBubble; + activity.mCalled = false; + // Assigning the activity to the record before calling onCreate() allows + // ActivityThread#getActivity() lookup for the callbacks triggered from + // ActivityLifecycleCallbacks#onActivityCreated() or + // ActivityLifecycleCallback#onActivityPostCreated(). + r.activity = activity; + if (r.isPersistable()) { + mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); + } else { + mInstrumentation.callActivityOnCreate(activity, r.state); + } + if (!activity.mCalled) { + throw new SuperNotCalledException( + "Activity " + r.intent.getComponent().toShortString() + + " did not call through to super.onCreate()"); + } + r.mLastReportedWindowingMode = config.windowConfiguration.getWindowingMode(); + } + r.setState(ON_CREATE); + + } catch (SuperNotCalledException e) { + throw e; + + } catch (Exception e) { + if (!mInstrumentation.onException(activity, e)) { + throw new RuntimeException( + "Unable to start activity " + component + + ": " + e.toString(), e); + } + } + + return activity; + } + + @Override + public void handleStartActivity(ActivityClientRecord r, + PendingTransactionActions pendingActions, SceneTransitionInfo sceneTransitionInfo) { + final Activity activity = r.activity; + if (!r.stopped) { + throw new IllegalStateException("Can't start activity that is not stopped."); + } + if (r.activity.mFinished) { + // TODO(lifecycler): How can this happen? + return; + } + + unscheduleGcIdler(); + if (sceneTransitionInfo != null) { + activity.mSceneTransitionInfo = sceneTransitionInfo; + } + + // Start + activity.performStart("handleStartActivity"); + r.setState(ON_START); + + if (pendingActions == null) { + // No more work to do. + return; + } + + // Restore instance state + if (pendingActions.shouldRestoreInstanceState()) { + if (r.isPersistable()) { + if (r.state != null || r.persistentState != null) { + mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, + r.persistentState); + } + } else if (r.state != null) { + mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); + } + } + + // Call postOnCreate() + if (pendingActions.shouldCallOnPostCreate()) { + activity.mCalled = false; + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "onPostCreate"); + if (r.isPersistable()) { + mInstrumentation.callActivityOnPostCreate(activity, r.state, + r.persistentState); + } else { + mInstrumentation.callActivityOnPostCreate(activity, r.state); + } + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + if (!activity.mCalled) { + throw new SuperNotCalledException( + "Activity " + r.intent.getComponent().toShortString() + + " did not call through to super.onPostCreate()"); + } + } + + updateVisibility(r, true /* show */); + mSomeActivitiesChanged = true; + } + + /** + * Checks if {@link #mNetworkBlockSeq} is {@link #INVALID_PROC_STATE_SEQ} and if so, returns + * immediately. Otherwise, makes a blocking call to ActivityManagerService to wait for the + * network rules to get updated. + */ + private void checkAndBlockForNetworkAccess() { + synchronized (mNetworkPolicyLock) { + if (mNetworkBlockSeq != INVALID_PROC_STATE_SEQ) { + try { + ActivityManager.getService().waitForNetworkStateUpdate(mNetworkBlockSeq); + mNetworkBlockSeq = INVALID_PROC_STATE_SEQ; + } catch (RemoteException ignored) {} + } + } + } + + private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { + final int displayId = ActivityClient.getInstance().getDisplayId(r.token); + ContextImpl appContext = ContextImpl.createActivityContext( + this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); + + final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); + // For debugging purposes, if the activity's package name contains the value of + // the "debug.use-second-display" system property as a substring, then show + // its content on a secondary display if there is one. + String pkgName = SystemProperties.get("debug.second-display.pkg"); + if (pkgName != null && !pkgName.isEmpty() + && r.packageInfo.mPackageName.contains(pkgName)) { + for (int id : dm.getDisplayIds()) { + if (id != DEFAULT_DISPLAY) { + Display display = + dm.getCompatibleDisplay(id, appContext.getResources()); + appContext = (ContextImpl) appContext.createDisplayContext(display); + break; + } + } + } + return appContext; + } + + /** + * Creates the base context for the sandbox activity based on its corresponding SDK {@link + * ApplicationInfo} and flags. + */ + @Nullable + private ContextImpl createBaseContextForSandboxActivity(@NonNull ActivityClientRecord r) { + SdkSandboxActivityAuthority sdkSandboxActivityAuthority = + SdkSandboxActivityAuthority.getInstance(); + + ActivityContextInfo contextInfo; + try { + contextInfo = sdkSandboxActivityAuthority.getActivityContextInfo(r.intent); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Passed intent does not match an expected sandbox activity", e); + return null; + } catch (IllegalStateException e) { + Log.e(TAG, "SDK customized context flag is disabled", e); + return null; + } catch (Exception e) { // generic catch to unexpected exceptions + Log.e(TAG, "Failed to create context for sandbox activity", e); + return null; + } + + final int displayId = ActivityClient.getInstance().getDisplayId(r.token); + final LoadedApk sdkApk = getPackageInfo( + contextInfo.getSdkApplicationInfo(), + r.packageInfo.getCompatibilityInfo(), + contextInfo.getContextFlags()); + + final ContextImpl activityContext = ContextImpl.createActivityContext( + this, sdkApk, r.activityInfo, r.token, displayId, r.overrideConfig); + + // Set sandbox app's context as the application context for sdk context + activityContext.mPackageInfo.makeApplicationInner( + /*forceDefaultAppClass=*/false, mInstrumentation); + + return activityContext; + } + + /** + * Extended implementation of activity launch. Used when server requests a launch or relaunch. + */ + @Override + public Activity handleLaunchActivity(ActivityClientRecord r, + PendingTransactionActions pendingActions, int deviceId, Intent customIntent) { + // If we are getting ready to gc after going to the background, well + // we are back active so skip it. + unscheduleGcIdler(); + mSomeActivitiesChanged = true; + + if (r.profilerInfo != null) { + mProfiler.setProfiler(r.profilerInfo); + mProfiler.startProfiling(); + } + + // Make sure we are running with the most recent config and resource paths. + applyPendingApplicationInfoChanges(r.activityInfo.packageName); + mConfigurationController.handleConfigurationChanged(null, null); + updateDeviceIdForNonUIContexts(deviceId); + + if (localLOGV) Slog.v( + TAG, "Handling launch of " + r); + + // Initialize before creating the activity + if (ThreadedRenderer.sRendererEnabled + && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { + HardwareRenderer.preload(); + } + WindowManagerGlobal.initialize(); + + // Hint the GraphicsEnvironment that an activity is launching on the process. + GraphicsEnvironment.hintActivityLaunch(); + + final Activity a = performLaunchActivity(r, customIntent); + + if (a != null) { + r.createdConfig = new Configuration(mConfigurationController.getConfiguration()); + reportSizeConfigurations(r); + if (!r.activity.mFinished && pendingActions != null) { + pendingActions.setOldState(r.state); + pendingActions.setRestoreInstanceState(true); + pendingActions.setCallOnPostCreate(true); + } + } else { + // If there was an error, for any reason, tell the activity manager to stop us. + ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED, + null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); + } + + return a; + } + + private void reportSizeConfigurations(ActivityClientRecord r) { + if (mActivitiesToBeDestroyed.containsKey(r.token)) { + // Size configurations of a destroyed activity is meaningless. + return; + } + Configuration[] configurations = r.activity.getResources().getSizeConfigurations(); + if (configurations == null) { + return; + } + r.mSizeConfigurations = new SizeConfigurationBuckets(configurations); + ActivityClient.getInstance().reportSizeConfigurations(r.token, r.mSizeConfigurations); + } + + private void deliverNewIntents(ActivityClientRecord r, List intents) { + final int N = intents.size(); + for (int i=0; i intents) { + checkAndBlockForNetworkAccess(); + deliverNewIntents(r, intents); + } + + public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) { + // Filling for autofill has a few differences: + // - it does not need an AssistContent + // - it does not call onProvideAssistData() + // - it needs an IAutoFillCallback + boolean forAutofill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTOFILL; + // When only the AssistContent is requested, omit the AsssistStructure + boolean requestedOnlyContent = cmd.requestType == ActivityManager.ASSIST_CONTEXT_CONTENT; + + // TODO: decide if lastSessionId logic applies to autofill sessions + if (mLastSessionId != cmd.sessionId) { + // Clear the existing structures + mLastSessionId = cmd.sessionId; + for (int i = mLastAssistStructures.size() - 1; i >= 0; i--) { + AssistStructure structure = mLastAssistStructures.get(i).get(); + if (structure != null) { + structure.clearSendChannel(); + } + mLastAssistStructures.remove(i); + } + } + + Bundle data = new Bundle(); + AssistStructure structure = null; + AssistContent content = forAutofill ? null : new AssistContent(); + final long startTime = SystemClock.uptimeMillis(); + ActivityClientRecord r = mActivities.get(cmd.activityToken); + Uri referrer = null; + if (r != null) { + if (!forAutofill) { + r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data); + r.activity.onProvideAssistData(data); + referrer = r.activity.onProvideReferrer(); + } + if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutofill + || requestedOnlyContent) { + if (!requestedOnlyContent) { + structure = new AssistStructure(r.activity, forAutofill, cmd.flags); + } + Intent activityIntent = r.activity.getIntent(); + boolean notSecure = r.window == null || + (r.window.getAttributes().flags + & WindowManager.LayoutParams.FLAG_SECURE) == 0; + if (activityIntent != null && notSecure) { + if (!forAutofill) { + Intent intent = new Intent(activityIntent); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + content.setDefaultIntent(intent); + } + } else { + if (!forAutofill) { + content.setDefaultIntent(new Intent()); + } + } + if (!forAutofill) { + r.activity.onProvideAssistContent(content); + } + } + } + + if (!requestedOnlyContent) { + if (structure == null) { + structure = new AssistStructure(); + } + + // TODO: decide if lastSessionId logic applies to autofill sessions + + structure.setAcquisitionStartTime(startTime); + structure.setAcquisitionEndTime(SystemClock.uptimeMillis()); + + mLastAssistStructures.add(new WeakReference<>(structure)); + } + + IActivityTaskManager mgr = ActivityTaskManager.getService(); + try { + mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content, referrer); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** Fetches the user actions for the corresponding activity */ + private void handleRequestDirectActions(@NonNull IBinder activityToken, + @NonNull IVoiceInteractor interactor, @NonNull CancellationSignal cancellationSignal, + @NonNull RemoteCallback callback, int retryCount) { + final ActivityClientRecord r = mActivities.get(activityToken); + if (r == null) { + Log.w(TAG, "requestDirectActions(): no activity for " + activityToken); + callback.sendResult(null); + return; + } + final int lifecycleState = r.getLifecycleState(); + if (lifecycleState < ON_START) { + // TODO(b/234173463): requestDirectActions callback should indicate errors + if (retryCount > 0) { + mH.sendMessageDelayed( + PooledLambda.obtainMessage(ActivityThread::handleRequestDirectActions, + ActivityThread.this, activityToken, interactor, cancellationSignal, + callback, retryCount - 1), REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS); + return; + } + Log.w(TAG, "requestDirectActions(" + r + "): wrong lifecycle: " + lifecycleState); + callback.sendResult(null); + return; + } + if (lifecycleState >= ON_STOP) { + Log.w(TAG, "requestDirectActions(" + r + "): wrong lifecycle: " + lifecycleState); + callback.sendResult(null); + return; + } + if (r.activity.mVoiceInteractor == null + || r.activity.mVoiceInteractor.mInteractor.asBinder() + != interactor.asBinder()) { + if (r.activity.mVoiceInteractor != null) { + r.activity.mVoiceInteractor.destroy(); + } + r.activity.mVoiceInteractor = new VoiceInteractor(interactor, r.activity, + r.activity, Looper.myLooper()); + } + r.activity.onGetDirectActions(cancellationSignal, (actions) -> { + Objects.requireNonNull(actions); + Preconditions.checkCollectionElementsNotNull(actions, "actions"); + if (!actions.isEmpty()) { + final int actionCount = actions.size(); + for (int i = 0; i < actionCount; i++) { + final DirectAction action = actions.get(i); + action.setSource(r.activity.getTaskId(), r.activity.getAssistToken()); + } + final Bundle result = new Bundle(); + result.putParcelable(DirectAction.KEY_ACTIONS_LIST, + new ParceledListSlice<>(actions)); + callback.sendResult(result); + } else { + callback.sendResult(null); + } + }); + } + + /** Performs an actions in the corresponding activity */ + private void handlePerformDirectAction(@NonNull IBinder activityToken, + @NonNull String actionId, @Nullable Bundle arguments, + @NonNull CancellationSignal cancellationSignal, + @NonNull RemoteCallback resultCallback) { + final ActivityClientRecord r = mActivities.get(activityToken); + if (r != null) { + final int lifecycleState = r.getLifecycleState(); + if (lifecycleState < ON_START || lifecycleState >= ON_STOP) { + resultCallback.sendResult(null); + return; + } + final Bundle nonNullArguments = (arguments != null) ? arguments : Bundle.EMPTY; + r.activity.onPerformDirectAction(actionId, nonNullArguments, cancellationSignal, + resultCallback::sendResult); + } else { + resultCallback.sendResult(null); + } + } + + public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) { + ActivityClientRecord r = mActivities.get(token); + if (r != null) { + r.activity.onTranslucentConversionComplete(drawComplete); + } + } + + public void onNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) { + ActivityClientRecord r = mActivities.get(token); + if (r != null) { + r.activity.onNewSceneTransitionInfo(info); + } + } + + public void handleInstallProvider(ProviderInfo info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + installContentProviders(mInitialApplication, Arrays.asList(info)); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + } + + private void handleEnterAnimationComplete(IBinder token) { + ActivityClientRecord r = mActivities.get(token); + if (r != null) { + r.activity.dispatchEnterAnimationComplete(); + } + } + + private void handleStartBinderTracking() { + Binder.enableStackTracking(); + } + + private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) { + try { + Binder.disableStackTracking(); + Binder.getTransactionTracker().writeTracesToFile(fd); + } finally { + IoUtils.closeQuietly(fd); + Binder.getTransactionTracker().clearTraces(); + } + } + + @Override + public void handlePictureInPictureRequested(ActivityClientRecord r) { + final boolean receivedByApp = r.activity.onPictureInPictureRequested(); + if (!receivedByApp) { + // Previous recommendation was for apps to enter picture-in-picture in + // onUserLeavingHint() for cases such as the app being put into the background. For + // backwards compatibility with apps that are not using the newer + // onPictureInPictureRequested() callback, we schedule the life cycle events needed to + // trigger onUserLeavingHint(), then we return the activity to its previous state. + schedulePauseWithUserLeaveHintAndReturnToCurrentState(r); + } + } + + @Override + public void handlePictureInPictureStateChanged(@NonNull ActivityClientRecord r, + PictureInPictureUiState pipState) { + r.activity.onPictureInPictureUiStateChanged(pipState); + } + + /** + * Register a splash screen manager to this process. + */ + public void registerSplashScreenManager( + @NonNull SplashScreen.SplashScreenManagerGlobal manager) { + synchronized (this) { + mSplashScreenGlobal = manager; + } + } + + @Override + public boolean isHandleSplashScreenExit(@NonNull IBinder token) { + synchronized (this) { + return mSplashScreenGlobal != null && mSplashScreenGlobal.containsExitListener(token); + } + } + + @Override + public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r, + @Nullable SplashScreenView.SplashScreenViewParcelable parcelable, + @NonNull SurfaceControl startingWindowLeash) { + final DecorView decorView = (DecorView) r.window.peekDecorView(); + if (parcelable != null && decorView != null) { + createSplashScreen(r, decorView, parcelable, startingWindowLeash); + } else { + // shouldn't happen! + Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach"); + } + } + + private void createSplashScreen(ActivityClientRecord r, DecorView decorView, + SplashScreenView.SplashScreenViewParcelable parcelable, + @NonNull SurfaceControl startingWindowLeash) { + final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity); + final SplashScreenView view = builder.createFromParcel(parcelable).build(); + view.attachHostWindow(r.window); + decorView.addView(view); + view.requestLayout(); + + view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + private boolean mHandled = false; + @Override + public boolean onPreDraw() { + if (mHandled) { + return true; + } + mHandled = true; + // Transfer the splash screen view from shell to client. + // Call syncTransferSplashscreenViewTransaction at the first onPreDraw, so we can + // ensure the client view is ready to show, and can use applyTransactionOnDraw to + // make all transitions happen at the same frame. + syncTransferSplashscreenViewTransaction( + view, r.token, decorView, startingWindowLeash); + view.post(() -> view.getViewTreeObserver().removeOnPreDrawListener(this)); + return true; + } + }); + } + + private void reportSplashscreenViewShown(IBinder token, SplashScreenView view) { + ActivityClient.getInstance().reportSplashScreenAttached(token); + synchronized (this) { + if (mSplashScreenGlobal != null) { + mSplashScreenGlobal.handOverSplashScreenView(token, view); + } + } + } + + private void syncTransferSplashscreenViewTransaction(SplashScreenView view, IBinder token, + View decorView, @NonNull SurfaceControl startingWindowLeash) { + // Ensure splash screen view is shown before remove the splash screen window. + // Once the copied splash screen view is onDrawn on decor view, use applyTransactionOnDraw + // to ensure the transfer of surface view and hide starting window are happen at the same + // frame. + final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.hide(startingWindowLeash); + + decorView.getViewRootImpl().applyTransactionOnDraw(transaction); + view.syncTransferSurfaceOnDraw(); + // Tell server we can remove the starting window + decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view)); + } + + /** + * Cycle activity through onPause and onUserLeaveHint so that PIP is entered if supported, then + * return to its previous state. This allows activities that rely on onUserLeaveHint instead of + * onPictureInPictureRequested to enter picture-in-picture. + */ + private void schedulePauseWithUserLeaveHintAndReturnToCurrentState(ActivityClientRecord r) { + final int prevState = r.getLifecycleState(); + if (prevState != ON_RESUME && prevState != ON_PAUSE) { + return; + } + + switch (prevState) { + case ON_RESUME: + // Schedule a PAUSE then return to RESUME. + schedulePauseWithUserLeavingHint(r); + scheduleResume(r); + break; + case ON_PAUSE: + // Schedule a RESUME then return to PAUSE. + scheduleResume(r); + schedulePauseWithUserLeavingHint(r); + break; + } + } + + private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) { + final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); + final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token, + r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags, + /* dontReport */ false, /* autoEnteringPip */ false); + transaction.addTransactionItem(pauseActivityItem); + executeTransaction(transaction); + } + + private void scheduleResume(ActivityClientRecord r) { + final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token, + /* isForward */ false, /* shouldSendCompatFakeFocus */ false); + transaction.addTransactionItem(resumeActivityItem); + executeTransaction(transaction); + } + + private void handleLocalVoiceInteractionStarted(IBinder token, IVoiceInteractor interactor) { + final ActivityClientRecord r = mActivities.get(token); + if (r != null) { + r.voiceInteractor = interactor; + r.activity.setVoiceInteractor(interactor); + if (interactor == null) { + r.activity.onLocalVoiceInteractionStopped(); + } else { + r.activity.onLocalVoiceInteractionStarted(); + } + } + } + + private static boolean attemptAttachAgent(String agent, ClassLoader classLoader) { + try { + VMDebug.attachAgent(agent, classLoader); + return true; + } catch (IOException e) { + Slog.e(TAG, "Attaching agent with " + classLoader + " failed: " + agent); + return false; + } + } + + static void handleAttachAgent(String agent, LoadedApk loadedApk) { + ClassLoader classLoader = loadedApk != null ? loadedApk.getClassLoader() : null; + if (attemptAttachAgent(agent, classLoader)) { + return; + } + if (classLoader != null) { + attemptAttachAgent(agent, null); + } + } + + static void handleAttachStartupAgents(String dataDir) { + try { + Path codeCache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath(); + if (!Files.exists(codeCache)) { + return; + } + Path startupPath = codeCache.resolve("startup_agents"); + if (Files.exists(startupPath)) { + try (DirectoryStream startupFiles = Files.newDirectoryStream(startupPath)) { + for (Path p : startupFiles) { + handleAttachAgent( + p.toAbsolutePath().toString() + + "=" + + dataDir, + null); + } + } + } + } catch (Exception e) { + // Ignored. + } + } + + private void updateUiTranslationState(IBinder activityToken, int state, + TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds, + UiTranslationSpec uiTranslationSpec) { + final ActivityClientRecord r = mActivities.get(activityToken); + if (r == null) { + Log.w(TAG, "updateUiTranslationState(): no activity for " + activityToken); + return; + } + r.activity.updateUiTranslationState( + state, sourceSpec, targetSpec, viewIds, uiTranslationSpec); + } + + private static final ThreadLocal sCurrentBroadcastIntent = new ThreadLocal(); + + /** + * Return the Intent that's currently being handled by a + * BroadcastReceiver on this thread, or null if none. + * @hide + */ + public static Intent getIntentBeingBroadcast() { + return sCurrentBroadcastIntent.get(); + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + private void handleReceiver(ReceiverData data) { + // If we are getting ready to gc after going to the background, well + // we are back active so skip it. + unscheduleGcIdler(); + + String component = data.intent.getComponent().getClassName(); + + final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo); + + IActivityManager mgr = ActivityManager.getService(); + + Application app; + BroadcastReceiver receiver; + ContextImpl context; + try { + app = packageInfo.makeApplicationInner(false, mInstrumentation); + context = (ContextImpl) app.getBaseContext(); + if (data.info.splitName != null) { + context = (ContextImpl) context.createContextForSplit(data.info.splitName); + } + if (data.info.attributionTags != null && data.info.attributionTags.length > 0) { + final String attributionTag = data.info.attributionTags[0]; + context = (ContextImpl) context.createAttributionContext(attributionTag); + } + java.lang.ClassLoader cl = context.getClassLoader(); + data.intent.setExtrasClassLoader(cl); + data.intent.prepareToEnterProcess( + isProtectedComponent(data.info) || isProtectedBroadcast(data.intent), + context.getAttributionSource()); + data.setExtrasClassLoader(cl); + receiver = packageInfo.getAppFactory() + .instantiateReceiver(cl, data.info.name, data.intent); + } catch (Exception e) { + if (DEBUG_BROADCAST) Slog.i(TAG, + "Finishing failed broadcast to " + data.intent.getComponent()); + data.sendFinished(mgr); + throw new RuntimeException( + "Unable to instantiate receiver " + component + + ": " + e.toString(), e); + } + + try { + if (localLOGV) Slog.v( + TAG, "Performing receive of " + data.intent + + ": app=" + app + + ", appName=" + app.getPackageName() + + ", pkg=" + packageInfo.getPackageName() + + ", comp=" + data.intent.getComponent().toShortString() + + ", dir=" + packageInfo.getAppDir()); + + sCurrentBroadcastIntent.set(data.intent); + receiver.setPendingResult(data); + receiver.onReceive(context.getReceiverRestrictedContext(), + data.intent); + } catch (Exception e) { + if (DEBUG_BROADCAST) Slog.i(TAG, + "Finishing failed broadcast to " + data.intent.getComponent()); + data.sendFinished(mgr); + if (!mInstrumentation.onException(receiver, e)) { + throw new RuntimeException( + "Unable to start receiver " + component + + ": " + e.toString(), e); + } + } finally { + sCurrentBroadcastIntent.set(null); + } + + if (receiver.getPendingResult() != null) { + data.finish(); + } + } + + // Instantiate a BackupAgent and tell it that it's alive + private void handleCreateBackupAgent(CreateBackupAgentData data) { + if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data); + + // Validity check the requested target package's uid against ours + try { + PackageInfo requestedPackage = getPackageManager().getPackageInfo( + data.appInfo.packageName, 0, UserHandle.myUserId()); + if (requestedPackage.applicationInfo.uid != Process.myUid()) { + Slog.w(TAG, "Asked to instantiate non-matching package " + + data.appInfo.packageName); + return; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + // no longer idle; we have backup work to do + unscheduleGcIdler(); + + // instantiate the BackupAgent class named in the manifest + final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo); + String packageName = packageInfo.mPackageName; + if (packageName == null) { + Slog.d(TAG, "Asked to create backup agent for nonexistent package"); + return; + } + + String classname = getBackupAgentName(data); + + try { + IBinder binder = null; + ArrayMap backupAgents = getBackupAgentsForUser(data.userId); + BackupAgent agent = backupAgents.get(packageName); + if (agent != null) { + // reusing the existing instance + if (DEBUG_BACKUP) { + Slog.v(TAG, "Reusing existing agent instance"); + } + binder = agent.onBind(); + } else { + try { + if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname); + + java.lang.ClassLoader cl = packageInfo.getClassLoader(); + agent = (BackupAgent) cl.loadClass(classname).newInstance(); + + // set up the agent's context + ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + context.setOuterContext(agent); + agent.attach(context); + + agent.onCreate(UserHandle.of(data.userId), data.backupDestination, + getOperationTypeFromBackupMode(data.backupMode)); + binder = agent.onBind(); + backupAgents.put(packageName, agent); + } catch (Exception e) { + // If this is during restore, fail silently; otherwise go + // ahead and let the user see the crash. + Slog.e(TAG, "Agent threw during creation: " + e); + if (data.backupMode != ApplicationThreadConstants.BACKUP_MODE_RESTORE + && data.backupMode != + ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL) { + throw e; + } + // falling through with 'binder' still null + } + } + + // tell the OS that we're live now + try { + ActivityManager.getService().backupAgentCreated(packageName, binder, data.userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } catch (Exception e) { + throw new RuntimeException("Unable to create BackupAgent " + + classname + ": " + e.toString(), e); + } + } + + @OperationType + private static int getOperationTypeFromBackupMode(int backupMode) { + switch (backupMode) { + case ApplicationThreadConstants.BACKUP_MODE_RESTORE: + case ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL: + return OperationType.RESTORE; + case ApplicationThreadConstants.BACKUP_MODE_FULL: + case ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL: + return OperationType.BACKUP; + default: + Slog.w(TAG, "Invalid backup mode when initialising BackupAgent: " + + backupMode); + return OperationType.UNKNOWN; + } + } + + private String getBackupAgentName(CreateBackupAgentData data) { + String agentName = data.appInfo.backupAgentName; + // full backup operation but no app-supplied agent? use the default implementation + if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL + || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) { + agentName = DEFAULT_FULL_BACKUP_AGENT; + } + return agentName; + } + + // Tear down a BackupAgent + private void handleDestroyBackupAgent(CreateBackupAgentData data) { + if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data); + + final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo); + String packageName = packageInfo.mPackageName; + ArrayMap backupAgents = getBackupAgentsForUser(data.userId); + BackupAgent agent = backupAgents.get(packageName); + if (agent != null) { + try { + agent.onDestroy(); + } catch (Exception e) { + Slog.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo); + e.printStackTrace(); + } + backupAgents.remove(packageName); + } else { + Slog.w(TAG, "Attempt to destroy unknown backup agent " + data); + } + } + + private ArrayMap getBackupAgentsForUser(int userId) { + ArrayMap backupAgents = mBackupAgentsByUser.get(userId); + if (backupAgents == null) { + backupAgents = new ArrayMap<>(); + mBackupAgentsByUser.put(userId, backupAgents); + } + return backupAgents; + } + + @UnsupportedAppUsage + private void handleCreateService(CreateServiceData data) { + // If we are getting ready to gc after going to the background, well + // we are back active so skip it. + unscheduleGcIdler(); + + final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo); + Service service = null; + try { + if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); + + Application app = packageInfo.makeApplicationInner(false, mInstrumentation); + + final java.lang.ClassLoader cl; + if (data.info.splitName != null) { + cl = packageInfo.getSplitClassLoader(data.info.splitName); + } else { + cl = packageInfo.getClassLoader(); + } + service = packageInfo.getAppFactory() + .instantiateService(cl, data.info.name, data.intent); + ContextImpl context = ContextImpl.getImpl(service + .createServiceBaseContext(this, packageInfo)); + if (data.info.splitName != null) { + context = (ContextImpl) context.createContextForSplit(data.info.splitName); + } + if (data.info.attributionTags != null && data.info.attributionTags.length > 0) { + final String attributionTag = data.info.attributionTags[0]; + context = (ContextImpl) context.createAttributionContext(attributionTag); + } + // Service resources must be initialized with the same loaders as the application + // context. + context.getResources().addLoaders( + app.getResources().getLoaders().toArray(new ResourcesLoader[0])); + + context.setOuterContext(service); + service.attach(context, this, data.info.name, data.token, app, + ActivityManager.getService()); + if (!service.isUiContext()) { // WindowProviderService is a UI Context. + if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT) { + service.updateDeviceId(mLastReportedDeviceId); + } else { + VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class); + if (vdm != null && vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) { + service.updateDeviceId(mLastReportedDeviceId); + } + } + } + service.onCreate(); + mServicesData.put(data.token, data); + mServices.put(data.token, service); + try { + ActivityManager.getService().serviceDoneExecuting( + data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0, null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } catch (Exception e) { + if (!mInstrumentation.onException(service, e)) { + throw new RuntimeException( + "Unable to create service " + data.info.name + + ": " + e.toString(), e); + } + } + } + + private void handleBindService(BindServiceData data) { + CreateServiceData createData = mServicesData.get(data.token); + Service s = mServices.get(data.token); + if (DEBUG_SERVICE) + Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind); + if (s != null) { + try { + data.intent.setExtrasClassLoader(s.getClassLoader()); + data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), + s.getAttributionSource()); + try { + if (!data.rebind) { + IBinder binder = s.onBind(data.intent); + ActivityManager.getService().publishService( + data.token, data.intent, binder); + } else { + s.onRebind(data.intent); + ActivityManager.getService().serviceDoneExecuting( + data.token, SERVICE_DONE_EXECUTING_REBIND, 0, 0, data.intent); + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to bind to service " + s + + " with " + data.intent + ": " + e.toString(), e); + } + } + } + } + + private void handleUnbindService(BindServiceData data) { + CreateServiceData createData = mServicesData.get(data.token); + Service s = mServices.get(data.token); + if (s != null) { + try { + data.intent.setExtrasClassLoader(s.getClassLoader()); + data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), + s.getAttributionSource()); + boolean doRebind = s.onUnbind(data.intent); + try { + if (doRebind) { + ActivityManager.getService().unbindFinished( + data.token, data.intent, doRebind); + } else { + ActivityManager.getService().serviceDoneExecuting( + data.token, SERVICE_DONE_EXECUTING_UNBIND, 0, 0, data.intent); + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to unbind to service " + s + + " with " + data.intent + ": " + e.toString(), e); + } + } + } + } + + private void handleDumpGfxInfo(DumpComponentInfo info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + ThreadedRenderer.handleDumpGfxInfo(info.fd.getFileDescriptor(), info.args); + } catch (Exception e) { + Log.w(TAG, "Caught exception from dumpGfxInfo()", e); + } finally { + IoUtils.closeQuietly(info.fd); + StrictMode.setThreadPolicy(oldPolicy); + } + } + + private void handleDumpService(DumpComponentInfo info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + Service s = mServices.get(info.token); + if (s != null) { + PrintWriter pw = new FastPrintWriter(new FileOutputStream( + info.fd.getFileDescriptor())); + s.dump(info.fd.getFileDescriptor(), pw, info.args); + pw.flush(); + } + } finally { + IoUtils.closeQuietly(info.fd); + StrictMode.setThreadPolicy(oldPolicy); + } + } + + private void handleDumpResources(DumpResourcesData info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + PrintWriter pw = new FastPrintWriter(new FileOutputStream( + info.fd.getFileDescriptor())); + + Resources.dumpHistory(pw, ""); + pw.flush(); + if (info.finishCallback != null) { + info.finishCallback.sendResult(null); + } + } finally { + IoUtils.closeQuietly(info.fd); + StrictMode.setThreadPolicy(oldPolicy); + } + } + + private void handleDumpActivity(DumpComponentInfo info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + ActivityClientRecord r = mActivities.get(info.token); + if (r != null && r.activity != null) { + PrintWriter pw = new FastPrintWriter(new FileOutputStream( + info.fd.getFileDescriptor())); + r.activity.dumpInternal(info.prefix, info.fd.getFileDescriptor(), pw, info.args); + pw.flush(); + } + } finally { + IoUtils.closeQuietly(info.fd); + StrictMode.setThreadPolicy(oldPolicy); + } + } + + private void handleDumpProvider(DumpComponentInfo info) { + final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + try { + ProviderClientRecord r = mLocalProviders.get(info.token); + if (r != null && r.mLocalProvider != null) { + PrintWriter pw = new FastPrintWriter(new FileOutputStream( + info.fd.getFileDescriptor())); + r.mLocalProvider.dump(info.fd.getFileDescriptor(), pw, info.args); + pw.flush(); + } + } finally { + IoUtils.closeQuietly(info.fd); + StrictMode.setThreadPolicy(oldPolicy); + } + } + + private void handleServiceArgs(ServiceArgsData data) { + CreateServiceData createData = mServicesData.get(data.token); + Service s = mServices.get(data.token); + if (s != null) { + try { + if (data.args != null) { + data.args.setExtrasClassLoader(s.getClassLoader()); + data.args.prepareToEnterProcess(isProtectedComponent(createData.info), + s.getAttributionSource()); + } + int res; + if (!data.taskRemoved) { + res = s.onStartCommand(data.args, data.flags, data.startId); + } else { + s.onTaskRemoved(data.args); + res = Service.START_TASK_REMOVED_COMPLETE; + } + + QueuedWork.waitToFinish(); + + try { + ActivityManager.getService().serviceDoneExecuting( + data.token, SERVICE_DONE_EXECUTING_START, data.startId, res, null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to start service " + s + + " with " + data.args + ": " + e.toString(), e); + } + } + } + } + + private void handleStopService(IBinder token) { + mServicesData.remove(token); + Service s = mServices.remove(token); + if (s != null) { + try { + if (localLOGV) Slog.v(TAG, "Destroying service " + s); + s.onDestroy(); + s.detachAndCleanUp(); + Context context = s.getBaseContext(); + if (context instanceof ContextImpl) { + final String who = s.getClassName(); + ((ContextImpl) context).scheduleFinalCleanup(who, "Service"); + } + + QueuedWork.waitToFinish(); + + try { + ActivityManager.getService().serviceDoneExecuting( + token, SERVICE_DONE_EXECUTING_STOP, 0, 0, null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to stop service " + s + + ": " + e.toString(), e); + } + Slog.i(TAG, "handleStopService: exception for " + token, e); + } + } else { + Slog.i(TAG, "handleStopService: token=" + token + " not found."); + } + //Slog.i(TAG, "Running services: " + mServices); + } + + private void handleTimeoutService(IBinder token, int startId) { + Service s = mServices.get(token); + if (s != null) { + try { + if (localLOGV) Slog.v(TAG, "Timeout short service " + s); + + // Unlike other service callbacks, we don't do serviceDoneExecuting() here. + // "service executing" state is used to boost the procstate / oom-adj, but + // for short-FGS timeout, we have a specific control for them anyway, so + // we don't have to do that. + s.callOnTimeout(startId); + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to call onTimeout on service " + s + + ": " + e.toString(), e); + } + Slog.i(TAG, "handleTimeoutService: exception for " + token, e); + } + } else { + Slog.wtf(TAG, "handleTimeoutService: token=" + token + " not found."); + } + } + + private void handleTimeoutServiceForType(IBinder token, int startId, + @ServiceInfo.ForegroundServiceType int fgsType) { + Service s = mServices.get(token); + if (s != null) { + try { + if (localLOGV) Slog.v(TAG, "Timeout service " + s); + + s.callOnTimeLimitExceeded(startId, fgsType); + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to call onTimeLimitExceeded on service " + s + ": " + e, e); + } + Slog.i(TAG, "handleTimeoutServiceForType: exception for " + token, e); + } + } else { + Slog.wtf(TAG, "handleTimeoutServiceForType: token=" + token + " not found."); + } + } + + /** + * Resume the activity. + * @param r Target activity record. + * @param finalStateRequest Flag indicating if this is part of final state resolution for a + * transaction. + * @param reason Reason for performing the action. + * + * @return {@code true} that was resumed, {@code false} otherwise. + */ + @VisibleForTesting + public boolean performResumeActivity(ActivityClientRecord r, boolean finalStateRequest, + String reason) { + if (localLOGV) { + Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished); + } + if (r.activity.mFinished) { + return false; + } + if (r.getLifecycleState() == ON_RESUME) { + if (!finalStateRequest) { + final RuntimeException e = new IllegalStateException( + "Trying to resume activity which is already resumed"); + Slog.e(TAG, e.getMessage(), e); + Slog.e(TAG, r.getStateString()); + // TODO(lifecycler): A double resume request is possible when an activity + // receives two consequent transactions with relaunch requests and "resumed" + // final state requests and the second relaunch is omitted. We still try to + // handle two resume requests for the final state. For cases other than this + // one, we don't expect it to happen. + } + return false; + } + if (finalStateRequest) { + r.hideForNow = false; + r.activity.mStartedActivity = false; + } + try { + r.activity.onStateNotSaved(); + r.activity.mFragments.noteStateNotSaved(); + checkAndBlockForNetworkAccess(); + if (r.pendingIntents != null) { + deliverNewIntents(r, r.pendingIntents); + r.pendingIntents = null; + } + if (r.pendingResults != null) { + deliverResults(r, r.pendingResults, reason); + r.pendingResults = null; + } + r.activity.performResume(r.startsNotResumed, reason); + + r.state = null; + r.persistentState = null; + r.setState(ON_RESUME); + + reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming"); + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException("Unable to resume activity " + + r.intent.getComponent().toShortString() + ": " + e.toString(), e); + } + } + return true; + } + + static final void cleanUpPendingRemoveWindows(ActivityClientRecord r, boolean force) { + if (r.mPreserveWindow && !force) { + return; + } + if (r.mPendingRemoveWindow != null) { + r.mPendingRemoveWindowManager.removeViewImmediate( + r.mPendingRemoveWindow.getDecorView()); + IBinder wtoken = r.mPendingRemoveWindow.getDecorView().getWindowToken(); + if (wtoken != null) { + WindowManagerGlobal.getInstance().closeAll(wtoken, + r.activity.getClass().getName(), "Activity"); + } + } + r.mPendingRemoveWindow = null; + r.mPendingRemoveWindowManager = null; + } + + @Override + public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest, + boolean isForward, boolean shouldSendCompatFakeFocus, String reason) { + // If we are getting ready to gc after going to the background, well + // we are back active so skip it. + unscheduleGcIdler(); + mSomeActivitiesChanged = true; + + // TODO Push resumeArgs into the activity for consideration + // skip below steps for double-resume and r.mFinish = true case. + if (!performResumeActivity(r, finalStateRequest, reason)) { + return; + } + if (mActivitiesToBeDestroyed.containsKey(r.token)) { + // Although the activity is resumed, it is going to be destroyed. So the following + // UI operations are unnecessary and also prevents exception because its token may + // be gone that window manager cannot recognize it. All necessary cleanup actions + // performed below will be done while handling destruction. + return; + } + + final Activity a = r.activity; + + if (localLOGV) { + Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity + + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); + } + + final int forwardBit = isForward + ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; + + // If the window hasn't yet been added to the window manager, + // and this guy didn't finish itself or start another activity, + // then go ahead and add the window. + boolean willBeVisible = !a.mStartedActivity; + if (!willBeVisible) { + willBeVisible = ActivityClient.getInstance().willActivityBeVisible( + a.getActivityToken()); + } + if (r.window == null && !a.mFinished && willBeVisible) { + r.window = r.activity.getWindow(); + View decor = r.window.getDecorView(); + decor.setVisibility(View.INVISIBLE); + ViewManager wm = a.getWindowManager(); + WindowManager.LayoutParams l = r.window.getAttributes(); + a.mDecor = decor; + l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + l.softInputMode |= forwardBit; + if (r.mPreserveWindow) { + a.mWindowAdded = true; + r.mPreserveWindow = false; + // Normally the ViewRoot sets up callbacks with the Activity + // in addView->ViewRootImpl#setView. If we are instead reusing + // the decor view we have to notify the view root that the + // callbacks may have changed. + ViewRootImpl impl = decor.getViewRootImpl(); + if (impl != null) { + impl.notifyChildRebuilt(); + } + } + if (a.mVisibleFromClient) { + if (!a.mWindowAdded) { + a.mWindowAdded = true; + wm.addView(decor, l); + } else { + // The activity will get a callback for this {@link LayoutParams} change + // earlier. However, at that time the decor will not be set (this is set + // in this method), so no action will be taken. This call ensures the + // callback occurs with the decor set. + a.onWindowAttributesChanged(l); + } + } + + // If the window has already been added, but during resume + // we started another activity, then don't yet make the + // window visible. + } else if (!willBeVisible) { + if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); + r.hideForNow = true; + } + + // Get rid of anything left hanging around. + cleanUpPendingRemoveWindows(r, false /* force */); + + // The window is now visible if it has been added, we are not + // simply finishing, and we are not starting another activity. + if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { + if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); + ViewRootImpl impl = r.window.getDecorView().getViewRootImpl(); + WindowManager.LayoutParams l = impl != null + ? impl.mWindowAttributes : r.window.getAttributes(); + if ((l.softInputMode + & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) + != forwardBit) { + l.softInputMode = (l.softInputMode + & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) + | forwardBit; + if (r.activity.mVisibleFromClient) { + ViewManager wm = a.getWindowManager(); + View decor = r.window.getDecorView(); + wm.updateViewLayout(decor, l); + } + } + + r.activity.mVisibleFromServer = true; + mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } + + if (shouldSendCompatFakeFocus) { + // Attaching to a window is asynchronous with the activity being resumed, + // so it's possible we will need to send a fake focus event after attaching + if (impl != null) { + impl.dispatchCompatFakeFocus(); + } else { + r.window.getDecorView().fakeFocusAfterAttachingToWindow(); + } + } + } + + mNewActivities.add(r); + if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); + Looper.myQueue().addIdleHandler(new Idler()); + } + + + @Override + public void handleTopResumedActivityChanged(ActivityClientRecord r, boolean onTop, + String reason) { + if (DEBUG_ORDER) { + Slog.d(TAG, "Received position change to top: " + onTop + " for activity: " + r); + } + + if (r.isTopResumedActivity == onTop) { + if (!Build.IS_DEBUGGABLE) { + Slog.w(TAG, "Activity top position already set to onTop=" + onTop); + return; + } + // TODO(b/209744518): Remove this short-term workaround while fixing the binder failure. + Slog.e(TAG, "Activity top position already set to onTop=" + onTop); + } + + r.isTopResumedActivity = onTop; + + if (r.getLifecycleState() == ON_RESUME) { + reportTopResumedActivityChanged(r, onTop, "topStateChangedWhenResumed"); + } else { + if (DEBUG_ORDER) { + Slog.d(TAG, "Won't deliver top position change in state=" + r.getLifecycleState()); + } + } + } + + /** + * Call {@link Activity#onTopResumedActivityChanged(boolean)} if its top resumed state changed + * since the last report. + */ + private void reportTopResumedActivityChanged(ActivityClientRecord r, boolean onTop, + String reason) { + if (r.lastReportedTopResumedState != onTop) { + r.lastReportedTopResumedState = onTop; + r.activity.performTopResumedActivityChanged(onTop, reason); + } + } + + @Override + public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving, + int configChanges, boolean autoEnteringPip, PendingTransactionActions pendingActions, + String reason) { + if (userLeaving) { + performUserLeavingActivity(r); + } + + r.activity.mConfigChangeFlags |= configChanges; + if (autoEnteringPip) { + // Set mIsInPictureInPictureMode earlier in case of auto-enter-pip, see also + // {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}. + r.activity.mIsInPictureInPictureMode = true; + } + performPauseActivity(r, finished, reason, pendingActions); + + // Make sure any pending writes are now committed. + if (r.isPreHoneycomb()) { + QueuedWork.waitToFinish(); + } + mSomeActivitiesChanged = true; + } + + final void performUserLeavingActivity(ActivityClientRecord r) { + mInstrumentation.callActivityOnPictureInPictureRequested(r.activity); + mInstrumentation.callActivityOnUserLeaving(r.activity); + } + + final Bundle performPauseActivity(IBinder token, boolean finished, String reason, + PendingTransactionActions pendingActions) { + ActivityClientRecord r = mActivities.get(token); + return r != null ? performPauseActivity(r, finished, reason, pendingActions) : null; + } + + /** + * Pause the activity. + * @return Saved instance state for pre-Honeycomb apps if it was saved, {@code null} otherwise. + */ + private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, + PendingTransactionActions pendingActions) { + if (r.paused) { + if (r.activity.mFinished) { + // If we are finishing, we won't call onResume() in certain cases. + // So here we likewise don't want to call onPause() if the activity + // isn't resumed. + return null; + } + RuntimeException e = new RuntimeException( + "Performing pause of activity that is not resumed: " + + r.intent.getComponent().toShortString()); + Slog.e(TAG, e.getMessage(), e); + } + if (finished) { + r.activity.mFinished = true; + } + + // Pre-Honeycomb apps always save their state before pausing + final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb(); + if (shouldSaveState) { + callActivityOnSaveInstanceState(r); + } + + performPauseActivityIfNeeded(r, reason); + + // Notify any outstanding on paused listeners + ArrayList listeners; + synchronized (mOnPauseListeners) { + listeners = mOnPauseListeners.remove(r.activity); + } + int size = (listeners != null ? listeners.size() : 0); + for (int i = 0; i < size; i++) { + listeners.get(i).onPaused(r.activity); + } + + final Bundle oldState = pendingActions != null ? pendingActions.getOldState() : null; + if (oldState != null) { + // We need to keep around the original state, in case we need to be created again. + // But we only do this for pre-Honeycomb apps, which always save their state when + // pausing, so we can not have them save their state when restarting from a paused + // state. For HC and later, we want to (and can) let the state be saved as the + // normal part of stopping the activity. + if (r.isPreHoneycomb()) { + r.state = oldState; + } + } + + return shouldSaveState ? r.state : null; + } + + private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) { + if (r.paused) { + // You are already paused silly... + return; + } + + // Always reporting top resumed position loss when pausing an activity. If necessary, it + // will be restored in performResumeActivity(). + reportTopResumedActivityChanged(r, false /* onTop */, "pausing"); + + try { + r.activity.mCalled = false; + mInstrumentation.callActivityOnPause(r.activity); + if (!r.activity.mCalled) { + throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent) + + " did not call through to super.onPause()"); + } + } catch (SuperNotCalledException e) { + throw e; + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException("Unable to pause activity " + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); + } + } + r.setState(ON_PAUSE); + } + + // TODO(b/176961850): Make LocalActivityManager call performStopActivityInner. We cannot remove + // this since it's a high usage hidden API. + /** Called from {@link LocalActivityManager}. */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 176961850, + publicAlternatives = "{@code N/A}") + final void performStopActivity(IBinder token, boolean saveState, String reason) { + ActivityClientRecord r = mActivities.get(token); + performStopActivityInner(r, null /* stopInfo */, saveState, false /* finalStateRequest */, + reason); + } + + private static final class ProviderRefCount { + public final ContentProviderHolder holder; + public final ProviderClientRecord client; + public int stableCount; + public int unstableCount; + + // When this is set, the stable and unstable ref counts are 0 and + // we have a pending operation scheduled to remove the ref count + // from the activity manager. On the activity manager we are still + // holding an unstable ref, though it is not reflected in the counts + // here. + public boolean removePending; + + ProviderRefCount(ContentProviderHolder inHolder, + ProviderClientRecord inClient, int sCount, int uCount) { + holder = inHolder; + client = inClient; + stableCount = sCount; + unstableCount = uCount; + } + } + + /** + * Core implementation of stopping an activity. + * @param r Target activity client record. + * @param info Action that will report activity stop to server. + * @param saveState Flag indicating whether the activity state should be saved. + * @param finalStateRequest Flag indicating if this call is handling final lifecycle state + * request for a transaction. + * @param reason Reason for performing this operation. + */ + private void performStopActivityInner(ActivityClientRecord r, StopInfo info, + boolean saveState, boolean finalStateRequest, String reason) { + if (localLOGV) Slog.v(TAG, "Performing stop of " + r); + if (r.stopped) { + if (r.activity.mFinished) { + // If we are finishing, we won't call onResume() in certain + // cases. So here we likewise don't want to call onStop() + // if the activity isn't resumed. + return; + } + if (!finalStateRequest) { + final RuntimeException e = new RuntimeException( + "Performing stop of activity that is already stopped: " + + r.intent.getComponent().toShortString()); + Slog.e(TAG, e.getMessage(), e); + Slog.e(TAG, r.getStateString()); + } + } + + // One must first be paused before stopped... + performPauseActivityIfNeeded(r, reason); + + if (info != null) { + try { + // First create a thumbnail for the activity... + // For now, don't create the thumbnail here; we are + // doing that by doing a screen snapshot. + info.setDescription(r.activity.onCreateDescription()); + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException( + "Unable to save state of activity " + + r.intent.getComponent().toShortString() + + ": " + e.toString(), e); + } + } + } + + callActivityOnStop(r, saveState, reason); + } + + /** + * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates + * the client record's state. + * All calls to stop an activity must be done through this method to make sure that + * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call. + */ + private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) { + // Before P onSaveInstanceState was called before onStop, starting with P it's + // called after. Before Honeycomb state was always saved before onPause. + final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null + && !r.isPreHoneycomb(); + final boolean isPreP = r.isPreP(); + if (shouldSaveState && isPreP) { + callActivityOnSaveInstanceState(r); + } + + try { + r.activity.performStop(r.mPreserveWindow, reason); + } catch (SuperNotCalledException e) { + throw e; + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException( + "Unable to stop activity " + + r.intent.getComponent().toShortString() + + ": " + e.toString(), e); + } + } + r.setState(ON_STOP); + + if (shouldSaveState && !isPreP) { + callActivityOnSaveInstanceState(r); + } + } + + private void updateVisibility(ActivityClientRecord r, boolean show) { + View v = r.activity.mDecor; + if (v != null) { + if (show) { + if (!r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = true; + mNumVisibleActivities++; + if (r.activity.mVisibleFromClient) { + r.activity.makeVisible(); + } + } + } else { + if (r.activity.mVisibleFromServer) { + r.activity.mVisibleFromServer = false; + mNumVisibleActivities--; + v.setVisibility(View.INVISIBLE); + } + } + } + } + + @Override + public void handleStopActivity(ActivityClientRecord r, int configChanges, + PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { + r.activity.mConfigChangeFlags |= configChanges; + + final StopInfo stopInfo = new StopInfo(); + performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest, + reason); + + if (localLOGV) Slog.v( + TAG, "Finishing stop of " + r + ": win=" + r.window); + + updateVisibility(r, false); + + // Make sure any pending writes are now committed. + if (!r.isPreHoneycomb()) { + QueuedWork.waitToFinish(); + } + + stopInfo.setActivity(r); + stopInfo.setState(r.state); + stopInfo.setPersistentState(r.persistentState); + pendingActions.setStopInfo(stopInfo); + mSomeActivitiesChanged = true; + } + + /** + * Schedule the call to tell the activity manager we have stopped. We don't do this + * immediately, because we want to have a chance for any other pending work (in particular + * memory trim requests) to complete before you tell the activity manager to proceed and allow + * us to go fully into the background. + */ + @Override + public void reportStop(PendingTransactionActions pendingActions) { + mH.post(pendingActions.getStopInfo()); + } + + @Override + public void performRestartActivity(ActivityClientRecord r, boolean start) { + if (r.stopped) { + r.activity.performRestart(start); + if (start) { + r.setState(ON_START); + } + } + } + + @Override + public void reportRefresh(ActivityClientRecord r) { + ActivityClient.getInstance().activityRefreshed(r.token); + } + + private void handleSetCoreSettings(Bundle coreSettings) { + synchronized (mCoreSettingsLock) { + mCoreSettings = coreSettings; + } + onCoreSettingsChange(); + } + + private void onCoreSettingsChange() { + if (updateDebugViewAttributeState()) { + // request all activities to relaunch for the changes to take place + relaunchAllActivities(true /* preserveWindows */, "onCoreSettingsChange"); + } + } + + private boolean updateDebugViewAttributeState() { + boolean previousState = View.sDebugViewAttributes; + + // mCoreSettings is only updated from the main thread, while this function is only called + // from main thread as well, so no need to lock here. + View.sDebugViewAttributesApplicationPackage = mCoreSettings.getString( + Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, ""); + String currentPackage = (mBoundApplication != null && mBoundApplication.appInfo != null) + ? mBoundApplication.appInfo.packageName : ""; + View.sDebugViewAttributes = + mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0 + || View.sDebugViewAttributesApplicationPackage.equals(currentPackage); + return previousState != View.sDebugViewAttributes; + } + + private void relaunchAllActivities(boolean preserveWindows, String reason) { + Log.i(TAG, "Relaunch all activities: " + reason); + for (int i = mActivities.size() - 1; i >= 0; i--) { + scheduleRelaunchActivityIfPossible(mActivities.valueAt(i), preserveWindows); + } + } + + private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) { + mCompatibilityInfo = data.info; + LoadedApk apk = peekPackageInfo(data.pkg, false); + if (apk != null) { + apk.setCompatibilityInfo(data.info); + } + apk = peekPackageInfo(data.pkg, true); + if (apk != null) { + apk.setCompatibilityInfo(data.info); + } + mConfigurationController.handleConfigurationChanged(data.info); + } + + private void deliverResults(ActivityClientRecord r, List results, String reason) { + final int N = results.size(); + for (int i=0; i results, String reason) { + if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r); + final boolean resumed = !r.paused; + if (!r.activity.mFinished && r.activity.mDecor != null + && r.hideForNow && resumed) { + // We had hidden the activity because it started another + // one... we have gotten a result back and we are not + // paused, so make sure our window is visible. + updateVisibility(r, true); + } + if (resumed) { + try { + // Now we are idle. + r.activity.mCalled = false; + mInstrumentation.callActivityOnPause(r.activity); + if (!r.activity.mCalled) { + throw new SuperNotCalledException( + "Activity " + r.intent.getComponent().toShortString() + + " did not call through to super.onPause()"); + } + } catch (SuperNotCalledException e) { + throw e; + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException( + "Unable to pause activity " + + r.intent.getComponent().toShortString() + + ": " + e.toString(), e); + } + } + } + checkAndBlockForNetworkAccess(); + deliverResults(r, results, reason); + if (resumed) { + r.activity.performResume(false, reason); + } + } + + /** Core implementation of activity destroy call. */ + void performDestroyActivity(ActivityClientRecord r, boolean finishing, + int configChanges, boolean getNonConfigInstance, String reason) { + Class activityClass; + if (localLOGV) Slog.v(TAG, "Performing finish of " + r); + activityClass = r.activity.getClass(); + r.activity.mConfigChangeFlags |= configChanges; + if (finishing) { + r.activity.mFinished = true; + } + + performPauseActivityIfNeeded(r, "destroy"); + + if (!r.stopped) { + callActivityOnStop(r, false /* saveState */, "destroy"); + } + if (getNonConfigInstance) { + try { + r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException("Unable to retain activity " + + r.intent.getComponent().toShortString() + ": " + e.toString(), e); + } + } + } + try { + r.activity.mCalled = false; + mInstrumentation.callActivityOnDestroy(r.activity); + if (!r.activity.mCalled) { + throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent) + + " did not call through to super.onDestroy()"); + } + if (r.window != null) { + r.window.closeAllPanels(); + } + } catch (SuperNotCalledException e) { + throw e; + } catch (Exception e) { + if (!mInstrumentation.onException(r.activity, e)) { + throw new RuntimeException("Unable to destroy activity " + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); + } + } + r.setState(ON_DESTROY); + schedulePurgeIdler(); + synchronized (this) { + if (mSplashScreenGlobal != null) { + mSplashScreenGlobal.tokenDestroyed(r.token); + } + } + // updatePendingActivityConfiguration() reads from mActivities to update + // ActivityClientRecord which runs in a different thread. Protect modifications to + // mActivities to avoid race. + synchronized (mResourcesManager) { + mActivities.remove(r.token); + } + StrictMode.decrementExpectedActivityCount(activityClass); + } + + private static String safeToComponentShortString(Intent intent) { + ComponentName component = intent.getComponent(); + return component == null ? "[Unknown]" : component.toShortString(); + } + + @Override + public Map getActivitiesToBeDestroyed() { + return mActivitiesToBeDestroyed; + } + + @Override + public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, + boolean getNonConfigInstance, String reason) { + performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason); + cleanUpPendingRemoveWindows(r, finishing); + WindowManager wm = r.activity.getWindowManager(); + View v = r.activity.mDecor; + if (v != null) { + if (r.activity.mVisibleFromServer) { + mNumVisibleActivities--; + } + IBinder wtoken = v.getWindowToken(); + if (r.activity.mWindowAdded) { + if (r.mPreserveWindow) { + // Hold off on removing this until the new activity's window is being added. + r.mPendingRemoveWindow = r.window; + r.mPendingRemoveWindowManager = wm; + // We can only keep the part of the view hierarchy that we control, + // everything else must be removed, because it might not be able to + // behave properly when activity is relaunching. + r.window.clearContentView(); + } else { + final ViewRootImpl viewRoot = v.getViewRootImpl(); + if (viewRoot != null) { + // Clear callbacks to avoid the destroyed activity from receiving + // configuration or camera compat changes that are no longer effective. + viewRoot.setActivityConfigCallback(null); + } + wm.removeViewImmediate(v); + } + } + if (wtoken != null && r.mPendingRemoveWindow == null) { + WindowManagerGlobal.getInstance().closeAll(wtoken, + r.activity.getClass().getName(), "Activity"); + } else if (r.mPendingRemoveWindow != null) { + // We're preserving only one window, others should be closed so app views + // will be detached before the final tear down. It should be done now because + // some components (e.g. WebView) rely on detach callbacks to perform receiver + // unregister and other cleanup. + WindowManagerGlobal.getInstance().closeAllExceptView(r.token, v, + r.activity.getClass().getName(), "Activity"); + } + r.activity.mDecor = null; + } + if (r.mPendingRemoveWindow == null) { + // If we are delaying the removal of the activity window, then + // we can't clean up all windows here. Note that we can't do + // so later either, which means any windows that aren't closed + // by the app will leak. Well we try to warning them a lot + // about leaking windows, because that is a bug, so if they are + // using this recreate facility then they get to live with leaks. + WindowManagerGlobal.getInstance().closeAll(r.token, + r.activity.getClass().getName(), "Activity"); + } + + // Mocked out contexts won't be participating in the normal + // process lifecycle, but if we're running with a proper + // ApplicationContext we need to have it tear down things + // cleanly. + Context c = r.activity.getBaseContext(); + if (c instanceof ContextImpl) { + ((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity"); + } + if (finishing) { + ActivityClient.getInstance().activityDestroyed(r.token); + mNewActivities.remove(r); + } + mSomeActivitiesChanged = true; + } + + @Override + public ActivityClientRecord prepareRelaunchActivity(@NonNull IBinder token, + @Nullable List pendingResults, + @Nullable List pendingNewIntents, int configChanges, + @NonNull MergedConfiguration config, boolean preserveWindow, + @NonNull ActivityWindowInfo activityWindowInfo) { + ActivityClientRecord target = null; + boolean scheduleRelaunch = false; + + synchronized (mResourcesManager) { + for (int i=0; i ON_STOP) { + Log.w(TAG, "Activity state must be in [ON_START..ON_STOP] in order to be relaunched," + + "current state is " + prevState); + return; + } + + ActivityClient.getInstance().activityLocalRelaunch(r.token); + // Initialize a relaunch request. + final MergedConfiguration mergedConfiguration = new MergedConfiguration( + r.createdConfig != null + ? r.createdConfig : mConfigurationController.getConfiguration(), + r.overrideConfig); + final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain( + r.token, null /* pendingResults */, null /* pendingIntents */, + 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow, + r.getActivityWindowInfo()); + // Make sure to match the existing lifecycle state in the end of the transaction. + final ActivityLifecycleItem lifecycleRequest = + TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); + // Schedule the transaction. + final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); + transaction.addTransactionItem(activityRelaunchItem); + transaction.addTransactionItem(lifecycleRequest); + executeTransaction(transaction); + } + + private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, int configChanges, + @Nullable List pendingResults, + @Nullable List pendingIntents, + @NonNull PendingTransactionActions pendingActions, boolean startsNotResumed, + @NonNull Configuration overrideConfig, @NonNull ActivityWindowInfo activityWindowInfo, + @NonNull String reason) { + // Preserve last used intent, it may be set from Activity#setIntent(). + final Intent customIntent = r.activity.mIntent; + // Need to ensure state is saved. + if (!r.paused) { + performPauseActivity(r, false, reason, null /* pendingActions */); + } + if (!r.stopped) { + callActivityOnStop(r, true /* saveState */, reason); + } + + handleDestroyActivity(r, false, configChanges, true, reason); + + r.activity = null; + r.window = null; + r.hideForNow = false; + // Merge any pending results and pending intents; don't just replace them + if (pendingResults != null) { + if (r.pendingResults == null) { + r.pendingResults = pendingResults; + } else { + r.pendingResults.addAll(pendingResults); + } + } + if (pendingIntents != null) { + if (r.pendingIntents == null) { + r.pendingIntents = pendingIntents; + } else { + r.pendingIntents.addAll(pendingIntents); + } + } + r.startsNotResumed = startsNotResumed; + r.overrideConfig = overrideConfig; + r.mActivityWindowInfo = activityWindowInfo; + + handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent); + } + + @Override + public void reportRelaunch(ActivityClientRecord r) { + ActivityClient.getInstance().activityRelaunched(r.token); + } + + private void callActivityOnSaveInstanceState(ActivityClientRecord r) { + r.state = new Bundle(); + r.state.setAllowFds(false); + if (r.isPersistable()) { + r.persistentState = new PersistableBundle(); + mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state, + r.persistentState); + } else { + mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); + } + } + + @Override + public ArrayList collectComponentCallbacks(boolean includeUiContexts) { + ArrayList callbacks + = new ArrayList(); + + synchronized (mResourcesManager) { + final int NAPP = mAllApplications.size(); + for (int i=0; i= 0; i--) { + final Activity a = mActivities.valueAt(i).activity; + if (a != null && !a.mFinished) { + callbacks.add(a); + } + } + } + final int NSVC = mServices.size(); + for (int i=0; i nonUIContexts = new ArrayList<>(); + // Update Application and Service contexts with implicit device association. + // UI Contexts are able to derived their device Id association from the display. + synchronized (mResourcesManager) { + final int numApps = mAllApplications.size(); + for (int i = 0; i < numApps; i++) { + nonUIContexts.add(mAllApplications.get(i)); + } + final int numServices = mServices.size(); + for (int i = 0; i < numServices; i++) { + final Service service = mServices.valueAt(i); + // WindowProviderService is a UI Context. + if (!service.isUiContext()) { + nonUIContexts.add(service); + } + } + } + for (Context context : nonUIContexts) { + try { + context.updateDeviceId(deviceId); + } catch (IllegalArgumentException e) { + // It can happen that the system already closed/removed a virtual device + // and the passed deviceId is no longer valid. + // TODO(b/263355088): check for validity of deviceId before updating + // instead of catching this exception once VDM add an API to validate ids. + } + } + } + + @Override + public void handleConfigurationChanged(Configuration config, int deviceId) { + mConfigurationController.handleConfigurationChanged(config); + updateDeviceIdForNonUIContexts(deviceId); + + // These are only done to maintain @UnsupportedAppUsage and should be removed someday. + mCurDefaultDisplayDpi = mConfigurationController.getCurDefaultDisplayDpi(); + mConfiguration = mConfigurationController.getConfiguration(); + mPendingConfiguration = mConfigurationController.getPendingConfiguration( + false /* clearPending */); + } + + @Override + public void handleWindowContextInfoChanged(@NonNull IBinder clientToken, + @NonNull WindowContextInfo info) { + WindowTokenClientController.getInstance().onWindowContextInfoChanged(clientToken, info); + } + + @Override + public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) { + WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken); + } + + /** + * Sends windowing mode change callbacks to {@link Activity} if applicable. + * + * See also {@link Activity#onMultiWindowModeChanged(boolean, Configuration)} and + * {@link Activity#onPictureInPictureModeChanged(boolean, Configuration)} + */ + private void handleWindowingModeChangeIfNeeded(ActivityClientRecord r, + Configuration newConfiguration) { + final Activity activity = r.activity; + final int newWindowingMode = newConfiguration.windowConfiguration.getWindowingMode(); + final int oldWindowingMode = r.mLastReportedWindowingMode; + if (oldWindowingMode == newWindowingMode) return; + // PiP callback is sent before the MW one. + if (newWindowingMode == WINDOWING_MODE_PINNED) { + activity.dispatchPictureInPictureModeChanged(true, newConfiguration); + } else if (oldWindowingMode == WINDOWING_MODE_PINNED) { + activity.dispatchPictureInPictureModeChanged(false, newConfiguration); + } + final boolean wasInMultiWindowMode = WindowConfiguration.inMultiWindowMode( + oldWindowingMode); + final boolean nowInMultiWindowMode = WindowConfiguration.inMultiWindowMode( + newWindowingMode); + if (wasInMultiWindowMode != nowInMultiWindowMode) { + activity.dispatchMultiWindowModeChanged(nowInMultiWindowMode, newConfiguration); + } + r.mLastReportedWindowingMode = newWindowingMode; + } + + private void applyPendingApplicationInfoChanges(String packageName) { + final ApplicationInfo ai; + synchronized (mResourcesManager) { + ai = mPendingAppInfoUpdates.remove(packageName); + } + if (ai == null) { + return; + } + handleApplicationInfoChanged(ai); + } + + /** + * Updates the application info. + * + * This only works in the system process. Must be called on the main thread. + */ + public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) { + Preconditions.checkState(mSystemThread, "Must only be called in the system process"); + handleApplicationInfoChanged(ai); + } + + @VisibleForTesting(visibility = PACKAGE) + public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { + // Updates triggered by package installation go through a package update + // receiver. Here we try to capture ApplicationInfo changes that are + // caused by other sources, such as overlays. That means we want to be as conservative + // about code changes as possible. Take the diff of the old ApplicationInfo and the new + // to see if anything needs to change. + LoadedApk apk; + LoadedApk resApk; + // Update all affected loaded packages with new package information + synchronized (mResourcesManager) { + WeakReference ref = mPackages.get(ai.packageName); + apk = ref != null ? ref.get() : null; + ref = mResourcePackages.get(ai.packageName); + resApk = ref != null ? ref.get() : null; + for (ActivityClientRecord ar : mActivities.values()) { + if (ar.activityInfo.applicationInfo.packageName.equals(ai.packageName)) { + ar.activityInfo.applicationInfo = ai; + if (apk != null || resApk != null) { + ar.packageInfo = apk != null ? apk : resApk; + } else { + apk = ar.packageInfo; + } + } + } + } + + if (apk != null) { + final ArrayList oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths); + apk.updateApplicationInfo(ai, oldPaths); + } + if (resApk != null) { + final ArrayList oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths); + resApk.updateApplicationInfo(ai, oldPaths); + } + + ResourcesImpl beforeImpl = getApplication().getResources().getImpl(); + + synchronized (mResourcesManager) { + // Update all affected Resources objects to use new ResourcesImpl + mResourcesManager.applyAllPendingAppInfoUpdates(); + } + + ResourcesImpl afterImpl = getApplication().getResources().getImpl(); + + if ((beforeImpl != afterImpl) && !Arrays.equals(beforeImpl.getAssets().getApkAssets(), + afterImpl.getAssets().getApkAssets())) { + List beforeAssets = Arrays.asList(beforeImpl.getAssets().getApkPaths()); + List afterAssets = Arrays.asList(afterImpl.getAssets().getApkPaths()); + + List onlyBefore = new ArrayList<>(beforeAssets); + onlyBefore.removeAll(afterAssets); + List onlyAfter = new ArrayList<>(afterAssets); + onlyAfter.removeAll(beforeAssets); + + Slog.i(TAG, "ApplicationInfo updating for " + ai.packageName + ", new timestamp: " + + ai.createTimestamp + "\nassets removed: " + onlyBefore + "\nassets added: " + + onlyAfter); + + if (DEBUG_APP_INFO) { + Slog.v(TAG, "ApplicationInfo updating for " + ai.packageName + + ", assets before change: " + beforeAssets + "\n assets after change: " + + afterAssets); + } + } + } + + /** + * Sets the supplied {@code overrideConfig} as pending for the {@code token}. Calling + * this method prevents any calls to + * {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int, + * ActivityWindowInfo)} from processing any configurations older than {@code overrideConfig}. + */ + @Override + public void updatePendingActivityConfiguration(@NonNull IBinder token, + @NonNull Configuration overrideConfig) { + synchronized (mPendingOverrideConfigs) { + final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(token); + if (pendingOverrideConfig != null + && !pendingOverrideConfig.isOtherSeqNewer(overrideConfig)) { + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Activity has newer configuration pending so this transaction will" + + " be dropped. overrideConfig=" + overrideConfig + + " pendingOverrideConfig=" + pendingOverrideConfig); + } + return; + } + mPendingOverrideConfigs.put(token, overrideConfig); + } + } + + @Override + public void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r, + @NonNull Configuration overrideConfig, int displayId, + @NonNull ActivityWindowInfo activityWindowInfo) { + handleActivityConfigurationChanged(r, overrideConfig, displayId, activityWindowInfo, + // This is the only place that uses alwaysReportChange=true. The entry point should + // be from ActivityConfigurationChangeItem or MoveToDisplayItem, so the server side + // has confirmed the activity should handle the configuration instead of relaunch. + // If Activity#onConfigurationChanged is called unexpectedly, then we can know it is + // something wrong from server side. + true /* alwaysReportChange */); + } + + /** + * Handle new activity configuration and/or move to a different display. This method is a noop + * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been + * called with a newer config than {@code overrideConfig}. + * + * @param r Target activity record. + * @param overrideConfig Activity override config. + * @param displayId Id of the display where activity was moved to, -1 if there was no move and + * value didn't change. + * @param activityWindowInfo the window info of the given activity. + */ + void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r, + @NonNull Configuration overrideConfig, int displayId, + @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) { + synchronized (mPendingOverrideConfigs) { + final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token); + if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) { + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Activity has newer configuration pending so drop this" + + " transaction. overrideConfig=" + overrideConfig + + " pendingOverrideConfig=" + pendingOverrideConfig); + } + return; + } + mPendingOverrideConfigs.remove(r.token); + } + + if (displayId == INVALID_DISPLAY) { + // If INVALID_DISPLAY is passed assume that the activity should keep its current + // display. + displayId = r.activity.getDisplayId(); + } + final boolean movedToDifferentDisplay = isDifferentDisplay( + r.activity.getDisplayId(), displayId); + if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig) + && !movedToDifferentDisplay) { + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Activity already handled newer configuration so drop this" + + " transaction. overrideConfig=" + overrideConfig + " r.overrideConfig=" + + r.overrideConfig); + } + return; + } + + // Perform updates. + r.overrideConfig = overrideConfig; + r.mActivityWindowInfo = activityWindowInfo; + // TODO(b/287582673): notify on ActivityWindowInfo change + final ViewRootImpl viewRoot = r.activity.mDecor != null + ? r.activity.mDecor.getViewRootImpl() : null; + + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Handle activity config changed, activity:" + + r.activityInfo.name + ", displayId=" + r.activity.getDisplayId() + + (movedToDifferentDisplay ? (", newDisplayId=" + displayId) : "") + + ", config=" + overrideConfig); + } + final Configuration reportedConfig = performConfigurationChangedForActivity(r, + mConfigurationController.getCompatConfiguration(), + movedToDifferentDisplay ? displayId : r.activity.getDisplayId(), + alwaysReportChange); + // Notify the ViewRootImpl instance about configuration changes. It may have initiated this + // update to make sure that resources are updated before updating itself. + if (viewRoot != null) { + if (movedToDifferentDisplay) { + viewRoot.onMovedToDisplay(displayId, reportedConfig); + } + viewRoot.updateConfiguration(displayId); + } + mSomeActivitiesChanged = true; + } + + final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { + if (start) { + try { + switch (profileType) { + default: + mProfiler.setProfiler(profilerInfo); + mProfiler.startProfiling(); + break; + } + } catch (RuntimeException e) { + Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile + + " -- can the process access this path?"); + } finally { + profilerInfo.closeFd(); + } + } else { + switch (profileType) { + default: + mProfiler.stopProfiling(); + break; + } + } + } + + /** + * Public entrypoint to stop profiling. This is required to end profiling when the app crashes, + * so that profiler data won't be lost. + * + * @hide + */ + public void stopProfiling() { + if (mProfiler != null) { + mProfiler.stopProfiling(); + } + } + + static void handleDumpHeap(DumpHeapData dhd) { + if (dhd.runGc) { + System.gc(); + System.runFinalization(); + System.gc(); + } + try (ParcelFileDescriptor fd = dhd.fd) { + if (dhd.managed) { + Debug.dumpHprofData(dhd.path, fd.getFileDescriptor()); + } else if (dhd.mallocInfo) { + Debug.dumpNativeMallocInfo(fd.getFileDescriptor()); + } else { + Debug.dumpNativeHeap(fd.getFileDescriptor()); + } + } catch (IOException e) { + if (dhd.managed) { + Slog.w(TAG, "Managed heap dump failed on path " + dhd.path + + " -- can the process access this path?", e); + } else { + Slog.w(TAG, "Failed to dump heap", e); + } + } catch (RuntimeException e) { + // This should no longer happening now that we're copying the file descriptor. + Slog.wtf(TAG, "Heap dumper threw a runtime exception", e); + } + try { + ActivityManager.getService().dumpHeapFinished(dhd.path); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + if (dhd.finishCallback != null) { + dhd.finishCallback.sendResult(null); + } + } + + final void handleDispatchPackageBroadcast(int cmd, String[] packages) { + boolean hasPkgInfo = false; + switch (cmd) { + case ApplicationThreadConstants.PACKAGE_REMOVED: + case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL: + { + final boolean killApp = cmd == ApplicationThreadConstants.PACKAGE_REMOVED; + if (packages == null) { + break; + } + synchronized (mResourcesManager) { + for (int i = packages.length - 1; i >= 0; i--) { + if (!hasPkgInfo) { + WeakReference ref = mPackages.get(packages[i]); + if (ref != null && ref.get() != null) { + hasPkgInfo = true; + } else { + ref = mResourcePackages.get(packages[i]); + if (ref != null && ref.get() != null) { + hasPkgInfo = true; + } + } + } + if (killApp) { + // Keep in sync with "perhaps it was removed" case below. + mPackages.remove(packages[i]); + mResourcePackages.remove(packages[i]); + } + } + } + break; + } + case ApplicationThreadConstants.PACKAGE_REPLACED: + { + if (packages == null) { + break; + } + + List packagesHandled = new ArrayList<>(); + + synchronized (mResourcesManager) { + for (int i = packages.length - 1; i >= 0; i--) { + String packageName = packages[i]; + WeakReference ref = mPackages.get(packageName); + LoadedApk pkgInfo = ref != null ? ref.get() : null; + if (pkgInfo != null) { + hasPkgInfo = true; + } else { + ref = mResourcePackages.get(packageName); + pkgInfo = ref != null ? ref.get() : null; + if (pkgInfo != null) { + hasPkgInfo = true; + } + } + // If the package is being replaced, yet it still has a valid + // LoadedApk object, the package was updated with _DONT_KILL. + // Adjust it's internal references to the application info and + // resources. + if (pkgInfo != null) { + packagesHandled.add(packageName); + try { + final ApplicationInfo aInfo = + sPackageManager.getApplicationInfo( + packageName, + PackageManager.GET_SHARED_LIBRARY_FILES, + UserHandle.myUserId()); + + if (aInfo != null) { + if (mActivities.size() > 0) { + for (ActivityClientRecord ar : mActivities.values()) { + if (ar.activityInfo.applicationInfo.packageName + .equals(packageName)) { + ar.activityInfo.applicationInfo = aInfo; + ar.packageInfo = pkgInfo; + } + } + } + + final String[] oldResDirs = {pkgInfo.getResDir()}; + + final ArrayList oldPaths = new ArrayList<>(); + LoadedApk.makePaths( + this, pkgInfo.getApplicationInfo(), oldPaths); + pkgInfo.updateApplicationInfo(aInfo, oldPaths); + + // Update affected Resources objects to use new ResourcesImpl + mResourcesManager.appendPendingAppInfoUpdate(oldResDirs, + aInfo); + mResourcesManager.applyAllPendingAppInfoUpdates(); + } + } catch (RemoteException e) { + } + } else { + // No package, perhaps it was removed? + Slog.e(TAG, "Package [" + packages[i] + "] reported as REPLACED," + + " but missing application info. Assuming REMOVED."); + mPackages.remove(packages[i]); + mResourcePackages.remove(packages[i]); + } + } + } + + try { + getPackageManager().notifyPackagesReplacedReceived( + packagesHandled.toArray(new String[0])); + } catch (RemoteException ignored) { + } + + break; + } + } + ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); + } + + final void handleLowMemory() { + final ArrayList callbacks = + collectComponentCallbacks(true /* includeUiContexts */); + + final int N = callbacks.size(); + for (int i=0; i= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { + PropertyInvalidatedCache.onTrimMemory(); + } + + final ArrayList callbacks = + collectComponentCallbacks(true /* includeUiContexts */); + + final int N = callbacks.size(); + for (int i = 0; i < N; i++) { + callbacks.get(i).onTrimMemory(level); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + WindowManagerGlobal.getInstance().trimMemory(level); + + if (SystemProperties.getInt("debug.am.run_gc_trim_level", Integer.MAX_VALUE) <= level) { + unscheduleGcIdler(); + doGcIfNeeded("tm"); + } + if (SystemProperties.getInt("debug.am.run_mallopt_trim_level", Integer.MAX_VALUE) + <= level) { + unschedulePurgeIdler(); + purgePendingResources(); + } + } + + private void setupGraphicsSupport(Context context) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupGraphicsSupport"); + + // The system package doesn't have real data directories, so don't set up cache paths. + if (!"android".equals(context.getPackageName())) { + // This cache location probably points at credential-encrypted + // storage which may not be accessible yet; assign it anyway instead + // of pointing at device-encrypted storage. + final File cacheDir = context.getCacheDir(); + if (cacheDir != null) { + // Provide a usable directory for temporary files + String tmpdir = cacheDir.getAbsolutePath(); + System.setProperty("java.io.tmpdir", tmpdir); + try { + android.system.Os.setenv("TMPDIR", tmpdir, true); + } catch (ErrnoException ex) { + Log.w(TAG, "Unable to initialize $TMPDIR", ex); + } + } else { + Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property " + + "due to missing cache directory"); + } + + // Setup a location to store generated/compiled graphics code. + final Context deviceContext = context.createDeviceProtectedStorageContext(); + final File codeCacheDir = deviceContext.getCodeCacheDir(); + final File deviceCacheDir = deviceContext.getCacheDir(); + if (codeCacheDir != null && deviceCacheDir != null) { + try { + int uid = Process.myUid(); + String[] packages = getPackageManager().getPackagesForUid(uid); + if (packages != null) { + HardwareRenderer.setupDiskCache(deviceCacheDir); + RenderScriptCacheDir.setupDiskCache(codeCacheDir); + } + } catch (RemoteException e) { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + throw e.rethrowFromSystemServer(); + } + } else { + Log.w(TAG, "Unable to use shader/script cache: missing code-cache directory"); + } + } + + // mCoreSettings is only updated from the main thread, while this function is only called + // from main thread as well, so no need to lock here. + GraphicsEnvironment.getInstance().setup(context, mCoreSettings); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + /** + * Returns the correct library directory for the current ABI. + *

+ * If we're dealing with a multi-arch application that has both 32 and 64 bit shared + * libraries, we might need to choose the secondary depending on what the current + * runtime's instruction set is. + */ + private String getInstrumentationLibrary(ApplicationInfo appInfo, InstrumentationInfo insInfo) { + if (appInfo.primaryCpuAbi != null && appInfo.secondaryCpuAbi != null + && appInfo.secondaryCpuAbi.equals(insInfo.secondaryCpuAbi)) { + // Get the instruction set supported by the secondary ABI. In the presence + // of a native bridge this might be different than the one secondary ABI used. + String secondaryIsa = + VMRuntime.getInstructionSet(appInfo.secondaryCpuAbi); + final String secondaryDexCodeIsa = + SystemProperties.get("ro.dalvik.vm.isa." + secondaryIsa); + secondaryIsa = secondaryDexCodeIsa.isEmpty() ? secondaryIsa : secondaryDexCodeIsa; + + final String runtimeIsa = VMRuntime.getRuntime().vmInstructionSet(); + if (runtimeIsa.equals(secondaryIsa)) { + return insInfo.secondaryNativeLibraryDir; + } + } + return insInfo.nativeLibraryDir; + } + + private void modifyBuildName(String field, String value) { + try { + Build clazz = new Build(); + final Field field2 = clazz.getClass().getDeclaredField(field); + field2.setAccessible(true); + // Field modifiersField2 = Field.class.getDeclaredField("modifiers"); + Field modifiersField2 = Field.class.getDeclaredField("accessFlags");; + modifiersField2.setAccessible(true); + modifiersField2.setInt(field2,field2.getModifiers()&~Modifier.FINAL); + field2.set(null, value); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + public void modifyBuildVersionFiled(String field, String value) { + try { + Build.VERSION clazz = new Build.VERSION(); + // Class clazz = Class.forName("android.os.Build$VERSION"); + final Field field1 = clazz.getClass().getDeclaredField(field); + field1.setAccessible(true); + Field modifiersField2 = Field.class.getDeclaredField("accessFlags"); + modifiersField2.setAccessible(true); + modifiersField2.setInt(field1,field1.getModifiers()&~Modifier.FINAL); + field1.set(null,value); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } + @UnsupportedAppUsage + private void handleBindApplication(AppBindData data) { + mDdmSyncStageUpdater.next(Stage.Bind); + +//ccynice + Log.w(TAG, "CCYNICE xxxxxxxxxxxxxxxx reset Build"); + modifyBuildName("DEVICE", SystemProperties.get("ro.product.device",Build.DEVICE)); + modifyBuildName("MODEL", SystemProperties.get("ro.product.model",Build.MODEL)); + modifyBuildName("PRODUCT", SystemProperties.get("ro.product.name",Build.PRODUCT)); + modifyBuildName("BOARD", SystemProperties.get("ro.product.board",Build.BOARD)); + modifyBuildName("MANUFACTURER", SystemProperties.get("ro.product.manufacturer",Build.MANUFACTURER)); + modifyBuildName("BRAND", SystemProperties.get("ro.product.brand",Build.BRAND)); + + modifyBuildName("ID", SystemProperties.get("ro.build.id",Build.ID)); + modifyBuildName("DISPLAY", SystemProperties.get("ro.build.display.id",Build.DISPLAY)); + modifyBuildName("HOST", SystemProperties.get("ro.build.host",Build.HOST)); + modifyBuildName("TYPE", SystemProperties.get("ro.build.type",Build.TYPE)); + modifyBuildName("FINGERPRINT", SystemProperties.get("ro.build.fingerprint",Build.FINGERPRINT)); + + modifyBuildVersionFiled("RELEASE", SystemProperties.get("ro.build.version.release","12.0")); + modifyBuildVersionFiled("INCREMENTAL", SystemProperties.get("ro.build.version.incremental","user")); + // Register the UI Thread as a sensitive thread to the runtime. + VMRuntime.registerSensitiveThread(); + // In the case the stack depth property exists, pass it down to the runtime. + String property = SystemProperties.get("debug.allocTracker.stackDepth"); + if (property.length() != 0) { + VMDebug.setAllocTrackerStackDepth(Integer.parseInt(property)); + } + if (data.trackAllocation) { + DdmVmInternal.setRecentAllocationsTrackingEnabled(true); + } + // Note when this process has started. + Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), + data.startRequestedElapsedTime, data.startRequestedUptime); + + AppCompatCallbacks.install(data.disabledCompatChanges); + // Let libcore handle any compat changes after installing the list of compat changes. + AppSpecializationHooks.handleCompatChangesBeforeBindingApplication(); + + // Initialize the zip path validator callback depending on the targetSdk. + // This has to be after AppCompatCallbacks#install() so that the Compat + // checks work accordingly. + initZipPathValidatorCallback(); + + mBoundApplication = data; + mConfigurationController.setConfiguration(data.config); + mConfigurationController.setCompatConfiguration(data.config); + mConfiguration = mConfigurationController.getConfiguration(); + mCompatibilityInfo = data.compatInfo; + + mProfiler = new Profiler(); + String agent = null; + if (data.initProfilerInfo != null) { + mProfiler.profileFile = data.initProfilerInfo.profileFile; + mProfiler.profileFd = data.initProfilerInfo.profileFd; + mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval; + mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler; + mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput; + mProfiler.mClockType = data.initProfilerInfo.clockType; + if (data.initProfilerInfo.attachAgentDuringBind) { + agent = data.initProfilerInfo.agent; + } + } + + // send up app name; do this *before* waiting for debugger + Process.setArgV0(data.processName); + android.ddm.DdmHandleAppName.setAppName(data.processName, + data.appInfo.packageName, + UserHandle.myUserId()); + VMRuntime.setProcessPackageName(data.appInfo.packageName); + mDdmSyncStageUpdater.next(Stage.Named); + + // Pass data directory path to ART. This is used for caching information and + // should be set before any application code is loaded. + VMRuntime.setProcessDataDirectory(data.appInfo.dataDir); + + if (mProfiler.profileFd != null) { + mProfiler.startProfiling(); + } + + // If the app is Honeycomb MR1 or earlier, switch its AsyncTask + // implementation to use the pool executor. Normally, we use the + // serialized executor as the default. This has to happen in the + // main thread so the main looper is set right. + if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { + AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + // Let the util.*Array classes maintain "undefined" for apps targeting Pie or earlier. + UtilConfig.setThrowExceptionForUpperArrayOutOfBounds( + data.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q); + + Message.updateCheckRecycle(data.appInfo.targetSdkVersion); + + // Supply the targetSdkVersion to the UI rendering module, which may + // need it in cases where it does not have access to the appInfo. + android.graphics.Compatibility.setTargetSdkVersion(data.appInfo.targetSdkVersion); + + /* + * Before spawning a new process, reset the time zone to be the system time zone. + * This needs to be done because the system time zone could have changed after the + * the spawning of this process. Without doing this this process would have the incorrect + * system time zone. + */ + TimeZone.setDefault(null); + + /* + * Set the LocaleList. This may change once we create the App Context. + */ + LocaleList.setDefault(data.config.getLocales()); + + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + try { + Typeface.setSystemFontMap(data.mSerializedSystemFontMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to parse serialized system font map"); + Typeface.loadPreinstalledSystemFontMap(); + } + } + + synchronized (mResourcesManager) { + /* + * Update the system configuration since its preloaded and might not + * reflect configuration changes. The configuration object passed + * in AppBindData can be safely assumed to be up to date + */ + mResourcesManager.applyConfigurationToResources(data.config, data.compatInfo); + mCurDefaultDisplayDpi = data.config.densityDpi; + + // This calls mResourcesManager so keep it within the synchronized block. + mConfigurationController.applyCompatConfiguration(); + } + + final boolean isSdkSandbox = data.sdkSandboxClientAppPackage != null; + data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */, + false /* securityViolation */, true /* includeCode */, + false /* registerPackage */, isSdkSandbox); + if (isSdkSandbox) { + data.info.setSdkSandboxStorage(data.sdkSandboxClientAppVolumeUuid, + data.sdkSandboxClientAppPackage); + } + + if (agent != null) { + handleAttachAgent(agent, data.info); + } + + /** + * Switch this process to density compatibility mode if needed. + */ + if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) + == 0) { + mDensityCompatMode = true; + Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); + } + mConfigurationController.updateDefaultDensity(data.config.densityDpi); + + // mCoreSettings is only updated from the main thread, while this function is only called + // from main thread as well, so no need to lock here. + final String use24HourSetting = mCoreSettings.getString(Settings.System.TIME_12_24); + Boolean is24Hr = null; + if (use24HourSetting != null) { + is24Hr = "24".equals(use24HourSetting) ? Boolean.TRUE : Boolean.FALSE; + } + // null : use locale default for 12/24 hour formatting, + // false : use 12 hour format, + // true : use 24 hour format. + DateFormat.set24HourTimePref(is24Hr); + + updateDebugViewAttributeState(); + + StrictMode.initThreadDefaults(data.appInfo); + StrictMode.initVmDefaults(data.appInfo); + + // Allow binder tracing, and application-generated systrace messages if we're profileable. + boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable(); + Trace.setAppTracingAllowed(isAppProfileable); + if ((isAppProfileable || Build.IS_DEBUGGABLE) && data.enableBinderTracking) { + Binder.enableStackTracking(); + } + + // Initialize heap profiling. + if (isAppProfileable || Build.IS_DEBUGGABLE) { + nInitZygoteChildHeapProfiling(); + } + + // Allow renderer debugging features if we're debuggable. + HardwareRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE); + HardwareRenderer.setPackageName(data.appInfo.packageName); + + // Pass the current context to HardwareRenderer + HardwareRenderer.setContextForInit(getSystemContext()); + if (data.persistent) { + HardwareRenderer.setIsSystemOrPersistent(); + } + + // Instrumentation info affects the class loader, so load it before + // setting up the app context. + final InstrumentationInfo ii; + if (data.instrumentationName != null) { + ii = prepareInstrumentation(data); + } else { + ii = null; + } + + final IActivityManager mgr = ActivityManager.getService(); + final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); + mConfigurationController.updateLocaleListFromAppContext(appContext); + + // Initialize the default http proxy in this process. + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies"); + try { + // In pre-boot mode (doing initial launch to collect password), not all system is up. + // This includes the connectivity service, so trying to obtain ConnectivityManager at + // that point would return null. Check whether the ConnectivityService is available, and + // avoid crashing with a NullPointerException if it is not. + final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + if (b != null) { + final ConnectivityManager cm = + appContext.getSystemService(ConnectivityManager.class); + Proxy.setHttpProxyConfiguration(cm.getDefaultProxy()); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + if (!Process.isIsolated()) { + final int oldMask = StrictMode.allowThreadDiskWritesMask(); + try { + setupGraphicsSupport(appContext); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } else { + HardwareRenderer.setIsolatedProcess(true); + } + + // Install the Network Security Config Provider. This must happen before the application + // code is loaded to prevent issues with instances of TLS objects being created before + // the provider is installed. + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "NetworkSecurityConfigProvider.install"); + NetworkSecurityConfigProvider.install(appContext); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + + // For backward compatibility, TrafficStats needs static access to the application context. + // But for isolated apps which cannot access network related services, service discovery + // is restricted. Hence, calling this would result in NPE. + if (!Process.isIsolated()) { + TrafficStats.init(appContext); + } + + // Continue loading instrumentation. + if (ii != null) { + initInstrumentation(ii, data, appContext); + } else { + mInstrumentation = new Instrumentation(); + mInstrumentation.basicInit(this); + } + + if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) { + dalvik.system.VMRuntime.getRuntime().clearGrowthLimit(); + } else { + // Small heap, clamp to the current growth limit and let the heap release + // pages after the growth limit to the non growth limit capacity. b/18387825 + dalvik.system.VMRuntime.getRuntime().clampGrowthLimit(); + } + + // Allow disk access during application and provider setup. This could + // block processing ordered broadcasts, but later processing would + // probably end up doing the same disk access. + Application app; + final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); + final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy(); + + if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) { + mDdmSyncStageUpdater.next(Stage.Debugger); + if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) { + waitForDebugger(data); + } else if (data.debugMode == ApplicationThreadConstants.DEBUG_SUSPEND) { + suspendAllAndSendVmStart(data); + } + // Nothing special to do in case of DEBUG_ON. + } + mDdmSyncStageUpdater.next(Stage.Running); + + try { + // If the app is being launched for full backup or restore, bring it up in + // a restricted environment with the base application class. + app = data.info.makeApplicationInner(data.restrictedBackupMode, null); + + // Propagate autofill compat state + app.setAutofillOptions(data.autofillOptions); + + // Propagate Content Capture options + app.setContentCaptureOptions(data.contentCaptureOptions); + sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName); + + mInitialApplication = app; + final boolean updateHttpProxy; + synchronized (this) { + updateHttpProxy = mUpdateHttpProxyOnBind; + // This synchronized block ensures that any subsequent call to updateHttpProxy() + // will see a non-null mInitialApplication. + } + if (updateHttpProxy) { + ActivityThread.updateHttpProxy(app); + } + + // don't bring up providers in restricted mode; they may depend on the + // app's custom Application class + if (!data.restrictedBackupMode) { + if (!ArrayUtils.isEmpty(data.providers)) { + installContentProviders(app, data.providers); + } + } + + // Do this after providers, since instrumentation tests generally start their + // test thread at this point, and we don't want that racing. + try { + mInstrumentation.onCreate(data.instrumentationArgs); + } + catch (Exception e) { + throw new RuntimeException( + "Exception thrown in onCreate() of " + + data.instrumentationName + ": " + e.toString(), e); + } + try { + mInstrumentation.callApplicationOnCreate(app); + } catch (Exception e) { + if (!mInstrumentation.onException(app, e)) { + throw new RuntimeException( + "Unable to create application " + app.getClass().getName() + + ": " + e.toString(), e); + } + } + } finally { + // If the app targets < O-MR1, or doesn't change the thread policy + // during startup, clobber the policy to maintain behavior of b/36951662 + if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1 + || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) { + StrictMode.setThreadPolicy(savedPolicy); + } + } + + // Preload fonts resources + FontsContract.setApplicationContextForResources(appContext); + if (!Process.isIsolated()) { + try { + final ApplicationInfo info = + getPackageManager().getApplicationInfo( + data.appInfo.packageName, + PackageManager.GET_META_DATA /*flags*/, + UserHandle.myUserId()); + if (info.metaData != null) { + final int preloadedFontsResource = info.metaData.getInt( + ApplicationInfo.METADATA_PRELOADED_FONTS, 0); + if (preloadedFontsResource != 0) { + data.info.getResources().preloadFonts(preloadedFontsResource); + } + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + try { + mgr.finishAttachApplication(mStartSeq); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + + // Set binder transaction callback after finishing bindApplication + Binder.setTransactionCallback(new IBinderCallback() { + @Override + public void onTransactionError(int pid, int code, int flags, int err) { + try { + mgr.frozenBinderTransactionDetected(pid, code, flags, err); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + }); + } + + @UnsupportedAppUsage + private void waitForDebugger(AppBindData data) { + final IActivityManager mgr = ActivityManager.getService(); + Slog.w(TAG, "Application " + data.info.getPackageName() + + " is waiting for the debugger ..."); + + try { + mgr.showWaitingForDebugger(mAppThread, true); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + + Debug.waitForDebugger(); + + try { + mgr.showWaitingForDebugger(mAppThread, false); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + @UnsupportedAppUsage + private void suspendAllAndSendVmStart(AppBindData data) { + final IActivityManager mgr = ActivityManager.getService(); + Slog.w(TAG, "Application " + data.info.getPackageName() + + " is suspending. Debugger needs to resume to continue."); + + try { + mgr.showWaitingForDebugger(mAppThread, true); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + + Debug.suspendAllAndSendVmStart(); + + try { + mgr.showWaitingForDebugger(mAppThread, false); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip + * entry names. + * Otherwise: clear the callback to the default validation. + */ + private void initZipPathValidatorCallback() { + if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) { + ZipPathValidator.setCallback(new SafeZipPathValidatorCallback()); + } else { + ZipPathValidator.clearCallback(); + } + } + + private void handleSetContentCaptureOptionsCallback(String packageName) { + if (mContentCaptureOptionsCallback != null) { + return; + } + + IBinder b = ServiceManager.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); + if (b == null) { + return; + } + + IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b); + mContentCaptureOptionsCallback = new IContentCaptureOptionsCallback.Stub() { + @Override + public void setContentCaptureOptions(ContentCaptureOptions options) + throws RemoteException { + if (mInitialApplication != null) { + mInitialApplication.setContentCaptureOptions(options); + } + } + }; + try { + service.registerContentCaptureOptionsCallback(packageName, + mContentCaptureOptionsCallback); + } catch (RemoteException e) { + Slog.w(TAG, "registerContentCaptureOptionsCallback() failed: " + + packageName, e); + mContentCaptureOptionsCallback = null; + } + } + + private void handleInstrumentWithoutRestart(AppBindData data) { + try { + data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + data.info = getPackageInfoNoCheck(data.appInfo); + mInstrumentingWithoutRestart = true; + final InstrumentationInfo ii = prepareInstrumentation(data); + final ContextImpl appContext = + ContextImpl.createAppContext(this, data.info); + + initInstrumentation(ii, data, appContext); + + try { + mInstrumentation.onCreate(data.instrumentationArgs); + } catch (Exception e) { + throw new RuntimeException( + "Exception thrown in onCreate() of " + + data.instrumentationName + ": " + e.toString(), e); + } + + } catch (Exception e) { + Slog.e(TAG, "Error in handleInstrumentWithoutRestart", e); + } + } + + private InstrumentationInfo prepareInstrumentation(AppBindData data) { + final InstrumentationInfo ii; + try { + ii = getPackageManager().getInstrumentationInfoAsUser(data.instrumentationName, + 0 /* flags */, UserHandle.myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + if (ii == null) { + throw new RuntimeException( + "Unable to find instrumentation info for: " + data.instrumentationName); + } + + // Warn of potential ABI mismatches. + if (!Objects.equals(data.appInfo.primaryCpuAbi, ii.primaryCpuAbi) + || !Objects.equals(data.appInfo.secondaryCpuAbi, ii.secondaryCpuAbi)) { + Slog.w(TAG, "Package uses different ABI(s) than its instrumentation: " + + "package[" + data.appInfo.packageName + "]: " + + data.appInfo.primaryCpuAbi + ", " + data.appInfo.secondaryCpuAbi + + " instrumentation[" + ii.packageName + "]: " + + ii.primaryCpuAbi + ", " + ii.secondaryCpuAbi); + } + + mInstrumentationPackageName = ii.packageName; + mInstrumentationAppDir = ii.sourceDir; + mInstrumentationSplitAppDirs = ii.splitSourceDirs; + mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii); + mInstrumentedAppDir = data.info.getAppDir(); + mInstrumentedSplitAppDirs = data.info.getSplitAppDirs(); + mInstrumentedLibDir = data.info.getLibDir(); + + return ii; + } + + private void initInstrumentation( + InstrumentationInfo ii, AppBindData data, ContextImpl appContext) { + ApplicationInfo instrApp; + try { + instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0, + UserHandle.myUserId()); + } catch (RemoteException e) { + instrApp = null; + } + if (instrApp == null) { + instrApp = new ApplicationInfo(); + } + ii.copyTo(instrApp); + instrApp.initForUser(UserHandle.myUserId()); + final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, + appContext.getClassLoader(), false, true, false); + + // The test context's op package name == the target app's op package name, because + // the app ops manager checks the op package name against the real calling UID, + // which is what the target package name is associated with. + // In the case of instrumenting an sdk running in the sdk sandbox, appContext refers + // to the context of the sdk running in the sandbox. Since the sandbox does not have + // access to data outside the sandbox, we require the instrContext to point to the + // sdk in the sandbox as well, and not to the test context. + final ContextImpl instrContext = + (data.isSdkInSandbox) + ? appContext + : ContextImpl.createAppContext(this, pi, appContext.getOpPackageName()); + + try { + final ClassLoader cl = instrContext.getClassLoader(); + mInstrumentation = (Instrumentation) + cl.loadClass(data.instrumentationName.getClassName()).newInstance(); + } catch (Exception e) { + throw new RuntimeException( + "Unable to instantiate instrumentation " + + data.instrumentationName + ": " + e.toString(), e); + } + + final ComponentName component = new ComponentName(ii.packageName, ii.name); + mInstrumentation.init(this, instrContext, appContext, component, + data.instrumentationWatcher, data.instrumentationUiAutomationConnection); + + if (mProfiler.profileFile != null && !ii.handleProfiling + && mProfiler.profileFd == null) { + mProfiler.handlingProfiling = true; + final File file = new File(mProfiler.profileFile); + file.getParentFile().mkdirs(); + Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); + } + } + + private void handleFinishInstrumentationWithoutRestart() { + mInstrumentation.onDestroy(); + mInstrumentationPackageName = null; + mInstrumentationAppDir = null; + mInstrumentationSplitAppDirs = null; + mInstrumentationLibDir = null; + mInstrumentedAppDir = null; + mInstrumentedSplitAppDirs = null; + mInstrumentedLibDir = null; + mInstrumentingWithoutRestart = false; + } + + /*package*/ final void finishInstrumentation(int resultCode, Bundle results) { + IActivityManager am = ActivityManager.getService(); + if (mProfiler.profileFile != null && mProfiler.handlingProfiling + && mProfiler.profileFd == null) { + Debug.stopMethodTracing(); + } + //Slog.i(TAG, "am: " + ActivityManager.getService() + // + ", app thr: " + mAppThread); + try { + am.finishInstrumentation(mAppThread, resultCode, results); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + if (mInstrumentingWithoutRestart) { + sendMessage(H.FINISH_INSTRUMENTATION_WITHOUT_RESTART, null); + } + } + + @UnsupportedAppUsage + private void installContentProviders( + Context context, List providers) { + final ArrayList results = new ArrayList<>(); + + for (ProviderInfo cpi : providers) { + if (DEBUG_PROVIDER) { + StringBuilder buf = new StringBuilder(128); + buf.append("Pub "); + buf.append(cpi.authority); + buf.append(": "); + buf.append(cpi.name); + Log.i(TAG, buf.toString()); + } + ContentProviderHolder cph = installProvider(context, null, cpi, + false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); + if (cph != null) { + cph.noReleaseNeeded = true; + results.add(cph); + } + } + + try { + ActivityManager.getService().publishContentProviders( + getApplicationThread(), results); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + @UnsupportedAppUsage + public final IContentProvider acquireProvider( + Context c, String auth, int userId, boolean stable) { + final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); + if (provider != null) { + return provider; + } + + // There is a possible race here. Another thread may try to acquire + // the same provider at the same time. When this happens, we want to ensure + // that the first one wins. + // Note that we cannot hold the lock while acquiring and installing the + // provider since it might take a long time to run and it could also potentially + // be re-entrant in the case where the provider is in the same process. + ContentProviderHolder holder; + final ProviderKey key = getGetProviderKey(auth, userId); + try { + synchronized (key) { + holder = ActivityManager.getService().getContentProvider( + getApplicationThread(), c.getOpPackageName(), auth, userId, stable); + // If the returned holder is non-null but its provider is null and it's not + // local, we'll need to wait for the publishing of the provider. + if (holder != null && holder.provider == null && !holder.mLocal) { + synchronized (key.mLock) { + if (key.mHolder != null) { + if (DEBUG_PROVIDER) { + Slog.i(TAG, "already received provider: " + auth); + } + } else { + key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + } + holder = key.mHolder; + } + if (holder != null && holder.provider == null) { + // probably timed out + holder = null; + } + } + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } catch (InterruptedException e) { + holder = null; + } finally { + // Clear the holder from the key since the key itself is never cleared. + synchronized (key.mLock) { + key.mHolder = null; + } + } + if (holder == null) { + if (UserManager.get(c).isUserUnlocked(userId)) { + Slog.e(TAG, "Failed to find provider info for " + auth); + } else { + Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)"); + } + return null; + } + + // Install provider will increment the reference count for us, and break + // any ties in the race. + holder = installProvider(c, holder, holder.info, + true /*noisy*/, holder.noReleaseNeeded, stable); + return holder.provider; + } + + private ProviderKey getGetProviderKey(String auth, int userId) { + final ProviderKey key = new ProviderKey(auth, userId); + synchronized (mGetProviderKeys) { + ProviderKey lock = mGetProviderKeys.computeIfAbsent(key, k -> k); + return lock; + } + } + + private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) { + if (stable) { + prc.stableCount += 1; + if (prc.stableCount == 1) { + // We are acquiring a new stable reference on the provider. + int unstableDelta; + if (prc.removePending) { + // We have a pending remove operation, which is holding the + // last unstable reference. At this point we are converting + // that unstable reference to our new stable reference. + unstableDelta = -1; + // Cancel the removal of the provider. + if (DEBUG_PROVIDER) { + Slog.v(TAG, "incProviderRef: stable " + + "snatched provider from the jaws of death"); + } + prc.removePending = false; + // There is a race! It fails to remove the message, which + // will be handled in completeRemoveProvider(). + mH.removeMessages(H.REMOVE_PROVIDER, prc); + } else { + unstableDelta = 0; + } + try { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "incProviderRef Now stable - " + + prc.holder.info.name + ": unstableDelta=" + + unstableDelta); + } + ActivityManager.getService().refContentProvider( + prc.holder.connection, 1, unstableDelta); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + } else { + prc.unstableCount += 1; + if (prc.unstableCount == 1) { + // We are acquiring a new unstable reference on the provider. + if (prc.removePending) { + // Oh look, we actually have a remove pending for the + // provider, which is still holding the last unstable + // reference. We just need to cancel that to take new + // ownership of the reference. + if (DEBUG_PROVIDER) { + Slog.v(TAG, "incProviderRef: unstable " + + "snatched provider from the jaws of death"); + } + prc.removePending = false; + mH.removeMessages(H.REMOVE_PROVIDER, prc); + } else { + // First unstable ref, increment our count in the + // activity manager. + try { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "incProviderRef: Now unstable - " + + prc.holder.info.name); + } + ActivityManager.getService().refContentProvider( + prc.holder.connection, 0, 1); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + } + } + } + + @UnsupportedAppUsage + public final IContentProvider acquireExistingProvider( + Context c, String auth, int userId, boolean stable) { + synchronized (mProviderMap) { + final ProviderKey key = new ProviderKey(auth, userId); + final ProviderClientRecord pr = mProviderMap.get(key); + if (pr == null) { + return null; + } + + IContentProvider provider = pr.mProvider; + IBinder jBinder = provider.asBinder(); + if (!jBinder.isBinderAlive()) { + // The hosting process of the provider has died; we can't + // use this one. + Log.i(TAG, "Acquiring provider " + auth + " for user " + userId + + ": existing object's process dead"); + handleUnstableProviderDiedLocked(jBinder, true); + return null; + } + + // Only increment the ref count if we have one. If we don't then the + // provider is not reference counted and never needs to be released. + ProviderRefCount prc = mProviderRefCountMap.get(jBinder); + if (prc != null) { + incProviderRefLocked(prc, stable); + } + return provider; + } + } + + @UnsupportedAppUsage + public final boolean releaseProvider(IContentProvider provider, boolean stable) { + if (provider == null) { + return false; + } + + IBinder jBinder = provider.asBinder(); + synchronized (mProviderMap) { + ProviderRefCount prc = mProviderRefCountMap.get(jBinder); + if (prc == null) { + // The provider has no ref count, no release is needed. + return false; + } + + boolean lastRef = false; + if (stable) { + if (prc.stableCount == 0) { + if (DEBUG_PROVIDER) Slog.v(TAG, + "releaseProvider: stable ref count already 0, how?"); + return false; + } + prc.stableCount -= 1; + if (prc.stableCount == 0) { + // What we do at this point depends on whether there are + // any unstable refs left: if there are, we just tell the + // activity manager to decrement its stable count; if there + // aren't, we need to enqueue this provider to be removed, + // and convert to holding a single unstable ref while + // doing so. + lastRef = prc.unstableCount == 0; + try { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "releaseProvider: No longer stable w/lastRef=" + + lastRef + " - " + prc.holder.info.name); + } + ActivityManager.getService().refContentProvider( + prc.holder.connection, -1, lastRef ? 1 : 0); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + } else { + if (prc.unstableCount == 0) { + if (DEBUG_PROVIDER) Slog.v(TAG, + "releaseProvider: unstable ref count already 0, how?"); + return false; + } + prc.unstableCount -= 1; + if (prc.unstableCount == 0) { + // If this is the last reference, we need to enqueue + // this provider to be removed instead of telling the + // activity manager to remove it at this point. + lastRef = prc.stableCount == 0; + if (!lastRef) { + try { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "releaseProvider: No longer unstable - " + + prc.holder.info.name); + } + ActivityManager.getService().refContentProvider( + prc.holder.connection, 0, -1); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + } + } + + if (lastRef) { + if (!prc.removePending) { + // Schedule the actual remove asynchronously, since we don't know the context + // this will be called in. + if (DEBUG_PROVIDER) { + Slog.v(TAG, "releaseProvider: Enqueueing pending removal - " + + prc.holder.info.name); + } + prc.removePending = true; + Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc); + mH.sendMessageDelayed(msg, CONTENT_PROVIDER_RETAIN_TIME); + } else { + Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name); + } + } + return true; + } + } + + final void completeRemoveProvider(ProviderRefCount prc) { + synchronized (mProviderMap) { + if (!prc.removePending) { + // There was a race! Some other client managed to acquire + // the provider before the removal was completed. + // Abort the removal. We will do it later. + if (DEBUG_PROVIDER) Slog.v(TAG, "completeRemoveProvider: lost the race, " + + "provider still in use"); + return; + } + + // More complicated race!! Some client managed to acquire the + // provider and release it before the removal was completed. + // Continue the removal, and abort the next remove message. + prc.removePending = false; + + final IBinder jBinder = prc.holder.provider.asBinder(); + ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder); + if (existingPrc == prc) { + mProviderRefCountMap.remove(jBinder); + } + + for (int i=mProviderMap.size()-1; i>=0; i--) { + ProviderClientRecord pr = mProviderMap.valueAt(i); + IBinder myBinder = pr.mProvider.asBinder(); + if (myBinder == jBinder) { + mProviderMap.removeAt(i); + } + } + } + + try { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "removeProvider: Invoking ActivityManagerService." + + "removeContentProvider(" + prc.holder.info.name + ")"); + } + ActivityManager.getService().removeContentProvider( + prc.holder.connection, false); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + + @UnsupportedAppUsage + final void handleUnstableProviderDied(IBinder provider, boolean fromClient) { + synchronized (mProviderMap) { + handleUnstableProviderDiedLocked(provider, fromClient); + } + } + + final void handleUnstableProviderDiedLocked(IBinder provider, boolean fromClient) { + ProviderRefCount prc = mProviderRefCountMap.get(provider); + if (prc != null) { + if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider " + + provider + " " + prc.holder.info.name); + mProviderRefCountMap.remove(provider); + for (int i=mProviderMap.size()-1; i>=0; i--) { + ProviderClientRecord pr = mProviderMap.valueAt(i); + if (pr != null && pr.mProvider.asBinder() == provider) { + Slog.i(TAG, "Removing dead content provider:" + pr.mProvider.toString()); + mProviderMap.removeAt(i); + } + } + + if (fromClient) { + // We found out about this due to execution in our client + // code. Tell the activity manager about it now, to ensure + // that the next time we go to do anything with the provider + // it knows it is dead (so we don't race with its death + // notification). + try { + ActivityManager.getService().unstableProviderDied( + prc.holder.connection); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + } + } + + final void appNotRespondingViaProvider(IBinder provider) { + synchronized (mProviderMap) { + ProviderRefCount prc = mProviderRefCountMap.get(provider); + if (prc != null) { + try { + ActivityManager.getService() + .appNotRespondingViaProvider(prc.holder.connection); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + + private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider, + ContentProvider localProvider, ContentProviderHolder holder) { + final String auths[] = holder.info.authority.split(";"); + final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid); + + if (provider != null) { + // If this provider is hosted by the core OS and cannot be upgraded, + // then I guess we're okay doing blocking calls to it. + for (String auth : auths) { + switch (auth) { + case ContactsContract.AUTHORITY: + case CallLog.AUTHORITY: + case CallLog.SHADOW_AUTHORITY: + case BlockedNumberContract.AUTHORITY: + case CalendarContract.AUTHORITY: + case Downloads.Impl.AUTHORITY: + case "telephony": + Binder.allowBlocking(provider.asBinder()); + } + } + } + + final ProviderClientRecord pcr = new ProviderClientRecord( + auths, provider, localProvider, holder); + for (String auth : auths) { + final ProviderKey key = new ProviderKey(auth, userId); + final ProviderClientRecord existing = mProviderMap.get(key); + if (existing != null) { + Slog.w(TAG, "Content provider " + pcr.mHolder.info.name + + " already published as " + auth); + } else { + mProviderMap.put(key, pcr); + } + } + return pcr; + } + + /** + * Installs the provider. + * + * Providers that are local to the process or that come from the system server + * may be installed permanently which is indicated by setting noReleaseNeeded to true. + * Other remote providers are reference counted. The initial reference count + * for all reference counted providers is one. Providers that are not reference + * counted do not have a reference count (at all). + * + * This method detects when a provider has already been installed. When this happens, + * it increments the reference count of the existing provider (if appropriate) + * and returns the existing provider. This can happen due to concurrent + * attempts to acquire the same provider. + */ + @UnsupportedAppUsage + private ContentProviderHolder installProvider(Context context, + ContentProviderHolder holder, ProviderInfo info, + boolean noisy, boolean noReleaseNeeded, boolean stable) { + ContentProvider localProvider = null; + IContentProvider provider; + if (holder == null || holder.provider == null) { + if (DEBUG_PROVIDER || noisy) { + Slog.d(TAG, "Loading provider " + info.authority + ": " + + info.name); + } + Context c = null; + ApplicationInfo ai = info.applicationInfo; + if (context.getPackageName().equals(ai.packageName)) { + c = context; + } else if (mInitialApplication != null && + mInitialApplication.getPackageName().equals(ai.packageName)) { + c = mInitialApplication; + } else { + try { + c = context.createPackageContext(ai.packageName, + Context.CONTEXT_INCLUDE_CODE); + } catch (PackageManager.NameNotFoundException e) { + // Ignore + } + } + if (c == null) { + Slog.w(TAG, "Unable to get context for package " + + ai.packageName + + " while loading content provider " + + info.name); + return null; + } + + if (info.splitName != null) { + try { + c = c.createContextForSplit(info.splitName); + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + } + if (info.attributionTags != null && info.attributionTags.length > 0) { + final String attributionTag = info.attributionTags[0]; + c = c.createAttributionContext(attributionTag); + } + + try { + final java.lang.ClassLoader cl = c.getClassLoader(); + LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); + if (packageInfo == null) { + // System startup case. + packageInfo = getSystemContext().mPackageInfo; + } + localProvider = packageInfo.getAppFactory() + .instantiateProvider(cl, info.name); + provider = localProvider.getIContentProvider(); + if (provider == null) { + Slog.e(TAG, "Failed to instantiate class " + + info.name + " from sourceDir " + + info.applicationInfo.sourceDir); + return null; + } + if (DEBUG_PROVIDER) Slog.v( + TAG, "Instantiating local provider " + info.name); + // XXX Need to create the correct context for this provider. + localProvider.attachInfo(c, info); + } catch (java.lang.Exception e) { + if (!mInstrumentation.onException(null, e)) { + throw new RuntimeException( + "Unable to get provider " + info.name + + ": " + e.toString(), e); + } + return null; + } + } else { + provider = holder.provider; + if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": " + + info.name); + } + + ContentProviderHolder retHolder; + + synchronized (mProviderMap) { + if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider + + " / " + info.name); + IBinder jBinder = provider.asBinder(); + if (localProvider != null) { + ComponentName cname = new ComponentName(info.packageName, info.name); + ProviderClientRecord pr = mLocalProvidersByName.get(cname); + if (pr != null) { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "installProvider: lost the race, " + + "using existing local provider"); + } + provider = pr.mProvider; + } else { + holder = new ContentProviderHolder(info); + holder.provider = provider; + holder.noReleaseNeeded = true; + pr = installProviderAuthoritiesLocked(provider, localProvider, holder); + mLocalProviders.put(jBinder, pr); + mLocalProvidersByName.put(cname, pr); + } + retHolder = pr.mHolder; + } else { + ProviderRefCount prc = mProviderRefCountMap.get(jBinder); + if (prc != null) { + if (DEBUG_PROVIDER) { + Slog.v(TAG, "installProvider: lost the race, updating ref count"); + } + // We need to transfer our new reference to the existing + // ref count, releasing the old one... but only if + // release is needed (that is, it is not running in the + // system process). + if (!noReleaseNeeded) { + incProviderRefLocked(prc, stable); + try { + ActivityManager.getService().removeContentProvider( + holder.connection, stable); + } catch (RemoteException e) { + //do nothing content provider object is dead any way + } + } + } else { + ProviderClientRecord client = installProviderAuthoritiesLocked( + provider, localProvider, holder); + if (noReleaseNeeded) { + prc = new ProviderRefCount(holder, client, 1000, 1000); + } else { + prc = stable + ? new ProviderRefCount(holder, client, 1, 0) + : new ProviderRefCount(holder, client, 0, 1); + } + mProviderRefCountMap.put(jBinder, prc); + } + retHolder = prc.holder; + } + } + return retHolder; + } + + private void handleRunIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { + try { + Method main = Class.forName(entryPoint).getMethod("main", String[].class); + main.invoke(null, new Object[]{entryPointArgs}); + } catch (ReflectiveOperationException e) { + throw new AndroidRuntimeException("runIsolatedEntryPoint failed", e); + } + // The process will be empty after this method returns; exit the VM now. + System.exit(0); + } + + @UnsupportedAppUsage + private void attach(boolean system, long startSeq) { + sCurrentActivityThread = this; + mConfigurationController = new ConfigurationController(this); + mSystemThread = system; + mStartSeq = startSeq; + mDdmSyncStageUpdater.next(Stage.Attach); + + if (!system) { + android.ddm.DdmHandleAppName.setAppName("", + UserHandle.myUserId()); + RuntimeInit.setApplicationObject(mAppThread.asBinder()); + final IActivityManager mgr = ActivityManager.getService(); + try { + mgr.attachApplication(mAppThread, startSeq); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + // Watch for getting close to heap limit. + BinderInternal.addGcWatcher(new Runnable() { + @Override public void run() { + if (!mSomeActivitiesChanged) { + return; + } + Runtime runtime = Runtime.getRuntime(); + long dalvikMax = runtime.maxMemory(); + long dalvikUsed = runtime.totalMemory() - runtime.freeMemory(); + if (dalvikUsed > ((3*dalvikMax)/4)) { + if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024) + + " total=" + (runtime.totalMemory()/1024) + + " used=" + (dalvikUsed/1024)); + mSomeActivitiesChanged = false; + try { + ActivityTaskManager.getService().releaseSomeActivities(mAppThread); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + }); + } else { + // Don't set application object here -- if the system crashes, + // we can't display an alert, we just want to die die die. + android.ddm.DdmHandleAppName.setAppName("system_process", + UserHandle.myUserId()); + try { + mInstrumentation = new Instrumentation(); + mInstrumentation.basicInit(this); + ContextImpl context = ContextImpl.createAppContext( + this, getSystemContext().mPackageInfo); + mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null); + mInitialApplication.onCreate(); + } catch (Exception e) { + throw new RuntimeException( + "Unable to instantiate Application():" + e.toString(), e); + } + } + + ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> { + synchronized (mResourcesManager) { + // We need to apply this change to the resources immediately, because upon returning + // the view hierarchy will be informed about it. + if (mResourcesManager.applyConfigurationToResources(globalConfig, + null /* compat */)) { + mConfigurationController.updateLocaleListFromAppContext( + mInitialApplication.getApplicationContext()); + + // This actually changed the resources! Tell everyone about it. + final Configuration updatedConfig = + mConfigurationController.updatePendingConfiguration(globalConfig); + if (updatedConfig != null) { + sendMessage(H.CONFIGURATION_CHANGED, globalConfig); + mPendingConfiguration = updatedConfig; + } + } + } + }; + ViewRootImpl.addConfigCallback(configChangedCallback); + } + + @UnsupportedAppUsage + public static ActivityThread systemMain() { + ThreadedRenderer.initForSystemProcess(); + ActivityThread thread = new ActivityThread(); + thread.attach(true, 0); + return thread; + } + + public static void updateHttpProxy(@NonNull Context context) { + final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); + Proxy.setHttpProxyConfiguration(cm.getDefaultProxy()); + } + + @UnsupportedAppUsage + public final void installSystemProviders(List providers) { + if (providers != null) { + installContentProviders(mInitialApplication, providers); + } + } + + /** + * Caller should NEVER mutate the Bundle returned from here + */ + Bundle getCoreSettings() { + synchronized (mCoreSettingsLock) { + return mCoreSettings; + } + } + + public int getIntCoreSetting(String key, int defaultValue) { + synchronized (mCoreSettingsLock) { + if (mCoreSettings != null) { + return mCoreSettings.getInt(key, defaultValue); + } + return defaultValue; + } + } + + /** + * Get the string value of the given key from core settings. + */ + public String getStringCoreSetting(String key, String defaultValue) { + synchronized (mCoreSettingsLock) { + if (mCoreSettings != null) { + return mCoreSettings.getString(key, defaultValue); + } + return defaultValue; + } + } + + float getFloatCoreSetting(String key, float defaultValue) { + synchronized (mCoreSettingsLock) { + if (mCoreSettings != null) { + return mCoreSettings.getFloat(key, defaultValue); + } + return defaultValue; + } + } + + private static class AndroidOs extends ForwardingOs { + /** + * Install selective syscall interception. For example, this is used to + * implement special filesystem paths that will be redirected to + * {@link ContentResolver#openFileDescriptor(Uri, String)}. + */ + public static void install() { + // If feature is disabled, we don't need to install + if (!DEPRECATE_DATA_COLUMNS) return; + + // Install interception and make sure it sticks! + Os def; + do { + def = Os.getDefault(); + } while (!Os.compareAndSetDefault(def, new AndroidOs(def))); + } + + private AndroidOs(Os os) { + super(os); + } + + private FileDescriptor openDeprecatedDataPath(String path, int mode) throws ErrnoException { + final Uri uri = ContentResolver.translateDeprecatedDataPath(path); + Log.v(TAG, "Redirecting " + path + " to " + uri); + + final ContentResolver cr = currentActivityThread().getApplication() + .getContentResolver(); + try { + final FileDescriptor fd = new FileDescriptor(); + fd.setInt$(cr.openFileDescriptor(uri, + FileUtils.translateModePosixToString(mode)).detachFd()); + return fd; + } catch (SecurityException e) { + throw new ErrnoException(e.getMessage(), OsConstants.EACCES); + } catch (FileNotFoundException e) { + throw new ErrnoException(e.getMessage(), OsConstants.ENOENT); + } + } + + private void deleteDeprecatedDataPath(String path) throws ErrnoException { + final Uri uri = ContentResolver.translateDeprecatedDataPath(path); + Log.v(TAG, "Redirecting " + path + " to " + uri); + + final ContentResolver cr = currentActivityThread().getApplication() + .getContentResolver(); + try { + if (cr.delete(uri, null, null) == 0) { + throw new FileNotFoundException(); + } + } catch (SecurityException e) { + throw new ErrnoException(e.getMessage(), OsConstants.EACCES); + } catch (FileNotFoundException e) { + throw new ErrnoException(e.getMessage(), OsConstants.ENOENT); + } + } + + @Override + public boolean access(String path, int mode) throws ErrnoException { + if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { + // If we opened it okay, then access check succeeded + IoUtils.closeQuietly( + openDeprecatedDataPath(path, FileUtils.translateModeAccessToPosix(mode))); + return true; + } else { + return super.access(path, mode); + } + } + + @Override + public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { + if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { + return openDeprecatedDataPath(path, mode); + } else { + return super.open(path, flags, mode); + } + } + + @Override + public StructStat stat(String path) throws ErrnoException { + if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { + final FileDescriptor fd = openDeprecatedDataPath(path, OsConstants.O_RDONLY); + try { + return android.system.Os.fstat(fd); + } finally { + IoUtils.closeQuietly(fd); + } + } else { + return super.stat(path); + } + } + + @Override + public void unlink(String path) throws ErrnoException { + if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { + deleteDeprecatedDataPath(path); + } else { + super.unlink(path); + } + } + + @Override + public void remove(String path) throws ErrnoException { + if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { + deleteDeprecatedDataPath(path); + } else { + super.remove(path); + } + } + + @Override + public void rename(String oldPath, String newPath) throws ErrnoException { + try { + super.rename(oldPath, newPath); + } catch (ErrnoException e) { + // On emulated volumes, we have bind mounts for /Android/data and + // /Android/obb, which prevents move from working across those directories + // and other directories on the filesystem. To work around that, try to + // recover by doing a copy instead. + // Note that we only do this for "/storage/emulated", because public volumes + // don't have these bind mounts, neither do private volumes that are not + // the primary storage. + if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/emulated") + && newPath.startsWith("/storage/emulated")) { + Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath); + try { + Files.move(new File(oldPath).toPath(), new File(newPath).toPath(), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e2) { + Log.e(TAG, "Rename recovery failed ", e2); + throw e; + } + } else { + throw e; + } + } + } + } + + public static void main(String[] args) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); + + // Install selective syscall interception + AndroidOs.install(); + + // CloseGuard defaults to true and can be quite spammy. We + // disable it here, but selectively enable it later (via + // StrictMode) on debug builds, but using DropBox, not logs. + CloseGuard.setEnabled(false); + + Environment.initForCurrentUser(); + + // Make sure TrustedCertificateStore looks in the right place for CA certificates + final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); + TrustedCertificateStore.setDefaultUserDirectory(configDir); + + // Call per-process mainline module initialization. + initializeMainlineModules(); + + Process.setArgV0(""); + + Looper.prepareMainLooper(); + + // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. + // It will be in the format "seq=114" + long startSeq = 0; + if (args != null) { + for (int i = args.length - 1; i >= 0; --i) { + if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { + startSeq = Long.parseLong( + args[i].substring(PROC_START_SEQ_IDENT.length())); + } + } + } + ActivityThread thread = new ActivityThread(); + thread.attach(false, startSeq); + + if (sMainThreadHandler == null) { + sMainThreadHandler = thread.getHandler(); + } + + if (false) { + Looper.myLooper().setMessageLogging(new + LogPrinter(Log.DEBUG, "ActivityThread")); + } + + // End of event ActivityThreadMain. + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + Looper.loop(); + + throw new RuntimeException("Main thread loop unexpectedly exited"); + } + + /** + * Call various initializer APIs in mainline modules that need to be called when each process + * starts. + */ + public static void initializeMainlineModules() { + TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager()); + StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager()); + MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager()); + MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager()); + BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager()); + BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> { + BinderCallsStats.startForBluetooth(context); + }); + NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager()); + DeviceConfigInitializer.setDeviceConfigServiceManager(new DeviceConfigServiceManager()); + SeFrameworkInitializer.setSeServiceManager(new SeServiceManager()); + if (android.server.Flags.telemetryApisService()) { + ProfilingFrameworkInitializer.setProfilingServiceManager(new ProfilingServiceManager()); + } + } + + private void purgePendingResources() { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "purgePendingResources"); + nPurgePendingResources(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + /** + * Returns whether the provided {@link ActivityInfo} {@code ai} is a protected component. + * + * @see #isProtectedComponent(ComponentInfo, String) + */ + public static boolean isProtectedComponent(@NonNull ActivityInfo ai) { + return isProtectedComponent(ai, ai.permission); + } + + /** + * Returns whether the provided {@link ServiceInfo} {@code si} is a protected component. + * + * @see #isProtectedComponent(ComponentInfo, String) + */ + public static boolean isProtectedComponent(@NonNull ServiceInfo si) { + return isProtectedComponent(si, si.permission); + } + + /** + * Returns whether the provided {@link ComponentInfo} {@code ci} with the specified {@code + * permission} is a protected component. + * + *

A component is protected if it is not exported, or if the specified {@code permission} is + * a signature permission. + */ + private static boolean isProtectedComponent(@NonNull ComponentInfo ci, + @Nullable String permission) { + // Bail early when this process isn't looking for violations + if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false; + + // TODO: consider optimizing by having AMS pre-calculate this value + if (!ci.exported) { + return true; + } + if (permission != null) { + try { + PermissionInfo pi = getPermissionManager().getPermissionInfo(permission, + currentOpPackageName(), 0); + return (pi != null) && pi.getProtection() == PermissionInfo.PROTECTION_SIGNATURE; + } catch (RemoteException ignored) { + } + } + return false; + } + + /** + * Returns whether the action within the provided {@code intent} is a protected broadcast. + */ + public static boolean isProtectedBroadcast(@NonNull Intent intent) { + // Bail early when this process isn't looking for violations + if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false; + + // TODO: consider optimizing by having AMS pre-calculate this value + try { + return getPackageManager().isProtectedBroadcast(intent.getAction()); + } catch (RemoteException ignored) { + } + return false; + } + + @Override + public boolean isInDensityCompatMode() { + return mDensityCompatMode; + } + + // ------------------ Regular JNI ------------------------ + private native void nPurgePendingResources(); + private native void nInitZygoteChildHeapProfiling(); +} diff --git a/aosp/frameworks/base/core/java/android/app/ApplicationPackageManager.java b/aosp/frameworks/base/core/java/android/app/ApplicationPackageManager.java new file mode 100755 index 0000000000000000000000000000000000000000..dc014414a0ee5300b2927ce3b32fff0eeb21ce46 --- /dev/null +++ b/aosp/frameworks/base/core/java/android/app/ApplicationPackageManager.java @@ -0,0 +1,4103 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED; +import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_NOT_COLORED; +import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON; +import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON_BADGE; +import static android.app.admin.DevicePolicyResources.UNDEFINED; +import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256; +import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; +import static android.content.pm.Checksum.TYPE_WHOLE_MD5; +import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA1; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA256; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA512; + +import android.annotation.CallbackExecutor; +import android.annotation.DrawableRes; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringRes; +import android.annotation.UserIdInt; +import android.annotation.XmlRes; +import android.app.admin.DevicePolicyManager; +import android.app.role.RoleManager; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.ApkChecksum; +import android.content.pm.ApplicationInfo; +import android.content.pm.ArchivedPackageInfo; +import android.content.pm.ChangedPackages; +import android.content.pm.Checksum; +import android.content.pm.ComponentInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.IOnChecksumsReadyListener; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageManager; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.IPackageStatsObserver; +import android.content.pm.InstallSourceInfo; +import android.content.pm.InstantAppInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.KeySet; +import android.content.pm.ModuleInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.SharedLibraryInfo; +import android.content.pm.SuspendDialogInfo; +import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.VersionedPackage; +import android.content.pm.dex.ArtManager; +import android.content.pm.parsing.ApkLiteParseUtils; +import android.content.res.ApkAssets; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.IRemoteCallback; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.RemoteException; +import android.os.StrictMode; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; +import android.permission.PermissionControllerManager; +import android.permission.PermissionManager; +import android.provider.Settings; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructStat; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.LauncherIcons; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.Immutable; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.SomeArgs; +import com.android.internal.util.UserIcons; + +import dalvik.system.VMRuntime; + +import libcore.util.EmptyArray; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.Function; + +/** @hide */ +public class ApplicationPackageManager extends PackageManager { + private static final String TAG = "ApplicationPackageManager"; + private static final boolean DEBUG_ICONS = false; + + private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB + + // Default flags to use with PackageManager when no flags are given. + private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES; + + /** Default set of checksums - includes all available checksums. + * @see PackageManager#requestChecksums */ + private static final int DEFAULT_CHECKSUMS = + TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256 + | TYPE_WHOLE_SHA512 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 + | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; + + // Name of the resource which provides background permission button string + public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS = + "app_permission_button_allow_always"; + + // Name of the package which the permission controller's resources are in. + public static final String PERMISSION_CONTROLLER_RESOURCE_PACKAGE = + "com.android.permissioncontroller"; + + private volatile UserManager mUserManager; + private volatile PermissionManager mPermissionManager; + private volatile PackageInstaller mInstaller; + private volatile ArtManager mArtManager; + private volatile DevicePolicyManager mDevicePolicyManager; + private volatile String mPermissionsControllerPackageName; + + @GuardedBy("mDelegates") + private final ArrayList mDelegates = new ArrayList<>(); + + @NonNull + @GuardedBy("mPackageMonitorCallbacks") + private final ArraySet mPackageMonitorCallbacks = new ArraySet<>(); + + UserManager getUserManager() { + if (mUserManager == null) { + mUserManager = UserManager.get(mContext); + } + return mUserManager; + } + + DevicePolicyManager getDevicePolicyManager() { + if (mDevicePolicyManager == null) { + mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); + } + return mDevicePolicyManager; + } + + private PermissionManager getPermissionManager() { + if (mPermissionManager == null) { + mPermissionManager = mContext.getSystemService(PermissionManager.class); + } + return mPermissionManager; + } + + @Override + public int getUserId() { + return mContext.getUserId(); + } + + @Override + public PackageInfo getPackageInfo(String packageName, int flags) + throws NameNotFoundException { + return getPackageInfo(packageName, PackageInfoFlags.of(flags)); + } + + @Override + public PackageInfo getPackageInfo(String packageName, PackageInfoFlags flags) + throws NameNotFoundException { + return getPackageInfoAsUser(packageName, flags, getUserId()); + } + + @Override + public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags) + throws NameNotFoundException { + return getPackageInfo(versionedPackage, PackageInfoFlags.of(flags)); + } + + @Override + public PackageInfo getPackageInfo(VersionedPackage versionedPackage, PackageInfoFlags flags) + throws NameNotFoundException { + final int userId = getUserId(); + try { + PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage, + updateFlagsForPackage(flags.getValue(), userId), userId); + if (pi != null) { + return pi; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + throw new NameNotFoundException(versionedPackage.toString()); + } + + @Override + public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { + return getPackageInfoAsUser(packageName, PackageInfoFlags.of(flags), userId); + } + + @Override + public PackageInfo getPackageInfoAsUser(String packageName, PackageInfoFlags flags, int userId) + throws NameNotFoundException { + PackageInfo pi = + getPackageInfoAsUserCached( + packageName, + updateFlagsForPackage(flags.getValue(), userId), + userId); + if (pi == null) { + throw new NameNotFoundException(packageName); + } + return pi; + } + + @Override + public String[] currentToCanonicalPackageNames(String[] names) { + try { + return mPM.currentToCanonicalPackageNames(names); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String[] canonicalToCurrentPackageNames(String[] names) { + try { + return mPM.canonicalToCurrentPackageNames(names); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public Intent getLaunchIntentForPackage(String packageName) { + // First see if the package has an INFO activity; the existence of + // such an activity is implied to be the desired front-door for the + // overall package (such as if it has multiple launcher entries). + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(Intent.CATEGORY_INFO); + intentToResolve.setPackage(packageName); + List ris = queryIntentActivities(intentToResolve, 0); + + // Otherwise, try to find a main launcher activity. + if (ris == null || ris.size() <= 0) { + // reuse the intent instance + intentToResolve.removeCategory(Intent.CATEGORY_INFO); + intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); + intentToResolve.setPackage(packageName); + ris = queryIntentActivities(intentToResolve, 0); + } + if (ris == null || ris.size() <= 0) { + return null; + } + Intent intent = new Intent(intentToResolve); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClassName(ris.get(0).activityInfo.packageName, + ris.get(0).activityInfo.name); + return intent; + } + + @Override + public Intent getLeanbackLaunchIntentForPackage(String packageName) { + return getLaunchIntentForPackageAndCategory(packageName, Intent.CATEGORY_LEANBACK_LAUNCHER); + } + + @Override + public Intent getCarLaunchIntentForPackage(String packageName) { + return getLaunchIntentForPackageAndCategory(packageName, Intent.CATEGORY_CAR_LAUNCHER); + } + + private Intent getLaunchIntentForPackageAndCategory(String packageName, String category) { + // Try to find a main launcher activity for the given categories. + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(category); + intentToResolve.setPackage(packageName); + List ris = queryIntentActivities(intentToResolve, 0); + + if (ris == null || ris.size() <= 0) { + return null; + } + Intent intent = new Intent(intentToResolve); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClassName(ris.get(0).activityInfo.packageName, + ris.get(0).activityInfo.name); + return intent; + } + + @Override + public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) { + try { + return mPM.getLaunchIntentSenderForPackage(packageName, mContext.getPackageName(), + mContext.getAttributionTag(), getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int[] getPackageGids(String packageName) throws NameNotFoundException { + return getPackageGids(packageName, 0); + } + + @Override + public int[] getPackageGids(String packageName, int flags) + throws NameNotFoundException { + return getPackageGids(packageName, PackageInfoFlags.of(flags)); + } + + @Override + public int[] getPackageGids(String packageName, PackageInfoFlags flags) + throws NameNotFoundException { + final int userId = getUserId(); + try { + int[] gids = mPM.getPackageGids(packageName, + updateFlagsForPackage(flags.getValue(), userId), userId); + if (gids != null) { + return gids; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(packageName); + } + + @Override + public int getPackageUid(String packageName, int flags) throws NameNotFoundException { + return getPackageUid(packageName, PackageInfoFlags.of(flags)); + } + + @Override + public int getPackageUid(String packageName, PackageInfoFlags flags) + throws NameNotFoundException { + return getPackageUidAsUser(packageName, flags, getUserId()); + } + + @Override + public int getPackageUidAsUser(String packageName, int userId) throws NameNotFoundException { + return getPackageUidAsUser(packageName, 0, userId); + } + + @Override + public int getPackageUidAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { + return getPackageUidAsUser(packageName, PackageInfoFlags.of(flags), userId); + } + + @Override + public int getPackageUidAsUser(String packageName, PackageInfoFlags flags, int userId) + throws NameNotFoundException { + try { + int uid = mPM.getPackageUid(packageName, + updateFlagsForPackage(flags.getValue(), userId), userId); + if (uid >= 0) { + return uid; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(packageName); + } + + @Override + @SuppressWarnings("unchecked") + public List getAllPermissionGroups(int flags) { + return getPermissionManager().getAllPermissionGroups(flags); + } + + @Override + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) + throws NameNotFoundException { + final PermissionGroupInfo permissionGroupInfo = getPermissionManager() + .getPermissionGroupInfo(groupName, flags); + if (permissionGroupInfo == null) { + throw new NameNotFoundException(groupName); + } + return permissionGroupInfo; + } + + @Override + public PermissionInfo getPermissionInfo(String permName, int flags) + throws NameNotFoundException { + final PermissionInfo permissionInfo = getPermissionManager().getPermissionInfo(permName, + flags); + if (permissionInfo == null) { + throw new NameNotFoundException(permName); + } + return permissionInfo; + } + + @Override + @SuppressWarnings("unchecked") + public List queryPermissionsByGroup(String groupName, int flags) + throws NameNotFoundException { + final List permissionInfos = getPermissionManager().queryPermissionsByGroup( + groupName, flags); + if (permissionInfos == null) { + throw new NameNotFoundException(groupName); + } + return permissionInfos; + } + + @Override + public void getPlatformPermissionsForGroup(@NonNull String permissionGroupName, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer> callback) { + final PermissionControllerManager permissionControllerManager = mContext.getSystemService( + PermissionControllerManager.class); + permissionControllerManager.getPlatformPermissionsForGroup(permissionGroupName, executor, + callback); + } + + @Override + public void getGroupOfPlatformPermission(@NonNull String permissionName, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer callback) { + final PermissionControllerManager permissionControllerManager = mContext.getSystemService( + PermissionControllerManager.class); + permissionControllerManager.getGroupOfPlatformPermission(permissionName, executor, + callback); + } + + @Override + public boolean arePermissionsIndividuallyControlled() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_permissionsIndividuallyControlled); + } + + @Override + public boolean isWirelessConsentModeEnabled() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_wirelessConsentRequired); + } + + @Override + public ApplicationInfo getApplicationInfo(String packageName, int flags) + throws NameNotFoundException { + return getApplicationInfo(packageName, ApplicationInfoFlags.of(flags)); + } + + @Override + public ApplicationInfo getApplicationInfo(String packageName, ApplicationInfoFlags flags) + throws NameNotFoundException { + return getApplicationInfoAsUser(packageName, flags, getUserId()); + } + + @Override + public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { + return getApplicationInfoAsUser(packageName, ApplicationInfoFlags.of(flags), userId); + } + + @Override + public ApplicationInfo getApplicationInfoAsUser(String packageName, ApplicationInfoFlags flags, + int userId) throws NameNotFoundException { + ApplicationInfo ai = getApplicationInfoAsUserCached( + packageName, + updateFlagsForApplication(flags.getValue(), userId), + userId); + if (ai == null) { + throw new NameNotFoundException(packageName); + } + return maybeAdjustApplicationInfo(ai); + } + + private static ApplicationInfo maybeAdjustApplicationInfo(ApplicationInfo info) { + // If we're dealing with a multi-arch application that has both + // 32 and 64 bit shared libraries, we might need to choose the secondary + // depending on what the current runtime's instruction set is. + if (info.primaryCpuAbi != null && info.secondaryCpuAbi != null) { + final String runtimeIsa = VMRuntime.getRuntime().vmInstructionSet(); + + // Get the instruction set that the libraries of secondary Abi is supported. + // In presence of a native bridge this might be different than the one secondary Abi used. + String secondaryIsa = VMRuntime.getInstructionSet(info.secondaryCpuAbi); + final String secondaryDexCodeIsa = SystemProperties.get("ro.dalvik.vm.isa." + secondaryIsa); + secondaryIsa = secondaryDexCodeIsa.isEmpty() ? secondaryIsa : secondaryDexCodeIsa; + + // If the runtimeIsa is the same as the primary isa, then we do nothing. + // Everything will be set up correctly because info.nativeLibraryDir will + // correspond to the right ISA. + if (runtimeIsa.equals(secondaryIsa)) { + ApplicationInfo modified = new ApplicationInfo(info); + modified.nativeLibraryDir = info.secondaryNativeLibraryDir; + return modified; + } + } + return info; + } + + @Override + public int getTargetSdkVersion(@NonNull String packageName) throws NameNotFoundException { + try { + int version = mPM.getTargetSdkVersion(packageName); + if (version != -1) { + return version; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + throw new PackageManager.NameNotFoundException(packageName); + } + + @Override + public ActivityInfo getActivityInfo(ComponentName className, int flags) + throws NameNotFoundException { + return getActivityInfo(className, ComponentInfoFlags.of(flags)); + } + + @Override + public ActivityInfo getActivityInfo(ComponentName className, ComponentInfoFlags flags) + throws NameNotFoundException { + final int userId = getUserId(); + try { + ActivityInfo ai = mPM.getActivityInfo(className, + updateFlagsForComponent(flags.getValue(), userId, null), userId); + if (ai != null) { + return ai; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(className.toString()); + } + + @Override + public ActivityInfo getReceiverInfo(ComponentName className, int flags) + throws NameNotFoundException { + return getReceiverInfo(className, ComponentInfoFlags.of(flags)); + } + + @Override + public ActivityInfo getReceiverInfo(ComponentName className, ComponentInfoFlags flags) + throws NameNotFoundException { + final int userId = getUserId(); + try { + ActivityInfo ai = mPM.getReceiverInfo(className, + updateFlagsForComponent(flags.getValue(), userId, null), userId); + if (ai != null) { + return ai; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(className.toString()); + } + + @Override + public ServiceInfo getServiceInfo(ComponentName className, int flags) + throws NameNotFoundException { + return getServiceInfo(className, ComponentInfoFlags.of(flags)); + } + + @Override + public ServiceInfo getServiceInfo(ComponentName className, ComponentInfoFlags flags) + throws NameNotFoundException { + final int userId = getUserId(); + try { + ServiceInfo si = mPM.getServiceInfo(className, + updateFlagsForComponent(flags.getValue(), userId, null), userId); + if (si != null) { + return si; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(className.toString()); + } + + @Override + public ProviderInfo getProviderInfo(ComponentName className, int flags) + throws NameNotFoundException { + return getProviderInfo(className, ComponentInfoFlags.of(flags)); + } + + @Override + public ProviderInfo getProviderInfo(ComponentName className, ComponentInfoFlags flags) + throws NameNotFoundException { + final int userId = getUserId(); + try { + ProviderInfo pi = mPM.getProviderInfo(className, + updateFlagsForComponent(flags.getValue(), userId, null), userId); + if (pi != null) { + return pi; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(className.toString()); + } + + @Override + public String[] getSystemSharedLibraryNames() { + try { + return mPM.getSystemSharedLibraryNames(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public @NonNull List getSharedLibraries(int flags) { + return this.getSharedLibraries(PackageInfoFlags.of(flags)); + } + + /** @hide + * @param flags */ + @Override + public @NonNull List getSharedLibraries(PackageInfoFlags flags) { + return getSharedLibrariesAsUser(flags, getUserId()); + } + + /** @hide */ + @Override + public @NonNull List getSharedLibrariesAsUser(int flags, int userId) { + return getSharedLibrariesAsUser(PackageInfoFlags.of(flags), userId); + } + + /** @hide */ + @Override + @SuppressWarnings("unchecked") + public @NonNull List getSharedLibrariesAsUser(PackageInfoFlags flags, + int userId) { + try { + ParceledListSlice sharedLibs = mPM.getSharedLibraries( + mContext.getOpPackageName(), flags.getValue(), userId); + if (sharedLibs == null) { + return Collections.emptyList(); + } + return sharedLibs.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @NonNull + @Override + public List getDeclaredSharedLibraries(@NonNull String packageName, + int flags) { + return getDeclaredSharedLibraries(packageName, PackageInfoFlags.of(flags)); + } + + @NonNull + @Override + @SuppressWarnings("unchecked") + public List getDeclaredSharedLibraries(@NonNull String packageName, + PackageInfoFlags flags) { + try { + ParceledListSlice sharedLibraries = mPM.getDeclaredSharedLibraries( + packageName, flags.getValue(), mContext.getUserId()); + return sharedLibraries != null ? sharedLibraries.getList() : Collections.emptyList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public @NonNull String getServicesSystemSharedLibraryPackageName() { + try { + return mPM.getServicesSystemSharedLibraryPackageName(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + public @NonNull String getSharedSystemSharedLibraryPackageName() { + try { + return mPM.getSharedSystemSharedLibraryPackageName(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public ChangedPackages getChangedPackages(int sequenceNumber) { + try { + return mPM.getChangedPackages(sequenceNumber, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @SuppressWarnings("unchecked") + public FeatureInfo[] getSystemAvailableFeatures() { + try { + ParceledListSlice parceledList = + mPM.getSystemAvailableFeatures(); + if (parceledList == null) { + return new FeatureInfo[0]; + } + final List list = parceledList.getList(); + final FeatureInfo[] res = new FeatureInfo[list.size()]; + for (int i = 0; i < res.length; i++) { + res[i] = list.get(i); + } + return res; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean hasSystemFeature(String name) { + return hasSystemFeature(name, 0); + } + + /** + * Identifies a single hasSystemFeature query. + */ + @Immutable + private static final class HasSystemFeatureQuery { + public final String name; + public final int version; + public HasSystemFeatureQuery(String n, int v) { + name = n; + version = v; + } + @Override + public String toString() { + return String.format("HasSystemFeatureQuery(name=\"%s\", version=%d)", + name, version); + } + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof HasSystemFeatureQuery) { + HasSystemFeatureQuery r = (HasSystemFeatureQuery) o; + return Objects.equals(name, r.name) && version == r.version; + } else { + return false; + } + } + @Override + public int hashCode() { + return Objects.hashCode(name) * 13 + version; + } + } + + // Make this cache relatively large. There are many system features and + // none are ever invalidated. MPTS tests suggests that the cache should + // hold at least 150 entries. + private final static PropertyInvalidatedCache + mHasSystemFeatureCache = + new PropertyInvalidatedCache( + 256, "cache_key.has_system_feature") { + @Override + public Boolean recompute(HasSystemFeatureQuery query) { + try { + return ActivityThread.currentActivityThread().getPackageManager(). + hasSystemFeature(query.name, query.version); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + }; + + @Override + public boolean hasSystemFeature(String name, int version) { + //ccynice for chccyniceeck SystemFeature able + if(Process.myUid()>10000){ + final String packageName2 = mContext.getOpPackageName(); + if (packageName2.contains("com.antutu.benchmark.full") || + packageName2.contains("com.antutu.ABenchMark")) + { + Log.v(TAG,Process.myUid()+" hasSystemFeature name:" + name); + return true; + } + } + return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); + } + + /** @hide */ + public void disableHasSystemFeatureCache() { + mHasSystemFeatureCache.disableLocal(); + } + + /** @hide */ + public static void invalidateHasSystemFeatureCache() { + mHasSystemFeatureCache.invalidateCache(); + } + + @Override + public int checkPermission(String permName, String pkgName) { + return getPermissionManager().checkPackageNamePermission(permName, pkgName, + mContext.getDeviceId(), getUserId()); + } + + @Override + public boolean isPermissionRevokedByPolicy(String permName, String pkgName) { + return getPermissionManager().isPermissionRevokedByPolicy(pkgName, permName); + } + + /** + * @hide + */ + @Override + public String getPermissionControllerPackageName() { + if (mPermissionsControllerPackageName == null) { + try { + mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return mPermissionsControllerPackageName; + } + + /** + * @hide + */ + @Override + public String getSdkSandboxPackageName() { + try { + return mPM.getSdkSandboxPackageName(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean addPermission(PermissionInfo info) { + return getPermissionManager().addPermission(info, false); + } + + @Override + public boolean addPermissionAsync(PermissionInfo info) { + return getPermissionManager().addPermission(info, true); + } + + @Override + public void removePermission(String name) { + getPermissionManager().removePermission(name); + } + + @Override + public void grantRuntimePermission(String packageName, String permissionName, + UserHandle user) { + getPermissionManager().grantRuntimePermission(packageName, permissionName, user); + } + + @Override + public void revokeRuntimePermission(String packageName, String permName, UserHandle user) { + revokeRuntimePermission(packageName, permName, user, null); + } + + @Override + public void revokeRuntimePermission(String packageName, String permName, UserHandle user, + String reason) { + getPermissionManager().revokeRuntimePermission(packageName, permName, user, reason); + } + + @Override + public int getPermissionFlags(String permName, String packageName, UserHandle user) { + return getPermissionManager().getPermissionFlags(packageName, permName, user); + } + + @Override + public void updatePermissionFlags(String permName, String packageName, + int flagMask, int flagValues, UserHandle user) { + getPermissionManager().updatePermissionFlags(packageName, permName, flagMask, flagValues, + user); + } + + @Override + public @NonNull Set getWhitelistedRestrictedPermissions( + @NonNull String packageName, @PermissionWhitelistFlags int flags) { + return getPermissionManager().getAllowlistedRestrictedPermissions(packageName, flags); + } + + @Override + public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, @PermissionWhitelistFlags int flags) { + return getPermissionManager().addAllowlistedRestrictedPermission(packageName, permName, + flags); + } + + @Override + public boolean setAutoRevokeWhitelisted(@NonNull String packageName, boolean whitelisted) { + return getPermissionManager().setAutoRevokeExempted(packageName, whitelisted); + } + + @Override + public boolean isAutoRevokeWhitelisted(@NonNull String packageName) { + return getPermissionManager().isAutoRevokeExempted(packageName); + } + + @Override + public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, @PermissionWhitelistFlags int flags) { + return getPermissionManager().removeAllowlistedRestrictedPermission(packageName, permName, + flags); + } + + @Override + @UnsupportedAppUsage + public boolean shouldShowRequestPermissionRationale(String permName) { + return getPermissionManager().shouldShowRequestPermissionRationale(permName); + } + + @Override + public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) { + Intent intent = super.buildRequestPermissionsIntent(permissions); + intent.putExtra(EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, mContext.getDeviceId()); + return intent; + } + + @Override + public CharSequence getBackgroundPermissionOptionLabel() { + try { + + String permissionController = getPermissionControllerPackageName(); + Context context = + mContext.createPackageContext(permissionController, 0); + + int textId = context.getResources().getIdentifier(APP_PERMISSION_BUTTON_ALLOW_ALWAYS, + "string", PERMISSION_CONTROLLER_RESOURCE_PACKAGE); + if (textId != 0) { + return context.getText(textId); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Permission controller not found.", e); + } + return ""; + } + + @Override + public int checkSignatures(String pkg1, String pkg2) { + try { + return mPM.checkSignatures(pkg1, pkg2, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int checkSignatures(int uid1, int uid2) { + try { + return mPM.checkUidSignatures(uid1, uid2); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean hasSigningCertificate( + String packageName, byte[] certificate, @CertificateInputType int type) { + try { + return mPM.hasSigningCertificate(packageName, certificate, type); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean hasSigningCertificate( + int uid, byte[] certificate, @CertificateInputType int type) { + try { + return mPM.hasUidSigningCertificate(uid, certificate, type); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static List encodeCertificates(List certs) throws + CertificateEncodingException { + if (certs == null) { + return null; + } + List result = new ArrayList<>(certs.size()); + for (Certificate cert : certs) { + if (!(cert instanceof X509Certificate)) { + throw new CertificateEncodingException("Only X509 certificates supported."); + } + result.add(cert.getEncoded()); + } + return result; + } + + @Override + public void requestChecksums(@NonNull String packageName, boolean includeSplits, + @Checksum.TypeMask int required, @NonNull List trustedInstallers, + @NonNull OnChecksumsReadyListener onChecksumsReadyListener) + throws CertificateEncodingException, NameNotFoundException { + Objects.requireNonNull(packageName); + Objects.requireNonNull(onChecksumsReadyListener); + Objects.requireNonNull(trustedInstallers); + if (trustedInstallers == TRUST_ALL) { + trustedInstallers = null; + } else if (trustedInstallers == TRUST_NONE) { + trustedInstallers = Collections.emptyList(); + } else if (trustedInstallers.isEmpty()) { + throw new IllegalArgumentException( + "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty " + + "list of certificates."); + } + try { + IOnChecksumsReadyListener onChecksumsReadyListenerDelegate = + new IOnChecksumsReadyListener.Stub() { + @Override + public void onChecksumsReady(List checksums) + throws RemoteException { + onChecksumsReadyListener.onChecksumsReady(checksums); + } + }; + mPM.requestPackageChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required, + encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate, + getUserId()); + } catch (ParcelableException e) { + e.maybeRethrow(PackageManager.NameNotFoundException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Wrap the cached value in a class that does deep compares on string + * arrays. The comparison is needed only for the verification mode of + * PropertyInvalidatedCache; this mode is only enabled for debugging. + * The return result is an array of strings but the order in the array + * is not important. To properly compare two arrays, the arrays are + * sorted before the comparison. + */ + private static class GetPackagesForUidResult { + private final String [] mValue; + GetPackagesForUidResult(String []s) { + mValue = s; + } + public String[] value() { + return mValue; + } + @Override + public String toString() { + return Arrays.toString(mValue); + } + @Override + public int hashCode() { + return Arrays.hashCode(mValue); + } + /** + * Arrays.sort() throws an NPE if passed a null pointer, so nulls + * are handled first. + */ + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof GetPackagesForUidResult) { + String [] r = ((GetPackagesForUidResult) o).mValue; + String [] l = mValue; + if ((r == null) != (l == null)) { + return false; + } else if (r == null) { + return true; + } + // Both arrays are non-null. Sort before comparing. + Arrays.sort(r); + Arrays.sort(l); + return Arrays.equals(l, r); + } else { + return false; + } + } + } + + private static final String CACHE_KEY_PACKAGES_FOR_UID_PROPERTY = + "cache_key.get_packages_for_uid"; + private static final PropertyInvalidatedCache + mGetPackagesForUidCache = + new PropertyInvalidatedCache( + 32, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) { + @Override + public GetPackagesForUidResult recompute(Integer uid) { + try { + return new GetPackagesForUidResult( + ActivityThread.currentActivityThread(). + getPackageManager().getPackagesForUid(uid)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override + public String queryToString(Integer uid) { + return String.format("uid=%d", uid.intValue()); + } + }; + + @Override + public String[] getPackagesForUid(int uid) { + return mGetPackagesForUidCache.query(uid).value(); + } + + /** @hide */ + public static void disableGetPackagesForUidCache() { + mGetPackagesForUidCache.disableLocal(); + } + + /** @hide */ + public static void invalidateGetPackagesForUidCache() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_PACKAGES_FOR_UID_PROPERTY); + } + + @Override + public String getNameForUid(int uid) { + try { + return mPM.getNameForUid(uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String[] getNamesForUids(int[] uids) { + try { + return mPM.getNamesForUids(uids); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int getUidForSharedUser(String sharedUserName) + throws NameNotFoundException { + try { + int uid = mPM.getUidForSharedUser(sharedUserName); + if (uid != Process.INVALID_UID) { + return uid; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + throw new NameNotFoundException("No shared userid for user:"+sharedUserName); + } + + @Override + public List getInstalledModules(int flags) { + try { + return mPM.getInstalledModules(flags); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public ModuleInfo getModuleInfo(String packageName, int flags) throws NameNotFoundException { + try { + ModuleInfo mi = mPM.getModuleInfo(packageName, flags); + if (mi != null) { + return mi; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException("No module info for package: " + packageName); + } + + @SuppressWarnings("unchecked") + @Override + public List getInstalledPackages(int flags) { + return getInstalledPackages(PackageInfoFlags.of(flags)); + } + + @SuppressWarnings("unchecked") + @Override + public List getInstalledPackages(PackageInfoFlags flags) { + return getInstalledPackagesAsUser(flags, getUserId()); + } + + /** @hide */ + @Override + @SuppressWarnings("unchecked") + public List getInstalledPackagesAsUser(int flags, int userId) { + return getInstalledPackagesAsUser(PackageInfoFlags.of(flags), userId); + } + + /** @hide */ + @Override + @SuppressWarnings("unchecked") + public List getInstalledPackagesAsUser(PackageInfoFlags flags, int userId) { + try { + ParceledListSlice parceledList = + mPM.getInstalledPackages(updateFlagsForPackage(flags.getValue(), userId), + userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @NonNull + public PersistableBundle getAppMetadata(@NonNull String packageName) + throws NameNotFoundException { + PersistableBundle appMetadata = null; + ParcelFileDescriptor pfd = null; + try { + pfd = mPM.getAppMetadataFd(packageName, getUserId()); + } catch (ParcelableException e) { + e.maybeRethrow(NameNotFoundException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + if (pfd != null) { + try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + appMetadata = PersistableBundle.readFromStream(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return appMetadata != null ? appMetadata : new PersistableBundle(); + } + + @Override + public @AppMetadataSource int getAppMetadataSource(@NonNull String packageName) + throws NameNotFoundException { + Objects.requireNonNull(packageName, "packageName cannot be null"); + int source = PackageManager.APP_METADATA_SOURCE_UNKNOWN; + try { + source = mPM.getAppMetadataSource(packageName, getUserId()); + } catch (ParcelableException e) { + e.maybeRethrow(NameNotFoundException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return source; + } + + @SuppressWarnings("unchecked") + @Override + public List getPackagesHoldingPermissions(String[] permissions, int flags) { + return this.getPackagesHoldingPermissions(permissions, PackageInfoFlags.of(flags)); + } + + @SuppressWarnings("unchecked") + @Override + public List getPackagesHoldingPermissions(String[] permissions, + PackageInfoFlags flags) { + final int userId = getUserId(); + try { + ParceledListSlice parceledList = + mPM.getPackagesHoldingPermissions(permissions, + updateFlagsForPackage(flags.getValue(), userId), userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @SuppressWarnings("unchecked") + @Override + public List getInstalledApplications(int flags) { + return getInstalledApplicationsAsUser(flags, getUserId()); + } + + @Override + public List getInstalledApplications(ApplicationInfoFlags flags) { + return getInstalledApplicationsAsUser(flags, getUserId()); + } + + /** @hide */ + @SuppressWarnings("unchecked") + @Override + public List getInstalledApplicationsAsUser(int flags, int userId) { + return getInstalledApplicationsAsUser(ApplicationInfoFlags.of(flags), userId); + } + + /** @hide */ + @SuppressWarnings("unchecked") + @Override + public List getInstalledApplicationsAsUser(ApplicationInfoFlags flags, + int userId) { + try { + ParceledListSlice parceledList = + mPM.getInstalledApplications(updateFlagsForApplication( + flags.getValue(), userId), userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @SuppressWarnings("unchecked") + @Override + public List getInstantApps() { + try { + ParceledListSlice slice = mPM.getInstantApps(getUserId()); + if (slice != null) { + return slice.getList(); + } + return Collections.emptyList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public Drawable getInstantAppIcon(String packageName) { + try { + Bitmap bitmap = mPM.getInstantAppIcon(packageName, getUserId()); + if (bitmap != null) { + return new BitmapDrawable(null, bitmap); + } + return null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean isInstantApp() { + return isInstantApp(mContext.getPackageName()); + } + + @Override + public boolean isInstantApp(String packageName) { + try { + return mPM.isInstantApp(packageName, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public int getInstantAppCookieMaxBytes() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES, + DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES); + } + + @Override + public int getInstantAppCookieMaxSize() { + return getInstantAppCookieMaxBytes(); + } + + @Override + public @NonNull byte[] getInstantAppCookie() { + try { + final byte[] cookie = mPM.getInstantAppCookie(mContext.getPackageName(), getUserId()); + if (cookie != null) { + return cookie; + } else { + return EmptyArray.BYTE; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void clearInstantAppCookie() { + updateInstantAppCookie(null); + } + + @Override + public void updateInstantAppCookie(@NonNull byte[] cookie) { + if (cookie != null && cookie.length > getInstantAppCookieMaxBytes()) { + throw new IllegalArgumentException("instant cookie longer than " + + getInstantAppCookieMaxBytes()); + } + try { + mPM.setInstantAppCookie(mContext.getPackageName(), cookie, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Override + public boolean setInstantAppCookie(@NonNull byte[] cookie) { + try { + return mPM.setInstantAppCookie(mContext.getPackageName(), cookie, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public ResolveInfo resolveActivity(Intent intent, int flags) { + return resolveActivity(intent, ResolveInfoFlags.of(flags)); + } + + @Override + public ResolveInfo resolveActivity(Intent intent, ResolveInfoFlags flags) { + return resolveActivityAsUser(intent, flags, getUserId()); + } + + @Override + public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) { + return resolveActivityAsUser(intent, ResolveInfoFlags.of(flags), userId); + } + + @Override + public ResolveInfo resolveActivityAsUser(Intent intent, ResolveInfoFlags flags, int userId) { + try { + return mPM.resolveIntent( + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public List queryIntentActivities(Intent intent, int flags) { + return queryIntentActivities(intent, ResolveInfoFlags.of(flags)); + } + + @Override + public List queryIntentActivities(Intent intent, ResolveInfoFlags flags) { + return queryIntentActivitiesAsUser(intent, flags, getUserId()); + } + + /** @hide Same as above but for a specific user */ + @Override + public List queryIntentActivitiesAsUser(Intent intent, int flags, int userId) { + return queryIntentActivitiesAsUser(intent, ResolveInfoFlags.of(flags), userId); + } + + /** @hide Same as above but for a specific user */ + @Override + @SuppressWarnings("unchecked") + public List queryIntentActivitiesAsUser(Intent intent, ResolveInfoFlags flags, + int userId) { + try { + ParceledListSlice parceledList = mPM.queryIntentActivities( + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public List queryIntentActivityOptions(ComponentName caller, Intent[] specifics, + Intent intent, int flags) { + return queryIntentActivityOptions(caller, + specifics == null ? null : new ArrayList<>(Arrays.asList(specifics)), + intent, ResolveInfoFlags.of(flags)); + } + + @Override + @SuppressWarnings("unchecked") + public List queryIntentActivityOptions(ComponentName caller, + List specifics, Intent intent, ResolveInfoFlags flags) { + final int userId = getUserId(); + final ContentResolver resolver = mContext.getContentResolver(); + + String[] specificTypes = null; + if (specifics != null) { + final int numSpecifics = specifics.size(); + for (int i = 0; i < numSpecifics; i++) { + Intent sp = specifics.get(i); + if (sp != null) { + String t = sp.resolveTypeIfNeeded(resolver); + if (t != null) { + if (specificTypes == null) { + specificTypes = new String[numSpecifics]; + } + specificTypes[i] = t; + } + } + } + } + + try { + ParceledListSlice parceledList = mPM.queryIntentActivityOptions( + caller, + specifics == null ? null : specifics.toArray(new Intent[0]), + specificTypes, + intent, + intent.resolveTypeIfNeeded(resolver), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @Override + public List queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) { + return queryBroadcastReceiversAsUser(intent, ResolveInfoFlags.of(flags), userId); + } + + /** + * @hide + */ + @Override + @SuppressWarnings("unchecked") + public List queryBroadcastReceiversAsUser(Intent intent, ResolveInfoFlags flags, + int userId) { + try { + ParceledListSlice parceledList = mPM.queryIntentReceivers( + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public List queryBroadcastReceivers(Intent intent, int flags) { + return queryBroadcastReceivers(intent, ResolveInfoFlags.of(flags)); + } + + @Override + public List queryBroadcastReceivers(Intent intent, ResolveInfoFlags flags) { + return queryBroadcastReceiversAsUser(intent, flags, getUserId()); + } + + @Override + public ResolveInfo resolveServiceAsUser(Intent intent, int flags, @UserIdInt int userId) { + return resolveServiceAsUser(intent, ResolveInfoFlags.of(flags), userId); + } + + @Override + public ResolveInfo resolveServiceAsUser(Intent intent, ResolveInfoFlags flags, + @UserIdInt int userId) { + try { + return mPM.resolveService( + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public ResolveInfo resolveService(Intent intent, int flags) { + return resolveService(intent, ResolveInfoFlags.of(flags)); + } + + @Override + public ResolveInfo resolveService(Intent intent, ResolveInfoFlags flags) { + return resolveServiceAsUser(intent, flags, getUserId()); + } + + @Override + public List queryIntentServicesAsUser(Intent intent, int flags, int userId) { + return queryIntentServicesAsUser(intent, ResolveInfoFlags.of(flags), userId); + } + + @Override + @SuppressWarnings("unchecked") + public List queryIntentServicesAsUser(Intent intent, ResolveInfoFlags flags, + int userId) { + try { + ParceledListSlice parceledList = mPM.queryIntentServices( + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public List queryIntentServices(Intent intent, int flags) { + return queryIntentServices(intent, ResolveInfoFlags.of(flags)); + } + + @Override + public List queryIntentServices(Intent intent, ResolveInfoFlags flags) { + return queryIntentServicesAsUser(intent, flags, getUserId()); + } + + @Override + public List queryIntentContentProvidersAsUser( + Intent intent, int flags, int userId) { + return queryIntentContentProvidersAsUser(intent, ResolveInfoFlags.of(flags), userId); + } + + @Override + @SuppressWarnings("unchecked") + public List queryIntentContentProvidersAsUser( + Intent intent, ResolveInfoFlags flags, int userId) { + try { + ParceledListSlice parceledList = mPM.queryIntentContentProviders( + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + updateFlagsForComponent(flags.getValue(), userId, intent), + userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public List queryIntentContentProviders(Intent intent, int flags) { + return queryIntentContentProviders(intent, ResolveInfoFlags.of(flags)); + } + + @Override + public List queryIntentContentProviders(Intent intent, ResolveInfoFlags flags) { + return queryIntentContentProvidersAsUser(intent, flags, getUserId()); + } + + @Override + public ProviderInfo resolveContentProvider(String name, int flags) { + return resolveContentProvider(name, ComponentInfoFlags.of(flags)); + } + + @Override + public ProviderInfo resolveContentProvider(String name, ComponentInfoFlags flags) { + return resolveContentProviderAsUser(name, flags, getUserId()); + } + + /** @hide **/ + @Override + public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) { + return resolveContentProviderAsUser(name, ComponentInfoFlags.of(flags), userId); + } + + /** @hide **/ + @Override + public ProviderInfo resolveContentProviderAsUser(String name, ComponentInfoFlags flags, + int userId) { + try { + return mPM.resolveContentProvider(name, + updateFlagsForComponent(flags.getValue(), userId, null), userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public List queryContentProviders(String processName, int uid, int flags) { + return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags)); + } + + @Override + public List queryContentProviders(String processName, int uid, + ComponentInfoFlags flags) { + return queryContentProviders(processName, uid, flags, null); + } + + @Override + public List queryContentProviders(String processName, + int uid, int flags, String metaDataKey) { + return queryContentProviders(processName, uid, ComponentInfoFlags.of(flags), metaDataKey); + } + + @Override + @SuppressWarnings("unchecked") + public List queryContentProviders(String processName, + int uid, ComponentInfoFlags flags, String metaDataKey) { + try { + ParceledListSlice slice = mPM.queryContentProviders(processName, uid, + updateFlagsForComponent(flags.getValue(), UserHandle.getUserId(uid), + null), metaDataKey); + return slice != null ? slice.getList() : Collections.emptyList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public InstrumentationInfo getInstrumentationInfo( + ComponentName className, int flags) + throws NameNotFoundException { + try { + InstrumentationInfo ii = mPM.getInstrumentationInfoAsUser( + className, flags, getUserId()); + if (ii != null) { + return ii; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + throw new NameNotFoundException(className.toString()); + } + + @Override + @SuppressWarnings("unchecked") + public List queryInstrumentation( + String targetPackage, int flags) { + try { + ParceledListSlice parceledList = + mPM.queryInstrumentationAsUser(targetPackage, flags, getUserId()); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Nullable + @Override + public Drawable getDrawable(String packageName, @DrawableRes int resId, + @Nullable ApplicationInfo appInfo) { + final ResourceName name = new ResourceName(packageName, resId); + final Drawable cachedIcon = getCachedIcon(name); + if (cachedIcon != null) { + return cachedIcon; + } + + if (appInfo == null) { + try { + appInfo = getApplicationInfo(packageName, sDefaultFlags); + } catch (NameNotFoundException e) { + return null; + } + } + + if (resId != 0) { + try { + final Resources r = getResourcesForApplication(appInfo); + final Drawable dr = r.getDrawable(resId, null); + if (dr != null) { + putCachedIcon(name, dr); + } + + if (false) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resId) + + " from package " + packageName + + ": app scale=" + r.getCompatibilityInfo().applicationScale + + ", caller scale=" + mContext.getResources() + .getCompatibilityInfo().applicationScale, + e); + } + if (DEBUG_ICONS) { + Log.v(TAG, "Getting drawable 0x" + + Integer.toHexString(resId) + " from " + r + + ": " + dr); + } + return dr; + } catch (NameNotFoundException e) { + Log.w("PackageManager", "Failure retrieving resources for " + + appInfo.packageName); + } catch (Resources.NotFoundException e) { + Log.w("PackageManager", "Failure retrieving resources for " + + appInfo.packageName + ": " + e.getMessage()); + } catch (Exception e) { + // If an exception was thrown, fall through to return + // default icon. + Log.w("PackageManager", "Failure retrieving icon 0x" + + Integer.toHexString(resId) + " in package " + + packageName, e); + } + } + + return null; + } + + @Override public Drawable getActivityIcon(ComponentName activityName) + throws NameNotFoundException { + return getActivityInfo(activityName, sDefaultFlags).loadIcon(this); + } + + @Override public Drawable getActivityIcon(Intent intent) + throws NameNotFoundException { + if (intent.getComponent() != null) { + return getActivityIcon(intent.getComponent()); + } + + ResolveInfo info = resolveActivity(intent, MATCH_DEFAULT_ONLY); + if (info != null) { + return info.activityInfo.loadIcon(this); + } + + throw new NameNotFoundException(intent.toUri(0)); + } + + @Override public Drawable getDefaultActivityIcon() { + return mContext.getDrawable(com.android.internal.R.drawable.sym_def_app_icon); + } + + @Override public Drawable getApplicationIcon(ApplicationInfo info) { + return info.loadIcon(this); + } + + @Override public Drawable getApplicationIcon(String packageName) + throws NameNotFoundException { + return getApplicationIcon(getApplicationInfo(packageName, sDefaultFlags)); + } + + @Override + public Drawable getActivityBanner(ComponentName activityName) + throws NameNotFoundException { + return getActivityInfo(activityName, sDefaultFlags).loadBanner(this); + } + + @Override + public Drawable getActivityBanner(Intent intent) + throws NameNotFoundException { + if (intent.getComponent() != null) { + return getActivityBanner(intent.getComponent()); + } + + ResolveInfo info = resolveActivity( + intent, MATCH_DEFAULT_ONLY); + if (info != null) { + return info.activityInfo.loadBanner(this); + } + + throw new NameNotFoundException(intent.toUri(0)); + } + + @Override + public Drawable getApplicationBanner(ApplicationInfo info) { + return info.loadBanner(this); + } + + @Override + public Drawable getApplicationBanner(String packageName) + throws NameNotFoundException { + return getApplicationBanner(getApplicationInfo(packageName, sDefaultFlags)); + } + + @Override + public Drawable getActivityLogo(ComponentName activityName) + throws NameNotFoundException { + return getActivityInfo(activityName, sDefaultFlags).loadLogo(this); + } + + @Override + public Drawable getActivityLogo(Intent intent) + throws NameNotFoundException { + if (intent.getComponent() != null) { + return getActivityLogo(intent.getComponent()); + } + + ResolveInfo info = resolveActivity(intent, MATCH_DEFAULT_ONLY); + if (info != null) { + return info.activityInfo.loadLogo(this); + } + + throw new NameNotFoundException(intent.toUri(0)); + } + + @Override + public Drawable getApplicationLogo(ApplicationInfo info) { + return info.loadLogo(this); + } + + @Override + public Drawable getApplicationLogo(String packageName) + throws NameNotFoundException { + return getApplicationLogo(getApplicationInfo(packageName, sDefaultFlags)); + } + + @Override + public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { + if (!hasUserBadge(user.getIdentifier())) { + return icon; + } + + final Drawable badgeForeground = getDevicePolicyManager().getResources().getDrawable( + getUpdatableUserIconBadgeId(user), + SOLID_COLORED, + () -> getDefaultUserIconBadge(user)); + + Drawable badge = new LauncherIcons(mContext).getBadgeDrawable( + badgeForeground, + getUserBadgeColor(user, false)); + return getBadgedDrawable(icon, badge, null, true); + } + + private String getUpdatableUserIconBadgeId(UserHandle user) { + return getUserManager().isManagedProfile(user.getIdentifier()) + ? WORK_PROFILE_ICON_BADGE : UNDEFINED; + } + + private Drawable getDefaultUserIconBadge(UserHandle user) { + return mContext.getDrawable(getUserManager().getUserIconBadgeResId(user.getIdentifier())); + } + + @Override + public Drawable getUserBadgedDrawableForDensity(Drawable drawable, UserHandle user, + Rect badgeLocation, int badgeDensity) { + Drawable badgeDrawable = getUserBadgeForDensity(user, badgeDensity); + if (badgeDrawable == null) { + return drawable; + } + return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true); + } + + /** + * Returns the color of the user's actual badge (not the badge's shadow). + * @param checkTheme whether to check the theme to determine the badge color. This should be + * true if the background is determined by the theme. Otherwise, if + * checkTheme is false, returns the color assuming a light background. + */ + private int getUserBadgeColor(UserHandle user, boolean checkTheme) { + if (checkTheme && mContext.getResources().getConfiguration().isNightModeActive()) { + return getUserManager().getUserBadgeDarkColor(user.getIdentifier()); + } + return getUserManager().getUserBadgeColor(user.getIdentifier()); + } + + @Override + public Drawable getUserBadgeForDensity(UserHandle user, int density) { + // This is part of the shadow, not the main color, and is not actually corp-specific. + Drawable badgeColor = getProfileIconForDensity(user, + com.android.internal.R.drawable.ic_corp_badge_color, density); + if (badgeColor == null) { + return null; + } + + final Drawable badgeForeground = getDevicePolicyManager().getResources() + .getDrawableForDensity( + getUpdatableUserBadgeId(user), + SOLID_COLORED, + density, + () -> getDefaultUserBadgeForDensity(user, density)); + + badgeForeground.setTint(getUserBadgeColor(user, false)); + Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground }); + return badge; + } + + private String getUpdatableUserBadgeId(UserHandle user) { + return getUserManager().isManagedProfile(user.getIdentifier()) + ? WORK_PROFILE_ICON : UNDEFINED; + } + + private Drawable getDefaultUserBadgeForDensity(UserHandle user, int density) { + return getDrawableForDensity( + getUserManager().getUserBadgeResId(user.getIdentifier()), density); + } + + /** + * Returns the badge color based on whether device has dark theme enabled or not. + */ + @Override + public Drawable getUserBadgeForDensityNoBackground(UserHandle user, int density) { + if (!hasUserBadge(user.getIdentifier())) { + return null; + } + + final Drawable badge = getDevicePolicyManager().getResources().getDrawableForDensity( + getUpdatableUserBadgeId(user), + SOLID_NOT_COLORED, + density, + () -> getDefaultUserBadgeNoBackgroundForDensity(user, density)); + + if (badge != null) { + badge.setTint(getUserBadgeColor(user, true)); + } + return badge; + } + + private Drawable getDefaultUserBadgeNoBackgroundForDensity(UserHandle user, int density) { + return getDrawableForDensity( + getUserManager().getUserBadgeNoBackgroundResId(user.getIdentifier()), density); + } + + private Drawable getDrawableForDensity(int drawableId, int density) { + if (density <= 0) { + density = mContext.getResources().getDisplayMetrics().densityDpi; + } + return mContext.getResources().getDrawableForDensity(drawableId, density); + } + + private Drawable getProfileIconForDensity(UserHandle user, int drawableId, int density) { + if (hasUserBadge(user.getIdentifier())) { + return getDrawableForDensity(drawableId, density); + } + return null; + } + + @Override + public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) { + return getUserManager().getBadgedLabelForUser(label, user); + } + + @Override + public Resources getResourcesForActivity(ComponentName activityName) + throws NameNotFoundException { + return getResourcesForApplication( + getActivityInfo(activityName, sDefaultFlags).applicationInfo); + } + + @Override + public Resources getResourcesForApplication(@NonNull ApplicationInfo app) + throws NameNotFoundException { + return getResourcesForApplication(app, null); + } + + @Override + public Resources getResourcesForApplication(@NonNull ApplicationInfo app, + @Nullable Configuration configuration) throws NameNotFoundException { + if (app.packageName.equals("system")) { + Context sysuiContext = mContext.mMainThread.getSystemUiContext(); + if (configuration != null) { + sysuiContext = sysuiContext.createConfigurationContext(configuration); + } + return sysuiContext.getResources(); + } + final boolean sameUid = (app.uid == Process.myUid()); + final Resources r = mContext.mMainThread.getTopLevelResources( + sameUid ? app.sourceDir : app.publicSourceDir, + sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, + app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles, + mContext.mPackageInfo, configuration); + if (r != null) { + return r; + } + throw new NameNotFoundException("Unable to open " + app.publicSourceDir); + } + + @Override + public Resources getResourcesForApplication(String appPackageName) + throws NameNotFoundException { + return getResourcesForApplication( + getApplicationInfo(appPackageName, sDefaultFlags)); + } + + /** @hide */ + @Override + public Resources getResourcesForApplicationAsUser(String appPackageName, int userId) + throws NameNotFoundException { + if (userId < 0) { + throw new IllegalArgumentException( + "Call does not support special user #" + userId); + } + if ("system".equals(appPackageName)) { + return mContext.mMainThread.getSystemUiContext().getResources(); + } + try { + ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, sDefaultFlags, userId); + if (ai != null) { + return getResourcesForApplication(ai); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); + } + + volatile int mCachedSafeMode = -1; + + @Override + public boolean isSafeMode() { + try { + if (mCachedSafeMode < 0) { + mCachedSafeMode = mPM.isSafeMode() ? 1 : 0; + } + return mCachedSafeMode != 0; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) { + getPermissionManager().addOnPermissionsChangeListener(listener); + } + + @Override + public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) { + getPermissionManager().removeOnPermissionsChangeListener(listener); + } + + @UnsupportedAppUsage + static void configurationChanged() { + synchronized (sSync) { + sIconCache.clear(); + sStringCache.clear(); + } + } + + @UnsupportedAppUsage + protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) { + mContext = context; + mPM = pm; + } + + /** + * Update given flags when being used to request {@link PackageInfo}. + */ + private long updateFlagsForPackage(long flags, int userId) { + if ((flags & (GET_ACTIVITIES | GET_RECEIVERS | GET_SERVICES | GET_PROVIDERS)) != 0) { + // Caller is asking for component details, so they'd better be + // asking for specific Direct Boot matching behavior + if ((flags & (MATCH_DIRECT_BOOT_UNAWARE + | MATCH_DIRECT_BOOT_AWARE + | MATCH_DIRECT_BOOT_AUTO)) == 0) { + onImplicitDirectBoot(userId); + } + } + return flags; + } + + /** + * Update given flags when being used to request {@link ApplicationInfo}. + */ + private long updateFlagsForApplication(long flags, int userId) { + return updateFlagsForPackage(flags, userId); + } + + /** + * Update given flags when being used to request {@link ComponentInfo}. + */ + private long updateFlagsForComponent(@ComponentInfoFlagsBits long flags, int userId, + Intent intent) { + if (intent != null) { + if ((intent.getFlags() & Intent.FLAG_DIRECT_BOOT_AUTO) != 0) { + flags |= MATCH_DIRECT_BOOT_AUTO; + } + } + + // Caller is asking for component details, so they'd better be + // asking for specific Direct Boot matching behavior + if ((flags & (MATCH_DIRECT_BOOT_UNAWARE + | MATCH_DIRECT_BOOT_AWARE + | MATCH_DIRECT_BOOT_AUTO)) == 0) { + onImplicitDirectBoot(userId); + } + return flags; + } + + private void onImplicitDirectBoot(int userId) { + // Only report if someone is relying on implicit behavior while the user + // is locked; code running when unlocked is going to see both aware and + // unaware components. + if (StrictMode.vmImplicitDirectBootEnabled()) { + // We can cache the unlocked state for the userId we're running as, + // since any relocking of that user will always result in our + // process being killed to release any CE FDs we're holding onto. + if (userId == UserHandle.myUserId()) { + if (mUserUnlocked) { + return; + } else if (mContext.getSystemService(UserManager.class) + .isUserUnlockingOrUnlocked(userId)) { + mUserUnlocked = true; + } else { + StrictMode.onImplicitDirectBoot(); + } + } else if (!mContext.getSystemService(UserManager.class) + .isUserUnlockingOrUnlocked(userId)) { + StrictMode.onImplicitDirectBoot(); + } + } + } + + @Nullable + private Drawable getCachedIcon(@NonNull ResourceName name) { + synchronized (sSync) { + final WeakReference wr = sIconCache.get(name); + if (DEBUG_ICONS) Log.v(TAG, "Get cached weak drawable ref for " + + name + ": " + wr); + if (wr != null) { // we have the activity + final Drawable.ConstantState state = wr.get(); + if (state != null) { + if (DEBUG_ICONS) { + Log.v(TAG, "Get cached drawable state for " + name + ": " + state); + } + // Note: It's okay here to not use the newDrawable(Resources) variant + // of the API. The ConstantState comes from a drawable that was + // originally created by passing the proper app Resources instance + // which means the state should already contain the proper + // resources specific information (like density.) See + // BitmapDrawable.BitmapState for instance. + return state.newDrawable(); + } + // our entry has been purged + sIconCache.remove(name); + } + } + return null; + } + + private void putCachedIcon(@NonNull ResourceName name, @NonNull Drawable dr) { + synchronized (sSync) { + sIconCache.put(name, new WeakReference<>(dr.getConstantState())); + if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable state for " + name + ": " + dr); + } + } + + static void handlePackageBroadcast(int cmd, String[] pkgList, boolean hasPkgInfo) { + boolean immediateGc = false; + if (cmd == ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE) { + immediateGc = true; + } + if (pkgList != null && (pkgList.length > 0)) { + boolean needCleanup = false; + for (String ssp : pkgList) { + synchronized (sSync) { + for (int i=sIconCache.size()-1; i>=0; i--) { + ResourceName nm = sIconCache.keyAt(i); + if (nm.packageName.equals(ssp)) { + //Log.i(TAG, "Removing cached drawable for " + nm); + sIconCache.removeAt(i); + needCleanup = true; + } + } + for (int i=sStringCache.size()-1; i>=0; i--) { + ResourceName nm = sStringCache.keyAt(i); + if (nm.packageName.equals(ssp)) { + //Log.i(TAG, "Removing cached string for " + nm); + sStringCache.removeAt(i); + needCleanup = true; + } + } + } + } + if (needCleanup || hasPkgInfo) { + if (immediateGc) { + // Schedule an immediate gc. + Runtime.getRuntime().gc(); + } else { + ActivityThread.currentActivityThread().scheduleGcIdler(); + } + } + } + } + + private static final class ResourceName { + final String packageName; + final int iconId; + + ResourceName(String _packageName, int _iconId) { + packageName = _packageName; + iconId = _iconId; + } + + ResourceName(ApplicationInfo aInfo, int _iconId) { + this(aInfo.packageName, _iconId); + } + + ResourceName(ComponentInfo cInfo, int _iconId) { + this(cInfo.applicationInfo.packageName, _iconId); + } + + ResourceName(ResolveInfo rInfo, int _iconId) { + this(rInfo.activityInfo.applicationInfo.packageName, _iconId); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ResourceName that = (ResourceName) o; + + if (iconId != that.iconId) return false; + return !(packageName != null ? + !packageName.equals(that.packageName) : that.packageName != null); + + } + + @Override + public int hashCode() { + int result; + result = packageName.hashCode(); + result = 31 * result + iconId; + return result; + } + + @Override + public String toString() { + return "{ResourceName " + packageName + " / " + iconId + "}"; + } + } + + private CharSequence getCachedString(ResourceName name) { + synchronized (sSync) { + WeakReference wr = sStringCache.get(name); + if (wr != null) { // we have the activity + CharSequence cs = wr.get(); + if (cs != null) { + return cs; + } + // our entry has been purged + sStringCache.remove(name); + } + } + return null; + } + + private void putCachedString(ResourceName name, CharSequence cs) { + synchronized (sSync) { + sStringCache.put(name, new WeakReference(cs)); + } + } + + @Override + public CharSequence getText(String packageName, @StringRes int resid, + ApplicationInfo appInfo) { + ResourceName name = new ResourceName(packageName, resid); + CharSequence text = getCachedString(name); + if (text != null) { + return text; + } + if (appInfo == null) { + try { + appInfo = getApplicationInfo(packageName, sDefaultFlags); + } catch (NameNotFoundException e) { + return null; + } + } + try { + Resources r = getResourcesForApplication(appInfo); + text = r.getText(resid); + putCachedString(name, text); + return text; + } catch (NameNotFoundException e) { + Log.w("PackageManager", "Failure retrieving resources for " + + appInfo.packageName); + } catch (RuntimeException e) { + // If an exception was thrown, fall through to return + // default icon. + Log.w("PackageManager", "Failure retrieving text 0x" + + Integer.toHexString(resid) + " in package " + + packageName, e); + } + return null; + } + + @Override + public XmlResourceParser getXml(String packageName, @XmlRes int resid, + ApplicationInfo appInfo) { + if (appInfo == null) { + try { + appInfo = getApplicationInfo(packageName, sDefaultFlags); + } catch (NameNotFoundException e) { + return null; + } + } + try { + Resources r = getResourcesForApplication(appInfo); + return r.getXml(resid); + } catch (RuntimeException e) { + // If an exception was thrown, fall through to return + // default icon. + Log.w("PackageManager", "Failure retrieving xml 0x" + + Integer.toHexString(resid) + " in package " + + packageName, e); + } catch (NameNotFoundException e) { + Log.w("PackageManager", "Failure retrieving resources for " + + appInfo.packageName); + } + return null; + } + + @Override + public CharSequence getApplicationLabel(ApplicationInfo info) { + return info.loadLabel(this); + } + + @Override + public int installExistingPackage(String packageName) throws NameNotFoundException { + return installExistingPackage(packageName, INSTALL_REASON_UNKNOWN); + } + + @Override + public int installExistingPackage(String packageName, int installReason) + throws NameNotFoundException { + return installExistingPackageAsUser(packageName, installReason, getUserId()); + } + + @Override + public int installExistingPackageAsUser(String packageName, int userId) + throws NameNotFoundException { + return installExistingPackageAsUser(packageName, INSTALL_REASON_UNKNOWN, + userId); + } + + private int installExistingPackageAsUser(String packageName, int installReason, int userId) + throws NameNotFoundException { + try { + int res = mPM.installExistingPackageAsUser(packageName, userId, + INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, installReason, null); + if (res == INSTALL_FAILED_INVALID_URI) { + throw new NameNotFoundException("Package " + packageName + " doesn't exist"); + } + return res; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void verifyPendingInstall(int id, int response) { + try { + mPM.verifyPendingInstall(id, response); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, + long millisecondsToDelay) { + try { + mPM.extendVerificationTimeout(id, verificationCodeAtTimeout, millisecondsToDelay); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void verifyIntentFilter(int id, int verificationCode, List failedDomains) { + try { + mPM.verifyIntentFilter(id, verificationCode, failedDomains); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int getIntentVerificationStatusAsUser(String packageName, int userId) { + try { + return mPM.getIntentVerificationStatus(packageName, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean updateIntentVerificationStatusAsUser(String packageName, int status, int userId) { + try { + return mPM.updateIntentVerificationStatus(packageName, status, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @SuppressWarnings("unchecked") + public List getIntentFilterVerifications(String packageName) { + try { + ParceledListSlice parceledList = + mPM.getIntentFilterVerifications(packageName); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @SuppressWarnings("unchecked") + public List getAllIntentFilters(String packageName) { + try { + ParceledListSlice parceledList = + mPM.getAllIntentFilters(packageName); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String getDefaultBrowserPackageNameAsUser(int userId) { + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + return roleManager.getBrowserRoleHolder(userId); + } + + @Override + public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) { + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + return roleManager.setBrowserRoleHolder(packageName, userId); + } + + @Override + public void setInstallerPackageName(String targetPackage, + String installerPackageName) { + try { + mPM.setInstallerPackageName(targetPackage, installerPackageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void setUpdateAvailable(String packageName, boolean updateAvailable) { + try { + mPM.setUpdateAvailable(packageName, updateAvailable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String getInstallerPackageName(String packageName) { + try { + return mPM.getInstallerPackageName(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @NonNull + public InstallSourceInfo getInstallSourceInfo(String packageName) throws NameNotFoundException { + final InstallSourceInfo installSourceInfo; + try { + installSourceInfo = mPM.getInstallSourceInfo(packageName, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + if (installSourceInfo == null) { + throw new NameNotFoundException(packageName); + } + return installSourceInfo; + } + + @Override + public boolean isAppArchivable(String packageName) throws NameNotFoundException { + try { + Objects.requireNonNull(packageName); + return mPM.isAppArchivable(packageName, new UserHandle(getUserId())); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int getMoveStatus(int moveId) { + try { + return mPM.getMoveStatus(moveId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void registerMoveCallback(MoveCallback callback, Handler handler) { + synchronized (mDelegates) { + final MoveCallbackDelegate delegate = new MoveCallbackDelegate(callback, + handler.getLooper()); + try { + mPM.registerMoveCallback(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mDelegates.add(delegate); + } + } + + @Override + public void unregisterMoveCallback(MoveCallback callback) { + synchronized (mDelegates) { + for (Iterator i = mDelegates.iterator(); i.hasNext();) { + final MoveCallbackDelegate delegate = i.next(); + if (delegate.mCallback == callback) { + try { + mPM.unregisterMoveCallback(delegate); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + i.remove(); + } + } + } + } + + @Override + public int movePackage(String packageName, VolumeInfo vol) { + try { + final String volumeUuid; + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { + volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; + } else if (vol.isPrimaryPhysical()) { + volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + volumeUuid = Objects.requireNonNull(vol.fsUuid); + } + + return mPM.movePackage(packageName, volumeUuid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + return getPackageCurrentVolume(app, storage); + } + + @VisibleForTesting + protected @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app, + StorageManager storage) { + if (app.isInternal()) { + return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); + } else { + return storage.findVolumeByUuid(app.volumeUuid); + } + } + + @Override + public @NonNull List getPackageCandidateVolumes(ApplicationInfo app) { + final StorageManager storageManager = mContext.getSystemService(StorageManager.class); + return getPackageCandidateVolumes(app, storageManager, mPM); + } + + @VisibleForTesting + protected @NonNull List getPackageCandidateVolumes(ApplicationInfo app, + StorageManager storageManager, IPackageManager pm) { + final VolumeInfo currentVol = getPackageCurrentVolume(app, storageManager); + final List vols = storageManager.getVolumes(); + final List candidates = new ArrayList<>(); + for (VolumeInfo vol : vols) { + if (Objects.equals(vol, currentVol) + || isPackageCandidateVolume(mContext, app, vol, pm)) { + candidates.add(vol); + } + } + return candidates; + } + + @VisibleForTesting + protected boolean isForceAllowOnExternal(Context context) { + return Settings.Global.getInt( + context.getContentResolver(), Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0; + } + + @VisibleForTesting + protected boolean isAllow3rdPartyOnInternal(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_allow3rdPartyAppOnInternal); + } + + private boolean isPackageCandidateVolume( + ContextImpl context, ApplicationInfo app, VolumeInfo vol, IPackageManager pm) { + final boolean forceAllowOnExternal = isForceAllowOnExternal(context); + + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { + return app.isSystemApp() || isAllow3rdPartyOnInternal(context); + } + + // System apps and apps demanding internal storage can't be moved + // anywhere else + if (app.isSystemApp()) { + return false; + } + if (!forceAllowOnExternal + && (app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY + || app.installLocation == PackageInfo.INSTALL_LOCATION_UNSPECIFIED)) { + return false; + } + + // Gotta be able to write there + if (!vol.isMountedWritable()) { + return false; + } + + // Moving into an ASEC on public primary is only option internal + if (vol.isPrimaryPhysical()) { + return app.isInternal(); + } + + // Some apps can't be moved. (e.g. device admins) + try { + if (pm.isPackageDeviceAdminOnAnyUser(app.packageName)) { + return false; + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + // Otherwise we can move to any private volume + return (vol.getType() == VolumeInfo.TYPE_PRIVATE); + } + + @Override + public int movePrimaryStorage(VolumeInfo vol) { + try { + final String volumeUuid; + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { + volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; + } else if (vol.isPrimaryPhysical()) { + volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + volumeUuid = Objects.requireNonNull(vol.fsUuid); + } + + return mPM.movePrimaryStorage(volumeUuid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final String volumeUuid = storage.getPrimaryStorageUuid(); + return storage.findVolumeByQualifiedUuid(volumeUuid); + } + + @Override + public @NonNull List getPrimaryStorageCandidateVolumes() { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final VolumeInfo currentVol = getPrimaryStorageCurrentVolume(); + final List vols = storage.getVolumes(); + final List candidates = new ArrayList<>(); + if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, + storage.getPrimaryStorageUuid()) && currentVol != null) { + // TODO: support moving primary physical to emulated volume + candidates.add(currentVol); + } else { + for (VolumeInfo vol : vols) { + if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) { + candidates.add(vol); + } + } + } + return candidates; + } + + private static boolean isPrimaryStorageCandidateVolume(VolumeInfo vol) { + // Private internal is always an option + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { + return true; + } + + // Gotta be able to write there + if (!vol.isMountedWritable()) { + return false; + } + + // We can move to any private volume + return (vol.getType() == VolumeInfo.TYPE_PRIVATE); + } + + @Override + @UnsupportedAppUsage + public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) { + deletePackageAsUser(packageName, observer, flags, getUserId()); + } + + @Override + public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, + int flags, int userId) { + try { + mPM.deletePackageAsUser(packageName, VERSION_CODE_HIGHEST, + observer, userId, flags); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void clearApplicationUserData(String packageName, + IPackageDataObserver observer) { + try { + mPM.clearApplicationUserData(packageName, observer, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override + public void deleteApplicationCacheFiles(String packageName, + IPackageDataObserver observer) { + try { + mPM.deleteApplicationCacheFiles(packageName, observer); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void deleteApplicationCacheFilesAsUser(String packageName, int userId, + IPackageDataObserver observer) { + try { + mPM.deleteApplicationCacheFilesAsUser(packageName, userId, observer); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void freeStorageAndNotify(String volumeUuid, long idealStorageSize, + IPackageDataObserver observer) { + try { + mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, 0, observer); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) { + try { + mPM.freeStorage(volumeUuid, freeStorageSize, 0, pi); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String[] setDistractingPackageRestrictions(String[] packages, int distractionFlags) { + try { + return mPM.setDistractingPackageRestrictionsAsUser(packages, distractionFlags, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String[] setPackagesSuspended(String[] packageNames, boolean suspended, + PersistableBundle appExtras, PersistableBundle launcherExtras, + String dialogMessage) { + final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage) + ? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build() + : null; + return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, + dialogInfo, 0); + } + + @Override + public String[] setPackagesSuspended(String[] packageNames, boolean suspended, + PersistableBundle appExtras, PersistableBundle launcherExtras, + SuspendDialogInfo dialogInfo) { + return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, + dialogInfo, 0); + } + + @Override + public String[] setPackagesSuspended(String[] packageNames, boolean suspended, + PersistableBundle appExtras, PersistableBundle launcherExtras, + SuspendDialogInfo dialogInfo, int flags) { + try { + return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras, + launcherExtras, dialogInfo, flags, mContext.getOpPackageName(), + UserHandle.myUserId() /* suspendingUserId */, getUserId() /* targetUserId */); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String[] getUnsuspendablePackages(String[] packageNames) { + try { + return mPM.getUnsuspendablePackagesForUser(packageNames, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public Bundle getSuspendedPackageAppExtras() { + try { + return mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(), getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public String getSuspendingPackage(String suspendedPackage) { + try { + return mPM.getSuspendingPackage(suspendedPackage, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean isPackageSuspendedForUser(String packageName, int userId) { + try { + return mPM.isPackageSuspendedForUser(packageName, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public boolean isPackageSuspended(String packageName) throws NameNotFoundException { + try { + return isPackageSuspendedForUser(packageName, getUserId()); + } catch (IllegalArgumentException ie) { + throw new NameNotFoundException(packageName); + } + } + + @Override + public boolean isPackageSuspended() { + return isPackageSuspendedForUser(mContext.getOpPackageName(), getUserId()); + } + + @Override + public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException { + try { + return mPM.isPackageQuarantinedForUser(packageName, getUserId()); + } catch (IllegalArgumentException ie) { + throw new NameNotFoundException(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException { + try { + return mPM.isPackageStoppedForUser(packageName, getUserId()); + } catch (IllegalArgumentException ie) { + throw new NameNotFoundException(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public void setApplicationCategoryHint(String packageName, int categoryHint) { + try { + mPM.setApplicationCategoryHint(packageName, categoryHint, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @UnsupportedAppUsage + public void getPackageSizeInfoAsUser(String packageName, int userHandle, + IPackageStatsObserver observer) { + final String msg = "Shame on you for calling the hidden API " + + "getPackageSizeInfoAsUser(). Shame!"; + if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) { + throw new UnsupportedOperationException(msg); + } else if (observer != null) { + Log.d(TAG, msg); + try { + observer.onGetStatsCompleted(null, false); + } catch (RemoteException ignored) { + } + } + } + + @Override + public void addPackageToPreferred(String packageName) { + Log.w(TAG, "addPackageToPreferred() is a no-op"); + } + + @Override + public void removePackageFromPreferred(String packageName) { + Log.w(TAG, "removePackageFromPreferred() is a no-op"); + } + + @Override + public List getPreferredPackages(int flags) { + Log.w(TAG, "getPreferredPackages() is a no-op"); + return Collections.emptyList(); + } + + @Override + public void addPreferredActivity(IntentFilter filter, + int match, ComponentName[] set, ComponentName activity) { + try { + mPM.addPreferredActivity(filter, match, set, activity, getUserId(), false); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void addPreferredActivityAsUser(IntentFilter filter, int match, + ComponentName[] set, ComponentName activity, int userId) { + try { + mPM.addPreferredActivity(filter, match, set, activity, userId, false); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void replacePreferredActivity(IntentFilter filter, + int match, ComponentName[] set, ComponentName activity) { + try { + mPM.replacePreferredActivity(filter, match, set, activity, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void replacePreferredActivityAsUser(IntentFilter filter, + int match, ComponentName[] set, ComponentName activity, + int userId) { + try { + mPM.replacePreferredActivity(filter, match, set, activity, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void clearPackagePreferredActivities(String packageName) { + try { + mPM.clearPackagePreferredActivities(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void addUniquePreferredActivity(@NonNull IntentFilter filter, int match, + @Nullable ComponentName[] set, @NonNull ComponentName activity) { + try { + mPM.addPreferredActivity(filter, match, set, activity, getUserId(), true); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int getPreferredActivities(List outFilters, + List outActivities, String packageName) { + try { + return mPM.getPreferredActivities(outFilters, outActivities, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public ComponentName getHomeActivities(List outActivities) { + try { + return mPM.getHomeActivities(outActivities); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void setSyntheticAppDetailsActivityEnabled(String packageName, boolean enabled) { + try { + ComponentName componentName = new ComponentName(packageName, + APP_DETAILS_ACTIVITY_CLASS_NAME); + mPM.setComponentEnabledSetting(componentName, enabled + ? COMPONENT_ENABLED_STATE_DEFAULT + : COMPONENT_ENABLED_STATE_DISABLED, + DONT_KILL_APP, getUserId(), mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean getSyntheticAppDetailsActivityEnabled(String packageName) { + try { + ComponentName componentName = new ComponentName(packageName, + APP_DETAILS_ACTIVITY_CLASS_NAME); + int state = mPM.getComponentEnabledSetting(componentName, getUserId()); + return state == COMPONENT_ENABLED_STATE_ENABLED + || state == COMPONENT_ENABLED_STATE_DEFAULT; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void setComponentEnabledSetting(ComponentName componentName, + int newState, int flags) { + try { + mPM.setComponentEnabledSetting(componentName, newState, flags, getUserId(), + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void setComponentEnabledSettings(List settings) { + try { + mPM.setComponentEnabledSettings(settings, getUserId(), mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int getComponentEnabledSetting(ComponentName componentName) { + try { + return mPM.getComponentEnabledSetting(componentName, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void setApplicationEnabledSetting(String packageName, + int newState, int flags) { + try { + mPM.setApplicationEnabledSetting(packageName, newState, flags, + getUserId(), mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int getApplicationEnabledSetting(String packageName) { + try { + return mPM.getApplicationEnabledSetting(packageName, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void flushPackageRestrictionsAsUser(int userId) { + try { + mPM.flushPackageRestrictionsAsUser(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, + UserHandle user) { + try { + return mPM.setApplicationHiddenSettingAsUser(packageName, hidden, + user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) { + try { + return mPM.getApplicationHiddenSettingAsUser(packageName, user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public void setSystemAppState(String packageName, @SystemAppState int state) { + try { + switch (state) { + case SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN: + mPM.setSystemAppHiddenUntilInstalled(packageName, true); + break; + case SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE: + mPM.setSystemAppHiddenUntilInstalled(packageName, false); + break; + case SYSTEM_APP_STATE_INSTALLED: + mPM.setSystemAppInstallState(packageName, true, getUserId()); + break; + case SYSTEM_APP_STATE_UNINSTALLED: + mPM.setSystemAppInstallState(packageName, false, getUserId()); + break; + default: + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public KeySet getKeySetByAlias(String packageName, String alias) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(alias); + try { + return mPM.getKeySetByAlias(packageName, alias); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public KeySet getSigningKeySet(String packageName) { + Objects.requireNonNull(packageName); + try { + return mPM.getSigningKeySet(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public boolean isSignedBy(String packageName, KeySet ks) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(ks); + try { + return mPM.isPackageSignedByKeySet(packageName, ks); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + @Override + public boolean isSignedByExactly(String packageName, KeySet ks) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(ks); + try { + return mPM.isPackageSignedByKeySetExactly(packageName, ks); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @Override + public VerifierDeviceIdentity getVerifierDeviceIdentity() { + try { + return mPM.getVerifierDeviceIdentity(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean isUpgrade() { + return isDeviceUpgrading(); + } + + @Override + public boolean isDeviceUpgrading() { + try { + return mPM.isDeviceUpgrading(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public PackageInstaller getPackageInstaller() { + if (mInstaller == null) { + try { + mInstaller = new PackageInstaller(mPM.getPackageInstaller(), + mContext.getPackageName(), mContext.getAttributionTag(), getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return mInstaller; + } + + @Override + public boolean isPackageAvailable(String packageName) { + try { + return mPM.isPackageAvailable(packageName, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @Override + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { + try { + mPM.addCrossProfileIntentFilter(filter, mContext.getOpPackageName(), + sourceUserId, targetUserId, flags); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @Override + public boolean removeCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, + int targetUserId, int flags) { + try { + return mPM.removeCrossProfileIntentFilter(filter, mContext.getOpPackageName(), + sourceUserId, targetUserId, flags); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @Override + public void clearCrossProfileIntentFilters(int sourceUserId) { + try { + mPM.clearCrossProfileIntentFilters(sourceUserId, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + public Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) { + Drawable dr = loadUnbadgedItemIcon(itemInfo, appInfo); + if (itemInfo.showUserIcon != UserHandle.USER_NULL) { + return dr; + } + return getUserBadgedIcon(dr, new UserHandle(getUserId())); + } + + /** + * @hide + */ + public Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo, + @Nullable ApplicationInfo appInfo) { + if (itemInfo.showUserIcon != UserHandle.USER_NULL) { + // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a + // generic user icon (users generally lack permission to view each other's actual icons) + int targetUserId = itemInfo.showUserIcon; + return UserIcons.getDefaultUserIcon( + mContext.getResources(), targetUserId, /* light= */ false); + } + Drawable dr = null; + if (itemInfo.packageName != null) { + if (itemInfo.isArchived) { + dr = getArchivedAppIcon(itemInfo.packageName); + } else { + dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo); + } + } + if (dr == null && itemInfo != appInfo && appInfo != null) { + dr = loadUnbadgedItemIcon(appInfo, appInfo); + } + if (dr == null) { + dr = itemInfo.loadDefaultIcon(this); + } + return dr; + } + + private Drawable getBadgedDrawable(Drawable drawable, Drawable badgeDrawable, + Rect badgeLocation, boolean tryBadgeInPlace) { + final int badgedWidth = drawable.getIntrinsicWidth(); + final int badgedHeight = drawable.getIntrinsicHeight(); + final boolean canBadgeInPlace = tryBadgeInPlace + && (drawable instanceof BitmapDrawable) + && ((BitmapDrawable) drawable).getBitmap().isMutable(); + + final Bitmap bitmap; + if (canBadgeInPlace) { + bitmap = ((BitmapDrawable) drawable).getBitmap(); + } else { + bitmap = Bitmap.createBitmap(badgedWidth, badgedHeight, Bitmap.Config.ARGB_8888); + } + Canvas canvas = new Canvas(bitmap); + + if (!canBadgeInPlace) { + drawable.setBounds(0, 0, badgedWidth, badgedHeight); + drawable.draw(canvas); + } + + if (badgeLocation != null) { + if (badgeLocation.left < 0 || badgeLocation.top < 0 + || badgeLocation.width() > badgedWidth || badgeLocation.height() > badgedHeight) { + throw new IllegalArgumentException("Badge location " + badgeLocation + + " not in badged drawable bounds " + + new Rect(0, 0, badgedWidth, badgedHeight)); + } + badgeDrawable.setBounds(0, 0, badgeLocation.width(), badgeLocation.height()); + + canvas.save(); + canvas.translate(badgeLocation.left, badgeLocation.top); + badgeDrawable.draw(canvas); + canvas.restore(); + } else { + badgeDrawable.setBounds(0, 0, badgedWidth, badgedHeight); + badgeDrawable.draw(canvas); + } + + if (!canBadgeInPlace) { + BitmapDrawable mergedDrawable = new BitmapDrawable(mContext.getResources(), bitmap); + + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + mergedDrawable.setTargetDensity(bitmapDrawable.getBitmap().getDensity()); + } + + return mergedDrawable; + } + + return drawable; + } + + private boolean hasUserBadge(int userId) { + return getUserManager().hasBadge(userId); + } + + /** + * @hide + */ + @Override + public int getInstallReason(String packageName, UserHandle user) { + try { + return mPM.getInstallReason(packageName, user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ + private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements + Handler.Callback { + private static final int MSG_CREATED = 1; + private static final int MSG_STATUS_CHANGED = 2; + + final MoveCallback mCallback; + final Handler mHandler; + + public MoveCallbackDelegate(MoveCallback callback, Looper looper) { + mCallback = callback; + mHandler = new Handler(looper, this); + } + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_CREATED: { + final SomeArgs args = (SomeArgs) msg.obj; + mCallback.onCreated(args.argi1, (Bundle) args.arg2); + args.recycle(); + return true; + } + case MSG_STATUS_CHANGED: { + final SomeArgs args = (SomeArgs) msg.obj; + mCallback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3); + args.recycle(); + return true; + } + } + return false; + } + + @Override + public void onCreated(int moveId, Bundle extras) { + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = moveId; + args.arg2 = extras; + mHandler.obtainMessage(MSG_CREATED, args).sendToTarget(); + } + + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = moveId; + args.argi2 = status; + args.arg3 = estMillis; + mHandler.obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget(); + } + } + + private final ContextImpl mContext; + @UnsupportedAppUsage + private final IPackageManager mPM; + + /** Assume locked until we hear otherwise */ + private volatile boolean mUserUnlocked = false; + + private static final Object sSync = new Object(); + private static ArrayMap> sIconCache + = new ArrayMap>(); + private static ArrayMap> sStringCache + = new ArrayMap>(); + + @Override + public boolean canRequestPackageInstalls() { + try { + return mPM.canRequestPackageInstalls(mContext.getPackageName(), getUserId()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public ComponentName getInstantAppResolverSettingsComponent() { + try { + return mPM.getInstantAppResolverSettingsComponent(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public ComponentName getInstantAppInstallerComponent() { + try { + return mPM.getInstantAppInstallerComponent(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getInstantAppAndroidId(String packageName, UserHandle user) { + try { + return mPM.getInstantAppAndroidId(packageName, user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + private static class DexModuleRegisterResult { + final String dexModulePath; + final boolean success; + final String message; + + private DexModuleRegisterResult(String dexModulePath, boolean success, String message) { + this.dexModulePath = dexModulePath; + this.success = success; + this.message = message; + } + } + + private static class DexModuleRegisterCallbackDelegate + extends android.content.pm.IDexModuleRegisterCallback.Stub + implements Handler.Callback { + private static final int MSG_DEX_MODULE_REGISTERED = 1; + private final DexModuleRegisterCallback callback; + private final Handler mHandler; + + DexModuleRegisterCallbackDelegate(@NonNull DexModuleRegisterCallback callback) { + this.callback = callback; + mHandler = new Handler(Looper.getMainLooper(), this); + } + + @Override + public void onDexModuleRegistered(@NonNull String dexModulePath, boolean success, + @Nullable String message)throws RemoteException { + mHandler.obtainMessage(MSG_DEX_MODULE_REGISTERED, + new DexModuleRegisterResult(dexModulePath, success, message)).sendToTarget(); + } + + @Override + public boolean handleMessage(Message msg) { + if (msg.what != MSG_DEX_MODULE_REGISTERED) { + return false; + } + DexModuleRegisterResult result = (DexModuleRegisterResult)msg.obj; + callback.onDexModuleRegistered(result.dexModulePath, result.success, result.message); + return true; + } + } + + @Override + public void registerDexModule(@NonNull String dexModule, + @Nullable DexModuleRegisterCallback callback) { + // Create the callback delegate to be passed to package manager service. + DexModuleRegisterCallbackDelegate callbackDelegate = null; + if (callback != null) { + callbackDelegate = new DexModuleRegisterCallbackDelegate(callback); + } + + // Check if this is a shared module by looking if the others can read it. + boolean isSharedModule = false; + try { + StructStat stat = Os.stat(dexModule); + if ((OsConstants.S_IROTH & stat.st_mode) != 0) { + isSharedModule = true; + } + } catch (ErrnoException e) { + if (callbackDelegate != null) { + callback.onDexModuleRegistered(dexModule, false, + "Could not get stat the module file: " + e.getMessage()); + } + return; + } + + // Invoke the package manager service. + try { + mPM.registerDexModule(mContext.getPackageName(), dexModule, + isSharedModule, callbackDelegate); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public CharSequence getHarmfulAppWarning(String packageName) { + try { + return mPM.getHarmfulAppWarning(packageName, getUserId()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public void setHarmfulAppWarning(String packageName, CharSequence warning) { + try { + mPM.setHarmfulAppWarning(packageName, warning, getUserId()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public ArtManager getArtManager() { + if (mArtManager == null) { + try { + mArtManager = new ArtManager(mContext, mPM.getArtManager()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return mArtManager; + } + + @Override + public String getDefaultTextClassifierPackageName() { + try { + return mPM.getDefaultTextClassifierPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getSystemTextClassifierPackageName() { + try { + return mPM.getSystemTextClassifierPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getAttentionServicePackageName() { + try { + return mPM.getAttentionServicePackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getRotationResolverPackageName() { + try { + return mPM.getRotationResolverPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getWellbeingPackageName() { + try { + return mPM.getWellbeingPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getAppPredictionServicePackageName() { + try { + return mPM.getAppPredictionServicePackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getSystemCaptionsServicePackageName() { + try { + return mPM.getSystemCaptionsServicePackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getSetupWizardPackageName() { + try { + return mPM.getSetupWizardPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public String getIncidentReportApproverPackageName() { + try { + return mPM.getIncidentReportApproverPackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public boolean isPackageStateProtected(String packageName, int userId) { + try { + return mPM.isPackageStateProtected(packageName, userId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public void sendDeviceCustomizationReadyBroadcast() { + try { + mPM.sendDeviceCustomizationReadyBroadcast(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public boolean isAutoRevokeWhitelisted() { + try { + return mPM.isAutoRevokeWhitelisted(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public void setMimeGroup(String mimeGroup, Set mimeTypes) { + try { + mPM.setMimeGroup(mContext.getPackageName(), mimeGroup, new ArrayList<>(mimeTypes)); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @NonNull + @Override + public Set getMimeGroup(String group) { + try { + List mimeGroup = mPM.getMimeGroup(mContext.getPackageName(), group); + return new ArraySet<>(mimeGroup); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public Property getProperty(String propertyName, String packageName) + throws NameNotFoundException { + Objects.requireNonNull(packageName); + Objects.requireNonNull(propertyName); + return getPropertyAsUser(propertyName, packageName, null /* className */, getUserId()); + } + + @Override + public Property getProperty(String propertyName, ComponentName component) + throws NameNotFoundException { + Objects.requireNonNull(component); + Objects.requireNonNull(propertyName); + return getPropertyAsUser(propertyName, + component.getPackageName(), component.getClassName(), getUserId()); + } + + @Override + public Property getPropertyAsUser(@NonNull String propertyName, @NonNull String packageName, + @Nullable String className, int userId) throws NameNotFoundException { + Objects.requireNonNull(packageName); + Objects.requireNonNull(propertyName); + try { + final Property property = mPM.getPropertyAsUser(propertyName, + packageName, className, userId); + if (property == null) { + throw new NameNotFoundException(); + } + return property; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List queryApplicationProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice parceledList = + mPM.queryProperty(propertyName, TYPE_APPLICATION); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List queryActivityProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice parceledList = + mPM.queryProperty(propertyName, TYPE_ACTIVITY); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List queryProviderProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice parceledList = + mPM.queryProperty(propertyName, TYPE_PROVIDER); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List queryReceiverProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice parceledList = + mPM.queryProperty(propertyName, TYPE_RECEIVER); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List queryServiceProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice parceledList = + mPM.queryProperty(propertyName, TYPE_SERVICE); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public boolean canPackageQuery(@NonNull String sourcePackageName, + @NonNull String targetPackageName) throws NameNotFoundException { + Objects.requireNonNull(sourcePackageName); + Objects.requireNonNull(targetPackageName); + return canPackageQuery(sourcePackageName, new String[]{targetPackageName})[0]; + } + + @Override + @NonNull + public boolean[] canPackageQuery(@NonNull String sourcePackageName, + @NonNull String[] targetPackageNames) throws NameNotFoundException { + Objects.requireNonNull(sourcePackageName); + Objects.requireNonNull(targetPackageNames); + try { + return mPM.canPackageQuery(sourcePackageName, targetPackageNames, getUserId()); + } catch (ParcelableException e) { + e.maybeRethrow(PackageManager.NameNotFoundException.class); + throw new RuntimeException(e); + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); + } + } + + @Override + public void makeUidVisible(int recipientUid, int visibleUid) { + try { + mPM.makeUidVisible(recipientUid, visibleUid); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) { + try { + var parcel = mPM.getArchivedPackage(packageName, mContext.getUserId()); + if (parcel == null) { + return null; + } + return new ArchivedPackageInfo(parcel); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public boolean canUserUninstall(String packageName, UserHandle user) { + try { + return mPM.getBlockUninstallForUser(packageName, user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public boolean shouldShowNewAppInstalledNotification() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED, 0) == 1; + } + + @Override + public void relinquishUpdateOwnership(String targetPackage) { + Objects.requireNonNull(targetPackage); + try { + mPM.relinquishUpdateOwnership(targetPackage); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { + Objects.requireNonNull(callback); + try { + mPM.registerPackageMonitorCallback(callback, userId); + synchronized (mPackageMonitorCallbacks) { + if (mPackageMonitorCallbacks.contains(callback)) { + throw new IllegalStateException( + "registerPackageMonitorCallback: callback already registered: " + + callback); + } + mPackageMonitorCallbacks.add(callback); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) { + Objects.requireNonNull(callback); + try { + mPM.unregisterPackageMonitorCallback(callback); + synchronized (mPackageMonitorCallbacks) { + mPackageMonitorCallbacks.remove(callback); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Nullable + private Drawable getArchivedAppIcon(String packageName) { + try { + Bitmap archivedAppIcon = mPM.getArchivedAppIcon(packageName, + new UserHandle(getUserId()), + mContext.getPackageName()); + if (archivedAppIcon == null) { + return null; + } + return new BitmapDrawable(null, archivedAppIcon); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to retrieve archived app icon: " + e.getMessage()); + return null; + } + } + + @Override + public T parseAndroidManifest(@NonNull File apkFile, + @NonNull Function parserFunction) throws IOException { + Objects.requireNonNull(apkFile, "apkFile cannot be null"); + Objects.requireNonNull(parserFunction, "parserFunction cannot be null"); + try (XmlResourceParser xmlResourceParser = getAndroidManifestParser(apkFile)) { + return parserFunction.apply(xmlResourceParser); + } catch (IOException e) { + Log.w(TAG, "Failed to get the android manifest parser", e); + throw e; + } + } + + private static XmlResourceParser getAndroidManifestParser(@NonNull File apkFile) + throws IOException { + ApkAssets apkAssets = null; + try { + apkAssets = ApkAssets.loadFromPath(apkFile.getAbsolutePath()); + return apkAssets.openXml(ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME); + } finally { + if (apkAssets != null) { + try { + apkAssets.close(); + } catch (Throwable ignored) { + Log.w(TAG, "Failed to close apkAssets", ignored); + } + } + } + } +} diff --git a/aosp/frameworks/base/core/java/android/os/Build.java b/aosp/frameworks/base/core/java/android/os/Build.java new file mode 100755 index 0000000000000000000000000000000000000000..d4293cc22ee2dfa7d5a3668922b71b4609466045 --- /dev/null +++ b/aosp/frameworks/base/core/java/android/os/Build.java @@ -0,0 +1,1593 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.Manifest; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.ActivityThread; +import android.app.Application; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; +import android.sysprop.DeviceProperties; +import android.sysprop.SocProperties; +import android.sysprop.TelephonyProperties; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Slog; +import android.view.View; + +import dalvik.system.VMRuntime; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Information about the current build, extracted from system properties. + */ +@RavenwoodKeepWholeClass +public class Build { + private static final String TAG = "Build"; + + /** Value used for when a build property is unknown. */ + public static final String UNKNOWN = "unknown"; + + /** Either a changelist number, or a label like "M4-rc20". */ + public static final String ID = getString("ro.build.id"); + + /** A build ID string meant for displaying to the user */ + public static final String DISPLAY = getString("ro.build.display.id"); + + /** The name of the overall product. */ + public static final String PRODUCT = getString("ro.product.name"); + + /** + * The product name for attestation. In non-default builds (like the AOSP build) the value of + * the 'PRODUCT' system property may be different to the one provisioned to KeyMint, + * and Keymint attestation would still attest to the product name which was provisioned. + * @hide + */ + @Nullable + @TestApi + public static final String PRODUCT_FOR_ATTESTATION = getVendorDeviceIdProperty("name"); + + /** The name of the industrial design. */ + public static final String DEVICE = getString("ro.product.device"); + + /** + * The device name for attestation. In non-default builds (like the AOSP build) the value of + * the 'DEVICE' system property may be different to the one provisioned to KeyMint, + * and Keymint attestation would still attest to the device name which was provisioned. + * @hide + */ + @Nullable + @TestApi + public static final String DEVICE_FOR_ATTESTATION = + getVendorDeviceIdProperty("device"); + + /** The name of the underlying board, like "goldfish". */ + public static final String BOARD = getString("ro.product.board"); + + /** + * The name of the instruction set (CPU type + ABI convention) of native code. + * + * @deprecated Use {@link #SUPPORTED_ABIS} instead. + */ + @Deprecated + public static final String CPU_ABI; + + /** + * The name of the second instruction set (CPU type + ABI convention) of native code. + * + * @deprecated Use {@link #SUPPORTED_ABIS} instead. + */ + @Deprecated + public static final String CPU_ABI2; + + /** The manufacturer of the product/hardware. */ + public static final String MANUFACTURER = getString("ro.product.manufacturer"); + + /** + * The manufacturer name for attestation. In non-default builds (like the AOSP build) the value + * of the 'MANUFACTURER' system property may be different to the one provisioned to KeyMint, + * and Keymint attestation would still attest to the manufacturer which was provisioned. + * @hide + */ + @Nullable + @TestApi + public static final String MANUFACTURER_FOR_ATTESTATION = + getVendorDeviceIdProperty("manufacturer"); + + /** The consumer-visible brand with which the product/hardware will be associated, if any. */ + public static final String BRAND = getString("ro.product.brand"); + + /** + * The product brand for attestation. In non-default builds (like the AOSP build) the value of + * the 'BRAND' system property may be different to the one provisioned to KeyMint, + * and Keymint attestation would still attest to the product brand which was provisioned. + * @hide + */ + @Nullable + @TestApi + public static final String BRAND_FOR_ATTESTATION = getVendorDeviceIdProperty("brand"); + + /** The end-user-visible name for the end product. */ + public static final String MODEL = getString("ro.product.model"); + + /** + * The product model for attestation. In non-default builds (like the AOSP build) the value of + * the 'MODEL' system property may be different to the one provisioned to KeyMint, + * and Keymint attestation would still attest to the product model which was provisioned. + * @hide + */ + @Nullable + @TestApi + public static final String MODEL_FOR_ATTESTATION = getVendorDeviceIdProperty("model"); + + /** The manufacturer of the device's primary system-on-chip. */ + @NonNull + public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN); + + /** The model name of the device's primary system-on-chip. */ + @NonNull + public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN); + + /** The system bootloader version number. */ + public static final String BOOTLOADER = getString("ro.bootloader"); + + /** + * The radio firmware version number. + * + * @deprecated The radio firmware version is frequently not + * available when this class is initialized, leading to a blank or + * "unknown" value for this string. Use + * {@link #getRadioVersion} instead. + */ + @Deprecated + public static final String RADIO = joinListOrElse( + TelephonyProperties.baseband_version(), UNKNOWN); + + /** The name of the hardware (from the kernel command line or /proc). */ + public static final String HARDWARE = getString("ro.hardware"); + + /** + * The SKU of the hardware (from the kernel command line). + * + *

The SKU is reported by the bootloader to configure system software features. + * If no value is supplied by the bootloader, this is reported as {@link #UNKNOWN}. + + */ + @NonNull + public static final String SKU = getString("ro.boot.hardware.sku"); + + /** + * The SKU of the device as set by the original design manufacturer (ODM). + * + *

This is a runtime-initialized property set during startup to configure device + * services. If no value is set, this is reported as {@link #UNKNOWN}. + * + *

The ODM SKU may have multiple variants for the same system SKU in case a manufacturer + * produces variants of the same design. For example, the same build may be released with + * variations in physical keyboard and/or display hardware, each with a different ODM SKU. + */ + @NonNull + public static final String ODM_SKU = getString("ro.boot.product.hardware.sku"); + + /** + * Whether this build was for an emulator device. + * @hide + */ + @UnsupportedAppUsage + @TestApi + public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1"); + + /** + * A hardware serial number, if available. Alphanumeric only, case-insensitive. + * This field is always set to {@link Build#UNKNOWN}. + * + * @deprecated Use {@link #getSerial()} instead. + **/ + @Deprecated + // IMPORTANT: This field should be initialized via a function call to + // prevent its value being inlined in the app during compilation because + // we will later set it to the value based on the app's target SDK. + public static final String SERIAL = getString("ro.serialno"); + + /** + * Gets the hardware serial number, if available. + * + *

Note: Root access may allow you to modify device identifiers, such as + * the hardware serial number. If you change these identifiers, you can not use + * key attestation to obtain + * proof of the device's original identifiers. KeyMint will reject an ID attestation request + * if the identifiers provided by the frameworks do not match the identifiers it was + * provisioned with. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app has carrier privileges (see {@link + * android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * android.app.role.RoleManager#isRoleHeld(String)}). + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then {@link Build#UNKNOWN} is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @return The serial number if specified. + */ + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static String getSerial() { + IDeviceIdentifiersPolicyService service = IDeviceIdentifiersPolicyService.Stub + .asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)); + try { + Application application = ActivityThread.currentApplication(); + String callingPackage = application != null ? application.getPackageName() : null; + return service.getSerialForPackage(callingPackage, null); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + return getString("ro.serialno"); + //return UNKNOWN; + } + + /** + * An ordered list of ABIs supported by this device. The most preferred ABI is the first + * element in the list. + * + * See {@link #SUPPORTED_32_BIT_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}. + */ + public static final String[] SUPPORTED_ABIS = getStringList("ro.product.cpu.abilist", ","); + + /** + * An ordered list of 32 bit ABIs supported by this device. The most preferred ABI + * is the first element in the list. + * + * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}. + */ + public static final String[] SUPPORTED_32_BIT_ABIS = + getStringList("ro.product.cpu.abilist32", ","); + + /** + * An ordered list of 64 bit ABIs supported by this device. The most preferred ABI + * is the first element in the list. + * + * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_32_BIT_ABIS}. + */ + public static final String[] SUPPORTED_64_BIT_ABIS = + getStringList("ro.product.cpu.abilist64", ","); + + /** {@hide} */ + @TestApi + public static boolean is64BitAbi(String abi) { + return VMRuntime.is64BitAbi(abi); + } + + static { + /* + * Adjusts CPU_ABI and CPU_ABI2 depending on whether or not a given process is 64 bit. + * 32 bit processes will always see 32 bit ABIs in these fields for backward + * compatibility. + */ + final String[] abiList; + if (android.os.Process.is64Bit()) { + abiList = SUPPORTED_64_BIT_ABIS; + } else { + abiList = SUPPORTED_32_BIT_ABIS; + } + + CPU_ABI = abiList[0]; + if (abiList.length > 1) { + CPU_ABI2 = abiList[1]; + } else { + CPU_ABI2 = ""; + } + } + + /** Various version strings. */ + public static class VERSION { + /** + * The internal value used by the underlying source control to + * represent this build. E.g., a perforce changelist number + * or a git hash. + */ + public static final String INCREMENTAL = getString("ro.build.version.incremental"); + + /** + * The user-visible version string. E.g., "1.0" or "3.4b5" or "bananas". + * + * This field is an opaque string. Do not assume that its value + * has any particular structure or that values of RELEASE from + * different releases can be somehow ordered. + */ + public static final String RELEASE = getString("ro.build.version.release"); + + /** + * The version string. May be {@link #RELEASE} or {@link #CODENAME} if + * not a final release build. + */ + @NonNull public static final String RELEASE_OR_CODENAME = getString( + "ro.build.version.release_or_codename"); + + /** + * The version string we show to the user; may be {@link #RELEASE} or + * a descriptive string if not a final release build. + */ + @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY = getString( + "ro.build.version.release_or_preview_display"); + + /** + * The base OS build the product is based on. + */ + public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", ""); + + /** + * The user-visible security patch level. This value represents the date when the device + * most recently applied a security patch. + */ + public static final String SECURITY_PATCH = SystemProperties.get( + "ro.build.version.security_patch", ""); + + /** + * The media performance class of the device or 0 if none. + *

+ * If this value is not 0, the device conforms to the media performance class + * definition of the SDK version of this value. This value never changes while a device is + * booted, but it may increase when the hardware manufacturer provides an OTA update. + *

+ * Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with + * {@link Build.VERSION_CODES#R}. + */ + public static final int MEDIA_PERFORMANCE_CLASS = + DeviceProperties.media_performance_class().orElse(0); + + /** + * The user-visible SDK version of the framework in its raw String + * representation; use {@link #SDK_INT} instead. + * + * @deprecated Use {@link #SDK_INT} to easily get this as an integer. + */ + @Deprecated + public static final String SDK = getString("ro.build.version.sdk"); + + /** + * The SDK version of the software currently running on this hardware + * device. This value never changes while a device is booted, but it may + * increase when the hardware manufacturer provides an OTA update. + *

+ * Possible values are defined in {@link Build.VERSION_CODES}. + */ + public static final int SDK_INT = SystemProperties.getInt( + "ro.build.version.sdk", 0); + + /** + * The SDK version of the software that initially shipped on + * this hardware device. It never changes during the lifetime + * of the device, even when {@link #SDK_INT} increases due to an OTA + * update. + *

+ * Possible values are defined in {@link Build.VERSION_CODES}. + * + * @see #SDK_INT + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public static final int DEVICE_INITIAL_SDK_INT = SystemProperties + .getInt("ro.product.first_api_level", 0); + + /** + * The developer preview revision of a prerelease SDK. This value will always + * be 0 on production platform builds/devices. + * + *

When this value is nonzero, any new API added since the last + * officially published {@link #SDK_INT API level} is only guaranteed to be present + * on that specific preview revision. For example, an API Activity.fooBar() + * might be present in preview revision 1 but renamed or removed entirely in + * preview revision 2, which may cause an app attempting to call it to crash + * at runtime.

+ * + *

Experimental apps targeting preview APIs should check this value for + * equality (==) with the preview SDK revision they were built for + * before using any prerelease platform APIs. Apps that detect a preview SDK revision + * other than the specific one they expect should fall back to using APIs from + * the previously published API level only to avoid unwanted runtime exceptions. + *

+ */ + public static final int PREVIEW_SDK_INT = SystemProperties.getInt( + "ro.build.version.preview_sdk", 0); + + /** + * The SDK fingerprint for a given prerelease SDK. This value will always be + * {@code REL} on production platform builds/devices. + * + *

When this value is not {@code REL}, it contains a string fingerprint of the API + * surface exposed by the preview SDK. Preview platforms with different API surfaces + * will have different {@code PREVIEW_SDK_FINGERPRINT}. + * + *

This attribute is intended for use by installers for finer grained targeting of + * packages. Applications targeting preview APIs should not use this field and should + * instead use {@code PREVIEW_SDK_INT} or use reflection or other runtime checks to + * detect the presence of an API or guard themselves against unexpected runtime + * behavior. + * + * @hide + */ + @SystemApi + @NonNull public static final String PREVIEW_SDK_FINGERPRINT = SystemProperties.get( + "ro.build.version.preview_sdk_fingerprint", "REL"); + + /** + * The current development codename, or the string "REL" if this is + * a release build. + */ + public static final String CODENAME = getString("ro.build.version.codename"); + + /** + * All known codenames that are present in {@link VERSION_CODES}. + * + *

This includes in development codenames as well, i.e. if {@link #CODENAME} is not "REL" + * then the value of that is present in this set. + * + *

If a particular string is not present in this set, then it is either not a codename + * or a codename for a future release. For example, during Android R development, "Tiramisu" + * was not a known codename. + * + * @hide + */ + @SystemApi + @NonNull public static final Set KNOWN_CODENAMES = + new ArraySet<>(getStringList("ro.build.version.known_codenames", ",")); + + private static final String[] ALL_CODENAMES + = getStringList("ro.build.version.all_codenames", ","); + + /** + * @hide + */ + @UnsupportedAppUsage + @TestApi + public static final String[] ACTIVE_CODENAMES = "REL".equals(ALL_CODENAMES[0]) + ? new String[0] : ALL_CODENAMES; + + /** + * The SDK version to use when accessing resources. + * Use the current SDK version code. For every active development codename + * we are operating under, we bump the assumed resource platform version by 1. + * @hide + */ + @TestApi + public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length; + + /** + * The current lowest supported value of app target SDK. Applications targeting + * lower values may not function on devices running this SDK version. Its possible + * values are defined in {@link Build.VERSION_CODES}. + * + * @hide + */ + public static final int MIN_SUPPORTED_TARGET_SDK_INT = SystemProperties.getInt( + "ro.build.version.min_supported_target_sdk", 0); + } + + /** + * Enumeration of the currently known SDK version codes. These are the + * values that can be found in {@link VERSION#SDK}. Version numbers + * increment monotonically with each official platform release. + */ + public static class VERSION_CODES { + /** + * Magic version number for a current development build, which has + * not yet turned into an official release. + */ + // This must match VMRuntime.SDK_VERSION_CUR_DEVELOPMENT. + public static final int CUR_DEVELOPMENT = 10000; + + /** + * The original, first, version of Android. Yay! + * + *

Released publicly as Android 1.0 in September 2008. + */ + public static final int BASE = 1; + + /** + * First Android update. + * + *

Released publicly as Android 1.1 in February 2009. + */ + public static final int BASE_1_1 = 2; + + /** + * C. + * + *

Released publicly as Android 1.5 in April 2009. + */ + public static final int CUPCAKE = 3; + + /** + * D. + * + *

Released publicly as Android 1.6 in September 2009. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • They must explicitly request the + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission to be + * able to modify the contents of the SD card. (Apps targeting + * earlier versions will always request the permission.) + *
  • They must explicitly request the + * {@link android.Manifest.permission#READ_PHONE_STATE} permission to be + * able to be able to retrieve phone state info. (Apps targeting + * earlier versions will always request the permission.) + *
  • They are assumed to support different screen densities and + * sizes. (Apps targeting earlier versions are assumed to only support + * medium density normal size screens unless otherwise indicated). + * They can still explicitly specify screen support either way with the + * supports-screens manifest tag. + *
  • {@link android.widget.TabHost} will use the new dark tab + * background design. + *
+ */ + public static final int DONUT = 4; + + /** + * E. + * + *

Released publicly as Android 2.0 in October 2009. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • The {@link android.app.Service#onStartCommand + * Service.onStartCommand} function will return the new + * {@link android.app.Service#START_STICKY} behavior instead of the + * old compatibility {@link android.app.Service#START_STICKY_COMPATIBILITY}. + *
  • The {@link android.app.Activity} class will now execute back + * key presses on the key up instead of key down, to be able to detect + * canceled presses from virtual keys. + *
  • The {@link android.widget.TabWidget} class will use a new color scheme + * for tabs. In the new scheme, the foreground tab has a medium gray background + * the background tabs have a dark gray background. + *
+ */ + public static final int ECLAIR = 5; + + /** + * E incremental update. + * + *

Released publicly as Android 2.0.1 in December 2009. + */ + public static final int ECLAIR_0_1 = 6; + + /** + * E MR1. + * + *

Released publicly as Android 2.1 in January 2010. + */ + public static final int ECLAIR_MR1 = 7; + + /** + * F. + * + *

Released publicly as Android 2.2 in May 2010. + */ + public static final int FROYO = 8; + + /** + * G. + * + *

Released publicly as Android 2.3 in December 2010. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • The application's notification icons will be shown on the new + * dark status bar background, so must be visible in this situation. + *
+ */ + public static final int GINGERBREAD = 9; + + /** + * G MR1. + * + *

Released publicly as Android 2.3.3 in February 2011. + */ + public static final int GINGERBREAD_MR1 = 10; + + /** + * H. + * + *

Released publicly as Android 3.0 in February 2011. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • The default theme for applications is now dark holographic: + * {@link android.R.style#Theme_Holo}. + *
  • On large screen devices that do not have a physical menu + * button, the soft (compatibility) menu is disabled. + *
  • The activity lifecycle has changed slightly as per + * {@link android.app.Activity}. + *
  • An application will crash if it does not call through + * to the super implementation of its + * {@link android.app.Activity#onPause Activity.onPause()} method. + *
  • When an application requires a permission to access one of + * its components (activity, receiver, service, provider), this + * permission is no longer enforced when the application wants to + * access its own component. This means it can require a permission + * on a component that it does not itself hold and still access that + * component. + *
  • {@link android.content.Context#getSharedPreferences + * Context.getSharedPreferences()} will not automatically reload + * the preferences if they have changed on storage, unless + * {@link android.content.Context#MODE_MULTI_PROCESS} is used. + *
  • {@link android.view.ViewGroup#setMotionEventSplittingEnabled} + * will default to true. + *
  • {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} + * is enabled by default on windows. + *
  • {@link android.widget.PopupWindow#isSplitTouchEnabled() + * PopupWindow.isSplitTouchEnabled()} will return true by default. + *
  • {@link android.widget.GridView} and {@link android.widget.ListView} + * will use {@link android.view.View#setActivated View.setActivated} + * for selected items if they do not implement {@link android.widget.Checkable}. + *
  • {@link android.widget.Scroller} will be constructed with + * "flywheel" behavior enabled by default. + *
+ */ + public static final int HONEYCOMB = 11; + + /** + * H MR1. + * + *

Released publicly as Android 3.1 in May 2011. + */ + public static final int HONEYCOMB_MR1 = 12; + + /** + * H MR2. + * + *

Released publicly as Android 3.2 in July 2011. + *

Update to Honeycomb MR1 to support 7 inch tablets, improve + * screen compatibility mode, etc.

+ * + *

As of this version, applications that don't say whether they + * support XLARGE screens will be assumed to do so only if they target + * {@link #HONEYCOMB} or later; it had been {@link #GINGERBREAD} or + * later. Applications that don't support a screen size at least as + * large as the current screen will provide the user with a UI to + * switch them in to screen size compatibility mode.

+ * + *

This version introduces new screen size resource qualifiers + * based on the screen size in dp: see + * {@link android.content.res.Configuration#screenWidthDp}, + * {@link android.content.res.Configuration#screenHeightDp}, and + * {@link android.content.res.Configuration#smallestScreenWidthDp}. + * Supplying these in <supports-screens> as per + * {@link android.content.pm.ApplicationInfo#requiresSmallestWidthDp}, + * {@link android.content.pm.ApplicationInfo#compatibleWidthLimitDp}, and + * {@link android.content.pm.ApplicationInfo#largestWidthLimitDp} is + * preferred over the older screen size buckets and for older devices + * the appropriate buckets will be inferred from them.

+ * + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • New {@link android.content.pm.PackageManager#FEATURE_SCREEN_PORTRAIT} + * and {@link android.content.pm.PackageManager#FEATURE_SCREEN_LANDSCAPE} + * features were introduced in this release. Applications that target + * previous platform versions are assumed to require both portrait and + * landscape support in the device; when targeting Honeycomb MR1 or + * greater the application is responsible for specifying any specific + * orientation it requires.

    + *
  • {@link android.os.AsyncTask} will use the serial executor + * by default when calling {@link android.os.AsyncTask#execute}.

    + *
  • {@link android.content.pm.ActivityInfo#configChanges + * ActivityInfo.configChanges} will have the + * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_SIZE} and + * {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE} + * bits set; these need to be cleared for older applications because + * some developers have done absolute comparisons against this value + * instead of correctly masking the bits they are interested in. + *

+ */ + public static final int HONEYCOMB_MR2 = 13; + + /** + * I. + * + *

Released publicly as Android 4.0 in October 2011. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • For devices without a dedicated menu key, the software compatibility + * menu key will not be shown even on phones. By targeting Ice Cream Sandwich + * or later, your UI must always have its own menu UI affordance if needed, + * on both tablets and phones. The ActionBar will take care of this for you. + *
  • 2d drawing hardware acceleration is now turned on by default. + * You can use + * {@link android.R.attr#hardwareAccelerated android:hardwareAccelerated} + * to turn it off if needed, although this is strongly discouraged since + * it will result in poor performance on larger screen devices. + *
  • The default theme for applications is now the "device default" theme: + * {@link android.R.style#Theme_DeviceDefault}. This may be the + * holo dark theme or a different dark theme defined by the specific device. + * The {@link android.R.style#Theme_Holo} family must not be modified + * for a device to be considered compatible. Applications that explicitly + * request a theme from the Holo family will be guaranteed that these themes + * will not change character within the same platform version. Applications + * that wish to blend in with the device should use a theme from the + * {@link android.R.style#Theme_DeviceDefault} family. + *
  • Managed cursors can now throw an exception if you directly close + * the cursor yourself without stopping the management of it; previously failures + * would be silently ignored. + *
  • The fadingEdge attribute on views will be ignored (fading edges is no + * longer a standard part of the UI). A new requiresFadingEdge attribute allows + * applications to still force fading edges on for special cases. + *
  • {@link android.content.Context#bindService Context.bindService()} + * will not automatically add in {@link android.content.Context#BIND_WAIVE_PRIORITY}. + *
  • App Widgets will have standard padding automatically added around + * them, rather than relying on the padding being baked into the widget itself. + *
  • An exception will be thrown if you try to change the type of a + * window after it has been added to the window manager. Previously this + * would result in random incorrect behavior. + *
  • {@link android.view.animation.AnimationSet} will parse out + * the duration, fillBefore, fillAfter, repeatMode, and startOffset + * XML attributes that are defined. + *
  • {@link android.app.ActionBar#setHomeButtonEnabled + * ActionBar.setHomeButtonEnabled()} is false by default. + *
+ */ + public static final int ICE_CREAM_SANDWICH = 14; + + /** + * I MR1. + * + *

Released publicly as Android 4.03 in December 2011. + */ + public static final int ICE_CREAM_SANDWICH_MR1 = 15; + + /** + * J. + * + *

Released publicly as Android 4.1 in July 2012. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • You must explicitly request the {@link android.Manifest.permission#READ_CALL_LOG} + * and/or {@link android.Manifest.permission#WRITE_CALL_LOG} permissions; + * access to the call log is no longer implicitly provided through + * {@link android.Manifest.permission#READ_CONTACTS} and + * {@link android.Manifest.permission#WRITE_CONTACTS}. + *
  • {@link android.widget.RemoteViews} will throw an exception if + * setting an onClick handler for views being generated by a + * {@link android.widget.RemoteViewsService} for a collection container; + * previously this just resulted in a warning log message. + *
  • New {@link android.app.ActionBar} policy for embedded tabs: + * embedded tabs are now always stacked in the action bar when in portrait + * mode, regardless of the size of the screen. + *
  • {@link android.webkit.WebSettings#setAllowFileAccessFromFileURLs(boolean) + * WebSettings.setAllowFileAccessFromFileURLs} and + * {@link android.webkit.WebSettings#setAllowUniversalAccessFromFileURLs(boolean) + * WebSettings.setAllowUniversalAccessFromFileURLs} default to false. + *
  • Calls to {@link android.content.pm.PackageManager#setComponentEnabledSetting + * PackageManager.setComponentEnabledSetting} will now throw an + * IllegalArgumentException if the given component class name does not + * exist in the application's manifest. + *
  • {@code NfcAdapter.setNdefPushMessage}, + * {@code NfcAdapter.setNdefPushMessageCallback} and + * {@code NfcAdapter.setOnNdefPushCompleteCallback} will throw + * IllegalStateException if called after the Activity has been destroyed. + *
  • Accessibility services must require the new + * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission or + * they will not be available for use. + *
  • {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS + * AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} must be set + * for unimportant views to be included in queries. + *
+ */ + public static final int JELLY_BEAN = 16; + + /** + * J MR1. + * + *

Released publicly as Android 4.2 in November 2012. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • Content Providers: The default value of {@code android:exported} is now + * {@code false}. See + * + * the android:exported section in the provider documentation for more details.
  • + *
  • {@link android.view.View#getLayoutDirection() View.getLayoutDirection()} + * can return different values than {@link android.view.View#LAYOUT_DIRECTION_LTR} + * based on the locale etc. + *
  • {@link android.webkit.WebView#addJavascriptInterface(Object, String) + * WebView.addJavascriptInterface} requires explicit annotations on methods + * for them to be accessible from Javascript. + *
+ */ + public static final int JELLY_BEAN_MR1 = 17; + + /** + * J MR2. + * + *

Released publicly as Android 4.3 in July 2013. + */ + public static final int JELLY_BEAN_MR2 = 18; + + /** + * K. + * + *

Released publicly as Android 4.4 in October 2013. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see the + * Android KitKat overview.

+ *
    + *
  • The default result of + * {@link android.preference.PreferenceActivity#isValidFragment(String) + * PreferenceActivity.isValueFragment} becomes false instead of true.
  • + *
  • In {@link android.webkit.WebView}, apps targeting earlier versions will have + * JS URLs evaluated directly and any result of the evaluation will not replace + * the current page content. Apps targetting KITKAT or later that load a JS URL will + * have the result of that URL replace the content of the current page
  • + *
  • {@link android.app.AlarmManager#set AlarmManager.set} becomes interpreted as + * an inexact value, to give the system more flexibility in scheduling alarms.
  • + *
  • {@link android.content.Context#getSharedPreferences(String, int) + * Context.getSharedPreferences} no longer allows a null name.
  • + *
  • {@link android.widget.RelativeLayout} changes to compute wrapped content + * margins correctly.
  • + *
  • {@link android.app.ActionBar}'s window content overlay is allowed to be + * drawn.
  • + *
  • The {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} + * permission is now always enforced.
  • + *
  • Access to package-specific external storage directories belonging + * to the calling app no longer requires the + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} or + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} + * permissions.
  • + *
+ */ + public static final int KITKAT = 19; + + /** + * K for watches. + * + *

Released publicly as Android 4.4W in June 2014. + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • {@link android.app.AlertDialog} might not have a default background if the theme does + * not specify one.
  • + *
+ */ + public static final int KITKAT_WATCH = 20; + + /** + * Temporary until we completely switch to {@link #LOLLIPOP}. + * @hide + */ + public static final int L = 21; + + /** + * L. + * + *

Released publicly as Android 5.0 in November 2014. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see the + * Android Lollipop overview.

+ *
    + *
  • {@link android.content.Context#bindService Context.bindService} now + * requires an explicit Intent, and will throw an exception if given an implicit + * Intent.
  • + *
  • {@link android.app.Notification.Builder Notification.Builder} will + * not have the colors of their various notification elements adjusted to better + * match the new material design look.
  • + *
  • {@link android.os.Message} will validate that a message is not currently + * in use when it is recycled.
  • + *
  • Hardware accelerated drawing in windows will be enabled automatically + * in most places.
  • + *
  • {@link android.widget.Spinner} throws an exception if attaching an + * adapter with more than one item type.
  • + *
  • If the app is a launcher, the launcher will be available to the user + * even when they are using corporate profiles (which requires that the app + * use {@link android.content.pm.LauncherApps} to correctly populate its + * apps UI).
  • + *
  • Calling {@link android.app.Service#stopForeground Service.stopForeground} + * with removeNotification false will modify the still posted notification so that + * it is no longer forced to be ongoing.
  • + *
  • A {@link android.service.dreams.DreamService} must require the + * {@link android.Manifest.permission#BIND_DREAM_SERVICE} permission to be usable.
  • + *
+ */ + public static final int LOLLIPOP = 21; + + /** + * L MR1. + * + *

Released publicly as Android 5.1 in March 2015. + *

For more information about this release, see the + * Android 5.1 APIs. + */ + public static final int LOLLIPOP_MR1 = 22; + + /** + * M. + * + *

Released publicly as Android 6.0 in October 2015. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see the + * Android 6.0 Marshmallow overview.

+ *
    + *
  • Runtime permissions. Dangerous permissions are no longer granted at + * install time, but must be requested by the application at runtime through + * {@link android.app.Activity#requestPermissions}.
  • + *
  • Bluetooth and Wi-Fi scanning now requires holding the location permission.
  • + *
  • {@link android.app.AlarmManager#setTimeZone AlarmManager.setTimeZone} will fail if + * the given timezone is non-Olson.
  • + *
  • Activity transitions will only return shared + * elements mapped in the returned view hierarchy back to the calling activity.
  • + *
  • {@link android.view.View} allows a number of behaviors that may break + * existing apps: Canvas throws an exception if restore() is called too many times, + * widgets may return a hint size when returning UNSPECIFIED measure specs, and it + * will respect the attributes {@link android.R.attr#foreground}, + * {@link android.R.attr#foregroundGravity}, {@link android.R.attr#foregroundTint}, and + * {@link android.R.attr#foregroundTintMode}.
  • + *
  • {@link android.view.MotionEvent#getButtonState MotionEvent.getButtonState} + * will no longer report {@link android.view.MotionEvent#BUTTON_PRIMARY} + * and {@link android.view.MotionEvent#BUTTON_SECONDARY} as synonyms for + * {@link android.view.MotionEvent#BUTTON_STYLUS_PRIMARY} and + * {@link android.view.MotionEvent#BUTTON_STYLUS_SECONDARY}.
  • + *
  • {@link android.widget.ScrollView} now respects the layout param margins + * when measuring.
  • + *
+ */ + public static final int M = 23; + + /** + * N. + * + *

Released publicly as Android 7.0 in August 2016. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see + * the Android Nougat overview.

+ *
    + *
  • {@link android.app.DownloadManager.Request#setAllowedNetworkTypes + * DownloadManager.Request.setAllowedNetworkTypes} + * will disable "allow over metered" when specifying only + * {@link android.app.DownloadManager.Request#NETWORK_WIFI}.
  • + *
  • {@link android.app.DownloadManager} no longer allows access to raw + * file paths.
  • + *
  • {@link android.app.Notification.Builder#setShowWhen + * Notification.Builder.setShowWhen} + * must be called explicitly to have the time shown, and various other changes in + * {@link android.app.Notification.Builder Notification.Builder} to how notifications + * are shown.
  • + *
  • {@link android.content.Context#MODE_WORLD_READABLE} and + * {@link android.content.Context#MODE_WORLD_WRITEABLE} are no longer supported.
  • + *
  • {@link android.os.FileUriExposedException} will be thrown to applications.
  • + *
  • Applications will see global drag and drops as per + * {@link android.view.View#DRAG_FLAG_GLOBAL}.
  • + *
  • {@link android.webkit.WebView#evaluateJavascript WebView.evaluateJavascript} + * will not persist state from an empty WebView.
  • + *
  • {@link android.animation.AnimatorSet} will not ignore calls to end() before + * start().
  • + *
  • {@link android.app.AlarmManager#cancel(android.app.PendingIntent) + * AlarmManager.cancel} will throw a NullPointerException if given a null operation.
  • + *
  • {@link android.app.FragmentManager} will ensure fragments have been created + * before being placed on the back stack.
  • + *
  • {@link android.app.FragmentManager} restores fragments in + * {@link android.app.Fragment#onCreate Fragment.onCreate} rather than after the + * method returns.
  • + *
  • {@link android.R.attr#resizeableActivity} defaults to true.
  • + *
  • {@link android.graphics.drawable.AnimatedVectorDrawable} throws exceptions when + * opening invalid VectorDrawable animations.
  • + *
  • {@link android.view.ViewGroup.MarginLayoutParams} will no longer be dropped + * when converting between some types of layout params (such as + * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} to + * {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams}).
  • + *
  • Your application processes will not be killed when the device density changes.
  • + *
  • Drag and drop. After a view receives the + * {@link android.view.DragEvent#ACTION_DRAG_ENTERED} event, when the drag shadow moves into + * a descendant view that can accept the data, the view receives the + * {@link android.view.DragEvent#ACTION_DRAG_EXITED} event and won’t receive + * {@link android.view.DragEvent#ACTION_DRAG_LOCATION} and + * {@link android.view.DragEvent#ACTION_DROP} events while the drag shadow is within that + * descendant view, even if the descendant view returns false from its handler + * for these events.
  • + *
+ */ + public static final int N = 24; + + /** + * N MR1. + * + *

Released publicly as Android 7.1 in October 2016. + *

For more information about this release, see + * Android 7.1 for + * Developers. + */ + public static final int N_MR1 = 25; + + /** + * O. + * + *

Released publicly as Android 8.0 in August 2017. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see + * the Android Oreo overview.

+ *
    + *
  • Background execution limits + * are applied to the application.
  • + *
  • The behavior of AccountManager's + * {@link android.accounts.AccountManager#getAccountsByType}, + * {@link android.accounts.AccountManager#getAccountsByTypeAndFeatures}, and + * {@link android.accounts.AccountManager#hasFeatures} has changed as documented there.
  • + *
  • {@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE_PRE_26} + * is now returned as + * {@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE}.
  • + *
  • The {@link android.app.NotificationManager} now requires the use of notification + * channels.
  • + *
  • Changes to the strict mode that are set in + * {@link Application#onCreate Application.onCreate} will no longer be clobbered after + * that function returns.
  • + *
  • A shared library apk with native code will have that native code included in + * the library path of its clients.
  • + *
  • {@link android.content.Context#getSharedPreferences Context.getSharedPreferences} + * in credential encrypted storage will throw an exception before the user is unlocked.
  • + *
  • Attempting to retrieve a {@link Context#FINGERPRINT_SERVICE} on a device that + * does not support that feature will now throw a runtime exception.
  • + *
  • {@link android.app.Fragment} will stop any active view animations when + * the fragment is stopped.
  • + *
  • Some compatibility code in Resources that attempts to use the default Theme + * the app may be using will be turned off, requiring the app to explicitly request + * resources with the right theme.
  • + *
  • {@link android.content.ContentResolver#notifyChange ContentResolver.notifyChange} and + * {@link android.content.ContentResolver#registerContentObserver + * ContentResolver.registerContentObserver} + * will throw a SecurityException if the caller does not have permission to access + * the provider (or the provider doesn't exit); otherwise the call will be silently + * ignored.
  • + *
  • {@link android.hardware.camera2.CameraDevice#createCaptureRequest + * CameraDevice.createCaptureRequest} will enable + * {@link android.hardware.camera2.CaptureRequest#CONTROL_ENABLE_ZSL} by default for + * still image capture.
  • + *
  • WallpaperManager's {@link android.app.WallpaperManager#getWallpaperFile}, + * {@link android.app.WallpaperManager#getDrawable}, + * {@link android.app.WallpaperManager#getFastDrawable}, + * {@link android.app.WallpaperManager#peekDrawable}, and + * {@link android.app.WallpaperManager#peekFastDrawable} will throw an exception + * if you can not access the wallpaper.
  • + *
  • The behavior of + * {@link android.hardware.usb.UsbDeviceConnection#requestWait UsbDeviceConnection.requestWait} + * is modified as per the documentation there.
  • + *
  • {@link StrictMode.VmPolicy.Builder#detectAll StrictMode.VmPolicy.Builder.detectAll} + * will also enable {@link StrictMode.VmPolicy.Builder#detectContentUriWithoutPermission} + * and {@link StrictMode.VmPolicy.Builder#detectUntaggedSockets}.
  • + *
  • {@link StrictMode.ThreadPolicy.Builder#detectAll StrictMode.ThreadPolicy.Builder.detectAll} + * will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.
  • + *
  • {@link android.provider.DocumentsContract}'s various methods will throw failure + * exceptions back to the caller instead of returning null. + *
  • {@link View#hasFocusable() View.hasFocusable} now includes auto-focusable views.
  • + *
  • {@link android.view.SurfaceView} will no longer always change the underlying + * Surface object when something about it changes; apps need to look at the current + * state of the object to determine which things they are interested in have changed.
  • + *
  • {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} must be + * used for overlay windows, other system overlay window types are not allowed.
  • + *
  • {@link android.view.ViewTreeObserver#addOnDrawListener + * ViewTreeObserver.addOnDrawListener} will throw an exception if called from within + * onDraw.
  • + *
  • {@link android.graphics.Canvas#setBitmap Canvas.setBitmap} will no longer preserve + * the current matrix and clip stack of the canvas.
  • + *
  • {@link android.widget.ListPopupWindow#setHeight ListPopupWindow.setHeight} + * will throw an exception if a negative height is supplied.
  • + *
  • {@link android.widget.TextView} will use internationalized input for numbers, + * dates, and times.
  • + *
  • {@link android.widget.Toast} must be used for showing toast windows; the toast + * window type can not be directly used.
  • + *
  • {@link android.net.wifi.WifiManager#getConnectionInfo WifiManager.getConnectionInfo} + * requires that the caller hold the location permission to return BSSID/SSID
  • + *
  • {@link android.net.wifi.p2p.WifiP2pManager#requestPeers WifiP2pManager.requestPeers} + * requires the caller hold the location permission.
  • + *
  • {@link android.R.attr#maxAspectRatio} defaults to 0, meaning there is no restriction + * on the app's maximum aspect ratio (so it can be stretched to fill larger screens).
  • + *
  • {@link android.R.attr#focusable} defaults to a new state ({@code auto}) where it will + * inherit the value of {@link android.R.attr#clickable} unless explicitly overridden.
  • + *
  • A default theme-appropriate focus-state highlight will be supplied to all Views + * which don't provide a focus-state drawable themselves. This can be disabled by setting + * {@link android.R.attr#defaultFocusHighlightEnabled} to false.
  • + *
+ */ + public static final int O = 26; + + /** + * O MR1. + * + *

Released publicly as Android 8.1 in December 2017. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see + * Android 8.1 features and + * APIs.

+ *
    + *
  • Apps exporting and linking to apk shared libraries must explicitly + * enumerate all signing certificates in a consistent order.
  • + *
  • {@link android.R.attr#screenOrientation} can not be used to request a fixed + * orientation if the associated activity is not fullscreen and opaque.
  • + *
+ * + */ + public static final int O_MR1 = 27; + + /** + * P. + * + *

Released publicly as Android 9 in August 2018. + *

Applications targeting this or a later release will get these + * new changes in behavior. For more information about this release, see the + * Android 9 Pie overview.

+ *
    + *
  • {@link android.app.Service#startForeground Service.startForeground} requires + * that apps hold the permission + * {@link android.Manifest.permission#FOREGROUND_SERVICE}.
  • + *
  • {@link android.widget.LinearLayout} will always remeasure weighted children, + * even if there is no excess space.
  • + *
+ * + */ + public static final int P = 28; + + /** + * Q. + * + *

Released publicly as Android 10 in September 2019. + *

Applications targeting this or a later release will get these new changes in behavior. + * For more information about this release, see the + * Android 10 overview.

+ * + * + */ + public static final int Q = 29; + + /** + * R. + * + *

Released publicly as Android 11 in September 2020. + *

Applications targeting this or a later release will get these new changes in behavior. + * For more information about this release, see the + * Android 11 overview.

+ * + * + */ + public static final int R = 30; + + /** + * S. + */ + public static final int S = 31; + + /** + * S V2. + * + * Once more unto the breach, dear friends, once more. + */ + public static final int S_V2 = 32; + + /** + * Tiramisu. + */ + public static final int TIRAMISU = 33; + + /** + * Upside Down Cake. + */ + public static final int UPSIDE_DOWN_CAKE = 34; + + /** + * Vanilla Ice Cream. + */ + @FlaggedApi(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM) + public static final int VANILLA_ICE_CREAM = CUR_DEVELOPMENT; + } + + /** The type of build, like "user" or "eng". */ + public static final String TYPE = getString("ro.build.type"); + + /** Comma-separated tags describing the build, like "unsigned,debug". */ + public static final String TAGS = getString("ro.build.tags"); + + /** A string that uniquely identifies this build. Do not attempt to parse this value. */ + public static final String FINGERPRINT = deriveFingerprint(); + + /** + * Some devices split the fingerprint components between multiple + * partitions, so we might derive the fingerprint at runtime. + */ + private static String deriveFingerprint() { + String finger = SystemProperties.get("ro.build.fingerprint"); + if (TextUtils.isEmpty(finger)) { + finger = getString("ro.product.brand") + '/' + + getString("ro.product.name") + '/' + + getString("ro.product.device") + ':' + + getString("ro.build.version.release") + '/' + + getString("ro.build.id") + '/' + + getString("ro.build.version.incremental") + ':' + + getString("ro.build.type") + '/' + + getString("ro.build.tags"); + } + return finger; + } + + /** + * Ensure that raw fingerprint system property is defined. If it was derived + * dynamically by {@link #deriveFingerprint()} this is where we push the + * derived value into the property service. + * + * @hide + */ + public static void ensureFingerprintProperty() { + if (TextUtils.isEmpty(SystemProperties.get("ro.build.fingerprint"))) { + try { + SystemProperties.set("ro.build.fingerprint", FINGERPRINT); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Failed to set fingerprint property", e); + } + } + } + + /** + * A multiplier for various timeouts on the system. + * + * The intent is that products targeting software emulators that are orders of magnitude slower + * than real hardware may set this to a large number. On real devices and hardware-accelerated + * virtualized devices this should not be set. + * + * @hide + */ + public static final int HW_TIMEOUT_MULTIPLIER = + SystemProperties.getInt("ro.hw_timeout_multiplier", 1); + + /** + * True if Treble is enabled and required for this device. + * + * @hide + */ + public static final boolean IS_TREBLE_ENABLED = + SystemProperties.getBoolean("ro.treble.enabled", false); + + /** + * Verifies the current flash of the device is consistent with what + * was expected at build time. + * + * Treble devices will verify the Vendor Interface (VINTF). A device + * launched without Treble: + * + * 1) Checks that device fingerprint is defined and that it matches across + * various partitions. + * 2) Verifies radio and bootloader partitions are those expected in the build. + * + * @hide + */ + public static boolean isBuildConsistent() { + // Don't care on eng builds. Incremental build may trigger false negative. + if (IS_ENG) return true; + + if (IS_TREBLE_ENABLED) { + int result = VintfObject.verifyBuildAtBoot(); + + if (result != 0) { + Slog.e(TAG, "Vendor interface is incompatible, error=" + + String.valueOf(result)); + } + + return result == 0; + } + + final String system = SystemProperties.get("ro.system.build.fingerprint"); + final String vendor = SystemProperties.get("ro.vendor.build.fingerprint"); + final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint"); + final String requiredBootloader = SystemProperties.get("ro.build.expect.bootloader"); + final String currentBootloader = SystemProperties.get("ro.bootloader"); + final String requiredRadio = SystemProperties.get("ro.build.expect.baseband"); + final String currentRadio = joinListOrElse( + TelephonyProperties.baseband_version(), ""); + + if (TextUtils.isEmpty(system)) { + Slog.e(TAG, "Required ro.system.build.fingerprint is empty!"); + return false; + } + + if (!TextUtils.isEmpty(vendor)) { + if (!Objects.equals(system, vendor)) { + Slog.e(TAG, "Mismatched fingerprints; system reported " + system + + " but vendor reported " + vendor); + return false; + } + } + + /* TODO: Figure out issue with checks failing + if (!TextUtils.isEmpty(bootimage)) { + if (!Objects.equals(system, bootimage)) { + Slog.e(TAG, "Mismatched fingerprints; system reported " + system + + " but bootimage reported " + bootimage); + return false; + } + } + + if (!TextUtils.isEmpty(requiredBootloader)) { + if (!Objects.equals(currentBootloader, requiredBootloader)) { + Slog.e(TAG, "Mismatched bootloader version: build requires " + requiredBootloader + + " but runtime reports " + currentBootloader); + return false; + } + } + + if (!TextUtils.isEmpty(requiredRadio)) { + if (!Objects.equals(currentRadio, requiredRadio)) { + Slog.e(TAG, "Mismatched radio version: build requires " + requiredRadio + + " but runtime reports " + currentRadio); + return false; + } + } + */ + + return true; + } + + /** Build information for a particular device partition. */ + public static class Partition { + /** The name identifying the system partition. */ + public static final String PARTITION_NAME_SYSTEM = "system"; + /** @hide */ + public static final String PARTITION_NAME_BOOTIMAGE = "bootimage"; + /** @hide */ + public static final String PARTITION_NAME_ODM = "odm"; + /** @hide */ + public static final String PARTITION_NAME_OEM = "oem"; + /** @hide */ + public static final String PARTITION_NAME_PRODUCT = "product"; + /** @hide */ + public static final String PARTITION_NAME_SYSTEM_EXT = "system_ext"; + /** @hide */ + public static final String PARTITION_NAME_VENDOR = "vendor"; + + private final String mName; + private final String mFingerprint; + private final long mTimeMs; + + private Partition(String name, String fingerprint, long timeMs) { + mName = name; + mFingerprint = fingerprint; + mTimeMs = timeMs; + } + + /** The name of this partition, e.g. "system", or "vendor" */ + @NonNull + public String getName() { + return mName; + } + + /** The build fingerprint of this partition, see {@link Build#FINGERPRINT}. */ + @NonNull + public String getFingerprint() { + return mFingerprint; + } + + /** The time (ms since epoch), at which this partition was built, see {@link Build#TIME}. */ + public long getBuildTimeMillis() { + return mTimeMs; + } + + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof Partition)) { + return false; + } + Partition op = (Partition) o; + return mName.equals(op.mName) + && mFingerprint.equals(op.mFingerprint) + && mTimeMs == op.mTimeMs; + } + + @Override + public int hashCode() { + return Objects.hash(mName, mFingerprint, mTimeMs); + } + } + + /** + * Get build information about partitions that have a separate fingerprint defined. + * + * The list includes partitions that are suitable candidates for over-the-air updates. This is + * not an exhaustive list of partitions on the device. + */ + @NonNull + public static List getFingerprintedPartitions() { + ArrayList partitions = new ArrayList(); + + String[] names = new String[] { + Partition.PARTITION_NAME_BOOTIMAGE, + Partition.PARTITION_NAME_ODM, + Partition.PARTITION_NAME_PRODUCT, + Partition.PARTITION_NAME_SYSTEM_EXT, + Partition.PARTITION_NAME_SYSTEM, + Partition.PARTITION_NAME_VENDOR + }; + for (String name : names) { + String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint"); + if (TextUtils.isEmpty(fingerprint)) { + continue; + } + long time = getLong("ro." + name + ".build.date.utc") * 1000; + partitions.add(new Partition(name, fingerprint, time)); + } + + return partitions; + } + + // The following properties only make sense for internal engineering builds. + + /** The time at which the build was produced, given in milliseconds since the UNIX epoch. */ + public static final long TIME = getLong("ro.build.date.utc") * 1000; + public static final String USER = getString("ro.build.user"); + public static final String HOST = getString("ro.build.host"); + + /** + * Returns true if the device is running a debuggable build such as "userdebug" or "eng". + * + * Debuggable builds allow users to gain root access via local shell, attach debuggers to any + * application regardless of whether they have the "debuggable" attribute set, or downgrade + * selinux into "permissive" mode in particular. + * @hide + */ + @UnsupportedAppUsage + public static final boolean IS_DEBUGGABLE = + SystemProperties.getInt("ro.debuggable", 0) == 1; + + /** + * Returns true if the device is running a debuggable build such as "userdebug" or "eng". + * + * Debuggable builds allow users to gain root access via local shell, attach debuggers to any + * application regardless of whether they have the "debuggable" attribute set, or downgrade + * selinux into "permissive" mode in particular. + * @hide + */ + @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static boolean isDebuggable() { + return IS_DEBUGGABLE; + } + + /** {@hide} */ + public static final boolean IS_ENG = "eng".equals(TYPE); + /** {@hide} */ + public static final boolean IS_USERDEBUG = "userdebug".equals(TYPE); + /** {@hide} */ + public static final boolean IS_USER = "user".equals(TYPE); + + /** + * Whether this build is running on ARC, the Android Runtime for Chrome + * (https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md). + * Prior to R this was implemented as a container but from R this will be + * a VM. The name of the property remains ro.boot.conntainer as it is + * referenced in other projects. + * + * We should try to avoid checking this flag if possible to minimize + * unnecessarily diverging from non-container Android behavior. + * Checking this flag is acceptable when low-level resources being + * different, e.g. the availability of certain capabilities, access to + * system resources being restricted, and the fact that the host OS might + * handle some features for us. + * For higher-level behavior differences, other checks should be preferred. + * @hide + */ + public static final boolean IS_ARC = + SystemProperties.getBoolean("ro.boot.container", false); + + /** + * Specifies whether the permissions needed by a legacy app should be + * reviewed before any of its components can run. A legacy app is one + * with targetSdkVersion < 23, i.e apps using the old permission model. + * If review is not required, permissions are reviewed before the app + * is installed. + * + * @hide + * @removed + */ + @SystemApi + public static final boolean PERMISSIONS_REVIEW_REQUIRED = true; + + /** + * Returns the version string for the radio firmware. May return + * null (if, for instance, the radio is not currently on). + */ + public static String getRadioVersion() { + return joinListOrElse(TelephonyProperties.baseband_version(), null); + } + + @UnsupportedAppUsage + private static String getString(String property) { + return SystemProperties.get(property, UNKNOWN); + } + /** + * Return attestation specific proerties. + * @param property model, name, brand, device or manufacturer. + * @return property value or UNKNOWN + */ + private static String getVendorDeviceIdProperty(String property) { + String attestProp = getString( + TextUtils.formatSimple("ro.product.%s_for_attestation", property)); + return attestProp.equals(UNKNOWN) + ? getString(TextUtils.formatSimple("ro.product.vendor.%s", property)) : attestProp; + } + + private static String[] getStringList(String property, String separator) { + String value = SystemProperties.get(property); + if (value.isEmpty()) { + return new String[0]; + } else { + return value.split(separator); + } + } + + @UnsupportedAppUsage + private static long getLong(String property) { + try { + return Long.parseLong(SystemProperties.get(property)); + } catch (NumberFormatException e) { + return -1; + } + } + + private static String joinListOrElse(List list, String defaultValue) { + String ret = list.stream().map(elem -> elem == null ? "" : elem.toString()) + .collect(Collectors.joining(",")); + return ret.isEmpty() ? defaultValue : ret; + } +} diff --git a/aosp/frameworks/base/core/java/android/os/pushserver/IRTCPushServer.aidl b/aosp/frameworks/base/core/java/android/os/pushserver/IRTCPushServer.aidl new file mode 100755 index 0000000000000000000000000000000000000000..e573fef0a4db873626c4f2e37aade08b827a3847 --- /dev/null +++ b/aosp/frameworks/base/core/java/android/os/pushserver/IRTCPushServer.aidl @@ -0,0 +1,21 @@ +// IRTCPushServer.aidl +package android.os.pushserver; + +// Declare any non-default types here with import statements +/** + * @hide + */ +interface IRTCPushServer { + /** + * @hide + */ + void setPushParams(@nullable String roomId,@nullable String uId,@nullable String signalurl);//设置推流服务的RoomId 及 用户ID + /** + * @hide + */ + boolean isInPublish(@nullable String roomId);//给定是否在推流,可以传递指定房间ID,默认为空,则判断所有房间 + /** + * @hide + */ + void leaveRoom(@nullable String roomId); //离开房间,并停止推流 +} \ No newline at end of file diff --git a/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java b/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java new file mode 100755 index 0000000000000000000000000000000000000000..d3aef980637e36ead45c4e993e6c6dfffb3264a8 --- /dev/null +++ b/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -0,0 +1,872 @@ +package com.android.settingslib; + +import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL; +import static android.webkit.Flags.updateServiceV2; + +import android.annotation.ColorInt; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.content.pm.UserInfo; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.drawable.Drawable; +import android.hardware.usb.UsbManager; +import android.hardware.usb.UsbPort; +import android.hardware.usb.UsbPortStatus; +import android.hardware.usb.flags.Flags; +import android.location.LocationManager; +import android.media.AudioManager; +import android.net.NetworkCapabilities; +import android.net.TetheringManager; +import android.net.vcn.VcnTransportInfo; +import android.net.wifi.WifiInfo; +import android.os.BatteryManager; +import android.os.Build; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; +import android.print.PrintManager; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.telephony.AccessNetworkConstants; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.webkit.IWebViewUpdateService; +import android.webkit.WebViewFactory; +import android.webkit.WebViewProviderInfo; +import android.webkit.WebViewUpdateManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.UserIcons; +import com.android.launcher3.icons.BaseIconFactory.IconOptions; +import com.android.launcher3.icons.IconFactory; +import com.android.launcher3.util.UserIconInfo; +import com.android.settingslib.drawable.UserIconDrawable; +import com.android.settingslib.fuelgauge.BatteryStatus; +import com.android.settingslib.utils.BuildCompatUtils; + +import java.text.NumberFormat; +import java.time.Duration; +import java.util.List; + +public class Utils { + + private static final String TAG = "Utils"; + + public static final String INCOMPATIBLE_CHARGER_WARNING_DISABLED = + "incompatible_charger_warning_disabled"; + public static final String WIRELESS_CHARGING_NOTIFICATION_TIMESTAMP = + "wireless_charging_notification_timestamp"; + + @VisibleForTesting + static final String STORAGE_MANAGER_ENABLED_PROPERTY = "ro.storage_manager.enabled"; + + @VisibleForTesting static final long WIRELESS_CHARGING_DEFAULT_TIMESTAMP = -1L; + + @VisibleForTesting + static final long WIRELESS_CHARGING_NOTIFICATION_THRESHOLD_MILLIS = + Duration.ofDays(30).toMillis(); + + @VisibleForTesting + static final String WIRELESS_CHARGING_WARNING_ENABLED = "wireless_charging_warning_enabled"; + + private static Signature[] sSystemSignature; + private static String sPermissionControllerPackageName; + private static String sServicesSystemSharedLibPackageName; + private static String sSharedSystemSharedLibPackageName; + private static String sDefaultWebViewPackageName; + + static final int[] WIFI_PIE = { + com.android.internal.R.drawable.ic_wifi_signal_0, + com.android.internal.R.drawable.ic_wifi_signal_1, + com.android.internal.R.drawable.ic_wifi_signal_2, + com.android.internal.R.drawable.ic_wifi_signal_3, + com.android.internal.R.drawable.ic_wifi_signal_4 + }; + + static final int[] SHOW_X_WIFI_PIE = { + R.drawable.ic_show_x_wifi_signal_0, + R.drawable.ic_show_x_wifi_signal_1, + R.drawable.ic_show_x_wifi_signal_2, + R.drawable.ic_show_x_wifi_signal_3, + R.drawable.ic_show_x_wifi_signal_4 + }; + + /** Update the location enable state. */ + public static void updateLocationEnabled( + @NonNull Context context, boolean enabled, int userId, int source) { + Settings.Secure.putIntForUser( + context.getContentResolver(), Settings.Secure.LOCATION_CHANGER, source, userId); + + LocationManager locationManager = context.getSystemService(LocationManager.class); + locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId)); + } + + /** + * Return string resource that best describes combination of tethering options available on this + * device. + */ + public static int getTetheringLabel(TetheringManager tm) { + String[] usbRegexs = tm.getTetherableUsbRegexs(); + String[] wifiRegexs = tm.getTetherableWifiRegexs(); + String[] bluetoothRegexs = tm.getTetherableBluetoothRegexs(); + + boolean usbAvailable = usbRegexs.length != 0; + boolean wifiAvailable = wifiRegexs.length != 0; + boolean bluetoothAvailable = bluetoothRegexs.length != 0; + + if (wifiAvailable && usbAvailable && bluetoothAvailable) { + return R.string.tether_settings_title_all; + } else if (wifiAvailable && usbAvailable) { + return R.string.tether_settings_title_all; + } else if (wifiAvailable && bluetoothAvailable) { + return R.string.tether_settings_title_all; + } else if (wifiAvailable) { + return R.string.tether_settings_title_wifi; + } else if (usbAvailable && bluetoothAvailable) { + return R.string.tether_settings_title_usb_bluetooth; + } else if (usbAvailable) { + return R.string.tether_settings_title_usb; + } else { + return R.string.tether_settings_title_bluetooth; + } + } + + /** Returns a label for the user, in the form of "User: user name" or "Work profile". */ + public static String getUserLabel(Context context, UserInfo info) { + String name = info != null ? info.name : null; + if (info.isManagedProfile()) { + // We use predefined values for managed profiles + return BuildCompatUtils.isAtLeastT() + ? getUpdatableManagedUserTitle(context) + : context.getString(R.string.managed_user_title); + } else if (info.isGuest()) { + name = context.getString(com.android.internal.R.string.guest_name); + } + if (name == null && info != null) { + name = Integer.toString(info.id); + } else if (info == null) { + name = context.getString(R.string.unknown); + } + return context.getResources().getString(R.string.running_process_item_user_label, name); + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private static String getUpdatableManagedUserTitle(Context context) { + return context.getSystemService(DevicePolicyManager.class) + .getResources() + .getString( + WORK_PROFILE_USER_LABEL, + () -> context.getString(R.string.managed_user_title)); + } + + /** Returns a circular icon for a user. */ + public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) { + final int iconSize = UserIconDrawable.getDefaultSize(context); + if (user.isManagedProfile()) { + Drawable drawable = UserIconDrawable.getManagedUserDrawable(context); + drawable.setBounds(0, 0, iconSize, iconSize); + return drawable; + } + if (user.iconPath != null) { + Bitmap icon = um.getUserIcon(user.id); + if (icon != null) { + return new UserIconDrawable(iconSize).setIcon(icon).bake(); + } + } + return new UserIconDrawable(iconSize) + .setIconDrawable( + UserIcons.getDefaultUserIcon( + context.getResources(), user.id, /* light= */ false)) + .bake(); + } + + /** Formats a double from 0.0..100.0 with an option to round */ + public static String formatPercentage(double percentage, boolean round) { + final int localPercentage = round ? Math.round((float) percentage) : (int) percentage; + return formatPercentage(localPercentage); + } + + /** Formats the ratio of amount/total as a percentage. */ + public static String formatPercentage(long amount, long total) { + return formatPercentage(((double) amount) / total); + } + + /** Formats an integer from 0..100 as a percentage. */ + public static String formatPercentage(int percentage) { + return formatPercentage(((double) percentage) / 100.0); + } + + /** Formats a double from 0.0..1.0 as a percentage. */ + public static String formatPercentage(double percentage) { + return NumberFormat.getPercentInstance().format(percentage); + } + + public static int getBatteryLevel(Intent batteryChangedIntent) { + int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); + int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); + return (level * 100) / scale; + } + + /** + * Get battery status string + * + * @param context the context + * @param batteryChangedIntent battery broadcast intent received from {@link + * Intent.ACTION_BATTERY_CHANGED}. + * @param compactStatus to present compact battery charging string if {@code true} + * @return battery status string + */ + @NonNull + public static String getBatteryStatus( + @NonNull Context context, @NonNull Intent batteryChangedIntent, boolean compactStatus) { + final int status = + batteryChangedIntent.getIntExtra( + BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); + final Resources res = context.getResources(); + + String statusString = res.getString(R.string.battery_info_status_unknown); + final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent); + + if (batteryStatus.isCharged()) { + statusString = + res.getString( + compactStatus + ? R.string.battery_info_status_full_charged + : R.string.battery_info_status_full); + } else { + if (status == BatteryManager.BATTERY_STATUS_CHARGING) { + if (compactStatus) { + statusString = res.getString(R.string.battery_info_status_charging); + } else if (batteryStatus.isPluggedInWired()) { + switch (batteryStatus.getChargingSpeed(context)) { + case BatteryStatus.CHARGING_FAST: + statusString = + res.getString(R.string.battery_info_status_charging_fast); + break; + case BatteryStatus.CHARGING_SLOWLY: + statusString = + res.getString(R.string.battery_info_status_charging_slow); + break; + default: + statusString = res.getString(R.string.battery_info_status_charging); + break; + } + } else if (batteryStatus.isPluggedInDock()) { + statusString = res.getString(R.string.battery_info_status_charging_dock); + } else { + statusString = res.getString(R.string.battery_info_status_charging_wireless); + } + } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) { + statusString = res.getString(R.string.battery_info_status_discharging); + } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { + statusString = res.getString(R.string.battery_info_status_not_charging); + } + } + + return statusString; + } + + public static ColorStateList getColorAccent(Context context) { + return getColorAttr(context, android.R.attr.colorAccent); + } + + public static ColorStateList getColorError(Context context) { + return getColorAttr(context, android.R.attr.colorError); + } + + @ColorInt + public static int getColorAccentDefaultColor(Context context) { + return getColorAttrDefaultColor(context, android.R.attr.colorAccent); + } + + @ColorInt + public static int getColorErrorDefaultColor(Context context) { + return getColorAttrDefaultColor(context, android.R.attr.colorError); + } + + @ColorInt + public static int getColorStateListDefaultColor(Context context, int resId) { + final ColorStateList list = + context.getResources().getColorStateList(resId, context.getTheme()); + return list.getDefaultColor(); + } + + /** + * This method computes disabled color from normal color + * + * @param context the context + * @param inputColor normal color. + * @return disabled color. + */ + @ColorInt + public static int getDisabled(Context context, int inputColor) { + return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); + } + + @ColorInt + public static int applyAlphaAttr(Context context, int attr, int inputColor) { + TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); + float alpha = ta.getFloat(0, 0); + ta.recycle(); + return applyAlpha(alpha, inputColor); + } + + @ColorInt + public static int applyAlpha(float alpha, int inputColor) { + alpha *= Color.alpha(inputColor); + return Color.argb( + (int) (alpha), + Color.red(inputColor), + Color.green(inputColor), + Color.blue(inputColor)); + } + + @ColorInt + public static int getColorAttrDefaultColor(Context context, int attr) { + return getColorAttrDefaultColor(context, attr, 0); + } + + /** Get color styled attribute {@code attr}, default to {@code defValue} if not found. */ + @ColorInt + public static int getColorAttrDefaultColor(Context context, int attr, @ColorInt int defValue) { + TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); + @ColorInt int colorAccent = ta.getColor(0, defValue); + ta.recycle(); + return colorAccent; + } + + public static ColorStateList getColorAttr(Context context, int attr) { + TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); + ColorStateList stateList = null; + try { + stateList = ta.getColorStateList(0); + } finally { + ta.recycle(); + } + return stateList; + } + + public static int getThemeAttr(Context context, int attr) { + return getThemeAttr(context, attr, 0); + } + + public static int getThemeAttr(Context context, int attr, int defaultValue) { + TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); + int theme = ta.getResourceId(0, defaultValue); + ta.recycle(); + return theme; + } + + public static Drawable getDrawable(Context context, int attr) { + TypedArray ta = context.obtainStyledAttributes(new int[] {attr}); + Drawable drawable = ta.getDrawable(0); + ta.recycle(); + return drawable; + } + + /** + * Create a color matrix suitable for a ColorMatrixColorFilter that modifies only the color but + * preserves the alpha for a given drawable + * + * @return a color matrix that uses the source alpha and given color + */ + public static ColorMatrix getAlphaInvariantColorMatrixForColor(@ColorInt int color) { + int r = Color.red(color); + int g = Color.green(color); + int b = Color.blue(color); + + ColorMatrix cm = + new ColorMatrix( + new float[] { + 0, 0, 0, 0, r, + 0, 0, 0, 0, g, + 0, 0, 0, 0, b, + 0, 0, 0, 1, 0 + }); + + return cm; + } + + /** + * Create a ColorMatrixColorFilter to tint a drawable but retain its alpha characteristics + * + * @return a ColorMatrixColorFilter which changes the color of the output but is invariant on + * the source alpha + */ + public static ColorFilter getAlphaInvariantColorFilterForColor(@ColorInt int color) { + return new ColorMatrixColorFilter(getAlphaInvariantColorMatrixForColor(color)); + } + + /** + * Determine whether a package is a "system package", in which case certain things (like + * disabling notifications or disabling the package altogether) should be disallowed. + * + *

Note: This function is just for UI treatment, and should not be used for security + * purposes. + * + * @deprecated Use {@link ApplicationInfo#isSignedWithPlatformKey()} and {@link + * #isEssentialPackage} instead. + */ + @Deprecated + public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) { + if (sSystemSignature == null) { + sSystemSignature = new Signature[] {getSystemSignature(pm)}; + } + return (sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg))) + || isEssentialPackage(resources, pm, pkg.packageName); + } + + private static Signature getFirstSignature(PackageInfo pkg) { + if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { + return pkg.signatures[0]; + } + return null; + } + + private static Signature getSystemSignature(PackageManager pm) { + try { + final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); + return getFirstSignature(sys); + } catch (NameNotFoundException e) { + } + return null; + } + + /** + * Determine whether a package is a "essential package". + * + *

In which case certain things (like disabling the package) should be disallowed. + */ + public static boolean isEssentialPackage( + Resources resources, PackageManager pm, String packageName) { + if (sPermissionControllerPackageName == null) { + sPermissionControllerPackageName = pm.getPermissionControllerPackageName(); + } + if (sServicesSystemSharedLibPackageName == null) { + sServicesSystemSharedLibPackageName = pm.getServicesSystemSharedLibraryPackageName(); + } + if (sSharedSystemSharedLibPackageName == null) { + sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName(); + } + return packageName.equals(sPermissionControllerPackageName) + || packageName.equals(sServicesSystemSharedLibPackageName) + || packageName.equals(sSharedSystemSharedLibPackageName) + || packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME) + || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName())) + || isDeviceProvisioningPackage(resources, packageName); + } + + /** + * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, + * returns {@code false}. + */ + public static boolean isDeviceProvisioningPackage(Resources resources, String packageName) { + String deviceProvisioningPackage = + resources.getString(com.android.internal.R.string.config_deviceProvisioningPackage); + return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName); + } + + /** Fetch the package name of the default WebView provider. */ + @Nullable + private static String getDefaultWebViewPackageName() { + if (sDefaultWebViewPackageName != null) { + return sDefaultWebViewPackageName; + } + + WebViewProviderInfo provider = null; + + if (android.webkit.Flags.updateServiceIpcWrapper()) { + WebViewUpdateManager manager = WebViewUpdateManager.getInstance(); + if (manager != null) { + provider = manager.getDefaultWebViewPackage(); + } + } else { + try { + IWebViewUpdateService service = WebViewFactory.getUpdateService(); + if (service != null) { + provider = service.getDefaultWebViewPackage(); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException when trying to fetch default WebView package Name", e); + } + } + + if (provider != null) { + sDefaultWebViewPackageName = provider.packageName; + } + return sDefaultWebViewPackageName; + } + + /** + * Returns the Wifi icon resource for a given RSSI level. + * + * @param level The number of bars to show (0-4) + * @throws IllegalArgumentException if an invalid RSSI level is given. + */ + public static int getWifiIconResource(int level) { + return getWifiIconResource(false /* showX */, level); + } + + /** + * Returns the Wifi icon resource for a given RSSI level. + * + * @param showX True if a connected Wi-Fi network has the problem which should show Pie+x signal + * icon to users. + * @param level The number of bars to show (0-4) + * @throws IllegalArgumentException if an invalid RSSI level is given. + */ + public static int getWifiIconResource(boolean showX, int level) { + if (level < 0 || level >= WIFI_PIE.length) { + throw new IllegalArgumentException("No Wifi icon found for level: " + level); + } + return showX ? SHOW_X_WIFI_PIE[level] : WIFI_PIE[level]; + } + + public static int getDefaultStorageManagerDaysToRetain(Resources resources) { + int defaultDays = Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT; + try { + defaultDays = + resources.getInteger( + com.android.internal.R.integer + .config_storageManagerDaystoRetainDefault); + } catch (Resources.NotFoundException e) { + // We are likely in a test environment. + } + return defaultDays; + } + + public static boolean isWifiOnly(Context context) { + return !context.getSystemService(TelephonyManager.class).isDataCapable(); + } + + /** Returns if the automatic storage management feature is turned on or not. */ + public static boolean isStorageManagerEnabled(Context context) { + boolean isDefaultOn; + try { + isDefaultOn = SystemProperties.getBoolean(STORAGE_MANAGER_ENABLED_PROPERTY, false); + } catch (Resources.NotFoundException e) { + isDefaultOn = false; + } + return Settings.Secure.getInt( + context.getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, + isDefaultOn ? 1 : 0) + != 0; + } + + /** get that {@link AudioManager#getMode()} is in ringing/call/communication(VoIP) status. */ + public static boolean isAudioModeOngoingCall(Context context) { + final AudioManager audioManager = context.getSystemService(AudioManager.class); + final int audioMode = audioManager.getMode(); + return audioMode == AudioManager.MODE_RINGTONE + || audioMode == AudioManager.MODE_IN_CALL + || audioMode == AudioManager.MODE_IN_COMMUNICATION; + } + + /** + * Return the service state is in-service or not. To make behavior consistent with SystemUI and + * Settings/AboutPhone/SIM status UI + * + * @param serviceState Service state. {@link ServiceState} + */ + public static boolean isInService(ServiceState serviceState) { + //ccynice for sim + return true; + /* + if (serviceState == null) { + return false; + } + int state = getCombinedServiceState(serviceState); + if (state == ServiceState.STATE_POWER_OFF + || state == ServiceState.STATE_OUT_OF_SERVICE + || state == ServiceState.STATE_EMERGENCY_ONLY) { + return false; + } else { + return true; + }*/ + } + + /** + * Return the combined service state. To make behavior consistent with SystemUI and + * Settings/AboutPhone/SIM status UI. + * + *

This method returns a single service state int if either the voice reg state is {@link + * ServiceState#STATE_IN_SERVICE} or if data network is registered via a WWAN transport type. We + * consider the combined service state of an IWLAN network to be OOS. + * + * @param serviceState Service state. {@link ServiceState} + */ + public static int getCombinedServiceState(ServiceState serviceState) { + if (serviceState == null) { + return ServiceState.STATE_OUT_OF_SERVICE; + } + + final int voiceRegState = serviceState.getVoiceRegState(); + + // Consider a mobile connection to be "in service" if either voice is IN_SERVICE + // or the data registration reports IN_SERVICE on a transport type of WWAN. This + // effectively excludes the IWLAN condition. IWLAN connections imply service via + // Wi-Fi rather than cellular, and so we do not consider these transports when + // determining if cellular is "in service". + + if (voiceRegState == ServiceState.STATE_OUT_OF_SERVICE + || voiceRegState == ServiceState.STATE_EMERGENCY_ONLY) { + if (isDataRegInWwanAndInService(serviceState)) { + return ServiceState.STATE_IN_SERVICE; + } + } + + return voiceRegState; + } + + // ServiceState#mDataRegState can be set to IN_SERVICE if the network is registered + // on either a WLAN or WWAN network. Since we want to exclude the WLAN network, we can + // query the WWAN network directly and check for its registration state + private static boolean isDataRegInWwanAndInService(ServiceState serviceState) { + final NetworkRegistrationInfo networkRegWwan = + serviceState.getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + + if (networkRegWwan == null) { + return false; + } + + return networkRegWwan.isInService(); + } + + /** Get the corresponding adaptive icon drawable. */ + public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) { + int userType = UserIconInfo.TYPE_MAIN; + try { + UserInfo ui = + context.getSystemService(UserManager.class).getUserInfo(user.getIdentifier()); + if (ui != null) { + if (ui.isCloneProfile()) { + userType = UserIconInfo.TYPE_CLONED; + } else if (ui.isManagedProfile()) { + userType = UserIconInfo.TYPE_WORK; + } else if (ui.isPrivateProfile()) { + userType = UserIconInfo.TYPE_PRIVATE; + } + } + } catch (Exception e) { + // Ignore + } + try (IconFactory iconFactory = IconFactory.obtain(context)) { + return iconFactory + .createBadgedIconBitmap( + icon, new IconOptions().setUser(new UserIconInfo(user, userType))) + .newIcon(context); + } + } + + /** Get the {@link Drawable} that represents the app icon */ + public static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) { + return getBadgedIcon( + context, + appInfo.loadUnbadgedIcon(context.getPackageManager()), + UserHandle.getUserHandleForUid(appInfo.uid)); + } + + /** + * Returns a bitmap with rounded corner. + * + * @param context application context. + * @param source bitmap to apply round corner. + * @param cornerRadius corner radius value. + */ + @NonNull + public static Bitmap convertCornerRadiusBitmap( + @NonNull Context context, @NonNull Bitmap source, @NonNull float cornerRadius) { + final Bitmap roundedBitmap = + Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); + final RoundedBitmapDrawable drawable = + RoundedBitmapDrawableFactory.create(context.getResources(), source); + drawable.setAntiAlias(true); + drawable.setCornerRadius(cornerRadius); + final Canvas canvas = new Canvas(roundedBitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return roundedBitmap; + } + + /** + * Returns the WifiInfo for the underlying WiFi network of the VCN network, returns null if the + * input NetworkCapabilities is not for a VCN network with underlying WiFi network. + * + * @param networkCapabilities NetworkCapabilities of the network. + */ + @Nullable + public static WifiInfo tryGetWifiInfoForVcn(NetworkCapabilities networkCapabilities) { + if (networkCapabilities.getTransportInfo() == null + || !(networkCapabilities.getTransportInfo() instanceof VcnTransportInfo)) { + return null; + } + VcnTransportInfo vcnTransportInfo = + (VcnTransportInfo) networkCapabilities.getTransportInfo(); + return vcnTransportInfo.getWifiInfo(); + } + + /** Whether there is any incompatible chargers in the current UsbPort? */ + public static boolean containsIncompatibleChargers(Context context, String tag) { + // Avoid the caller doesn't have permission to read the "Settings.Secure" data. + try { + // Whether the incompatible charger warning is disabled or not + if (Settings.Secure.getInt( + context.getContentResolver(), INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0) + == 1) { + Log.d(tag, "containsIncompatibleChargers: disabled"); + return false; + } + } catch (Exception e) { + Log.e(tag, "containsIncompatibleChargers()", e); + return false; + } + + final List usbPortList = context.getSystemService(UsbManager.class).getPorts(); + if (usbPortList == null || usbPortList.isEmpty()) { + return false; + } + for (UsbPort usbPort : usbPortList) { + Log.d(tag, "usbPort: " + usbPort); + if (!usbPort.supportsComplianceWarnings()) { + continue; + } + final UsbPortStatus usbStatus = usbPort.getStatus(); + if (usbStatus == null || !usbStatus.isConnected()) { + continue; + } + final int[] complianceWarnings = usbStatus.getComplianceWarnings(); + if (complianceWarnings == null || complianceWarnings.length == 0) { + continue; + } + for (int complianceWarningType : complianceWarnings) { + if (Flags.enableUsbDataComplianceWarning() + && Flags.enableInputPowerLimitedWarning()) { + switch (complianceWarningType) { + case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED: + case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: + return true; + default: + break; + } + } else { + switch (complianceWarningType) { + case UsbPortStatus.COMPLIANCE_WARNING_OTHER: + case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: + return true; + default: + break; + } + } + } + } + return false; + } + + /** Whether to show the wireless charging notification. */ + public static boolean shouldShowWirelessChargingNotification( + @NonNull Context context, @NonNull String tag) { + try { + return shouldShowWirelessChargingNotificationInternal(context, tag); + } catch (Exception e) { + Log.e(tag, "shouldShowWirelessChargingNotification()", e); + return false; + } + } + + /** Stores the timestamp of the wireless charging notification. */ + public static void updateWirelessChargingNotificationTimestamp( + @NonNull Context context, long timestamp, @NonNull String tag) { + try { + Secure.putLong( + context.getContentResolver(), + WIRELESS_CHARGING_NOTIFICATION_TIMESTAMP, + timestamp); + } catch (Exception e) { + Log.e(tag, "setWirelessChargingNotificationTimestamp()", e); + } + } + + /** Whether to show the wireless charging warning in Settings. */ + public static boolean shouldShowWirelessChargingWarningTip( + @NonNull Context context, @NonNull String tag) { + try { + return Secure.getInt(context.getContentResolver(), WIRELESS_CHARGING_WARNING_ENABLED, 0) + == 1; + } catch (Exception e) { + Log.e(tag, "shouldShowWirelessChargingWarningTip()", e); + } + return false; + } + + /** Stores the state of whether the wireless charging warning in Settings is enabled. */ + public static void updateWirelessChargingWarningEnabled( + @NonNull Context context, boolean enabled, @NonNull String tag) { + try { + Secure.putInt( + context.getContentResolver(), + WIRELESS_CHARGING_WARNING_ENABLED, + enabled ? 1 : 0); + } catch (Exception e) { + Log.e(tag, "setWirelessChargingWarningEnabled()", e); + } + } + + private static boolean shouldShowWirelessChargingNotificationInternal( + @NonNull Context context, @NonNull String tag) { + final long lastNotificationTimeMillis = + Secure.getLong( + context.getContentResolver(), + WIRELESS_CHARGING_NOTIFICATION_TIMESTAMP, + WIRELESS_CHARGING_DEFAULT_TIMESTAMP); + if (isWirelessChargingNotificationDisabled(lastNotificationTimeMillis)) { + return false; + } + if (isInitialWirelessChargingNotification(lastNotificationTimeMillis)) { + updateWirelessChargingNotificationTimestamp(context, System.currentTimeMillis(), tag); + updateWirelessChargingWarningEnabled(context, /* enabled= */ true, tag); + return true; + } + final long durationMillis = System.currentTimeMillis() - lastNotificationTimeMillis; + final boolean show = durationMillis > WIRELESS_CHARGING_NOTIFICATION_THRESHOLD_MILLIS; + Log.d(tag, "shouldShowWirelessChargingNotification = " + show); + if (show) { + updateWirelessChargingNotificationTimestamp(context, System.currentTimeMillis(), tag); + updateWirelessChargingWarningEnabled(context, /* enabled= */ true, tag); + } + return show; + } + + private static boolean isWirelessChargingNotificationDisabled(long lastNotificationTimeMillis) { + return lastNotificationTimeMillis == Long.MIN_VALUE; + } + + private static boolean isInitialWirelessChargingNotification(long lastNotificationTimeMillis) { + return lastNotificationTimeMillis == WIRELESS_CHARGING_DEFAULT_TIMESTAMP; + } +} diff --git a/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java new file mode 100755 index 0000000000000000000000000000000000000000..12a42bf8b6bf08a34d797b4535bfeae5686bc070 --- /dev/null +++ b/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.settingslib.graph; + +import static com.android.settingslib.flags.Flags.newStatusBarIcons; + +import android.animation.ArgbEvaluator; +import android.annotation.IntRange; +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Path.Direction; +import android.graphics.Path.FillType; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.DrawableWrapper; +import android.os.Handler; +import android.telephony.CellSignalStrength; +import android.util.LayoutDirection; +import android.util.PathParser; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.R; +import com.android.settingslib.Utils; +import android.os.SystemProperties; + +/** + * Drawable displaying a mobile cell signal indicator. + */ +public class SignalDrawable extends DrawableWrapper { + + private static final String TAG = "SignalDrawable"; + + private static final int NUM_DOTS = 3; + + private static final float VIEWPORT = 24f; + private static final float PAD = 2f / VIEWPORT; + + private static final float DOT_SIZE = 3f / VIEWPORT; + private static final float DOT_PADDING = 1.5f / VIEWPORT; + + // All of these are masks to push all of the drawable state into one int for easy callbacks + // and flow through sysui. + private static final int LEVEL_MASK = 0xff; + private static final int NUM_LEVEL_SHIFT = 8; + private static final int NUM_LEVEL_MASK = 0xff << NUM_LEVEL_SHIFT; + private static final int STATE_SHIFT = 16; + private static final int STATE_MASK = 0xff << STATE_SHIFT; + private static final int STATE_CUT = 2; + private static final int STATE_CARRIER_CHANGE = 3; + + private static final long DOT_DELAY = 1000; + + // Check the config for which icon we want to use + private static final int ICON_RES = SignalDrawable.getIconRes(); + + private final Paint mForegroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint mTransparentPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final int mDarkModeFillColor; + private final int mLightModeFillColor; + private final Path mCutoutPath = new Path(); + private final Path mForegroundPath = new Path(); + private final Path mAttributionPath = new Path(); + private final Matrix mAttributionScaleMatrix = new Matrix(); + private final Path mScaledAttributionPath = new Path(); + private final Handler mHandler; + private final float mCutoutWidthFraction; + private final float mCutoutHeightFraction; + private float mDarkIntensity = -1; + private final int mIntrinsicSize; + private boolean mAnimating; + private int mCurrentDot; + + public SignalDrawable(Context context) { + super(context.getDrawable(ICON_RES)); + final String attributionPathString = context.getString( + com.android.internal.R.string.config_signalAttributionPath); + mAttributionPath.set(PathParser.createPathFromPathData(attributionPathString)); + updateScaledAttributionPath(); + mCutoutWidthFraction = context.getResources().getFloat( + com.android.internal.R.dimen.config_signalCutoutWidthFraction); + mCutoutHeightFraction = context.getResources().getFloat( + com.android.internal.R.dimen.config_signalCutoutHeightFraction); + mDarkModeFillColor = Utils.getColorStateListDefaultColor(context, + R.color.dark_mode_icon_color_single_tone); + mLightModeFillColor = Utils.getColorStateListDefaultColor(context, + R.color.light_mode_icon_color_single_tone); + mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size); + mTransparentPaint.setColor(context.getColor(android.R.color.transparent)); + mTransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + mHandler = new Handler(); + setDarkIntensity(0); + } + + private void updateScaledAttributionPath() { + if (getBounds().isEmpty()) { + mAttributionScaleMatrix.setScale(1f, 1f); + } else { + mAttributionScaleMatrix.setScale( + getBounds().width() / VIEWPORT, getBounds().height() / VIEWPORT); + } + mAttributionPath.transform(mAttributionScaleMatrix, mScaledAttributionPath); + } + + @Override + public int getIntrinsicWidth() { + return mIntrinsicSize; + } + + @Override + public int getIntrinsicHeight() { + return mIntrinsicSize; + } + + private void updateAnimation() { + boolean shouldAnimate = isInState(STATE_CARRIER_CHANGE) && isVisible(); + if (shouldAnimate == mAnimating) return; + mAnimating = shouldAnimate; + if (shouldAnimate) { + mChangeDot.run(); + } else { + mHandler.removeCallbacks(mChangeDot); + } + } + + @Override + protected boolean onLevelChange(int packedState) { + super.onLevelChange(unpackLevel(packedState)); + updateAnimation(); + setTintList(ColorStateList.valueOf(mForegroundPaint.getColor())); + invalidateSelf(); + return true; + } + + private int unpackLevel(int packedState) { + int numBins = (packedState & NUM_LEVEL_MASK) >> NUM_LEVEL_SHIFT; + int cutOutOffset = 0; + int levelOffset = numBins == (CellSignalStrength.getNumSignalStrengthLevels() + 1) ? 10 : 0; + int level = (packedState & LEVEL_MASK); + + if (newStatusBarIcons()) { + if (isInState(STATE_CUT)) { + cutOutOffset = 20; + } + } + + return level + levelOffset + cutOutOffset; + } + + public void setDarkIntensity(float darkIntensity) { + if (darkIntensity == mDarkIntensity) { + return; + } + setTintList(ColorStateList.valueOf(getFillColor(darkIntensity))); + } + + @Override + public void setTintList(ColorStateList tint) { + super.setTintList(tint); + int colorForeground = mForegroundPaint.getColor(); + mForegroundPaint.setColor(tint.getDefaultColor()); + if (colorForeground != mForegroundPaint.getColor()) invalidateSelf(); + } + + private int getFillColor(float darkIntensity) { + return getColorForDarkIntensity( + darkIntensity, mLightModeFillColor, mDarkModeFillColor); + } + + private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) { + return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + updateScaledAttributionPath(); + invalidateSelf(); + } + + @Override + public void draw(@NonNull Canvas canvas) { + canvas.saveLayer(null, null); + final float width = getBounds().width(); + final float height = getBounds().height(); + + boolean isRtl = getLayoutDirection() == LayoutDirection.RTL; + if (isRtl) { + canvas.save(); + // Mirror the drawable + canvas.translate(width, 0); + canvas.scale(-1.0f, 1.0f); + } + super.draw(canvas); + mCutoutPath.reset(); + mCutoutPath.setFillType(FillType.WINDING); + + final float padding = Math.round(PAD * width); + + if (isInState(STATE_CARRIER_CHANGE)) { + float dotSize = (DOT_SIZE * height); + float dotPadding = (DOT_PADDING * height); + float dotSpacing = dotPadding + dotSize; + float x = width - padding - dotSize; + float y = height - padding - dotSize; + mForegroundPath.reset(); + drawDotAndPadding(x, y, dotPadding, dotSize, 2); + drawDotAndPadding(x - dotSpacing, y, dotPadding, dotSize, 1); + drawDotAndPadding(x - dotSpacing * 2, y, dotPadding, dotSize, 0); + canvas.drawPath(mCutoutPath, mTransparentPaint); + canvas.drawPath(mForegroundPath, mForegroundPaint); + } else if (!newStatusBarIcons() && isInState(STATE_CUT)) { + float cutX = (mCutoutWidthFraction * width / VIEWPORT); + float cutY = (mCutoutHeightFraction * height / VIEWPORT); + mCutoutPath.moveTo(width, height); + mCutoutPath.rLineTo(-cutX, 0); + mCutoutPath.rLineTo(0, -cutY); + mCutoutPath.rLineTo(cutX, 0); + mCutoutPath.rLineTo(0, cutY); + canvas.drawPath(mCutoutPath, mTransparentPaint); + canvas.drawPath(mScaledAttributionPath, mForegroundPaint); + } + if (isRtl) { + canvas.restore(); + } + canvas.restore(); + } + + private void drawDotAndPadding(float x, float y, + float dotPadding, float dotSize, int i) { + if (i == mCurrentDot) { + // Draw dot + mForegroundPath.addRect(x, y, x + dotSize, y + dotSize, Direction.CW); + // Draw dot padding + mCutoutPath.addRect(x - dotPadding, y - dotPadding, x + dotSize + dotPadding, + y + dotSize + dotPadding, Direction.CW); + } + } + + @Override + public void setAlpha(@IntRange(from = 0, to = 255) int alpha) { + super.setAlpha(alpha); + mForegroundPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + super.setColorFilter(colorFilter); + mForegroundPaint.setColorFilter(colorFilter); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + updateAnimation(); + return changed; + } + + private final Runnable mChangeDot = new Runnable() { + @Override + public void run() { + if (++mCurrentDot == NUM_DOTS) { + mCurrentDot = 0; + } + invalidateSelf(); + mHandler.postDelayed(mChangeDot, DOT_DELAY); + } + }; + + /** + * Returns whether this drawable is in the specified state. + * + * @param state must be one of {@link #STATE_CARRIER_CHANGE} or {@link #STATE_CUT} + */ + private boolean isInState(int state) { + //ccynice for sim.signalLevel + return false; + //return getState(getLevel()) == state; + } + + public static int getState(int fullState) { +//ccynice for sim.signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + } + + public static int getState(int level, int numLevels, boolean cutOut) { + +//ccynice for sim.signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + } + + /** Returns the state representing empty mobile signal with the given number of levels. */ + public static int getEmptyState(int numLevels) { + return getState(0, numLevels, true); + } + + /** Returns the state representing carrier change with the given number of levels. */ + public static int getCarrierChangeState(int numLevels) { + return (STATE_CARRIER_CHANGE << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT); + } + + private static int getIconRes() { + if (newStatusBarIcons()) { + return R.drawable.ic_mobile_level_list; + } else { + return com.android.internal.R.drawable.ic_signal_cellular; + } + } +} diff --git a/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java new file mode 100755 index 0000000000000000000000000000000000000000..9d45151280b3029440416040d129fcfa6e4fc107 --- /dev/null +++ b/aosp/frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ + +package com.android.settingslib.wifi; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkKey; +import android.net.NetworkRequest; +import android.net.NetworkScoreManager; +import android.net.ScoredNetwork; +import android.net.TransportInfo; +import android.net.vcn.VcnTransportInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiNetworkScoreCache; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.provider.Settings; + +import androidx.annotation.Nullable; + +import com.android.settingslib.R; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Track status of Wi-Fi for the Sys UI. + */ +@SuppressLint("MissingPermission") +public class WifiStatusTracker { + private static final int HISTORY_SIZE = 32; + private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + private final Context mContext; + private final WifiNetworkScoreCache mWifiNetworkScoreCache; + private final WifiManager mWifiManager; + private final NetworkScoreManager mNetworkScoreManager; + private final ConnectivityManager mConnectivityManager; + private final Handler mHandler; + private final Handler mMainThreadHandler; + private final Set mNetworks = new HashSet<>(); + private int mPrimaryNetworkId; + // Save the previous HISTORY_SIZE states for logging. + private final String[] mHistory = new String[HISTORY_SIZE]; + // Where to copy the next state into. + private int mHistoryIndex; + private final WifiNetworkScoreCache.CacheListener mCacheListener; + private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_WIFI) + .addTransportType(TRANSPORT_CELLULAR) + .build(); + private final NetworkCallback mNetworkCallback = + new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) { + // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable + // and onLinkPropertiesChanged. + @Override + public void onCapabilitiesChanged( + Network network, NetworkCapabilities networkCapabilities) { + WifiInfo wifiInfo = getMainOrUnderlyingWifiInfo(networkCapabilities); + boolean isWifi = connectionIsWifi(networkCapabilities, wifiInfo); + // As long as it is a WiFi network, we will log it in the dumpsys for debugging. + if (isWifi) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("onCapabilitiesChanged: ") + .append("network=").append(network).append(",") + .append("networkCapabilities=").append(networkCapabilities) + .toString(); + recordLastWifiNetwork(log); + } + // Ignore the WiFi network if it doesn't contain any valid WifiInfo, or it is not the + // primary WiFi. + if (wifiInfo == null || !wifiInfo.isPrimary()) { + // Remove the network from the tracking list once it becomes non-primary. + if (mNetworks.contains(network.getNetId())) { + mNetworks.remove(network.getNetId()); + } + return; + } + if (!mNetworks.contains(network.getNetId())) { + mNetworks.add(network.getNetId()); + } + mPrimaryNetworkId = network.getNetId(); + updateWifiInfo(wifiInfo); + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); + } + + @Override + public void onLost(Network network) { + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("onLost: ") + .append("network=").append(network) + .toString(); + recordLastWifiNetwork(log); + if (mNetworks.contains(network.getNetId())) { + mNetworks.remove(network.getNetId()); + } + if (network.getNetId() != mPrimaryNetworkId) { + return; + } + updateWifiInfo(null); + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); + } + }; + private final NetworkCallback mDefaultNetworkCallback = + new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) { + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + // network is now the default network, and its capabilities are nc. + // This method will always be called immediately after the network becomes the + // default, in addition to any time the capabilities change while the network is + // the default. + mDefaultNetwork = network; + mDefaultNetworkCapabilities = nc; + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); + } + @Override + public void onLost(Network network) { + // The system no longer has a default network. + mDefaultNetwork = null; + mDefaultNetworkCapabilities = null; + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); + } + }; + private Network mDefaultNetwork = null; + private NetworkCapabilities mDefaultNetworkCapabilities = null; + private final Runnable mCallback; + + private WifiInfo mWifiInfo; + public boolean enabled; + public boolean isCaptivePortal; + public boolean isDefaultNetwork; + public boolean isCarrierMerged; + public int subId; + public int state; + public boolean connected; + public String ssid; + public int rssi; + public int level; + public String statusLabel; + + public WifiStatusTracker(Context context, WifiManager wifiManager, + NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager, + Runnable callback) { + this(context, wifiManager, networkScoreManager, connectivityManager, callback, null, null); + } + + public WifiStatusTracker(Context context, WifiManager wifiManager, + NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager, + Runnable callback, Handler foregroundHandler, Handler backgroundHandler) { + mContext = context; + mWifiManager = wifiManager; + mWifiNetworkScoreCache = new WifiNetworkScoreCache(context); + mNetworkScoreManager = networkScoreManager; + mConnectivityManager = connectivityManager; + mCallback = callback; + if (backgroundHandler == null) { + HandlerThread handlerThread = new HandlerThread("WifiStatusTrackerHandler"); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + } else { + mHandler = backgroundHandler; + } + mMainThreadHandler = foregroundHandler == null + ? new Handler(Looper.getMainLooper()) : foregroundHandler; + mCacheListener = + new WifiNetworkScoreCache.CacheListener(mHandler) { + @Override + public void networkCacheUpdated(List updatedNetworks) { + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); + } + }; + } + + public void setListening(boolean listening) { + if (listening) { + mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, + mWifiNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK); + mWifiNetworkScoreCache.registerListener(mCacheListener); + mConnectivityManager.registerNetworkCallback( + mNetworkRequest, mNetworkCallback, mHandler); + mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler); + } else { + mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, + mWifiNetworkScoreCache); + mWifiNetworkScoreCache.unregisterListener(); + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); + } + } + + /** + * Fetches initial state as if a WifiManager.NETWORK_STATE_CHANGED_ACTION have been received. + * This replaces the dependency on the initial sticky broadcast. + */ + public void fetchInitialState() { + if (mWifiManager == null) { + return; + } + updateWifiState(); + final NetworkInfo networkInfo = + mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + connected = networkInfo != null && networkInfo.isConnected(); + mWifiInfo = null; + ssid = null; + if (connected) { + mWifiInfo = mWifiManager.getConnectionInfo(); + if (mWifiInfo != null) { + if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) { + ssid = mWifiInfo.getPasspointProviderFriendlyName(); + } else { + ssid = getValidSsid(mWifiInfo); + } + isCarrierMerged = mWifiInfo.isCarrierMerged(); + subId = mWifiInfo.getSubscriptionId(); + updateRssi(mWifiInfo.getRssi()); + maybeRequestNetworkScore(); + } + } + updateStatusLabel(); + } + + public void handleBroadcast(Intent intent) { + if (mWifiManager == null) { + return; + } + String action = intent.getAction(); + if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + //ccynice + updateWifiState(); + //fetchInitialState(); + } + } + + private void updateWifiInfo(WifiInfo wifiInfo) { + updateWifiState(); + connected = wifiInfo != null; + mWifiInfo = wifiInfo; + ssid = null; + if (mWifiInfo != null) { + if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) { + ssid = mWifiInfo.getPasspointProviderFriendlyName(); + } else { + ssid = getValidSsid(mWifiInfo); + } + isCarrierMerged = mWifiInfo.isCarrierMerged(); + subId = mWifiInfo.getSubscriptionId(); + updateRssi(mWifiInfo.getRssi()); + maybeRequestNetworkScore(); + } + } + + private void updateWifiState() { + state = mWifiManager.getWifiState(); + enabled = state == WifiManager.WIFI_STATE_ENABLED; + } + + private void updateRssi(int newRssi) { + rssi = newRssi; + level = mWifiManager.calculateSignalLevel(rssi); + } + + private void maybeRequestNetworkScore() { + NetworkKey networkKey = NetworkKey.createFromWifiInfo(mWifiInfo); + if (mWifiNetworkScoreCache.getScoredNetwork(networkKey) == null) { + mNetworkScoreManager.requestScores(new NetworkKey[]{ networkKey }); + } + } + + private void updateStatusLabel() { + if (mWifiManager == null) { + return; + } + NetworkCapabilities networkCapabilities; + isDefaultNetwork = mDefaultNetworkCapabilities != null + && connectionIsWifi(mDefaultNetworkCapabilities); + if (isDefaultNetwork) { + // Wifi is connected and the default network. + networkCapabilities = mDefaultNetworkCapabilities; + } else { + networkCapabilities = mConnectivityManager.getNetworkCapabilities( + mWifiManager.getCurrentNetwork()); + } + isCaptivePortal = false; + if (networkCapabilities != null) { + if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) { + statusLabel = mContext.getString(R.string.wifi_status_sign_in_required); + isCaptivePortal = true; + return; + } else if (networkCapabilities.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { + statusLabel = mContext.getString(R.string.wifi_limited_connection); + return; + } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + final String mode = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.PRIVATE_DNS_MODE); + if (networkCapabilities.isPrivateDnsBroken()) { + statusLabel = mContext.getString(R.string.private_dns_broken); + } else { + statusLabel = mContext.getString(R.string.wifi_status_no_internet); + } + return; + } else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null + && mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + statusLabel = mContext.getString( + com.android.wifitrackerlib.R.string.wifi_connected_low_quality); + return; + } + } + + ScoredNetwork scoredNetwork = + mWifiNetworkScoreCache.getScoredNetwork(NetworkKey.createFromWifiInfo(mWifiInfo)); + statusLabel = scoredNetwork == null + ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi); + } + + @Nullable + private WifiInfo getMainOrUnderlyingWifiInfo( + @Nullable NetworkCapabilities networkCapabilities) { + if (networkCapabilities == null) { + return null; + } + + WifiInfo mainWifiInfo = getMainWifiInfo(networkCapabilities); + if (mainWifiInfo != null) { + return mainWifiInfo; + } + + // Only CELLULAR networks may have underlying wifi information that's relevant to SysUI, + // so skip the underlying network check if it's not CELLULAR. + if (!networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + return mainWifiInfo; + } + + List underlyingNetworks = networkCapabilities.getUnderlyingNetworks(); + if (underlyingNetworks == null) { + return null; + } + + // Some connections, like VPN connections, may have underlying networks that are + // eventually traced to a wifi or carrier merged connection. So, check those underlying + // networks for possible wifi information as well. See b/225902574. + for (Network underlyingNetwork : underlyingNetworks) { + NetworkCapabilities underlyingNetworkCapabilities = + mConnectivityManager.getNetworkCapabilities(underlyingNetwork); + WifiInfo underlyingWifiInfo = getMainWifiInfo(underlyingNetworkCapabilities); + if (underlyingWifiInfo != null) { + return underlyingWifiInfo; + } + } + + return null; + } + + @Nullable + private WifiInfo getMainWifiInfo(@Nullable NetworkCapabilities networkCapabilities) { + if (networkCapabilities == null) { + return null; + } + boolean canHaveWifiInfo = networkCapabilities.hasTransport(TRANSPORT_WIFI) + || networkCapabilities.hasTransport(TRANSPORT_CELLULAR); + if (!canHaveWifiInfo) { + return null; + } + + TransportInfo transportInfo = networkCapabilities.getTransportInfo(); + if (transportInfo instanceof VcnTransportInfo) { + // This VcnTransportInfo logic is copied from + // [com.android.settingslib.Utils.tryGetWifiInfoForVcn]. It's copied instead of + // re-used because it makes the logic here clearer. + return ((VcnTransportInfo) transportInfo).getWifiInfo(); + } else if (transportInfo instanceof WifiInfo) { + return (WifiInfo) transportInfo; + } else { + return null; + } + } + + private boolean connectionIsWifi(NetworkCapabilities networkCapabilities) { + return connectionIsWifi( + networkCapabilities, + getMainOrUnderlyingWifiInfo(networkCapabilities)); + } + + private boolean connectionIsWifi( + @Nullable NetworkCapabilities networkCapabilities, WifiInfo wifiInfo) { + if (networkCapabilities == null) { + return false; + } + return wifiInfo != null || networkCapabilities.hasTransport(TRANSPORT_WIFI); + } + + /** Refresh the status label on Locale changed. */ + public void refreshLocale() { + updateStatusLabel(); + mMainThreadHandler.post(() -> postResults()); + } + + private String getValidSsid(WifiInfo info) { + String ssid = info.getSSID(); + if (ssid != null && !WifiManager.UNKNOWN_SSID.equals(ssid)) { + return ssid; + } + return null; + } + + private void recordLastWifiNetwork(String log) { + mHistory[mHistoryIndex] = log; + mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; + } + + private void postResults() { + mCallback.run(); + } + + /** Dump function. */ + public void dump(PrintWriter pw) { + pw.println(" - WiFi Network History ------"); + int size = 0; + for (int i = 0; i < HISTORY_SIZE; i++) { + if (mHistory[i] != null) size++; + } + // Print out the previous states in ordered number. + for (int i = mHistoryIndex + HISTORY_SIZE - 1; + i >= mHistoryIndex + HISTORY_SIZE - size; i--) { + pw.println(" Previous WiFiNetwork(" + + (mHistoryIndex + HISTORY_SIZE - i) + "): " + + mHistory[i & (HISTORY_SIZE - 1)]); + } + } +} diff --git a/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java new file mode 100755 index 0000000000000000000000000000000000000000..c98fe9bec0023e783e2589f42d62683faa018fec --- /dev/null +++ b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -0,0 +1,1473 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.dialog; + +import static com.android.settingslib.mobile.MobileMappings.getIconKey; +import static com.android.settingslib.mobile.MobileMappings.mapIconSets; +import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource; +import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.AnyThread; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.AccessNetworkConstants; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; +import android.telephony.SignalStrength; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyDisplayInfo; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; + +import com.android.internal.logging.UiEventLogger; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.DeviceInfoUtils; +import com.android.settingslib.SignalIcon; +import com.android.settingslib.Utils; +import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.mobile.MobileMappings; +import com.android.settingslib.mobile.TelephonyIcons; +import com.android.settingslib.net.SignalStrengthUtil; +import com.android.settingslib.wifi.WifiUtils; +import com.android.settingslib.wifi.dpp.WifiDppIntentHelper; +import com.android.systemui.animation.ActivityTransitionAnimator; +import com.android.systemui.animation.DialogTransitionAnimator; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.res.R; +import com.android.systemui.statusbar.connectivity.AccessPointController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.toast.SystemUIToast; +import com.android.systemui.toast.ToastFactory; +import com.android.systemui.util.CarrierConfigTracker; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.wifitrackerlib.HotspotNetworkEntry; +import com.android.wifitrackerlib.MergedCarrierEntry; +import com.android.wifitrackerlib.WifiEntry; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +import android.os.SystemProperties; + + +import javax.inject.Inject; + +/** + * Controller for Internet Dialog. + */ +public class InternetDialogController implements AccessPointController.AccessPointCallback { + + private static final String TAG = "InternetDialogController"; + private static final String ACTION_NETWORK_PROVIDER_SETTINGS = + "android.settings.NETWORK_PROVIDER_SETTINGS"; + private static final String ACTION_WIFI_SCANNING_SETTINGS = + "android.settings.WIFI_SCANNING_SETTINGS"; + /** + * Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} + */ + private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; + /** + * When starting this activity, this extra can also be specified to supply a Bundle of arguments + * to pass to that fragment when it is instantiated during the initial creation of the activity. + */ + private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS = + ":settings:show_fragment_args"; + private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch"; + public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT); + public static final int NO_CELL_DATA_TYPE_ICON = 0; + private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off; + private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT = + R.string.tap_a_network_to_connect; + private static final int SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS = + R.string.unlock_to_view_networks; + private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS = + R.string.wifi_empty_list_wifi_on; + private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE = + R.string.non_carrier_network_unavailable; + private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE = + R.string.all_network_unavailable; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final TelephonyDisplayInfo DEFAULT_TELEPHONY_DISPLAY_INFO = + new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false); + + static final int MAX_WIFI_ENTRY_COUNT = 3; + + private final FeatureFlags mFeatureFlags; + + @VisibleForTesting + /** Should be accessible only to the main thread. */ + final Map mSubIdTelephonyDisplayInfoMap = new HashMap<>(); + @VisibleForTesting + /** Should be accessible only to the main thread. */ + final Map mSubIdTelephonyManagerMap = new HashMap<>(); + @VisibleForTesting + /** Should be accessible only to the main thread. */ + final Map mSubIdTelephonyCallbackMap = new HashMap<>(); + + private WifiManager mWifiManager; + private Context mContext; + private SubscriptionManager mSubscriptionManager; + private TelephonyManager mTelephonyManager; + private ConnectivityManager mConnectivityManager; + private CarrierConfigTracker mCarrierConfigTracker; + private Handler mHandler; + private Handler mWorkerHandler; + private MobileMappings.Config mConfig = null; + private Executor mExecutor; + private AccessPointController mAccessPointController; + private IntentFilter mConnectionStateFilter; + @VisibleForTesting + @Nullable + InternetDialogCallback mCallback; + private UiEventLogger mUiEventLogger; + private BroadcastDispatcher mBroadcastDispatcher; + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private GlobalSettings mGlobalSettings; + private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback; + private WindowManager mWindowManager; + private ToastFactory mToastFactory; + private SignalDrawable mSignalDrawable; + private SignalDrawable mSecondarySignalDrawable; // For the secondary mobile data sub in DSDS + private LocationController mLocationController; + private DialogTransitionAnimator mDialogTransitionAnimator; + private boolean mHasWifiEntries; + private WifiStateWorker mWifiStateWorker; + private boolean mHasActiveSubId; + + @VisibleForTesting + static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f; + @VisibleForTesting + static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f; + @VisibleForTesting + static final long SHORT_DURATION_TIMEOUT = 4000; + @VisibleForTesting + protected ActivityStarter mActivityStarter; + @VisibleForTesting + protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener; + @VisibleForTesting + protected WifiUtils.InternetIconInjector mWifiIconInjector; + @VisibleForTesting + protected boolean mCanConfigWifi; + @VisibleForTesting + protected KeyguardStateController mKeyguardStateController; + @VisibleForTesting + protected boolean mHasEthernet = false; + @VisibleForTesting + protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor; + @VisibleForTesting + protected boolean mCarrierNetworkChangeMode; + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onRefreshCarrierInfo() { + if (mCallback != null) { + mCallback.onRefreshCarrierInfo(); + } + } + + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + if (mCallback != null) { + mCallback.onSimStateChanged(); + } + } + }; + + protected List getSubscriptionInfo() { + return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(); + } + + @Inject + public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger, + ActivityStarter starter, AccessPointController accessPointController, + SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, + @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, + @Main Handler handler, @Main Executor mainExecutor, + BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor, + GlobalSettings globalSettings, KeyguardStateController keyguardStateController, + WindowManager windowManager, ToastFactory toastFactory, + @Background Handler workerHandler, + CarrierConfigTracker carrierConfigTracker, + LocationController locationController, + DialogTransitionAnimator dialogTransitionAnimator, + WifiStateWorker wifiStateWorker, + FeatureFlags featureFlags + ) { + if (DEBUG) { + Log.d(TAG, "Init InternetDialogController"); + } + mHandler = handler; + mWorkerHandler = workerHandler; + mExecutor = mainExecutor; + mContext = context; + mGlobalSettings = globalSettings; + mWifiManager = wifiManager; + mTelephonyManager = telephonyManager; + mConnectivityManager = connectivityManager; + mSubscriptionManager = subscriptionManager; + mCarrierConfigTracker = carrierConfigTracker; + mBroadcastDispatcher = broadcastDispatcher; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mKeyguardStateController = keyguardStateController; + mConnectionStateFilter = new IntentFilter(); + mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); + mConnectionStateFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); + mUiEventLogger = uiEventLogger; + mActivityStarter = starter; + mAccessPointController = accessPointController; + mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext); + mConnectivityManagerNetworkCallback = new DataConnectivityListener(); + mWindowManager = windowManager; + mToastFactory = toastFactory; + mSignalDrawable = new SignalDrawable(mContext); + mSecondarySignalDrawable = new SignalDrawable(mContext); + mLocationController = locationController; + mDialogTransitionAnimator = dialogTransitionAnimator; + mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor(); + mWifiStateWorker = wifiStateWorker; + mFeatureFlags = featureFlags; + } + + void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) { + if (DEBUG) { + Log.d(TAG, "onStart"); + } + mCallback = callback; + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + mAccessPointController.addAccessPointCallback(this); + mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter, + mExecutor); + // Listen the subscription changes + mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener(); + refreshHasActiveSubId(); + mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, + mOnSubscriptionsChangedListener); + mDefaultDataSubId = getDefaultDataSubscriptionId(); + if (DEBUG) { + Log.d(TAG, "Init, SubId: " + mDefaultDataSubId); + } + mConfig = MobileMappings.Config.readConfig(mContext); + mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId); + mSubIdTelephonyManagerMap.put(mDefaultDataSubId, mTelephonyManager); + InternetTelephonyCallback telephonyCallback = + new InternetTelephonyCallback(mDefaultDataSubId); + mSubIdTelephonyCallbackMap.put(mDefaultDataSubId, telephonyCallback); + mTelephonyManager.registerTelephonyCallback(mExecutor, telephonyCallback); + // Listen the connectivity changes + mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback); + mCanConfigWifi = canConfigWifi; + scanWifiAccessPoints(); + } + + void onStop() { + if (DEBUG) { + Log.d(TAG, "onStop"); + } + mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver); + for (TelephonyManager tm : mSubIdTelephonyManagerMap.values()) { + TelephonyCallback callback = mSubIdTelephonyCallbackMap.get(tm.getSubscriptionId()); + if (callback != null) { + tm.unregisterTelephonyCallback(callback); + } else if (DEBUG) { + Log.e(TAG, "Unexpected null telephony call back for Sub " + tm.getSubscriptionId()); + } + } + mSubIdTelephonyManagerMap.clear(); + mSubIdTelephonyCallbackMap.clear(); + mSubIdTelephonyDisplayInfoMap.clear(); + mSubscriptionManager.removeOnSubscriptionsChangedListener( + mOnSubscriptionsChangedListener); + mAccessPointController.removeAccessPointCallback(this); + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback); + mConnectedWifiInternetMonitor.unregisterCallback(); + mCallback = null; + } + + @VisibleForTesting + boolean isAirplaneModeEnabled() { + return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0; + } + + void setAirplaneModeDisabled() { + mConnectivityManager.setAirplaneMode(false); + } + + @VisibleForTesting + protected int getDefaultDataSubscriptionId() { + return mSubscriptionManager.getDefaultDataSubscriptionId(); + } + + @VisibleForTesting + protected Intent getSettingsIntent() { + return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + + @Nullable + protected Intent getWifiDetailsSettingsIntent(String key) { + if (TextUtils.isEmpty(key)) { + if (DEBUG) { + Log.d(TAG, "connected entry's key is empty"); + } + return null; + } + return WifiUtils.getWifiDetailsSettingsIntent(key); + } + + CharSequence getDialogTitleText() { + if (isAirplaneModeEnabled()) { + return mContext.getText(R.string.airplane_mode); + } + return mContext.getText(R.string.quick_settings_internet_label); + } + + @Nullable + CharSequence getSubtitleText(boolean isProgressBarVisible) { + if (mCanConfigWifi && !isWifiEnabled()) { + // When Wi-Fi is disabled. + // Sub-Title: Wi-Fi is off + if (DEBUG) { + Log.d(TAG, "Wi-Fi off."); + } + return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF); + } + + if (isDeviceLocked()) { + // When the device is locked. + // Sub-Title: Unlock to view networks + if (DEBUG) { + Log.d(TAG, "The device is locked."); + } + return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS); + } + + if (mHasWifiEntries) { + return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null; + } + + if (mCanConfigWifi && isProgressBarVisible) { + // When the Wi-Fi scan result callback is received + // Sub-Title: Searching for networks... + return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS); + } + + if (isCarrierNetworkActive()) { + return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); + } + + // Sub-Title: + // show non_carrier_network_unavailable + // - while Wi-Fi on + no Wi-Fi item + // - while Wi-Fi on + no Wi-Fi item + mobile data off + // show all_network_unavailable: + // - while Wi-Fi on + no Wi-Fi item + no carrier item + // - while Wi-Fi on + no Wi-Fi item + service is out of service + // - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data. + if (DEBUG) { + Log.d(TAG, "No Wi-Fi item."); + } + boolean isActiveOnNonDds = getActiveAutoSwitchNonDdsSubId() != SubscriptionManager + .INVALID_SUBSCRIPTION_ID; + if (!hasActiveSubId() || (!isVoiceStateInService(mDefaultDataSubId) + && !isDataStateInService(mDefaultDataSubId) && !isActiveOnNonDds)) { + if (DEBUG) { + Log.d(TAG, "No carrier or service is out of service."); + } + return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE); + } + + if (mCanConfigWifi && !isMobileDataEnabled()) { + if (DEBUG) { + Log.d(TAG, "Mobile data off"); + } + return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); + } + + if (!activeNetworkIsCellular()) { + if (DEBUG) { + Log.d(TAG, "No carrier data."); + } + return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE); + } + + if (mCanConfigWifi) { + return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE); + } + return null; + } + + @Nullable + Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) { + Drawable drawable = getWifiDrawable(wifiEntry); + if (drawable == null) { + return null; + } + drawable.setTint(mContext.getColor(R.color.connected_network_primary_color)); + return drawable; + } + + /** + * Returns a Wi-Fi icon {@link Drawable}. + * + * @param wifiEntry {@link WifiEntry} + */ + @Nullable + Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) { + if (wifiEntry instanceof HotspotNetworkEntry) { + int deviceType = ((HotspotNetworkEntry) wifiEntry).getDeviceType(); + return mContext.getDrawable(getHotspotIconResource(deviceType)); + } + // If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable + // will be returned. + if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) { + return null; + } + return mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel()); + } + + Drawable getSignalStrengthDrawable(int subId) { + Drawable drawable = mContext.getDrawable( + R.drawable.ic_signal_strength_zero_bar_no_internet); + try { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null"); + } + return drawable; + } + + boolean isCarrierNetworkActive = isCarrierNetworkActive(); + if (isDataStateInService(subId) || isVoiceStateInService(subId) + || isCarrierNetworkActive) { + AtomicReference shared = new AtomicReference<>(); + shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive, subId)); + drawable = shared.get(); + } + + int tintColor = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorTertiary); + if (activeNetworkIsCellular() || isCarrierNetworkActive) { + tintColor = mContext.getColor(R.color.connected_network_primary_color); + } + drawable.setTint(tintColor); + } catch (Throwable e) { + e.printStackTrace(); + } + return drawable; + } + + /** + * To get the signal bar icon with level. + * + * @return The Drawable which is a signal bar icon with level. + */ + Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive, int subId) { + TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager); + final SignalStrength strength = tm.getSignalStrength(); + int level = (strength == null) ? 0 : strength.getLevel(); + int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; + if (isCarrierNetworkActive) { + level = getCarrierNetworkLevel(); + numLevels = WifiEntry.WIFI_LEVEL_MAX + 1; + } else if (mSubscriptionManager != null && shouldInflateSignalStrength(subId)) { + level += 1; + numLevels += 1; + } + return getSignalStrengthIcon(subId, mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, + !isMobileDataEnabled()); + } + + Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels, + int iconType, boolean cutOut) { + boolean isForDds = subId == mDefaultDataSubId; + int levelDrawable = + mCarrierNetworkChangeMode ? SignalDrawable.getCarrierChangeState(numLevels) + : SignalDrawable.getState(level, numLevels, cutOut); + if (isForDds) { + mSignalDrawable.setLevel(levelDrawable); + } else { + mSecondarySignalDrawable.setLevel(levelDrawable); + } + + // Make the network type drawable + final Drawable networkDrawable = + iconType == NO_CELL_DATA_TYPE_ICON + ? EMPTY_DRAWABLE + : context.getResources().getDrawable(iconType, context.getTheme()); + + // Overlay the two drawables + final Drawable[] layers = {networkDrawable, isForDds + ? mSignalDrawable : mSecondarySignalDrawable}; + final int iconSize = + context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size); + + final LayerDrawable icons = new LayerDrawable(layers); + // Set the network type icon at the top left + icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT); + // Set the signal strength icon at the bottom right + icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT); + icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize); + icons.setTintList(Utils.getColorAttr(context, android.R.attr.textColorTertiary)); + return icons; + } + + private boolean shouldInflateSignalStrength(int subId) { + return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); + } + + private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) { + final Map displayNames = getUniqueSubscriptionDisplayNames(context); + return displayNames.getOrDefault(subscriptionId, ""); + } + + private Map getUniqueSubscriptionDisplayNames(Context context) { + class DisplayInfo { + DisplayInfo(SubscriptionInfo subscriptionInfo, CharSequence originalName) { + this.subscriptionInfo = subscriptionInfo; + this.originalName = originalName; + } + + public SubscriptionInfo subscriptionInfo; + public CharSequence originalName; + public CharSequence uniqueName; + } + + // Map of SubscriptionId to DisplayName + final Supplier> originalInfos = + () -> getSubscriptionInfo() + .stream() + .filter(i -> { + // Filter out null values. + return (i != null && i.getDisplayName() != null); + }) + .map(i -> new DisplayInfo(i, i.getDisplayName().toString().trim())); + + // A Unique set of display names + Set uniqueNames = new HashSet<>(); + // Return the set of duplicate names + final Set duplicateOriginalNames = originalInfos.get() + .filter(info -> !uniqueNames.add(info.originalName)) + .map(info -> info.originalName) + .collect(Collectors.toSet()); + + // If a display name is duplicate, append the final 4 digits of the phone number. + // Creates a mapping of Subscription id to original display name + phone number display name + final Supplier> uniqueInfos = () -> originalInfos.get().map(info -> { + if (duplicateOriginalNames.contains(info.originalName)) { + // This may return null, if the user cannot view the phone number itself. + final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context, + info.subscriptionInfo); + String lastFourDigits = ""; + if (phoneNumber != null) { + lastFourDigits = (phoneNumber.length() > 4) + ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber; + } + + if (TextUtils.isEmpty(lastFourDigits)) { + info.uniqueName = info.originalName; + } else { + info.uniqueName = info.originalName + " " + lastFourDigits; + } + + } else { + info.uniqueName = info.originalName; + } + return info; + }); + + // Check uniqueness a second time. + // We might not have had permission to view the phone numbers. + // There might also be multiple phone numbers whose last 4 digits the same. + uniqueNames.clear(); + final Set duplicatePhoneNames = uniqueInfos.get() + .filter(info -> !uniqueNames.add(info.uniqueName)) + .map(info -> info.uniqueName) + .collect(Collectors.toSet()); + + return uniqueInfos.get().map(info -> { + if (duplicatePhoneNames.contains(info.uniqueName)) { + info.uniqueName = info.originalName + " " + + info.subscriptionInfo.getSubscriptionId(); + } + return info; + }).collect(Collectors.toMap( + info -> info.subscriptionInfo.getSubscriptionId(), + info -> info.uniqueName)); + } + + /** + * @return the subId of the visible non-DDS if it's actively being used for data, otherwise + * return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + */ + int getActiveAutoSwitchNonDdsSubId() { + if (!mFeatureFlags.isEnabled(Flags.QS_SECONDARY_DATA_SUB_INFO)) { + // sets the non-DDS to be not found to hide its visual + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo( + SubscriptionManager.getActiveDataSubscriptionId()); + if (subInfo != null && subInfo.getSubscriptionId() != mDefaultDataSubId + && !subInfo.isOpportunistic()) { + int subId = subInfo.getSubscriptionId(); + if (mSubIdTelephonyManagerMap.get(subId) == null) { + TelephonyManager secondaryTm = mTelephonyManager.createForSubscriptionId(subId); + InternetTelephonyCallback telephonyCallback = new InternetTelephonyCallback(subId); + secondaryTm.registerTelephonyCallback(mExecutor, telephonyCallback); + mSubIdTelephonyCallbackMap.put(subId, telephonyCallback); + mSubIdTelephonyManagerMap.put(subId, secondaryTm); + } + return subId; + } + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + } + + CharSequence getMobileNetworkTitle(int subId) { + return getUniqueSubscriptionDisplayName(subId, mContext); + } + + String getMobileNetworkSummary(int subId) { + String description = getNetworkTypeDescription(mContext, mConfig, subId); + return getMobileSummary(mContext, description, subId); + } + + /** + * Get currently description of mobile network type. + */ + private String getNetworkTypeDescription(Context context, MobileMappings.Config config, + int subId) { + TelephonyDisplayInfo telephonyDisplayInfo = + mSubIdTelephonyDisplayInfoMap.getOrDefault(subId, DEFAULT_TELEPHONY_DISPLAY_INFO); + String iconKey = getIconKey(telephonyDisplayInfo); + + if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) { + if (DEBUG) { + Log.d(TAG, "The description of network type is empty."); + } + return ""; + } + + int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription; + SignalIcon.MobileIconGroup iconGroup; + if (isCarrierNetworkActive()) { + iconGroup = TelephonyIcons.CARRIER_MERGED_WIFI; + resId = iconGroup.dataContentDescription; + } else if (mCarrierNetworkChangeMode) { + iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; + resId = iconGroup.dataContentDescription; + } + + return resId != 0 + ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : ""; + } + + private String getMobileSummary(Context context, String networkTypeDescription, int subId) { + if (!isMobileDataEnabled()) { + return context.getString(R.string.mobile_data_off_summary); + } + + String summary = networkTypeDescription; + boolean isForDds = subId == mDefaultDataSubId; + int activeSubId = getActiveAutoSwitchNonDdsSubId(); + boolean isOnNonDds = activeSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID; + // Set network description for the carrier network when connecting to the carrier network + // under the airplane mode ON. + if (activeNetworkIsCellular() || isCarrierNetworkActive()) { + summary = context.getString(R.string.preference_summary_default_combination, + context.getString( + isForDds // if nonDds is active, explains Dds status as poor connection + ? (isOnNonDds ? R.string.mobile_data_poor_connection + : R.string.mobile_data_connection_active) + : R.string.mobile_data_temp_connection_active), + networkTypeDescription); + } else if (!isDataStateInService(subId)) { + summary = context.getString(R.string.mobile_data_no_connection); + } + return summary; + } + + private void startActivity(Intent intent, View view) { + ActivityTransitionAnimator.Controller controller = + mDialogTransitionAnimator.createActivityTransitionController(view); + + if (controller == null && mCallback != null) { + mCallback.dismissDialog(); + } + + mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller); + } + + void launchNetworkSetting(View view) { + startActivity(getSettingsIntent(), view); + } + + void launchWifiDetailsSetting(String key, View view) { + Intent intent = getWifiDetailsSettingsIntent(key); + if (intent != null) { + startActivity(intent, view); + } + } + + void launchMobileNetworkSettings(View view) { + final int subId = getActiveAutoSwitchNonDdsSubId(); + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Log.w(TAG, "launchMobileNetworkSettings fail, invalid subId:" + subId); + return; + } + startActivity(getSubSettingIntent(subId), view); + } + + Intent getSubSettingIntent(int subId) { + final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS); + final Bundle fragmentArgs = new Bundle(); + // Special contract for Settings to highlight permission row + fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID); + intent.putExtra(Settings.EXTRA_SUB_ID, subId); + intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs); + return intent; + } + + void launchWifiScanningSetting(View view) { + final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent, view); + } + + /** + * Enable or disable Wi-Fi. + * + * @param enabled {@code true} to enable, {@code false} to disable. + */ + @AnyThread + public void setWifiEnabled(boolean enabled) { + mWifiStateWorker.setWifiEnabled(enabled); + } + + /** + * Return whether Wi-Fi is enabled or disabled. + * + * @return {@code true} if Wi-Fi is enabled or enabling + * @see WifiManager#getWifiState() + */ + @AnyThread + public boolean isWifiEnabled() { + return mWifiStateWorker.isWifiEnabled(); + } + + void connectCarrierNetwork() { + String errorLogPrefix = "Fail to connect carrier network : "; + + if (!isMobileDataEnabled()) { + if (DEBUG) { + Log.d(TAG, errorLogPrefix + "settings OFF"); + } + return; + } + if (isDeviceLocked()) { + if (DEBUG) { + Log.d(TAG, errorLogPrefix + "device locked"); + } + return; + } + if (activeNetworkIsCellular()) { + Log.d(TAG, errorLogPrefix + "already active"); + return; + } + + MergedCarrierEntry mergedCarrierEntry = + mAccessPointController.getMergedCarrierEntry(); + if (mergedCarrierEntry == null) { + Log.e(TAG, errorLogPrefix + "no merged entry"); + return; + } + + if (!mergedCarrierEntry.canConnect()) { + Log.w(TAG, errorLogPrefix + "merged entry connect state " + + mergedCarrierEntry.getConnectedState()); + return; + } + + mergedCarrierEntry.connect(null /* ConnectCallback */, false); + makeOverlayToast(R.string.wifi_wont_autoconnect_for_now); + } + + boolean isCarrierNetworkActive() { + //ccynice for wifi + return SystemProperties.getBoolean("net.wifi.isCarrierNetworkActive",true); + /* + final MergedCarrierEntry mergedCarrierEntry = + mAccessPointController.getMergedCarrierEntry(); + return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork();*/ + } + + int getCarrierNetworkLevel() { + final MergedCarrierEntry mergedCarrierEntry = + mAccessPointController.getMergedCarrierEntry(); + if (mergedCarrierEntry == null) return WifiEntry.WIFI_LEVEL_MIN; + + int level = mergedCarrierEntry.getLevel(); + // To avoid icons not found with WIFI_LEVEL_UNREACHABLE(-1), use WIFI_LEVEL_MIN(0) instead. + if (level < WifiEntry.WIFI_LEVEL_MIN) level = WifiEntry.WIFI_LEVEL_MIN; + return level; + } + + @WorkerThread + void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) { + // If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier + // Wi-Fi state together. + if (mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(subId)) { + return; + } + + final MergedCarrierEntry entry = mAccessPointController.getMergedCarrierEntry(); + if (entry == null) { + if (DEBUG) { + Log.d(TAG, "MergedCarrierEntry is null, can not set the status."); + } + return; + } + entry.setEnabled(enabled); + } + + WifiManager getWifiManager() { + return mWifiManager; + } + + TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + + SubscriptionManager getSubscriptionManager() { + return mSubscriptionManager; + } + + /** + * @return whether there is the carrier item in the slice. + */ + boolean hasActiveSubId() { + //ccynice for wifi + return SystemProperties.getBoolean("net.wifi.hasActiveSubId",true); + /* + if (isAirplaneModeEnabled() || mTelephonyManager == null) { + return false; + } + + return mHasActiveSubId;*/ + } + + private void refreshHasActiveSubId() { + if (mSubscriptionManager == null) { + mHasActiveSubId = false; + Log.e(TAG, "SubscriptionManager is null, set mHasActiveSubId = false"); + return; + } + + mHasActiveSubId = mSubscriptionManager.getActiveSubscriptionIdList().length > 0; + Log.i(TAG, "mHasActiveSubId:" + mHasActiveSubId); + } + + /** + * Return {@code true} if mobile data is enabled + */ + boolean isMobileDataEnabled() { + if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) { + return false; + } + return true; + } + + /** + * Set whether to enable data for {@code subId}, also whether to disable data for other + * subscription + */ + void setMobileDataEnabled(Context context, int subId, boolean enabled, + boolean disableOtherSubscriptions) { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null, can not set mobile data."); + } + return; + } + + if (mSubscriptionManager == null) { + if (DEBUG) { + Log.d(TAG, "SubscriptionManager is null, can not set mobile data."); + } + return; + } + + mTelephonyManager.setDataEnabledForReason( + TelephonyManager.DATA_ENABLED_REASON_USER, enabled); + if (disableOtherSubscriptions) { + final List subInfoList = + mSubscriptionManager.getActiveSubscriptionInfoList(); + if (subInfoList != null) { + for (SubscriptionInfo subInfo : subInfoList) { + // We never disable mobile data for opportunistic subscriptions. + if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) { + context.getSystemService(TelephonyManager.class).createForSubscriptionId( + subInfo.getSubscriptionId()).setDataEnabled(false); + } + } + } + } + mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled)); + } + + void setAutoDataSwitchMobileDataPolicy(int subId, boolean enable) { + TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager); + if (tm == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null, can not set mobile data."); + } + return; + } + tm.setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, enable); + } + + boolean isDataStateInService(int subId) { + TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager); + final ServiceState serviceState = tm.getServiceState(); + NetworkRegistrationInfo regInfo = + (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + return (regInfo == null) ? false : regInfo.isRegistered(); + } + + boolean isVoiceStateInService(int subId) { + if (mTelephonyManager == null) { + if (DEBUG) { + Log.d(TAG, "TelephonyManager is null, can not detect voice state."); + } + return false; + } + + TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager); + final ServiceState serviceState = tm.getServiceState(); + return serviceState != null + && serviceState.getState() == serviceState.STATE_IN_SERVICE; + } + + public boolean isDeviceLocked() { + return !mKeyguardStateController.isUnlocked(); + } + + boolean activeNetworkIsCellular() { + if (mConnectivityManager == null) { + if (DEBUG) { + Log.d(TAG, "ConnectivityManager is null, can not check active network."); + } + return false; + } + + final Network activeNetwork = mConnectivityManager.getActiveNetwork(); + if (activeNetwork == null) { + return false; + } + final NetworkCapabilities networkCapabilities = + mConnectivityManager.getNetworkCapabilities(activeNetwork); + if (networkCapabilities == null) { + return false; + } + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); + } + + boolean connect(WifiEntry ap) { + if (ap == null) { + if (DEBUG) { + Log.d(TAG, "No Wi-Fi ap to connect."); + } + return false; + } + + if (ap.getWifiConfiguration() != null) { + if (DEBUG) { + Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId); + } + } else { + if (DEBUG) { + Log.d(TAG, "connect to unsaved network " + ap.getTitle()); + } + } + ap.connect(new WifiEntryConnectCallback(mActivityStarter, ap, this)); + return false; + } + + @WorkerThread + boolean isWifiScanEnabled() { + if (!mLocationController.isLocationEnabled()) { + return false; + } + return mWifiManager != null && mWifiManager.isScanAlwaysAvailable(); + } + + static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback { + final ActivityStarter mActivityStarter; + final WifiEntry mWifiEntry; + final InternetDialogController mInternetDialogController; + + WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry, + InternetDialogController internetDialogController) { + mActivityStarter = activityStarter; + mWifiEntry = connectWifiEntry; + mInternetDialogController = internetDialogController; + } + + @Override + public void onConnectResult(@ConnectStatus int status) { + if (DEBUG) { + Log.d(TAG, "onConnectResult " + status); + } + + if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) { + final Intent intent = WifiUtils.getWifiDialogIntent(mWifiEntry.getKey(), + true /* connectForCaller */); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mActivityStarter.startActivity(intent, false /* dismissShade */); + } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) { + mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message); + } else { + if (DEBUG) { + Log.d(TAG, "connect failure reason=" + status); + } + } + } + } + + private void scanWifiAccessPoints() { + if (mCanConfigWifi) { + mAccessPointController.scanForAccessPoints(); + } + } + + @Override + @WorkerThread + public void onAccessPointsChanged(List accessPoints) { + if (!mCanConfigWifi) { + return; + } + + WifiEntry connectedEntry = null; + List wifiEntries = null; + final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size(); + final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT); + if (accessPointsSize > 0) { + wifiEntries = new ArrayList<>(); + final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize; + mConnectedWifiInternetMonitor.unregisterCallback(); + for (int i = 0; i < count; i++) { + WifiEntry entry = accessPoints.get(i); + mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry); + if (connectedEntry == null && entry.isDefaultNetwork() + && entry.hasInternetAccess()) { + connectedEntry = entry; + } else { + wifiEntries.add(entry); + } + } + mHasWifiEntries = true; + } else { + mHasWifiEntries = false; + } + + if (mCallback != null) { + mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries); + } + } + + @Override + public void onSettingsActivityTriggered(Intent settingsIntent) { + } + + @Override + public void onWifiScan(boolean isScan) { + if (!isWifiEnabled() || isDeviceLocked()) { + mCallback.onWifiScan(false); + return; + } + mCallback.onWifiScan(isScan); + } + + private class InternetTelephonyCallback extends TelephonyCallback implements + TelephonyCallback.DataConnectionStateListener, + TelephonyCallback.DisplayInfoListener, + TelephonyCallback.ServiceStateListener, + TelephonyCallback.SignalStrengthsListener, + TelephonyCallback.UserMobileDataStateListener, + TelephonyCallback.CarrierNetworkListener{ + + private final int mSubId; + private InternetTelephonyCallback(int subId) { + mSubId = subId; + } + + @Override + public void onServiceStateChanged(@NonNull ServiceState serviceState) { + if (mCallback != null) { + mCallback.onServiceStateChanged(serviceState); + } + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + if (mCallback != null) { + mCallback.onDataConnectionStateChanged(state, networkType); + } + } + + @Override + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { + if (mCallback != null) { + mCallback.onSignalStrengthsChanged(signalStrength); + } + } + + @Override + public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { + mSubIdTelephonyDisplayInfoMap.put(mSubId, telephonyDisplayInfo); + if (mCallback != null) { + mCallback.onDisplayInfoChanged(telephonyDisplayInfo); + } + } + + @Override + public void onUserMobileDataStateChanged(boolean enabled) { + if (mCallback != null) { + mCallback.onUserMobileDataStateChanged(enabled); + } + } + + @Override + public void onCarrierNetworkChange(boolean active) { + mCarrierNetworkChangeMode = active; + if (mCallback != null) { + mCallback.onCarrierNetworkChange(active); + } + } + } + + private class InternetOnSubscriptionChangedListener + extends SubscriptionManager.OnSubscriptionsChangedListener { + InternetOnSubscriptionChangedListener() { + super(); + } + + @Override + public void onSubscriptionsChanged() { + refreshHasActiveSubId(); + updateListener(); + } + } + + private class DataConnectivityListener extends ConnectivityManager.NetworkCallback { + @Override + @WorkerThread + public void onCapabilitiesChanged(@NonNull Network network, + @NonNull NetworkCapabilities capabilities) { + mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET); + if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport( + NetworkCapabilities.TRANSPORT_WIFI))) { + scanWifiAccessPoints(); + } + // update UI + if (mCallback != null) { + mCallback.onCapabilitiesChanged(network, capabilities); + } + } + + @Override + @WorkerThread + public void onLost(@NonNull Network network) { + mHasEthernet = false; + if (mCallback != null) { + mCallback.onLost(network); + } + } + } + + /** + * Helper class for monitoring the Internet access of the connected WifiEntry. + */ + @VisibleForTesting + protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback { + + private WifiEntry mWifiEntry; + + public void registerCallbackIfNeed(WifiEntry entry) { + if (entry == null || mWifiEntry != null) { + return; + } + // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet + // access. Then we don't need to listen to the callback to update the Wi-Fi entries. + if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED + || (entry.isDefaultNetwork() && entry.hasInternetAccess())) { + return; + } + mWifiEntry = entry; + entry.setListener(this); + } + + public void unregisterCallback() { + if (mWifiEntry == null) { + return; + } + mWifiEntry.setListener(null); + mWifiEntry = null; + } + + @MainThread + @Override + public void onUpdated() { + if (mWifiEntry == null) { + return; + } + WifiEntry entry = mWifiEntry; + if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) { + unregisterCallback(); + return; + } + if (entry.isDefaultNetwork() && entry.hasInternetAccess()) { + unregisterCallback(); + // Trigger onAccessPointsChanged() to update the Wi-Fi entries. + scanWifiAccessPoints(); + } + } + } + + /** + * Return {@code true} If the Ethernet exists + */ + @MainThread + public boolean hasEthernet() { + return mHasEthernet; + } + + private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) { + if (DEBUG) { + Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED"); + } + mConfig = MobileMappings.Config.readConfig(context); + updateListener(); + } else if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) { + updateListener(); + } + } + }; + + private void updateListener() { + int defaultDataSubId = getDefaultDataSubscriptionId(); + if (mDefaultDataSubId == getDefaultDataSubscriptionId()) { + if (DEBUG) { + Log.d(TAG, "DDS: no change"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "DDS: defaultDataSubId:" + defaultDataSubId); + } + if (SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)) { + // clean up old defaultDataSubId + TelephonyCallback oldCallback = mSubIdTelephonyCallbackMap.get(mDefaultDataSubId); + if (oldCallback != null) { + mTelephonyManager.unregisterTelephonyCallback(oldCallback); + } else if (DEBUG) { + Log.e(TAG, "Unexpected null telephony call back for Sub " + mDefaultDataSubId); + } + mSubIdTelephonyCallbackMap.remove(mDefaultDataSubId); + mSubIdTelephonyDisplayInfoMap.remove(mDefaultDataSubId); + mSubIdTelephonyManagerMap.remove(mDefaultDataSubId); + + // create for new defaultDataSubId + mTelephonyManager = mTelephonyManager.createForSubscriptionId(defaultDataSubId); + mSubIdTelephonyManagerMap.put(defaultDataSubId, mTelephonyManager); + InternetTelephonyCallback newCallback = new InternetTelephonyCallback(defaultDataSubId); + mSubIdTelephonyCallbackMap.put(defaultDataSubId, newCallback); + mTelephonyManager.registerTelephonyCallback(mHandler::post, newCallback); + mCallback.onSubscriptionsChanged(defaultDataSubId); + } + mDefaultDataSubId = defaultDataSubId; + } + + boolean mayLaunchShareWifiSettings(WifiEntry wifiEntry) { + Intent intent = getConfiguratorQrCodeGeneratorIntentOrNull(wifiEntry); + if (intent == null) { + return false; + } + if (mCallback != null) { + mCallback.dismissDialog(); + } + mActivityStarter.startActivity(intent, false /* dismissShade */); + return true; + } + + interface InternetDialogCallback { + + void onRefreshCarrierInfo(); + + void onSimStateChanged(); + + void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities); + + void onLost(@NonNull Network network); + + void onSubscriptionsChanged(int defaultDataSubId); + + void onServiceStateChanged(ServiceState serviceState); + + void onDataConnectionStateChanged(int state, int networkType); + + void onSignalStrengthsChanged(SignalStrength signalStrength); + + void onUserMobileDataStateChanged(boolean enabled); + + void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo); + + void onCarrierNetworkChange(boolean active); + + void dismissDialog(); + + void onAccessPointsChanged(@Nullable List wifiEntries, + @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries); + + void onWifiScan(boolean isScan); + } + + void makeOverlayToast(int stringId) { + final Resources res = mContext.getResources(); + + final SystemUIToast systemUIToast = mToastFactory.createToast(mContext, + res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(), + res.getConfiguration().orientation); + if (systemUIToast == null) { + return; + } + + View toastView = systemUIToast.getView(); + + final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + params.format = PixelFormat.TRANSLUCENT; + params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; + params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + params.y = systemUIToast.getYOffset(); + + int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(), + res.getConfiguration().getLayoutDirection()); + params.gravity = absGravity; + if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { + params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT; + } + if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { + params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT; + } + + mWindowManager.addView(toastView, params); + + Animator inAnimator = systemUIToast.getInAnimation(); + if (inAnimator != null) { + inAnimator.start(); + } + + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Animator outAnimator = systemUIToast.getOutAnimation(); + if (outAnimator != null) { + outAnimator.start(); + outAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + mWindowManager.removeViewImmediate(toastView); + } + }); + } + } + }, SHORT_DURATION_TIMEOUT); + } + + Intent getConfiguratorQrCodeGeneratorIntentOrNull(WifiEntry wifiEntry) { + if (!mFeatureFlags.isEnabled(Flags.SHARE_WIFI_QS_BUTTON) || wifiEntry == null + || mWifiManager == null || !wifiEntry.canShare()) { + return null; + } + Intent intent = new Intent(); + intent.setAction(WifiDppIntentHelper.ACTION_CONFIGURATOR_AUTH_QR_CODE_GENERATOR); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + WifiDppIntentHelper.setConfiguratorIntentExtra(intent, mWifiManager, + wifiEntry.getWifiConfiguration()); + return intent; + } +} diff --git a/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java new file mode 100755 index 0000000000000000000000000000000000000000..2465a8333fd0f59e553f5428f984211c54362d30 --- /dev/null +++ b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.connectivity; + +import android.content.Intent; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.util.IndentingPrintWriter; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.android.systemui.settings.UserTracker; +import com.android.wifitrackerlib.MergedCarrierEntry; +import com.android.wifitrackerlib.WifiEntry; +import com.android.wifitrackerlib.WifiPickerTracker; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +import android.net.IpConfiguration; +import android.net.IpConfiguration.IpAssignment; +import android.net.IpConfiguration.ProxySettings; +import android.net.wifi.WifiConfiguration; +import android.os.SystemProperties; + + +/** */ +public class AccessPointControllerImpl implements AccessPointController, + WifiPickerTracker.WifiPickerTrackerCallback, + LifecycleOwner { + private static final String TAG = "AccessPointController"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + // This string extra specifies a network to open the connect dialog on, so the user can enter + // network credentials. This is used by quick settings for secured networks. + private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; + + private static final int[] ICONS = WifiIcons.WIFI_FULL_ICONS; + + private final ArrayList mCallbacks = new ArrayList(); + private final UserManager mUserManager; + private final UserTracker mUserTracker; + private final Executor mMainExecutor; + + private @Nullable WifiPickerTracker mWifiPickerTracker; + private WifiPickerTrackerFactory mWifiPickerTrackerFactory; + + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + + private int mCurrentUser; + + public AccessPointControllerImpl( + UserManager userManager, + UserTracker userTracker, + Executor mainExecutor, + WifiPickerTrackerFactory wifiPickerTrackerFactory + ) { + mUserManager = userManager; + mUserTracker = userTracker; + mCurrentUser = userTracker.getUserId(); + mMainExecutor = mainExecutor; + mWifiPickerTrackerFactory = wifiPickerTrackerFactory; + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); + } + + /** + * Initializes the controller. + * + * Will create a WifiPickerTracker associated to this controller. + */ + public void init() { + if (mWifiPickerTracker == null) { + mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this, TAG); + } + + //ccynice + //final int numSavedNetworks = mWifiPickerTracker.getNumSavedNetworks(); + + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } + + @Override + protected void finalize() throws Throwable { + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.DESTROYED)); + super.finalize(); + } + + public boolean canConfigWifi() { + if (!mWifiPickerTrackerFactory.isSupported()) return false; + return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, + new UserHandle(mCurrentUser)); + } + + public boolean canConfigMobileData() { + return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, + UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin(); + } + + void onUserSwitched(int newUserId) { + mCurrentUser = newUserId; + } + + @Override + public void addAccessPointCallback(AccessPointCallback callback) { + if (callback == null || mCallbacks.contains(callback)) return; + if (DEBUG) Log.d(TAG, "addCallback " + callback); + mCallbacks.add(callback); + if (mCallbacks.size() == 1) { + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED)); + } + } + + @Override + public void removeAccessPointCallback(AccessPointCallback callback) { + if (callback == null) return; + if (DEBUG) Log.d(TAG, "removeCallback " + callback); + mCallbacks.remove(callback); + if (mCallbacks.isEmpty()) { + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); + } + } + + @Override + public void scanForAccessPoints() { + if (mWifiPickerTracker == null) { + fireAccessPointsCallback(Collections.emptyList()); + return; + } + List entries = mWifiPickerTracker.getWifiEntries(); + WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry(); + if (connectedEntry != null) { + entries.add(0, connectedEntry); + } + fireAccessPointsCallback(entries); + } + + @Override + public MergedCarrierEntry getMergedCarrierEntry() { + if (mWifiPickerTracker == null) { + fireAccessPointsCallback(Collections.emptyList()); + return null; + } + return mWifiPickerTracker.getMergedCarrierEntry(); + } + + @Override + public int getIcon(WifiEntry ap) { + int level = ap.getLevel(); + return ICONS[Math.max(0, level)]; + } + + /** + * Connects to a {@link WifiEntry} if it's saved or does not require security. + * + * If the entry is not saved and requires security, will trigger + * {@link AccessPointCallback#onSettingsActivityTriggered}. + * @param ap + * @return {@code true} if {@link AccessPointCallback#onSettingsActivityTriggered} is triggered + */ + public boolean connect(@Nullable WifiEntry ap) { + if (ap == null) return false; + if (DEBUG) { + if (ap.getWifiConfiguration() != null) { + Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId); + } else { + Log.d(TAG, "connect to unsaved network " + ap.getTitle()); + } + } + if (ap.isSaved()) { + ap.connect(mConnectCallback); + } else { + // Unknown network, need to add it. + if (ap.getSecurity() != WifiEntry.SECURITY_NONE) { + Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); + intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsid()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + fireSettingsIntentCallback(intent); + return true; + } else { + ap.connect(mConnectCallback); + } + } + return false; + } + + private void fireSettingsIntentCallback(Intent intent) { + for (AccessPointCallback callback : mCallbacks) { + callback.onSettingsActivityTriggered(intent); + } + } + + private void fireAccessPointsCallback(List aps) { + for (AccessPointCallback callback : mCallbacks) { + callback.onAccessPointsChanged(aps); + } + } + + private void fireWifiScanCallback(boolean isScan) { + for (AccessPointCallback callback : mCallbacks) { + callback.onWifiScan(isScan); + } + } + + void dump(PrintWriter pw) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + ipw.println("AccessPointControllerImpl:"); + ipw.increaseIndent(); + ipw.println("Callbacks: " + Arrays.toString(mCallbacks.toArray())); + ipw.println("WifiPickerTracker: " + mWifiPickerTracker.toString()); + if (mWifiPickerTracker != null && !mCallbacks.isEmpty()) { + ipw.println("Connected: " + mWifiPickerTracker.getConnectedWifiEntry()); + ipw.println("Other wifi entries: " + + Arrays.toString(mWifiPickerTracker.getWifiEntries().toArray())); + } else if (mWifiPickerTracker != null) { + ipw.println("WifiPickerTracker not started, cannot get reliable entries"); + } + ipw.decreaseIndent(); + } + + @Override + public void onWifiStateChanged() { + scanForAccessPoints(); + } + + @Override + public void onWifiEntriesChanged() { + scanForAccessPoints(); + } + + @Override + public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int reason) { + onWifiEntriesChanged(); + if (reason == WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) { + fireWifiScanCallback(false /* isScan */); + } + } + + @Override + public void onNumSavedNetworksChanged() { + // Do nothing + } + + @Override + public void onNumSavedSubscriptionsChanged() { + // Do nothing + } + + @Override + public void onScanRequested() { + fireWifiScanCallback(true /* isScan */); + } + + private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() { + @Override + public void onConnectResult(int status) { + if (status == CONNECT_STATUS_SUCCESS) { + if (DEBUG) Log.d(TAG, "connect success"); + } else { + if (DEBUG) Log.d(TAG, "connect failure reason=" + status); + } + } + }; +} diff --git a/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java new file mode 100755 index 0000000000000000000000000000000000000000..5461dc70fb8bf3ecf5525936b01a4367f226cac9 --- /dev/null +++ b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.connectivity; + +import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; + +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.net.NetworkCapabilities; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings.Global; +import android.telephony.CellSignalStrength; +import android.telephony.CellSignalStrengthCdma; +import android.telephony.SignalStrength; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.Html; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.SignalIcon.MobileIconGroup; +import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.mobile.MobileMappings.Config; +import com.android.settingslib.mobile.MobileStatusTracker; +import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus; +import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; +import com.android.settingslib.mobile.TelephonyIcons; +import com.android.settingslib.net.SignalStrengthUtil; +import com.android.systemui.res.R; +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy; +import com.android.systemui.util.CarrierConfigTracker; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.BitSet; +import java.util.List; +import java.util.Map; + +import android.os.SystemProperties; + + +/** + * Monitors the mobile signal changes and update the SysUI icons. + */ +public class MobileSignalController extends SignalController { + private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + private static final int STATUS_HISTORY_SIZE = 64; + private final TelephonyManager mPhone; + private final CarrierConfigTracker mCarrierConfigTracker; + private final SubscriptionDefaults mDefaults; + private final MobileMappingsProxy mMobileMappingsProxy; + private final String mNetworkNameDefault; + private final String mNetworkNameSeparator; + private final ContentObserver mObserver; + // Save entire info for logging, we only use the id. + final SubscriptionInfo mSubscriptionInfo; + private Map mNetworkToIconLookup; + + private MobileIconGroup mDefaultIcons; + private Config mConfig; + @VisibleForTesting + boolean mInflateSignalStrengths = false; + @VisibleForTesting + final MobileStatusTracker mMobileStatusTracker; + + // Save the previous STATUS_HISTORY_SIZE states for logging. + private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE]; + // Where to copy the next state into. + private int mMobileStatusHistoryIndex; + + private final MobileStatusTracker.Callback mMobileCallback = + new MobileStatusTracker.Callback() { + private String mLastStatus; + + @Override + public void onMobileStatusChanged(boolean updateTelephony, + MobileStatus mobileStatus) { + if (DEBUG) { + Log.d(mTag, "onMobileStatusChanged=" + + " updateTelephony=" + updateTelephony + + " mobileStatus=" + mobileStatus.toString()); + } + String currentStatus = mobileStatus.toString(); + if (!currentStatus.equals(mLastStatus)) { + mLastStatus = currentStatus; + String status = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append(currentStatus) + .toString(); + recordLastMobileStatus(status); + } + updateMobileStatus(mobileStatus); + if (updateTelephony) { + updateTelephony(); + } else { + notifyListenersIfNecessary(); + } + } + }; + + // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't + // need listener lists anymore. + public MobileSignalController( + Context context, + Config config, + boolean hasMobileData, + TelephonyManager phone, + CallbackHandler callbackHandler, + NetworkControllerImpl networkController, + MobileMappingsProxy mobileMappingsProxy, + SubscriptionInfo info, + SubscriptionDefaults defaults, + Looper receiverLooper, + CarrierConfigTracker carrierConfigTracker, + MobileStatusTrackerFactory mobileStatusTrackerFactory + ) { + super("MobileSignalController(" + info.getSubscriptionId() + ")", context, + NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler, + networkController); + mCarrierConfigTracker = carrierConfigTracker; + mConfig = config; + mPhone = phone; + mDefaults = defaults; + mSubscriptionInfo = info; + mMobileMappingsProxy = mobileMappingsProxy; + mNetworkNameSeparator = getTextIfExists( + R.string.status_bar_network_name_separator).toString(); + mNetworkNameDefault = getTextIfExists( + com.android.internal.R.string.lockscreen_carrier_default).toString(); + + mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig); + mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig); + + String networkName = info.getCarrierName() != null ? info.getCarrierName().toString() + : mNetworkNameDefault; + mLastState.networkName = mCurrentState.networkName = networkName; + mLastState.networkNameData = mCurrentState.networkNameData = networkName; + mLastState.enabled = mCurrentState.enabled = hasMobileData; + mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons; + mObserver = new ContentObserver(new Handler(receiverLooper)) { + @Override + public void onChange(boolean selfChange) { + updateTelephony(); + } + }; + mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback); + } + + void setConfiguration(Config config) { + mConfig = config; + updateInflateSignalStrength(); + mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig); + mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig); + updateTelephony(); + } + + void setAirplaneMode(boolean airplaneMode) { + mCurrentState.airplaneMode = airplaneMode; + notifyListenersIfNecessary(); + } + + void setUserSetupComplete(boolean userSetup) { + mCurrentState.userSetup = userSetup; + notifyListenersIfNecessary(); + } + + @Override + public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { + boolean isValidated = validatedTransports.get(mTransportType); + mCurrentState.isDefault = connectedTransports.get(mTransportType); + // Only show this as not having connectivity if we are default. + mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0; + notifyListenersIfNecessary(); + } + + void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { + mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode; + updateTelephony(); + } + + /** + * Start listening for phone state changes. + */ + public void registerListener() { + mMobileStatusTracker.setListening(true); + mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA), + true, mObserver); + mContext.getContentResolver().registerContentObserver(Global.getUriFor( + Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()), + true, mObserver); + } + + /** + * Stop listening for phone state changes. + */ + public void unregisterListener() { + mMobileStatusTracker.setListening(false); + mContext.getContentResolver().unregisterContentObserver(mObserver); + } + + private void updateInflateSignalStrength() { + mInflateSignalStrengths = SignalStrengthUtil.shouldInflateSignalStrength(mContext, + mSubscriptionInfo.getSubscriptionId()); + } + + private int getNumLevels() { + if (mInflateSignalStrengths) { + return CellSignalStrength.getNumSignalStrengthLevels() + 1; + } + return CellSignalStrength.getNumSignalStrengthLevels(); + } + + @Override + public int getCurrentIconId() { + if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) { + return SignalDrawable.getCarrierChangeState(getNumLevels()); + } else if (mCurrentState.connected) { + int level = mCurrentState.level; + if (mInflateSignalStrengths) { + level++; + } + boolean dataDisabled = mCurrentState.userSetup + && (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED + || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA + && mCurrentState.defaultDataOff)); + boolean noInternet = mCurrentState.inetCondition == 0; + boolean cutOut = dataDisabled || noInternet; + return SignalDrawable.getState(level, getNumLevels(), cutOut); + } else if (mCurrentState.enabled) { + return SignalDrawable.getEmptyState(getNumLevels()); + } else { + return 0; + } + } + + @Override + public int getQsCurrentIconId() { + return getCurrentIconId(); + } + + @Override + public void notifyListeners(SignalCallback callback) { + // If the device is on carrier merged WiFi, we should let WifiSignalController to control + // the SysUI states. + if (mNetworkController.isCarrierMergedWifi(mSubscriptionInfo.getSubscriptionId())) { + return; + } + MobileIconGroup icons = getIcons(); + + String contentDescription = getTextIfExists(getContentDescription()).toString(); + CharSequence dataContentDescriptionHtml = getTextIfExists(icons.dataContentDescription); + + //TODO: Hacky + // The data content description can sometimes be shown in a text view and might come to us + // as HTML. Strip any styling here so that listeners don't have to care + CharSequence dataContentDescription = Html.fromHtml( + dataContentDescriptionHtml.toString(), 0).toString(); + if (mCurrentState.inetCondition == 0) { + dataContentDescription = mContext.getString(R.string.data_connection_no_internet); + } + + int iconId = mCurrentState.getNetworkTypeIcon(mContext); + final QsInfo qsInfo = getQsInfo(contentDescription, iconId); + final SbInfo sbInfo = getSbInfo(contentDescription, iconId); + + MobileDataIndicators mobileDataIndicators = new MobileDataIndicators( + sbInfo.icon, + qsInfo.icon, + sbInfo.ratTypeIcon, + qsInfo.ratTypeIcon, + mCurrentState.hasActivityIn(), + mCurrentState.hasActivityOut(), + dataContentDescription, + dataContentDescriptionHtml, + qsInfo.description, + mSubscriptionInfo.getSubscriptionId(), + mCurrentState.roaming, + sbInfo.showTriangle); + callback.setMobileDataIndicators(mobileDataIndicators); + } + + private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) { + int qsTypeIcon = 0; + IconState qsIcon = null; + CharSequence qsDescription = null; + + if (mCurrentState.dataSim) { + // only show QS icons if the state is also default + if (!mCurrentState.isDefault) { + return new QsInfo(qsTypeIcon, qsIcon, qsDescription); + } + + if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) { + qsTypeIcon = dataTypeIcon; + } + + boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency; + qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription); + + if (!mCurrentState.isEmergency) { + qsDescription = mCurrentState.networkName; + } + } + + return new QsInfo(qsTypeIcon, qsIcon, qsDescription); + } + + private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) { + final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault(); + IconState statusIcon = new IconState( + mCurrentState.enabled && !mCurrentState.airplaneMode, + getCurrentIconId(), contentDescription); + + boolean showDataIconInStatusBar = + (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled; + int typeIcon = + (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0; + boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; + + return new SbInfo(showTriangle, typeIcon, statusIcon); + } + + @Override + protected MobileState cleanState() { + return new MobileState(); + } + + public boolean isInService() { + return mCurrentState.isInService(); + } + + String getNetworkNameForCarrierWiFi() { + return mPhone.getSimOperatorName(); + } + + private boolean isRoaming() { + // During a carrier change, roaming indications need to be suppressed. + if (isCarrierNetworkChangeActive()) { + return false; + } + if (mCurrentState.isCdma()) { + return mPhone.getCdmaEnhancedRoamingIndicatorDisplayNumber() + != TelephonyManager.ERI_OFF; + } else { + return mCurrentState.isRoaming(); + } + } + + private boolean isCarrierNetworkChangeActive() { + return mCurrentState.carrierNetworkChangeMode; + } + + void handleBroadcast(Intent intent) { + String action = intent.getAction(); + if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) { + updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false), + intent.getStringExtra(TelephonyManager.EXTRA_SPN), + intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN), + intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false), + intent.getStringExtra(TelephonyManager.EXTRA_PLMN)); + notifyListenersIfNecessary(); + } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) { + updateDataSim(); + notifyListenersIfNecessary(); + } else if (action.equals(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) { + int carrierId = intent.getIntExtra( + TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID); + mCurrentState.setCarrierId(carrierId); + } + } + + private void updateDataSim() { + int activeDataSubId = mDefaults.getActiveDataSubId(); + if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) { + mCurrentState.dataSim = activeDataSubId == mSubscriptionInfo.getSubscriptionId(); + } else { + // There doesn't seem to be a data sim selected, however if + // there isn't a MobileSignalController with dataSim set, then + // QS won't get any callbacks and will be blank. Instead + // lets just assume we are the data sim (which will basically + // show one at random) in QS until one is selected. The user + // should pick one soon after, so we shouldn't be in this state + // for long. + mCurrentState.dataSim = true; + } + } + + /** + * Updates the network's name based on incoming spn and plmn. + */ + void updateNetworkName(boolean showSpn, String spn, String dataSpn, + boolean showPlmn, String plmn) { + if (CHATTY) { + Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + + " spn=" + spn + " dataSpn=" + dataSpn + + " showPlmn=" + showPlmn + " plmn=" + plmn); + } + StringBuilder str = new StringBuilder(); + StringBuilder strData = new StringBuilder(); + if (showPlmn && plmn != null) { + str.append(plmn); + strData.append(plmn); + } + if (showSpn && spn != null) { + if (str.length() != 0) { + str.append(mNetworkNameSeparator); + } + str.append(spn); + } + if (str.length() != 0) { + mCurrentState.networkName = str.toString(); + } else { + mCurrentState.networkName = mNetworkNameDefault; + } + if (showSpn && dataSpn != null) { + if (strData.length() != 0) { + strData.append(mNetworkNameSeparator); + } + strData.append(dataSpn); + } + if (strData.length() != 0) { + mCurrentState.networkNameData = strData.toString(); + } else { + mCurrentState.networkNameData = mNetworkNameDefault; + } + } + + /** + * Extracts the CellSignalStrengthCdma from SignalStrength then returns the level + */ + private int getCdmaLevel(SignalStrength signalStrength) { + List signalStrengthCdma = + signalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class); + if (!signalStrengthCdma.isEmpty()) { + return signalStrengthCdma.get(0).getLevel(); + } + return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + + private void updateMobileStatus(MobileStatus mobileStatus) { + mCurrentState.setFromMobileStatus(mobileStatus); + } + + int getSignalLevel(SignalStrength signalStrength) { + //ccynice for sim.signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + /* + if (signalStrength == null) { + Log.d(mTag, "getSignalLevel signalStrength="+signalStrength); + return 0; + } + if (!signalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) { + Log.d(mTag, "getSignalLevel 1111"); + return getCdmaLevel(signalStrength); + } else { + Log.d(mTag, "getSignalLevel 2222"); + return signalStrength.getLevel(); + }*/ + } + + /** + * Updates the current state based on ServiceState, SignalStrength, DataState, + * TelephonyDisplayInfo, and sim state. It should be called any time one of these is updated. + * This will call listeners if necessary. + */ + private void updateTelephony() { + if (DEBUG) { + Log.d(mTag, "updateTelephonySignalStrength: hasService=" + + mCurrentState.isInService() + + " ss=" + mCurrentState.signalStrength + + " displayInfo=" + mCurrentState.telephonyDisplayInfo); + } + checkDefaultData(); + mCurrentState.connected = mCurrentState.isInService(); + if (mCurrentState.connected) { + mCurrentState.level = getSignalLevel(mCurrentState.signalStrength); + } + + mCurrentState.setCarrierId(mPhone.getSimCarrierId()); + String iconKey = mMobileMappingsProxy.getIconKey(mCurrentState.telephonyDisplayInfo); + if (mNetworkToIconLookup.get(iconKey) != null) { + mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey); + } else { + mCurrentState.iconGroup = mDefaultIcons; + } + mCurrentState.dataConnected = mCurrentState.isDataConnected(); + + mCurrentState.roaming = isRoaming(); + if (isCarrierNetworkChangeActive()) { + mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE; + } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) { + if (mSubscriptionInfo.getSubscriptionId() != mDefaults.getDefaultDataSubId()) { + mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA; + } else { + mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED; + } + } + if (mCurrentState.isEmergencyOnly() != mCurrentState.isEmergency) { + mCurrentState.isEmergency = mCurrentState.isEmergencyOnly(); + mNetworkController.recalculateEmergency(); + } + // Fill in the network name if we think we have it. + if (mCurrentState.networkName.equals(mNetworkNameDefault) + && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) { + mCurrentState.networkName = mCurrentState.getOperatorAlphaShort(); + } + // If this is the data subscription, update the currentState data name + if (mCurrentState.networkNameData.equals(mNetworkNameDefault) + && mCurrentState.dataSim + && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) { + mCurrentState.networkNameData = mCurrentState.getOperatorAlphaShort(); + } + + notifyListenersIfNecessary(); + } + + /** + * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one + */ + private void checkDefaultData() { + if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) { + mCurrentState.defaultDataOff = false; + return; + } + + mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled(); + } + + void onMobileDataChanged() { + checkDefaultData(); + notifyListenersIfNecessary(); + } + + boolean isDataDisabled() { + return !mPhone.isDataConnectionAllowed(); + } + + @VisibleForTesting + void setActivity(int activity) { + mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT + || activity == TelephonyManager.DATA_ACTIVITY_IN; + mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT + || activity == TelephonyManager.DATA_ACTIVITY_OUT; + notifyListenersIfNecessary(); + } + + private void recordLastMobileStatus(String mobileStatus) { + mMobileStatusHistory[mMobileStatusHistoryIndex] = mobileStatus; + mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE; + } + + @Override + public void dump(PrintWriter pw) { + super.dump(pw); + pw.println(" mSubscription=" + mSubscriptionInfo + ","); + pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ","); + pw.println(" isDataDisabled=" + isDataDisabled() + ","); + pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ","); + pw.println(" mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening()); + pw.println(" MobileStatusHistory"); + int size = 0; + for (int i = 0; i < STATUS_HISTORY_SIZE; i++) { + if (mMobileStatusHistory[i] != null) { + size++; + } + } + // Print out the previous states in ordered number. + for (int i = mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - 1; + i >= mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - size; i--) { + pw.println(" Previous MobileStatus(" + + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): " + + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]); + } + + dumpTableData(pw); + } + + /** Box for QS icon info */ + private static final class QsInfo { + final int ratTypeIcon; + final IconState icon; + final CharSequence description; + + QsInfo(int typeIcon, IconState iconState, CharSequence desc) { + ratTypeIcon = typeIcon; + icon = iconState; + description = desc; + } + + @Override + public String toString() { + return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon; + } + } + + /** Box for status bar icon info */ + private static final class SbInfo { + final boolean showTriangle; + final int ratTypeIcon; + final IconState icon; + + SbInfo(boolean show, int typeIcon, IconState iconState) { + showTriangle = show; + ratTypeIcon = typeIcon; + icon = iconState; + } + + @Override + public String toString() { + return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon + + " icon=" + icon; + } + } +} diff --git a/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java new file mode 100755 index 0000000000000000000000000000000000000000..77920f80fbf67277d18c5111bdcde22342cf7ba3 --- /dev/null +++ b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -0,0 +1,1486 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.connectivity; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.CellSignalStrength; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.util.MathUtils; +import android.util.SparseArray; + +import androidx.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.Utils; +import com.android.settingslib.mobile.MobileMappings.Config; +import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; +import com.android.settingslib.mobile.TelephonyIcons; +import com.android.settingslib.net.DataUsageController; +import com.android.systemui.Dumpable; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.log.core.LogLevel; +import com.android.systemui.log.dagger.StatusBarNetworkControllerLog; +import com.android.systemui.qs.tiles.dialog.InternetDialogManager; +import com.android.systemui.res.R; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DataSaverController; +import com.android.systemui.statusbar.policy.DataSaverControllerImpl; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.telephony.TelephonyListenerManager; +import com.android.systemui.util.CarrierConfigTracker; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import dalvik.annotation.optimization.NeverCompile; +import kotlin.Unit; + +/** Platform implementation of the network controller. **/ +@SysUISingleton +public class NetworkControllerImpl extends BroadcastReceiver + implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider, Dumpable { + // debug + static final String TAG = "NetworkController"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + // additional diagnostics, but not logspew + static final boolean CHATTY = Log.isLoggable(TAG + "Chat", Log.DEBUG); + + private static final int EMERGENCY_NO_CONTROLLERS = 0; + private static final int EMERGENCY_FIRST_CONTROLLER = 100; + private static final int EMERGENCY_VOICE_CONTROLLER = 200; + private static final int EMERGENCY_NO_SUB = 300; + private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400; + private static final int HISTORY_SIZE = 16; + private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + + private final Context mContext; + private final TelephonyManager mPhone; + private final TelephonyListenerManager mTelephonyListenerManager; + private final WifiManager mWifiManager; + private final ConnectivityManager mConnectivityManager; + private final SubscriptionManager mSubscriptionManager; + private final boolean mHasMobileDataFeature; + private final SubscriptionDefaults mSubDefaults; + private final DataSaverController mDataSaverController; + private final UserTracker mUserTracker; + private final BroadcastDispatcher mBroadcastDispatcher; + private final DemoModeController mDemoModeController; + private final Object mLock = new Object(); + private Config mConfig; + private final CarrierConfigTracker mCarrierConfigTracker; + private final DumpManager mDumpManager; + private final LogBuffer mLogBuffer; + private final MobileSignalControllerFactory mMobileFactory; + + private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener; + private int mActiveMobileDataSubscription = INVALID_SUBSCRIPTION_ID; + + // Subcontrollers. + @VisibleForTesting + final WifiSignalController mWifiSignalController; + + //@VisibleForTesting + + //ccynice for close systeui ether ico update + //final EthernetSignalController mEthernetSignalController; + + @VisibleForTesting + final SparseArray mMobileSignalControllers = new SparseArray<>(); + // When no SIMs are around at setup, and one is added later, it seems to default to the first + // SIM for most actions. This may be null if there aren't any SIMs around. + private MobileSignalController mDefaultSignalController; + private final AccessPointControllerImpl mAccessPoints; + private final DataUsageController mDataUsageController; + + private boolean mInetCondition; // Used for Logging and demo. + + // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are + // connected and validated, respectively. + private final BitSet mConnectedTransports = new BitSet(); + private final BitSet mValidatedTransports = new BitSet(); + + // States that don't belong to a subcontroller. + private boolean mAirplaneMode = false; + private boolean mHasNoSubs; + private boolean mNoDefaultNetwork = false; + private boolean mNoNetworksAvailable = true; + private Locale mLocale = null; + // This list holds our ordering. + private List mCurrentSubscriptions = new ArrayList<>(); + + // Save the previous HISTORY_SIZE states for logging. + private final String[] mHistory = new String[HISTORY_SIZE]; + // Where to copy the next state into. + private int mHistoryIndex; + + @VisibleForTesting + boolean mListening; + + // The current user ID. + private int mCurrentUserId; + + private OnSubscriptionsChangedListener mSubscriptionListener; + private NetworkCapabilities mLastDefaultNetworkCapabilities; + // Handler that all broadcasts are received on. + private final Handler mReceiverHandler; + private final Looper mBgLooper; + private final Executor mBgExecutor; + // Handler that all callbacks are made on. + private final CallbackHandler mCallbackHandler; + private final StatusBarPipelineFlags mStatusBarPipelineFlags; + + private int mEmergencySource; + private boolean mIsEmergency; + + @VisibleForTesting + ServiceState mLastServiceState; + private boolean mUserSetup; + private boolean mSimDetected; + private boolean mForceCellularValidated; + private InternetDialogManager mInternetDialogManager; + private Handler mMainHandler; + + private ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onConfigChanged(Configuration newConfig) { + mConfig = Config.readConfig(mContext); + mReceiverHandler.post(() -> handleConfigurationChanged()); + } + }; + + private final UserTracker.Callback mUserChangedCallback = + new UserTracker.Callback() { + @Override + public void onUserChanged(int newUser, @NonNull Context userContext) { + NetworkControllerImpl.this.onUserSwitched(newUser); + } + }; + + /** + * Construct this controller object and register for updates. + */ + @Inject + public NetworkControllerImpl( + Context context, + @Background Looper bgLooper, + @Background Executor bgExecutor, + SubscriptionManager subscriptionManager, + CallbackHandler callbackHandler, + DeviceProvisionedController deviceProvisionedController, + BroadcastDispatcher broadcastDispatcher, + UserTracker userTracker, + ConnectivityManager connectivityManager, + TelephonyManager telephonyManager, + TelephonyListenerManager telephonyListenerManager, + @Nullable WifiManager wifiManager, + AccessPointControllerImpl accessPointController, + StatusBarPipelineFlags statusBarPipelineFlags, + DemoModeController demoModeController, + CarrierConfigTracker carrierConfigTracker, + WifiStatusTrackerFactory trackerFactory, + MobileSignalControllerFactory mobileFactory, + @Main Handler handler, + InternetDialogManager internetDialogManager, + DumpManager dumpManager, + @StatusBarNetworkControllerLog LogBuffer logBuffer) { + this(context, connectivityManager, + telephonyManager, + telephonyListenerManager, + wifiManager, + subscriptionManager, + Config.readConfig(context), + bgLooper, + bgExecutor, + callbackHandler, + accessPointController, + statusBarPipelineFlags, + new DataUsageController(context), + new SubscriptionDefaults(), + deviceProvisionedController, + broadcastDispatcher, + userTracker, + demoModeController, + carrierConfigTracker, + trackerFactory, + mobileFactory, + handler, + dumpManager, + logBuffer); + mReceiverHandler.post(mRegisterListeners); + mInternetDialogManager = internetDialogManager; + } + + @VisibleForTesting + NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, + TelephonyManager telephonyManager, + TelephonyListenerManager telephonyListenerManager, + WifiManager wifiManager, + SubscriptionManager subManager, + Config config, + Looper bgLooper, + Executor bgExecutor, + CallbackHandler callbackHandler, + AccessPointControllerImpl accessPointController, + StatusBarPipelineFlags statusBarPipelineFlags, + DataUsageController dataUsageController, + SubscriptionDefaults defaultsHandler, + DeviceProvisionedController deviceProvisionedController, + BroadcastDispatcher broadcastDispatcher, + UserTracker userTracker, + DemoModeController demoModeController, + CarrierConfigTracker carrierConfigTracker, + WifiStatusTrackerFactory trackerFactory, + MobileSignalControllerFactory mobileFactory, + @Main Handler handler, + DumpManager dumpManager, + LogBuffer logBuffer + ) { + mContext = context; + mTelephonyListenerManager = telephonyListenerManager; + mConfig = config; + mMainHandler = handler; + mReceiverHandler = new Handler(bgLooper); + mBgLooper = bgLooper; + mBgExecutor = bgExecutor; + mCallbackHandler = callbackHandler; + mStatusBarPipelineFlags = statusBarPipelineFlags; + mDataSaverController = new DataSaverControllerImpl(context); + mBroadcastDispatcher = broadcastDispatcher; + mMobileFactory = mobileFactory; + + mSubscriptionManager = subManager; + mSubDefaults = defaultsHandler; + mConnectivityManager = connectivityManager; + mHasMobileDataFeature = telephonyManager.isDataCapable(); + mDemoModeController = demoModeController; + mCarrierConfigTracker = carrierConfigTracker; + mDumpManager = dumpManager; + mLogBuffer = logBuffer; + + // telephony + mPhone = telephonyManager; + + // wifi + mWifiManager = wifiManager; + + mLocale = mContext.getResources().getConfiguration().locale; + mAccessPoints = accessPointController; + mDataUsageController = dataUsageController; + mDataUsageController.setNetworkController(this); + // TODO: Find a way to move this into DataUsageController. + mDataUsageController.setCallback(new DataUsageController.Callback() { + @Override + public void onMobileDataEnabled(boolean enabled) { + mCallbackHandler.setMobileDataEnabled(enabled); + notifyControllersMobileDataChanged(); + } + }); + + mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature, + mCallbackHandler, this, mWifiManager, trackerFactory, + mReceiverHandler); + + //ccynice for close systeui ether ico update + //mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this); + + // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it + updateAirplaneMode(true /* force callback */); + mUserTracker = userTracker; + mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler)); + + deviceProvisionedController.addCallback(new DeviceProvisionedListener() { + @Override + public void onUserSetupChanged() { + setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup()); + } + }); + // Get initial user setup state + setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup()); + + WifiManager.ScanResultsCallback scanResultsCallback = + new WifiManager.ScanResultsCallback() { + @Override + public void onScanResultsAvailable() { + mNoNetworksAvailable = true; + for (ScanResult scanResult : mWifiManager.getScanResults()) { + if (!scanResult.SSID.equals(mWifiSignalController.getState().ssid)) { + mNoNetworksAvailable = false; + break; + } + } + // Only update the network availability if there is no default network. + if (mNoDefaultNetwork) { + mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, + mNoNetworksAvailable); + } + } + }; + + if (mWifiManager != null) { + mWifiManager.registerScanResultsCallback(mReceiverHandler::post, scanResultsCallback); + } + + NetworkCallback callback = + new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO){ + private Network mLastNetwork; + private NetworkCapabilities mLastNetworkCapabilities; + + @Override + public void onLost(Network network) { + mLastNetwork = null; + mLastNetworkCapabilities = null; + mLastDefaultNetworkCapabilities = null; + String callback = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("onLost: ") + .append("network=").append(network) + .toString(); + recordLastNetworkCallback(callback); + updateConnectivity(); + } + + @Override + public void onCapabilitiesChanged( + Network network, NetworkCapabilities networkCapabilities) { + boolean lastValidated = (mLastNetworkCapabilities != null) + && mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED); + boolean validated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED); + + // This callback is invoked a lot (i.e. when RSSI changes), so avoid updating + // icons when connectivity state has remained the same. + if (network.equals(mLastNetwork) && validated == lastValidated) { + // Should not rely on getTransportTypes() returning the same order of transport + // types. So sort the array before comparing. + int[] newTypes = getProcessedTransportTypes(networkCapabilities); + Arrays.sort(newTypes); + + int[] lastTypes = (mLastNetworkCapabilities != null) + ? getProcessedTransportTypes(mLastNetworkCapabilities) : null; + if (lastTypes != null) Arrays.sort(lastTypes); + + if (Arrays.equals(newTypes, lastTypes)) { + return; + } + } + mLastNetwork = network; + mLastNetworkCapabilities = networkCapabilities; + mLastDefaultNetworkCapabilities = networkCapabilities; + String callback = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append("onCapabilitiesChanged: ") + .append("network=").append(network).append(",") + .append("networkCapabilities=").append(networkCapabilities) + .toString(); + recordLastNetworkCallback(callback); + updateConnectivity(); + } + }; + // Even though this callback runs on the receiver handler thread which also processes the + // CONNECTIVITY_ACTION broadcasts, the broadcast and callback might come in at different + // times. This is safe since updateConnectivity() builds the list of transports from + // scratch. + // TODO: Move off of the deprecated CONNECTIVITY_ACTION broadcast and rely on callbacks + // exclusively for status bar icons. + mConnectivityManager.registerDefaultNetworkCallback(callback, mReceiverHandler); + // Run the listener on our bg looper + mPhoneStateListener = subId -> { + mBgExecutor.execute(() -> { + // For data switching from A to B, we assume B is validated for up to 2 seconds if: + // 1) A and B are in the same subscription group e.g. CBRS data switch. And + // 2) A was validated before the switch. + // This is to provide smooth transition for UI without showing cross during data + // switch. + if (keepCellularValidationBitInSwitch(mActiveMobileDataSubscription, subId)) { + if (DEBUG) Log.d(TAG, ": mForceCellularValidated to true."); + mForceCellularValidated = true; + mReceiverHandler.removeCallbacks(mClearForceValidated); + mReceiverHandler.postDelayed(mClearForceValidated, 2000); + } + mActiveMobileDataSubscription = subId; + doUpdateMobileControllers(); + }); + }; + + mDemoModeController.addCallback(this); + + mDumpManager.registerNormalDumpable(TAG, this); + } + + private final Runnable mClearForceValidated = () -> { + if (DEBUG) Log.d(TAG, ": mClearForceValidated"); + mForceCellularValidated = false; + updateConnectivity(); + }; + + boolean isInGroupDataSwitch(int subId1, int subId2) { + SubscriptionInfo info1 = mSubscriptionManager.getActiveSubscriptionInfo(subId1); + SubscriptionInfo info2 = mSubscriptionManager.getActiveSubscriptionInfo(subId2); + return (info1 != null && info2 != null && info1.getGroupUuid() != null + && info1.getGroupUuid().equals(info2.getGroupUuid())); + } + + boolean keepCellularValidationBitInSwitch(int sourceSubId, int destSubId) { + return mValidatedTransports.get(TRANSPORT_CELLULAR) + && isInGroupDataSwitch(sourceSubId, destSubId); + } + + public DataSaverController getDataSaverController() { + return mDataSaverController; + } + + @VisibleForTesting + void registerListeners() { + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.registerListener(); + } + if (mSubscriptionListener == null) { + mSubscriptionListener = new SubListener(mBgLooper); + } + mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); + mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener); + + // broadcasts + IntentFilter filter = new IntentFilter(); + filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(Intent.ACTION_SERVICE_STATE); + filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); + filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY); + filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); + filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED); + filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); + filter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler); + mListening = true; + + // Initial setup of connectivity. Handled as if we had received a sticky broadcast of + // ConnectivityManager.CONNECTIVITY_ACTION. + mReceiverHandler.post(this::updateConnectivity); + + // Initial setup of WifiSignalController. Handled as if we had received a sticky broadcast + // of WifiManager.WIFI_STATE_CHANGED_ACTION or WifiManager.NETWORK_STATE_CHANGED_ACTION + mReceiverHandler.post(mWifiSignalController::fetchInitialState); + + // Initial setup of mLastServiceState. Only run if there is no service state yet. + // Each MobileSignalController will also get their corresponding + mReceiverHandler.post(() -> { + if (mLastServiceState == null) { + mLastServiceState = mPhone.getServiceState(); + if (mMobileSignalControllers.size() == 0) { + recalculateEmergency(); + } + } + }); + updateMobileControllers(); + + // Initial setup of emergency information. Handled as if we had received a sticky broadcast + // of TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED. + mReceiverHandler.post(this::recalculateEmergency); + } + + private void unregisterListeners() { + mListening = false; + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.unregisterListener(); + } + mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener); + mBroadcastDispatcher.unregisterReceiver(this); + } + + @Override + public AccessPointController getAccessPointController() { + return mAccessPoints; + } + + @Override + public DataUsageController getMobileDataController() { + return mDataUsageController; + } + + /** */ + public void addEmergencyListener(EmergencyListener listener) { + mCallbackHandler.setListening(listener, true); + mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); + } + + /** */ + public void removeEmergencyListener(EmergencyListener listener) { + mCallbackHandler.setListening(listener, false); + } + + /** */ + public boolean hasMobileDataFeature() { + return mHasMobileDataFeature; + } + + /** */ + public boolean hasVoiceCallingFeature() { + return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; + } + + private int[] getProcessedTransportTypes(NetworkCapabilities networkCapabilities) { + int[] transportTypes = networkCapabilities.getTransportTypes(); + for (int i = 0; i < transportTypes.length; i++) { + // For VCN over WiFi, the transportType is set to be TRANSPORT_CELLULAR in the + // NetworkCapabilities, but we need to convert it into TRANSPORT_WIFI in order to + // distinguish it from VCN over Cellular. + if (transportTypes[i] == NetworkCapabilities.TRANSPORT_CELLULAR + && Utils.tryGetWifiInfoForVcn(networkCapabilities) != null) { + transportTypes[i] = NetworkCapabilities.TRANSPORT_WIFI; + break; + } + } + return transportTypes; + } + + private MobileSignalController getDataController() { + int dataSubId = mSubDefaults.getActiveDataSubId(); + return getControllerWithSubId(dataSubId); + } + + private MobileSignalController getControllerWithSubId(int subId) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + if (DEBUG) Log.e(TAG, "No data sim selected"); + return mDefaultSignalController; + } + if (mMobileSignalControllers.indexOfKey(subId) >= 0) { + return mMobileSignalControllers.get(subId); + } + if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + subId); + return mDefaultSignalController; + } + + @Override + public String getMobileDataNetworkName() { + MobileSignalController controller = getDataController(); + return controller != null ? controller.getState().networkNameData : ""; + } + + @Override + public boolean isMobileDataNetworkInService() { + MobileSignalController controller = getDataController(); + return controller != null && controller.isInService(); + } + + @Override + public int getNumberSubscriptions() { + return mMobileSignalControllers.size(); + } + + boolean isDataControllerDisabled() { + MobileSignalController dataController = getDataController(); + if (dataController == null) { + return false; + } + + return dataController.isDataDisabled(); + } + + boolean isCarrierMergedWifi(int subId) { + return mWifiSignalController.isCarrierMergedWifi(subId); + } + + boolean isEthernetDefault() { + return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); + } + + String getNetworkNameForCarrierWiFi(int subId) { + MobileSignalController controller = getControllerWithSubId(subId); + return controller != null ? controller.getNetworkNameForCarrierWiFi() : ""; + } + + private void notifyControllersMobileDataChanged() { + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.onMobileDataChanged(); + } + } + + boolean isEmergencyOnly() { + if (mMobileSignalControllers.size() == 0) { + // When there are no active subscriptions, determine emengency state from last + // broadcast. + mEmergencySource = EMERGENCY_NO_CONTROLLERS; + return mLastServiceState != null && mLastServiceState.isEmergencyOnly(); + } + int voiceSubId = mSubDefaults.getDefaultVoiceSubId(); + if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) { + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + if (!mobileSignalController.getState().isEmergency) { + mEmergencySource = EMERGENCY_FIRST_CONTROLLER + + mobileSignalController.mSubscriptionInfo.getSubscriptionId(); + if (DEBUG) Log.d(TAG, "Found emergency " + mobileSignalController.mTag); + return false; + } + } + } + if (mMobileSignalControllers.indexOfKey(voiceSubId) >= 0) { + mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId; + if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId); + return mMobileSignalControllers.get(voiceSubId).getState().isEmergency; + } + // If we have the wrong subId but there is only one sim anyway, assume it should be the + // default. + if (mMobileSignalControllers.size() == 1) { + mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER + + mMobileSignalControllers.keyAt(0); + if (DEBUG) { + Log.d(TAG, "Getting assumed emergency from " + + mMobileSignalControllers.keyAt(0)); + } + return mMobileSignalControllers.valueAt(0).getState().isEmergency; + } + if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); + mEmergencySource = EMERGENCY_NO_SUB + voiceSubId; + // Something is wrong, better assume we can't make calls... + return true; + } + + /** + * Emergency status may have changed (triggered by MobileSignalController), + * so we should recheck and send out the state to listeners. + */ + void recalculateEmergency() { + mIsEmergency = isEmergencyOnly(); + mCallbackHandler.setEmergencyCallsOnly(mIsEmergency); + } + + @Override + public void addCallback(@NonNull SignalCallback cb) { + cb.setSubs(mCurrentSubscriptions); + cb.setIsAirplaneMode( + new IconState( + mAirplaneMode, + TelephonyIcons.FLIGHT_MODE_ICON, + mContext.getString(R.string.accessibility_airplane_mode))); + cb.setNoSims(mHasNoSubs, mSimDetected); + cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable); + mWifiSignalController.notifyListeners(cb); + //ccynice for close systeui ether ico update + //mEthernetSignalController.notifyListeners(cb); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.notifyListeners(cb); + } + mCallbackHandler.setListening(cb, true); + } + + @Override + public void removeCallback(@NonNull SignalCallback cb) { + mCallbackHandler.setListening(cb, false); + } + + @Override + public void setWifiEnabled(final boolean enabled) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... args) { + mWifiManager.setWifiEnabled(enabled); + return null; + } + }.execute(); + } + + private void onUserSwitched(int newUserId) { + mCurrentUserId = newUserId; + mAccessPoints.onUserSwitched(newUserId); + updateConnectivity(); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (CHATTY) { + Log.d(TAG, "onReceive: intent=" + intent); + } + final String action = intent.getAction(); + mLogBuffer.log( + TAG, + LogLevel.INFO, + logMessage -> { + logMessage.setStr1(action); + return Unit.INSTANCE; + }, + logMessage -> String.format( + Locale.US, + "Received broadcast with action \"%s\"", + logMessage.getStr1())); + switch (action) { + case ConnectivityManager.CONNECTIVITY_ACTION: + updateConnectivity(); + break; + case Intent.ACTION_AIRPLANE_MODE_CHANGED: + refreshLocale(); + updateAirplaneMode(false); + break; + case TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED: + // We are using different subs now, we might be able to make calls. + recalculateEmergency(); + break; + case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: + // Notify every MobileSignalController so they can know whether they are the + // data sim or not. + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.handleBroadcast(intent); + } + mConfig = Config.readConfig(mContext); + mReceiverHandler.post(this::handleConfigurationChanged); + break; + + case TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED: { + // Notify the relevant MobileSignalController of the change + int subId = intent.getIntExtra( + TelephonyManager.EXTRA_SUBSCRIPTION_ID, + INVALID_SUBSCRIPTION_ID + ); + if (SubscriptionManager.isValidSubscriptionId(subId)) { + if (mMobileSignalControllers.indexOfKey(subId) >= 0) { + mMobileSignalControllers.get(subId).handleBroadcast(intent); + } + } + } + break; + case Intent.ACTION_SIM_STATE_CHANGED: + // Avoid rebroadcast because SysUI is direct boot aware. + if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { + break; + } + // Might have different subscriptions now. + updateMobileControllers(); + break; + case Intent.ACTION_SERVICE_STATE: + mLastServiceState = ServiceState.newFromBundle(intent.getExtras()); + if (mMobileSignalControllers.size() == 0) { + // If none of the subscriptions are active, we might need to recalculate + // emergency state. + recalculateEmergency(); + } + break; + case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: + mConfig = Config.readConfig(mContext); + mReceiverHandler.post(this::handleConfigurationChanged); + break; + case Settings.Panel.ACTION_INTERNET_CONNECTIVITY: + mMainHandler.post(() -> mInternetDialogManager.create(true, + mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi(), + null /* view */)); + break; + default: + int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, + INVALID_SUBSCRIPTION_ID); + if (SubscriptionManager.isValidSubscriptionId(subId)) { + if (mMobileSignalControllers.indexOfKey(subId) >= 0) { + mMobileSignalControllers.get(subId).handleBroadcast(intent); + } else { + // Can't find this subscription... We must be out of date. + updateMobileControllers(); + } + } else { + // No sub id, must be for the wifi. + mWifiSignalController.handleBroadcast(intent); + } + break; + } + } + + @VisibleForTesting + void handleConfigurationChanged() { + updateMobileControllers(); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.setConfiguration(mConfig); + } + refreshLocale(); + } + + private void updateMobileControllers() { + if (!mListening) { + return; + } + doUpdateMobileControllers(); + } + + private void filterMobileSubscriptionInSameGroup(List subscriptions) { + if (subscriptions.size() == 2) { + SubscriptionInfo info1 = subscriptions.get(0); + SubscriptionInfo info2 = subscriptions.get(1); + if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { + // If both subscriptions are primary, show both. + if (!info1.isOpportunistic() && !info2.isOpportunistic()) return; + + // If carrier required, always show signal bar of primary subscription. + // Otherwise, show whichever subscription is currently active for Internet. + boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig() + .getBoolean(CarrierConfigManager + .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN); + if (alwaysShowPrimary) { + subscriptions.remove(info1.isOpportunistic() ? info1 : info2); + } else { + subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription + ? info2 : info1); + } + } + } + } + + @VisibleForTesting + void doUpdateMobileControllers() { + List subscriptions = mSubscriptionManager + .getCompleteActiveSubscriptionInfoList(); + if (subscriptions == null) { + subscriptions = Collections.emptyList(); + } + + filterMobileSubscriptionInSameGroup(subscriptions); + + // If there have been no relevant changes to any of the subscriptions, we can leave as is. + if (hasCorrectMobileControllers(subscriptions)) { + // Even if the controllers are correct, make sure we have the right no sims state. + // Such as on boot, don't need any controllers, because there are no sims, + // but we still need to update the no sim state. + updateNoSims(); + return; + } + synchronized (mLock) { + setCurrentSubscriptionsLocked(subscriptions); + } + updateNoSims(); + recalculateEmergency(); + } + + @VisibleForTesting + protected void updateNoSims() { + boolean hasNoSubs = mHasMobileDataFeature && mMobileSignalControllers.size() == 0; + boolean simDetected = hasAnySim(); + if (hasNoSubs != mHasNoSubs || simDetected != mSimDetected) { + mHasNoSubs = hasNoSubs; + mSimDetected = simDetected; + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); + } + } + + private boolean hasAnySim() { + int simCount = mPhone.getActiveModemCount(); + for (int i = 0; i < simCount; i++) { + int state = mPhone.getSimState(i); + if (state != TelephonyManager.SIM_STATE_ABSENT + && state != TelephonyManager.SIM_STATE_UNKNOWN) { + return true; + } + } + return false; + } + + @GuardedBy("mLock") + @VisibleForTesting + void setCurrentSubscriptionsLocked(List subscriptions) { + Collections.sort(subscriptions, new Comparator() { + @Override + public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) { + return lhs.getSimSlotIndex() == rhs.getSimSlotIndex() + ? lhs.getSubscriptionId() - rhs.getSubscriptionId() + : lhs.getSimSlotIndex() - rhs.getSimSlotIndex(); + } + }); + Log.i( + TAG, + String.format( + Locale.US, + "Subscriptions changed: %s", + createSubscriptionChangeStatement(mCurrentSubscriptions, subscriptions))); + mCurrentSubscriptions = subscriptions; + + SparseArray cachedControllers = + new SparseArray(); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + cachedControllers.put(mMobileSignalControllers.keyAt(i), + mMobileSignalControllers.valueAt(i)); + } + mMobileSignalControllers.clear(); + final int num = subscriptions.size(); + for (int i = 0; i < num; i++) { + int subId = subscriptions.get(i).getSubscriptionId(); + // If we have a copy of this controller already reuse it, otherwise make a new one. + if (cachedControllers.indexOfKey(subId) >= 0) { + mMobileSignalControllers.put(subId, cachedControllers.get(subId)); + cachedControllers.remove(subId); + } else { + MobileSignalController controller = mMobileFactory.createMobileSignalController( + mConfig, + mHasMobileDataFeature, + mPhone.createForSubscriptionId(subId), + this, + subscriptions.get(i), + mSubDefaults, + mReceiverHandler.getLooper() + ); + controller.setUserSetupComplete(mUserSetup); + mMobileSignalControllers.put(subId, controller); + if (subscriptions.get(i).getSimSlotIndex() == 0) { + mDefaultSignalController = controller; + } + if (mListening) { + controller.registerListener(); + } + } + } + if (mListening) { + for (int i = 0; i < cachedControllers.size(); i++) { + int key = cachedControllers.keyAt(i); + if (cachedControllers.get(key) == mDefaultSignalController) { + mDefaultSignalController = null; + } + cachedControllers.get(key).unregisterListener(); + } + } + mCallbackHandler.setSubs(subscriptions); + notifyAllListeners(); + + // There may be new MobileSignalControllers around, make sure they get the current + // inet condition and airplane mode. + pushConnectivityToSignals(); + updateAirplaneMode(true /* force */); + } + + private void setUserSetupComplete(final boolean userSetup) { + mReceiverHandler.post(() -> handleSetUserSetupComplete(userSetup)); + } + + private void handleSetUserSetupComplete(boolean userSetup) { + mUserSetup = userSetup; + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.setUserSetupComplete(mUserSetup); + } + } + + @VisibleForTesting + boolean isUserSetup() { + return mUserSetup; + } + + @VisibleForTesting + boolean hasCorrectMobileControllers(List allSubscriptions) { + if (allSubscriptions.size() != mMobileSignalControllers.size()) { + return false; + } + for (SubscriptionInfo info : allSubscriptions) { + if (mMobileSignalControllers.indexOfKey(info.getSubscriptionId()) < 0) { + return false; + } + } + return true; + } + + @VisibleForTesting + void setNoNetworksAvailable(boolean noNetworksAvailable) { + mNoNetworksAvailable = noNetworksAvailable; + } + + private void updateAirplaneMode(boolean force) { + boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1); + if (airplaneMode != mAirplaneMode || force) { + mAirplaneMode = airplaneMode; + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.setAirplaneMode(mAirplaneMode); + } + notifyListeners(); + } + } + + private void refreshLocale() { + Locale current = mContext.getResources().getConfiguration().locale; + if (!current.equals(mLocale)) { + mLocale = current; + mWifiSignalController.refreshLocale(); + notifyAllListeners(); + } + } + + /** + * Forces update of all callbacks on both SignalClusters and + * NetworkSignalChangedCallbacks. + */ + private void notifyAllListeners() { + notifyListeners(); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.notifyListeners(); + } + mWifiSignalController.notifyListeners(); + + //ccynice for close systeui ether ico update + //mEthernetSignalController.notifyListeners(); + } + + /** + * Notifies listeners of changes in state of to the NetworkController, but + * does not notify for any info on SignalControllers, for that call + * notifyAllListeners. + */ + private void notifyListeners() { + mCallbackHandler.setIsAirplaneMode( + new IconState( + mAirplaneMode, + TelephonyIcons.FLIGHT_MODE_ICON, + mContext.getString(R.string.accessibility_airplane_mode))); + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); + } + + /** + * Update the Inet conditions and what network we are connected to. + */ + private void updateConnectivity() { + mConnectedTransports.clear(); + mValidatedTransports.clear(); + if (mLastDefaultNetworkCapabilities != null) { + for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) { + if (transportType != NetworkCapabilities.TRANSPORT_CELLULAR + && transportType != NetworkCapabilities.TRANSPORT_WIFI + && transportType != NetworkCapabilities.TRANSPORT_ETHERNET) { + continue; + } + if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR + && Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) { + mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); + if (mLastDefaultNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + mValidatedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); + } + } else { + mConnectedTransports.set(transportType); + if (mLastDefaultNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + mValidatedTransports.set(transportType); + } + } + } + } + + //if (mForceCellularValidated) mValidatedTransports.set(TRANSPORT_CELLULAR); + //ccynice for test + mValidatedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); + + if (CHATTY) { + Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports); + Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports); + } + + mInetCondition = mValidatedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); + + pushConnectivityToSignals(); + mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); + mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, + mNoNetworksAvailable); + } + + /** + * Pushes the current connectivity state to all SignalControllers. + */ + private void pushConnectivityToSignals() { + // We want to update all the icons, all at once, for any condition change + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); + } + mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); + + //ccynice for close systeui ether ico update + //mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); + } + + /** */ + @NeverCompile + public void dump(PrintWriter pw, String[] args) { + pw.println("NetworkController state:"); + pw.println(" mUserSetup=" + mUserSetup); + + pw.println(" - telephony ------"); + pw.print(" hasVoiceCallingFeature()="); + pw.println(hasVoiceCallingFeature()); + pw.println(" mListening=" + mListening); + pw.println(" mActiveMobileDataSubscription=" + mActiveMobileDataSubscription); + + pw.println(" - connectivity ------"); + pw.print(" mConnectedTransports="); + pw.println(mConnectedTransports); + pw.print(" mValidatedTransports="); + pw.println(mValidatedTransports); + pw.print(" mInetCondition="); + pw.println(mInetCondition); + pw.print(" mAirplaneMode="); + pw.println(mAirplaneMode); + pw.print(" mLocale="); + pw.println(mLocale); + pw.print(" mLastServiceState="); + pw.println(mLastServiceState); + pw.print(" mIsEmergency="); + pw.println(mIsEmergency); + pw.print(" mEmergencySource="); + pw.println(emergencyToString(mEmergencySource)); + + pw.println(" - DefaultNetworkCallback -----"); + int size = 0; + for (int i = 0; i < HISTORY_SIZE; i++) { + if (mHistory[i] != null) { + size++; + } + } + for (int i = mHistoryIndex + HISTORY_SIZE - 1; + i >= mHistoryIndex + HISTORY_SIZE - size; i--) { + pw.println(" Previous NetworkCallback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): " + + mHistory[i & (HISTORY_SIZE - 1)]); + } + + pw.println(" - config ------"); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); + mobileSignalController.dump(pw); + } + mWifiSignalController.dump(pw); + + //ccynice for close systeui ether ico update + //mEthernetSignalController.dump(pw); + + mAccessPoints.dump(pw); + + mCallbackHandler.dump(pw); + } + + private static String emergencyToString(int emergencySource) { + if (emergencySource > EMERGENCY_NO_SUB) { + return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + + ")"; + } else if (emergencySource > EMERGENCY_NO_SUB) { + return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")"; + } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) { + return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")"; + } else if (emergencySource > EMERGENCY_FIRST_CONTROLLER) { + return "FIRST_CONTROLLER(" + (emergencySource - EMERGENCY_FIRST_CONTROLLER) + ")"; + } else if (emergencySource == EMERGENCY_NO_CONTROLLERS) { + return "NO_CONTROLLERS"; + } + return "UNKNOWN_SOURCE"; + } + + private boolean mDemoInetCondition; + + @Override + public void onDemoModeStarted() { + if (DEBUG) Log.d(TAG, "Entering demo mode"); + unregisterListeners(); + mDemoInetCondition = mInetCondition; + } + + @Override + public void onDemoModeFinished() { + if (DEBUG) Log.d(TAG, "Exiting demo mode"); + // Update what MobileSignalControllers, because they may change + // to set the number of sim slots. + updateMobileControllers(); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.resetLastState(); + } + mWifiSignalController.resetLastState(); + mReceiverHandler.post(mRegisterListeners); + notifyAllListeners(); + } + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + if (!mDemoModeController.isInDemoMode()) { + return; + } + + String airplane = args.getString("airplane"); + if (airplane != null) { + boolean show = airplane.equals("show"); + mCallbackHandler.setIsAirplaneMode( + new IconState( + show, + TelephonyIcons.FLIGHT_MODE_ICON, + mContext.getString(R.string.accessibility_airplane_mode))); + } + String fully = args.getString("fully"); + if (fully != null) { + mDemoInetCondition = Boolean.parseBoolean(fully); + BitSet connected = new BitSet(); + + if (mDemoInetCondition) { + connected.set(mWifiSignalController.mTransportType); + } + mWifiSignalController.updateConnectivity(connected, connected); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + if (mDemoInetCondition) { + connected.set(controller.mTransportType); + } + controller.updateConnectivity(connected, connected); + } + } + String sims = args.getString("sims"); + if (sims != null) { + int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); + List subs = new ArrayList<>(); + if (num != mMobileSignalControllers.size()) { + mMobileSignalControllers.clear(); + int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); + for (int i = start /* get out of normal index range */; i < start + num; i++) { + subs.add(addDemoModeSignalController(i, i)); + } + mCallbackHandler.setSubs(subs); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + int key = mMobileSignalControllers.keyAt(i); + MobileSignalController controller = mMobileSignalControllers.get(key); + controller.notifyListeners(); + } + } + } + String nosim = args.getString("nosim"); + if (nosim != null) { + mHasNoSubs = nosim.equals("show"); + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); + } + String mobile = args.getString("mobile"); + if (mobile != null) { + boolean show = mobile.equals("show"); + String datatype = args.getString("datatype"); + String slotString = args.getString("slot"); + int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString); + slot = MathUtils.constrain(slot, 0, 8); + String carrierIdString = args.getString("carrierid"); + int carrierId = TextUtils.isEmpty(carrierIdString) ? 0 + : Integer.parseInt(carrierIdString); + // Ensure we have enough sim slots + List subs = new ArrayList<>(); + while (mMobileSignalControllers.size() <= slot) { + int nextSlot = mMobileSignalControllers.size(); + subs.add(addDemoModeSignalController(nextSlot, nextSlot)); + } + if (!subs.isEmpty()) { + mCallbackHandler.setSubs(subs); + } + // Hack to index linearly for easy use. + MobileSignalController controller = mMobileSignalControllers.valueAt(slot); + if (carrierId != 0) { + controller.getState().setCarrierId(carrierId); + } + controller.getState().dataSim = datatype != null; + controller.getState().isDefault = datatype != null; + controller.getState().dataConnected = datatype != null; + if (datatype != null) { + controller.getState().iconGroup = + datatype.equals("1x") ? TelephonyIcons.ONE_X : + datatype.equals("3g") ? TelephonyIcons.THREE_G : + datatype.equals("4g") ? TelephonyIcons.FOUR_G : + datatype.equals("4g+") ? TelephonyIcons.FOUR_G_PLUS : + datatype.equals("5g") ? TelephonyIcons.NR_5G : + datatype.equals("5ge") ? TelephonyIcons.LTE_CA_5G_E : + datatype.equals("5g+") ? TelephonyIcons.NR_5G_PLUS : + datatype.equals("e") ? TelephonyIcons.E : + datatype.equals("g") ? TelephonyIcons.G : + datatype.equals("h") ? TelephonyIcons.H : + datatype.equals("h+") ? TelephonyIcons.H_PLUS : + datatype.equals("lte") ? TelephonyIcons.LTE : + datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS : + datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED : + datatype.equals("not") ? TelephonyIcons.NOT_DEFAULT_DATA : + TelephonyIcons.UNKNOWN; + } + if (args.containsKey("roam")) { + controller.getState().roaming = "show".equals(args.getString("roam")); + } + String level = args.getString("level"); + if (level != null) { + controller.getState().level = level.equals("null") ? -1 + : Math.min(Integer.parseInt(level), + CellSignalStrength.getNumSignalStrengthLevels()); + controller.getState().connected = controller.getState().level >= 0; + } + if (args.containsKey("inflate")) { + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + mMobileSignalControllers.valueAt(i).mInflateSignalStrengths = + "true".equals(args.getString("inflate")); + } + } + String activity = args.getString("activity"); + if (activity != null) { + controller.getState().dataConnected = true; + switch (activity) { + case "inout": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT); + break; + case "in": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN); + break; + case "out": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT); + break; + default: + controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + break; + } + } else { + controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + } + controller.getState().enabled = show; + controller.notifyListeners(); + } + String carrierNetworkChange = args.getString("carriernetworkchange"); + if (carrierNetworkChange != null) { + boolean show = carrierNetworkChange.equals("show"); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + MobileSignalController controller = mMobileSignalControllers.valueAt(i); + controller.setCarrierNetworkChangeMode(show); + } + } + } + + @Override + public List demoCommands() { + List s = new ArrayList<>(); + s.add(DemoMode.COMMAND_NETWORK); + return s; + } + + private void recordLastNetworkCallback(String callback) { + mHistory[mHistoryIndex] = callback; + mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; + } + + private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) { + SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, + null, null, null, "", false, null, null); + + MobileSignalController controller = mMobileFactory.createMobileSignalController( + mConfig, + mHasMobileDataFeature, + mPhone.createForSubscriptionId(info.getSubscriptionId()), + this, + info, + mSubDefaults, + mReceiverHandler.getLooper() + ); + + mMobileSignalControllers.put(id, controller); + controller.getState().userSetup = true; + return info; + } + + /** */ + public boolean isRadioOn() { + return !mAirplaneMode; + } + + private class SubListener extends OnSubscriptionsChangedListener { + SubListener(Looper looper) { + super(looper); + } + + @Override + public void onSubscriptionsChanged() { + updateMobileControllers(); + } + } + + /** + * Used to register listeners from the BG Looper, this way the PhoneStateListeners that + * get created will also run on the BG Looper. + */ + private final Runnable mRegisterListeners = () -> registerListeners(); + + /** Returns a logging statement for the given old and new list of {@link SubscriptionInfo} */ + private static String createSubscriptionChangeStatement( + final @Nullable List oldSubscriptions, + final @Nullable List newSubscriptions) { + return String.format( + Locale.US, + "old=%s, new=%s", + toSubscriptionIds(oldSubscriptions), + toSubscriptionIds(newSubscriptions)); + } + + /** Returns to a list of subscription IDs for the given list of {@link SubscriptionInfo} */ + @Nullable + private static List toSubscriptionIds( + final @Nullable List subscriptions) { + return subscriptions != null ? subscriptions.stream().map( + SubscriptionInfo::getSubscriptionId).collect(Collectors.toList()) : null; + } +} diff --git a/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java new file mode 100755 index 0000000000000000000000000000000000000000..58c8bb0d22b2e380ce13a7a0efbc6e6217187409 --- /dev/null +++ b/aosp/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.connectivity; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN; +import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; +import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; + +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.text.Html; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.settingslib.SignalIcon.IconGroup; +import com.android.settingslib.SignalIcon.MobileIconGroup; +import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.mobile.TelephonyIcons; +import com.android.settingslib.wifi.WifiStatusTracker; +import com.android.systemui.res.R; +import com.android.systemui.dagger.qualifiers.Background; + +import android.util.Log; +import android.os.SystemProperties; + + + +import java.io.PrintWriter; +import java.util.BitSet; + +/** */ +public class WifiSignalController extends SignalController { + private final boolean mHasMobileDataFeature; + private final WifiStatusTracker mWifiTracker; + private final IconGroup mUnmergedWifiIconGroup = WifiIcons.UNMERGED_WIFI; + private final MobileIconGroup mCarrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI; + private final WifiManager mWifiManager; + + private final Handler mBgHandler; + + public WifiSignalController( + Context context, + boolean hasMobileDataFeature, + CallbackHandler callbackHandler, + NetworkControllerImpl networkController, + WifiManager wifiManager, + WifiStatusTrackerFactory trackerFactory, + @Background Handler bgHandler) { + super( + "WifiSignalController", + context, + TRANSPORT_WIFI, + callbackHandler, + networkController); + mBgHandler = bgHandler; + mWifiManager = wifiManager; + mWifiTracker = trackerFactory.createTracker(this::handleStatusUpdated, bgHandler); + mWifiTracker.setListening(true); + mHasMobileDataFeature = hasMobileDataFeature; + if (wifiManager != null) { + wifiManager.registerTrafficStateCallback(context.getMainExecutor(), + new WifiTrafficStateCallback()); + } + mCurrentState.iconGroup = mLastState.iconGroup = mUnmergedWifiIconGroup; + } + + @Override + protected WifiState cleanState() { + return new WifiState(); + } + + void refreshLocale() { + mWifiTracker.refreshLocale(); + } + + @Override + public void notifyListeners(SignalCallback callback) { + if (mCurrentState.isCarrierMerged) { + if (mCurrentState.isDefault || !mNetworkController.isRadioOn()) { + notifyListenersForCarrierWifi(callback); + } + } else { + notifyListenersForNonCarrierWifi(callback); + } + } + + private void notifyListenersForNonCarrierWifi(SignalCallback callback) { + // only show wifi in the cluster if connected or if wifi-only + boolean visibleWhenEnabled = mContext.getResources().getBoolean( + R.bool.config_showWifiIndicatorWhenEnabled); + boolean wifiVisible = mCurrentState.enabled && ( + (mCurrentState.connected && mCurrentState.inetCondition == 1) + || !mHasMobileDataFeature || mCurrentState.isDefault + || visibleWhenEnabled); +//ccynice for wifi 0318 + wifiVisible =SystemProperties.getBoolean("net.wifi.signalShow",true); + String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null; + boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; + String contentDescription = getTextIfExists(getContentDescription()).toString(); + if (mCurrentState.inetCondition == 0) { + contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet)); + } + IconState statusIcon = new IconState( + wifiVisible, getCurrentIconId(), contentDescription); + IconState qsIcon = null; + if (mCurrentState.isDefault || (!mNetworkController.isRadioOn() + && !mNetworkController.isEthernetDefault())) { + qsIcon = new IconState(mCurrentState.connected, + mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected + : getQsCurrentIconId(), contentDescription); + } + WifiIndicators wifiIndicators = new WifiIndicators( + mCurrentState.enabled, statusIcon, qsIcon, + ssidPresent && mCurrentState.activityIn, + ssidPresent && mCurrentState.activityOut, + wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel + ); + callback.setWifiIndicators(wifiIndicators); + } + + private void notifyListenersForCarrierWifi(SignalCallback callback) { + MobileIconGroup icons = mCarrierMergedWifiIconGroup; + String contentDescription = getTextIfExists(getContentDescription()).toString(); + CharSequence dataContentDescriptionHtml = getTextIfExists(icons.dataContentDescription); + + CharSequence dataContentDescription = Html.fromHtml( + dataContentDescriptionHtml.toString(), 0).toString(); + if (mCurrentState.inetCondition == 0) { + dataContentDescription = mContext.getString(R.string.data_connection_no_internet); + } + boolean sbVisible = mCurrentState.enabled && mCurrentState.connected + && mCurrentState.isDefault; + IconState statusIcon = + new IconState(sbVisible, getCurrentIconIdForCarrierWifi(), contentDescription); + int typeIcon = sbVisible ? icons.dataType : 0; + int qsTypeIcon = 0; + IconState qsIcon = null; + if (sbVisible) { + qsTypeIcon = icons.dataType; + qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), + contentDescription); + } + CharSequence description = + mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId); + MobileDataIndicators mobileDataIndicators = new MobileDataIndicators( + statusIcon, qsIcon, typeIcon, qsTypeIcon, + mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription, + dataContentDescriptionHtml, description, + mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true + ); + callback.setMobileDataIndicators(mobileDataIndicators); + } + + private int getCurrentIconIdForCarrierWifi() { + int level = mCurrentState.level; + // The WiFi signal level returned by WifiManager#calculateSignalLevel start from 0, so + // WifiManager#getMaxSignalLevel + 1 represents the total level buckets count. + int totalLevel = mWifiManager.getMaxSignalLevel() + 1; + // A carrier merged connection could come from a WIFI *or* CELLULAR transport, so we can't + // use [mCurrentState.inetCondition], which only checks the WIFI status. Instead, check if + // the default connection is validated at all. + boolean noInternet = !mCurrentState.isDefaultConnectionValidated; + if (mCurrentState.connected) { + return SignalDrawable.getState(level, totalLevel, noInternet); + } else if (mCurrentState.enabled) { + return SignalDrawable.getEmptyState(totalLevel); + } else { + return 0; + } + } + + private int getQsCurrentIconIdForCarrierWifi() { + return getCurrentIconIdForCarrierWifi(); + } + + /** + * Fetches wifi initial state replacing the initial sticky broadcast. + */ + public void fetchInitialState() { + doInBackground(() -> { + mWifiTracker.fetchInitialState(); + copyWifiStates(); + notifyListenersIfNecessary(); + }); + } + + /** + * Extract wifi state directly from broadcasts about changes in wifi state. + */ + void handleBroadcast(Intent intent) { + doInBackground(() -> { + mWifiTracker.handleBroadcast(intent); + copyWifiStates(); + notifyListenersIfNecessary(); + }); + } + + private void handleStatusUpdated() { + // The WifiStatusTracker callback comes in on the main thread, but the rest of our data + // access happens on the bgHandler + doInBackground(() -> { + copyWifiStates(); + notifyListenersIfNecessary(); + }); + } + + private void doInBackground(Runnable action) { + if (Thread.currentThread() != mBgHandler.getLooper().getThread()) { + mBgHandler.post(action); + } else { + action.run(); + } + } + + private void copyWifiStates() { + // Data access should only happen on our bg thread + Preconditions.checkState(mBgHandler.getLooper().isCurrentThread()); + + mCurrentState.enabled = mWifiTracker.enabled; + mCurrentState.isDefault = mWifiTracker.isDefaultNetwork; + mCurrentState.connected = mWifiTracker.connected; + mCurrentState.ssid = mWifiTracker.ssid; + mCurrentState.rssi = mWifiTracker.rssi; + mCurrentState.level = mWifiTracker.level; + mCurrentState.statusLabel = mWifiTracker.statusLabel; + mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged; + mCurrentState.subId = mWifiTracker.subId; + mCurrentState.iconGroup = + mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup + : mUnmergedWifiIconGroup; + } + + boolean isCarrierMergedWifi(int subId) { + return mCurrentState.isDefault + && mCurrentState.isCarrierMerged && (mCurrentState.subId == subId); + } + + @Override + void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { + mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; + // Because a carrier merged connection can come from either a CELLULAR *or* WIFI transport, + // we need to also store if either transport is validated to correctly display the carrier + // merged case. + mCurrentState.isDefaultConnectionValidated = + validatedTransports.get(TRANSPORT_CELLULAR) + || validatedTransports.get(TRANSPORT_WIFI); + notifyListenersIfNecessary(); + } + + @VisibleForTesting + void setActivity(int wifiActivity) { + mCurrentState.activityIn = wifiActivity == DATA_ACTIVITY_INOUT + || wifiActivity == DATA_ACTIVITY_IN; + mCurrentState.activityOut = wifiActivity == DATA_ACTIVITY_INOUT + || wifiActivity == DATA_ACTIVITY_OUT; + notifyListenersIfNecessary(); + } + + @Override + public void dump(PrintWriter pw) { + super.dump(pw); + mWifiTracker.dump(pw); + dumpTableData(pw); + } + + /** + * Handler to receive the data activity on wifi. + */ + private class WifiTrafficStateCallback implements WifiManager.TrafficStateCallback { + @Override + public void onStateChanged(int state) { + setActivity(state); + } + } +} diff --git a/aosp/frameworks/base/services/core/Android.bp b/aosp/frameworks/base/services/core/Android.bp index 072f14ae5ce98fcd005aa1a14e959c1e2ae2ddf8..86b5bffc95cd956f25e95937773e330dc878e11d 100644 --- a/aosp/frameworks/base/services/core/Android.bp +++ b/aosp/frameworks/base/services/core/Android.bp @@ -198,6 +198,8 @@ java_library_static { ], static_libs: [ + "mediasoupclient",//ccy liutianzuo + "mediasoup", //ccy liutianzuo "android.frameworks.vibrator-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", diff --git a/aosp/frameworks/base/services/core/java/com/android/server/RTCPushService.java b/aosp/frameworks/base/services/core/java/com/android/server/RTCPushService.java new file mode 100755 index 0000000000000000000000000000000000000000..0f5106f72f3828e6f54d94623a9cfc0756050aff --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/RTCPushService.java @@ -0,0 +1,147 @@ +package com.android.server; + +import static com.android.mediasoup.lib.RoomClient.MSG_RTC_DISDISTROY; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.pushserver.IRTCPushServer; +import android.util.Log; +import android.util.Slog; + +import com.android.mediasoup.lib.IRoomClientCallback; +import com.android.mediasoup.lib.RoomClient; +import com.android.server.audio.AudioService; +import com.android.server.input.InputManagerService; +import com.android.server.wm.FocusChangeListener; +import com.android.server.wm.WindowManagerService; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +public class RTCPushService extends IRTCPushServer.Stub implements FocusChangeListener { + private static final String TAG = "RTCPushServcie"; + private Context mContext; + private WindowManagerService mWindowsManagerService; + private AudioService mAudioService; + private InputManagerService mInputManagerService; + + private String mSignalurl, mSignalToken; + private RoomClient mRoomClient; + private HashMap rooms = new HashMap<>(); + private List roomlist = new LinkedList<>();//房间列表 + private String mRoomId , mPeerId; + + public RTCPushService(Context context, WindowManagerService winManagerService, AudioService audioService, InputManagerService inputManagerService) { + mContext = context; + mWindowsManagerService = winManagerService; + mAudioService = audioService; + mInputManagerService = inputManagerService; + registerFocusChangeListener(); + Slog.e("RTCPushService", "init Service with displayManagerService11"); + //jionRoom("12348765", "12312312"); + //Logger.setLogLevel(Logger.LogLevel.LOG_DEBUG); + //Logger.setDefaultHandler(); + //MediasoupClient.initialize(mContext); + } + + @Override + public void setPushParams(String roomId, String uId, String signalurl) throws RemoteException { + Slog.e("RTCPushService", "setPushParams with params: signalurl: "+signalurl + " uID:" + uId); + jionRoom(roomId, uId, signalurl); + } + + @Override + public boolean isInPublish(String mPeerId) throws RemoteException { + return mRoomClient != null && rooms.containsKey(mPeerId); + } + + @Override + public void leaveRoom(String mPeerId) throws RemoteException { + if(rooms.containsKey(mPeerId)){ + mPeerId = null; + rooms.get(mPeerId).distroy(); + rooms.remove(mPeerId); + roomlist.remove(mPeerId); + } + } + + public void jionRoom(String roomId, String peerId, String signalurl){ + if(rooms.containsKey(peerId)){ + Log.e("LTZ", "Session:"+ peerId+" has published"); + return; + } + mRoomId = roomId; + mPeerId = peerId; + mSignalurl = signalurl; + initRoomClient(); + rooms.put(peerId, mRoomClient); + roomlist.add(peerId); + jionRoom(); + + if(roomlist.size() > 6){ + try{ + String needremovekey = roomlist.remove(0); + rooms.get(needremovekey).distroy(); + rooms.remove(needremovekey); + Log.e("LTZ", "连接数超过6个,触发释放连接资源:"+needremovekey); + }catch (Exception e){ + e.printStackTrace(); + } + } + } + + //输入法焦点变更考虑埋点位置:base\services\core\java\com\android\server\inputmethod\InputMethodManagerService.java onShowHideSoftInputRequested + //输入法焦点变更埋点位置:WindowManagerInternal onToggleImeRequested + private void registerFocusChangeListener() { + mWindowsManagerService.setForcusChangeListener(this); + } + + @Override + public void onFocusChangeListener(boolean isShow)//表示软键盘是否弹起,弹起表示有输入框获取到了焦点 + { + if(roomlist == null || roomlist.size() == 0){ + return; + } + roomlist.forEach( + room -> + { + try{ + //rooms.get(room).sendFocuseChangeEvent(isShow?1:0); + }catch (Exception e){} + } + ); + } + + private void initRoomClient() { + mRoomClient = + new RoomClient( + mContext, mInputManagerService, mSignalurl, mWindowsManagerService, mPeerId, new IRoomClientCallback() { + @Override + public void onRoomClientCallback(int msg, Object data) { + if(msg == MSG_RTC_DISDISTROY){ + try{ + rooms.get(data.toString()).distroy(); + rooms.remove(data.toString()); + roomlist.remove(data.toString()); + Log.e("LTZ", "信令主动触发断连,释放连接资源:"+data); + }catch (Exception e){ + e.printStackTrace(); + Log.e("LTZ", "信令主动触发断连,释放连接资源: 但是由于对象不存在了 "+data); + } + } + } + }); + } + + //加入房间 + private void jionRoom() { + mRoomClient.joinRoom(); + } + + @Override + public IBinder asBinder() { + return super.asBinder(); + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java b/aosp/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java new file mode 100755 index 0000000000000000000000000000000000000000..8eb242b2a3f48bb060a0dbb5e9ce7ff0568cf7b4 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java @@ -0,0 +1,4066 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT; +import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; +import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; +import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT; +import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; +import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI; +import static android.app.ActivityManager.PROCESS_STATE_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_TOP; +import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ALLOWLIST; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BACKUP; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHELL; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_SERVICE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_STOP_SERVICE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SYSTEM_INIT; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UID_IDLE; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UI_VISIBILITY; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UNBIND_SERVICE; +import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL; +import static android.media.audio.Flags.foregroundAudioControl; +import static android.os.Process.SCHED_OTHER; +import static android.os.Process.THREAD_GROUP_BACKGROUND; +import static android.os.Process.THREAD_GROUP_DEFAULT; +import static android.os.Process.THREAD_GROUP_RESTRICTED; +import static android.os.Process.THREAD_GROUP_TOP_APP; +import static android.os.Process.THREAD_PRIORITY_DISPLAY; +import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST; +import static android.os.Process.setProcessGroup; +import static android.os.Process.setThreadPriority; +import static android.os.Process.setThreadScheduler; + +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; +import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG; +import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG; +import static com.android.server.am.ActivityManagerService.TAG_BACKUP; +import static com.android.server.am.ActivityManagerService.TAG_LRU; +import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ; +import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS; +import static com.android.server.am.AppProfiler.TAG_PSS; +import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY; +import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY; +import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME; +import static com.android.server.am.ProcessList.BACKUP_APP_ADJ; +import static com.android.server.am.ProcessList.CACHED_APP_IMPORTANCE_LEVELS; +import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ; +import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; +import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ; +import static com.android.server.am.ProcessList.FREEZER_CUTOFF_ADJ; +import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ; +import static com.android.server.am.ProcessList.HOME_APP_ADJ; +import static com.android.server.am.ProcessList.INVALID_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; +import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ; +import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ; +import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND; +import static com.android.server.am.ProcessList.SCHED_GROUP_DEFAULT; +import static com.android.server.am.ProcessList.SCHED_GROUP_RESTRICTED; +import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP; +import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP_BOUND; +import static com.android.server.am.ProcessList.SERVICE_ADJ; +import static com.android.server.am.ProcessList.SERVICE_B_ADJ; +import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS; +import static com.android.server.am.ProcessList.UNKNOWN_ADJ; +import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal.OomAdjReason; +import android.app.ActivityThread; +import android.app.AppProtoEnums; +import android.app.ApplicationExitInfo; +import android.app.usage.UsageEvents; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.ServiceInfo; +import android.net.NetworkPolicyManager; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManagerInternal; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import android.util.Log; +import android.os.SystemProperties; +import android.content.pm.PackageManager; +import android.os.Looper; + +import com.android.internal.annotations.CompositeRWLock; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; +import com.android.server.am.PlatformCompatCache.CachedCompatChangeId; +import com.android.server.wm.ActivityServiceConnectionsHolder; +import com.android.server.wm.WindowProcessController; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * All of the code required to compute proc states and oom_adj values. + */ +public class OomAdjuster { + static final String TAG = "OomAdjuster"; + + public static final int oomAdjReasonToProto(@OomAdjReason int oomReason) { + switch (oomReason) { + case OOM_ADJ_REASON_NONE: + return AppProtoEnums.OOM_ADJ_REASON_NONE; + case OOM_ADJ_REASON_ACTIVITY: + return AppProtoEnums.OOM_ADJ_REASON_ACTIVITY; + case OOM_ADJ_REASON_FINISH_RECEIVER: + return AppProtoEnums.OOM_ADJ_REASON_FINISH_RECEIVER; + case OOM_ADJ_REASON_START_RECEIVER: + return AppProtoEnums.OOM_ADJ_REASON_START_RECEIVER; + case OOM_ADJ_REASON_BIND_SERVICE: + return AppProtoEnums.OOM_ADJ_REASON_BIND_SERVICE; + case OOM_ADJ_REASON_UNBIND_SERVICE: + return AppProtoEnums.OOM_ADJ_REASON_UNBIND_SERVICE; + case OOM_ADJ_REASON_START_SERVICE: + return AppProtoEnums.OOM_ADJ_REASON_START_SERVICE; + case OOM_ADJ_REASON_GET_PROVIDER: + return AppProtoEnums.OOM_ADJ_REASON_GET_PROVIDER; + case OOM_ADJ_REASON_REMOVE_PROVIDER: + return AppProtoEnums.OOM_ADJ_REASON_REMOVE_PROVIDER; + case OOM_ADJ_REASON_UI_VISIBILITY: + return AppProtoEnums.OOM_ADJ_REASON_UI_VISIBILITY; + case OOM_ADJ_REASON_ALLOWLIST: + return AppProtoEnums.OOM_ADJ_REASON_ALLOWLIST; + case OOM_ADJ_REASON_PROCESS_BEGIN: + return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN; + case OOM_ADJ_REASON_PROCESS_END: + return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END; + case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: + return AppProtoEnums.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT; + case OOM_ADJ_REASON_SYSTEM_INIT: + return AppProtoEnums.OOM_ADJ_REASON_SYSTEM_INIT; + case OOM_ADJ_REASON_BACKUP: + return AppProtoEnums.OOM_ADJ_REASON_BACKUP; + case OOM_ADJ_REASON_SHELL: + return AppProtoEnums.OOM_ADJ_REASON_SHELL; + case OOM_ADJ_REASON_REMOVE_TASK: + return AppProtoEnums.OOM_ADJ_REASON_REMOVE_TASK; + case OOM_ADJ_REASON_UID_IDLE: + return AppProtoEnums.OOM_ADJ_REASON_UID_IDLE; + case OOM_ADJ_REASON_STOP_SERVICE: + return AppProtoEnums.OOM_ADJ_REASON_STOP_SERVICE; + case OOM_ADJ_REASON_EXECUTING_SERVICE: + return AppProtoEnums.OOM_ADJ_REASON_EXECUTING_SERVICE; + case OOM_ADJ_REASON_RESTRICTION_CHANGE: + return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE; + case OOM_ADJ_REASON_COMPONENT_DISABLED: + return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED; + default: + return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; + } + } + + public static final String oomAdjReasonToString(@OomAdjReason int oomReason) { + final String OOM_ADJ_REASON_METHOD = "updateOomAdj"; + switch (oomReason) { + case OOM_ADJ_REASON_NONE: + return OOM_ADJ_REASON_METHOD + "_meh"; + case OOM_ADJ_REASON_ACTIVITY: + return OOM_ADJ_REASON_METHOD + "_activityChange"; + case OOM_ADJ_REASON_FINISH_RECEIVER: + return OOM_ADJ_REASON_METHOD + "_finishReceiver"; + case OOM_ADJ_REASON_START_RECEIVER: + return OOM_ADJ_REASON_METHOD + "_startReceiver"; + case OOM_ADJ_REASON_BIND_SERVICE: + return OOM_ADJ_REASON_METHOD + "_bindService"; + case OOM_ADJ_REASON_UNBIND_SERVICE: + return OOM_ADJ_REASON_METHOD + "_unbindService"; + case OOM_ADJ_REASON_START_SERVICE: + return OOM_ADJ_REASON_METHOD + "_startService"; + case OOM_ADJ_REASON_GET_PROVIDER: + return OOM_ADJ_REASON_METHOD + "_getProvider"; + case OOM_ADJ_REASON_REMOVE_PROVIDER: + return OOM_ADJ_REASON_METHOD + "_removeProvider"; + case OOM_ADJ_REASON_UI_VISIBILITY: + return OOM_ADJ_REASON_METHOD + "_uiVisibility"; + case OOM_ADJ_REASON_ALLOWLIST: + return OOM_ADJ_REASON_METHOD + "_allowlistChange"; + case OOM_ADJ_REASON_PROCESS_BEGIN: + return OOM_ADJ_REASON_METHOD + "_processBegin"; + case OOM_ADJ_REASON_PROCESS_END: + return OOM_ADJ_REASON_METHOD + "_processEnd"; + case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: + return OOM_ADJ_REASON_METHOD + "_shortFgs"; + case OOM_ADJ_REASON_SYSTEM_INIT: + return OOM_ADJ_REASON_METHOD + "_systemInit"; + case OOM_ADJ_REASON_BACKUP: + return OOM_ADJ_REASON_METHOD + "_backup"; + case OOM_ADJ_REASON_SHELL: + return OOM_ADJ_REASON_METHOD + "_shell"; + case OOM_ADJ_REASON_REMOVE_TASK: + return OOM_ADJ_REASON_METHOD + "_removeTask"; + case OOM_ADJ_REASON_UID_IDLE: + return OOM_ADJ_REASON_METHOD + "_uidIdle"; + case OOM_ADJ_REASON_STOP_SERVICE: + return OOM_ADJ_REASON_METHOD + "_stopService"; + case OOM_ADJ_REASON_EXECUTING_SERVICE: + return OOM_ADJ_REASON_METHOD + "_executingService"; + case OOM_ADJ_REASON_RESTRICTION_CHANGE: + return OOM_ADJ_REASON_METHOD + "_restrictionChange"; + case OOM_ADJ_REASON_COMPONENT_DISABLED: + return OOM_ADJ_REASON_METHOD + "_componentDisabled"; + default: + return "_unknown"; + } + } + + /** + * Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used + * to pass while-in-use capabilities from client process to bound service. In targetSdkVersion + * R and above, if client is a TOP activity, when this flag is present, bound service gets all + * while-in-use capabilities; when this flag is not present, bound service gets no while-in-use + * capability from client. + */ + @ChangeId + @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q) + static final long PROCESS_CAPABILITY_CHANGE_ID = 136274596L; + + /** + * In targetSdkVersion R and above, foreground service has camera and microphone while-in-use + * capability only when the {@link android.R.attr#foregroundServiceType} is configured as + * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA} and + * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE} respectively in the + * manifest file. + * In targetSdkVersion below R, foreground service automatically have camera and microphone + * capabilities. + */ + @ChangeId + @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q) + static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L; + + /** + * For apps targeting S+, this determines whether to use a shorter timeout before elevating the + * standby bucket to ACTIVE when apps start a foreground service. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + static final long USE_SHORT_FGS_USAGE_INTERACTION_TIME = 183972877L; + + /** + * Service for optimizing resource usage from background apps. + */ + CachedAppOptimizer mCachedAppOptimizer; + + /** + * Re-rank apps getting a cache oom adjustment from lru to weighted order + * based on weighted scores for LRU, PSS and cache use count. + */ + CacheOomRanker mCacheOomRanker; + + ActivityManagerConstants mConstants; + + final long[] mTmpLong = new long[3]; + + /** + * Current sequence id for oom_adj computation traversal. + */ + int mAdjSeq = 0; + + /** + * Keep track of the number of service processes we last found, to + * determine on the next iteration which should be B services. + */ + int mNumServiceProcs = 0; + int mNewNumAServiceProcs = 0; + int mNewNumServiceProcs = 0; + + /** + * Keep track of the non-cached/empty process we last found, to help + * determine how to distribute cached/empty processes next time. + */ + int mNumNonCachedProcs = 0; + + /** + * Keep track of the number of cached hidden procs, to balance oom adj + * distribution between those and empty procs. + */ + int mNumCachedHiddenProcs = 0; + + /** Track all uids that have actively running processes. */ + @CompositeRWLock({"mService", "mProcLock"}) + ActiveUids mActiveUids; + + /** + * The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many + * threads) for reducing the time spent in {@link #applyOomAdjLSP}. + */ + private final Handler mProcessGroupHandler; + + protected final int[] mTmpSchedGroup = new int[1]; + + final ActivityManagerService mService; + final ProcessList mProcessList; + final ActivityManagerGlobalLock mProcLock; + + private final int mNumSlots; + protected final ArrayList mTmpProcessList = new ArrayList(); + protected final ArrayList mTmpProcessList2 = new ArrayList(); + protected final ArrayList mTmpBecameIdle = new ArrayList(); + protected final ActiveUids mTmpUidRecords; + protected final ArrayDeque mTmpQueue; + protected final ArraySet mTmpProcessSet = new ArraySet<>(); + protected final ArraySet mPendingProcessSet = new ArraySet<>(); + protected final ArraySet mProcessesInCycle = new ArraySet<>(); + + /** + * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate + * could be called recursively because of the indirect calls during the update; + * however the oomAdjUpdate itself doesn't support recursion - in this case we'd + * have to queue up the new targets found during the update, and perform another + * round of oomAdjUpdate at the end of last update. + */ + @GuardedBy("mService") + private boolean mOomAdjUpdateOngoing = false; + + /** + * Flag to mark if there is a pending full oomAdjUpdate. + */ + @GuardedBy("mService") + private boolean mPendingFullOomAdjUpdate = false; + + private Handler mainHandler; + /** Overrideable by a test */ + @VisibleForTesting + protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + ApplicationInfo app, boolean defaultValue) { + return PlatformCompatCache.getInstance() + .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { + this(service, processList, activeUids, createAdjusterThread()); + } + + static ServiceThread createAdjusterThread() { + // The process group is usually critical to the response time of foreground app, so the + // setter should apply it as soon as possible. + final ServiceThread adjusterThread = + new ServiceThread(TAG, THREAD_PRIORITY_TOP_APP_BOOST, false /* allowIo */); + adjusterThread.start(); + return adjusterThread; + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + ServiceThread adjusterThread) { + mService = service; + mProcessList = processList; + mProcLock = service.mProcLock; + mActiveUids = activeUids; + + mConstants = mService.mConstants; + mCachedAppOptimizer = new CachedAppOptimizer(mService); + mCacheOomRanker = new CacheOomRanker(service); + + mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { + final int pid = msg.arg1; + final int group = msg.arg2; + if (pid == ActivityManagerService.MY_PID) { + // Skip setting the process group for system_server, keep it as default. + return true; + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup " + + msg.obj + " to " + group); + } + try { + setProcessGroup(pid, group); + } catch (Exception e) { + if (DEBUG_ALL) { + Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + return true; + }); + mTmpUidRecords = new ActiveUids(service, false); + mTmpQueue = new ArrayDeque(mConstants.CUR_MAX_CACHED_PROCESSES << 1); + mNumSlots = ((CACHED_APP_MAX_ADJ - CACHED_APP_MIN_ADJ + 1) >> 1) + / CACHED_APP_IMPORTANCE_LEVELS; + mainHandler = new Handler(Looper.getMainLooper()); + } + + void initSettings() { + mCachedAppOptimizer.init(); + mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor()); + if (mService.mConstants.KEEP_WARMING_SERVICES.size() > 0) { + final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); + mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mService) { + handleUserSwitchedLocked(); + } + } + }, filter, null, mService.mHandler); + } + } + + /** + * Update the keep-warming service flags upon user switches + */ + @VisibleForTesting + @GuardedBy("mService") + void handleUserSwitchedLocked() { + mProcessList.forEachLruProcessesLOSP(false, + this::updateKeepWarmIfNecessaryForProcessLocked); + } + + @GuardedBy("mService") + private void updateKeepWarmIfNecessaryForProcessLocked(final ProcessRecord app) { + final ArraySet warmServices = mService.mConstants.KEEP_WARMING_SERVICES; + boolean includeWarmPkg = false; + final PackageList pkgList = app.getPkgList(); + for (int j = warmServices.size() - 1; j >= 0; j--) { + if (pkgList.containsKey(warmServices.valueAt(j).getPackageName())) { + includeWarmPkg = true; + break; + } + } + if (!includeWarmPkg) { + return; + } + final ProcessServiceRecord psr = app.mServices; + for (int j = psr.numberOfRunningServices() - 1; j >= 0; j--) { + psr.getRunningServiceAt(j).updateKeepWarmLocked(); + } + } + + /** + * Perform oom adj update on the given process. It does NOT do the re-computation + * if there is a cycle, caller should check {@link #mProcessesInCycle} and do it on its own. + */ + @GuardedBy({"mService", "mProcLock"}) + private boolean performUpdateOomAdjLSP(ProcessRecord app, int cachedAdj, + ProcessRecord topApp, long now, @OomAdjReason int oomAdjReason) { + if (app.getThread() == null) { + return false; + } + + app.mState.resetCachedInfo(); + app.mState.setCurBoundByNonBgRestrictedApp(false); + UidRecord uidRec = app.getUidRecord(); + if (uidRec != null) { + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec); + } + uidRec.reset(); + } + + // Check if this process is in the pending list too, remove from pending list if so. + mPendingProcessSet.remove(app); + + mProcessesInCycle.clear(); + computeOomAdjLSP(app, cachedAdj, topApp, false, now, false, true, oomAdjReason, true); + if (!mProcessesInCycle.isEmpty()) { + // We can't use the score here if there is a cycle, abort. + for (int i = mProcessesInCycle.size() - 1; i >= 0; i--) { + // Reset the adj seq + mProcessesInCycle.valueAt(i).mState.setCompletedAdjSeq(mAdjSeq - 1); + } + return true; + } + + if (uidRec != null) { + // After uidRec.reset() above, for UidRecord with multiple processes (ProcessRecord), + // we need to apply all ProcessRecord into UidRecord. + uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP); + if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT + && (uidRec.getSetProcState() != uidRec.getCurProcState() + || uidRec.getSetCapability() != uidRec.getCurCapability() + || uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) { + final ActiveUids uids = mTmpUidRecords; + uids.clear(); + uids.put(uidRec.getUid(), uidRec); + updateUidsLSP(uids, SystemClock.elapsedRealtime()); + } + } + + return applyOomAdjLSP(app, false, now, SystemClock.elapsedRealtime(), oomAdjReason); + } + + /** + * Update OomAdj for all processes in LRU list + */ + @GuardedBy("mService") + void updateOomAdjLocked(@OomAdjReason int oomAdjReason) { + synchronized (mProcLock) { + updateOomAdjLSP(oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void updateOomAdjLSP(@OomAdjReason int oomAdjReason) { + if (checkAndEnqueueOomAdjTargetLocked(null)) { + // Simply return as there is an oomAdjUpdate ongoing + return; + } + try { + mOomAdjUpdateOngoing = true; + performUpdateOomAdjLSP(oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) { + final ProcessRecord topApp = mService.getTopApp(); + // Clear any pending ones because we are doing a full update now. + mPendingProcessSet.clear(); + mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false; + updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true); + } + + /** + * Update OomAdj for specific process and its reachable processes (with direction/indirect + * bindings from this process); Note its clients' proc state won't be re-evaluated if this proc + * is hosting any service/content provider. + * + * @param app The process to update, or null to update all processes + * @param oomAdjReason + */ + @GuardedBy("mService") + boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) { + synchronized (mProcLock) { + return updateOomAdjLSP(app, oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { + if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) { + updateOomAdjLSP(oomAdjReason); + return true; + } + + if (checkAndEnqueueOomAdjTargetLocked(app)) { + // Simply return true as there is an oomAdjUpdate ongoing + return true; + } + + try { + mOomAdjUpdateOngoing = true; + return performUpdateOomAdjLSP(app, oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { + final ProcessRecord topApp = mService.getTopApp(); + + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason)); + mService.mOomAdjProfiler.oomAdjStarted(); + mAdjSeq++; + + final ProcessStateRecord state = app.mState; + final boolean wasCached = state.isCached(); + final int oldAdj = state.getCurRawAdj(); + final int cachedAdj = oldAdj >= CACHED_APP_MIN_ADJ + ? oldAdj : UNKNOWN_ADJ; + + // Firstly, try to see if the importance of itself gets changed + final boolean wasBackground = ActivityManager.isProcStateBackground( + state.getSetProcState()); + final int oldCap = state.getSetCapability(); + state.setContainsCycle(false); + state.setProcStateChanged(false); + state.resetCachedInfo(); + state.setCurBoundByNonBgRestrictedApp(false); + // Check if this process is in the pending list too, remove from pending list if so. + mPendingProcessSet.remove(app); + app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason); + boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, + SystemClock.uptimeMillis(), oomAdjReason); + // The 'app' here itself might or might not be in the cycle, for example, + // the case A <=> B vs. A -> B <=> C; anyway, if we spot a cycle here, re-compute them. + if (!success || (wasCached == state.isCached() && oldAdj != INVALID_ADJ + && mProcessesInCycle.isEmpty() /* Force re-compute if there is a cycle */ + && oldCap == state.getCurCapability() + && wasBackground == ActivityManager.isProcStateBackground( + state.getSetProcState()))) { + mProcessesInCycle.clear(); + // Okay, it's unchanged, it won't impact any service it binds to, we're done here. + if (DEBUG_OOM_ADJ) { + Slog.i(TAG_OOM_ADJ, "No oomadj changes for " + app); + } + mService.mOomAdjProfiler.oomAdjEnded(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + return success; + } + + // Next to find out all its reachable processes + ArrayList processes = mTmpProcessList; + ActiveUids uids = mTmpUidRecords; + mPendingProcessSet.add(app); + + // Add all processes with cycles into the list to scan + for (int i = mProcessesInCycle.size() - 1; i >= 0; i--) { + mPendingProcessSet.add(mProcessesInCycle.valueAt(i)); + } + mProcessesInCycle.clear(); + + boolean containsCycle = collectReachableProcessesLocked(mPendingProcessSet, + processes, uids); + + // Clear the pending set as they should've been included in 'processes'. + mPendingProcessSet.clear(); + + if (!containsCycle) { + // Remove this app from the return list because we've done the computation on it. + processes.remove(app); + } + + int size = processes.size(); + if (size > 0) { + mAdjSeq--; + // Update these reachable processes + updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false); + } else if (state.getCurRawAdj() == UNKNOWN_ADJ) { + // In case the app goes from non-cached to cached but it doesn't have other reachable + // processes, its adj could be still unknown as of now, assign one. + processes.add(app); + assignCachedAdjIfNecessary(processes); + applyOomAdjLSP(app, false, SystemClock.uptimeMillis(), + SystemClock.elapsedRealtime(), oomAdjReason); + } + mTmpProcessList.clear(); + mService.mOomAdjProfiler.oomAdjEnded(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + return true; + } + + /** + * Collect the reachable processes from the given {@code apps}, the result will be + * returned in the given {@code processes}, which will include the processes from + * the given {@code apps}. + */ + @GuardedBy("mService") + protected boolean collectReachableProcessesLocked(ArraySet apps, + ArrayList processes, ActiveUids uids) { + final ArrayDeque queue = mTmpQueue; + queue.clear(); + processes.clear(); + for (int i = 0, size = apps.size(); i < size; i++) { + final ProcessRecord app = apps.valueAt(i); + app.mState.setReachable(true); + queue.offer(app); + } + + uids.clear(); + + // Track if any of them reachables could include a cycle + boolean containsCycle = false; + // Scan downstreams of the process record + for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) { + processes.add(pr); + final UidRecord uidRec = pr.getUidRecord(); + if (uidRec != null) { + uids.put(uidRec.getUid(), uidRec); + } + final ProcessServiceRecord psr = pr.mServices; + for (int i = psr.numberOfConnections() - 1; i >= 0; i--) { + ConnectionRecord cr = psr.getConnectionAt(i); + ProcessRecord service = cr.hasFlag(ServiceInfo.FLAG_ISOLATED_PROCESS) + ? cr.binding.service.isolationHostProc : cr.binding.service.app; + if (service == null || service == pr + || ((service.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ) + && (service.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) { + continue; + } + containsCycle |= service.mState.isReachable(); + if (service.mState.isReachable()) { + continue; + } + if (cr.hasFlag(Context.BIND_WAIVE_PRIORITY) + && cr.notHasFlag(Context.BIND_TREAT_LIKE_ACTIVITY + | Context.BIND_ADJUST_WITH_ACTIVITY)) { + continue; + } + queue.offer(service); + service.mState.setReachable(true); + } + final ProcessProviderRecord ppr = pr.mProviders; + for (int i = ppr.numberOfProviderConnections() - 1; i >= 0; i--) { + ContentProviderConnection cpc = ppr.getProviderConnectionAt(i); + ProcessRecord provider = cpc.provider.proc; + if (provider == null || provider == pr + || ((provider.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ) + && (provider.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) { + continue; + } + containsCycle |= provider.mState.isReachable(); + if (provider.mState.isReachable()) { + continue; + } + queue.offer(provider); + provider.mState.setReachable(true); + } + // See if this process has any corresponding SDK sandbox processes running, and if so + // scan them as well. + final List sdkSandboxes = + mProcessList.getSdkSandboxProcessesForAppLocked(pr.uid); + final int numSdkSandboxes = sdkSandboxes != null ? sdkSandboxes.size() : 0; + for (int i = numSdkSandboxes - 1; i >= 0; i--) { + ProcessRecord sdkSandbox = sdkSandboxes.get(i); + containsCycle |= sdkSandbox.mState.isReachable(); + if (sdkSandbox.mState.isReachable()) { + continue; + } + queue.offer(sdkSandbox); + sdkSandbox.mState.setReachable(true); + } + // If this process is a sandbox itself, also scan the app on whose behalf its running + if (pr.isSdkSandbox) { + for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) { + ServiceRecord s = psr.getRunningServiceAt(is); + ArrayMap> serviceConnections = + s.getConnections(); + for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) { + ArrayList clist = serviceConnections.valueAt(conni); + for (int i = clist.size() - 1; i >= 0; i--) { + ConnectionRecord cr = clist.get(i); + ProcessRecord attributedApp = cr.binding.attributedClient; + if (attributedApp == null || attributedApp == pr + || ((attributedApp.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ) + && (attributedApp.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) { + continue; + } + if (attributedApp.mState.isReachable()) { + continue; + } + queue.offer(attributedApp); + attributedApp.mState.setReachable(true); + } + } + } + } + } + + int size = processes.size(); + if (size > 0) { + // Reverse the process list, since the updateOomAdjInnerLSP scans from the end of it. + for (int l = 0, r = size - 1; l < r; l++, r--) { + final ProcessRecord t = processes.get(l); + final ProcessRecord u = processes.get(r); + t.mState.setReachable(false); + u.mState.setReachable(false); + processes.set(l, u); + processes.set(r, t); + } + } + + return containsCycle; + } + + /** + * Enqueue the given process for a later oom adj update + */ + @GuardedBy("mService") + void enqueueOomAdjTargetLocked(ProcessRecord app) { + if (app != null && app.mState.getMaxAdj() > FOREGROUND_APP_ADJ) { + mPendingProcessSet.add(app); + } + } + + @GuardedBy("mService") + void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) { + if (app != null) { + mPendingProcessSet.remove(app); + if (procDied) { + PlatformCompatCache.getInstance().invalidate(app.info); + } + } + } + + /** + * Check if there is an ongoing oomAdjUpdate, enqueue the given process record + * to {@link #mPendingProcessSet} if there is one. + * + * @param app The target app to get an oomAdjUpdate, or a full oomAdjUpdate if it's null. + * @return {@code true} if there is an ongoing oomAdjUpdate. + */ + @GuardedBy("mService") + private boolean checkAndEnqueueOomAdjTargetLocked(@Nullable ProcessRecord app) { + if (!mOomAdjUpdateOngoing) { + return false; + } + if (app != null) { + mPendingProcessSet.add(app); + } else { + mPendingFullOomAdjUpdate = true; + } + return true; + } + + /** + * Kick off an oom adj update pass for the pending targets which are enqueued via + * {@link #enqueueOomAdjTargetLocked}. + */ + @GuardedBy("mService") + void updateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) { + // First check if there is pending full update + if (mPendingFullOomAdjUpdate) { + mPendingFullOomAdjUpdate = false; + mPendingProcessSet.clear(); + updateOomAdjLocked(oomAdjReason); + return; + } + if (mPendingProcessSet.isEmpty()) { + return; + } + + if (mOomAdjUpdateOngoing) { + // There's another oomAdjUpdate ongoing, return from here now; + // that ongoing update would call us again at the end of it. + return; + } + try { + mOomAdjUpdateOngoing = true; + performUpdateOomAdjPendingTargetsLocked(oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy("mService") + private void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) { + final ProcessRecord topApp = mService.getTopApp(); + + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason)); + mService.mOomAdjProfiler.oomAdjStarted(); + + final ArrayList processes = mTmpProcessList; + final ActiveUids uids = mTmpUidRecords; + collectReachableProcessesLocked(mPendingProcessSet, processes, uids); + mPendingProcessSet.clear(); + synchronized (mProcLock) { + updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, true, false); + } + processes.clear(); + + mService.mOomAdjProfiler.oomAdjEnded(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + /** + * Update OomAdj for all processes within the given list (could be partial), or the whole LRU + * list if the given list is null; when it's partial update, each process's client proc won't + * get evaluated recursively here. + * + *

Note: If the given {@code processes} is not null, the expectation to it is, the caller + * must have called {@link collectReachableProcessesLocked} on it. + */ + @GuardedBy({"mService", "mProcLock"}) + protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp, + ArrayList processes, ActiveUids uids, boolean potentialCycles, + boolean startProfiling) { + final boolean fullUpdate = processes == null; + final ArrayList activeProcesses = fullUpdate + ? mProcessList.getLruProcessesLOSP() : processes; + ActiveUids activeUids = uids; + if (activeUids == null) { + final int numUids = mActiveUids.size(); + activeUids = mTmpUidRecords; + activeUids.clear(); + for (int i = 0; i < numUids; i++) { + UidRecord uidRec = mActiveUids.valueAt(i); + activeUids.put(uidRec.getUid(), uidRec); + } + } + + if (startProfiling) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason)); + mService.mOomAdjProfiler.oomAdjStarted(); + } + final long now = SystemClock.uptimeMillis(); + final long nowElapsed = SystemClock.elapsedRealtime(); + final long oldTime = now - mConstants.mMaxEmptyTimeMillis; + final int numProc = activeProcesses.size(); + + mAdjSeq++; + if (fullUpdate) { + mNewNumServiceProcs = 0; + mNewNumAServiceProcs = 0; + } + + // Reset state in all uid records. + resetUidRecordsLsp(activeUids); + + boolean retryCycles = false; + boolean computeClients = fullUpdate || potentialCycles; + + // need to reset cycle state before calling computeOomAdjLSP because of service conns + for (int i = numProc - 1; i >= 0; i--) { + ProcessRecord app = activeProcesses.get(i); + final ProcessStateRecord state = app.mState; + state.setReachable(false); + // No need to compute again it has been evaluated in previous iteration + if (state.getAdjSeq() != mAdjSeq) { + state.setContainsCycle(false); + state.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY); + state.setCurRawAdj(UNKNOWN_ADJ); + state.setSetCapability(PROCESS_CAPABILITY_NONE); + state.resetCachedInfo(); + state.setCurBoundByNonBgRestrictedApp(false); + } + } + mProcessesInCycle.clear(); + for (int i = numProc - 1; i >= 0; i--) { + ProcessRecord app = activeProcesses.get(i); + final ProcessStateRecord state = app.mState; + if (!app.isKilledByAm() && app.getThread() != null) { + state.setProcStateChanged(false); + app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason); + // It won't enter cycle if not computing clients. + computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, fullUpdate, now, false, + computeClients, oomAdjReason, true); + // if any app encountered a cycle, we need to perform an additional loop later + retryCycles |= state.containsCycle(); + // Keep the completedAdjSeq to up to date. + state.setCompletedAdjSeq(mAdjSeq); + } + } + + if (mCacheOomRanker.useOomReranking()) { + mCacheOomRanker.reRankLruCachedAppsLSP(mProcessList.getLruProcessesLSP(), + mProcessList.getLruProcessServiceStartLOSP()); + } + + if (computeClients) { // There won't be cycles if we didn't compute clients above. + // Cycle strategy: + // - Retry computing any process that has encountered a cycle. + // - Continue retrying until no process was promoted. + // - Iterate from least important to most important. + int cycleCount = 0; + while (retryCycles && cycleCount < 10) { + cycleCount++; + retryCycles = false; + + for (int i = 0; i < numProc; i++) { + ProcessRecord app = activeProcesses.get(i); + final ProcessStateRecord state = app.mState; + if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) { + state.decAdjSeq(); + state.decCompletedAdjSeq(); + } + } + + for (int i = 0; i < numProc; i++) { + ProcessRecord app = activeProcesses.get(i); + final ProcessStateRecord state = app.mState; + if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) { + if (computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, true, now, + true, true, oomAdjReason, true)) { + retryCycles = true; + } + } + } + } + } + mProcessesInCycle.clear(); + + assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP()); + + postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime); + + if (startProfiling) { + mService.mOomAdjProfiler.oomAdjEnded(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void resetUidRecordsLsp(@NonNull ActiveUids activeUids) { + // Reset state in all uid records. + for (int i = activeUids.size() - 1; i >= 0; i--) { + final UidRecord uidRec = activeUids.valueAt(i); + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec); + } + uidRec.reset(); + } + } + + @GuardedBy({"mService", "mProcLock"}) + protected void postUpdateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, ActiveUids activeUids, + long now, long nowElapsed, long oldTime) { + mNumNonCachedProcs = 0; + mNumCachedHiddenProcs = 0; + + final boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids, + oomAdjReason); + mNumServiceProcs = mNewNumServiceProcs; + + if (mService.mAlwaysFinishActivities) { + // Need to do this on its own message because the stack may not + // be in a consistent state at this point. + mService.mAtmInternal.scheduleDestroyAllActivities("always-finish"); + } + + if (allChanged) { + mService.mAppProfiler.requestPssAllProcsLPr(now, false, + mService.mProcessStats.isMemFactorLowered()); + } + + updateUidsLSP(activeUids, nowElapsed); + + synchronized (mService.mProcessStats.mLock) { + final long nowUptime = SystemClock.uptimeMillis(); + if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) { + mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, + mService.mProcessStats)); + } + + // Run this after making sure all procstates are updated. + mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, nowUptime); + } + + if (DEBUG_OOM_ADJ) { + final long duration = SystemClock.uptimeMillis() - now; + if (false) { + Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms", + new RuntimeException("here").fillInStackTrace()); + } else { + Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms"); + } + } + } + + @GuardedBy({"mService", "mProcLock"}) + protected void assignCachedAdjIfNecessary(ArrayList lruList) { + final int numLru = lruList.size(); + if (mConstants.USE_TIERED_CACHED_ADJ) { + final long now = SystemClock.uptimeMillis(); + for (int i = numLru - 1; i >= 0; i--) { + ProcessRecord app = lruList.get(i); + final ProcessStateRecord state = app.mState; + final ProcessCachedOptimizerRecord opt = app.mOptRecord; + if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj() + >= UNKNOWN_ADJ) { + final ProcessServiceRecord psr = app.mServices; + int targetAdj = CACHED_APP_MIN_ADJ; + + if (opt != null && opt.isFreezeExempt()) { + // BIND_WAIVE_PRIORITY and the like get oom_adj 900 + targetAdj += 0; + } else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ) + && (state.getLastStateTime() + + mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) { + // Older cached apps get 950 + targetAdj += 50; + } else { + // Newer cached apps get 910 + targetAdj += 10; + } + state.setCurRawAdj(targetAdj); + state.setCurAdj(psr.modifyRawOomAdj(targetAdj)); + } + } + } else { + // First update the OOM adjustment for each of the + // application processes based on their current state. + int curCachedAdj = CACHED_APP_MIN_ADJ; + int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2); + int curCachedImpAdj = 0; + int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS; + int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2); + + final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES; + final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES + - emptyProcessLimit; + // Let's determine how many processes we have running vs. + // how many slots we have for background processes; we may want + // to put multiple processes in a slot of there are enough of + // them. + int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs; + if (numEmptyProcs > cachedProcessLimit) { + // If there are more empty processes than our limit on cached + // processes, then use the cached process limit for the factor. + // This ensures that the really old empty processes get pushed + // down to the bottom, so if we are running low on memory we will + // have a better chance at keeping around more cached processes + // instead of a gazillion empty processes. + numEmptyProcs = cachedProcessLimit; + } + int cachedFactor = (mNumCachedHiddenProcs > 0 + ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1) + / mNumSlots; + if (cachedFactor < 1) cachedFactor = 1; + + int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots; + if (emptyFactor < 1) emptyFactor = 1; + + int stepCached = -1; + int stepEmpty = -1; + int lastCachedGroup = 0; + int lastCachedGroupImportance = 0; + int lastCachedGroupUid = 0; + + + for (int i = numLru - 1; i >= 0; i--) { + ProcessRecord app = lruList.get(i); + final ProcessStateRecord state = app.mState; + // If we haven't yet assigned the final cached adj + // to the process, do that now. + if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj() + >= UNKNOWN_ADJ) { + final ProcessServiceRecord psr = app.mServices; + switch (state.getCurProcState()) { + case PROCESS_STATE_CACHED_ACTIVITY: + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + case ActivityManager.PROCESS_STATE_CACHED_RECENT: + // Figure out the next cached level, taking into account groups. + boolean inGroup = false; + final int connectionGroup = psr.getConnectionGroup(); + if (connectionGroup != 0) { + final int connectionImportance = psr.getConnectionImportance(); + if (lastCachedGroupUid == app.uid + && lastCachedGroup == connectionGroup) { + // This is in the same group as the last process, just tweak + // adjustment by importance. + if (connectionImportance > lastCachedGroupImportance) { + lastCachedGroupImportance = connectionImportance; + if (curCachedAdj < nextCachedAdj + && curCachedAdj < CACHED_APP_MAX_ADJ) { + curCachedImpAdj++; + } + } + inGroup = true; + } else { + lastCachedGroupUid = app.uid; + lastCachedGroup = connectionGroup; + lastCachedGroupImportance = connectionImportance; + } + } + if (!inGroup && curCachedAdj != nextCachedAdj) { + stepCached++; + curCachedImpAdj = 0; + if (stepCached >= cachedFactor) { + stepCached = 0; + curCachedAdj = nextCachedAdj; + nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2; + if (nextCachedAdj > CACHED_APP_MAX_ADJ) { + nextCachedAdj = CACHED_APP_MAX_ADJ; + } + } + } + // This process is a cached process holding activities... + // assign it the next cached value for that type, and then + // step that cached level. + state.setCurRawAdj(curCachedAdj + curCachedImpAdj); + state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj)); + if (DEBUG_LRU) { + Slog.d(TAG_LRU, "Assigning activity LRU #" + i + + " adj: " + state.getCurAdj() + + " (curCachedAdj=" + curCachedAdj + + " curCachedImpAdj=" + curCachedImpAdj + ")"); + } + break; + default: + // Figure out the next cached level. + if (curEmptyAdj != nextEmptyAdj) { + stepEmpty++; + if (stepEmpty >= emptyFactor) { + stepEmpty = 0; + curEmptyAdj = nextEmptyAdj; + nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2; + if (nextEmptyAdj > CACHED_APP_MAX_ADJ) { + nextEmptyAdj = CACHED_APP_MAX_ADJ; + } + } + } + // For everything else, assign next empty cached process + // level and bump that up. Note that this means that + // long-running services that have dropped down to the + // cached level will be treated as empty (since their process + // state is still as a service), which is what we want. + state.setCurRawAdj(curEmptyAdj); + state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj)); + if (DEBUG_LRU) { + Slog.d(TAG_LRU, "Assigning empty LRU #" + i + + " adj: " + state.getCurAdj() + + " (curEmptyAdj=" + curEmptyAdj + + ")"); + } + break; + } + } + } + } + } + private long mNextNoKillDebugMessageTime; + + private double mLastFreeSwapPercent = 1.00; + + private static double getFreeSwapPercent() { + return CachedAppOptimizer.getFreeSwapPercent(); + } + + //ccynice + private void restartApp(String packageName) { + PackageManager packageManager = mService.mContext.getPackageManager(); + Intent launchIntent = packageManager.getLaunchIntentForPackage(packageName); + + if(mainHandler==null) + mainHandler = new Handler(Looper.getMainLooper()); + + if (launchIntent != null) { + mainHandler.postDelayed(new Runnable() { + @Override + public void run() { + mService.mContext.startActivity(launchIntent); + } + }, 15000); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, + final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) { + ArrayList lruList = mProcessList.getLruProcessesLOSP(); + final int numLru = lruList.size(); + + final boolean doKillExcessiveProcesses = shouldKillExcessiveProcesses(now); + if (!doKillExcessiveProcesses) { + if (mNextNoKillDebugMessageTime < now) { + Slog.d(TAG, "Not killing cached processes"); // STOPSHIP Remove it b/222365734 + mNextNoKillDebugMessageTime = now + 5000; // Every 5 seconds + } + } + final int emptyProcessLimit = doKillExcessiveProcesses + ? mConstants.CUR_MAX_EMPTY_PROCESSES : Integer.MAX_VALUE; + final int cachedProcessLimit = doKillExcessiveProcesses + ? (mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit) : Integer.MAX_VALUE; + int lastCachedGroup = 0; + int lastCachedGroupUid = 0; + int numCached = 0; + int numCachedExtraGroup = 0; + int numEmpty = 0; + int numTrimming = 0; + + //ccynice + String white_list = SystemProperties.get("persist.pm.alive.app_white_list", "zzz"); + + + boolean proactiveKillsEnabled = mConstants.PROACTIVE_KILLS_ENABLED; + double lowSwapThresholdPercent = mConstants.LOW_SWAP_THRESHOLD_PERCENT; + double freeSwapPercent = proactiveKillsEnabled ? getFreeSwapPercent() : 1.00; + ProcessRecord lruCachedApp = null; + + for (int i = numLru - 1; i >= 0; i--) { + ProcessRecord app = lruList.get(i); + final ProcessStateRecord state = app.mState; + if (!app.isKilledByAm() && app.getThread() != null) { + // We don't need to apply the update for the process which didn't get computed + if (state.getCompletedAdjSeq() == mAdjSeq) { + applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason); + } + + if (app.isPendingFinishAttach()) { + // Avoid trimming processes that are still initializing. If they aren't + // hosting any components yet because they may be unfairly killed. + // We however apply the oom scores set at #setAttachingProcessStatesLSP. + continue; + } + + final ProcessServiceRecord psr = app.mServices; + // Count the number of process types. + switch (state.getCurProcState()) { + case PROCESS_STATE_CACHED_ACTIVITY: + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + mNumCachedHiddenProcs++; + numCached++; + final int connectionGroup = psr.getConnectionGroup(); + if (connectionGroup != 0) { + if (lastCachedGroupUid == app.info.uid + && lastCachedGroup == connectionGroup) { + // If this process is the next in the same group, we don't + // want it to count against our limit of the number of cached + // processes, so bump up the group count to account for it. + numCachedExtraGroup++; + } else { + lastCachedGroupUid = app.info.uid; + lastCachedGroup = connectionGroup; + } + } else { + lastCachedGroupUid = lastCachedGroup = 0; + } + if ((numCached - numCachedExtraGroup) > cachedProcessLimit) { + app.killLocked("cached #" + numCached, + "too many cached", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED, + true); + //ccynice + if(app.info!=null&&app.info.uid>10000&&white_list.contains(app.info.packageName)) + restartApp(app.info.packageName); + + } else if (proactiveKillsEnabled) { + lruCachedApp = app; + } + break; + case PROCESS_STATE_CACHED_EMPTY: + if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES + && app.getLastActivityTime() < oldTime) { + app.killLocked("empty for " + ((now + - app.getLastActivityTime()) / 1000) + "s", + "empty for too long", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_TRIM_EMPTY, + true); + //ccynice + if(app.info!=null&&app.info.uid>10000&&white_list.contains(app.info.packageName)) + restartApp(app.info.packageName); + } else { + numEmpty++; + if (numEmpty > emptyProcessLimit) { + app.killLocked("empty #" + numEmpty, + "too many empty", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, + true); + //ccynice + if(app.info!=null&&app.info.uid>10000&&white_list.contains(app.info.packageName)) + restartApp(app.info.packageName); + } else if (proactiveKillsEnabled) { + lruCachedApp = app; + } + } + break; + default: + mNumNonCachedProcs++; + break; + } + + // TODO: b/319163103 - limit isolated/sandbox trimming to just the processes + // evaluated in the current update. + if (app.isolated && psr.numberOfRunningServices() <= 0 + && app.getIsolatedEntryPoint() == null) { + // If this is an isolated process, there are no services + // running in it, and it's not a special process with a + // custom entry point, then the process is no longer + // needed. We agressively kill these because we can by + // definition not re-use the same process again, and it is + // good to avoid having whatever code was running in them + // left sitting around after no longer needed. + app.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true); + //ccynice + if(app.info!=null&&app.info.uid>10000&&white_list.contains(app.info.packageName)) + restartApp(app.info.packageName); + } else if (app.isSdkSandbox && psr.numberOfRunningServices() <= 0 + && app.getActiveInstrumentation() == null) { + // If this is an SDK sandbox process and there are no services running it, we + // aggressively kill the sandbox as we usually don't want to re-use the same + // sandbox again. + app.killLocked("sandbox not needed", ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_SDK_SANDBOX_NOT_NEEDED, true); + } else { + // Keeping this process, update its uid. + updateAppUidRecLSP(app); + } + + if (state.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME + && !app.isKilledByAm()) { + numTrimming++; + } + } + } + + if (proactiveKillsEnabled // Proactive kills enabled? + && doKillExcessiveProcesses // Should kill excessive processes? + && freeSwapPercent < lowSwapThresholdPercent // Swap below threshold? + && lruCachedApp != null // If no cached app, let LMKD decide + // If swap is non-decreasing, give reclaim a chance to catch up + && freeSwapPercent < mLastFreeSwapPercent) { + lruCachedApp.killLocked("swap low and too many cached", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED, + true); + } + + mLastFreeSwapPercent = freeSwapPercent; + + return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming, now); + } + + @GuardedBy({"mService", "mProcLock"}) + protected void updateAppUidRecIfNecessaryLSP(final ProcessRecord app) { + if (!app.isKilledByAm() && app.getThread() != null) { + if (app.isolated && app.mServices.numberOfRunningServices() <= 0 + && app.getIsolatedEntryPoint() == null) { + // No op. + } else { + // Keeping this process, update its uid. + updateAppUidRecLSP(app); + } + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void updateAppUidRecLSP(ProcessRecord app) { + final UidRecord uidRec = app.getUidRecord(); + if (uidRec != null) { + final ProcessStateRecord state = app.mState; + uidRec.setEphemeral(app.info.isInstantApp()); + if (uidRec.getCurProcState() > state.getCurProcState()) { + uidRec.setCurProcState(state.getCurProcState()); + } + if (app.mServices.hasForegroundServices()) { + uidRec.setForegroundServices(true); + } + uidRec.setCurCapability(uidRec.getCurCapability() | state.getCurCapability()); + } + } + + @GuardedBy({"mService", "mProcLock"}) + protected void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) { + // This compares previously set procstate to the current procstate in regards to whether + // or not the app's network access will be blocked. So, this needs to be called before + // we update the UidRecord's procstate by calling {@link UidRecord#setSetProcState}. + mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids); + + ArrayList becameIdle = mTmpBecameIdle; + becameIdle.clear(); + + // Update from any uid changes. + if (mService.mLocalPowerManager != null) { + mService.mLocalPowerManager.startUidChanges(); + } + for (int i = activeUids.size() - 1; i >= 0; i--) { + final UidRecord uidRec = activeUids.valueAt(i); + if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT) { + if (uidRec.getSetProcState() != uidRec.getCurProcState() + || uidRec.getSetCapability() != uidRec.getCurCapability() + || uidRec.isSetAllowListed() != uidRec.isCurAllowListed() + || uidRec.getProcAdjChanged()) { + int uidChange = 0; + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec + + ": proc state from " + uidRec.getSetProcState() + " to " + + uidRec.getCurProcState() + ", capability from " + + uidRec.getSetCapability() + " to " + uidRec.getCurCapability() + + ", allowlist from " + uidRec.isSetAllowListed() + + " to " + uidRec.isCurAllowListed() + + ", procAdjChanged: " + uidRec.getProcAdjChanged()); + } + if (ActivityManager.isProcStateBackground(uidRec.getCurProcState()) + && !uidRec.isCurAllowListed()) { + // UID is now in the background (and not on the temp allowlist). Was it + // previously in the foreground (or on the temp allowlist)? + // Or, it wasn't in the foreground / allowlist, but its last background + // timestamp is also 0, this means it's never been in the + // foreground / allowlist since it's born at all. + if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState()) + || uidRec.isSetAllowListed() + || uidRec.getLastBackgroundTime() == 0) { + uidRec.setLastBackgroundTime(nowElapsed); + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + // Note: the background settle time is in elapsed realtime, while + // the handler time base is uptime. All this means is that we may + // stop background uids later than we had intended, but that only + // happens because the device was sleeping so we are okay anyway. + mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, + mConstants.BACKGROUND_SETTLE_TIME); + } + } + if (uidRec.isIdle() && !uidRec.isSetIdle()) { + uidChange |= UidRecord.CHANGE_IDLE; + if (uidRec.getSetProcState() != PROCESS_STATE_NONEXISTENT) { + // don't stop the bg services if it's just started. + becameIdle.add(uidRec); + } + } + } else { + if (uidRec.isIdle()) { + uidChange |= UidRecord.CHANGE_ACTIVE; + EventLogTags.writeAmUidActive(uidRec.getUid()); + uidRec.setIdle(false); + } + uidRec.setLastBackgroundTime(0); + uidRec.setLastIdleTime(0); + } + final boolean wasCached = uidRec.getSetProcState() + > ActivityManager.PROCESS_STATE_RECEIVER; + final boolean isCached = uidRec.getCurProcState() + > ActivityManager.PROCESS_STATE_RECEIVER; + if (wasCached != isCached + || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) { + uidChange |= isCached ? UidRecord.CHANGE_CACHED : + UidRecord.CHANGE_UNCACHED; + } + if (uidRec.getSetCapability() != uidRec.getCurCapability()) { + uidChange |= UidRecord.CHANGE_CAPABILITY; + } + if (uidRec.getSetProcState() != uidRec.getCurProcState()) { + uidChange |= UidRecord.CHANGE_PROCSTATE; + } + if (uidRec.getProcAdjChanged()) { + uidChange |= UidRecord.CHANGE_PROCADJ; + } + uidRec.setSetProcState(uidRec.getCurProcState()); + uidRec.setSetCapability(uidRec.getCurCapability()); + uidRec.setSetAllowListed(uidRec.isCurAllowListed()); + uidRec.setSetIdle(uidRec.isIdle()); + uidRec.clearProcAdjChanged(); + if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0 + || (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) { + mService.mAtmInternal.onUidProcStateChanged( + uidRec.getUid(), uidRec.getSetProcState()); + } + if (uidChange != 0) { + mService.enqueueUidChangeLocked(uidRec, -1, uidChange); + } + if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0 + || (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) { + mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(), + uidRec.getCurCapability()); + } + if (uidRec.hasForegroundServices()) { + mService.mServices.foregroundServiceProcStateChangedLocked(uidRec); + } + } + } + mService.mInternal.deletePendingTopUid(uidRec.getUid(), nowElapsed); + } + if (mService.mLocalPowerManager != null) { + mService.mLocalPowerManager.finishUidChanges(); + } + + int size = becameIdle.size(); + if (size > 0) { + // If we have any new uids that became idle this time, we need to make sure + // they aren't left with running services. + for (int i = size - 1; i >= 0; i--) { + mService.mServices.stopInBackgroundLocked(becameIdle.get(i).getUid()); + } + } + } + + /** + * Return true if we should kill excessive cached/empty processes. + */ + private boolean shouldKillExcessiveProcesses(long nowUptime) { + final long lastUserUnlockingUptime = mService.mUserController.getLastUserUnlockingUptime(); + + if (lastUserUnlockingUptime == 0) { + // No users have been unlocked. + return !mConstants.mNoKillCachedProcessesUntilBootCompleted; + } + final long noKillCachedProcessesPostBootCompletedDurationMillis = + mConstants.mNoKillCachedProcessesPostBootCompletedDurationMillis; + if ((lastUserUnlockingUptime + noKillCachedProcessesPostBootCompletedDurationMillis) + > nowUptime) { + return false; + } + return true; + } + + protected final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback = + new ComputeOomAdjWindowCallback(); + + /** These methods are called inline during computeOomAdjLSP(), on the same thread */ + final class ComputeOomAdjWindowCallback + implements WindowProcessController.ComputeOomAdjCallback { + + ProcessRecord app; + int adj; + boolean foregroundActivities; + boolean mHasVisibleActivities; + int procState; + int schedGroup; + int appUid; + int logUid; + int processStateCurTop; + String mAdjType; + ProcessStateRecord mState; + + void initialize(ProcessRecord app, int adj, boolean foregroundActivities, + boolean hasVisibleActivities, int procState, int schedGroup, int appUid, + int logUid, int processStateCurTop) { + this.app = app; + this.adj = adj; + this.foregroundActivities = foregroundActivities; + this.mHasVisibleActivities = hasVisibleActivities; + this.procState = procState; + this.schedGroup = schedGroup; + this.appUid = appUid; + this.logUid = logUid; + this.processStateCurTop = processStateCurTop; + mAdjType = app.mState.getAdjType(); + this.mState = app.mState; + } + + @Override + public void onVisibleActivity() { + // App has a visible activity; only upgrade adjustment. + if (adj > VISIBLE_APP_ADJ) { + adj = VISIBLE_APP_ADJ; + mAdjType = "vis-activity"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app); + } + } + if (procState > processStateCurTop) { + procState = processStateCurTop; + mAdjType = "vis-activity"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to vis-activity (top): " + app); + } + } + if (schedGroup < SCHED_GROUP_DEFAULT) { + schedGroup = SCHED_GROUP_DEFAULT; + } + foregroundActivities = true; + mHasVisibleActivities = true; + } + + @Override + public void onPausedActivity() { + if (adj > PERCEPTIBLE_APP_ADJ) { + adj = PERCEPTIBLE_APP_ADJ; + mAdjType = "pause-activity"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: " + app); + } + } + if (procState > processStateCurTop) { + procState = processStateCurTop; + mAdjType = "pause-activity"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to pause-activity (top): " + app); + } + } + if (schedGroup < SCHED_GROUP_DEFAULT) { + schedGroup = SCHED_GROUP_DEFAULT; + } + foregroundActivities = true; + mHasVisibleActivities = false; + } + + @Override + public void onStoppingActivity(boolean finishing) { + if (adj > PERCEPTIBLE_APP_ADJ) { + adj = PERCEPTIBLE_APP_ADJ; + mAdjType = "stop-activity"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise adj to stop-activity: " + app); + } + } + + // For the process state, we will at this point consider the process to be cached. It + // will be cached either as an activity or empty depending on whether the activity is + // finishing. We do this so that we can treat the process as cached for purposes of + // memory trimming (determining current memory level, trim command to send to process) + // since there can be an arbitrary number of stopping processes and they should soon all + // go into the cached state. + if (!finishing) { + if (procState > PROCESS_STATE_LAST_ACTIVITY) { + procState = PROCESS_STATE_LAST_ACTIVITY; + mAdjType = "stop-activity"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to stop-activity: " + app); + } + } + } + foregroundActivities = true; + mHasVisibleActivities = false; + } + + @Override + public void onOtherActivity() { + if (procState > PROCESS_STATE_CACHED_ACTIVITY) { + procState = PROCESS_STATE_CACHED_ACTIVITY; + mAdjType = "cch-act"; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to cached activity: " + app); + } + } + mHasVisibleActivities = false; + } + } + + private boolean isScreenOnOrAnimatingLocked(ProcessStateRecord state) { + return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE + || state.isRunningRemoteAnimation(); + } + + @GuardedBy({"mService", "mProcLock"}) + protected boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj, + ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval, + boolean computeClients, int oomAdjReason, boolean couldRecurse) { + final ProcessStateRecord state = app.mState; + //ccynice + boolean alive_bl = false; + + if (couldRecurse) { + + //ccynice + String packageName ="unknown"; + + if(app.info!=null&&app.info.uid>10000){ + String white_list = SystemProperties.get("persist.pm.alive.app_white_list", "zzz"); + + packageName = app.info.packageName; + + if(white_list.contains(packageName)) + alive_bl = true; + } + if (mAdjSeq == state.getAdjSeq()) { + if (state.getAdjSeq() == state.getCompletedAdjSeq()) { + // This adjustment has already been computed successfully. + return false; + } else { + // The process is being computed, so there is a cycle. We cannot + // rely on this process's state. + state.setContainsCycle(true); + mProcessesInCycle.add(app); + + return false; + } + } + } + + int prevAppAdj = getInitialAdj(app); + int prevProcState = getInitialProcState(app); + int prevCapability = getInitialCapability(app); + + if (app.getThread() == null) { + state.setAdjSeq(mAdjSeq); + state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND); + state.setCurProcState(PROCESS_STATE_CACHED_EMPTY); + state.setCurAdj(CACHED_APP_MAX_ADJ); + state.setCurRawAdj(CACHED_APP_MAX_ADJ); + state.setCompletedAdjSeq(state.getAdjSeq()); + state.setCurCapability(PROCESS_CAPABILITY_NONE); + onProcessStateChanged(app, prevProcState); + onProcessOomAdjChanged(app, prevAppAdj); + return false; + } + + state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN); + state.setAdjSource(null); + state.setAdjTarget(null); + if (!couldRecurse || !cycleReEval) { + // Don't reset this flag when doing cycles re-evaluation. + state.setNoKillOnBgRestrictedAndIdle(false); + // If this UID is currently allowlisted, it should not be frozen. + final UidRecord uidRec = app.getUidRecord(); + app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed()); + } + + final int appUid = app.info.uid; + final int logUid = mService.mCurOomAdjUid; + + final ProcessServiceRecord psr = app.mServices; + + if (state.getMaxAdj() <= FOREGROUND_APP_ADJ) { + // The max adjustment doesn't allow this app to be anything + // below foreground, so it is not worth doing work for it. + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app); + } + state.setAdjType("fixed"); + state.setAdjSeq(mAdjSeq); + state.setCurRawAdj(state.getMaxAdj()); + state.setHasForegroundActivities(false); + state.setCurrentSchedulingGroup(SCHED_GROUP_DEFAULT); + state.setCurCapability(PROCESS_CAPABILITY_ALL); // BFSL allowed + state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT); + // System processes can do UI, and when they do we want to have + // them trim their memory after the user leaves the UI. To + // facilitate this, here we need to determine whether or not it + // is currently showing UI. + state.setSystemNoUi(true); + if (app == topApp) { + state.setSystemNoUi(false); + state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP); + state.setAdjType("pers-top-activity"); + } else if (state.hasTopUi()) { + // sched group/proc state adjustment is below + state.setSystemNoUi(false); + state.setAdjType("pers-top-ui"); + } else if (state.getCachedHasVisibleActivities()) { + state.setSystemNoUi(false); + } + if (!state.isSystemNoUi()) { + if (isScreenOnOrAnimatingLocked(state)) { + // screen on or animating, promote UI + state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI); + state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP); + } else if (!app.getWindowProcessController().isShowingUiWhileDozing()) { + // screen off, restrict UI scheduling + state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); + state.setCurrentSchedulingGroup(SCHED_GROUP_RESTRICTED); + } + } + state.setCurRawProcState(state.getCurProcState()); + state.setCurAdj(state.getMaxAdj()); + state.setCompletedAdjSeq(state.getAdjSeq()); + onProcessStateChanged(app, prevProcState); + onProcessOomAdjChanged(app, prevAppAdj); + // if curAdj is less than prevAppAdj, then this process was promoted + return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState; + } + + state.setSystemNoUi(false); + + final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState(); + + // Determine the importance of the process, starting with most + // important to least, and assign an appropriate OOM adjustment. + int adj; + int schedGroup; + int procState; + int capability = cycleReEval ? getInitialCapability(app) : 0; + + boolean foregroundActivities = false; + boolean hasVisibleActivities = false; + if (app == topApp && PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP) { + // The last app on the list is the foreground app. + adj = FOREGROUND_APP_ADJ; + if (mService.mAtmInternal.useTopSchedGroupForTopProcess()) { + schedGroup = SCHED_GROUP_TOP_APP; + state.setAdjType("top-activity"); + } else { + // Demote the scheduling group to avoid CPU contention if there is another more + // important process which also uses top-app, such as if SystemUI is animating. + schedGroup = SCHED_GROUP_DEFAULT; + state.setAdjType("intermediate-top-activity"); + } + foregroundActivities = true; + hasVisibleActivities = true; + procState = PROCESS_STATE_TOP; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app); + } + } else if (state.isRunningRemoteAnimation()) { + adj = VISIBLE_APP_ADJ; + schedGroup = SCHED_GROUP_TOP_APP; + state.setAdjType("running-remote-anim"); + procState = PROCESS_STATE_CUR_TOP; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app); + } + } else if (app.getActiveInstrumentation() != null) { + // Don't want to kill running instrumentation. + adj = FOREGROUND_APP_ADJ; + schedGroup = SCHED_GROUP_DEFAULT; + state.setAdjType("instrumentation"); + procState = PROCESS_STATE_FOREGROUND_SERVICE; + capability |= PROCESS_CAPABILITY_BFSL; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app); + } + } else if (state.getCachedIsReceivingBroadcast(mTmpSchedGroup)) { + // An app that is currently receiving a broadcast also + // counts as being in the foreground for OOM killer purposes. + // It's placed in a sched group based on the nature of the + // broadcast as reflected by which queue it's active in. + adj = FOREGROUND_APP_ADJ; + schedGroup = mTmpSchedGroup[0]; + state.setAdjType("broadcast"); + procState = ActivityManager.PROCESS_STATE_RECEIVER; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app); + } + } else if (psr.numberOfExecutingServices() > 0) { + // An app that is currently executing a service callback also + // counts as being in the foreground. + adj = FOREGROUND_APP_ADJ; + schedGroup = psr.shouldExecServicesFg() + ? SCHED_GROUP_DEFAULT : SCHED_GROUP_BACKGROUND; + state.setAdjType("exec-service"); + procState = PROCESS_STATE_SERVICE; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app); + } + } else if (app == topApp) { + adj = FOREGROUND_APP_ADJ; + schedGroup = SCHED_GROUP_BACKGROUND; + state.setAdjType("top-sleeping"); + foregroundActivities = true; + procState = PROCESS_STATE_CUR_TOP; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app); + } + } else { + // As far as we know the process is empty. We may change our mind later. + schedGroup = SCHED_GROUP_BACKGROUND; + // At this point we don't actually know the adjustment. Use the cached adj + // value that the caller wants us to. + adj = cachedAdj; + procState = PROCESS_STATE_CACHED_EMPTY; + if (!couldRecurse || !state.containsCycle()) { + state.setAdjType("cch-empty"); + } + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app); + } + } + + // Examine all activities if not already foreground. + if (!foregroundActivities && state.getCachedHasActivities()) { + state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback, + adj, foregroundActivities, hasVisibleActivities, procState, schedGroup, + appUid, logUid, PROCESS_STATE_CUR_TOP); + + adj = state.getCachedAdj(); + foregroundActivities = state.getCachedForegroundActivities(); + hasVisibleActivities = state.getCachedHasVisibleActivities(); + procState = state.getCachedProcState(); + schedGroup = state.getCachedSchedGroup(); + state.setAdjType(state.getCachedAdjType()); + } + + if (procState > PROCESS_STATE_CACHED_RECENT && state.getCachedHasRecentTasks()) { + procState = PROCESS_STATE_CACHED_RECENT; + state.setAdjType("cch-rec"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app); + } + } + + int capabilityFromFGS = 0; // capability from foreground service. + + final boolean hasForegroundServices = psr.hasForegroundServices(); + final boolean hasNonShortForegroundServices = psr.hasNonShortForegroundServices(); + final boolean hasShortForegroundServices = hasForegroundServices + && !psr.areAllShortForegroundServicesProcstateTimedOut(now); + + // Adjust for FGS or "has-overlay-ui". + if (adj > PERCEPTIBLE_APP_ADJ + || procState > PROCESS_STATE_FOREGROUND_SERVICE) { + String adjType = null; + int newAdj = 0; + int newProcState = 0; + + if (hasForegroundServices && hasNonShortForegroundServices) { + // For regular (non-short) FGS. + adjType = "fg-service"; + newAdj = PERCEPTIBLE_APP_ADJ; + newProcState = PROCESS_STATE_FOREGROUND_SERVICE; + capabilityFromFGS |= PROCESS_CAPABILITY_BFSL; + + } else if (hasShortForegroundServices) { + + // For short FGS. + adjType = "fg-service-short"; + + // We use MEDIUM_APP_ADJ + 1 so we can tell apart EJ + // (which uses MEDIUM_APP_ADJ + 1) + // from short-FGS. + // (We use +1 and +2, not +0 and +1, to be consistent with the following + // RECENT_FOREGROUND_APP_ADJ tweak) + newAdj = PERCEPTIBLE_MEDIUM_APP_ADJ + 1; + + // We give the FGS procstate, but not PROCESS_CAPABILITY_BFSL, so + // short-fgs can't start FGS from the background. + newProcState = PROCESS_STATE_FOREGROUND_SERVICE; + + } else if (state.hasOverlayUi()) { + adjType = "has-overlay-ui"; + newAdj = PERCEPTIBLE_APP_ADJ; + newProcState = PROCESS_STATE_IMPORTANT_FOREGROUND; + } + + if (adjType != null) { + adj = newAdj; + procState = newProcState; + state.setAdjType(adjType); + schedGroup = SCHED_GROUP_DEFAULT; + + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType + ": " + + app + " "); + } + } + } + + // If the app was recently in the foreground and moved to a foreground service status, + // allow it to get a higher rank in memory for some time, compared to other foreground + // services so that it can finish performing any persistence/processing of in-memory state. + if (psr.hasForegroundServices() && adj > PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + && (state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION > now + || state.getSetProcState() <= PROCESS_STATE_TOP)) { + if (psr.hasNonShortForegroundServices()) { + adj = PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; + state.setAdjType("fg-service-act"); + } else { + // For short-service FGS, we +1 the value, so we'll be able to detect it in + // various dashboards. + adj = PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 1; + state.setAdjType("fg-service-short-act"); + } + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app); + } + } + + // If the app was recently in the foreground and has expedited jobs running, + // allow it to get a higher rank in memory for some time, compared to other EJS and even + // foreground services so that it can finish performing any persistence/processing of + // in-memory state. + if (psr.hasTopStartedAlmostPerceptibleServices() + && (adj > PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2) + && (state.getLastTopTime() + + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION > now + || state.getSetProcState() <= PROCESS_STATE_TOP)) { + // For EJ, we +2 the value, so we'll be able to detect it in + // various dashboards. + adj = PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2; + // This shall henceforth be called the "EJ" exemption, despite utilizing the + // ALMOST_PERCEPTIBLE flag to work. + state.setAdjType("top-ej-act"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app); + } + } + + if (adj > PERCEPTIBLE_APP_ADJ + || procState > PROCESS_STATE_TRANSIENT_BACKGROUND) { + if (state.getForcingToImportant() != null) { + // This is currently used for toasts... they are not interactive, and + // we don't want them to cause the app to become fully foreground (and + // thus out of background check), so we yes the best background level we can. + adj = PERCEPTIBLE_APP_ADJ; + procState = PROCESS_STATE_TRANSIENT_BACKGROUND; + state.setAdjType("force-imp"); + state.setAdjSource(state.getForcingToImportant()); + schedGroup = SCHED_GROUP_DEFAULT; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app); + } + } + } + + if (state.getCachedIsHeavyWeight()) { + if (adj > HEAVY_WEIGHT_APP_ADJ) { + // We don't want to kill the current heavy-weight process. + adj = HEAVY_WEIGHT_APP_ADJ; + schedGroup = SCHED_GROUP_BACKGROUND; + state.setAdjType("heavy"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app); + } + } + if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { + procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; + state.setAdjType("heavy"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app); + } + } + } + + if (state.getCachedIsHomeProcess()) { + if (adj > HOME_APP_ADJ) { + // This process is hosting what we currently consider to be the + // home app, so we don't want to let it go into the background. + adj = HOME_APP_ADJ; + schedGroup = SCHED_GROUP_BACKGROUND; + state.setAdjType("home"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app); + } + } + if (procState > ActivityManager.PROCESS_STATE_HOME) { + procState = ActivityManager.PROCESS_STATE_HOME; + state.setAdjType("home"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app); + } + } + } + if (state.getCachedIsPreviousProcess() && state.getCachedHasActivities()) { + // This was the previous process that showed UI to the user. We want to + // try to keep it around more aggressively, to give a good experience + // around switching between two apps. However, we don't want to keep the + // process in this privileged state indefinitely. Eventually, allow the + // app to be demoted to cached. + if (procState >= PROCESS_STATE_LAST_ACTIVITY + && state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY + && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) { + procState = PROCESS_STATE_LAST_ACTIVITY; + schedGroup = SCHED_GROUP_BACKGROUND; + state.setAdjType("previous-expired"); + adj = CACHED_APP_MIN_ADJ; + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Expire prev adj: " + app); + } + } else { + if (adj > PREVIOUS_APP_ADJ) { + adj = PREVIOUS_APP_ADJ; + schedGroup = SCHED_GROUP_BACKGROUND; + state.setAdjType("previous"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app); + } + } + if (procState > PROCESS_STATE_LAST_ACTIVITY) { + procState = PROCESS_STATE_LAST_ACTIVITY; + state.setAdjType("previous"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app); + } + } + } + } + + if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj + + " reason=" + state.getAdjType()); + + // By default, we use the computed adjustment. It may be changed if + // there are applications dependent on our services or providers, but + // this gives us a baseline and makes sure we don't get into an + // infinite recursion. If we're re-evaluating due to cycles, use the previously computed + // values. + if (cycleReEval) { + procState = Math.min(procState, state.getCurRawProcState()); + adj = Math.min(adj, state.getCurRawAdj()); + schedGroup = Math.max(schedGroup, state.getCurrentSchedulingGroup()); + } + state.setCurRawAdj(adj); + state.setCurRawProcState(procState); + + state.setHasStartedServices(false); + state.setAdjSeq(mAdjSeq); + + final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId); + if (backupTarget != null && app == backupTarget.app) { + // If possible we want to avoid killing apps while they're being backed up + if (adj > BACKUP_APP_ADJ) { + if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app); + adj = BACKUP_APP_ADJ; + if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) { + procState = PROCESS_STATE_TRANSIENT_BACKGROUND; + } + state.setAdjType("backup"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app); + } + } + if (procState > ActivityManager.PROCESS_STATE_BACKUP) { + procState = ActivityManager.PROCESS_STATE_BACKUP; + state.setAdjType("backup"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app); + } + } + } + + state.setCurBoundByNonBgRestrictedApp(getInitialIsCurBoundByNonBgRestrictedApp(app)); + + state.setScheduleLikeTopApp(false); + + for (int is = psr.numberOfRunningServices() - 1; + is >= 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == SCHED_GROUP_BACKGROUND + || procState > PROCESS_STATE_TOP); + is--) { + ServiceRecord s = psr.getRunningServiceAt(is); + if (s.startRequested) { + state.setHasStartedServices(true); + if (procState > PROCESS_STATE_SERVICE) { + procState = PROCESS_STATE_SERVICE; + state.setAdjType("started-services"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to started service: " + app); + } + } + if (!s.mKeepWarming && state.hasShownUi() && !state.getCachedIsHomeProcess()) { + // If this process has shown some UI, let it immediately + // go to the LRU list because it may be pretty heavy with + // UI stuff. We'll tag it with a label just to help + // debug and understand what is going on. + if (adj > SERVICE_ADJ) { + state.setAdjType("cch-started-ui-services"); + } + } else { + if (s.mKeepWarming + || now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) { + // This service has seen some activity within + // recent memory, so we will keep its process ahead + // of the background processes. This does not apply + // to the SDK sandbox process since it should never + // be more important than its corresponding app. + if (!app.isSdkSandbox && adj > SERVICE_ADJ) { + adj = SERVICE_ADJ; + state.setAdjType("started-services"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise adj to started service: " + app); + } + } + } + // If we have let the service slide into the background + // state, still have some text describing what it is doing + // even though the service no longer has an impact. + if (adj > SERVICE_ADJ) { + state.setAdjType("cch-started-services"); + } + } + } + + if (s.isForeground) { + final int fgsType = s.foregroundServiceType; + if (s.isFgsAllowedWiu_forCapabilities()) { + capabilityFromFGS |= + (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION) + != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; + + if (foregroundAudioControl()) { // flag check + final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + | FOREGROUND_SERVICE_TYPE_CAMERA + | FOREGROUND_SERVICE_TYPE_MICROPHONE + | FOREGROUND_SERVICE_TYPE_PHONE_CALL; + capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0 + ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0; + } + + final boolean enabled = state.getCachedCompatChange( + CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY); + if (enabled) { + capabilityFromFGS |= + (fgsType & FOREGROUND_SERVICE_TYPE_CAMERA) + != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0; + capabilityFromFGS |= + (fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE) + != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0; + } else { + capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA + | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; + } + } + } + + if (!couldRecurse) { + // We're entering recursive functions below, if we're told it's not a recursive + // loop, abort here. + continue; + } + + + state.setCurRawAdj(adj); + state.setCurRawProcState(procState); + state.setCurrentSchedulingGroup(schedGroup); + state.setCurCapability(capability); + + ArrayMap> serviceConnections = s.getConnections(); + for (int conni = serviceConnections.size() - 1; + conni >= 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == SCHED_GROUP_BACKGROUND + || procState > PROCESS_STATE_TOP); + conni--) { + ArrayList clist = serviceConnections.valueAt(conni); + for (int i = 0; + i < clist.size() && (adj > FOREGROUND_APP_ADJ + || schedGroup == SCHED_GROUP_BACKGROUND + || procState > PROCESS_STATE_TOP); + i++) { + // XXX should compute this based on the max of + // all connected clients. + ConnectionRecord cr = clist.get(i); + if (cr.binding.client == app) { + // Binding to oneself is not interesting. + continue; + } + + computeServiceHostOomAdjLSP(cr, app, cr.binding.client, now, topApp, doingAll, + cycleReEval, computeClients, oomAdjReason, cachedAdj, true, false); + + adj = state.getCurRawAdj(); + procState = state.getCurRawProcState(); + schedGroup = state.getCurrentSchedulingGroup(); + capability = state.getCurCapability(); + } + } + } + + final ProcessProviderRecord ppr = app.mProviders; + for (int provi = ppr.numberOfProviders() - 1; + provi >= 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == SCHED_GROUP_BACKGROUND + || procState > PROCESS_STATE_TOP); + provi--) { + ContentProviderRecord cpr = ppr.getProviderAt(provi); + if (couldRecurse) { + // We're entering recursive functions below. + state.setCurRawAdj(adj); + state.setCurRawProcState(procState); + state.setCurrentSchedulingGroup(schedGroup); + state.setCurCapability(capability); + + for (int i = cpr.connections.size() - 1; + i >= 0 && (adj > FOREGROUND_APP_ADJ + || schedGroup == SCHED_GROUP_BACKGROUND + || procState > PROCESS_STATE_TOP); + i--) { + ContentProviderConnection conn = cpr.connections.get(i); + ProcessRecord client = conn.client; + computeProviderHostOomAdjLSP(conn, app, client, now, topApp, doingAll, + cycleReEval, computeClients, oomAdjReason, cachedAdj, true, false); + + adj = state.getCurRawAdj(); + procState = state.getCurRawProcState(); + schedGroup = state.getCurrentSchedulingGroup(); + capability = state.getCurCapability(); + } + } + // If the provider has external (non-framework) process + // dependencies, ensure that its adjustment is at least + // FOREGROUND_APP_ADJ. + if (cpr.hasExternalProcessHandles()) { + if (adj > FOREGROUND_APP_ADJ) { + adj = FOREGROUND_APP_ADJ; + state.setCurRawAdj(adj); + schedGroup = SCHED_GROUP_DEFAULT; + state.setAdjType("ext-provider"); + state.setAdjTarget(cpr.name); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise adj to external provider: " + app); + } + } + if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) { + procState = PROCESS_STATE_IMPORTANT_FOREGROUND; + state.setCurRawProcState(procState); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to external provider: " + app); + } + } + } + } + + if ((ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) { + if (adj > PREVIOUS_APP_ADJ) { + adj = PREVIOUS_APP_ADJ; + schedGroup = SCHED_GROUP_BACKGROUND; + state.setAdjType("recent-provider"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise adj to recent provider: " + app); + } + } + if (procState > PROCESS_STATE_LAST_ACTIVITY) { + procState = PROCESS_STATE_LAST_ACTIVITY; + state.setAdjType("recent-provider"); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise procstate to recent provider: " + app); + } + } + } + + if (procState >= PROCESS_STATE_CACHED_EMPTY) { + if (psr.hasClientActivities()) { + // This is a cached process, but with client activities. Mark it so. + procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT; + state.setAdjType("cch-client-act"); + } else if (psr.isTreatedLikeActivity()) { + // This is a cached process, but somebody wants us to treat it like it has + // an activity, okay! + procState = PROCESS_STATE_CACHED_ACTIVITY; + state.setAdjType("cch-as-act"); + } + } + + if (adj == SERVICE_ADJ) { + if (doingAll && !cycleReEval) { + state.setServiceB(mNewNumAServiceProcs > (mNumServiceProcs / 3)); + mNewNumServiceProcs++; + if (!state.isServiceB()) { + // This service isn't far enough down on the LRU list to + // normally be a B service, but if we are low on RAM and it + // is large we want to force it down since we would prefer to + // keep launcher over it. + long lastPssOrRss = mService.mAppProfiler.isProfilingPss() + ? app.mProfile.getLastPss() : app.mProfile.getLastRss(); + + // RSS is larger than PSS, but the RSS/PSS ratio varies per-process based on how + // many shared pages a process uses. The threshold is increased if the flag for + // reading RSS instead of PSS is enabled. + // + // TODO(b/296454553): Tune the second value so that the relative number of + // service B is similar before/after this flag is enabled. + double thresholdModifier = mService.mAppProfiler.isProfilingPss() + ? 1 : mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER; + double cachedRestoreThreshold = + mProcessList.getCachedRestoreThresholdKb() * thresholdModifier; + + if (!mService.mAppProfiler.isLastMemoryLevelNormal() + && lastPssOrRss >= cachedRestoreThreshold) { + state.setServiceHighRam(true); + state.setServiceB(true); + //Slog.i(TAG, "ADJ " + app + " high ram!"); + } else { + mNewNumAServiceProcs++; + //Slog.i(TAG, "ADJ " + app + " not high ram!"); + } + } else { + state.setServiceHighRam(false); + } + } + if (state.isServiceB()) { + adj = SERVICE_B_ADJ; + } + } + + state.setCurRawAdj(adj); + adj = psr.modifyRawOomAdj(adj); + if (adj > state.getMaxAdj()) { + adj = state.getMaxAdj(); + if (adj <= PERCEPTIBLE_LOW_APP_ADJ) { + schedGroup = SCHED_GROUP_DEFAULT; + } + } + + // Put bound foreground services in a special sched group for additional + // restrictions on screen off + if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE + && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE + && !state.shouldScheduleLikeTopApp()) { + if (schedGroup > SCHED_GROUP_RESTRICTED) { + schedGroup = SCHED_GROUP_RESTRICTED; + } + } + + // apply capability from FGS. + if (psr.hasForegroundServices()) { + capability |= capabilityFromFGS; + } + + capability |= getDefaultCapability(app, procState); + + // Procstates below BFGS should never have this capability. + if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + capability &= ~PROCESS_CAPABILITY_BFSL; + } + + if (app.isPendingFinishAttach()) { + // If the app is still starting up. We reset the computations to the + // hardcoded values in setAttachingProcessStatesLSP. This ensures that the app keeps + // hard-coded default 'startup' oom scores while starting up. When it finishes startup, + // we'll recompute oom scores based on it's actual hosted compoenents. + setAttachingProcessStatesLSP(app); + state.setAdjSeq(mAdjSeq); + state.setCompletedAdjSeq(state.getAdjSeq()); + return false; + } + + // Do final modification to adj. Everything we do between here and applying + // the final setAdj must be done in this function, because we will also use + // it when computing the final cached adj later. Note that we don't need to + // worry about this for max adj above, since max adj will always be used to + // keep it out of the cached vaues. + state.setCurCapability(capability); + state.updateLastInvisibleTime(hasVisibleActivities); + state.setHasForegroundActivities(foregroundActivities); + state.setCompletedAdjSeq(mAdjSeq); + + schedGroup = setIntermediateAdjLSP(app, adj, prevAppAdj, schedGroup); + setIntermediateProcStateLSP(app, procState, prevProcState); + + + //ccynice + if(alive_bl){ + state.setCurProcState(PROCESS_STATE_PERSISTENT); + state.setCurRawProcState(PROCESS_STATE_PERSISTENT); + } + + setIntermediateSchedGroupLSP(state, schedGroup); + + // if curAdj or curProcState improved, then this process was promoted + return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState + || state.getCurCapability() != prevCapability; + } + + /** + * @return The proposed change to the schedGroup. + */ + @GuardedBy({"mService", "mProcLock"}) + protected int setIntermediateAdjLSP(ProcessRecord app, int adj, int prevRawAppAdj, + int schedGroup) { + final ProcessStateRecord state = app.mState; + state.setCurRawAdj(adj); + + adj = app.mServices.modifyRawOomAdj(adj); + if (adj > state.getMaxAdj()) { + adj = state.getMaxAdj(); + if (adj <= PERCEPTIBLE_LOW_APP_ADJ) { + schedGroup = SCHED_GROUP_DEFAULT; + } + } + + state.setCurAdj(adj); + + return schedGroup; + } + + @GuardedBy({"mService", "mProcLock"}) + protected void setIntermediateProcStateLSP(ProcessRecord app, int procState, + int prevProcState) { + final ProcessStateRecord state = app.mState; + state.setCurProcState(procState); + state.setCurRawProcState(procState); + } + + @GuardedBy({"mService", "mProcLock"}) + protected void setIntermediateSchedGroupLSP(ProcessStateRecord state, int schedGroup) { + // Put bound foreground services in a special sched group for additional + // restrictions on screen off + if (state.getCurProcState() >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE + && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE + && !state.shouldScheduleLikeTopApp()) { + if (schedGroup > SCHED_GROUP_RESTRICTED) { + schedGroup = SCHED_GROUP_RESTRICTED; + } + } + + state.setCurrentSchedulingGroup(schedGroup); + } + + @GuardedBy({"mService", "mProcLock"}) + protected boolean computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app, + ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll, + boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj, + boolean couldRecurse, boolean dryRun) { + if (app.isPendingFinishAttach()) { + // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here. + return false; + } + + final ProcessStateRecord state = app.mState; + ProcessStateRecord cstate = client.mState; + boolean updated = false; + + if (couldRecurse) { + if (app.isSdkSandbox && cr.binding.attributedClient != null) { + // For SDK sandboxes, use the attributed client (eg the app that + // requested the sandbox) + client = cr.binding.attributedClient; + cstate = client.mState; + } + if (computeClients) { + computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true, + oomAdjReason, true); + } else { + cstate.setCurRawAdj(cstate.getCurAdj()); + cstate.setCurRawProcState(cstate.getCurProcState()); + } + } + + int clientAdj = cstate.getCurRawAdj(); + int clientProcState = cstate.getCurRawProcState(); + + final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP; + + int adj = state.getCurRawAdj(); + int procState = state.getCurRawProcState(); + int schedGroup = state.getCurrentSchedulingGroup(); + int capability = state.getCurCapability(); + + final int prevRawAdj = adj; + final int prevProcState = procState; + final int prevSchedGroup = schedGroup; + final int prevCapability = capability; + + final int appUid = app.info.uid; + final int logUid = mService.mCurOomAdjUid; + + if (!dryRun) { + state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp() + || cstate.isCurBoundByNonBgRestrictedApp() + || clientProcState <= PROCESS_STATE_BOUND_TOP + || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE + && !cstate.isBackgroundRestricted())); + } + + if (client.mOptRecord.shouldNotFreeze()) { + // Propagate the shouldNotFreeze flag down the bindings. + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + + boolean trackedProcState = false; + + // We always propagate PROCESS_CAPABILITY_BFSL over bindings here, + // but, right before actually setting it to the process, + // we check the final procstate, and remove it if the procsate is below BFGS. + capability |= getBfslCapabilityFromClient(client); + + if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) { + if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { + capability |= cstate.getCurCapability(); + } + + // If an app has network capability by default + // (by having procstate <= BFGS), then the apps it binds to will get + // elevated to a high enough procstate anyway to get network unless they + // request otherwise, so don't propagate the network capability by default + // in this case unless they explicitly request it. + if ((cstate.getCurCapability() + & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0) { + if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + // This is used to grant network access to Expedited Jobs. + if (cr.hasFlag(Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS)) { + capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; + } + } else { + capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; + } + } + if ((cstate.getCurCapability() + & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0) { + if (clientProcState <= PROCESS_STATE_IMPORTANT_FOREGROUND) { + // This is used to grant network access to User Initiated Jobs. + if (cr.hasFlag(Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) { + capability |= PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; + } + } + } + + if (couldRecurse && shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) { + return false; + } + + if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { + // If the other app is cached for any reason, for purposes here + // we are going to consider it empty. The specific cached state + // doesn't propagate except under certain conditions. + clientProcState = PROCESS_STATE_CACHED_EMPTY; + } + String adjType = null; + if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) { + // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen. + if (clientAdj < CACHED_APP_MIN_ADJ) { + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + // Not doing bind OOM management, so treat + // this guy more like a started service. + if (state.hasShownUi() && !state.getCachedIsHomeProcess()) { + // If this process has shown some UI, let it immediately + // go to the LRU list because it may be pretty heavy with + // UI stuff. We'll tag it with a label just to help + // debug and understand what is going on. + if (adj > clientAdj) { + adjType = "cch-bound-ui-services"; + } + + if (state.isCached() && dryRun) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + + clientAdj = adj; + clientProcState = procState; + } else { + if (now >= (cr.binding.service.lastActivity + + mConstants.MAX_SERVICE_INACTIVITY)) { + // This service has not seen activity within + // recent memory, so allow it to drop to the + // LRU list if there is no other reason to keep + // it around. We'll also tag it with a label just + // to help debug and undertand what is going on. + if (adj > clientAdj) { + adjType = "cch-bound-services"; + } + clientAdj = adj; + } + } + } + if (adj > clientAdj) { + // If this process has recently shown UI, and + // the process that is binding to it is less + // important than being visible, then we don't + // care about the binding as much as we care + // about letting this process get into the LRU + // list to be killed and restarted if needed for + // memory. + if (state.hasShownUi() && !state.getCachedIsHomeProcess() + && clientAdj > PERCEPTIBLE_APP_ADJ) { + if (adj >= CACHED_APP_MIN_ADJ) { + adjType = "cch-bound-ui-services"; + } + } else { + int newAdj; + int lbAdj = VISIBLE_APP_ADJ; // lower bound of adj. + if (cr.hasFlag(Context.BIND_ABOVE_CLIENT + | Context.BIND_IMPORTANT)) { + if (clientAdj >= PERSISTENT_SERVICE_ADJ) { + newAdj = clientAdj; + } else { + // make this service persistent + newAdj = PERSISTENT_SERVICE_ADJ; + schedGroup = SCHED_GROUP_DEFAULT; + procState = ActivityManager.PROCESS_STATE_PERSISTENT; + if (!dryRun) { + cr.trackProcState(procState, mAdjSeq); + } + trackedProcState = true; + } + } else if (cr.hasFlag(Context.BIND_NOT_PERCEPTIBLE) + && clientAdj <= PERCEPTIBLE_APP_ADJ + && adj >= (lbAdj = PERCEPTIBLE_LOW_APP_ADJ)) { + newAdj = PERCEPTIBLE_LOW_APP_ADJ; + } else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE) + && cr.notHasFlag(Context.BIND_NOT_FOREGROUND) + && clientAdj < PERCEPTIBLE_APP_ADJ + && adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) { + // This is for user-initiated jobs. + // We use APP_ADJ + 1 here, so we can tell them apart from FGS. + newAdj = PERCEPTIBLE_APP_ADJ + 1; + } else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE) + && cr.hasFlag(Context.BIND_NOT_FOREGROUND) + && clientAdj < PERCEPTIBLE_APP_ADJ + && adj >= (lbAdj = (PERCEPTIBLE_MEDIUM_APP_ADJ + 2))) { + // This is for expedited jobs. + // We use MEDIUM_APP_ADJ + 2 here, so we can tell apart + // EJ and short-FGS. + newAdj = PERCEPTIBLE_MEDIUM_APP_ADJ + 2; + } else if (cr.hasFlag(Context.BIND_NOT_VISIBLE) + && clientAdj < PERCEPTIBLE_APP_ADJ + && adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) { + newAdj = PERCEPTIBLE_APP_ADJ; + } else if (clientAdj >= PERCEPTIBLE_APP_ADJ) { + newAdj = clientAdj; + } else if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE) + && clientAdj <= VISIBLE_APP_ADJ + && adj > VISIBLE_APP_ADJ) { + newAdj = VISIBLE_APP_ADJ; + } else { + if (adj > VISIBLE_APP_ADJ) { + // TODO: Is this too limiting for apps bound from TOP? + newAdj = Math.max(clientAdj, lbAdj); + } else { + newAdj = adj; + } + } + + if (!cstate.isCached()) { + if (state.isCached() && dryRun) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + + if (adj > newAdj) { + adj = newAdj; + if (state.setCurRawAdj(adj, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + } + adjType = "service"; + } + } + } + if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND + | Context.BIND_IMPORTANT_BACKGROUND)) { + // This will treat important bound services identically to + // the top app, which may behave differently than generic + // foreground work. + final int curSchedGroup = cstate.getCurrentSchedulingGroup(); + if (curSchedGroup > schedGroup) { + if (cr.hasFlag(Context.BIND_IMPORTANT)) { + schedGroup = curSchedGroup; + } else { + schedGroup = SCHED_GROUP_DEFAULT; + } + } + if (clientProcState < PROCESS_STATE_TOP) { + // Special handling for above-top states (persistent + // processes). These should not bring the current process + // into the top state, since they are not on top. Instead + // give them the best bound state after that. + if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)) { + clientProcState = PROCESS_STATE_FOREGROUND_SERVICE; + } else if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) { + clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + } else if (mService.mWakefulness.get() + == PowerManagerInternal.WAKEFULNESS_AWAKE + && cr.hasFlag(Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) { + clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + } else { + clientProcState = + PROCESS_STATE_IMPORTANT_FOREGROUND; + } + } else if (clientProcState == PROCESS_STATE_TOP) { + // Go at most to BOUND_TOP, unless requested to elevate + // to client's state. + clientProcState = PROCESS_STATE_BOUND_TOP; + final boolean enabled = cstate.getCachedCompatChange( + CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY); + if (enabled) { + if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { + // TOP process passes all capabilities to the service. + capability |= cstate.getCurCapability(); + } else { + // TOP process passes no capability to the service. + } + } else { + // TOP process passes all capabilities to the service. + capability |= cstate.getCurCapability(); + } + } + } else if (cr.notHasFlag(Context.BIND_IMPORTANT_BACKGROUND)) { + if (clientProcState < PROCESS_STATE_TRANSIENT_BACKGROUND) { + clientProcState = + PROCESS_STATE_TRANSIENT_BACKGROUND; + } + } else { + if (clientProcState < PROCESS_STATE_IMPORTANT_BACKGROUND) { + clientProcState = + PROCESS_STATE_IMPORTANT_BACKGROUND; + } + } + + if (cr.hasFlag(Context.BIND_SCHEDULE_LIKE_TOP_APP) && clientIsSystem) { + schedGroup = SCHED_GROUP_TOP_APP; + if (dryRun) { + if (prevSchedGroup < schedGroup) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } else { + state.setScheduleLikeTopApp(true); + } + } + + if (!trackedProcState && !dryRun) { + cr.trackProcState(clientProcState, mAdjSeq); + } + + if (procState > clientProcState) { + procState = clientProcState; + if (state.setCurRawProcState(procState, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + if (adjType == null) { + adjType = "service"; + } + } + if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND + && cr.hasFlag(Context.BIND_SHOWING_UI) && !dryRun) { + app.setPendingUiClean(true); + } + if (adjType != null && !dryRun) { + state.setAdjType(adjType); + state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE); + state.setAdjSource(client); + state.setAdjSourceProcState(clientProcState); + state.setAdjTarget(cr.binding.service.instanceName); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType + + ": " + app + ", due to " + client + + " adj=" + adj + " procState=" + + ProcessList.makeProcStateString(procState)); + } + } + } else { // BIND_WAIVE_PRIORITY == true + // BIND_WAIVE_PRIORITY bindings are special when it comes to the + // freezer. Processes bound via WPRI are expected to be running, + // but they are not promoted in the LRU list to keep them out of + // cached. As a result, they can freeze based on oom_adj alone. + // Normally, bindToDeath would fire when a cached app would die + // in the background, but nothing will fire when a running process + // pings a frozen process. Accordingly, any cached app that is + // bound by an unfrozen app via a WPRI binding has to remain + // unfrozen. + if (clientAdj < CACHED_APP_MIN_ADJ) { + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + } + if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) { + if (!dryRun) { + app.mServices.setTreatLikeActivity(true); + } + if (clientProcState <= PROCESS_STATE_CACHED_ACTIVITY + && procState > PROCESS_STATE_CACHED_ACTIVITY) { + // This is a cached process, but somebody wants us to treat it like it has + // an activity, okay! + procState = PROCESS_STATE_CACHED_ACTIVITY; + state.setAdjType("cch-as-act"); + } + } + final ActivityServiceConnectionsHolder a = cr.activity; + if (cr.hasFlag(Context.BIND_ADJUST_WITH_ACTIVITY)) { + if (a != null && adj > FOREGROUND_APP_ADJ + && a.isActivityVisible()) { + adj = FOREGROUND_APP_ADJ; + if (state.setCurRawAdj(adj, dryRun)) { + return true; + } + if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND)) { + if (cr.hasFlag(Context.BIND_IMPORTANT)) { + schedGroup = SCHED_GROUP_TOP_APP_BOUND; + } else { + schedGroup = SCHED_GROUP_DEFAULT; + } + } + + if (!dryRun) { + state.setAdjType("service"); + state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE); + state.setAdjSource(a); + state.setAdjSourceProcState(procState); + state.setAdjTarget(cr.binding.service.instanceName); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, + "Raise to service w/activity: " + app); + } + } + } + } + + capability |= getDefaultCapability(app, procState); + + // Procstates below BFGS should never have this capability. + if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + capability &= ~PROCESS_CAPABILITY_BFSL; + } + if (!updated) { + updated = adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup + || (capability != prevCapability + && (capability & prevCapability) == prevCapability); + } + + if (dryRun) { + return updated; + } + if (adj < prevRawAdj) { + schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup); + } + if (procState < prevProcState) { + setIntermediateProcStateLSP(app, procState, prevProcState); + } + if (schedGroup > prevSchedGroup) { + setIntermediateSchedGroupLSP(state, schedGroup); + } + state.setCurCapability(capability); + + return updated; + } + + protected boolean computeProviderHostOomAdjLSP(ContentProviderConnection conn, + ProcessRecord app, ProcessRecord client, long now, ProcessRecord topApp, + boolean doingAll, boolean cycleReEval, boolean computeClients, int oomAdjReason, + int cachedAdj, boolean couldRecurse, boolean dryRun) { + if (app.isPendingFinishAttach()) { + // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here. + return false; + } + + final ProcessStateRecord state = app.mState; + final ProcessStateRecord cstate = client.mState; + + if (client == app) { + // Being our own client is not interesting. + return false; + } + if (couldRecurse) { + if (computeClients) { + computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true, + oomAdjReason, true); + } else if (couldRecurse) { + cstate.setCurRawAdj(cstate.getCurAdj()); + cstate.setCurRawProcState(cstate.getCurProcState()); + } + + if (shouldSkipDueToCycle(app, cstate, state.getCurRawProcState(), state.getCurRawAdj(), + cycleReEval)) { + return false; + } + } + + int clientAdj = cstate.getCurRawAdj(); + int clientProcState = cstate.getCurRawProcState(); + + int adj = state.getCurRawAdj(); + int procState = state.getCurRawProcState(); + int schedGroup = state.getCurrentSchedulingGroup(); + int capability = state.getCurCapability(); + + final int prevRawAdj = adj; + final int prevProcState = procState; + final int prevSchedGroup = schedGroup; + final int prevCapability = capability; + + final int appUid = app.info.uid; + final int logUid = mService.mCurOomAdjUid; + + // We always propagate PROCESS_CAPABILITY_BFSL to providers here, + // but, right before actually setting it to the process, + // we check the final procstate, and remove it if the procsate is below BFGS. + capability |= getBfslCapabilityFromClient(client); + + if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { + // If the other app is cached for any reason, for purposes here + // we are going to consider it empty. + clientProcState = PROCESS_STATE_CACHED_EMPTY; + } + if (client.mOptRecord.shouldNotFreeze()) { + // Propagate the shouldNotFreeze flag down the bindings. + if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + + if (!dryRun) { + state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp() + || cstate.isCurBoundByNonBgRestrictedApp() + || clientProcState <= PROCESS_STATE_BOUND_TOP + || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE + && !cstate.isBackgroundRestricted())); + } + + String adjType = null; + if (adj > clientAdj) { + if (state.hasShownUi() && !state.getCachedIsHomeProcess() + && clientAdj > PERCEPTIBLE_APP_ADJ) { + adjType = "cch-ui-provider"; + } else { + adj = Math.max(clientAdj, FOREGROUND_APP_ADJ); + if (state.setCurRawAdj(adj, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + adjType = "provider"; + } + + if (state.isCached() && !cstate.isCached() && dryRun) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + + if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) { + if (adjType == null) { + adjType = "provider"; + } + if (clientProcState == PROCESS_STATE_TOP) { + clientProcState = PROCESS_STATE_BOUND_TOP; + } else { + clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + } + } + + if (!dryRun) { + conn.trackProcState(clientProcState, mAdjSeq); + } + if (procState > clientProcState) { + procState = clientProcState; + if (state.setCurRawProcState(procState, dryRun)) { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } + } + if (cstate.getCurrentSchedulingGroup() > schedGroup) { + schedGroup = SCHED_GROUP_DEFAULT; + } + if (adjType != null && !dryRun) { + state.setAdjType(adjType); + state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo + .REASON_PROVIDER_IN_USE); + state.setAdjSource(client); + state.setAdjSourceProcState(clientProcState); + state.setAdjTarget(conn.provider.name); + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType + + ": " + app + ", due to " + client + + " adj=" + adj + " procState=" + + ProcessList.makeProcStateString(procState)); + } + } + + // Procstates below BFGS should never have this capability. + if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + capability &= ~PROCESS_CAPABILITY_BFSL; + } + + if (dryRun && (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup + || (capability != prevCapability + && (capability & prevCapability) == prevCapability))) { + return true; + } + + if (adj < prevRawAdj) { + schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup); + } + if (procState < prevProcState) { + setIntermediateProcStateLSP(app, procState, prevProcState); + } + if (schedGroup > prevSchedGroup) { + setIntermediateSchedGroupLSP(state, schedGroup); + } + state.setCurCapability(capability); + + return false; + } + + protected int getDefaultCapability(ProcessRecord app, int procState) { + final int networkCapabilities = + NetworkPolicyManager.getDefaultProcessNetworkCapabilities(procState); + final int baseCapabilities; + switch (procState) { + case PROCESS_STATE_PERSISTENT: + case PROCESS_STATE_PERSISTENT_UI: + case PROCESS_STATE_TOP: + baseCapabilities = PROCESS_CAPABILITY_ALL; // BFSL allowed + break; + case PROCESS_STATE_BOUND_TOP: + baseCapabilities = PROCESS_CAPABILITY_BFSL; + break; + case PROCESS_STATE_FOREGROUND_SERVICE: + if (app.getActiveInstrumentation() != null) { + baseCapabilities = PROCESS_CAPABILITY_ALL_IMPLICIT; + } else { + // Capability from foreground service is conditional depending on + // foregroundServiceType in the manifest file and the + // mAllowWhileInUsePermissionInFgs flag. + baseCapabilities = PROCESS_CAPABILITY_NONE; + } + break; + default: + baseCapabilities = PROCESS_CAPABILITY_NONE; + break; + } + return baseCapabilities | networkCapabilities; + } + + /** + * @return the BFSL capability from a client (of a service binding or provider). + */ + protected int getBfslCapabilityFromClient(ProcessRecord client) { + // Procstates above FGS should always have this flag. We shouldn't need this logic, + // but let's do it just in case. + if (client.mState.getCurProcState() < PROCESS_STATE_FOREGROUND_SERVICE) { + return PROCESS_CAPABILITY_BFSL; + } + // Otherwise, use the process's cur capability. + + // Note: BFSL is a per-UID check, not per-process, but here, the BFSL capability is still + // propagated on a per-process basis. + // + // For example, consider this case: + // - There are App 1 and App 2. + // - App 1 has two processes + // Proc #1A, procstate BFGS with CAPABILITY_BFSL + // Proc #1B, procstate FGS with no CAPABILITY_BFSL (i.e. process has a short FGS) + // And this process binds to Proc #2 of App 2. + // + // (Note because #1A has CAPABILITY_BFSL, App 1's UidRecord has CAPABILITY_BFSL.) + // + // - App 2 has one process: + // Proc #2, procstate FGS due to the above binding, _with no CAPABILITY_BFSL_. + // + // In this case, #2 will not get CAPABILITY_BFSL because the binding client (#1B) + // doesn't have this capability. (Even though App 1's UidRecord has it.) + // + // This may look weird, because App 2 _is_ still BFSL allowed, because "it's bound by + // an app that is BFSL-allowed". (See [bookmark: 61867f60-007c-408c-a2c4-e19e96056135] + // in ActiveServices.) + // + // So why don't we propagate PROCESS_CAPABILITY_BFSL from App 1's UID record? + // This is because short-FGS acts like "below BFGS" as far as BFSL is concerned, + // similar to how JobScheduler jobs are below BFGS and apps can't start FGS from there. + // + // If #1B was running a job instead of a short-FGS, then its procstate would be below BFGS. + // Then #2's procstate would also be below BFGS. So #2 wouldn't get CAPABILITY_BFSL. + // Similarly, if #1B has a short FGS, even though the procstate of #1B and #2 would be FGS, + // they both still wouldn't get CAPABILITY_BFSL. + // + // However, again, because #2 is bound by App 1, which is BFSL-allowed (because of #1A) + // App 2 would still BFSL-allowed, due to the aforementioned check in ActiveServices. + return client.mState.getCurCapability() & PROCESS_CAPABILITY_BFSL; + } + + /** + * Checks if for the given app and client, there's a cycle that should skip over the client + * for now or use partial values to evaluate the effect of the client binding. + * @param app + * @param client + * @param procState procstate evaluated so far for this app + * @param adj oom_adj evaluated so far for this app + * @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first + * evaluation. + * @return whether to skip using the client connection at this time + */ + private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessStateRecord client, + int procState, int adj, boolean cycleReEval) { + if (client.containsCycle()) { + // We've detected a cycle. We should retry computeOomAdjLSP later in + // case a later-checked connection from a client would raise its + // priority legitimately. + app.mState.setContainsCycle(true); + mProcessesInCycle.add(app); + // If the client has not been completely evaluated, check if it's worth + // using the partial values. + if (client.getCompletedAdjSeq() < mAdjSeq) { + if (cycleReEval) { + // If the partial values are no better, skip until the next + // attempt + if (client.getCurRawProcState() >= procState + && client.getCurRawAdj() >= adj) { + return true; + } + // Else use the client's partial procstate and adj to adjust the + // effect of the binding + } else { + return true; + } + } + } + return false; + } + + /** Inform the oomadj observer of changes to oomadj. Used by tests. */ + @GuardedBy("mService") + protected void reportOomAdjMessageLocked(String tag, String msg) { + Slog.d(tag, msg); + synchronized (mService.mOomAdjObserverLock) { + if (mService.mCurOomAdjObserver != null) { + mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg) + .sendToTarget(); + } + } + } + + void onWakefulnessChanged(int wakefulness) { + mCachedAppOptimizer.onWakefulnessChanged(wakefulness); + } + + /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */ + @GuardedBy({"mService", "mProcLock"}) + protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, + long nowElapsed, @OomAdjReason int oomAdjReson) { + boolean success = true; + final ProcessStateRecord state = app.mState; + final UidRecord uidRec = app.getUidRecord(); + + if (state.getCurRawAdj() != state.getSetRawAdj()) { + state.setSetRawAdj(state.getCurRawAdj()); + } + + int changes = 0; + + if (state.getCurAdj() != state.getSetAdj()) { + mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app); + } + + if (state.getCurAdj() != state.getSetAdj()) { + ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) { + String msg = "Set " + app.getPid() + " " + app.processName + " adj " + + state.getCurAdj() + ": " + state.getAdjType(); + reportOomAdjMessageLocked(TAG_OOM_ADJ, msg); + } + state.setSetAdj(state.getCurAdj()); + if (uidRec != null) { + uidRec.noteProcAdjChanged(); + } + state.setVerifiedAdj(INVALID_ADJ); + } + + final int curSchedGroup = state.getCurrentSchedulingGroup(); + if (state.getSetSchedGroup() != curSchedGroup) { + int oldSchedGroup = state.getSetSchedGroup(); + state.setSetSchedGroup(curSchedGroup); + if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) { + String msg = "Setting sched group of " + app.processName + + " to " + curSchedGroup + ": " + state.getAdjType(); + reportOomAdjMessageLocked(TAG_OOM_ADJ, msg); + } + if (app.getWaitingToKill() != null && app.mReceivers.numberOfCurReceivers() == 0 + && ActivityManager.isProcStateBackground(state.getSetProcState()) + && !state.hasStartedServices()) { + app.killLocked(app.getWaitingToKill(), ApplicationExitInfo.REASON_USER_REQUESTED, + ApplicationExitInfo.SUBREASON_REMOVE_TASK, true); + success = false; + } else { + int processGroup; + switch (curSchedGroup) { + case SCHED_GROUP_BACKGROUND: + processGroup = THREAD_GROUP_BACKGROUND; + break; + case SCHED_GROUP_TOP_APP: + case SCHED_GROUP_TOP_APP_BOUND: + processGroup = THREAD_GROUP_TOP_APP; + break; + case SCHED_GROUP_RESTRICTED: + processGroup = THREAD_GROUP_RESTRICTED; + break; + default: + processGroup = THREAD_GROUP_DEFAULT; + break; + } + mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage( + 0 /* unused */, app.getPid(), processGroup, app.processName)); + try { + final int renderThreadTid = app.getRenderThreadTid(); + if (curSchedGroup == SCHED_GROUP_TOP_APP) { + // do nothing if we already switched to RT + if (oldSchedGroup != SCHED_GROUP_TOP_APP) { + app.getWindowProcessController().onTopProcChanged(); + if (mService.mUseFifoUiScheduling) { + // Switch UI pipeline for app to SCHED_FIFO + state.setSavedPriority(Process.getThreadPriority(app.getPid())); + mService.scheduleAsFifoPriority(app.getPid(), true); + if (renderThreadTid != 0) { + mService.scheduleAsFifoPriority(renderThreadTid, + /* suppressLogs */true); + if (DEBUG_OOM_ADJ) { + Slog.d("UI_FIFO", "Set RenderThread (TID " + + renderThreadTid + ") to FIFO"); + } + } else { + if (DEBUG_OOM_ADJ) { + Slog.d("UI_FIFO", "Not setting RenderThread TID"); + } + } + } else { + // Boost priority for top app UI and render threads + setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST); + if (renderThreadTid != 0) { + try { + setThreadPriority(renderThreadTid, + THREAD_PRIORITY_TOP_APP_BOOST); + } catch (IllegalArgumentException e) { + // thread died, ignore + } + } + } + } + } else if (oldSchedGroup == SCHED_GROUP_TOP_APP + && curSchedGroup != SCHED_GROUP_TOP_APP) { + app.getWindowProcessController().onTopProcChanged(); + if (mService.mUseFifoUiScheduling) { + try { + // Reset UI pipeline to SCHED_OTHER + setThreadScheduler(app.getPid(), SCHED_OTHER, 0); + setThreadPriority(app.getPid(), state.getSavedPriority()); + if (renderThreadTid != 0) { + setThreadScheduler(renderThreadTid, + SCHED_OTHER, 0); + } + } catch (IllegalArgumentException e) { + Slog.w(TAG, + "Failed to set scheduling policy, thread does not exist:\n" + + e); + } catch (SecurityException e) { + Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e); + } + } else { + // Reset priority for top app UI and render threads + setThreadPriority(app.getPid(), 0); + } + + if (renderThreadTid != 0) { + setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY); + } + } + } catch (Exception e) { + if (DEBUG_ALL) { + Slog.w(TAG, "Failed setting thread priority of " + app.getPid(), e); + } + } + } + } + if (state.hasRepForegroundActivities() != state.hasForegroundActivities()) { + state.setRepForegroundActivities(state.hasForegroundActivities()); + changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES; + } + + updateAppFreezeStateLSP(app, oomAdjReson, false); + + if (state.getReportedProcState() != state.getCurProcState()) { + state.setReportedProcState(state.getCurProcState()); + if (app.getThread() != null) { + try { + if (false) { + //RuntimeException h = new RuntimeException("here"); + Slog.i(TAG, "Sending new process state " + state.getReportedProcState() + + " to " + app /*, h*/); + } + app.getThread().setProcessState(state.getReportedProcState()); + } catch (RemoteException e) { + } + } + } + boolean forceUpdatePssTime = false; + if (state.getSetProcState() == PROCESS_STATE_NONEXISTENT + || ProcessList.procStatesDifferForMem( + state.getCurProcState(), state.getSetProcState())) { + state.setLastStateTime(now); + forceUpdatePssTime = true; + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "Process state change from " + + ProcessList.makeProcStateString(state.getSetProcState()) + " to " + + ProcessList.makeProcStateString(state.getCurProcState()) + " next pss in " + + (app.mProfile.getNextPssTime() - now) + ": " + app); + } + } + synchronized (mService.mAppProfiler.mProfilerLock) { + app.mProfile.updateProcState(app.mState); + mService.mAppProfiler.updateNextPssTimeLPf( + state.getCurProcState(), app.mProfile, now, forceUpdatePssTime); + } + if (state.getSetProcState() != state.getCurProcState()) { + if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) { + String msg = "Proc state change of " + app.processName + + " to " + ProcessList.makeProcStateString(state.getCurProcState()) + + " (" + state.getCurProcState() + ")" + ": " + state.getAdjType(); + reportOomAdjMessageLocked(TAG_OOM_ADJ, msg); + } + boolean setImportant = state.getSetProcState() < PROCESS_STATE_SERVICE; + boolean curImportant = state.getCurProcState() < PROCESS_STATE_SERVICE; + if (setImportant && !curImportant) { + // This app is no longer something we consider important enough to allow to use + // arbitrary amounts of battery power. Note its current CPU time to later know to + // kill it if it is not behaving well. + state.setWhenUnimportant(now); + app.mProfile.mLastCpuTime.set(0); + } + // Inform UsageStats of important process state change + // Must be called before updating setProcState + maybeUpdateUsageStatsLSP(app, nowElapsed); + + maybeUpdateLastTopTime(state, now); + + state.setSetProcState(state.getCurProcState()); + if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) { + state.setNotCachedSinceIdle(false); + } + if (!doingAll) { + synchronized (mService.mProcessStats.mLock) { + mService.setProcessTrackerStateLOSP(app, + mService.mProcessStats.getMemFactorLocked()); + } + } else { + state.setProcStateChanged(true); + } + } else if (state.hasReportedInteraction()) { + final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange( + CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME); + final long interactionThreshold = fgsInteractionChangeEnabled + ? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S + : mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S; + // For apps that sit around for a long time in the interactive state, we need + // to report this at least once a day so they don't go idle. + if ((nowElapsed - state.getInteractionEventTime()) > interactionThreshold) { + maybeUpdateUsageStatsLSP(app, nowElapsed); + } + } else { + final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange( + CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME); + final long interactionThreshold = fgsInteractionChangeEnabled + ? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S + : mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S; + // For foreground services that sit around for a long time but are not interacted with. + if ((nowElapsed - state.getFgInteractionTime()) > interactionThreshold) { + maybeUpdateUsageStatsLSP(app, nowElapsed); + } + } + + if (state.getCurCapability() != state.getSetCapability()) { + state.setSetCapability(state.getCurCapability()); + } + + final boolean curBoundByNonBgRestrictedApp = state.isCurBoundByNonBgRestrictedApp(); + if (curBoundByNonBgRestrictedApp != state.isSetBoundByNonBgRestrictedApp()) { + state.setSetBoundByNonBgRestrictedApp(curBoundByNonBgRestrictedApp); + if (!curBoundByNonBgRestrictedApp && state.isBackgroundRestricted()) { + mService.mHandler.post(() -> { + synchronized (mService) { + mService.mServices.stopAllForegroundServicesLocked( + app.uid, app.info.packageName); + } + }); + } + } + + if (changes != 0) { + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, + "Changes in " + app + ": " + changes); + ActivityManagerService.ProcessChangeItem item = + mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid); + item.changes |= changes; + item.foregroundActivities = state.hasRepForegroundActivities(); + if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, + "Item " + Integer.toHexString(System.identityHashCode(item)) + + " " + app.toShortString() + ": changes=" + item.changes + + " foreground=" + item.foregroundActivities + + " type=" + state.getAdjType() + " source=" + state.getAdjSource() + + " target=" + state.getAdjTarget()); + } + + if (state.isCached() && !state.shouldNotKillOnBgRestrictedAndIdle()) { + // It's eligible to get killed when in UID idle and bg restricted mode, + // check if these states are just flipped. + if (!state.isSetCached() || state.isSetNoKillOnBgRestrictedAndIdle()) { + // Take the timestamp, we'd hold the killing for the background settle time + // (for states debouncing to avoid from thrashing). + state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed); + // Kick off the delayed checkup message if needed. + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, + mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs); + } + } + } + state.setSetCached(state.isCached()); + state.setSetNoKillOnBgRestrictedAndIdle(state.shouldNotKillOnBgRestrictedAndIdle()); + + return success; + } + + @GuardedBy({"mService", "mProcLock"}) + void setAttachingProcessStatesLSP(ProcessRecord app) { + int initialSchedGroup = SCHED_GROUP_DEFAULT; + int initialProcState = PROCESS_STATE_CACHED_EMPTY; + int initialCapability = PROCESS_CAPABILITY_NONE; + boolean initialCached = true; + final ProcessStateRecord state = app.mState; + final int prevProcState = state.getCurRawProcState(); + final int prevAdj = state.getCurRawAdj(); + // If the process has been marked as foreground, it is starting as the top app (with + // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread. + if (state.hasForegroundActivities()) { + try { + // The priority must be the same as how does {@link #applyOomAdjLSP} set for + // {@link SCHED_GROUP_TOP_APP}. We don't check render thread because it + // is not ready when attaching. + app.getWindowProcessController().onTopProcChanged(); + if (mService.mUseFifoUiScheduling) { + mService.scheduleAsFifoPriority(app.getPid(), true); + } else { + setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST); + } + if (isScreenOnOrAnimatingLocked(state)) { + initialSchedGroup = SCHED_GROUP_TOP_APP; + initialProcState = PROCESS_STATE_TOP; + } + initialCapability = PROCESS_CAPABILITY_ALL; + initialCached = false; + } catch (Exception e) { + Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e); + } + } + + state.setCurrentSchedulingGroup(initialSchedGroup); + state.setCurProcState(initialProcState); + state.setCurRawProcState(initialProcState); + state.setCurCapability(initialCapability); + + state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ); + state.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ); + state.setForcingToImportant(null); + state.setHasShownUi(false); + + onProcessStateChanged(app, prevProcState); + onProcessOomAdjChanged(app, prevAdj); + } + + // ONLY used for unit testing in OomAdjusterTests.java + @VisibleForTesting + void maybeUpdateUsageStats(ProcessRecord app, long nowElapsed) { + synchronized (mService) { + synchronized (mProcLock) { + maybeUpdateUsageStatsLSP(app, nowElapsed); + } + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void maybeUpdateUsageStatsLSP(ProcessRecord app, long nowElapsed) { + final ProcessStateRecord state = app.mState; + if (DEBUG_USAGE_STATS) { + Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList()) + + "] state changes: old = " + state.getSetProcState() + ", new = " + + state.getCurProcState()); + } + if (mService.mUsageStatsService == null) { + return; + } + final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange( + CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME); + boolean isInteraction; + // To avoid some abuse patterns, we are going to be careful about what we consider + // to be an app interaction. Being the top activity doesn't count while the display + // is sleeping, nor do short foreground services. + if (ActivityManager.isProcStateConsideredInteraction(state.getCurProcState())) { + isInteraction = true; + state.setFgInteractionTime(0); + } else if (state.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) { + if (state.getFgInteractionTime() == 0) { + state.setFgInteractionTime(nowElapsed); + isInteraction = false; + } else { + final long interactionTime = fgsInteractionChangeEnabled + ? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S + : mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S; + isInteraction = nowElapsed > state.getFgInteractionTime() + interactionTime; + } + } else { + isInteraction = + state.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND; + state.setFgInteractionTime(0); + } + final long interactionThreshold = fgsInteractionChangeEnabled + ? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S + : mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S; + if (isInteraction + && (!state.hasReportedInteraction() + || (nowElapsed - state.getInteractionEventTime()) > interactionThreshold)) { + state.setInteractionEventTime(nowElapsed); + String[] packages = app.getPackageList(); + if (packages != null) { + for (int i = 0; i < packages.length; i++) { + mService.mUsageStatsService.reportEvent(packages[i], app.userId, + UsageEvents.Event.SYSTEM_INTERACTION); + } + } + } + state.setReportedInteraction(isInteraction); + if (!isInteraction) { + state.setInteractionEventTime(0); + } + } + + private void maybeUpdateLastTopTime(ProcessStateRecord state, long nowUptime) { + if (state.getSetProcState() <= PROCESS_STATE_TOP + && state.getCurProcState() > PROCESS_STATE_TOP) { + state.setLastTopTime(nowUptime); + } + } + + /** + * Look for recently inactive apps and mark them idle after a grace period. If idled, stop + * any background services and inform listeners. + */ + @GuardedBy("mService") + void idleUidsLocked() { + final int N = mActiveUids.size(); + mService.mHandler.removeMessages(IDLE_UIDS_MSG); + if (N <= 0) { + return; + } + final long nowElapsed = SystemClock.elapsedRealtime(); + final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME; + long nextTime = 0; + if (mService.mLocalPowerManager != null) { + mService.mLocalPowerManager.startUidChanges(); + } + for (int i = N - 1; i >= 0; i--) { + final UidRecord uidRec = mActiveUids.valueAt(i); + final long bgTime = uidRec.getLastBackgroundTime(); + final long idleTime = uidRec.getLastIdleTime(); + if (bgTime > 0 && (!uidRec.isIdle() || idleTime == 0)) { + if (bgTime <= maxBgTime) { + EventLogTags.writeAmUidIdle(uidRec.getUid()); + synchronized (mProcLock) { + uidRec.setIdle(true); + uidRec.setSetIdle(true); + uidRec.setLastIdleTime(nowElapsed); + } + mService.doStopUidLocked(uidRec.getUid(), uidRec); + } else { + if (nextTime == 0 || nextTime > bgTime) { + nextTime = bgTime; + } + } + } + } + if (mService.mLocalPowerManager != null) { + mService.mLocalPowerManager.finishUidChanges(); + } + // Also check if there are any apps in cached and background restricted mode, + // if so, kill it if it's been there long enough, or kick off a msg to check + // it later. + if (mService.mConstants.mKillBgRestrictedAndCachedIdle) { + final ArraySet apps = mProcessList.mAppsInBackgroundRestricted; + for (int i = 0, size = apps.size(); i < size; i++) { + // Check to see if needs to be killed. + final long bgTime = mProcessList.killAppIfBgRestrictedAndCachedIdleLocked( + apps.valueAt(i), nowElapsed) - mConstants.BACKGROUND_SETTLE_TIME; + if (bgTime > 0 && (nextTime == 0 || nextTime > bgTime)) { + nextTime = bgTime; + } + } + } + if (nextTime > 0) { + mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, + nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed); + } + } + + @GuardedBy({"mService", "mProcLock"}) + void setAppIdTempAllowlistStateLSP(int uid, boolean onAllowlist) { + boolean changed = false; + for (int i = mActiveUids.size() - 1; i >= 0; i--) { + final UidRecord uidRec = mActiveUids.valueAt(i); + if (uidRec.getUid() == uid && uidRec.isCurAllowListed() != onAllowlist) { + uidRec.setCurAllowListed(onAllowlist); + changed = true; + } + } + if (changed) { + updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST); + } + } + + @GuardedBy({"mService", "mProcLock"}) + void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) { + boolean changed = false; + final UidRecord uidRec = mActiveUids.get(uid); + if (uidRec != null && uidRec.isCurAllowListed() != onAllowlist) { + uidRec.setCurAllowListed(onAllowlist); + updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST); + } + } + + @GuardedBy("mService") + void dumpProcessListVariablesLocked(ProtoOutputStream proto) { + proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq); + proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.getLruSeqLOSP()); + proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, + mNumNonCachedProcs); + proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs); + proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, + mNewNumServiceProcs); + + } + + @GuardedBy("mService") + void dumpSequenceNumbersLocked(PrintWriter pw) { + pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.getLruSeqLOSP()); + } + + @GuardedBy("mService") + void dumpProcCountsLocked(PrintWriter pw) { + pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs + + " (" + mProcessList.getLruSizeLOSP() + " total)" + + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs + + " mNumServiceProcs=" + mNumServiceProcs + + " mNewNumServiceProcs=" + mNewNumServiceProcs); + } + + @GuardedBy("mProcLock") + void dumpCachedAppOptimizerSettings(PrintWriter pw) { + mCachedAppOptimizer.dump(pw); + } + + @GuardedBy("mService") + void dumpCacheOomRankerSettings(PrintWriter pw) { + mCacheOomRanker.dump(pw); + } + + @GuardedBy({"mService", "mProcLock"}) + void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason, + boolean immediate) { + if (!mCachedAppOptimizer.useFreezer()) { + return; + } + + if (app.mOptRecord.isFreezeExempt()) { + return; + } + + final ProcessCachedOptimizerRecord opt = app.mOptRecord; + // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze + if (opt.isFrozen() && opt.shouldNotFreeze()) { + mCachedAppOptimizer.unfreezeAppLSP(app, + CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); + return; + } + + final ProcessStateRecord state = app.mState; + // Use current adjustment when freezing, set adjustment when unfreezing. + if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen() + && !opt.shouldNotFreeze()) { + if (!immediate) { + mCachedAppOptimizer.freezeAppAsyncLSP(app); + } else { + mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app); + } + } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) { + mCachedAppOptimizer.unfreezeAppLSP(app, + CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); + } + } + + @GuardedBy("mService") + void unfreezeTemporarily(ProcessRecord app, @OomAdjReason int reason) { + if (!mCachedAppOptimizer.useFreezer()) { + return; + } + + final ProcessCachedOptimizerRecord opt = app.mOptRecord; + if (!opt.isFrozen() && !opt.isPendingFreeze()) { + return; + } + + final ArrayList processes = mTmpProcessList; + final ActiveUids uids = mTmpUidRecords; + mTmpProcessSet.add(app); + collectReachableProcessesLocked(mTmpProcessSet, processes, uids); + mTmpProcessSet.clear(); + // Now processes contains app's downstream and app + final int size = processes.size(); + for (int i = 0; i < size; i++) { + ProcessRecord proc = processes.get(i); + mCachedAppOptimizer.unfreezeTemporarily(proc, reason); + } + processes.clear(); + } + + @GuardedBy("mService") + void onProcessEndLocked(@NonNull ProcessRecord app) { + // Empty, the OomAdjusterModernImpl will have an implementation. + } + + /** + * Called when the process state is changed outside of the OomAdjuster. + */ + @GuardedBy("mService") + void onProcessStateChanged(@NonNull ProcessRecord app, int prevProcState) { + // Empty, the OomAdjusterModernImpl will have an implementation. + } + + /** + * Called when the oom adj is changed outside of the OomAdjuster. + */ + @GuardedBy("mService") + void onProcessOomAdjChanged(@NonNull ProcessRecord app, int prevAdj) { + // Empty, the OomAdjusterModernImpl will have an implementation. + } + + @VisibleForTesting + void resetInternal() { + // Empty, the OomAdjusterModernImpl will have an implementation. + } + + @GuardedBy("mService") + protected int getInitialAdj(@NonNull ProcessRecord app) { + return app.mState.getCurAdj(); + } + + @GuardedBy("mService") + protected int getInitialProcState(@NonNull ProcessRecord app) { + return app.mState.getCurProcState(); + } + + @GuardedBy("mService") + protected int getInitialCapability(@NonNull ProcessRecord app) { + return app.mState.getCurCapability(); + } + + @GuardedBy("mService") + protected boolean getInitialIsCurBoundByNonBgRestrictedApp(@NonNull ProcessRecord app) { + // The caller will set the initial value in this implementation. + return app.mState.isCurBoundByNonBgRestrictedApp(); + } + + /** + * Evaluate the service connection, return {@code true} if the client will change the state + * of the service host process by the given connection. + */ + @GuardedBy("mService") + boolean evaluateServiceConnectionAdd(ProcessRecord client, ProcessRecord app, + ConnectionRecord cr) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + if (app.getSetAdj() <= client.getSetAdj() + && app.getSetProcState() <= client.getSetProcState() + && ((app.getSetCapability() & client.getSetCapability()) + == client.getSetCapability() + || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS))) { + // The service host process has better states than the client, so no change. + return false; + } + // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive + // since it's only evaluating one service connection. + return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(), + mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, + CACHED_APP_MIN_ADJ, false, true /* dryRun */); + } + + @GuardedBy("mService") + boolean evaluateServiceConnectionRemoval(ProcessRecord client, ProcessRecord app, + ConnectionRecord cr) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + + if (app.getSetAdj() < client.getSetAdj() + && app.getSetProcState() < client.getSetProcState()) { + // The service host process has better states than the client. + if (((app.getSetCapability() & client.getSetCapability()) == PROCESS_CAPABILITY_NONE) + || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) { + // The service host app doesn't get any capabilities from the client. + return false; + } + } + return true; + } + + @GuardedBy("mService") + boolean evaluateProviderConnectionAdd(ProcessRecord client, ProcessRecord app) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + if (app.getSetAdj() <= client.getSetAdj() + && app.getSetProcState() <= client.getSetProcState()) { + // The provider host process has better states than the client, so no change. + return false; + } + return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(), + mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, + false, true /* dryRun */); + } + + @GuardedBy("mService") + boolean evaluateProviderConnectionRemoval(ProcessRecord client, ProcessRecord app) { + if (evaluateConnectionPrelude(client, app)) { + return true; + } + if (app.getSetAdj() < client.getSetAdj() + && app.getSetProcState() < client.getSetProcState()) { + // The provider host process has better states than the client, so no change. + return false; + } + return true; + } + + private boolean evaluateConnectionPrelude(ProcessRecord client, ProcessRecord app) { + if (client == null || app == null) { + return true; + } + if (app.isSdkSandbox || app.isolated || app.isKilledByAm() || app.isKilled()) { + // Let's always re-evaluate them for now. + return true; + } + return false; + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/location/LocationManagerService.java b/aosp/frameworks/base/services/core/java/com/android/server/location/LocationManagerService.java new file mode 100755 index 0000000000000000000000000000000000000000..edba317b7119282c5714d9b8f6c350834358e950 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/location/LocationManagerService.java @@ -0,0 +1,1991 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.Manifest.permission.WRITE_SECURE_SETTINGS; +import static android.app.compat.CompatChanges.isChangeEnabled; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; +import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.location.LocationManager.BLOCK_PENDING_INTENT_SYSTEM_API_USAGE; +import static android.location.LocationManager.FUSED_PROVIDER; +import static android.location.LocationManager.GPS_HARDWARE_PROVIDER; +import static android.location.LocationManager.GPS_PROVIDER; +import static android.location.LocationManager.NETWORK_PROVIDER; +import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS; +import static android.location.provider.LocationProviderBase.ACTION_FUSED_PROVIDER; +import static android.location.provider.LocationProviderBase.ACTION_GNSS_PROVIDER; +import static android.location.provider.LocationProviderBase.ACTION_NETWORK_PROVIDER; + +import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; +import static com.android.server.location.LocationPermissions.PERMISSION_FINE; +import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import android.Manifest; +import android.Manifest.permission; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.AppOpsManager; +import android.app.PendingIntent; +import android.app.compat.CompatChanges; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Criteria; +import android.location.Geofence; +import android.location.GnssAntennaInfo; +import android.location.GnssCapabilities; +import android.location.GnssMeasurementCorrections; +import android.location.GnssMeasurementRequest; +import android.location.IGnssAntennaInfoListener; +import android.location.IGnssMeasurementsListener; +import android.location.IGnssNavigationMessageListener; +import android.location.IGnssNmeaListener; +import android.location.IGnssStatusListener; +import android.location.ILocationCallback; +import android.location.ILocationListener; +import android.location.ILocationManager; +import android.location.LastLocationRequest; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.LocationPackageTagsListener; +import android.location.LocationProvider; +import android.location.LocationRequest; +import android.location.LocationTime; +import android.location.provider.ForwardGeocodeRequest; +import android.location.provider.IGeocodeCallback; +import android.location.provider.IProviderRequestListener; +import android.location.provider.ProviderProperties; +import android.location.provider.ReverseGeocodeRequest; +import android.location.util.identity.CallerIdentity; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.ICancellationSignal; +import android.os.PackageTagsList; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.WorkSource; +import android.os.WorkSource.WorkChain; +import android.provider.Settings; +import android.stats.location.LocationStatsEnums; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.IndentingPrintWriter; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.location.eventlog.LocationEventLog; +import com.android.server.location.geofence.GeofenceManager; +import com.android.server.location.geofence.GeofenceProxy; +import com.android.server.location.gnss.GnssConfiguration; +import com.android.server.location.gnss.GnssManagerService; +import com.android.server.location.gnss.hal.GnssNative; +import com.android.server.location.injector.AlarmHelper; +import com.android.server.location.injector.AppForegroundHelper; +import com.android.server.location.injector.AppOpsHelper; +import com.android.server.location.injector.DeviceIdleHelper; +import com.android.server.location.injector.DeviceStationaryHelper; +import com.android.server.location.injector.EmergencyHelper; +import com.android.server.location.injector.Injector; + +import com.android.server.location.injector.LocationPermissionsHelper; +import com.android.server.location.injector.LocationPowerSaveModeHelper; +import com.android.server.location.injector.LocationUsageLogger; +import com.android.server.location.injector.PackageResetHelper; +import com.android.server.location.injector.ScreenInteractiveHelper; +import com.android.server.location.injector.SettingsHelper; +import com.android.server.location.injector.SystemAlarmHelper; +import com.android.server.location.injector.SystemAppForegroundHelper; +import com.android.server.location.injector.SystemAppOpsHelper; +import com.android.server.location.injector.SystemDeviceIdleHelper; +import com.android.server.location.injector.SystemDeviceStationaryHelper; +import com.android.server.location.injector.SystemEmergencyHelper; +import com.android.server.location.injector.SystemLocationPermissionsHelper; +import com.android.server.location.injector.SystemLocationPowerSaveModeHelper; +import com.android.server.location.injector.SystemPackageResetHelper; +import com.android.server.location.injector.SystemScreenInteractiveHelper; +import com.android.server.location.injector.SystemSettingsHelper; +import com.android.server.location.injector.SystemUserInfoHelper; +import com.android.server.location.injector.UserInfoHelper; +import com.android.server.location.provider.AbstractLocationProvider; +import com.android.server.location.provider.LocationProviderManager; +import com.android.server.location.provider.MockLocationProvider; +import com.android.server.location.provider.PassiveLocationProvider; +import com.android.server.location.provider.PassiveLocationProviderManager; +import com.android.server.location.provider.StationaryThrottlingLocationProvider; +import com.android.server.location.provider.proxy.ProxyGeocodeProvider; +import com.android.server.location.provider.proxy.ProxyLocationProvider; +import com.android.server.location.settings.LocationSettings; +import com.android.server.location.settings.LocationUserSettings; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; + +import android.os.Handler; +import android.os.Message; + + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Timer; +import java.util.TimerTask; +import android.os.SystemProperties; +import android.location.LocationResult; + + + +/** + * The service class that manages LocationProviders and issues location + * updates and alerts. + */ +public class LocationManagerService extends ILocationManager.Stub implements + LocationProviderManager.StateChangedListener { + + /** + * Controls lifecycle of LocationManagerService. + */ + public static class Lifecycle extends SystemService { + + private final LifecycleUserInfoHelper mUserInfoHelper; + private final SystemInjector mSystemInjector; + private final LocationManagerService mService; + + public Lifecycle(Context context) { + super(context); + mUserInfoHelper = new LifecycleUserInfoHelper(context); + mSystemInjector = new SystemInjector(context, mUserInfoHelper); + mService = new LocationManagerService(context, mSystemInjector); + } + + @Override + public void onStart() { + publishBinderService(Context.LOCATION_SERVICE, mService); + + // client caching behavior is only enabled after seeing the first invalidate + LocationManager.invalidateLocalLocationEnabledCaches(); + // disable caching for our own process + LocationManager.disableLocalLocationEnabledCaches(); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + // the location service must be functioning after this boot phase + mSystemInjector.onSystemReady(); + mService.onSystemReady(); + } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + // some providers rely on third party code, so we wait to initialize + // providers until third party code is allowed to run + mService.onSystemThirdPartyAppsCanStart(); + } + } + + @Override + public void onUserStarting(TargetUser user) { + mUserInfoHelper.onUserStarted(user.getUserIdentifier()); + + // log location enabled state and emergency state on start to minimize coverage loss + mService.logLocationEnabledState(); + mService.logEmergencyState(); + } + + @Override + public void onUserSwitching(TargetUser from, TargetUser to) { + mUserInfoHelper.onCurrentUserChanged(from.getUserIdentifier(), + to.getUserIdentifier()); + } + + @Override + public void onUserStopped(TargetUser user) { + mUserInfoHelper.onUserStopped(user.getUserIdentifier()); + } + + private static class LifecycleUserInfoHelper extends SystemUserInfoHelper { + + LifecycleUserInfoHelper(Context context) { + super(context); + } + + void onUserStarted(int userId) { + dispatchOnUserStarted(userId); + } + + void onUserStopped(int userId) { + dispatchOnUserStopped(userId); + } + + void onCurrentUserChanged(int fromUserId, int toUserId) { + dispatchOnCurrentUserChanged(fromUserId, toUserId); + } + } + } + + public static final String TAG = "LocationManagerService"; + public static final boolean D = Log.isLoggable(TAG, Log.DEBUG); + + private static final String ATTRIBUTION_TAG = "LocationService"; + + final Object mLock = new Object(); + + private final Context mContext; + private final Injector mInjector; + private final LocalService mLocalService; + + private final GeofenceManager mGeofenceManager; + private volatile @Nullable GnssManagerService mGnssManagerService = null; + private ProxyGeocodeProvider mGeocodeProvider; + + private final Object mDeprecatedGnssBatchingLock = new Object(); + @GuardedBy("mDeprecatedGnssBatchingLock") + private @Nullable ILocationListener mDeprecatedGnssBatchingListener; + + @GuardedBy("mLock") + private String mExtraLocationControllerPackage; + @GuardedBy("mLock") + private boolean mExtraLocationControllerPackageEnabled; + + // location provider managers + + private final PassiveLocationProviderManager mPassiveManager; + + // @GuardedBy("mProviderManagers") + // hold lock for writes, no lock necessary for simple reads + final CopyOnWriteArrayList mProviderManagers = + new CopyOnWriteArrayList<>(); + + @GuardedBy("mLock") + @Nullable LocationPackageTagsListener mLocationTagsChangedListener; + + + private final int START_REPORT_LOCATION = 100; + private final int STOP_REPORT_LOCATION = 200; + + private Handler mReportLocationHandler = new Handler() { + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + if (msg.what == START_REPORT_LOCATION) { + mReportLocationHandler.removeMessages(START_REPORT_LOCATION); + // 上报虚拟位置 + setvClustersLocation(); + mReportLocationHandler.sendEmptyMessageDelayed(START_REPORT_LOCATION, 2000); + } else if (msg.what == STOP_REPORT_LOCATION) { + mReportLocationHandler.removeMessages(START_REPORT_LOCATION); + } + } + }; + + LocationManagerService(Context context, Injector injector) { + mContext = context.createAttributionContext(ATTRIBUTION_TAG); + mInjector = injector; + mLocalService = new LocalService(); + LocalServices.addService(LocationManagerInternal.class, mLocalService); + + mGeofenceManager = new GeofenceManager(mContext, injector); + + mInjector.getLocationSettings().registerLocationUserSettingsListener( + this::onLocationUserSettingsChanged); + mInjector.getSettingsHelper().addOnLocationEnabledChangedListener( + this::onLocationModeChanged); + mInjector.getSettingsHelper().addAdasAllowlistChangedListener( + () -> refreshAppOpsRestrictions(UserHandle.USER_ALL) + ); + mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener( + () -> refreshAppOpsRestrictions(UserHandle.USER_ALL)); + mInjector.getUserInfoHelper().addListener((userId, change) -> { + if (change == UserInfoHelper.UserListener.USER_STARTED) { + refreshAppOpsRestrictions(userId); + } + }); + mInjector.getEmergencyHelper().addOnEmergencyStateChangedListener( + this::onEmergencyStateChanged); + + // set up passive provider first since it will be required for all other location providers, + // which are loaded later once the system is ready. + mPassiveManager = new PassiveLocationProviderManager(mContext, injector); + addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext)); + + // TODO: load the gps provider here as well, which will require refactoring + + // Let the package manager query which are the default location + // providers as they get certain permissions granted by default. + LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService( + LegacyPermissionManagerInternal.class); + permissionManagerInternal.setLocationPackagesProvider( + userId -> mContext.getResources().getStringArray( + com.android.internal.R.array.config_locationProviderPackageNames)); + permissionManagerInternal.setLocationExtraPackagesProvider( + userId -> mContext.getResources().getStringArray( + com.android.internal.R.array.config_locationExtraPackageNames)); + } + + @Nullable + LocationProviderManager getLocationProviderManager(String providerName) { + if (providerName == null) { + return null; + } + + for (LocationProviderManager manager : mProviderManagers) { + if (providerName.equals(manager.getName())) { + if (!manager.isVisibleToCaller()) { + return null; + } + return manager; + } + } + + return null; + } + + private LocationProviderManager getOrAddLocationProviderManager(String providerName) { + synchronized (mProviderManagers) { + for (LocationProviderManager manager : mProviderManagers) { + if (providerName.equals(manager.getName())) { + return manager; + } + } + + LocationProviderManager manager = new LocationProviderManager(mContext, mInjector, + providerName, mPassiveManager); + addLocationProviderManager(manager, null); + return manager; + } + } + + @VisibleForTesting + void addLocationProviderManager( + LocationProviderManager manager, @Nullable AbstractLocationProvider realProvider) { + synchronized (mProviderManagers) { + Preconditions.checkState(getLocationProviderManager(manager.getName()) == null); + + manager.startManager(this); + + if (realProvider != null) { + // custom logic wrapping all non-passive providers + if (manager != mPassiveManager) { + int defaultStationaryThrottlingSetting = + mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH) ? 0 : 1; + boolean enableStationaryThrottling = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, + defaultStationaryThrottlingSetting) != 0; + if (enableStationaryThrottling) { + realProvider = new StationaryThrottlingLocationProvider(manager.getName(), + mInjector, realProvider); + } + } + manager.setRealProvider(realProvider); + } + mProviderManagers.add(manager); + } + } + + private void removeLocationProviderManager(LocationProviderManager manager) { + synchronized (mProviderManagers) { + boolean removed = mProviderManagers.remove(manager); + Preconditions.checkArgument(removed); + manager.setMockProvider(null); + manager.setRealProvider(null); + manager.stopManager(); + } + } + + void onSystemReady() { + if (Build.IS_DEBUGGABLE) { + // on debug builds, watch for location noteOps while location is off. there are some + // scenarios (emergency location) where this is expected, but generally this should + // rarely occur, and may indicate bugs. dump occurrences to logs for further evaluation + AppOpsManager appOps = Objects.requireNonNull( + mContext.getSystemService(AppOpsManager.class)); + appOps.startWatchingNoted( + new int[]{AppOpsManager.OP_FINE_LOCATION, AppOpsManager.OP_COARSE_LOCATION}, + (code, uid, packageName, attributionTag, flags, result) -> { + if (!isLocationEnabledForUser(UserHandle.getUserId(uid))) { + Log.w(TAG, "location noteOp with location off - " + + CallerIdentity.forTest(uid, 0, packageName, attributionTag)); + } + }); + } + } + + void onSystemThirdPartyAppsCanStart() { + // network provider should always be initialized before the gps provider since the gps + // provider has unfortunate hard dependencies on the network provider + ProxyLocationProvider networkProvider = ProxyLocationProvider.create( + mContext, + NETWORK_PROVIDER, + ACTION_NETWORK_PROVIDER, + com.android.internal.R.bool.config_enableNetworkLocationOverlay, + com.android.internal.R.string.config_networkLocationProviderPackageName); + if (networkProvider != null) { + LocationProviderManager networkManager = new LocationProviderManager(mContext, + mInjector, NETWORK_PROVIDER, mPassiveManager); + addLocationProviderManager(networkManager, networkProvider); + } else { + Log.w(TAG, "no network location provider found"); + } + + // ensure that a fused provider exists which will work in direct boot + Preconditions.checkState(!mContext.getPackageManager().queryIntentServicesAsUser( + new Intent(ACTION_FUSED_PROVIDER), + MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(), + "Unable to find a direct boot aware fused location provider"); + + ProxyLocationProvider fusedProvider = ProxyLocationProvider.create( + mContext, + FUSED_PROVIDER, + ACTION_FUSED_PROVIDER, + com.android.internal.R.bool.config_enableFusedLocationOverlay, + com.android.internal.R.string.config_fusedLocationProviderPackageName); + if (fusedProvider != null) { + LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector, + FUSED_PROVIDER, mPassiveManager); + addLocationProviderManager(fusedManager, fusedProvider); + } else { + Log.wtf(TAG, "no fused location provider found"); + } + + // initialize gnss last because it has no awareness of boot phases and blindly assumes that + // all other location providers are loaded at initialization + boolean hasLocationFeature = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LOCATION); + if (hasLocationFeature && GnssNative.isSupported()) { + GnssConfiguration gnssConfiguration = new GnssConfiguration(mContext); + GnssNative gnssNative = GnssNative.create(mInjector, gnssConfiguration); + mGnssManagerService = new GnssManagerService(mContext, mInjector, gnssNative); + mGnssManagerService.onSystemReady(); + + boolean useGnssHardwareProvider = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_useGnssHardwareProvider); + AbstractLocationProvider gnssProvider = null; + if (!useGnssHardwareProvider) { + // TODO: Create a separate config_enableGnssLocationOverlay config resource + // if we want to selectively enable a GNSS overlay but disable a fused overlay. + gnssProvider = ProxyLocationProvider.create( + mContext, + GPS_PROVIDER, + ACTION_GNSS_PROVIDER, + com.android.internal.R.bool.config_enableFusedLocationOverlay, + com.android.internal.R.string.config_gnssLocationProviderPackageName); + } + if (gnssProvider == null) { + gnssProvider = mGnssManagerService.getGnssLocationProvider(); + } else { + // If we have a GNSS provider override, add the hardware provider as a standalone + // option for use by apps with the correct permission. Note the GNSS HAL can only + // support a single client, so mGnssManagerService.getGnssLocationProvider() can + // only be installed with a single provider. Locations from this provider won't + // be reported through the passive provider. + LocationProviderManager gnssHardwareManager = + new LocationProviderManager( + mContext, + mInjector, + GPS_HARDWARE_PROVIDER, + /*passiveManager=*/ null, + Collections.singletonList(Manifest.permission.LOCATION_HARDWARE)); + addLocationProviderManager( + gnssHardwareManager, mGnssManagerService.getGnssLocationProvider()); + } + + LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector, + GPS_PROVIDER, mPassiveManager); + addLocationProviderManager(gnssManager, gnssProvider); + } + + // bind to geocoder provider + mGeocodeProvider = ProxyGeocodeProvider.createAndRegister(mContext); + if (mGeocodeProvider == null) { + Log.e(TAG, "no geocoder provider found"); + } + + // bind to hardware activity recognition + HardwareActivityRecognitionProxy hardwareActivityRecognitionProxy = + HardwareActivityRecognitionProxy.createAndRegister(mContext); + if (hardwareActivityRecognitionProxy == null) { + Log.e(TAG, "unable to bind ActivityRecognitionProxy"); + } + + // bind to gnss geofence proxy + if (mGnssManagerService != null) { + GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, + mGnssManagerService.getGnssGeofenceProxy()); + if (provider == null) { + Log.e(TAG, "unable to bind to GeofenceProxy"); + } + } + + // create any predefined test providers + String[] testProviderStrings = mContext.getResources().getStringArray( + com.android.internal.R.array.config_testLocationProviders); + for (String testProviderString : testProviderStrings) { + String[] fragments = testProviderString.split(","); + String name = fragments[0].trim(); + ProviderProperties properties = new ProviderProperties.Builder() + .setHasNetworkRequirement(Boolean.parseBoolean(fragments[1])) + .setHasSatelliteRequirement(Boolean.parseBoolean(fragments[2])) + .setHasCellRequirement(Boolean.parseBoolean(fragments[3])) + .setHasMonetaryCost(Boolean.parseBoolean(fragments[4])) + .setHasAltitudeSupport(Boolean.parseBoolean(fragments[5])) + .setHasSpeedSupport(Boolean.parseBoolean(fragments[6])) + .setHasBearingSupport(Boolean.parseBoolean(fragments[7])) + .setPowerUsage(Integer.parseInt(fragments[8])) + .setAccuracy(Integer.parseInt(fragments[9])) + .build(); + final LocationProviderManager manager = getOrAddLocationProviderManager(name); + manager.setMockProvider(new MockLocationProvider(properties, + CallerIdentity.fromContext(mContext), Collections.emptySet())); + } + } + + private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings, + LocationUserSettings newSettings) { + if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) { + boolean enabled = newSettings.isAdasGnssLocationEnabled(); + + if (D) { + Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled); + } + + EVENT_LOG.logAdasLocationEnabled(userId, enabled); + + Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED) + .putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + } + } + + private void onLocationModeChanged(int userId) { + boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId); + LocationManager.invalidateLocalLocationEnabledCaches(); + + if (D) { + Log.d(TAG, "[u" + userId + "] location enabled = " + enabled); + } + + EVENT_LOG.logLocationEnabled(userId, enabled); + logLocationEnabledState(); + + Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION) + .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + + refreshAppOpsRestrictions(userId); + } + + private void onEmergencyStateChanged() { + this.logEmergencyState(); + } + + private void logEmergencyState() { + boolean isInEmergency = mInjector.getEmergencyHelper().isInEmergency(Long.MIN_VALUE); + mInjector.getLocationUsageLogger().logEmergencyStateChanged(isInEmergency); + } + + private void logLocationEnabledState() { + boolean locationEnabled = false; + // Location setting is considered on if it is enabled for any one user + int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds(); + for (int userId : runningUserIds) { + locationEnabled = mInjector.getSettingsHelper().isLocationEnabled(userId); + if (locationEnabled) { + break; + } + } + mInjector.getLocationUsageLogger() + .logLocationEnabledStateChanged(locationEnabled); + } + + @Override + public int getGnssYearOfHardware() { + return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssYearOfHardware(); + } + + @Override + @Nullable + public String getGnssHardwareModelName() { + return mGnssManagerService == null ? "" : mGnssManagerService.getGnssHardwareModelName(); + } + + @Override + public int getGnssBatchSize() { + return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssBatchSize(); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) + @Override + public void startGnssBatch(long periodNanos, ILocationListener listener, String packageName, + @Nullable String attributionTag, String listenerId) { + startGnssBatch_enforcePermission(); + + if (mGnssManagerService == null) { + return; + } + + long intervalMs = NANOSECONDS.toMillis(periodNanos); + + synchronized (mDeprecatedGnssBatchingLock) { + stopGnssBatch(); + + registerLocationListener( + GPS_PROVIDER, + new LocationRequest.Builder(intervalMs) + .setMaxUpdateDelayMillis( + intervalMs * mGnssManagerService.getGnssBatchSize()) + .setHiddenFromAppOps(true) + .build(), + listener, + packageName, + attributionTag, + listenerId); + mDeprecatedGnssBatchingListener = listener; + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) + @Override + public void flushGnssBatch() { + flushGnssBatch_enforcePermission(); + + if (mGnssManagerService == null) { + return; + } + + synchronized (mDeprecatedGnssBatchingLock) { + if (mDeprecatedGnssBatchingListener != null) { + requestListenerFlush(GPS_PROVIDER, mDeprecatedGnssBatchingListener, 0); + } + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) + @Override + public void stopGnssBatch() { + stopGnssBatch_enforcePermission(); + + if (mGnssManagerService == null) { + return; + } + + synchronized (mDeprecatedGnssBatchingLock) { + if (mDeprecatedGnssBatchingListener != null) { + ILocationListener listener = mDeprecatedGnssBatchingListener; + mDeprecatedGnssBatchingListener = null; + unregisterLocationListener(listener); + } + } + } + + @Override + public boolean hasProvider(String provider) { + return getLocationProviderManager(provider) != null; + } + + @Override + public List getAllProviders() { + ArrayList providers = new ArrayList<>(mProviderManagers.size()); + for (LocationProviderManager manager : mProviderManagers) { + if (manager.isVisibleToCaller()) { + providers.add(manager.getName()); + } + } + return providers; + } + + @Override + public List getProviders(Criteria criteria, boolean enabledOnly) { + if (!LocationPermissions.checkCallingOrSelfLocationPermission(mContext, + PERMISSION_COARSE)) { + return Collections.emptyList(); + } + + synchronized (mLock) { + ArrayList providers = new ArrayList<>(mProviderManagers.size()); + for (LocationProviderManager manager : mProviderManagers) { + if (manager.isVisibleToCaller()) { + String name = manager.getName(); + if (enabledOnly && !manager.isEnabled(UserHandle.getCallingUserId())) { + continue; + } + if (criteria != null + && !LocationProvider.propertiesMeetCriteria( + name, manager.getProperties(), criteria)) { + continue; + } + providers.add(name); + } + } + return providers; + } + } + + @Override + public String getBestProvider(Criteria criteria, boolean enabledOnly) { + List providers; + synchronized (mLock) { + providers = getProviders(criteria, enabledOnly); + if (providers.isEmpty()) { + providers = getProviders(null, enabledOnly); + } + } + + if (!providers.isEmpty()) { + if (providers.contains(FUSED_PROVIDER)) { + return FUSED_PROVIDER; + } else if (providers.contains(GPS_PROVIDER)) { + return GPS_PROVIDER; + } else if (providers.contains(NETWORK_PROVIDER)) { + return NETWORK_PROVIDER; + } else { + return providers.get(0); + } + } + + return null; + } + + @Override + public String[] getBackgroundThrottlingWhitelist() { + return mInjector.getSettingsHelper().getBackgroundThrottlePackageWhitelist().toArray( + new String[0]); + } + + @Override + public PackageTagsList getIgnoreSettingsAllowlist() { + return mInjector.getSettingsHelper().getIgnoreSettingsAllowlist(); + } + + @Override + public PackageTagsList getAdasAllowlist() { + return mInjector.getSettingsHelper().getAdasAllowlist(); + } + + @Nullable + @Override + public ICancellationSignal getCurrentLocation(String provider, LocationRequest request, + ILocationCallback consumer, String packageName, @Nullable String attributionTag, + String listenerId) { + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, + PERMISSION_COARSE); + + // clients in the system process must have an attribution tag set + Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null); + + request = validateLocationRequest(provider, request, identity); + + LocationProviderManager manager = getLocationProviderManager(provider); + Preconditions.checkArgument(manager != null, + "provider \"" + provider + "\" does not exist"); + + return manager.getCurrentLocation(request, identity, permissionLevel, consumer); + } + + @Override + public void registerLocationListener(String provider, LocationRequest request, + ILocationListener listener, String packageName, @Nullable String attributionTag, + String listenerId) { + ActivityManagerInternal managerInternal = + LocalServices.getService(ActivityManagerInternal.class); + if (managerInternal != null) { + managerInternal.logFgsApiBegin(ActivityManager.FOREGROUND_SERVICE_API_TYPE_LOCATION, + Binder.getCallingUid(), Binder.getCallingPid()); + } + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, + PERMISSION_COARSE); + + // clients in the system process should have an attribution tag set + if (identity.getPid() == Process.myPid() && attributionTag == null) { + Log.w(TAG, "system location request with no attribution tag", + new IllegalArgumentException()); + } + + request = validateLocationRequest(provider, request, identity); + + LocationProviderManager manager = getLocationProviderManager(provider); + Preconditions.checkArgument(manager != null, + "provider \"" + provider + "\" does not exist"); + + manager.registerLocationRequest(request, identity, permissionLevel, listener); + //a-zip-20230328 support vir location + mReportLocationHandler.sendEmptyMessage(START_REPORT_LOCATION); + } + + private void setvClustersLocation() { + //ccynice for gps fake + Location location=new Location(LocationManager.GPS_PROVIDER); + location.setLongitude(Double.parseDouble(SystemProperties.get("persist.location.lon","114.064524"))); + location.setLatitude(Double.parseDouble(SystemProperties.get("persist.location.lat","22.549054"))); + + location.setElapsedRealtimeNanos(System.nanoTime()); + location.makeComplete(); + LocationProviderManager locationProviderManager = getLocationProviderManager(LocationManager.GPS_PROVIDER); + if (locationProviderManager != null) { + locationProviderManager.onReportLocation(LocationResult.create(location)); + } else { + Log.d(TAG,"locationProviderManager is NULL"); + } + } + + @Override + public void registerLocationPendingIntent(String provider, LocationRequest request, + PendingIntent pendingIntent, String packageName, @Nullable String attributionTag) { + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + AppOpsManager.toReceiverId(pendingIntent)); + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, + PERMISSION_COARSE); + + // clients in the system process must have an attribution tag set + Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); + + // pending intents requests may not use system apis because we do not keep track if clients + // lose the relevant permissions, and thus should not get the benefit of those apis. its + // simplest to ensure these apis are simply never set for pending intent requests. the same + // does not apply for listener requests since those will have the process (including the + // listener) killed on permission removal + if (isChangeEnabled(BLOCK_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) { + boolean usesSystemApi = request.isLowPower() + || request.isHiddenFromAppOps() + || request.isLocationSettingsIgnored() + || !request.getWorkSource().isEmpty(); + if (usesSystemApi) { + throw new SecurityException( + "PendingIntent location requests may not use system APIs: " + request); + } + } + + request = validateLocationRequest(provider, request, identity); + + LocationProviderManager manager = getLocationProviderManager(provider); + Preconditions.checkArgument(manager != null, + "provider \"" + provider + "\" does not exist"); + + manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent); + } + + private LocationRequest validateLocationRequest(String provider, LocationRequest request, + CallerIdentity identity) { + // validate unsanitized request + if (!request.getWorkSource().isEmpty()) { + mContext.enforceCallingOrSelfPermission( + permission.UPDATE_DEVICE_STATS, + "setting a work source requires " + permission.UPDATE_DEVICE_STATS); + } + + // sanitize request + LocationRequest.Builder sanitized = new LocationRequest.Builder(request); + + if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) { + if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) + != PERMISSION_GRANTED) { + sanitized.setLowPower(false); + } + } + + WorkSource workSource = new WorkSource(request.getWorkSource()); + if (workSource.size() > 0 && workSource.getPackageName(0) == null) { + Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); + workSource.clear(); + } else { + List workChains = workSource.getWorkChains(); + if (workChains != null && !workChains.isEmpty() + && workChains.get(0).getAttributionTag() == null) { + Log.w(TAG, + "received (and ignoring) illegal worksource with no attribution tag"); + workSource.clear(); + } + } + + if (workSource.isEmpty()) { + identity.addToWorkSource(workSource); + } + sanitized.setWorkSource(workSource); + + request = sanitized.build(); + + // validate sanitized request + boolean isLocationProvider = mLocalService.isProvider(null, identity); + + if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, + identity.getUid())) { + mContext.enforceCallingOrSelfPermission( + permission.LOCATION_HARDWARE, + "low power request requires " + permission.LOCATION_HARDWARE); + } + if (request.isHiddenFromAppOps()) { + mContext.enforceCallingOrSelfPermission( + permission.UPDATE_APP_OPS_STATS, + "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS); + } + if (request.isAdasGnssBypass()) { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + throw new IllegalArgumentException( + "adas gnss bypass requests are only allowed on automotive devices"); + } + if (!GPS_PROVIDER.equals(provider)) { + throw new IllegalArgumentException( + "adas gnss bypass requests are only allowed on the \"gps\" provider"); + } + if (!isLocationProvider) { + LocationPermissions.enforceCallingOrSelfBypassPermission(mContext); + } + } + if (request.isLocationSettingsIgnored()) { + if (!isLocationProvider) { + LocationPermissions.enforceCallingOrSelfBypassPermission(mContext); + } + } + + return request; + } + + @Override + public void requestListenerFlush(String provider, ILocationListener listener, int requestCode) { + LocationProviderManager manager = getLocationProviderManager(provider); + Preconditions.checkArgument(manager != null, + "provider \"" + provider + "\" does not exist"); + + manager.flush(Objects.requireNonNull(listener), requestCode); + } + + @Override + public void requestPendingIntentFlush(String provider, PendingIntent pendingIntent, + int requestCode) { + LocationProviderManager manager = getLocationProviderManager(provider); + Preconditions.checkArgument(manager != null, + "provider \"" + provider + "\" does not exist"); + + manager.flush(Objects.requireNonNull(pendingIntent), requestCode); + } + + @Override + public void unregisterLocationListener(ILocationListener listener) { + ActivityManagerInternal managerInternal = + LocalServices.getService(ActivityManagerInternal.class); + if (managerInternal != null) { + managerInternal.logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_LOCATION, + Binder.getCallingUid(), Binder.getCallingPid()); + } + for (LocationProviderManager manager : mProviderManagers) { + manager.unregisterLocationRequest(listener); + } + mReportLocationHandler.sendEmptyMessage(STOP_REPORT_LOCATION); + Log.d(TAG,"unregisterLocationListener"); + } + + @Override + public void unregisterLocationPendingIntent(PendingIntent pendingIntent) { + for (LocationProviderManager manager : mProviderManagers) { + manager.unregisterLocationRequest(pendingIntent); + } + } + + @Override + public Location getLastLocation(String provider, LastLocationRequest request, + String packageName, @Nullable String attributionTag) { + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), + identity.getPid()); + LocationPermissions.enforceLocationPermission(identity.getUid(), permissionLevel, + PERMISSION_COARSE); + + // clients in the system process must have an attribution tag set + Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); + + request = validateLastLocationRequest(provider, request, identity); + + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return null; + } + + return manager.getLastLocation(request, identity, permissionLevel); + } + + private LastLocationRequest validateLastLocationRequest(String provider, + LastLocationRequest request, + CallerIdentity identity) { + // sanitize request + LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request); + + request = sanitized.build(); + + // validate request + boolean isLocationProvider = mLocalService.isProvider(null, identity); + + if (request.isHiddenFromAppOps()) { + mContext.enforceCallingOrSelfPermission( + permission.UPDATE_APP_OPS_STATS, + "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS); + } + + if (request.isAdasGnssBypass()) { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + throw new IllegalArgumentException( + "adas gnss bypass requests are only allowed on automotive devices"); + } + if (!GPS_PROVIDER.equals(provider)) { + throw new IllegalArgumentException( + "adas gnss bypass requests are only allowed on the \"gps\" provider"); + } + if (!isLocationProvider) { + LocationPermissions.enforceCallingOrSelfBypassPermission(mContext); + } + } + if (request.isLocationSettingsIgnored()) { + if (!isLocationProvider) { + LocationPermissions.enforceCallingOrSelfBypassPermission(mContext); + } + } + + return request; + } + + @Override + public LocationTime getGnssTimeMillis() { + return mLocalService.getGnssTimeMillis(); + } + + @android.annotation.EnforcePermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION}) + @Override + public void injectLocation(Location location) { + + super.injectLocation_enforcePermission(); + + Preconditions.checkArgument(location.isComplete()); + + int userId = UserHandle.getCallingUserId(); + LocationProviderManager manager = getLocationProviderManager(location.getProvider()); + if (manager != null && manager.isEnabled(userId)) { + manager.injectLastLocation(Objects.requireNonNull(location), userId); + } + } + + @Override + public void requestGeofence(Geofence geofence, PendingIntent intent, String packageName, + String attributionTag) { + mGeofenceManager.addGeofence(geofence, intent, packageName, attributionTag); + } + + @Override + public void removeGeofence(PendingIntent pendingIntent) { + mGeofenceManager.removeGeofence(pendingIntent); + } + + @Override + public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName, + @Nullable String attributionTag, String listenerId) { + if (mGnssManagerService != null) { + mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag, + listenerId); + } + } + + @Override + public void unregisterGnssStatusCallback(IGnssStatusListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.unregisterGnssStatusCallback(listener); + } + } + + @Override + public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName, + @Nullable String attributionTag, String listenerId) { + if (mGnssManagerService != null) { + mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag, + listenerId); + } + } + + @Override + public void unregisterGnssNmeaCallback(IGnssNmeaListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.unregisterGnssNmeaCallback(listener); + } + } + + @Override + public void addGnssMeasurementsListener(GnssMeasurementRequest request, + IGnssMeasurementsListener listener, String packageName, @Nullable String attributionTag, + String listenerId) { + if (mGnssManagerService != null) { + mGnssManagerService.addGnssMeasurementsListener(request, listener, packageName, + attributionTag, listenerId); + } + } + + @Override + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.removeGnssMeasurementsListener( + listener); + } + } + + @Override + public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName, + @Nullable String attributionTag, String listenerId) { + if (mGnssManagerService != null) { + mGnssManagerService.addGnssAntennaInfoListener(listener, packageName, attributionTag, + listenerId); + } + } + + @Override + public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.removeGnssAntennaInfoListener(listener); + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @Override + @RequiresPermission(INTERACT_ACROSS_USERS) + public void addProviderRequestListener(IProviderRequestListener listener) { + addProviderRequestListener_enforcePermission(); + for (LocationProviderManager manager : mProviderManagers) { + if (manager.isVisibleToCaller()) { + manager.addProviderRequestListener(listener); + } + } + } + + @Override + public void removeProviderRequestListener(IProviderRequestListener listener) { + for (LocationProviderManager manager : mProviderManagers) { + manager.removeProviderRequestListener(listener); + } + } + + @Override + public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) { + if (mGnssManagerService != null) { + mGnssManagerService.injectGnssMeasurementCorrections(corrections); + } + } + + @Override + public GnssCapabilities getGnssCapabilities() { + return mGnssManagerService == null ? new GnssCapabilities.Builder().build() + : mGnssManagerService.getGnssCapabilities(); + } + + @Override + public List getGnssAntennaInfos() { + return mGnssManagerService == null ? null : mGnssManagerService.getGnssAntennaInfos(); + } + + @Override + public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, + String packageName, @Nullable String attributionTag, String listenerId) { + if (mGnssManagerService != null) { + mGnssManagerService.addGnssNavigationMessageListener(listener, packageName, + attributionTag, listenerId); + } + } + + @Override + public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.removeGnssNavigationMessageListener( + listener); + } + } + + @Override + public void sendExtraCommand(String provider, String command, Bundle extras) { + LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE); + mContext.enforceCallingOrSelfPermission( + permission.ACCESS_LOCATION_EXTRA_COMMANDS, null); + + LocationProviderManager manager = getLocationProviderManager( + Objects.requireNonNull(provider)); + if (manager != null) { + manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), + Objects.requireNonNull(command), extras); + } + + mInjector.getLocationUsageLogger().logLocationApiUsage( + LocationStatsEnums.USAGE_STARTED, + LocationStatsEnums.API_SEND_EXTRA_COMMAND, + provider); + mInjector.getLocationUsageLogger().logLocationApiUsage( + LocationStatsEnums.USAGE_ENDED, + LocationStatsEnums.API_SEND_EXTRA_COMMAND, + provider); + } + + @Override + public ProviderProperties getProviderProperties(String provider) { + LocationProviderManager manager = getLocationProviderManager(provider); + Preconditions.checkArgument(manager != null, + "provider \"" + provider + "\" does not exist"); + return manager.getProperties(); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.READ_DEVICE_CONFIG) + @Override + public boolean isProviderPackage(@Nullable String provider, String packageName, + @Nullable String attributionTag) { + isProviderPackage_enforcePermission(); + + for (LocationProviderManager manager : mProviderManagers) { + if (provider != null && !provider.equals(manager.getName())) { + continue; + } + CallerIdentity identity = manager.getProviderIdentity(); + if (identity == null) { + continue; + } + if (identity.getPackageName().equals(packageName) && (attributionTag == null + || Objects.equals(identity.getAttributionTag(), attributionTag))) { + return true; + } + } + + return false; + } + + @android.annotation.EnforcePermission(android.Manifest.permission.READ_DEVICE_CONFIG) + @Override + public List getProviderPackages(String provider) { + getProviderPackages_enforcePermission(); + + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return Collections.emptyList(); + } + + CallerIdentity identity = manager.getProviderIdentity(); + if (identity == null) { + return Collections.emptyList(); + } + + return Collections.singletonList(identity.getPackageName()); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) + @Override + public void setExtraLocationControllerPackage(String packageName) { + super.setExtraLocationControllerPackage_enforcePermission(); + + synchronized (mLock) { + mExtraLocationControllerPackage = packageName; + } + } + + @Override + public String getExtraLocationControllerPackage() { + synchronized (mLock) { + return mExtraLocationControllerPackage; + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) + @Override + public void setExtraLocationControllerPackageEnabled(boolean enabled) { + super.setExtraLocationControllerPackageEnabled_enforcePermission(); + + synchronized (mLock) { + mExtraLocationControllerPackageEnabled = enabled; + } + } + + @Override + public boolean isExtraLocationControllerPackageEnabled() { + synchronized (mLock) { + return mExtraLocationControllerPackageEnabled + && (mExtraLocationControllerPackage != null); + } + } + + @Override + public void setLocationEnabledForUser(boolean enabled, int userId) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "setLocationEnabledForUser", null); + + mContext.enforceCallingOrSelfPermission(WRITE_SECURE_SETTINGS, null); + + LocationManager.invalidateLocalLocationEnabledCaches(); + mInjector.getSettingsHelper().setLocationEnabled(enabled, userId); + } + + @Override + public boolean isLocationEnabledForUser(int userId) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "isLocationEnabledForUser", null); + return mInjector.getSettingsHelper().isLocationEnabled(userId); + } + + @Override + public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "setAdasGnssLocationEnabledForUser", null); + + LocationPermissions.enforceCallingOrSelfBypassPermission(mContext); + + mInjector.getLocationSettings().updateUserSettings(userId, + settings -> settings.withAdasGnssLocationEnabled(enabled)); + } + + @Override + public boolean isAdasGnssLocationEnabledForUser(int userId) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "isAdasGnssLocationEnabledForUser", null); + return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled(); + } + + @Override + public boolean isProviderEnabledForUser(String provider, int userId) { + return mLocalService.isProviderEnabledForUser(provider, userId); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) + @Override + @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) + public void setAutomotiveGnssSuspended(boolean suspended) { + + super.setAutomotiveGnssSuspended_enforcePermission(); + + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + throw new IllegalStateException( + "setAutomotiveGnssSuspended only allowed on automotive devices"); + } + + if (mGnssManagerService != null) { + mGnssManagerService.setAutomotiveGnssSuspended(suspended); + } + } + + @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) + @Override + @RequiresPermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) + public boolean isAutomotiveGnssSuspended() { + + super.isAutomotiveGnssSuspended_enforcePermission(); + + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + throw new IllegalStateException( + "isAutomotiveGnssSuspended only allowed on automotive devices"); + } + + if (mGnssManagerService != null) { + return mGnssManagerService.isAutomotiveGnssSuspended(); + } + return false; + } + + @Override + public boolean isGeocodeAvailable() { + return mGeocodeProvider != null; + } + + @Override + public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) { + CallerIdentity identity = + CallerIdentity.fromBinder( + mContext, request.getCallingPackage(), request.getCallingAttributionTag()); + Preconditions.checkArgument(identity.getUid() == request.getCallingUid()); + + if (mGeocodeProvider != null) { + mGeocodeProvider.reverseGeocode(request, callback); + } else { + try { + callback.onError(null); + } catch (RemoteException e) { + // ignore + } + } + } + + @Override + public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) { + CallerIdentity identity = + CallerIdentity.fromBinder( + mContext, request.getCallingPackage(), request.getCallingAttributionTag()); + Preconditions.checkArgument(identity.getUid() == request.getCallingUid()); + + if (mGeocodeProvider != null) { + mGeocodeProvider.forwardGeocode(request, callback); + } else { + try { + callback.onError(null); + } catch (RemoteException e) { + // ignore + } + } + } + + @Override + public void addTestProvider(String provider, ProviderProperties properties, + List extraAttributionTags, String packageName, String attributionTag) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { + return; + } + + final LocationProviderManager manager = getOrAddLocationProviderManager(provider); + manager.setMockProvider(new MockLocationProvider(properties, identity, + new ArraySet<>(extraAttributionTags))); + } + + @Override + public void removeTestProvider(String provider, String packageName, String attributionTag) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { + return; + } + + synchronized (mLock) { + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return; + } + + manager.setMockProvider(null); + if (!manager.hasProvider()) { + removeLocationProviderManager(manager); + } + } + } + + @Override + public void setTestProviderLocation(String provider, Location location, String packageName, + String attributionTag) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, + attributionTag); + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { + return; + } + + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); + + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + throw new IllegalArgumentException("provider doesn't exist: " + provider); + } + + manager.setMockProviderLocation(location); + } + + @Override + public void setTestProviderEnabled(String provider, boolean enabled, String packageName, + String attributionTag) { + // unsafe is ok because app ops will verify the package name + CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, + attributionTag); + if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { + return; + } + + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + throw new IllegalArgumentException("provider doesn't exist: " + provider); + } + + manager.setMockProviderAllowed(enabled); + } + + @Override + public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out, + ParcelFileDescriptor err, String[] args) { + return new LocationShellCommand(mContext, this).exec( + this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), + args); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) { + return; + } + + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + + if (args.length > 0) { + LocationProviderManager manager = getLocationProviderManager(args[0]); + if (manager != null) { + ipw.println("Provider:"); + ipw.increaseIndent(); + manager.dump(fd, ipw, args); + ipw.decreaseIndent(); + + ipw.println("Event Log:"); + ipw.increaseIndent(); + EVENT_LOG.iterate(ipw::println, manager.getName()); + ipw.decreaseIndent(); + return; + } + + if ("--gnssmetrics".equals(args[0])) { + if (mGnssManagerService != null) { + mGnssManagerService.dump(fd, ipw, args); + } + return; + } + } + + ipw.println("Location Manager State:"); + ipw.increaseIndent(); + + ipw.println("User Info:"); + ipw.increaseIndent(); + mInjector.getUserInfoHelper().dump(fd, ipw, args); + ipw.decreaseIndent(); + + ipw.println("Location Settings:"); + ipw.increaseIndent(); + mInjector.getSettingsHelper().dump(fd, ipw, args); + mInjector.getLocationSettings().dump(fd, ipw, args); + ipw.decreaseIndent(); + + synchronized (mLock) { + if (mExtraLocationControllerPackage != null) { + ipw.println( + "Location Controller Extra Package: " + mExtraLocationControllerPackage + + (mExtraLocationControllerPackageEnabled ? " [enabled]" + : " [disabled]")); + } + } + + ipw.println("Location Providers:"); + ipw.increaseIndent(); + for (LocationProviderManager manager : mProviderManagers) { + manager.dump(fd, ipw, args); + } + ipw.decreaseIndent(); + + ipw.println("Historical Aggregate Location Provider Data:"); + ipw.increaseIndent(); + ArrayMap> aggregateStats = + EVENT_LOG.copyAggregateStats(); + for (int i = 0; i < aggregateStats.size(); i++) { + ipw.print(aggregateStats.keyAt(i)); + ipw.println(":"); + ipw.increaseIndent(); + ArrayMap providerStats = + aggregateStats.valueAt(i); + for (int j = 0; j < providerStats.size(); j++) { + ipw.print(providerStats.keyAt(j)); + ipw.print(": "); + providerStats.valueAt(j).updateTotals(); + ipw.println(providerStats.valueAt(j)); + } + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + + ipw.println("Historical Aggregate Gnss Measurement Provider Data:"); + ipw.increaseIndent(); + ArrayMap + gnssAggregateStats = EVENT_LOG.copyGnssMeasurementAggregateStats(); + for (int i = 0; i < gnssAggregateStats.size(); i++) { + ipw.print(gnssAggregateStats.keyAt(i)); + ipw.print(": "); + gnssAggregateStats.valueAt(i).updateTotals(); + ipw.println(gnssAggregateStats.valueAt(i)); + } + ipw.decreaseIndent(); + + if (mGnssManagerService != null) { + ipw.println("GNSS Manager:"); + ipw.increaseIndent(); + mGnssManagerService.dump(fd, ipw, args); + ipw.decreaseIndent(); + } + + ipw.println("Geofence Manager:"); + ipw.increaseIndent(); + mGeofenceManager.dump(fd, ipw, args); + ipw.decreaseIndent(); + + ipw.println("Event Log:"); + ipw.increaseIndent(); + EVENT_LOG.iterate(ipw::println); + ipw.decreaseIndent(); + } + + @Override + public void onStateChanged(String provider, AbstractLocationProvider.State oldState, + AbstractLocationProvider.State newState) { + if (!Objects.equals(oldState.identity, newState.identity)) { + refreshAppOpsRestrictions(UserHandle.USER_ALL); + } + + if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags) + || !Objects.equals(oldState.identity, newState.identity)) { + // since we're potentially affecting the tag lists for two different uids, acquire the + // lock to ensure providers cannot change while we're looping over the providers + // multiple times, which could lead to inconsistent results. + synchronized (mLock) { + LocationPackageTagsListener listener = mLocationTagsChangedListener; + if (listener != null) { + int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1; + int newUid = newState.identity != null ? newState.identity.getUid() : -1; + if (oldUid != -1) { + PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid); + FgThread.getHandler().post( + () -> listener.onLocationPackageTagsChanged(oldUid, tags)); + } + // if the new app id is the same as the old app id, no need to invoke the + // listener twice, it's already been taken care of + if (newUid != -1 && newUid != oldUid) { + PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid); + FgThread.getHandler().post( + () -> listener.onLocationPackageTagsChanged(newUid, tags)); + } + } + } + } + } + + private void refreshAppOpsRestrictions(int userId) { + if (userId == UserHandle.USER_ALL) { + final int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds(); + for (int i = 0; i < runningUserIds.length; i++) { + refreshAppOpsRestrictions(runningUserIds[i]); + } + return; + } + + Preconditions.checkArgument(userId >= 0); + + boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId); + + PackageTagsList allowedPackages = null; + if (!enabled) { + PackageTagsList.Builder builder = new PackageTagsList.Builder(); + for (LocationProviderManager manager : mProviderManagers) { + CallerIdentity identity = manager.getProviderIdentity(); + if (identity != null) { + builder.add(identity.getPackageName(), identity.getAttributionTag()); + } + } + builder.add(mInjector.getSettingsHelper().getIgnoreSettingsAllowlist()); + builder.add(mInjector.getSettingsHelper().getAdasAllowlist()); + allowedPackages = builder.build(); + } + + AppOpsManager appOpsManager = Objects.requireNonNull( + mContext.getSystemService(AppOpsManager.class)); + appOpsManager.setUserRestrictionForUser( + AppOpsManager.OP_COARSE_LOCATION, + !enabled, + LocationManagerService.this, + allowedPackages, + userId); + appOpsManager.setUserRestrictionForUser( + AppOpsManager.OP_FINE_LOCATION, + !enabled, + LocationManagerService.this, + allowedPackages, + userId); + } + + PackageTagsList calculateAppOpsLocationSourceTags(int uid) { + PackageTagsList.Builder builder = new PackageTagsList.Builder(); + for (LocationProviderManager manager : mProviderManagers) { + AbstractLocationProvider.State managerState = manager.getState(); + if (managerState.identity == null) { + continue; + } + if (managerState.identity.getUid() != uid) { + continue; + } + + builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags); + if (managerState.extraAttributionTags.isEmpty() + || managerState.identity.getAttributionTag() != null) { + builder.add(managerState.identity.getPackageName(), + managerState.identity.getAttributionTag()); + } else { + Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and " + + "a non-empty set of extra attribution tags - dropping the null " + + "attribution tag"); + } + } + return builder.build(); + } + + private class LocalService extends LocationManagerInternal { + + LocalService() {} + + @Override + public boolean isProviderEnabledForUser(@NonNull String provider, int userId) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, false, "isProviderEnabledForUser", null); + + LocationProviderManager manager = getLocationProviderManager(provider); + if (manager == null) { + return false; + } + + return manager.isEnabled(userId); + } + + @Override + public void addProviderEnabledListener(String provider, ProviderEnabledListener listener) { + LocationProviderManager manager = Objects.requireNonNull( + getLocationProviderManager(provider)); + manager.addEnabledListener(listener); + } + + @Override + public void removeProviderEnabledListener(String provider, + ProviderEnabledListener listener) { + LocationProviderManager manager = Objects.requireNonNull( + getLocationProviderManager(provider)); + manager.removeEnabledListener(listener); + } + + @Override + public boolean isProvider(@Nullable String provider, CallerIdentity identity) { + for (LocationProviderManager manager : mProviderManagers) { + if (provider != null && !provider.equals(manager.getName())) { + continue; + } + if (identity.equals(manager.getProviderIdentity()) && manager.isVisibleToCaller()) { + return true; + } + } + + return false; + } + + @Override + public @Nullable LocationTime getGnssTimeMillis() { + LocationProviderManager gpsManager = getLocationProviderManager(GPS_PROVIDER); + if (gpsManager == null) { + return null; + } + + Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL, + PERMISSION_FINE, false, Long.MAX_VALUE); + if (location == null) { + return null; + } + + return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos()); + } + + @Override + public void setLocationPackageTagsListener( + @Nullable LocationPackageTagsListener listener) { + synchronized (mLock) { + mLocationTagsChangedListener = listener; + + // calculate initial tag list and send to listener + if (listener != null) { + ArraySet uids = new ArraySet<>(mProviderManagers.size()); + for (LocationProviderManager manager : mProviderManagers) { + CallerIdentity identity = manager.getProviderIdentity(); + if (identity != null) { + uids.add(identity.getUid()); + } + } + + for (int uid : uids) { + PackageTagsList tags = calculateAppOpsLocationSourceTags(uid); + if (!tags.isEmpty()) { + FgThread.getHandler().post( + () -> listener.onLocationPackageTagsChanged(uid, tags)); + } + } + } + } + } + } + + private static final class SystemInjector implements Injector { + + private final Context mContext; + + private final SystemUserInfoHelper mUserInfoHelper; + private final LocationSettings mLocationSettings; + private final AlarmHelper mAlarmHelper; + private final SystemAppOpsHelper mAppOpsHelper; + private final SystemLocationPermissionsHelper mLocationPermissionsHelper; + private final SystemSettingsHelper mSettingsHelper; + private final SystemAppForegroundHelper mAppForegroundHelper; + private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper; + private final SystemScreenInteractiveHelper mScreenInteractiveHelper; + private final SystemDeviceStationaryHelper mDeviceStationaryHelper; + private final SystemDeviceIdleHelper mDeviceIdleHelper; + private final LocationUsageLogger mLocationUsageLogger; + private final PackageResetHelper mPackageResetHelper; + + // lazily instantiated since they may not always be used + + @GuardedBy("this") + @Nullable + private SystemEmergencyHelper mEmergencyCallHelper; + + @GuardedBy("this") + private boolean mSystemReady; + + SystemInjector(Context context, SystemUserInfoHelper userInfoHelper) { + mContext = context; + + mUserInfoHelper = userInfoHelper; + mLocationSettings = new LocationSettings(context); + mAlarmHelper = new SystemAlarmHelper(context); + mAppOpsHelper = new SystemAppOpsHelper(context); + mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context, + mAppOpsHelper); + mSettingsHelper = new SystemSettingsHelper(context); + mAppForegroundHelper = new SystemAppForegroundHelper(context); + mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context); + mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context); + mDeviceStationaryHelper = new SystemDeviceStationaryHelper(); + mDeviceIdleHelper = new SystemDeviceIdleHelper(context); + mLocationUsageLogger = new LocationUsageLogger(); + mPackageResetHelper = new SystemPackageResetHelper(context); + } + + synchronized void onSystemReady() { + mUserInfoHelper.onSystemReady(); + mAppOpsHelper.onSystemReady(); + mLocationPermissionsHelper.onSystemReady(); + mSettingsHelper.onSystemReady(); + mAppForegroundHelper.onSystemReady(); + mLocationPowerSaveModeHelper.onSystemReady(); + mScreenInteractiveHelper.onSystemReady(); + mDeviceStationaryHelper.onSystemReady(); + mDeviceIdleHelper.onSystemReady(); + + if (mEmergencyCallHelper != null) { + mEmergencyCallHelper.onSystemReady(); + } + + mSystemReady = true; + } + + @Override + public UserInfoHelper getUserInfoHelper() { + return mUserInfoHelper; + } + + @Override + public LocationSettings getLocationSettings() { + return mLocationSettings; + } + + @Override + public AlarmHelper getAlarmHelper() { + return mAlarmHelper; + } + + @Override + public AppOpsHelper getAppOpsHelper() { + return mAppOpsHelper; + } + + @Override + public LocationPermissionsHelper getLocationPermissionsHelper() { + return mLocationPermissionsHelper; + } + + @Override + public SettingsHelper getSettingsHelper() { + return mSettingsHelper; + } + + @Override + public AppForegroundHelper getAppForegroundHelper() { + return mAppForegroundHelper; + } + + @Override + public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() { + return mLocationPowerSaveModeHelper; + } + + @Override + public ScreenInteractiveHelper getScreenInteractiveHelper() { + return mScreenInteractiveHelper; + } + + @Override + public DeviceStationaryHelper getDeviceStationaryHelper() { + return mDeviceStationaryHelper; + } + + @Override + public DeviceIdleHelper getDeviceIdleHelper() { + return mDeviceIdleHelper; + } + + @Override + public synchronized EmergencyHelper getEmergencyHelper() { + if (mEmergencyCallHelper == null) { + mEmergencyCallHelper = new SystemEmergencyHelper(mContext); + if (mSystemReady) { + mEmergencyCallHelper.onSystemReady(); + } + } + + return mEmergencyCallHelper; + } + + @Override + public LocationUsageLogger getLocationUsageLogger() { + return mLocationUsageLogger; + } + + @Override + public PackageResetHelper getPackageResetHelper() { + return mPackageResetHelper; + } + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/wm/FocusChangeListener.java b/aosp/frameworks/base/services/core/java/com/android/server/wm/FocusChangeListener.java new file mode 100755 index 0000000000000000000000000000000000000000..e4419b73a02f740a7717ea8c7f5e9f2605d83a72 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/wm/FocusChangeListener.java @@ -0,0 +1,5 @@ +package com.android.server.wm; + +public interface FocusChangeListener { + public void onFocusChangeListener(boolean isShow);//表示软键盘是否弹起,弹起表示有输入框获取到了焦点 +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java b/aosp/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java new file mode 100755 index 0000000000000000000000000000000000000000..aad1ee491b09053f3a0ef214d7969fe361348470 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java @@ -0,0 +1,10068 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.Manifest.permission.ACCESS_SURFACE_FLINGER; +import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.INPUT_CONSUMER; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; +import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; +import static android.Manifest.permission.MANAGE_APP_TOKENS; +import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE; +import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.Manifest.permission.STATUS_BAR_SERVICE; +import static android.Manifest.permission.WRITE_SECURE_SETTINGS; +import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; +import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; +import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; +import static android.app.StatusBarManager.DISABLE_MASK; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; +import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; +import static android.content.pm.PackageManager.FEATURE_PC; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; +import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE; +import static android.os.Process.SYSTEM_UID; +import static android.os.Process.myPid; +import static android.os.Process.myUid; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; +import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; +import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; +import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; +import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY; +import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; +import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; +import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.fixScale; +import static android.view.WindowManagerGlobal.ADD_OKAY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; +import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; +import static android.window.WindowProviderService.isWindowProviderService; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; +import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR; +import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; +import static com.android.server.LockGuard.INDEX_WINDOW; +import static com.android.server.LockGuard.installLock; +import static com.android.server.policy.PhoneWindowManager.TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD; +import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; +import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL; +import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; +import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS; +import static com.android.server.wm.SensitiveContentPackages.PackageInfo; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerServiceDumpProto.BACK_NAVIGATION; +import static com.android.server.wm.WindowManagerServiceDumpProto.DISPLAY_FROZEN; +import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_APP; +import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_DISPLAY_ID; +import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW; +import static com.android.server.wm.WindowManagerServiceDumpProto.HARD_KEYBOARD_AVAILABLE; +import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW; +import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY; +import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER; +import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID; +import static com.android.window.flags.Flags.multiCrop; + +import android.Manifest; +import android.Manifest.permission; +import android.animation.ValueAnimator; +import android.annotation.EnforcePermission; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.ActivityThread; +import android.app.AppOpsManager; +import android.app.IActivityManager; +import android.app.IApplicationThread; +import android.app.IAssistDataReceiver; +import android.app.WindowConfiguration; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.TestUtilityService; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Region; +import android.hardware.configstore.V1_0.OptionalBool; +import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.input.InputManager; +import android.hardware.input.InputSettings; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Debug; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.os.InputConfig; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.PowerManager.ServiceType; +import android.os.PowerManagerInternal; +import android.os.PowerSaveState; +import android.os.RemoteCallback; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ServiceManager; +import android.os.ShellCallback; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.SystemService; +import android.os.Trace; +import android.os.UserHandle; +import android.provider.DeviceConfigInterface; +import android.provider.Settings; +import android.service.vr.IVrManager; +import android.service.vr.IVrStateCallbacks; +import android.sysprop.SurfaceFlingerProperties; +import android.text.format.DateUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.MergedConfiguration; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; +import android.util.TimeUtils; +import android.util.TypedValue; +import android.util.proto.ProtoOutputStream; +import android.view.Choreographer; +import android.view.ContentRecordingSession; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.IAppTransitionAnimationSpecsFuture; +import android.view.ICrossWindowBlurEnabledListener; +import android.view.IDecorViewGestureListener; +import android.view.IDisplayChangeWindowController; +import android.view.IDisplayFoldListener; +import android.view.IDisplayWindowInsetsController; +import android.view.IDisplayWindowListener; +import android.view.IInputFilter; +import android.view.IOnKeyguardExitResult; +import android.view.IPinnedTaskListener; +import android.view.IRecentsAnimationRunner; +import android.view.IRotationWatcher; +import android.view.IScrollCaptureResponseListener; +import android.view.ISystemGestureExclusionListener; +import android.view.IWallpaperVisibilityListener; +import android.view.IWindow; +import android.view.IWindowId; +import android.view.IWindowManager; +import android.view.IWindowSession; +import android.view.IWindowSessionCallback; +import android.view.InputApplicationHandle; +import android.view.InputChannel; +import android.view.InputDevice; +import android.view.InputWindowHandle; +import android.view.InsetsFrameProvider; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.KeyEvent; +import android.view.MagnificationSpec; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.RemoteAnimationAdapter; +import android.view.ScrollCaptureResponse; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; +import android.view.TaskTransitionSpec; +import android.view.View; +import android.view.View.FocusDirection; +import android.view.ViewDebug; +import android.view.WindowContentFrameStats; +import android.view.WindowInsets; +import android.view.WindowInsets.Type.InsetsType; +import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.RemoveContentMode; +import android.view.WindowManagerGlobal; +import android.view.WindowManagerPolicyConstants.PointerEventListener; +import android.view.displayhash.DisplayHash; +import android.view.displayhash.VerifiedDisplayHash; +import android.view.inputmethod.ImeTracker; +import android.window.AddToSurfaceSyncGroupResult; +import android.window.ClientWindowFrames; +import android.window.IGlobalDragListener; +import android.window.IScreenRecordingCallback; +import android.window.ISurfaceSyncGroupCompletedListener; +import android.window.ITaskFpsCallback; +import android.window.ITrustedPresentationListener; +import android.window.InputTransferToken; +import android.window.ScreenCapture; +import android.window.SystemPerformanceHinter; +import android.window.TaskSnapshot; +import android.window.TrustedPresentationThresholds; +import android.window.WindowContainerToken; +import android.window.WindowContextInfo; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.IResultReceiver; +import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; +import com.android.internal.policy.IShortcutService; +import com.android.internal.policy.KeyInterceptionInfo; +import com.android.internal.protolog.LegacyProtoLogImpl; +import com.android.internal.protolog.ProtoLogGroup; +import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.LatencyTracker; +import com.android.internal.view.WindowManagerPolicyThread; +import com.android.server.AnimationThread; +import com.android.server.DisplayThread; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.UiThread; +import com.android.server.Watchdog; +import com.android.server.input.InputManagerService; +import com.android.server.inputmethod.InputMethodManagerInternal; +import com.android.server.pm.UserManagerInternal; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; +import com.android.server.power.ShutdownThread; +import com.android.server.utils.PriorityDump; +import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils; +import com.android.window.flags.Flags; + +import dalvik.annotation.optimization.NeverCompile; + +import java.io.BufferedWriter; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.Socket; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Supplier; + +/** {@hide} */ +public class WindowManagerService extends IWindowManager.Stub + implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { + private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM; + private static final int TRACE_MAX_SECTION_NAME_LENGTH = 127; + + static final int LAYOUT_REPEAT_THRESHOLD = 4; + + static final boolean PROFILE_ORIENTATION = false; + + /** The maximum length we will accept for a loaded animation duration: + * this is 10 seconds. + */ + static final int MAX_ANIMATION_DURATION = 10 * 1000; + + /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */ + static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000; + + /** Amount of time to allow a last ANR message to exist before freeing the memory. */ + static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours + + // Maximum number of milliseconds to wait for input devices to be enumerated before + // proceding with safe mode detection. + private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; + + private static final int SYNC_INPUT_TRANSACTIONS_TIMEOUT_MS = 5000; + + // Poll interval in milliseconds for watching boot animation finished. + // TODO(b/159045990) Migrate to SystemService.waitForState with dedicated thread. + private static final int BOOT_ANIMATION_POLL_INTERVAL = 50; + + // The name of the boot animation service in init.rc. + private static final String BOOT_ANIMATION_SERVICE = "bootanim"; + + static final int UPDATE_FOCUS_NORMAL = 0; + /** Caller will assign layers */ + static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; + /** Caller is performing surface placement */ + static final int UPDATE_FOCUS_PLACING_SURFACES = 2; + /** Caller will performSurfacePlacement */ + static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; + /** Indicates we are removing the focused window when updating the focus. */ + static final int UPDATE_FOCUS_REMOVING_FOCUS = 4; + + private static final String SYSTEM_SECURE = "ro.secure"; + private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; + + private static final String DENSITY_OVERRIDE = "ro.config.density_override"; + private static final String SIZE_OVERRIDE = "ro.config.size_override"; + + private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.boot.emulator.circular"; + + static final int MY_PID = myPid(); + static final int MY_UID = myUid(); + + static final int LOGTAG_INPUT_FOCUS = 62001; + + /** + * Use WMShell for app transition. + */ + public static final String ENABLE_SHELL_TRANSITIONS = "persist.wm.debug.shell_transit"; + + /** + * @see #ENABLE_SHELL_TRANSITIONS + */ + public static final boolean sEnableShellTransitions = + SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, true); + + /** + * Allows a fullscreen windowing mode activity to launch in its desired orientation directly + * when the display has different orientation. + */ + static final boolean ENABLE_FIXED_ROTATION_TRANSFORM = + SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true); + + // Enums for animation scale update types. + @Retention(RetentionPolicy.SOURCE) + @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE}) + private @interface UpdateAnimationScaleMode {}; + private static final int WINDOW_ANIMATION_SCALE = 0; + private static final int TRANSITION_ANIMATION_SCALE = 1; + private static final int ANIMATION_DURATION_SCALE = 2; + + private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; + + final WindowManagerConstants mConstants; + + final WindowTracing mWindowTracing; + final TransitionTracer mTransitionTracer; + + private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider; + + final private KeyguardDisableHandler mKeyguardDisableHandler; + + private final RemoteCallbackList mKeyguardLockedStateListeners = + new RemoteCallbackList<>(); + private boolean mDispatchedKeyguardLockedState = false; + + // VR Vr2d Display Id. + int mVr2dDisplayId = INVALID_DISPLAY; + boolean mVrModeEnabled = false; + + /** + * Tracks a map of input tokens to info that is used to decide whether to intercept + * a key event. + */ + final Map mKeyInterceptionInfoForToken = + Collections.synchronizedMap(new ArrayMap<>()); + + final StartingSurfaceController mStartingSurfaceController; + + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { + @Override + public void onVrStateChanged(boolean enabled) { + synchronized (mGlobalLock) { + mVrModeEnabled = enabled; + mRoot.forAllDisplayPolicies(p -> p.onVrStateChangedLw(enabled)); + } + } + }; + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED: + mKeyguardDisableHandler.updateKeyguardEnabled(getSendingUserId()); + break; + } + } + }; + final WindowSurfacePlacer mWindowPlacerLocked; + + private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() { + @Override + public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, + boolean asProto) { + doDump(fd, pw, new String[] {"-a"}, asProto); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { + doDump(fd, pw, args, asProto); + } + }; + + /** + * Current user when multi-user is enabled. Don't show windows of non-current user. + */ + @UserIdInt int mCurrentUserId; + + final Context mContext; + + final boolean mHasPermanentDpad; + final long mDrawLockTimeoutMillis; + final boolean mAllowAnimationsInLowPowerMode; + + final boolean mAllowBootMessages; + + // Indicates whether the Assistant should show on top of the Dream (respectively, above + // everything else on screen). Otherwise, it will be put under always-on-top stacks. + final boolean mAssistantOnTopOfDream; + + /** + * If true, don't relaunch the activity upon receiving a configuration change to transition to + * or from the {@link UI_MODE_TYPE_DESK} uiMode, which is sent when docking. The configuration + * change will still be sent regardless, only the relaunch is skipped. Apps with desk resources + * are exempt from this and will behave like normal, since they may expect the relaunch upon the + * desk uiMode change. + */ + @VisibleForTesting + boolean mSkipActivityRelaunchWhenDocking; + + /** Device default insets types provided non-decor insets. */ + final int mDecorTypes; + + /** Device default insets types shall be excluded from config app sizes. */ + final int mConfigTypes; + + final boolean mLimitedAlphaCompositing; + final int mMaxUiWidth; + + @VisibleForTesting + WindowManagerPolicy mPolicy; + + final WindowManagerFlags mFlags; + + final IActivityManager mActivityManager; + final ActivityManagerInternal mAmInternal; + final UserManagerInternal mUmInternal; + + final AppOpsManager mAppOps; + final PackageManagerInternal mPmInternal; + private final TestUtilityService mTestUtilityService; + + @NonNull + final DisplayWindowSettingsProvider mDisplayWindowSettingsProvider; + @NonNull + final DisplayWindowSettings mDisplayWindowSettings; + + /** If the system should display notifications for apps displaying an alert window. */ + boolean mShowAlertWindowNotifications = true; + + /** + * All currently active sessions with clients. + */ + final ArraySet mSessions = new ArraySet<>(); + + /** Mapping from an IWindow IBinder to the server's Window object. */ + final HashMap mWindowMap = new HashMap<>(); + + /** Mapping from an InputWindowHandle token to the server's Window object. */ + final HashMap mInputToWindowMap = new HashMap<>(); + + /** Global service lock used by the package that owns this service. */ + final WindowManagerGlobalLock mGlobalLock; + + /** + * Windows that are being resized. Used so we can tell the client about + * the resize after closing the transaction in which we resized the + * underlying surface. + */ + final ArrayList mResizingWindows = new ArrayList<>(); + + /** + * Windows that their frames are being changed. Used so we can clear the frame-changing states + * after handling the moved or resized windows. + */ + final ArrayList mFrameChangingWindows = new ArrayList<>(); + + /** + * Mapping of displayId to {@link DisplayImePolicy}. + * Note that this can be accessed without holding the lock. + */ + volatile Map mDisplayImePolicyCache = Collections.unmodifiableMap( + new ArrayMap<>()); + + /** + * Windows whose surface should be destroyed. + */ + final ArrayList mDestroySurface = new ArrayList<>(); + + /** + * This is set when we have run out of memory, and will either be an empty + * list or contain windows that need to be force removed. + */ + final ArrayList mForceRemoves = new ArrayList<>(); + + /** + * The callbacks to make when the windows all have been drawn for a given + * {@link WindowContainer}. + */ + final ArrayMap, Message> mWaitingForDrawnCallbacks = new ArrayMap<>(); + + /** List of window currently causing non-system overlay windows to be hidden. */ + private ArrayList mHidingNonSystemOverlayWindows = new ArrayList<>(); + + /** + * In some cases (e.g. when {@link R.bool.config_reverseDefaultRotation} has value + * {@value true}) we need to map some orientation to others. This {@link SparseIntArray} + * contains the relation between the source orientation and the one to use. + */ + private final SparseIntArray mOrientationMapping = new SparseIntArray(); + + final AccessibilityController mAccessibilityController; + private RecentsAnimationController mRecentsAnimationController; + + Watermark mWatermark; + StrictModeFlash mStrictModeFlash; + EmulatorDisplayOverlay mEmulatorDisplayOverlay; + + final Rect mTmpRect = new Rect(); + + boolean mDisplayReady; + boolean mSafeMode; + boolean mDisplayEnabled = false; + boolean mSystemBooted = false; + boolean mForceDisplayEnabled = false; + boolean mShowingBootMessages = false; + boolean mSystemReady = false; + boolean mBootAnimationStopped = false; + long mBootWaitForWindowsStartTime = -1; + + /** Dump of the windows and app tokens at the time of the last ANR. Cleared after + * LAST_ANR_LIFETIME_DURATION_MSECS */ + String mLastANRState; + + // The root of the device window hierarchy. + @NonNull + final RootWindowContainer mRoot; + + final BLASTSyncEngine mSyncEngine; + + boolean mIsPc; + /** + * Flag that indicates that desktop mode is forced for public secondary screens. + * + * This includes several settings: + * - Set freeform windowing mode on external screen if it's supported and enabled. + * - Enable system decorations and IME on external screen. + * - TODO: Show mouse pointer on external screen. + */ + boolean mForceDesktopModeOnExternalDisplays; + + boolean mDisableTransitionAnimation; + + final RotationWatcherController mRotationWatcherController; + final WallpaperVisibilityListeners mWallpaperVisibilityListeners = + new WallpaperVisibilityListeners(); + + IDisplayChangeWindowController mDisplayChangeController = null; + private final DeathRecipient mDisplayChangeControllerDeath = + () -> mDisplayChangeController = null; + + final DisplayWindowListenerController mDisplayNotificationController; + final TaskSystemBarsListenerController mTaskSystemBarsListenerController; + + boolean mDisplayFrozen = false; + long mDisplayFreezeTime = 0; + int mLastDisplayFreezeDuration = 0; + Object mLastFinishedFreezeSource = null; + boolean mSwitchingUser = false; + + final static int WINDOWS_FREEZING_SCREENS_NONE = 0; + final static int WINDOWS_FREEZING_SCREENS_ACTIVE = 1; + final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2; + int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; + + /** Indicates that the system server is actively demanding the screen be frozen. */ + boolean mClientFreezingScreen = false; + int mAppsFreezingScreen = 0; + + @VisibleForTesting + boolean mPerDisplayFocusEnabled; + + // State while inside of layoutAndPlaceSurfacesLocked(). + boolean mFocusMayChange; + + // Number of windows whose insets state have been changed. + int mWindowsInsetsChanged = 0; + + // This is held as long as we have the screen frozen, to give us time to + // perform a rotation animation when turning off shows the lock screen which + // changes the orientation. + private final PowerManager.WakeLock mScreenFrozenLock; + + final TaskSnapshotController mTaskSnapshotController; + final SnapshotController mSnapshotController; + + final BlurController mBlurController; + final TaskFpsCallbackController mTaskFpsCallbackController; + + boolean mIsTouchDevice; + boolean mIsFakeTouchDevice; + + final H mH = new H(); + + /** + * Handler for things to run that have direct impact on an animation, i.e. animation tick, + * layout, starting window creation, whereas {@link H} runs things that are still important, but + * not as critical. + */ + final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper()); + + /** + * Used during task transitions to allow SysUI and launcher to customize task transitions. + */ + TaskTransitionSpec mTaskTransitionSpec; + + boolean mHardKeyboardAvailable; + WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; + + @Nullable ImeTargetChangeListener mImeTargetChangeListener; + + SettingsObserver mSettingsObserver; + final EmbeddedWindowController mEmbeddedWindowController; + final AnrController mAnrController; + + private final DisplayHashController mDisplayHashController; + + //ccy liutianzuo + private FocusChangeListener mFocusChangeListener = null; //给推流服务器回调焦点触发输入法的响应 + + public void setForcusChangeListener(FocusChangeListener alistener){ + mFocusChangeListener = alistener; + } + //end ccy + + volatile float mMaximumObscuringOpacityForTouch = + InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH; + + @VisibleForTesting + final WindowContextListenerController mWindowContextListenerController = + new WindowContextListenerController(); + + private InputTarget mFocusedInputTarget; + + @VisibleForTesting + final ContentRecordingController mContentRecordingController = new ContentRecordingController(); + + private final SurfaceSyncGroupController mSurfaceSyncGroupController = + new SurfaceSyncGroupController(); + + final TrustedPresentationListenerController mTrustedPresentationListenerController = + new TrustedPresentationListenerController(); + + @VisibleForTesting + final class SettingsObserver extends ContentObserver { + private final Uri mDisplayInversionEnabledUri = + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + private final Uri mWindowAnimationScaleUri = + Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE); + private final Uri mTransitionAnimationScaleUri = + Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); + private final Uri mAnimationDurationScaleUri = + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE); + private final Uri mImmersiveModeConfirmationsUri = + Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS); + private final Uri mPolicyControlUri = + Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL); + private final Uri mForceDesktopModeOnExternalDisplaysUri = Settings.Global.getUriFor( + Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS); + private final Uri mFreeformWindowUri = Settings.Global.getUriFor( + Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT); + private final Uri mForceResizableUri = Settings.Global.getUriFor( + DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES); + private final Uri mDevEnableNonResizableMultiWindowUri = Settings.Global.getUriFor( + DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW); + private final Uri mDisplaySettingsPathUri = Settings.Global.getUriFor( + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); + private final Uri mMaximumObscuringOpacityForTouchUri = Settings.Global.getUriFor( + Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH); + + public SettingsObserver() { + super(new Handler()); + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mWindowAnimationScaleUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mTransitionAnimationScaleUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mAnimationDurationScaleUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mPolicyControlUri, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(mForceDesktopModeOnExternalDisplaysUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(mDevEnableNonResizableMultiWindowUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mDisplaySettingsPathUri, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mMaximumObscuringOpacityForTouchUri, false, this, + UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mImmersiveModeConfirmationsUri.equals(uri) || mPolicyControlUri.equals(uri)) { + updateSystemUiSettings(true /* handleChange */); + return; + } + + if (mForceDesktopModeOnExternalDisplaysUri.equals(uri)) { + updateForceDesktopModeOnExternalDisplays(); + return; + } + + if (mFreeformWindowUri.equals(uri)) { + updateFreeformWindowManagement(); + return; + } + + if (mForceResizableUri.equals(uri)) { + updateForceResizableTasks(); + return; + } + + if (mDevEnableNonResizableMultiWindowUri.equals(uri)) { + updateDevEnableNonResizableMultiWindow(); + return; + } + + if (mDisplaySettingsPathUri.equals(uri)) { + updateDisplaySettingsLocation(); + return; + } + + if (mMaximumObscuringOpacityForTouchUri.equals(uri)) { + updateMaximumObscuringOpacityForTouch(); + return; + } + + @UpdateAnimationScaleMode + final int mode; + if (mWindowAnimationScaleUri.equals(uri)) { + mode = WINDOW_ANIMATION_SCALE; + } else if (mTransitionAnimationScaleUri.equals(uri)) { + mode = TRANSITION_ANIMATION_SCALE; + } else if (mAnimationDurationScaleUri.equals(uri)) { + mode = ANIMATION_DURATION_SCALE; + } else { + // Ignoring unrecognized content changes + return; + } + Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0); + mH.sendMessage(m); + } + + void loadSettings() { + updateSystemUiSettings(false /* handleChange */); + updateMaximumObscuringOpacityForTouch(); + } + + void updateMaximumObscuringOpacityForTouch() { + ContentResolver resolver = mContext.getContentResolver(); + mMaximumObscuringOpacityForTouch = Settings.Global.getFloat(resolver, + Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, + InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH); + if (mMaximumObscuringOpacityForTouch < 0.0f + || mMaximumObscuringOpacityForTouch > 1.0f) { + mMaximumObscuringOpacityForTouch = + InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH; + } + } + + void updateSystemUiSettings(boolean handleChange) { + synchronized (mGlobalLock) { + boolean changed = false; + if (handleChange) { + changed = getDefaultDisplayContentLocked().getDisplayPolicy() + .onSystemUiSettingsChanged(); + } else { + ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext); + } + if (changed) { + mWindowPlacerLocked.requestTraversal(); + } + } + } + + void updateForceDesktopModeOnExternalDisplays() { + ContentResolver resolver = mContext.getContentResolver(); + final boolean enableForceDesktopMode = Settings.Global.getInt(resolver, + DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; + if (mForceDesktopModeOnExternalDisplays == enableForceDesktopMode) { + return; + } + setForceDesktopModeOnExternalDisplays(enableForceDesktopMode); + } + + void updateFreeformWindowManagement() { + ContentResolver resolver = mContext.getContentResolver(); + final boolean freeformWindowManagement = mContext.getPackageManager().hasSystemFeature( + FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt( + resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; + + if (mAtmService.mSupportsFreeformWindowManagement != freeformWindowManagement) { + mAtmService.mSupportsFreeformWindowManagement = freeformWindowManagement; + synchronized (mGlobalLock) { + // Notify the root window container that the display settings value may change. + mRoot.onSettingsRetrieved(); + } + } + } + + void updateForceResizableTasks() { + ContentResolver resolver = mContext.getContentResolver(); + final boolean forceResizable = Settings.Global.getInt(resolver, + DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; + + mAtmService.mForceResizableActivities = forceResizable; + } + + void updateDevEnableNonResizableMultiWindow() { + ContentResolver resolver = mContext.getContentResolver(); + final boolean devEnableNonResizableMultiWindow = Settings.Global.getInt(resolver, + DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0; + + mAtmService.mDevEnableNonResizableMultiWindow = devEnableNonResizableMultiWindow; + } + + void updateDisplaySettingsLocation() { + final ContentResolver resolver = mContext.getContentResolver(); + final String filePath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); + synchronized (mGlobalLock) { + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(filePath); + mRoot.forAllDisplays(display -> { + mDisplayWindowSettings.applySettingsToDisplayLocked(display); + display.reconfigureDisplayLocked(); + }); + } + } + } + + PowerManager mPowerManager; + PowerManagerInternal mPowerManagerInternal; + + private float mWindowAnimationScaleSetting = 1.0f; + private float mTransitionAnimationScaleSetting = 1.0f; + private float mAnimatorDurationScaleSetting = 1.0f; + private boolean mAnimationsDisabled = false; + boolean mPointerLocationEnabled = false; + + final LetterboxConfiguration mLetterboxConfiguration; + + private boolean mIsIgnoreOrientationRequestDisabled; + + final InputManagerService mInputManager; + final DisplayManagerInternal mDisplayManagerInternal; + final DisplayManager mDisplayManager; + final ActivityTaskManagerService mAtmService; + + /** Indicates whether this device supports wide color gamut / HDR rendering */ + private boolean mHasWideColorGamutSupport; + private boolean mHasHdrSupport; + + /** Whether or not a layout can cause a wake up when theater mode is enabled. */ + boolean mAllowTheaterModeWakeFromLayout; + + final TaskPositioningController mTaskPositioningController; + final DragDropController mDragDropController; + + /** For frozen screen animations. */ + private int mExitAnimId, mEnterAnimId; + + /** The display that the rotation animation is applying to. */ + private int mFrozenDisplayId = INVALID_DISPLAY; + + /** Skip repeated ActivityRecords initialization. Note that AppWindowsToken's version of this + * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ + int mTransactionSequence; + + final WindowAnimator mAnimator; + SurfaceAnimationRunner mSurfaceAnimationRunner; + + /** + * Keeps track of which animations got transferred to which animators. Entries will get cleaned + * up when the animation finishes. + */ + final ArrayMap mAnimationTransferMap = new ArrayMap<>(); + + private WindowContentFrameStats mTempWindowRenderStats; + + final LatencyTracker mLatencyTracker; + + private ViewServer mViewServer; + final ArrayList mWindowChangeListeners = new ArrayList<>(); + boolean mWindowsChanged = false; + + public interface WindowChangeListener { + /** Notify on windows changed */ + void windowsChanged(); + + /** Notify on focus changed */ + void focusChanged(); + } + + final HighRefreshRateDenylist mHighRefreshRateDenylist; + + // Maintainer of a collection of all possible DisplayInfo for all configurations of the + // logical displays. + final PossibleDisplayInfoMapper mPossibleDisplayInfoMapper; + + static WindowManagerThreadPriorityBooster sThreadPriorityBooster = + new WindowManagerThreadPriorityBooster(); + + Function mSurfaceControlFactory; + Supplier mTransactionFactory; + + private final SurfaceControl.Transaction mTransaction; + + static void boostPriorityForLockedSection() { + sThreadPriorityBooster.boost(); + } + + static void resetPriorityAfterLockedSection() { + sThreadPriorityBooster.reset(); + } + + SystemPerformanceHinter mSystemPerformanceHinter; + + @GuardedBy("mGlobalLock") + final SensitiveContentPackages mSensitiveContentPackages = new SensitiveContentPackages(); + + /** Listener to notify activity manager about app transitions. */ + final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier + = new WindowManagerInternal.AppTransitionListener() { + + @Override + public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) { + } + + @Override + public void onAppTransitionFinishedLocked(IBinder token) { + final ActivityRecord atoken = ActivityRecord.forTokenLocked(token); + if (atoken == null) { + return; + } + + // While running a recents animation, this will get called early because we show the + // recents animation target activity immediately when the animation starts. Defer the + // mLaunchTaskBehind updates until recents animation finishes. + if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget(atoken)) { + mAtmService.mTaskSupervisor.scheduleLaunchTaskBehindComplete(atoken.token); + atoken.mLaunchTaskBehind = false; + } else { + atoken.updateReportedVisibilityLocked(); + // We should also defer sending the finished callback until the recents animation + // successfully finishes. + if (atoken.mEnteringAnimation && !isRecentsAnimationTarget(atoken)) { + atoken.mEnteringAnimation = false; + if (atoken.attachedToProcess()) { + try { + atoken.app.getThread().scheduleEnterAnimationComplete(atoken.token); + } catch (RemoteException e) { + } + } + } + } + } + }; + + final ArrayList mAppFreezeListeners = new ArrayList<>(); + + interface AppFreezeListener { + void onAppFreezeTimeout(); + } + + private final ScreenRecordingCallbackController mScreenRecordingCallbackController; + + public static WindowManagerService main(final Context context, final InputManagerService im, + final boolean showBootMsgs, WindowManagerPolicy policy, + ActivityTaskManagerService atm) { + final WindowManagerService wms = main(context, im, showBootMsgs, policy, atm, + new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, + SurfaceControl.Builder::new); + WindowManagerGlobal.setWindowManagerServiceForSystemProcess(wms); + return wms; + } + + /** + * Creates and returns an instance of the WindowManagerService. This call allows the caller + * to override factories that can be used to stub native calls during test. + */ + @VisibleForTesting + public static WindowManagerService main(final Context context, final InputManagerService im, + final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm, + DisplayWindowSettingsProvider displayWindowSettingsProvider, + Supplier transactionFactory, + Function surfaceControlFactory) { + final WindowManagerService[] wms = new WindowManagerService[1]; + DisplayThread.getHandler().runWithScissors(() -> + wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm, + displayWindowSettingsProvider, transactionFactory, + surfaceControlFactory), 0); + return wms[0]; + } + + private void initPolicy() { + UiThread.getHandler().runWithScissors(new Runnable() { + @Override + public void run() { + WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); + mPolicy.init(mContext, WindowManagerService.this); + } + }, 0); + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver result) { + new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result); + } + + private WindowManagerService(Context context, InputManagerService inputManager, + boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm, + DisplayWindowSettingsProvider displayWindowSettingsProvider, + Supplier transactionFactory, + Function surfaceControlFactory) { + installLock(this, INDEX_WINDOW); + mGlobalLock = atm.getGlobalLock(); + mAtmService = atm; + mContext = context; + mFlags = new WindowManagerFlags(); + mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); + mAllowBootMessages = showBootMsgs; + mLimitedAlphaCompositing = context.getResources().getBoolean( + com.android.internal.R.bool.config_sf_limitedAlpha); + mHasPermanentDpad = context.getResources().getBoolean( + com.android.internal.R.bool.config_hasPermanentDpad); + mDrawLockTimeoutMillis = context.getResources().getInteger( + com.android.internal.R.integer.config_drawLockTimeoutMillis); + mAllowAnimationsInLowPowerMode = context.getResources().getBoolean( + com.android.internal.R.bool.config_allowAnimationsInLowPowerMode); + mMaxUiWidth = context.getResources().getInteger( + com.android.internal.R.integer.config_maxUiWidth); + mDisableTransitionAnimation = context.getResources().getBoolean( + com.android.internal.R.bool.config_disableTransitionAnimation); + mPerDisplayFocusEnabled = context.getResources().getBoolean( + com.android.internal.R.bool.config_perDisplayFocusEnabled); + mAssistantOnTopOfDream = context.getResources().getBoolean( + com.android.internal.R.bool.config_assistantOnTopOfDream); + mSkipActivityRelaunchWhenDocking = context.getResources() + .getBoolean(R.bool.config_skipActivityRelaunchWhenDocking); + final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources() + .getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize) + && mFlags.mAllowsScreenSizeDecoupledFromStatusBarAndCutout; + if (!isScreenSizeDecoupledFromStatusBarAndCutout) { + mDecorTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars(); + mConfigTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.statusBars() + | WindowInsets.Type.navigationBars(); + } else { + mDecorTypes = WindowInsets.Type.navigationBars(); + mConfigTypes = WindowInsets.Type.navigationBars(); + } + + mLetterboxConfiguration = new LetterboxConfiguration( + // Using SysUI context to have access to Material colors extracted from Wallpaper. + ActivityThread.currentActivityThread().getSystemUiContext()); + + mInputManager = inputManager; // Must be before createDisplayContentLocked. + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + mPossibleDisplayInfoMapper = new PossibleDisplayInfoMapper(mDisplayManagerInternal); + + mSurfaceControlFactory = surfaceControlFactory; + mTransactionFactory = transactionFactory; + mTransaction = mTransactionFactory.get(); + + mPolicy = policy; + mAnimator = new WindowAnimator(this); + mRoot = new RootWindowContainer(this); + + final ContentResolver resolver = context.getContentResolver(); + + mSyncEngine = new BLASTSyncEngine(this); + + mWindowPlacerLocked = new WindowSurfacePlacer(this); + mSnapshotController = new SnapshotController(this); + mTaskSnapshotController = mSnapshotController.mTaskSnapshotController; + + mWindowTracing = WindowTracing.createDefaultAndStartLooper(this, + Choreographer.getInstance()); + + if (android.tracing.Flags.perfettoTransitionTracing()) { + mTransitionTracer = new PerfettoTransitionTracer(); + } else { + mTransitionTracer = new LegacyTransitionTracer(); + } + + LocalServices.addService(WindowManagerPolicy.class, mPolicy); + + mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); + + mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH); + + mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); + + if (mPowerManagerInternal != null) { + mPowerManagerInternal.registerLowPowerModeObserver( + new PowerManagerInternal.LowPowerModeListener() { + @Override + public int getServiceType() { + return ServiceType.ANIMATION; + } + + @Override + public void onLowPowerModeChanged(PowerSaveState result) { + synchronized (mGlobalLock) { + final boolean enabled = result.batterySaverEnabled; + if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) { + mAnimationsDisabled = enabled; + dispatchNewAnimatorScaleLocked(null); + } + } + } + }); + mAnimationsDisabled = mPowerManagerInternal + .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled; + } + mScreenFrozenLock = mPowerManager.newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); + mScreenFrozenLock.setReferenceCounted(false); + + mRotationWatcherController = new RotationWatcherController(this); + mDisplayNotificationController = new DisplayWindowListenerController(this); + mTaskSystemBarsListenerController = new TaskSystemBarsListenerController(); + + mActivityManager = ActivityManager.getService(); + mAmInternal = LocalServices.getService(ActivityManagerInternal.class); + mUmInternal = LocalServices.getService(UserManagerInternal.class); + mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); + AppOpsManager.OnOpChangedInternalListener opListener = + new AppOpsManager.OnOpChangedInternalListener() { + @Override public void onOpChanged(int op, String packageName) { + updateAppOpsState(); + } + }; + mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener); + mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener); + + mPmInternal = LocalServices.getService(PackageManagerInternal.class); + mTestUtilityService = LocalServices.getService(TestUtilityService.class); + final IntentFilter suspendPackagesFilter = new IntentFilter(); + suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); + suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); + context.registerReceiverAsUser(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String[] affectedPackages = + intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + final boolean suspended = + Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction()); + updateHiddenWhileSuspendedState(new ArraySet<>(Arrays.asList(affectedPackages)), + suspended); + } + }, UserHandle.ALL, suspendPackagesFilter, null, null); + + // Get persisted window scale setting + mWindowAnimationScaleSetting = getWindowAnimationScaleSetting(); + mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting(); + + setAnimatorDurationScale(getAnimatorDurationScaleSetting()); + + mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver, + DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; + + final String displaySettingsPath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); + mDisplayWindowSettingsProvider = displayWindowSettingsProvider; + if (displaySettingsPath != null) { + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath); + } + mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider); + + IntentFilter filter = new IntentFilter(); + // Track changes to DevicePolicyManager state so we can enable/disable keyguard. + filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); + + mLatencyTracker = LatencyTracker.getInstance(context); + + mSettingsObserver = new SettingsObserver(); + + mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory, + mPowerManagerInternal); + + mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); + + mTaskPositioningController = new TaskPositioningController(this); + mDragDropController = new DragDropController(this, mH.getLooper()); + + mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources()); + + mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL); + mConstants.start(new HandlerExecutor(mH)); + + LocalServices.addService(WindowManagerInternal.class, new LocalService()); + LocalServices.addService( + ImeTargetVisibilityPolicy.class, new ImeTargetVisibilityPolicyImpl()); + mEmbeddedWindowController = new EmbeddedWindowController(mAtmService, inputManager); + + mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources( + mContext.getResources()); + + mDisplayHashController = new DisplayHashController(mContext); + setGlobalShadowSettings(); + mAnrController = new AnrController(this); + mStartingSurfaceController = new StartingSurfaceController(this); + + mBlurController = new BlurController(mContext, mPowerManager); + mTaskFpsCallbackController = new TaskFpsCallbackController(mContext); + mAccessibilityController = new AccessibilityController(this); + mScreenRecordingCallbackController = new ScreenRecordingCallbackController(this); + mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> { + synchronized (mGlobalLock) { + DisplayContent dc = mRoot.getDisplayContent(displayId); + return (dc == null) ? null : dc.getSurfaceControl(); + } + }, mTransactionFactory); + mSystemPerformanceHinter.mTraceTag = TRACE_TAG_WINDOW_MANAGER; + } + + DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() { + return mDisplayAreaPolicyProvider; + } + + private void setGlobalShadowSettings() { + final TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); + float lightY = a.getDimension(R.styleable.Lighting_lightY, 0); + float lightZ = a.getDimension(R.styleable.Lighting_lightZ, 0); + float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0); + float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0); + float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0); + a.recycle(); + float[] ambientColor = {0.f, 0.f, 0.f, ambientShadowAlpha}; + float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha}; + SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ, + lightRadius); + } + + private float getTransitionAnimationScaleSetting() { + return fixScale(Settings.Global.getFloat(mContext.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat( + R.dimen.config_appTransitionAnimationDurationScaleDefault))); + } + + private float getAnimatorDurationScaleSetting() { + return fixScale(Settings.Global.getFloat(mContext.getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting)); + } + + private float getWindowAnimationScaleSetting() { + return fixScale(Settings.Global.getFloat(mContext.getContentResolver(), + Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting)); + } + + /** + * Called after all entities (such as the {@link ActivityManagerService}) have been set up and + * associated with the {@link WindowManagerService}. + */ + public void onInitReady() { + initPolicy(); + + // Add ourself to the Watchdog monitors. + Watchdog.getInstance().addMonitor(this); + createWatermark(); + showEmulatorDisplayOverlayIfNeeded(); + } + + public InputManagerCallback getInputManagerCallback() { + return mInputManagerCallback; + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + // The window manager only throws security exceptions, so let's + // log all others. + if (!(e instanceof SecurityException)) { + ProtoLog.wtf(WM_ERROR, "Window Manager Crash %s", e); + } + throw e; + } + } + + static boolean excludeWindowTypeFromTapOutTask(int windowType) { + switch (windowType) { + case TYPE_STATUS_BAR: + case TYPE_NOTIFICATION_SHADE: + case TYPE_NAVIGATION_BAR: + case TYPE_INPUT_METHOD_DIALOG: + case TYPE_VOLUME_OVERLAY: + return true; + } + return false; + } + + public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, + int displayId, int requestUserId, @InsetsType int requestedVisibleTypes, + InputChannel outInputChannel, InsetsState outInsetsState, + InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame, + float[] outSizeCompatScale) { + outActiveControls.set(null); + int[] appOp = new int[1]; + final boolean isRoundedCornerOverlay = (attrs.privateFlags + & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0; + int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, + appOp); + if (res != ADD_OKAY) { + return res; + } + + WindowState parentWindow = null; + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final long origId = Binder.clearCallingIdentity(); + final int type = attrs.type; + + synchronized (mGlobalLock) { + if (!mDisplayReady) { + throw new IllegalStateException("Display has not been initialialized"); + } + if (session.isClientDead()) { + ProtoLog.w(WM_ERROR, "Attempted to add window with a client %s " + + "that is dead. Aborting.", session); + return WindowManagerGlobal.ADD_APP_EXITING; + } + + final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token); + + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does " + + "not exist: %d. Aborting.", displayId); + return WindowManagerGlobal.ADD_INVALID_DISPLAY; + } + if (!displayContent.hasAccess(session.mUid)) { + ProtoLog.w(WM_ERROR, + "Attempted to add window to a display for which the application " + + "does not have access: %d. Aborting.", + displayContent.getDisplayId()); + return WindowManagerGlobal.ADD_INVALID_DISPLAY; + } + + if (mWindowMap.containsKey(client.asBinder())) { + ProtoLog.w(WM_ERROR, "Window %s is already added", client); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } + + if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { + parentWindow = windowForClientLocked(null, attrs.token, false); + if (parentWindow == null) { + ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; + } + if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW + && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { + ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; + } + } + + if (type == TYPE_PRESENTATION || type == TYPE_PRIVATE_PRESENTATION) { + mDisplayManagerInternal.onPresentation(displayContent.getDisplay().getDisplayId(), + /*isShown=*/ true); + } + + if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { + ProtoLog.w(WM_ERROR, + "Attempted to add private presentation window to a non-private display. " + + "Aborting."); + return WindowManagerGlobal.ADD_PERMISSION_DENIED; + } + + if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) { + ProtoLog.w(WM_ERROR, + "Attempted to add presentation window to a non-suitable display. " + + "Aborting."); + return WindowManagerGlobal.ADD_INVALID_DISPLAY; + } + + int userId = UserHandle.getUserId(session.mUid); + if (requestUserId != userId) { + try { + mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId, + false /*allowAll*/, ALLOW_NON_FULL, null, null); + } catch (Exception exp) { + ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d", + requestUserId); + return WindowManagerGlobal.ADD_INVALID_USER; + } + // It's fine to use this userId + userId = requestUserId; + } + + ActivityRecord activity = null; + final boolean hasParent = parentWindow != null; + // Use existing parent window token for child windows since they go in the same token + // as there parent window so we can apply the same policy on them. + WindowToken token = displayContent.getWindowToken( + hasParent ? parentWindow.mAttrs.token : attrs.token); + // If this is a child window, we want to apply the same type checking rules as the + // parent window type. + final int rootType = hasParent ? parentWindow.mAttrs.type : type; + + boolean addToastWindowRequiresToken = false; + + final IBinder windowContextToken = attrs.mWindowContextToken; + + if (token == null) { + if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type, + rootType, attrs.token, attrs.packageName)) { + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + if (hasParent) { + // Use existing parent window token for child windows. + token = parentWindow.mToken; + } else if (mWindowContextListenerController.hasListener(windowContextToken)) { + // Respect the window context token if the user provided it. + final IBinder binder = attrs.token != null ? attrs.token : windowContextToken; + final Bundle options = mWindowContextListenerController + .getOptions(windowContextToken); + token = new WindowToken.Builder(this, binder, type) + .setDisplayContent(displayContent) + .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow) + .setRoundedCornerOverlay(isRoundedCornerOverlay) + .setFromClientToken(true) + .setOptions(options) + .build(); + } else { + final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); + token = new WindowToken.Builder(this, binder, type) + .setDisplayContent(displayContent) + .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow) + .setRoundedCornerOverlay(isRoundedCornerOverlay) + .build(); + } + } else if (rootType >= FIRST_APPLICATION_WINDOW + && rootType <= LAST_APPLICATION_WINDOW) { + activity = token.asActivityRecord(); + if (activity == null) { + ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token " + + ".%s Aborting.", token); + return WindowManagerGlobal.ADD_NOT_APP_TOKEN; + } else if (activity.getParent() == null) { + ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token " + + ".%s Aborting.", token); + return WindowManagerGlobal.ADD_APP_EXITING; + } else if (type == TYPE_APPLICATION_STARTING) { + if (activity.mStartingWindow != null) { + ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + + "token with already existing starting window"); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } + if (activity.mStartingData == null) { + ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + + "token but already cleaned"); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } + } + } else if (rootType == TYPE_INPUT_METHOD) { + if (token.windowType != TYPE_INPUT_METHOD) { + ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (rootType == TYPE_VOICE_INTERACTION) { + if (token.windowType != TYPE_VOICE_INTERACTION) { + ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (rootType == TYPE_WALLPAPER) { + if (token.windowType != TYPE_WALLPAPER) { + ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { + if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { + ProtoLog.w(WM_ERROR, + "Attempted to add Accessibility overlay window with bad token " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (type == TYPE_TOAST) { + // Apps targeting SDK above N MR1 cannot arbitrary add toast windows. + addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName, + callingUid, parentWindow); + if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) { + ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (type == TYPE_QS_DIALOG) { + if (token.windowType != TYPE_QS_DIALOG) { + ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token " + + "%s. Aborting.", attrs.token); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } + } else if (token.asActivityRecord() != null) { + ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d", + rootType); + // It is not valid to use an app token with other system types; we will + // instead make a new token for it (as if null had been passed in for the token). + attrs.token = null; + token = new WindowToken.Builder(this, client.asBinder(), type) + .setDisplayContent(displayContent) + .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow) + .build(); + } + + final WindowState win = new WindowState(this, session, client, token, parentWindow, + appOp[0], attrs, viewVisibility, session.mUid, userId, + session.mCanAddInternalSystemWindow); + final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); + displayPolicy.adjustWindowParamsLw(win, win.mAttrs); + attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid); + attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid, + callingPid); + win.setRequestedVisibleTypes(requestedVisibleTypes); + + res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); + if (res != ADD_OKAY) { + return res; + } + + final boolean openInputChannels = (outInputChannel != null + && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0); + if (openInputChannels) { + win.openInputChannel(outInputChannel); + } + + // If adding a toast requires a token for this app we always schedule hiding + // toast windows to make sure they don't stick around longer then necessary. + // We hide instead of remove such windows as apps aren't prepared to handle + // windows being removed under them. + // + // If the app is older it can add toasts without a token and hence overlay + // other apps. To be maximally compatible with these apps we will hide the + // window after the toast timeout only if the focused window is from another + // UID, otherwise we allow unlimited duration. When a UID looses focus we + // schedule hiding all of its toast windows. + if (type == TYPE_TOAST) { + if (!displayContent.canAddToastWindowForUid(callingUid)) { + ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time."); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } + // Make sure this happens before we moved focus as one can make the + // toast focusable to force it not being hidden after the timeout. + // Focusable toasts are always timed out to prevent a focused app to + // show a focusable toasts while it has focus which will be kept on + // the screen after the activity goes away. + if (addToastWindowRequiresToken + || (attrs.flags & FLAG_NOT_FOCUSABLE) == 0 + || displayContent.mCurrentFocus == null + || displayContent.mCurrentFocus.mOwnerUid != callingUid) { + mH.sendMessageDelayed( + mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win), + win.mAttrs.hideTimeoutMilliseconds); + } + } + + // Switch to listen to the {@link WindowToken token}'s configuration changes when + // adding a window to the window context. Filter sub window type here because the sub + // window must be attached to the parent window, which is attached to the window context + // created window token. + if (!win.isChildWindow() + && mWindowContextListenerController.hasListener(windowContextToken)) { + final int windowContextType = mWindowContextListenerController + .getWindowType(windowContextToken); + final Bundle options = mWindowContextListenerController + .getOptions(windowContextToken); + if (type != windowContextType) { + ProtoLog.w(WM_ERROR, "Window types in WindowContext and" + + " LayoutParams.type should match! Type from LayoutParams is %d," + + " but type from WindowContext is %d", type, windowContextType); + // We allow WindowProviderService to add window other than windowContextType, + // but the WindowProviderService won't be associated with the window's + // WindowToken. + if (!isWindowProviderService(options)) { + return WindowManagerGlobal.ADD_INVALID_TYPE; + } + } else { + mWindowContextListenerController.updateContainerForWindowContextListener( + windowContextToken, token); + } + } + + // From now on, no exceptions or errors allowed! + if (displayContent.mCurrentFocus == null) { + displayContent.mWinAddedSinceNullFocus.add(win); + } + + if (excludeWindowTypeFromTapOutTask(type)) { + displayContent.mTapExcludedWindows.add(win); + } + + win.mSession.onWindowAdded(win); + mWindowMap.put(client.asBinder(), win); + win.initAppOpsState(); + + final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(), + UserHandle.getUserId(win.getOwningUid())); + win.setHiddenWhileSuspended(suspended); + + final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); + win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); + + boolean imMayMove = true; + + win.mToken.addWindow(win); + displayPolicy.addWindowLw(win, attrs); + displayPolicy.setDropInputModePolicy(win, win.mAttrs); + if (type == TYPE_APPLICATION_STARTING && activity != null) { + activity.attachStartingWindow(win); + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", + activity, win); + } else if (type == TYPE_INPUT_METHOD + // IME window is always touchable. + // Ignore non-touchable windows e.g. Stylus InkWindow.java. + && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) { + displayContent.setInputMethodWindowLocked(win); + imMayMove = false; + } else if (type == TYPE_INPUT_METHOD_DIALOG) { + displayContent.computeImeTarget(true /* updateImeTarget */); + imMayMove = false; + } else { + if (type == TYPE_WALLPAPER) { + displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (win.hasWallpaper()) { + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { + // If there is currently a wallpaper being shown, and + // the base layer of the new window is below the current + // layer of the target window, then adjust the wallpaper. + // This is to avoid a new window being placed between the + // wallpaper and its target. + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } + } + + final WindowStateAnimator winAnimator = win.mWinAnimator; + winAnimator.mEnterAnimationPending = true; + winAnimator.mEnteringAnimation = true; + + if (displayPolicy.areSystemBarsForcedConsumedLw()) { + res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; + } + if (displayContent.isInTouchMode()) { + res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; + } + if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) { + res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + } + + displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); + + boolean focusChanged = false; + if (win.canReceiveKeys()) { + focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, + false /*updateInputWindows*/); + if (focusChanged) { + imMayMove = false; + } + } + + if (imMayMove) { + displayContent.computeImeTarget(true /* updateImeTarget */); + if (win.isImeOverlayLayeringTarget()) { + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, + win.isVisibleRequestedOrAdding(), false /* removed */); + } + } + + // Don't do layout here, the window must call + // relayout to be displayed, so we'll do it there. + if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) { + // Assign child layers from the parent Task if the Activity is embedded. + win.getTask().assignChildLayers(); + } else { + win.getParent().assignChildLayers(); + } + + if (focusChanged) { + displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, + false /*updateInputWindows*/); + } + displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); + + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" + + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); + + boolean needToSendNewConfiguration = + win.isVisibleRequestedOrAdding() && displayContent.updateOrientation(); + if (win.providesDisplayDecorInsets()) { + needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo(); + } + if (needToSendNewConfiguration) { + displayContent.sendNewConfiguration(); + } + + // This window doesn't have a frame yet. Don't let this window cause the insets change. + displayContent.getInsetsStateController().updateAboveInsetsState( + false /* notifyInsetsChanged */); + + outInsetsState.set(win.getCompatInsetsState(), true /* copySources */); + getInsetsSourceControls(win, outActiveControls); + + if (win.mLayoutAttached) { + outAttachedFrame.set(win.getParentWindow().getFrame()); + if (win.mInvGlobalScale != 1f) { + outAttachedFrame.scale(win.mInvGlobalScale); + } + } else { + // Make this invalid which indicates a null attached frame. + outAttachedFrame.set(0, 0, -1, -1); + } + outSizeCompatScale[0] = win.getCompatScaleForClient(); + } + + Binder.restoreCallingIdentity(origId); + + return res; + } + + private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow, + int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) { + if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { + ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + if (rootType == TYPE_INPUT_METHOD) { + ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + if (rootType == TYPE_VOICE_INTERACTION) { + ProtoLog.w(WM_ERROR, + "Attempted to add voice interaction window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + if (rootType == TYPE_WALLPAPER) { + ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + if (rootType == TYPE_QS_DIALOG) { + ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + if (rootType == TYPE_ACCESSIBILITY_OVERLAY) { + ProtoLog.w(WM_ERROR, + "Attempted to add Accessibility overlay window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + if (type == TYPE_TOAST) { + // Apps targeting SDK above N MR1 cannot arbitrary add toast windows. + if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) { + ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token " + + "%s. Aborting.", tokenForLog); + return false; + } + } + return true; + } + + /** + * Get existing {@link DisplayContent} or create a new one if the display is registered in + * DisplayManager. + * + * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent} + * that corresponds to a display just added to DisplayManager has not yet been created. This + * usually means that the call of this method was initiated from outside of Activity or Window + * Manager. In most cases the regular getter should be used. + * @param displayId The preferred display Id. + * @param token The window token associated with the window we are trying to get display for. + * if not null then the display of the window token will be returned. Set to null + * is there isn't an a token associated with the request. + * @see RootWindowContainer#getDisplayContent(int) + */ + private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) { + if (token != null) { + final WindowToken wToken = mRoot.getWindowToken(token); + if (wToken != null) { + return wToken.getDisplayContent(); + } + } + + return mRoot.getDisplayContentOrCreate(displayId); + } + + private boolean doesAddToastWindowRequireToken(String packageName, int callingUid, + WindowState attachedWindow) { + // Try using the target SDK of the root window + if (attachedWindow != null) { + return attachedWindow.mActivityRecord != null + && attachedWindow.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.O; + } else { + // Otherwise, look at the package + final ApplicationInfo appInfo = mPmInternal.getApplicationInfo( + packageName, 0 /* flags */, SYSTEM_UID, UserHandle.getUserId(callingUid)); + if (appInfo == null || appInfo.uid != callingUid) { + throw new SecurityException("Package " + packageName + " not in UID " + + callingUid); + } + return appInfo.targetSdkVersion >= Build.VERSION_CODES.O; + } + } + + /** + * Set whether screen capture is disabled for all windows of a specific user from + * the device policy cache, or specific windows based on sensitive content protections. + */ + @Override + public void refreshScreenCaptureDisabled() { + int callingUid = Binder.getCallingUid(); + // MY_UID (Process.myUid()) should always be SYSTEM_UID here, but using MY_UID for tests + if (callingUid != MY_UID) { + throw new SecurityException("Only system can call refreshScreenCaptureDisabled."); + } + + synchronized (mGlobalLock) { + // Refresh secure surface for all windows. + mRoot.refreshSecureSurfaceState(); + } + } + + void removeClientToken(Session session, IBinder client) { + synchronized (mGlobalLock) { + WindowState win = windowForClientLocked(session, client, false); + if (win != null) { + win.removeIfPossible(); + return; + } + + // Remove embedded window map if the token belongs to an embedded window + mEmbeddedWindowController.remove(client); + } + } + + /** + * Performs some centralized bookkeeping clean-up on the window that is being removed. + * NOTE: Should only be called from {@link WindowState#removeImmediately()} + * TODO: Maybe better handled with a method {@link WindowContainer#removeChild} if we can + * figure-out a good way to have all parents of a WindowState doing the same thing without + * forgetting to add the wiring when a new parent of WindowState is added. + */ + void postWindowRemoveCleanupLocked(WindowState win) { + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "postWindowRemoveCleanupLocked: %s", win); + mWindowMap.remove(win.mClient.asBinder()); + + final DisplayContent dc = win.getDisplayContent(); + dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */); + + win.resetAppOpsState(); + + if (dc.mCurrentFocus == null) { + dc.mWinRemovedSinceNullFocus.add(win); + } + mEmbeddedWindowController.onWindowRemoved(win); + mResizingWindows.remove(win); + updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */); + mWindowsChanged = true; + ProtoLog.v(WM_DEBUG_WINDOW_MOVEMENT, "Final remove of window: %s", win); + + final DisplayContent displayContent = win.getDisplayContent(); + if (displayContent.mInputMethodWindow == win) { + displayContent.setInputMethodWindowLocked(null); + } + + final WindowToken token = win.mToken; + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing %s from %s", win, token); + // Window will already be removed from token before this post clean-up method is called. + if (token.isEmpty() && !token.mPersistOnEmpty) { + token.removeImmediately(); + } + + if (win.mActivityRecord != null) { + win.mActivityRecord.postWindowRemoveStartingWindowCleanup(win); + } + + if (win.mAttrs.type == TYPE_WALLPAPER) { + dc.mWallpaperController.clearLastWallpaperTimeoutTime(); + dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (dc.mWallpaperController.isWallpaperTarget(win)) { + dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } + + if (dc != null && !mWindowPlacerLocked.isInLayout()) { + dc.assignWindowLayers(true /* setLayoutNeeded */); + mWindowPlacerLocked.performSurfacePlacement(); + if (win.mActivityRecord != null) { + win.mActivityRecord.updateReportedVisibilityLocked(); + } + } + + dc.getInputMonitor().updateInputWindowsLw(true /*force*/); + } + + private void updateHiddenWhileSuspendedState(ArraySet packages, boolean suspended) { + synchronized (mGlobalLock) { + mRoot.updateHiddenWhileSuspendedState(packages, suspended); + } + } + + private void updateAppOpsState() { + synchronized (mGlobalLock) { + mRoot.updateAppOpsState(); + } + } + + static void logSurface(WindowState w, String msg, boolean withStackTrace) { + String str = " SURFACE " + msg + ": " + w; + if (withStackTrace) { + logWithStack(TAG, str); + } else { + Slog.i(TAG_WM, str); + } + } + + static void logWithStack(String tag, String s) { + RuntimeException e = null; + if (SHOW_STACK_CRAWLS) { + e = new RuntimeException(); + e.fillInStackTrace(); + } + Slog.i(tag, s, e); + } + + void clearTouchableRegion(Session session, IWindow client) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + WindowState w = windowForClientLocked(session, client, false); + w.clearClientTouchableRegion(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, + Rect visibleInsets, Region touchableRegion) { + int uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + WindowState w = windowForClientLocked(session, client, false); + if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w + + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets + + ", visibleInsets=" + w.mGivenVisibleInsets + " -> " + visibleInsets + + ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion + + ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets); + if (w != null) { + w.mGivenInsetsPending = false; + w.mGivenContentInsets.set(contentInsets); + w.mGivenVisibleInsets.set(visibleInsets); + w.mGivenTouchableRegion.set(touchableRegion); + w.mTouchableInsets = touchableInsets; + if (w.mGlobalScale != 1) { + w.mGivenContentInsets.scale(w.mGlobalScale); + w.mGivenVisibleInsets.scale(w.mGlobalScale); + w.mGivenTouchableRegion.scale(w.mGlobalScale); + } + w.setDisplayLayoutNeeded(); + w.updateSourceFrame(w.getFrame()); + mWindowPlacerLocked.performSurfacePlacement(); + w.getDisplayContent().getInputMonitor().updateInputWindowsLw(true); + + // We need to report touchable region changes to accessibility. + if (mAccessibilityController.hasCallbacks()) { + mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid( + uid, w.getDisplayContent().getDisplayId()); + } + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) { + final AccessibilityController.AccessibilityControllerInternalImpl a11yControllerInternal = + AccessibilityController.getAccessibilityControllerInternal(this); + synchronized (mGlobalLock) { + if (a11yControllerInternal.hasWindowManagerEventDispatcher()) { + WindowState window = mWindowMap.get(token); + if (window != null) { + a11yControllerInternal.onRectangleOnScreenRequested( + window.getDisplayId(), rectangle); + } + } + } + } + + public IWindowId getWindowId(IBinder token) { + synchronized (mGlobalLock) { + WindowState window = mWindowMap.get(token); + return window != null ? window.mWindowId : null; + } + } + + public void pokeDrawLock(Session session, IBinder token) { + synchronized (mGlobalLock) { + WindowState window = windowForClientLocked(session, token, false); + if (window != null) { + window.pokeDrawLockLw(mDrawLockTimeoutMillis); + } + } + } + + private boolean hasStatusBarPermission(int pid, int uid) { + return mContext.checkPermission(permission.STATUS_BAR, pid, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns whether this window can proceed with drawing or needs to retry later. + */ + public boolean cancelDraw(Session session, IWindow client) { + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return false; + } + + return win.cancelAndRedraw(); + } + } + + public int relayoutWindow(Session session, IWindow client, LayoutParams attrs, + int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq, + int lastSyncSeqId, ClientWindowFrames outFrames, + MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl, + InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, + Bundle outSyncIdBundle) { + if (outActiveControls != null) { + outActiveControls.set(null); + } + int result = 0; + boolean configChanged = false; + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return 0; + } + if (win.mRelayoutSeq < seq) { + win.mRelayoutSeq = seq; + } else if (win.mRelayoutSeq > seq) { + return 0; + } + + if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= lastSyncSeqId) { + // The client has reported the sync draw, but we haven't finished it yet. + // Don't let the client perform a non-sync draw at this time. + result |= RELAYOUT_RES_CANCEL_AND_REDRAW; + } + + final DisplayContent displayContent = win.getDisplayContent(); + final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); + + WindowStateAnimator winAnimator = win.mWinAnimator; + if (viewVisibility != View.GONE) { + win.setRequestedSize(requestedWidth, requestedHeight); + } + + int attrChanges = 0; + int flagChanges = 0; + int privateFlagChanges = 0; + if (attrs != null) { + displayPolicy.adjustWindowParamsLw(win, attrs); + attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid); + attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), uid, + pid); + int disableFlags = + (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK; + if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) { + disableFlags = 0; + } + win.mDisableFlags = disableFlags; + if (win.mAttrs.type != attrs.type) { + throw new IllegalArgumentException( + "Window type can not be changed after the window is added."); + } + if (!(win.mAttrs.providedInsets == null && attrs.providedInsets == null)) { + if (win.mAttrs.providedInsets == null || attrs.providedInsets == null + || (win.mAttrs.providedInsets.length != attrs.providedInsets.length)) { + throw new IllegalArgumentException( + "Insets amount can not be changed after the window is added."); + } else { + final int insetsTypes = attrs.providedInsets.length; + for (int i = 0; i < insetsTypes; i++) { + if (!win.mAttrs.providedInsets[i].idEquals(attrs.providedInsets[i])) { + throw new IllegalArgumentException( + "Insets ID can not be changed after the window is added."); + } + final InsetsFrameProvider.InsetsSizeOverride[] overrides = + win.mAttrs.providedInsets[i].getInsetsSizeOverrides(); + final InsetsFrameProvider.InsetsSizeOverride[] newOverrides = + attrs.providedInsets[i].getInsetsSizeOverrides(); + if (!(overrides == null && newOverrides == null)) { + if (overrides == null || newOverrides == null + || (overrides.length != newOverrides.length)) { + throw new IllegalArgumentException( + "Insets override types can not be changed after the " + + "window is added."); + } else { + final int overrideTypes = overrides.length; + for (int j = 0; j < overrideTypes; j++) { + if (overrides[j].getWindowType() + != newOverrides[j].getWindowType()) { + throw new IllegalArgumentException( + "Insets override types can not be changed after" + + " the window is added."); + } + } + } + } + } + } + } + + final boolean wasTrustedOverlay = win.isWindowTrustedOverlay(); + flagChanges = win.mAttrs.flags ^ attrs.flags; + privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags; + attrChanges = win.mAttrs.copyFrom(attrs); + final boolean layoutChanged = + (attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0; + if (layoutChanged || (attrChanges + & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) { + win.mLayoutNeeded = true; + } + if (layoutChanged && win.providesDisplayDecorInsets()) { + configChanged = displayPolicy.updateDecorInsetsInfo(); + } + if (wasTrustedOverlay != win.isWindowTrustedOverlay()) { + win.updateTrustedOverlay(); + } + if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0 + || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) { + win.mActivityRecord.checkKeyguardFlagsChanged(); + } + + if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) { + updateNonSystemOverlayWindowsVisibilityIfNeeded( + win, win.mWinAnimator.getShown()); + } + if ((attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED)) != 0) { + winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags + & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0); + } + // See if the DisplayWindowPolicyController wants to keep the activity on the window + if (displayContent.mDwpcHelper.hasController() + && win.mActivityRecord != null && (!win.mRelayoutCalled || flagChanges != 0 + || privateFlagChanges != 0)) { + int newOrChangedFlags = !win.mRelayoutCalled ? win.mAttrs.flags : flagChanges; + int newOrChangedPrivateFlags = + !win.mRelayoutCalled ? win.mAttrs.privateFlags : privateFlagChanges; + + if (!displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged( + win.mActivityRecord.info, newOrChangedFlags, newOrChangedPrivateFlags, + win.mAttrs.flags, + win.mAttrs.privateFlags)) { + mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY, + win.mActivityRecord.getTask())); + Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed," + + " can't remain on display " + displayContent.getDisplayId()); + return 0; + } + } + } + + if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility + + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs); + if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) { + winAnimator.mAlpha = attrs.alpha; + } + win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight); + + if (win.mAttrs.surfaceInsets.left != 0 + || win.mAttrs.surfaceInsets.top != 0 + || win.mAttrs.surfaceInsets.right != 0 + || win.mAttrs.surfaceInsets.bottom != 0) { + winAnimator.setOpaqueLocked(false); + } + + final int oldVisibility = win.mViewVisibility; + + // If the window is becoming visible, visibleOrAdding may change which may in turn + // change the IME target. + final boolean becameVisible = + (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE) + && viewVisibility == View.VISIBLE; + boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0 + || becameVisible; + boolean focusMayChange = win.mViewVisibility != viewVisibility + || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0) + || (!win.mRelayoutCalled); + + boolean wallpaperMayMove = win.mViewVisibility != viewVisibility + && win.hasWallpaper(); + wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0; + if ((flagChanges & FLAG_SECURE) != 0) { + win.setSecureLocked(win.isSecureLocked()); + } + + final boolean wasVisible = win.isVisible(); + + win.mRelayoutCalled = true; + win.mInRelayout = true; + + win.setViewVisibility(viewVisibility); + ProtoLog.i(WM_DEBUG_SCREEN_ON, + "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility, + viewVisibility, new RuntimeException().fillInStackTrace()); + + win.setDisplayLayoutNeeded(); + win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; + + // We should only relayout if the view is visible, it is a starting window, or the + // associated appToken is not hidden. + final boolean shouldRelayout = viewVisibility == View.VISIBLE && + (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING + || win.mActivityRecord.isClientVisible()); + + // If we are not currently running the exit animation, we need to see about starting + // one. + // This must be called before the call to performSurfacePlacement. + if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) { + if (DEBUG_VISIBILITY) { + Slog.i(TAG_WM, + "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit); + } + result |= RELAYOUT_RES_SURFACE_CHANGED; + // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag + // in DC#pendingLayoutChanges and update the wallpaper target later. + // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window + // when the window is about to exit, so we update the wallpaper target + // immediately here. Otherwise this window will be stuck in exiting and its + // surface remains on the screen. + // TODO(b/189856716): Allow destroying surface even if it belongs to the + // keyguard target. + if (wallpaperMayMove) { + displayContent.mWallpaperController.adjustWallpaperWindows(); + } + tryStartExitingAnimation(win, winAnimator); + } + + // Create surfaceControl before surface placement otherwise layout will be skipped + // (because WS.isGoneForLayout() is true when there is no surface. + if (shouldRelayout && outSurfaceControl != null) { + try { + result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); + } catch (Exception e) { + displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); + + ProtoLog.w(WM_ERROR, + "Exception thrown when creating surface for client %s (%s). %s", + client, win.mAttrs.getTitle(), e); + Binder.restoreCallingIdentity(origId); + return 0; + } + } + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + + if (shouldRelayout) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); + + result = win.relayoutVisibleWindow(result); + + if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { + focusMayChange = true; + } + if (win.mAttrs.type == TYPE_INPUT_METHOD + && displayContent.mInputMethodWindow == null) { + displayContent.setInputMethodWindowLocked(win); + imMayMove = true; + } + win.adjustStartingWindowFlags(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } else { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2"); + + winAnimator.mEnterAnimationPending = false; + winAnimator.mEnteringAnimation = false; + + if (outSurfaceControl != null) { + if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) { + // We already told the client to go invisible, but the message may not be + // handled yet, or it might want to draw a last frame. If we already have a + // surface, let the client use that, but don't create new surface at this + // point. + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface"); + winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } else { + if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); + + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_" + + win.mAttrs.getTitle()); + outSurfaceControl.release(); + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + } + + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + if (focusMayChange) { + if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) { + imMayMove = false; + } + } + + // updateFocusedWindowLocked() already assigned layers so we only need to + // reassign them at this point if the IM window state gets shuffled + boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; + if (imMayMove) { + displayContent.computeImeTarget(true /* updateImeTarget */); + if (toBeDisplayed) { + // Little hack here -- we -should- be able to rely on the function to return + // true if the IME has moved and needs its layer recomputed. However, if the IME + // was hidden and isn't actually moved in the list, its layer may be out of data + // so we make sure to recompute it. + displayContent.assignWindowLayers(false /* setLayoutNeeded */); + } + } + + if (wallpaperMayMove) { + displayContent.pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } + + if (win.mActivityRecord != null) { + displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord); + } + + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation"); + configChanged |= displayContent.updateOrientation(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + + if (toBeDisplayed && win.mIsWallpaper) { + displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */); + } + if (win.mActivityRecord != null) { + win.mActivityRecord.updateReportedVisibilityLocked(); + } + if (displayPolicy.areSystemBarsForcedConsumedLw()) { + result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; + } + if (!win.isGoneForLayout()) { + win.mResizedWhileGone = false; + } + + if (outFrames != null && outMergedConfiguration != null) { + win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration, + false /* useLatestConfig */, shouldRelayout); + + // Set resize-handled here because the values are sent back to the client. + win.onResizeHandled(); + } + + if (outInsetsState != null) { + outInsetsState.set(win.getCompatInsetsState(), true /* copySources */); + } + + ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b", + win, focusMayChange); + + if (DEBUG_LAYOUT) { + Slog.v(TAG_WM, "Relayout complete " + win + ": outFrames=" + outFrames); + } + win.mInRelayout = false; + + final boolean winVisibleChanged = win.isVisible() != wasVisible; + if (win.isImeOverlayLayeringTarget() && winVisibleChanged) { + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, + win.isVisible(), false /* removed */); + } + // Notify listeners about IME input target window visibility change. + final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win; + if (isImeInputTarget && winVisibleChanged) { + dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(), + win.isVisible() /* visible */, false /* removed */); + } + + if (outSyncIdBundle != null) { + final int maybeSyncSeqId; + if (win.syncNextBuffer() && viewVisibility == View.VISIBLE + && win.mSyncSeqId > lastSyncSeqId) { + maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1; + win.markRedrawForSyncReported(); + } else { + maybeSyncSeqId = -1; + } + outSyncIdBundle.putInt("seqid", maybeSyncSeqId); + } + + if (configChanged) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "relayoutWindow: postNewConfigurationToHandler"); + displayContent.sendNewConfiguration(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + if (outActiveControls != null) { + getInsetsSourceControls(win, outActiveControls); + } + } + + Binder.restoreCallingIdentity(origId); + return result; + } + + private void getInsetsSourceControls(WindowState win, InsetsSourceControl.Array outArray) { + final InsetsSourceControl[] controls = + win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win); + if (controls != null) { + final int length = controls.length; + final InsetsSourceControl[] outControls = new InsetsSourceControl[length]; + for (int i = 0; i < length; i++) { + // We will leave the critical section before returning the leash to the client, + // so we need to copy the leash to prevent others release the one that we are + // about to return. + if (controls[i] != null) { + // This source control is an extra copy if the client is not local. By setting + // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of + // SurfaceControl.writeToParcel. + outControls[i] = new InsetsSourceControl(controls[i]); + outControls[i].setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE); + } + } + outArray.set(outControls); + } + } + + private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) { + // Try starting an animation; if there isn't one, we + // can destroy the surface right away. + int transit = WindowManagerPolicy.TRANSIT_EXIT; + if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { + transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; + } + + if (win.isVisible() && win.isDisplayed() && win.mDisplayContent.okToAnimate()) { + String reason = null; + if (winAnimator.applyAnimationLocked(transit, false)) { + // This is a WMCore-driven window animation. + reason = "applyAnimation"; + } else if (win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) { + // This is already animating via a WMCore-driven window animation. + reason = "selfAnimating"; + } else { + if (win.mTransitionController.isShellTransitionsEnabled()) { + // Already animating as part of a shell-transition. Currently this only handles + // activity window because other types should be WMCore-driven. + if ((win.mActivityRecord != null && win.mActivityRecord.inTransition())) { + win.mTransitionController.mAnimatingExitWindows.add(win); + reason = "inTransition"; + } + } else if (win.isAnimating(PARENTS | TRANSITION, + ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) { + // Already animating as part of a legacy app-transition. + reason = "inLegacyTransition"; + } + } + if (reason != null) { + win.mAnimatingExit = true; + ProtoLog.d(WM_DEBUG_ANIM, + "Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win); + } + } + if (!win.mAnimatingExit) { + boolean stopped = win.mActivityRecord == null || win.mActivityRecord.mAppStopped; + // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces + // will later actually destroy the surface if we do not do so here. Normally we leave + // this to the exit animation. + win.mDestroying = true; + win.destroySurface(false, stopped); + } + if (mAccessibilityController.hasCallbacks()) { + mAccessibilityController.onWindowTransition(win, transit); + } + } + + private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, + WindowState win, WindowStateAnimator winAnimator) { + if (!win.mHasSurface) { + result |= RELAYOUT_RES_SURFACE_CHANGED; + } + + WindowSurfaceController surfaceController; + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl"); + surfaceController = winAnimator.createSurfaceLocked(); + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + if (surfaceController != null) { + surfaceController.getSurfaceControl(outSurfaceControl); + ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl); + + } else { + // For some reason there isn't a surface. Clear the + // caller's object so they see the same state. + ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win); + outSurfaceControl.release(); + } + + return result; + } + + public boolean outOfMemoryWindow(Session session, IWindow client) { + final long origId = Binder.clearCallingIdentity(); + + try { + synchronized (mGlobalLock) { + WindowState win = windowForClientLocked(session, client, false); + if (win == null) { + return false; + } + return mRoot.reclaimSomeSurfaceMemory(win.mWinAnimator, "from-client", false); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + void finishDrawingWindow(Session session, IWindow client, + @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) { + if (postDrawTransaction != null) { + postDrawTransaction.sanitize(Binder.getCallingPid(), Binder.getCallingUid()); + } + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + WindowState win = windowForClientLocked(session, client, false); + ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s", + win, (win != null ? win.mWinAnimator.drawStateToString() : "null")); + if (win != null && win.finishDrawing(postDrawTransaction, seqId)) { + if (win.hasWallpaper()) { + win.getDisplayContent().pendingLayoutChanges |= + WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } + win.setDisplayLayoutNeeded(); + mWindowPlacerLocked.requestTraversal(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + boolean checkCallingPermission(String permission, String func) { + return checkCallingPermission(permission, func, true /* printLog */); + } + + boolean checkCallingPermission(String permission, String func, boolean printLog) { + if (Binder.getCallingPid() == MY_PID) { + return true; + } + + if (mContext.checkCallingPermission(permission) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + if (printLog) { + ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s", + func, Binder.getCallingPid(), Binder.getCallingUid(), permission); + } + return false; + } + + @Override + public void addWindowToken(@NonNull IBinder binder, int type, int displayId, + @Nullable Bundle options) { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized (mGlobalLock) { + final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); + if (dc == null) { + ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s" + + " for non-exiting displayId=%d", binder, displayId); + return; + } + + WindowToken token = dc.getWindowToken(binder); + if (token != null) { + ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s" + + " for already created window token: %s" + + " displayId=%d", binder, token, displayId); + return; + } + if (type == TYPE_WALLPAPER) { + new WallpaperWindowToken(this, binder, true, dc, + true /* ownerCanManageAppTokens */, options); + } else { + new WindowToken.Builder(this, binder, type) + .setDisplayContent(dc) + .setPersistOnEmpty(true) + .setOwnerCanManageAppTokens(true) + .setOptions(options) + .build(); + } + } + } + + @Nullable + @Override + public WindowContextInfo attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread, + @NonNull IBinder clientToken, @LayoutParams.WindowType int type, int displayId, + @Nullable Bundle options) { + Objects.requireNonNull(appThread); + Objects.requireNonNull(clientToken); + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "attachWindowContextToDisplayArea", false /* printLog */); + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final WindowProcessController wpc = mAtmService.getProcessController(appThread); + if (wpc == null) { + ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: calling from" + + " non-existing process pid=%d uid=%d", callingPid, callingUid); + return null; + } + final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach" + + " to a non-existing display:%d", displayId); + return null; + } + // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after + // the feature b/155340867 is completed. + final DisplayArea da = dc.findAreaForWindowType(type, options, + callerCanManageAppTokens, false /* roundedCornerOverlay */); + mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken, + da, type, options, false /* shouldDispatchConfigWhenRegistering */); + return new WindowContextInfo(da.getConfiguration(), displayId); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Nullable + @Override + public WindowContextInfo attachWindowContextToDisplayContent( + @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) { + Objects.requireNonNull(appThread); + Objects.requireNonNull(clientToken); + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final WindowProcessController wpc = mAtmService.getProcessController(appThread); + if (wpc == null) { + ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayContent: calling from" + + " non-existing process pid=%d uid=%d", callingPid, callingUid); + return null; + } + // We use "getDisplayContent" instead of "getDisplayContentOrCreate" because + // this method may be called in DisplayPolicy's constructor and may cause + // infinite loop. In this scenario, we early return here and switch to do the + // registration in DisplayContent#onParentChanged at DisplayContent initialization. + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + if (callingPid != MY_PID) { + throw new WindowManager.InvalidDisplayException( + "attachWindowContextToDisplayContent: trying to attach to a" + + " non-existing display:" + displayId); + } + // Early return if this method is invoked from system process. + // See above comments for more detail. + return null; + } + + mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken, + dc, INVALID_WINDOW_TYPE, null /* options */, + false /* shouldDispatchConfigWhenRegistering */); + return new WindowContextInfo(dc.getConfiguration(), displayId); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Nullable + @Override + public WindowContextInfo attachWindowContextToWindowToken(@NonNull IApplicationThread appThread, + @NonNull IBinder clientToken, @NonNull IBinder token) { + Objects.requireNonNull(appThread); + Objects.requireNonNull(clientToken); + Objects.requireNonNull(token); + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "attachWindowContextToWindowToken", false /* printLog */); + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final WindowProcessController wpc = mAtmService.getProcessController(appThread); + if (wpc == null) { + ProtoLog.w(WM_ERROR, "attachWindowContextToWindowToken: calling from" + + " non-existing process pid=%d uid=%d", callingPid, callingUid); + return null; + } + final WindowToken windowToken = mRoot.getWindowToken(token); + if (windowToken == null) { + ProtoLog.w(WM_ERROR, "Then token:%s is invalid. It might be " + + "removed", token); + return null; + } + final int type = mWindowContextListenerController.getWindowType(clientToken); + if (type == INVALID_WINDOW_TYPE) { + throw new IllegalArgumentException("The clientToken:" + clientToken + + " should have been attached."); + } + if (type != windowToken.windowType) { + throw new IllegalArgumentException("The WindowToken's type should match" + + " the created WindowContext's type. WindowToken's type is " + + windowToken.windowType + ", while WindowContext's is " + type); + } + if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken, + callerCanManageAppTokens, callingUid)) { + return null; + } + mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken, + windowToken, windowToken.windowType, windowToken.mOptions, + false /* shouldDispatchConfigWhenRegistering */); + return new WindowContextInfo(windowToken.getConfiguration(), + windowToken.getDisplayContent().getDisplayId()); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void detachWindowContext(@NonNull IBinder clientToken) { + Objects.requireNonNull(clientToken); + final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, + "detachWindowContext", false /* printLog */); + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken, + callerCanManageAppTokens, callingUid)) { + return; + } + final WindowContainer wc = mWindowContextListenerController + .getContainer(clientToken); + + mWindowContextListenerController.unregisterWindowContainerListener(clientToken); + + final WindowToken token = wc.asWindowToken(); + if (token != null && token.isFromClient()) { + removeWindowToken(token.token, token.getDisplayContent().getDisplayId()); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** Returns {@code true} if this binder is a registered window token. */ + @Override + public boolean isWindowToken(IBinder binder) { + synchronized (mGlobalLock) { + return mRoot.getWindowToken(binder) != null; + } + + } + + void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + + if (dc == null) { + ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" + + " for non-exiting displayId=%d", binder, displayId); + return; + } + final WindowToken token = dc.removeWindowToken(binder, animateExit); + if (token == null) { + ProtoLog.w(WM_ERROR, + "removeWindowToken: Attempted to remove non-existing token: %s", + binder); + return; + } + + if (removeWindows) { + token.removeAllWindowsIfPossible(); + } + dc.getInputMonitor().updateInputWindowsLw(true /* force */); + } + } + + @Override + public void removeWindowToken(IBinder binder, int displayId) { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + final long origId = Binder.clearCallingIdentity(); + try { + removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** @see WindowManagerInternal#moveWindowTokenToDisplay(IBinder, int) */ + public void moveWindowTokenToDisplay(IBinder binder, int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + ProtoLog.w(WM_ERROR, "moveWindowTokenToDisplay: Attempted to move token: %s" + + " to non-exiting displayId=%d", binder, displayId); + return; + } + final WindowToken token = mRoot.getWindowToken(binder); + if (token == null) { + ProtoLog.w(WM_ERROR, + "moveWindowTokenToDisplay: Attempted to move non-existing token: %s", + binder); + return; + } + if (token.getDisplayContent() == dc) { + ProtoLog.w(WM_ERROR, + "moveWindowTokenToDisplay: Cannot move to the original display " + + "for token: %s", binder); + return; + } + dc.reParentWindowToken(token); + } + } + + // TODO(multi-display): remove when no default display use case. + void prepareAppTransitionNone() { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + getDefaultDisplayContentLocked().prepareAppTransition(TRANSIT_NONE); + } + + @Override + public void overridePendingAppTransitionMultiThumbFuture( + IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, + boolean scaleUp, int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + Slog.w(TAG, "Attempted to call overridePendingAppTransitionMultiThumbFuture" + + " for the display " + displayId + " that does not exist."); + return; + } + displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture, + callback, scaleUp); + } + } + + @Override + public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter, + int displayId) { + if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "overridePendingAppTransitionRemote()")) { + throw new SecurityException( + "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission"); + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + Slog.w(TAG, "Attempted to call overridePendingAppTransitionRemote" + + " for the display " + displayId + " that does not exist."); + return; + } + remoteAnimationAdapter.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid()); + displayContent.mAppTransition.overridePendingAppTransitionRemote( + remoteAnimationAdapter); + } + } + + @Override + public void endProlongedAnimations() { + // TODO: Remove once clients are updated. + } + + // TODO(multi-display): remove when no default display use case. + // (i.e. KeyguardController / RecentsAnimation) + public void executeAppTransition() { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + getDefaultDisplayContentLocked().executeAppTransition(); + } + + void initializeRecentsAnimation(int targetActivityType, + IRecentsAnimationRunner recentsAnimationRunner, + RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId, + SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) { + mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner, + callbacks, displayId); + mRoot.getDisplayContent(displayId).mAppTransition.updateBooster(); + mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity); + } + + @VisibleForTesting + void setRecentsAnimationController(RecentsAnimationController controller) { + mRecentsAnimationController = controller; + } + + RecentsAnimationController getRecentsAnimationController() { + return mRecentsAnimationController; + } + + void cancelRecentsAnimation( + @RecentsAnimationController.ReorderMode int reorderMode, String reason) { + if (mRecentsAnimationController != null) { + // This call will call through to cleanupAnimation() below after the animation is + // canceled + mRecentsAnimationController.cancelAnimation(reorderMode, reason); + } + } + + + void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { + if (mRecentsAnimationController != null) { + final RecentsAnimationController controller = mRecentsAnimationController; + mRecentsAnimationController = null; + controller.cleanupAnimation(reorderMode); + // TODO(multi-display): currently only default display support recents animation. + final DisplayContent dc = getDefaultDisplayContentLocked(); + if (dc.mAppTransition.isTransitionSet()) { + dc.mSkipAppTransitionAnimation = true; + } + dc.forAllWindowContainers((wc) -> { + if (wc.isAnimating(TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) { + wc.cancelAnimation(); + } + }); + } + } + + boolean isRecentsAnimationTarget(ActivityRecord r) { + return mRecentsAnimationController != null && mRecentsAnimationController.isTargetApp(r); + } + + boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) { + return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio( + aspectRatio); + } + + boolean isValidExpandedPictureInPictureAspectRatio(DisplayContent displayContent, + float aspectRatio) { + return displayContent.getPinnedTaskController().isValidExpandedPictureInPictureAspectRatio( + aspectRatio); + } + + @Override + public void notifyKeyguardTrustedChanged() { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) { + mRoot.ensureActivitiesVisible(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void screenTurningOff(int displayId, ScreenOffListener listener) { + mTaskSnapshotController.screenTurningOff(displayId, listener); + } + + @Override + public void triggerAnimationFailsafe() { + mH.sendEmptyMessage(H.ANIMATION_FAILSAFE); + } + + @Override + public void onKeyguardShowingAndNotOccludedChanged() { + mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); + dispatchKeyguardLockedState(); + } + + @Override + public void onPowerKeyDown(boolean isScreenOn) { + mRoot.forAllDisplayPolicies(p -> p.onPowerKeyDown(isScreenOn)); + } + + @Override + public void onUserSwitched() { + mSettingsObserver.updateSystemUiSettings(true /* handleChange */); + synchronized (mGlobalLock) { + // force a re-application of focused window sysui visibility on each display. + mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemBarAttributes); + } + } + + @Override + public void moveDisplayToTopIfAllowed(int displayId) { + moveDisplayToTopInternal(displayId); + syncInputTransactions(true /* waitForAnimations */); + } + + /** + * Moves the given display to the top. If it cannot be moved to the top this method does + * nothing (e.g. if the display has the flag FLAG_STEAL_TOP_FOCUS_DISABLED set). + * @param displayId The display to move to the top. + */ + void moveDisplayToTopInternal(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && mRoot.getTopChild() != displayContent) { + // Check whether anything prevents us from moving the display to the top. + if (!displayContent.canStealTopFocus()) { + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, + "Not moving display (displayId=%d) to top. Top focused displayId=%d. " + + "Reason: FLAG_STEAL_TOP_FOCUS_DISABLED", + displayId, mRoot.getTopFocusedDisplayContent().getDisplayId()); + return; + } + + // Nothing prevented us from moving the display to the top. Let's do it! + displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, + displayContent, true /* includingParents */); + } + } + } + + @Override + public boolean isAppTransitionStateIdle() { + return getDefaultDisplayContentLocked().mAppTransition.isIdle(); + } + + + // ------------------------------------------------------------- + // Misc IWindowSession methods + // ------------------------------------------------------------- + + /** Freeze the screen during a user-switch event. Called by UserController. */ + @Override + public void startFreezingScreen(int exitAnim, int enterAnim) { + if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, + "startFreezingScreen()")) { + throw new SecurityException("Requires FREEZE_SCREEN permission"); + } + + synchronized (mGlobalLock) { + if (!mClientFreezingScreen) { + mClientFreezingScreen = true; + final long origId = Binder.clearCallingIdentity(); + try { + startFreezingDisplay(exitAnim, enterAnim); + mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); + mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + } + + /** + * No longer actively demand that the screen remain frozen. + * Called by UserController after a user-switch. + * This doesn't necessarily immediately unlock the screen; it just allows it if we're ready. + */ + @Override + public void stopFreezingScreen() { + if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, + "stopFreezingScreen()")) { + throw new SecurityException("Requires FREEZE_SCREEN permission"); + } + + synchronized (mGlobalLock) { + if (mClientFreezingScreen) { + mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client"; + final long origId = Binder.clearCallingIdentity(); + try { + stopFreezingDisplayLocked(); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + } + + @Override + public void disableKeyguard(IBinder token, String tag, int userId) { + userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false /* allowAll */, ALLOW_FULL_ONLY, "disableKeyguard", null); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + final int callingUid = Binder.getCallingUid(); + final long origIdentity = Binder.clearCallingIdentity(); + try { + mKeyguardDisableHandler.disableKeyguard(token, tag, callingUid, userId); + } finally { + Binder.restoreCallingIdentity(origIdentity); + } + } + + @Override + public void reenableKeyguard(IBinder token, int userId) { + userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false /* allowAll */, ALLOW_FULL_ONLY, "reenableKeyguard", null); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires DISABLE_KEYGUARD permission"); + } + Objects.requireNonNull(token, "token is null"); + final int callingUid = Binder.getCallingUid(); + final long origIdentity = Binder.clearCallingIdentity(); + try { + mKeyguardDisableHandler.reenableKeyguard(token, callingUid, userId); + } finally { + Binder.restoreCallingIdentity(origIdentity); + } + } + + @EnforcePermission(android.Manifest.permission.DISABLE_KEYGUARD) + /** + * @see android.app.KeyguardManager#exitKeyguardSecurely + */ + @Override + public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) { + exitKeyguardSecurely_enforcePermission(); + + if (callback == null) { + throw new IllegalArgumentException("callback == null"); + } + + mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() { + @Override + public void onKeyguardExitResult(boolean success) { + try { + callback.onKeyguardExitResult(success); + } catch (RemoteException e) { + // Client has died, we don't care. + } + } + }); + } + + @Override + public boolean isKeyguardLocked() { + return mPolicy.isKeyguardLocked(); + } + + public boolean isKeyguardShowingAndNotOccluded() { + return mPolicy.isKeyguardShowingAndNotOccluded(); + } + + @Override + public boolean isKeyguardSecure(int userId) { + if (userId != UserHandle.getCallingUserId() + && !checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS, + "isKeyguardSecure")) { + throw new SecurityException("Requires INTERACT_ACROSS_USERS permission"); + } + + final long origId = Binder.clearCallingIdentity(); + try { + return mPolicy.isKeyguardSecure(userId); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) { + if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) { + throw new SecurityException("Requires CONTROL_KEYGUARD permission"); + } + if (mAtmService.mKeyguardController.isShowingDream()) { + mAtmService.mTaskSupervisor.wakeUp("leaveDream"); + } + synchronized (mGlobalLock) { + mPolicy.dismissKeyguardLw(callback, message); + } + } + + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + boolean registered = mKeyguardLockedStateListeners.register(listener); + if (!registered) { + Slog.w(TAG, "Failed to register listener: " + listener); + } + } + + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + mKeyguardLockedStateListeners.unregister(listener); + } + + private void enforceSubscribeToKeyguardLockedStatePermission() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE, + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE + + " permission required to subscribe to keyguard locked state changes"); + } + + private void dispatchKeyguardLockedState() { + mH.post(() -> { + final boolean isKeyguardLocked = mPolicy.isKeyguardShowing(); + if (mDispatchedKeyguardLockedState == isKeyguardLocked) { + return; + } + final int n = mKeyguardLockedStateListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged( + isKeyguardLocked); + } catch (RemoteException e) { + // Handled by the RemoteCallbackList. + } + } + mKeyguardLockedStateListeners.finishBroadcast(); + mDispatchedKeyguardLockedState = isKeyguardLocked; + }); + } + + void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, + @WindowManager.LayoutParams.WindowType int windowType, boolean visible, + boolean removed) { + if (mImeTargetChangeListener != null) { + if (DEBUG_INPUT_METHOD) { + Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token) + + ", type=" + ViewDebug.intToString(WindowManager.LayoutParams.class, + "type", windowType) + "visible=" + visible + ", removed=" + removed); + } + mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token, + windowType, visible, removed)); + } + } + + void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible, + boolean removed) { + if (mImeTargetChangeListener != null) { + if (DEBUG_INPUT_METHOD) { + Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token) + + "visible=" + visible + ", removed=" + removed); + } + mH.post(() -> mImeTargetChangeListener.onImeInputTargetVisibilityChanged(token, + visible, removed)); + } + } + + @Override + public void setSwitchingUser(boolean switching) { + if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "setSwitchingUser()")) { + throw new SecurityException("Requires INTERACT_ACROSS_USERS_FULL permission"); + } + mPolicy.setSwitchingUser(switching); + synchronized (mGlobalLock) { + mSwitchingUser = switching; + } + } + + @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW) + @Override + public void showGlobalActions() { + if (!checkCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, + "showGlobalActions()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + mPolicy.showGlobalActions(); + } + + @Override + public void closeSystemDialogs(String reason) { + int callingPid = Binder.getCallingPid(); + int callingUid = Binder.getCallingUid(); + if (!mAtmService.checkCanCloseSystemDialogs(callingPid, callingUid, null)) { + return; + } + synchronized (mGlobalLock) { + mRoot.closeSystemDialogs(reason); + } + } + + + @Override + public void setAnimationScale(int which, float scale) { + if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, + "setAnimationScale()")) { + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); + } + + scale = fixScale(scale); + switch (which) { + case 0: mWindowAnimationScaleSetting = scale; break; + case 1: mTransitionAnimationScaleSetting = scale; break; + case 2: mAnimatorDurationScaleSetting = scale; break; + } + + // Persist setting + mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); + } + + @Override + public void setAnimationScales(float[] scales) { + if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, + "setAnimationScale()")) { + throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); + } + + if (scales != null) { + if (scales.length >= 1) { + mWindowAnimationScaleSetting = fixScale(scales[0]); + } + if (scales.length >= 2) { + mTransitionAnimationScaleSetting = fixScale(scales[1]); + } + if (scales.length >= 3) { + mAnimatorDurationScaleSetting = fixScale(scales[2]); + dispatchNewAnimatorScaleLocked(null); + } + } + + // Persist setting + mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); + } + + private void setAnimatorDurationScale(float scale) { + mAnimatorDurationScaleSetting = scale; + ValueAnimator.setDurationScale(scale); + } + + public float getWindowAnimationScaleLocked() { + return mAnimationsDisabled ? 0 : mWindowAnimationScaleSetting; + } + + public float getTransitionAnimationScaleLocked() { + return mAnimationsDisabled ? 0 : mTransitionAnimationScaleSetting; + } + + @Override + public float getAnimationScale(int which) { + switch (which) { + case 0: return mWindowAnimationScaleSetting; + case 1: return mTransitionAnimationScaleSetting; + case 2: return mAnimatorDurationScaleSetting; + } + return 0; + } + + @Override + public float[] getAnimationScales() { + return new float[] { mWindowAnimationScaleSetting, mTransitionAnimationScaleSetting, + mAnimatorDurationScaleSetting }; + } + + @Override + public float getCurrentAnimatorScale() { + synchronized (mGlobalLock) { + return mAnimationsDisabled ? 0 : mAnimatorDurationScaleSetting; + } + } + + void dispatchNewAnimatorScaleLocked(Session session) { + mH.obtainMessage(H.NEW_ANIMATOR_SCALE, session).sendToTarget(); + } + + @Override + public void registerPointerEventListener(PointerEventListener listener, int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.registerPointerEventListener(listener); + } + } + } + + @Override + public void unregisterPointerEventListener(PointerEventListener listener, int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.unregisterPointerEventListener(listener); + } + } + } + + // Called by window manager policy. Not exposed externally. + @Override + public int getLidState() { + int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, + InputManagerService.SW_LID); + if (sw > 0) { + // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL. + return LID_CLOSED; + } else if (sw == 0) { + // Switch state: AKEY_STATE_UP. + return LID_OPEN; + } else { + // Switch state: AKEY_STATE_UNKNOWN. + return LID_ABSENT; + } + } + + // Called by window manager policy. Not exposed externally. + @Override + public void lockDeviceNow() { + lockNow(null); + } + + // Called by window manager policy. Not exposed externally. + @Override + public int getCameraLensCoverState() { + int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, + InputManagerService.SW_CAMERA_LENS_COVER); + if (sw > 0) { + // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL. + return CAMERA_LENS_COVERED; + } else if (sw == 0) { + // Switch state: AKEY_STATE_UP. + return CAMERA_LENS_UNCOVERED; + } else { + // Switch state: AKEY_STATE_UNKNOWN. + return CAMERA_LENS_COVER_ABSENT; + } + } + + // Called by window manager policy. Not exposed externally. + @Override + public void switchKeyboardLayout(int deviceId, int direction) { + mInputManager.switchKeyboardLayout(deviceId, direction); + } + + // Called by window manager policy. Not exposed externally. + @Override + public void shutdown(boolean confirm) { + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + } + + // Called by window manager policy. Not exposed externally. + @Override + public void reboot(boolean confirm) { + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(), + PowerManager.SHUTDOWN_USER_REQUESTED, confirm); + } + + // Called by window manager policy. Not exposed externally. + @Override + public void rebootSafeMode(boolean confirm) { + // Pass in the UI context, since ShutdownThread requires it (to show UI). + ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(), + confirm); + } + + public void setCurrentUser(@UserIdInt int newUserId) { + synchronized (mGlobalLock) { + mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_OPEN, null); + mCurrentUserId = newUserId; + mPolicy.setCurrentUserLw(newUserId); + mKeyguardDisableHandler.setCurrentUser(newUserId); + + // Hide windows that should not be seen by the new user. + mRoot.switchUser(newUserId); + mWindowPlacerLocked.performSurfacePlacement(); + + // Notify whether the root docked task exists for the current user + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + + // If the display is already prepared, update the density. + // Otherwise, we'll update it when it's prepared. + if (mDisplayReady) { + final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId); + final int targetDensity = forcedDensity != 0 + ? forcedDensity : displayContent.getInitialDisplayDensity(); + displayContent.setForcedDensity(targetDensity, UserHandle.USER_CURRENT); + } + } + } + + /* Called by WindowState */ + boolean isUserVisible(@UserIdInt int userId) { + return mUmInternal.isUserVisible(userId); + } + + @UserIdInt int getUserAssignedToDisplay(int displayId) { + return mUmInternal.getUserAssignedToDisplay(displayId); + } + + boolean shouldPlacePrimaryHomeOnDisplay(int displayId) { + int userId = mUmInternal.getUserAssignedToDisplay(displayId); + return shouldPlacePrimaryHomeOnDisplay(displayId, userId); + } + + boolean shouldPlacePrimaryHomeOnDisplay(int displayId, int userId) { + return mUmInternal.getMainDisplayAssignedToUser(userId) == displayId; + } + + public void enableScreenAfterBoot() { + synchronized (mGlobalLock) { + ProtoLog.i(WM_DEBUG_BOOT, "enableScreenAfterBoot: mDisplayEnabled=%b " + + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. " + + "%s", + mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, + new RuntimeException("here").fillInStackTrace()); + if (mSystemBooted) { + return; + } + mSystemBooted = true; + hideBootMessagesLocked(); + // If the screen still doesn't come up after 30 seconds, give + // up and turn it on. + mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000); + } + + mPolicy.systemBooted(); + + performEnableScreen(); + } + + @Override + public void enableScreenIfNeeded() { + synchronized (mGlobalLock) { + enableScreenIfNeededLocked(); + } + } + + void enableScreenIfNeededLocked() { + ProtoLog.i(WM_DEBUG_BOOT, "enableScreenIfNeededLocked: mDisplayEnabled=%b " + + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. " + + "%s", + mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, + new RuntimeException("here").fillInStackTrace()); + if (mDisplayEnabled) { + return; + } + if (!mSystemBooted && !mShowingBootMessages) { + return; + } + mH.sendEmptyMessage(H.ENABLE_SCREEN); + } + + public void performBootTimeout() { + synchronized (mGlobalLock) { + if (mDisplayEnabled) { + return; + } + ProtoLog.w(WM_ERROR, "***** BOOT TIMEOUT: forcing display enabled"); + mForceDisplayEnabled = true; + } + performEnableScreen(); + } + + /** + * Called when System UI has been started. + */ + public void onSystemUiStarted() { + mPolicy.onSystemUiStarted(); + } + + private void performEnableScreen() { + synchronized (mGlobalLock) { + ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b" + + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b" + + " mSystemBooted=%b. %s", mDisplayEnabled, + mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, + new RuntimeException("here").fillInStackTrace()); + if (mDisplayEnabled) { + return; + } + if (!mSystemBooted && !mShowingBootMessages) { + return; + } + + if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) { + return; + } + + // Don't enable the screen until all existing windows have been drawn. + if (!mForceDisplayEnabled) { + if (mBootWaitForWindowsStartTime < 0) { + // First time we will start waiting for all windows to be drawn. + mBootWaitForWindowsStartTime = SystemClock.elapsedRealtime(); + } + for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { + if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) { + return; + } + } + long waitTime = SystemClock.elapsedRealtime() - mBootWaitForWindowsStartTime; + mBootWaitForWindowsStartTime = -1; + if (waitTime > 10) { + ProtoLog.i(WM_DEBUG_BOOT, + "performEnableScreen: Waited %dms for all windows to be drawn", + waitTime); + } + } + + if (!mBootAnimationStopped) { + Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0); + // stop boot animation + // formerly we would just kill the process, but we now ask it to exit so it + // can choose where to stop the animation. + SystemProperties.set("service.bootanim.exit", "1"); + mBootAnimationStopped = true; + } + + if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) { + ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete"); + return; + } + + if (!SurfaceControl.bootFinished()) { + ProtoLog.w(WM_ERROR, "performEnableScreen: bootFinished() failed."); + return; + } + + EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis()); + Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0); + mDisplayEnabled = true; + ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!"); + + // Enable input dispatch. + mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled); + } + + try { + mActivityManager.bootAnimationComplete(); + } catch (RemoteException e) { + } + + mPolicy.enableScreenAfterBoot(); + + // Make sure the last requested orientation has been applied. + updateRotationUnchecked(false, false); + + synchronized (mGlobalLock) { + mAtmService.getTransitionController().mIsWaitingForDisplayEnabled = false; + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Notified TransitionController " + + "that the display is ready."); + } + } + + private boolean checkBootAnimationCompleteLocked() { + if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) { + mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED); + mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED, + BOOT_ANIMATION_POLL_INTERVAL); + ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Waiting for anim complete"); + return false; + } + ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Animation complete!"); + return true; + } + + public void showBootMessage(final CharSequence msg, final boolean always) { + boolean first = false; + synchronized (mGlobalLock) { + ProtoLog.i(WM_DEBUG_BOOT, "showBootMessage: msg=%s always=%b" + + " mAllowBootMessages=%b mShowingBootMessages=%b" + + " mSystemBooted=%b. %s", msg, always, mAllowBootMessages, + mShowingBootMessages, mSystemBooted, + new RuntimeException("here").fillInStackTrace()); + if (!mAllowBootMessages) { + return; + } + if (!mShowingBootMessages) { + if (!always) { + return; + } + first = true; + } + if (mSystemBooted) { + return; + } + mShowingBootMessages = true; + mPolicy.showBootMessage(msg, always); + } + if (first) { + performEnableScreen(); + } + } + + public void hideBootMessagesLocked() { + ProtoLog.i(WM_DEBUG_BOOT, "hideBootMessagesLocked: mDisplayEnabled=%b" + + " mForceDisplayEnabled=%b mShowingBootMessages=%b" + + " mSystemBooted=%b. %s", mDisplayEnabled, mForceDisplayEnabled, + mShowingBootMessages, mSystemBooted, + new RuntimeException("here").fillInStackTrace()); + if (mShowingBootMessages) { + mShowingBootMessages = false; + mPolicy.hideBootMessages(); + } + } + + /** + * Sets the touch mode state. + * + * If {@code com.android.internal.R.bool.config_perDisplayFocusEnabled} is set to true, then + * only the display represented by the {@code displayId} parameter will be requested to switch + * the touch mode state. Otherwise all displays that do not maintain their own focus and touch + * mode will be requested to switch their touch mode state (disregarding {@code displayId} + * parameter). + * + * To be able to change touch mode state, the caller must either own the focused window, or must + * have the {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE} permission. Instrumented + * process, sourced with {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE}, may switch + * touch mode at any time. + * + * @param inTouch the touch mode to set + * @param displayId the target display id + */ + @Override // Binder call + public void setInTouchMode(boolean inTouch, int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (mPerDisplayFocusEnabled && (displayContent == null + || displayContent.isInTouchMode() == inTouch)) { + return; + } + final boolean displayHasOwnTouchMode = + displayContent != null && displayContent.hasOwnFocus(); + if (displayHasOwnTouchMode && displayContent.isInTouchMode() == inTouch) { + return; + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final boolean hasPermission = hasTouchModePermission(pid); + final long token = Binder.clearCallingIdentity(); + try { + // If mPerDisplayFocusEnabled is set or the display maintains its own touch mode, + // then just update the display pointed by displayId + if (mPerDisplayFocusEnabled || displayHasOwnTouchMode) { + if (mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission, displayId)) { + displayContent.setInTouchMode(inTouch); + } + } else { // Otherwise update all displays that do not maintain their own touch mode + final int displayCount = mRoot.mChildren.size(); + for (int i = 0; i < displayCount; ++i) { + DisplayContent dc = mRoot.mChildren.get(i); + if (dc.isInTouchMode() == inTouch || dc.hasOwnFocus()) { + continue; + } + if (mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission, + dc.mDisplayId)) { + dc.setInTouchMode(inTouch); + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** + * Sets the touch mode state forcibly on all displays (disregarding both the value of + * {@code com.android.internal.R.bool.config_perDisplayFocusEnabled} and whether the display + * maintains its own focus and touch mode). + * + * @param inTouch the touch mode to set + */ + @Override // Binder call + public void setInTouchModeOnAllDisplays(boolean inTouch) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final boolean hasPermission = hasTouchModePermission(pid); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + for (int i = 0; i < mRoot.mChildren.size(); ++i) { + DisplayContent dc = mRoot.mChildren.get(i); + if (dc.isInTouchMode() != inTouch + && mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission, + dc.mDisplayId)) { + dc.setInTouchMode(inTouch); + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private boolean hasTouchModePermission(int pid) { + return mAtmService.instrumentationSourceHasPermission(pid, MODIFY_TOUCH_MODE_STATE) + || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()", + /* printlog= */ false); + } + + /** + * Returns the touch mode state for the display id passed as argument. + * + * This method will return the default touch mode state (represented by + * {@code com.android.internal.R.bool.config_defaultInTouchMode}) if the display passed as + * argument is no longer registered in {@RootWindowContainer}). + */ + @Override // Binder call + public boolean isInTouchMode(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + return mContext.getResources().getBoolean(R.bool.config_defaultInTouchMode); + } + return displayContent.isInTouchMode(); + } + } + + public void showEmulatorDisplayOverlayIfNeeded() { + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay) + && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false) + && Build.IS_EMULATOR) { + mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY)); + } + } + + public void showEmulatorDisplayOverlay() { + synchronized (mGlobalLock) { + + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> showEmulatorDisplayOverlay"); + if (mEmulatorDisplayOverlay == null) { + mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mContext, + getDefaultDisplayContentLocked(), + mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER) + * TYPE_LAYER_MULTIPLIER + 10, mTransaction); + } + mEmulatorDisplayOverlay.setVisibility(true, mTransaction); + mTransaction.apply(); + } + } + + // TODO: more accounting of which pid(s) turned it on, keep count, + // only allow disables from pids which have count on, etc. + @Override + public void showStrictModeViolation(boolean on) { + final int pid = Binder.getCallingPid(); + if (on) { + // Show the visualization, and enqueue a second message to tear it + // down if we don't hear back from the app. + mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 1, pid)); + mH.sendMessageDelayed(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid), + DateUtils.SECOND_IN_MILLIS); + } else { + mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid)); + } + } + + private void showStrictModeViolation(int arg, int pid) { + final boolean on = arg != 0; + synchronized (mGlobalLock) { + // Ignoring requests to enable the red border from clients which aren't on screen. + // (e.g. Broadcast Receivers in the background..) + if (on && !mRoot.canShowStrictModeViolation(pid)) { + return; + } + + if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation"); + // TODO: Modify this to use the surface trace once it is not going baffling. + // b/31532461 + // TODO(multi-display): support multiple displays + if (mStrictModeFlash == null) { + mStrictModeFlash = new StrictModeFlash(getDefaultDisplayContentLocked(), + mTransaction); + } + mStrictModeFlash.setVisibility(on, mTransaction); + mTransaction.apply(); + } + } + + @Override + public void setStrictModeVisualIndicatorPreference(String value) { + SystemProperties.set(StrictMode.VISUAL_PROPERTY, value); + } + + @Override + public Bitmap screenshotWallpaper() { + if (!checkCallingPermission(READ_FRAME_BUFFER, "screenshotWallpaper()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper"); + synchronized (mGlobalLock) { + // TODO(b/115486823) Screenshot at secondary displays if needed. + final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY); + return dc.mWallpaperController.screenshotWallpaperLocked(); + } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + + @Override + public SurfaceControl mirrorWallpaperSurface(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + return dc.mWallpaperController.mirrorWallpaperSurface(); + } + } + + @Nullable + private ScreenCapture.ScreenshotHardwareBuffer takeAssistScreenshot() { + if (!checkCallingPermission(READ_FRAME_BUFFER, "requestAssistScreenshot()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + + ScreenCapture.LayerCaptureArgs captureArgs; + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY); + if (displayContent == null) { + if (DEBUG_SCREENSHOT) { + Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId=" + + DEFAULT_DISPLAY); + } + captureArgs = null; + } else { + captureArgs = displayContent.getLayerCaptureArgs(); + } + } + + final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer; + if (captureArgs != null) { + ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture = + ScreenCapture.createSyncCaptureListener(); + + ScreenCapture.captureLayers(captureArgs, syncScreenCapture); + + screenshotBuffer = syncScreenCapture.getBuffer(); + } else { + screenshotBuffer = null; + } + + if (screenshotBuffer == null) { + Slog.w(TAG_WM, "Failed to take screenshot"); + } + + return screenshotBuffer; + } + + /** + * Takes a snapshot of the screen. In landscape mode this grabs the whole screen. + * In portrait mode, it grabs the upper region of the screen based on the vertical dimension + * of the target image. + */ + @Override + public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) { + final ScreenCapture.ScreenshotHardwareBuffer shb = takeAssistScreenshot(); + final Bitmap bm = shb != null ? shb.asBitmap() : null; + FgThread.getHandler().post(() -> { + try { + receiver.onHandleAssistScreenshot(bm); + } catch (RemoteException e) { + } + }); + + return true; + } + + /** + * Retrieves a snapshot. If restoreFromDisk equals equals {@code true}, DO NOT HOLD THE WINDOW + * MANAGER LOCK WHEN CALLING THIS METHOD! + */ + public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean isLowResolution, + boolean restoreFromDisk) { + return mTaskSnapshotController.getSnapshot(taskId, userId, restoreFromDisk, + isLowResolution); + } + + /** + * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. + * + * @param taskId The task ID of the task for which a Bitmap is requested. + * @param layerCaptureArgsBuilder A {@link ScreenCapture.LayerCaptureArgs.Builder} with + * arguments for how to capture the Bitmap. The caller can + * specify any arguments, but this method will ensure that the + * specified task's SurfaceControl is used and the crop is set to + * the bounds of that task. + * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could + * not be generated. + */ + @Nullable + public Bitmap captureTaskBitmap(int taskId, + @NonNull ScreenCapture.LayerCaptureArgs.Builder layerCaptureArgsBuilder) { + if (mTaskSnapshotController.shouldDisableSnapshots()) { + return null; + } + + synchronized (mGlobalLock) { + final Task task = mRoot.anyTaskForId(taskId); + if (task == null) { + return null; + } + + // The bounds returned by the task represent the task's position on the screen. However, + // we need to specify a crop relative to the task's surface control. Therefore, shift + // the task's bounds to 0,0 so that we have the correct size and position within the + // task's surface control. + task.getBounds(mTmpRect); + mTmpRect.offsetTo(0, 0); + + final SurfaceControl sc = task.getSurfaceControl(); + final ScreenCapture.ScreenshotHardwareBuffer buffer = ScreenCapture.captureLayers( + layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build()); + if (buffer == null) { + Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId); + return null; + } + + return buffer.asBitmap(); + } + } + + /** + * In case a task write/delete operation was lost because the system crashed, this makes sure to + * clean up the directory to remove obsolete files. + * + * @param persistentTaskIds A set of task ids that exist in our in-memory model. + * @param runningUserIds The ids of the list of users that have tasks loaded in our in-memory + * model. + */ + public void removeObsoleteTaskFiles(ArraySet persistentTaskIds, int[] runningUserIds) { + synchronized (mGlobalLock) { + mTaskSnapshotController.removeObsoleteTaskFiles(persistentTaskIds, runningUserIds); + } + } + + @Override + public void setFixedToUserRotation(int displayId, int fixedToUserRotation) { + if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, + "setFixedToUserRotation()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to set fixed to user rotation for a missing display."); + return; + } + display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + int getFixedToUserRotation(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to get fixed to user rotation for a missing display."); + return -1; + } + return display.getDisplayRotation().getFixedToUserRotationMode(); + } + } + + @Override + public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) { + if (!checkCallingPermission( + android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to setIgnoreOrientationRequest() for a missing display."); + return; + } + display.setIgnoreOrientationRequest(ignoreOrientationRequest); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + boolean getIgnoreOrientationRequest(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to getIgnoreOrientationRequest() for a missing display."); + return false; + } + return display.getIgnoreOrientationRequest(); + } + } + + /** + * Controls whether ignore orientation request logic in {@link DisplayArea} is disabled + * at runtime and how to optionally map some requested orientations to others. + * + *

Note: this assumes that {@link #mGlobalLock} is held by the caller. + * + * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the + * value of {@link DisplayArea#getIgnoreOrientationRequest} and app requested + * orientation is respected. + * @param fromOrientations The orientations we want to map to the correspondent orientations + * in toOrientation. + * @param toOrientations The orientations we map to the ones in fromOrientations at the same + * index + */ + void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + @Nullable int[] fromOrientations, @Nullable int[] toOrientations) { + mOrientationMapping.clear(); + if (fromOrientations != null && toOrientations != null + && fromOrientations.length == toOrientations.length) { + for (int i = 0; i < fromOrientations.length; i++) { + mOrientationMapping.put(fromOrientations[i], toOrientations[i]); + } + } + if (isIgnoreOrientationRequestDisabled == mIsIgnoreOrientationRequestDisabled) { + return; + } + mIsIgnoreOrientationRequestDisabled = isIgnoreOrientationRequestDisabled; + for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { + mRoot.getChildAt(i).onIsIgnoreOrientationRequestDisabledChanged(); + } + } + + /** + * When {@link mIsIgnoreOrientationRequestDisabled} is {@value true} this method returns the + * orientation to use in place of the one in input. It returns the same requestedOrientation in + * input otherwise. + * + * @param requestedOrientation The orientation that can be mapped. + * @return The orientation to use in place of requestedOrientation. + */ + int mapOrientationRequest(int requestedOrientation) { + if (!mIsIgnoreOrientationRequestDisabled) { + return requestedOrientation; + } + return mOrientationMapping.get(requestedOrientation, requestedOrientation); + } + + /** + * Whether the system ignores the value of {@link DisplayArea#getIgnoreOrientationRequest} and + * app requested orientation is respected. + * + *

Note: this assumes that {@link #mGlobalLock} is held by the caller. + */ + boolean isIgnoreOrientationRequestDisabled() { + return mIsIgnoreOrientationRequestDisabled + || !mLetterboxConfiguration.isIgnoreOrientationRequestAllowed(); + } + + @Override + public void freezeRotation(int rotation, String caller) { + freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation, caller); + } + + /** + * Freeze rotation changes. (Enable "rotation lock".) + * Persists across reboots. + * @param displayId The ID of the display to freeze. + * @param rotation The desired rotation to freeze to, or -1 to use the current rotation. + */ + @Override + public void freezeDisplayRotation(int displayId, int rotation, String caller) { + // TODO(multi-display): Track which display is rotated. + if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, + "freezeRotation()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } + if (rotation < -1 || rotation > Surface.ROTATION_270) { + throw new IllegalArgumentException("Rotation argument must be -1 or a valid " + + "rotation constant."); + } + ProtoLog.v(WM_DEBUG_ORIENTATION, + "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s", + getDefaultDisplayRotation(), rotation, caller); + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to freeze rotation for a missing display."); + return; + } + display.getDisplayRotation().freezeRotation(rotation, caller); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + updateRotationUnchecked(false, false); + } + + @Override + public void thawRotation(String caller) { + thawDisplayRotation(Display.DEFAULT_DISPLAY, caller); + } + + /** + * Thaw rotation changes. (Disable "rotation lock".) + * Persists across reboots. + */ + @Override + public void thawDisplayRotation(int displayId, String caller) { + if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, + "thawRotation()")) { + throw new SecurityException("Requires SET_ORIENTATION permission"); + } + + ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d, caller=%s", + getDefaultDisplayRotation(), caller); + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to thaw rotation for a missing display."); + return; + } + display.getDisplayRotation().thawRotation(caller); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + updateRotationUnchecked(false, false); + } + + @Override + public boolean isRotationFrozen() { + return isDisplayRotationFrozen(Display.DEFAULT_DISPLAY); + } + + @Override + public boolean isDisplayRotationFrozen(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to check if rotation is frozen on a missing display."); + return false; + } + return display.getDisplayRotation().isRotationFrozen(); + } + } + + int getDisplayUserRotation(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to get user rotation of a missing display."); + return -1; + } + return display.getDisplayRotation().getUserRotation(); + } + } + + /** + * Recalculate the current rotation. + * + * Called by the window manager policy whenever the state of the system changes + * such that the current rotation might need to be updated, such as when the + * device is docked or rotated into a new posture. + */ + @Override + public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { + updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); + } + + private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { + ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:" + + " alwaysSendConfiguration=%b forceRelayout=%b", + alwaysSendConfiguration, forceRelayout); + + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation"); + + final long origId = Binder.clearCallingIdentity(); + + try { + synchronized (mGlobalLock) { + boolean layoutNeeded = false; + final int displayCount = mRoot.mChildren.size(); + for (int i = 0; i < displayCount; ++i) { + final DisplayContent displayContent = mRoot.mChildren.get(i); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display"); + final boolean rotationChanged = displayContent.updateRotationUnchecked(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + + if (rotationChanged) { + mAtmService.getTaskChangeNotificationController() + .notifyOnActivityRotation(displayContent.mDisplayId); + } + + final boolean pendingRemoteDisplayChange = rotationChanged + && (displayContent.mRemoteDisplayChangeController + .isWaitingForRemoteDisplayChange() + || displayContent.mTransitionController.isCollecting()); + // Even if alwaysSend, we are waiting for a transition or remote to provide + // updated configuration, so we can't update configuration yet. + if (!pendingRemoteDisplayChange) { + // The layout-needed flag will be set if there is a rotation change, so + // only set it if the caller requests to force relayout. + if (forceRelayout) { + displayContent.setLayoutNeeded(); + layoutNeeded = true; + } + if (rotationChanged || alwaysSendConfiguration) { + displayContent.sendNewConfiguration(); + } + } + } + + if (layoutNeeded) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "updateRotation: performSurfacePlacement"); + mWindowPlacerLocked.performSurfacePlacement(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + + @Override + public int getDefaultDisplayRotation() { + synchronized (mGlobalLock) { + return getDefaultDisplayContentLocked().getRotation(); + } + } + + @Override + public void setDisplayChangeWindowController(IDisplayChangeWindowController controller) { + mAtmService.enforceTaskPermission("setDisplayWindowRotationController"); + try { + synchronized (mGlobalLock) { + if (mDisplayChangeController != null) { + mDisplayChangeController.asBinder().unlinkToDeath( + mDisplayChangeControllerDeath, 0); + mDisplayChangeController = null; + } + controller.asBinder().linkToDeath(mDisplayChangeControllerDeath, 0); + mDisplayChangeController = controller; + } + } catch (RemoteException e) { + throw new RuntimeException("Unable to set rotation controller", e); + } + } + + @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS) + @Override + public SurfaceControl addShellRoot(int displayId, IWindow client, + @WindowManager.ShellRootLayer int shellRootLayer) { + addShellRoot_enforcePermission(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return null; + } + return dc.addShellRoot(client, shellRootLayer); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS) + @Override + public void setShellRootAccessibilityWindow(int displayId, + @WindowManager.ShellRootLayer int shellRootLayer, IWindow target) { + setShellRootAccessibilityWindow_enforcePermission(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return; + } + ShellRoot root = dc.mShellRoots.get(shellRootLayer); + if (root == null) { + return; + } + root.setAccessibilityWindow(target); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS) + @Override + public void setDisplayWindowInsetsController( + int displayId, IDisplayWindowInsetsController insetsController) { + setDisplayWindowInsetsController_enforcePermission(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return; + } + dc.setRemoteInsetsController(insetsController); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS) + @Override + public void updateDisplayWindowRequestedVisibleTypes( + int displayId, @InsetsType int requestedVisibleTypes) { + updateDisplayWindowRequestedVisibleTypes_enforcePermission(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null || dc.mRemoteInsetsControlTarget == null) { + return; + } + dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes); + dc.getInsetsStateController().onRequestedVisibleTypesChanged( + dc.mRemoteInsetsControlTarget); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public int watchRotation(IRotationWatcher watcher, int displayId) { + final DisplayContent displayContent; + synchronized (mGlobalLock) { + displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException("Trying to register rotation event " + + "for invalid display: " + displayId); + } + mRotationWatcherController.registerDisplayRotationWatcher(watcher, displayId); + return displayContent.getRotation(); + } + } + + @Override + public void removeRotationWatcher(IRotationWatcher watcher) { + synchronized (mGlobalLock) { + mRotationWatcherController.removeRotationWatcher(watcher); + } + } + + @Surface.Rotation + @Override + public int registerProposedRotationListener(IBinder contextToken, IRotationWatcher listener) { + synchronized (mGlobalLock) { + final WindowContainer wc = + mRotationWatcherController.getAssociatedWindowContainer(contextToken); + if (wc == null) { + Slog.w(TAG, "Register rotation listener from non-existing token, uid=" + + Binder.getCallingUid()); + return Surface.ROTATION_0; + } + mRotationWatcherController.registerProposedRotationListener(listener, contextToken); + final WindowOrientationListener orientationListener = + wc.mDisplayContent.getDisplayRotation().getOrientationListener(); + if (orientationListener != null) { + // It may be -1 if sensor is disabled. + final int rotation = orientationListener.getProposedRotation(); + if (rotation >= Surface.ROTATION_0) { + return rotation; + } + } + return wc.getWindowConfiguration().getRotation(); + } + } + + @Override + public boolean registerWallpaperVisibilityListener(IWallpaperVisibilityListener listener, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException("Trying to register visibility event " + + "for invalid display: " + displayId); + } + mWallpaperVisibilityListeners.registerWallpaperVisibilityListener(listener, displayId); + return displayContent.mWallpaperController.isWallpaperVisible(); + } + } + + @Override + public void unregisterWallpaperVisibilityListener(IWallpaperVisibilityListener listener, + int displayId) { + synchronized (mGlobalLock) { + mWallpaperVisibilityListeners + .unregisterWallpaperVisibilityListener(listener, displayId); + } + } + + @Override + public void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException( + "Trying to register system gesture exclusion event for invalid display: " + + displayId); + } + displayContent.registerSystemGestureExclusionListener(listener); + } + } + + @Override + public void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException( + "Trying to unregister system gesture exclusion event for invalid display: " + + displayId); + } + displayContent.unregisterSystemGestureExclusionListener(listener); + } + } + + @Override + public void registerDecorViewGestureListener( + IDecorViewGestureListener listener, int displayId) { + if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT, + "registerDecorViewGestureListener()")) { + throw new SecurityException("Requires MONITOR_INPUT permission"); + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException( + "Trying to register DecorView gesture event listener" + + "for invalid display: " + + displayId); + } + displayContent.registerDecorViewGestureListener(listener); + } + } + + @Override + public void unregisterDecorViewGestureListener( + IDecorViewGestureListener listener, int displayId) { + if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT, + "unregisterSystemGestureExclusionListener()")) { + throw new SecurityException("Requires MONITOR_INPUT permission"); + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException( + "Trying to unregister DecorView gesture event listener" + + "for invalid display: " + + displayId); + } + displayContent.unregisterDecorViewGestureListener(listener); + } + } + + void reportDecorViewGestureChanged(Session session, IWindow window, boolean intercepted) { + synchronized (mGlobalLock) { + final WindowState win = + windowForClientLocked(session, window, false /* throwOnError */); + if (win == null) { + return; + } + win.getDisplayContent() + .updateDecorViewGestureIntercepted(win.mToken.token, intercepted); + } + } + + void reportSystemGestureExclusionChanged(Session session, IWindow window, + List exclusionRects) { + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, window, + false /* throwOnError */); + if (win == null) { + Slog.i(TAG_WM, + "reportSystemGestureExclusionChanged(): No window state for package:" + + session.mPackageName); + return; + } + if (win.setSystemGestureExclusion(exclusionRects)) { + win.getDisplayContent().updateSystemGestureExclusion(); + } + } + } + + void reportKeepClearAreasChanged(Session session, IWindow window, + List restricted, List unrestricted) { + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, window, + false /* throwOnError */); + if (win == null) { + Slog.i(TAG_WM, + "reportKeepClearAreasChanged(): No window state for package:" + + session.mPackageName); + return; + } + if (win.setKeepClearAreas(restricted, unrestricted)) { + win.getDisplayContent().updateKeepClearAreas(); + } + } + } + + @Override + public void registerDisplayFoldListener(IDisplayFoldListener listener) { + mPolicy.registerDisplayFoldListener(listener); + } + + @Override + public void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + mPolicy.unregisterDisplayFoldListener(listener); + } + + /** + * Overrides the folded area. + * + * @param area the overriding folded area or an empty {@code Rect} to clear the override. + */ + void setOverrideFoldedArea(@NonNull Rect area) { + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); + } + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mPolicy.setOverrideFoldedArea(area); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Get the display folded area. + */ + @NonNull Rect getFoldedArea() { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + return mPolicy.getFoldedArea(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Registers a hierarchy listener that gets callbacks when the hierarchy changes. The listener's + * onDisplayAdded() will not be called for the displays returned. + * + * @return the displayIds for the existing displays + */ + @Override + public int[] registerDisplayWindowListener(IDisplayWindowListener listener) { + mAtmService.enforceTaskPermission("registerDisplayWindowListener"); + final long ident = Binder.clearCallingIdentity(); + try { + return mDisplayNotificationController.registerListener(listener); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** Unregister a hierarchy listener so that it stops receiving callbacks. */ + @Override + public void unregisterDisplayWindowListener(IDisplayWindowListener listener) { + mAtmService.enforceTaskPermission("unregisterDisplayWindowListener"); + mDisplayNotificationController.unregisterListener(listener); + } + + @Override + public int getPreferredOptionsPanelGravity(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + return Gravity.CENTER | Gravity.BOTTOM; + } + return displayContent.getPreferredOptionsPanelGravity(); + } + } + + /** + * Starts the view server on the specified port. + * + * @param port The port to listener to. + * + * @return True if the server was successfully started, false otherwise. + * + * @see com.android.server.wm.ViewServer + * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT + */ + @Override + public boolean startViewServer(int port) { + if (isSystemSecure()) { + return false; + } + + if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) { + return false; + } + + if (port < 1024) { + return false; + } + + if (mViewServer != null) { + if (!mViewServer.isRunning()) { + try { + return mViewServer.start(); + } catch (IOException e) { + ProtoLog.w(WM_ERROR, "View server did not start"); + } + } + return false; + } + + try { + mViewServer = new ViewServer(this, port); + return mViewServer.start(); + } catch (IOException e) { + ProtoLog.w(WM_ERROR, "View server did not start"); + } + return false; + } + + private boolean isSystemSecure() { + return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) && + "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + } + + /** + * Stops the view server if it exists. + * + * @return True if the server stopped, false if it wasn't started or + * couldn't be stopped. + * + * @see com.android.server.wm.ViewServer + */ + @Override + public boolean stopViewServer() { + if (isSystemSecure()) { + return false; + } + + if (!checkCallingPermission(Manifest.permission.DUMP, "stopViewServer")) { + return false; + } + + if (mViewServer != null) { + return mViewServer.stop(); + } + return false; + } + + /** + * Indicates whether the view server is running. + * + * @return True if the server is running, false otherwise. + * + * @see com.android.server.wm.ViewServer + */ + @Override + public boolean isViewServerRunning() { + if (isSystemSecure()) { + return false; + } + + if (!checkCallingPermission(Manifest.permission.DUMP, "isViewServerRunning")) { + return false; + } + + return mViewServer != null && mViewServer.isRunning(); + } + + /** + * Lists all available windows in the system. The listing is written in the specified Socket's + * output stream with the following syntax: windowHashCodeInHexadecimal windowName + * Each line of the output represents a different window. + * + * @param client The remote client to send the listing to. + * @return false if an error occurred, true otherwise. + */ + boolean viewServerListWindows(Socket client) { + if (isSystemSecure()) { + return false; + } + + boolean result = true; + + final ArrayList windows = new ArrayList(); + synchronized (mGlobalLock) { + mRoot.forAllWindows(w -> { + windows.add(w); + }, false /* traverseTopToBottom */); + } + + BufferedWriter out = null; + + // Any uncaught exception will crash the system process + try { + OutputStream clientStream = client.getOutputStream(); + out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); + + final int count = windows.size(); + for (int i = 0; i < count; i++) { + final WindowState w = windows.get(i); + out.write(Integer.toHexString(System.identityHashCode(w))); + out.write(' '); + out.append(w.mAttrs.getTitle()); + out.write('\n'); + } + + out.write("DONE.\n"); + out.flush(); + } catch (Exception e) { + result = false; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + result = false; + } + } + } + + return result; + } + + // TODO(multidisplay): Extend to multiple displays. + /** + * Returns the focused window in the following format: + * windowHashCodeInHexadecimal windowName + * + * @param client The remote client to send the listing to. + * @return False if an error occurred, true otherwise. + */ + boolean viewServerGetFocusedWindow(Socket client) { + if (isSystemSecure()) { + return false; + } + + boolean result = true; + + WindowState focusedWindow = getFocusedWindow(); + + BufferedWriter out = null; + + // Any uncaught exception will crash the system process + try { + OutputStream clientStream = client.getOutputStream(); + out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024); + + if(focusedWindow != null) { + out.write(Integer.toHexString(System.identityHashCode(focusedWindow))); + out.write(' '); + out.append(focusedWindow.mAttrs.getTitle()); + } + out.write('\n'); + out.flush(); + } catch (Exception e) { + result = false; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + result = false; + } + } + } + + return result; + } + + /** + * Sends a command to a target window. The result of the command, if any, will be + * written in the output stream of the specified socket. + * + * The parameters must follow this syntax: + * windowHashcode extra + * + * Where XX is the length in characeters of the windowTitle. + * + * The first parameter is the target window. The window with the specified hashcode + * will be the target. If no target can be found, nothing happens. The extra parameters + * will be delivered to the target window and as parameters to the command itself. + * + * @param client The remote client to sent the result, if any, to. + * @param command The command to execute. + * @param parameters The command parameters. + * + * @return True if the command was successfully delivered, false otherwise. This does + * not indicate whether the command itself was successful. + */ + boolean viewServerWindowCommand(Socket client, String command, String parameters) { + if (isSystemSecure()) { + return false; + } + + boolean success = true; + Parcel data = null; + Parcel reply = null; + + BufferedWriter out = null; + + // Any uncaught exception will crash the system process + try { + // Find the hashcode of the window + int index = parameters.indexOf(' '); + if (index == -1) { + index = parameters.length(); + } + final String code = parameters.substring(0, index); + int hashCode = (int) Long.parseLong(code, 16); + + // Extract the command's parameter after the window description + if (index < parameters.length()) { + parameters = parameters.substring(index + 1); + } else { + parameters = ""; + } + + final WindowState window = findWindow(hashCode); + if (window == null) { + return false; + } + + data = Parcel.obtain(); + data.writeInterfaceToken("android.view.IWindow"); + data.writeString(command); + data.writeString(parameters); + data.writeInt(1); + ParcelFileDescriptor.fromSocket(client).writeToParcel(data, 0); + + reply = Parcel.obtain(); + + final IBinder binder = window.mClient.asBinder(); + // TODO: GET THE TRANSACTION CODE IN A SAFER MANNER + binder.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); + + reply.readException(); + + if (!client.isOutputShutdown()) { + out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); + out.write("DONE\n"); + out.flush(); + } + + } catch (Exception e) { + ProtoLog.w(WM_ERROR, "Could not send command %s with parameters %s. %s", command, + parameters, e); + success = false; + } finally { + if (data != null) { + data.recycle(); + } + if (reply != null) { + reply.recycle(); + } + if (out != null) { + try { + out.close(); + } catch (IOException e) { + + } + } + } + + return success; + } + + public void addWindowChangeListener(WindowChangeListener listener) { + synchronized (mGlobalLock) { + mWindowChangeListeners.add(listener); + } + } + + public void removeWindowChangeListener(WindowChangeListener listener) { + synchronized (mGlobalLock) { + mWindowChangeListeners.remove(listener); + } + } + + private void notifyWindowsChanged() { + WindowChangeListener[] windowChangeListeners; + synchronized (mGlobalLock) { + if(mWindowChangeListeners.isEmpty()) { + return; + } + windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()]; + windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners); + } + int N = windowChangeListeners.length; + for(int i = 0; i < N; i++) { + windowChangeListeners[i].windowsChanged(); + } + } + + private void notifyFocusChanged() { + WindowChangeListener[] windowChangeListeners; + synchronized (mGlobalLock) { + if(mWindowChangeListeners.isEmpty()) { + return; + } + windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()]; + windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners); + } + int N = windowChangeListeners.length; + for(int i = 0; i < N; i++) { + windowChangeListeners[i].focusChanged(); + } + } + + private WindowState findWindow(int hashCode) { + if (hashCode == -1) { + // TODO(multidisplay): Extend to multiple displays. + return getFocusedWindow(); + } + + synchronized (mGlobalLock) { + return mRoot.getWindow((w) -> System.identityHashCode(w) == hashCode); + } + } + + public Configuration computeNewConfiguration(int displayId) { + synchronized (mGlobalLock) { + return computeNewConfigurationLocked(displayId); + } + } + + private Configuration computeNewConfigurationLocked(int displayId) { + if (!mDisplayReady) { + return null; + } + final Configuration config = new Configuration(); + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + displayContent.computeScreenConfiguration(config); + return config; + } + + void notifyHardKeyboardStatusChange() { + final boolean available; + final WindowManagerInternal.OnHardKeyboardStatusChangeListener listener; + synchronized (mGlobalLock) { + listener = mHardKeyboardStatusChangeListener; + available = mHardKeyboardAvailable; + } + if (listener != null) { + listener.onHardKeyboardStatusChange(available); + } + } + + // ------------------------------------------------------------- + // Input Events and Focus Management + // ------------------------------------------------------------- + + final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this); + private boolean mEventDispatchingEnabled; + + @Override + public void setEventDispatching(boolean enabled) { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "setEventDispatching()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + } + + synchronized (mGlobalLock) { + mEventDispatchingEnabled = enabled; + if (mDisplayEnabled) { + mInputManagerCallback.setEventDispatchingLw(enabled); + } + } + } + + private WindowState getFocusedWindow() { + synchronized (mGlobalLock) { + return getFocusedWindowLocked(); + } + } + + WindowState getFocusedWindowLocked() { + // Return the focused window in the focused display. + return mRoot.getTopFocusedDisplayContent().mCurrentFocus; + } + + Task getImeFocusRootTaskLocked() { + // Don't use mCurrentFocus.getStack() because it returns home stack for system windows. + // Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE + // and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved + // to make room for IME, but the window is not the focused window that's taking input. + // TODO (b/111080190): Consider the case of multiple IMEs on multi-display. + final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent(); + final ActivityRecord focusedApp = topFocusedDisplay.mFocusedApp; + return (focusedApp != null && focusedApp.getTask() != null) + ? focusedApp.getTask().getRootTask() : null; + } + + public boolean detectSafeMode() { + if (!mInputManagerCallback.waitForInputDevicesReady( + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) { + ProtoLog.w(WM_ERROR, "Devices still not ready after waiting %d" + + " milliseconds before attempting to detect safe mode.", + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS); + } + + if (Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.SAFE_BOOT_DISALLOWED, 0) != 0) { + return false; + } + + int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, + KeyEvent.KEYCODE_MENU); + int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S); + int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, + KeyEvent.KEYCODE_DPAD_CENTER); + int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL, + InputManagerService.BTN_MOUSE); + int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, + KeyEvent.KEYCODE_VOLUME_DOWN); + mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0 + || volumeDownState > 0; + try { + if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0 + || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) { + mSafeMode = true; + SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, ""); + } + } catch (IllegalArgumentException e) { + } + if (mSafeMode) { + ProtoLog.i(WM_ERROR, "SAFE MODE ENABLED (menu=%d s=%d dpad=%d" + + " trackball=%d)", menuState, sState, dpadState, trackballState); + // May already be set if (for instance) this process has crashed + if (SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) == 0) { + SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1"); + } + } else { + ProtoLog.i(WM_ERROR, "SAFE MODE not enabled"); + } + mPolicy.setSafeMode(mSafeMode); + return mSafeMode; + } + + public void displayReady() { + synchronized (mGlobalLock) { + if (mMaxUiWidth > 0) { + mRoot.forAllDisplays(dc -> { + if (dc.mDisplay.getType() == Display.TYPE_INTERNAL) { + dc.setMaxUiWidth(mMaxUiWidth); + } + }); + } + applyForcedPropertiesForDefaultDisplay(); + mAnimator.ready(); + mDisplayReady = true; + mHasWideColorGamutSupport = queryWideColorGamutSupport(); + mHasHdrSupport = queryHdrSupport(); + mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TOUCHSCREEN); + mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_FAKETOUCH); + // Reconfigure all displays to make sure that the forced properties and + // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also + // affect the Configuration. + mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked); + } + } + + public void systemReady() { + mSystemReady = true; + mPolicy.systemReady(); + mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady); + mSnapshotController.systemReady(); + UiThread.getHandler().post(mSettingsObserver::loadSettings); + IVrManager vrManager = IVrManager.Stub.asInterface( + ServiceManager.getService(Context.VR_SERVICE)); + if (vrManager != null) { + try { + final boolean vrModeEnabled = vrManager.getVrModeState(); + synchronized (mGlobalLock) { + vrManager.registerListener(mVrStateCallbacks); + if (vrModeEnabled) { + mVrModeEnabled = vrModeEnabled; + mVrStateCallbacks.onVrStateChanged(vrModeEnabled); + } + } + } catch (RemoteException e) { + // Ignore, we cannot do anything if we failed to register VR mode listener + } + } + } + + + // Keep logic in sync with SurfaceFlingerProperties.cpp + // Consider exposing properties via ISurfaceComposer instead. + private static boolean queryWideColorGamutSupport() { + boolean defaultValue = false; + Optional hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display(); + if (hasWideColorProp.isPresent()) { + return hasWideColorProp.get(); + } + try { + ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); + OptionalBool hasWideColor = surfaceFlinger.hasWideColorDisplay(); + if (hasWideColor != null) { + return hasWideColor.value; + } + } catch (RemoteException e) { + // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store + } catch (NoSuchElementException e) { + return defaultValue; + } + return false; + } + + private static boolean queryHdrSupport() { + boolean defaultValue = false; + Optional hasHdrProp = SurfaceFlingerProperties.has_HDR_display(); + if (hasHdrProp.isPresent()) { + return hasHdrProp.get(); + } + try { + ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); + OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay(); + if (hasHdr != null) { + return hasHdr.value; + } + } catch (RemoteException e) { + // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store + } catch (NoSuchElementException e) { + return defaultValue; + } + return false; + } + + // Returns an input target which is mapped to the given input token. This can be a WindowState + // or an embedded window. + @Nullable InputTarget getInputTargetFromToken(IBinder inputToken) { + WindowState windowState = mInputToWindowMap.get(inputToken); + if (windowState != null) { + return windowState; + } + + EmbeddedWindowController.EmbeddedWindow embeddedWindow = + mEmbeddedWindowController.get(inputToken); + if (embeddedWindow != null) { + return embeddedWindow; + } + + return null; + } + + @Nullable InputTarget getInputTargetFromWindowTokenLocked(IBinder windowToken) { + InputTarget window = mWindowMap.get(windowToken); + if (window != null) { + return window; + } + window = mEmbeddedWindowController.getByWindowToken(windowToken); + return window; + } + + void reportFocusChanged(IBinder oldToken, IBinder newToken) { + InputTarget lastTarget; + InputTarget newTarget; + synchronized (mGlobalLock) { + lastTarget = getInputTargetFromToken(oldToken); + newTarget = getInputTargetFromToken(newToken); + if (newTarget == null && lastTarget == null) { + Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged"); + return; + } + mFocusedInputTarget = newTarget; + + mAccessibilityController.onFocusChanged(lastTarget, newTarget); + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget); + } + + // Call WindowState focus change observers + WindowState newFocusedWindow = newTarget != null ? newTarget.getWindowState() : null; + if (newFocusedWindow != null && newFocusedWindow.mInputChannelToken == newToken) { + mAnrController.onFocusChanged(newFocusedWindow); + newFocusedWindow.reportFocusChangedSerialized(true); + notifyFocusChanged(); + } + + WindowState lastFocusedWindow = lastTarget != null ? lastTarget.getWindowState() : null; + if (lastFocusedWindow != null && lastFocusedWindow.mInputChannelToken == oldToken) { + lastFocusedWindow.reportFocusChangedSerialized(false); + } + } + + // ------------------------------------------------------------- + // Async Handler + // ------------------------------------------------------------- + + final class H extends android.os.Handler { + public static final int WINDOW_FREEZE_TIMEOUT = 11; + + public static final int PERSIST_ANIMATION_SCALE = 14; + public static final int ENABLE_SCREEN = 16; + public static final int APP_FREEZE_TIMEOUT = 17; + public static final int REPORT_WINDOWS_CHANGE = 19; + + public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22; + public static final int BOOT_TIMEOUT = 23; + public static final int WAITING_FOR_DRAWN_TIMEOUT = 24; + public static final int SHOW_STRICT_MODE_VIOLATION = 25; + + public static final int CLIENT_FREEZE_TIMEOUT = 30; + public static final int NOTIFY_ACTIVITY_DRAWN = 32; + + public static final int NEW_ANIMATOR_SCALE = 34; + + public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36; + + public static final int CHECK_IF_BOOT_ANIMATION_FINISHED = 37; + public static final int RESET_ANR_MESSAGE = 38; + public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39; + + public static final int UPDATE_MULTI_WINDOW_STACKS = 41; + + public static final int UPDATE_ANIMATION_SCALE = 51; + public static final int WINDOW_HIDE_TIMEOUT = 52; + public static final int RESTORE_POINTER_ICON = 55; + public static final int SET_HAS_OVERLAY_UI = 58; + public static final int ANIMATION_FAILSAFE = 60; + public static final int RECOMPUTE_FOCUS = 61; + public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62; + public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64; + public static final int REPARENT_TASK_TO_DEFAULT_DISPLAY = 65; + public static final int INSETS_CHANGED = 66; + + /** + * Used to denote that an integer field in a message will not be used. + */ + public static final int UNUSED = 0; + + @Override + public void handleMessage(Message msg) { + if (DEBUG_WINDOW_TRACE) { + Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what); + } + switch (msg.what) { + case WINDOW_FREEZE_TIMEOUT: { + final DisplayContent displayContent = (DisplayContent) msg.obj; + synchronized (mGlobalLock) { + displayContent.onWindowFreezeTimeout(); + } + break; + } + + case PERSIST_ANIMATION_SCALE: { + Settings.Global.putFloat(mContext.getContentResolver(), + Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting); + Settings.Global.putFloat(mContext.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, + mTransitionAnimationScaleSetting); + Settings.Global.putFloat(mContext.getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting); + break; + } + + case UPDATE_ANIMATION_SCALE: { + @UpdateAnimationScaleMode + final int mode = msg.arg1; + switch (mode) { + case WINDOW_ANIMATION_SCALE: { + mWindowAnimationScaleSetting = getWindowAnimationScaleSetting(); + break; + } + case TRANSITION_ANIMATION_SCALE: { + mTransitionAnimationScaleSetting = + getTransitionAnimationScaleSetting(); + break; + } + case ANIMATION_DURATION_SCALE: { + mAnimatorDurationScaleSetting = getAnimatorDurationScaleSetting(); + dispatchNewAnimatorScaleLocked(null); + break; + } + } + break; + } + + case ENABLE_SCREEN: { + performEnableScreen(); + break; + } + + case APP_FREEZE_TIMEOUT: { + synchronized (mGlobalLock) { + ProtoLog.w(WM_ERROR, "App freeze timeout expired."); + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; + for (int i = mAppFreezeListeners.size() - 1; i >= 0; --i) { + mAppFreezeListeners.get(i).onAppFreezeTimeout(); + } + } + break; + } + + case CLIENT_FREEZE_TIMEOUT: { + synchronized (mGlobalLock) { + if (mClientFreezingScreen) { + mClientFreezingScreen = false; + mLastFinishedFreezeSource = "client-timeout"; + stopFreezingDisplayLocked(); + } + } + break; + } + + case REPORT_WINDOWS_CHANGE: { + if (mWindowsChanged) { + synchronized (mGlobalLock) { + mWindowsChanged = false; + } + notifyWindowsChanged(); + } + break; + } + + case REPORT_HARD_KEYBOARD_STATUS_CHANGE: { + notifyHardKeyboardStatusChange(); + break; + } + + case BOOT_TIMEOUT: { + performBootTimeout(); + break; + } + + case WAITING_FOR_DRAWN_TIMEOUT: { + final Message callback; + final WindowContainer container = (WindowContainer) msg.obj; + synchronized (mGlobalLock) { + ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s", + container.mWaitingForDrawn); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + for (int i = 0; i < container.mWaitingForDrawn.size(); i++) { + traceEndWaitingForWindowDrawn(container.mWaitingForDrawn.get(i)); + } + } + container.mWaitingForDrawn.clear(); + callback = mWaitingForDrawnCallbacks.remove(container); + } + if (callback != null) { + callback.sendToTarget(); + } + break; + } + + case SHOW_STRICT_MODE_VIOLATION: { + showStrictModeViolation(msg.arg1, msg.arg2); + break; + } + + case SHOW_EMULATOR_DISPLAY_OVERLAY: { + showEmulatorDisplayOverlay(); + break; + } + + case NOTIFY_ACTIVITY_DRAWN: { + final ActivityRecord activity = (ActivityRecord) msg.obj; + synchronized (mGlobalLock) { + if (activity.isAttached()) { + activity.getRootTask().notifyActivityDrawnLocked(activity); + } + } + break; + } + case NEW_ANIMATOR_SCALE: { + float scale = getCurrentAnimatorScale(); + ValueAnimator.setDurationScale(scale); + Session session = (Session)msg.obj; + if (session != null) { + try { + session.mCallback.onAnimatorScaleChanged(scale); + } catch (RemoteException e) { + } + } else { + ArrayList callbacks + = new ArrayList(); + synchronized (mGlobalLock) { + for (int i=0; i 0) { + // We need to update resizing windows and dispatch the new insets state + // to them. + mWindowPlacerLocked.performSurfacePlacement(); + } + } + break; + } + } + if (DEBUG_WINDOW_TRACE) { + Slog.v(TAG_WM, "handleMessage: exit"); + } + } + + /** Remove the previous messages with the same 'what' and 'obj' then send the new one. */ + void sendNewMessageDelayed(int what, Object obj, long delayMillis) { + removeMessages(what, obj); + sendMessageDelayed(obtainMessage(what, obj), delayMillis); + } + } + + // ------------------------------------------------------------- + // IWindowManager API + // ------------------------------------------------------------- + + @Override + public IWindowSession openSession(IWindowSessionCallback callback) { + return new Session(this, callback); + } + + @Override + public void getInitialDisplaySize(int displayId, Point size) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + size.x = displayContent.mInitialDisplayWidth; + size.y = displayContent.mInitialDisplayHeight; + } + } + } + + @Override + public void getBaseDisplaySize(int displayId, Point size) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + size.x = displayContent.mBaseDisplayWidth; + size.y = displayContent.mBaseDisplayHeight; + } + } + } + + @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Override + public void setForcedDisplaySize(int displayId, int width, int height) { + setForcedDisplaySize_enforcePermission(); + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setForcedSize(width, height); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Override + public void setForcedDisplayScalingMode(int displayId, int mode) { + setForcedDisplayScalingMode_enforcePermission(); + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setForcedScalingMode(mode); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) { + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setSandboxDisplayApis(sandboxDisplayApis); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** The global settings only apply to default display. */ + private boolean applyForcedPropertiesForDefaultDisplay() { + boolean changed = false; + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + // Display size. + String sizeStr = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.DISPLAY_SIZE_FORCED); + if (sizeStr == null || sizeStr.length() == 0) { + sizeStr = SystemProperties.get(SIZE_OVERRIDE, null); + } + if (sizeStr != null && sizeStr.length() > 0) { + final int pos = sizeStr.indexOf(','); + if (pos > 0 && sizeStr.lastIndexOf(',') == pos) { + try { + final Point size = displayContent.getValidForcedSize( + Integer.parseInt(sizeStr.substring(0, pos)), + Integer.parseInt(sizeStr.substring(pos + 1))); + final int width = size.x; + final int height = size.y; + if (displayContent.mBaseDisplayWidth != width + || displayContent.mBaseDisplayHeight != height) { + ProtoLog.i(WM_ERROR, "FORCED DISPLAY SIZE: %dx%d", width, height); + displayContent.updateBaseDisplayMetrics(width, height, + displayContent.mBaseDisplayDensity, + displayContent.mBaseDisplayPhysicalXDpi, + displayContent.mBaseDisplayPhysicalYDpi); + changed = true; + } + } catch (NumberFormatException ex) { + } + } + } + + // Display density. + final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId); + if (density != 0 && density != displayContent.mBaseDisplayDensity) { + displayContent.mBaseDisplayDensity = density; + changed = true; + } + + // Display scaling mode. + int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DISPLAY_SCALING_FORCE, 0); + if (displayContent.mDisplayScalingDisabled != (mode != 0)) { + ProtoLog.i(WM_ERROR, "FORCED DISPLAY SCALING DISABLED"); + displayContent.mDisplayScalingDisabled = true; + changed = true; + } + return changed; + } + + @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Override + public void clearForcedDisplaySize(int displayId) { + clearForcedDisplaySize_enforcePermission(); + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setForcedSize(displayContent.mInitialDisplayWidth, + displayContent.mInitialDisplayHeight, + displayContent.mInitialPhysicalXDpi, + displayContent.mInitialPhysicalXDpi); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public int getInitialDisplayDensity(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + return displayContent.getInitialDisplayDensity(); + } + + DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId); + if (info != null && info.hasAccess(Binder.getCallingUid())) { + return info.logicalDensityDpi; + } + } + return -1; + } + + @Override + public int getBaseDisplayDensity(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + return displayContent.mBaseDisplayDensity; + } + } + return -1; + } + + /** + * Return the display Id that has the given uniqueId. Unique ID is defined in + * {@link DisplayInfo#uniqueId}. + */ + @Override + public int getDisplayIdByUniqueId(String uniqueId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(uniqueId); + if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { + return displayContent.mDisplayId; + } + } + return -1; + } + + @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Override + public void setForcedDisplayDensityForUser(int displayId, int density, int userId) { + setForcedDisplayDensityForUser_enforcePermission(); + + final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser", + null); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setForcedDensity(density, targetUserId); + } else { + DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId); + if (info != null) { + mDisplayWindowSettings.setForcedDensity(info, density, userId); + } + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Override + public void clearForcedDisplayDensityForUser(int displayId, int userId) { + clearForcedDisplayDensityForUser_enforcePermission(); + + final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser", + null); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setForcedDensity(displayContent.getInitialDisplayDensity(), + callingUserId); + } else { + DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId); + if (info != null) { + mDisplayWindowSettings.setForcedDensity(info, info.logicalDensityDpi, + userId); + } + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * @param userId the ID of the user + * @return the forced display density for the specified user, if set, or + * {@code 0} if not set + */ + private int getForcedDisplayDensityForUserLocked(int userId) { + String densityStr = Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.DISPLAY_DENSITY_FORCED, userId); + if (densityStr == null || densityStr.length() == 0) { + densityStr = SystemProperties.get(DENSITY_OVERRIDE, null); + } + if (densityStr != null && densityStr.length() > 0) { + try { + return Integer.parseInt(densityStr); + } catch (NumberFormatException ex) { + } + } + return 0; + } + + @Override + public void startWindowTrace(){ + mWindowTracing.startTrace(null /* printwriter */); + } + + @Override + public void stopWindowTrace(){ + mWindowTracing.stopTrace(null /* printwriter */); + } + + @Override + public void saveWindowTraceToFile() { + mWindowTracing.saveForBugreport(null /* printwriter */); + } + + @Override + public boolean isWindowTraceEnabled() { + return mWindowTracing.isEnabled(); + } + + @Override + public void startTransitionTrace() { + mTransitionTracer.startTrace(null /* printwriter */); + } + + @Override + public void stopTransitionTrace() { + mTransitionTracer.stopTrace(null /* printwriter */); + } + + @Override + public boolean isTransitionTraceEnabled() { + return mTransitionTracer.isTracing(); + } + + @Override + public boolean registerCrossWindowBlurEnabledListener( + ICrossWindowBlurEnabledListener listener) { + return mBlurController.registerCrossWindowBlurEnabledListener(listener); + } + + @Override + public void unregisterCrossWindowBlurEnabledListener( + ICrossWindowBlurEnabledListener listener) { + mBlurController.unregisterCrossWindowBlurEnabledListener(listener); + } + + // ------------------------------------------------------------- + // Internals + // ------------------------------------------------------------- + + final WindowState windowForClientLocked(Session session, IWindow client, boolean throwOnError) { + return windowForClientLocked(session, client.asBinder(), throwOnError); + } + + final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) { + WindowState win = mWindowMap.get(client); + if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win); + if (win == null) { + if (throwOnError) { + throw new IllegalArgumentException( + "Requested window " + client + " does not exist"); + } + ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session, + Debug.getCallers(3)); + return null; + } + if (session != null && win.mSession != session) { + if (throwOnError) { + throw new IllegalArgumentException("Requested window " + client + " is in session " + + win.mSession + ", not " + session); + } + ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session, + Debug.getCallers(3)); + return null; + } + + return win; + } + + void makeWindowFreezingScreenIfNeededLocked(WindowState w) { + // If the screen is currently frozen, then keep it frozen until this window draws at its + // new orientation. + if (mFrozenDisplayId != INVALID_DISPLAY && mFrozenDisplayId == w.getDisplayId() + && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { + ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w); + // WindowsState#reportResized won't tell invisible requested window to redraw, + // so do not set it as changing orientation to avoid affecting draw state. + if (w.isVisibleRequested()) { + w.setOrientationChanging(true); + } + if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) { + mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; + // XXX should probably keep timeout from + // when we first froze the display. + mH.sendNewMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, w.getDisplayContent(), + WINDOW_FREEZE_TIMEOUT_DURATION); + } + } + } + + void checkDrawnWindowsLocked() { + if (mWaitingForDrawnCallbacks.isEmpty()) { + return; + } + for (int i = mWaitingForDrawnCallbacks.size() - 1; i >= 0; i--) { + final WindowContainer container = mWaitingForDrawnCallbacks.keyAt(i); + for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) { + final WindowState win = (WindowState) container.mWaitingForDrawn.get(j); + ProtoLog.i(WM_DEBUG_SCREEN_ON, + "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", + win, win.mRemoved, win.isVisible(), win.mHasSurface, + win.mWinAnimator.mDrawState); + if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) { + // Window has been removed or hidden; no draw will now happen, so stop waiting. + ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win); + container.mWaitingForDrawn.remove(win); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + traceEndWaitingForWindowDrawn(win); + } + } else if (win.hasDrawn()) { + // Window is now drawn (and shown). + ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win); + container.mWaitingForDrawn.remove(win); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + traceEndWaitingForWindowDrawn(win); + } + } + } + if (container.mWaitingForDrawn.isEmpty()) { + ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!"); + mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container); + mWaitingForDrawnCallbacks.removeAt(i).sendToTarget(); + } + } + } + + private void traceStartWaitingForWindowDrawn(WindowState window) { + final String traceName = TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD + "#" + + window.getWindowTag(); + final String shortenedTraceName = traceName.substring(0, Math.min( + TRACE_MAX_SECTION_NAME_LENGTH, traceName.length())); + Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, shortenedTraceName, /* cookie= */ 0); + } + + private void traceEndWaitingForWindowDrawn(WindowState window) { + final String traceName = TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD + "#" + + window.getWindowTag(); + final String shortenedTraceName = traceName.substring(0, Math.min( + TRACE_MAX_SECTION_NAME_LENGTH, traceName.length())); + Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, shortenedTraceName, /* cookie= */ 0); + } + + void requestTraversal() { + mWindowPlacerLocked.requestTraversal(); + } + + /** Note that Locked in this case is on mLayoutToAnim */ + void scheduleAnimationLocked() { + mAnimator.scheduleAnimation(); + } + + boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus"); + boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + return changed; + } + + void startFreezingDisplay(int exitAnim, int enterAnim) { + startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked()); + } + + void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) { + startFreezingDisplay(exitAnim, enterAnim, displayContent, + ROTATION_UNDEFINED /* overrideOriginalRotation */); + } + + void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent, + int overrideOriginalRotation) { + if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) { + return; + } + + if (!displayContent.isReady() || !displayContent.getDisplayPolicy().isScreenOnFully() + || displayContent.getDisplayInfo().state == Display.STATE_OFF + || !displayContent.okToAnimate()) { + // No need to freeze the screen before the display is ready, if the screen is off, + // or we can't currently animate. + return; + } + + displayContent.requestDisplayUpdate(() -> { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStartFreezingDisplay"); + doStartFreezingDisplay(exitAnim, enterAnim, displayContent, overrideOriginalRotation); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + }); + } + + private void doStartFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent, + int overrideOriginalRotation) { + ProtoLog.d(WM_DEBUG_ORIENTATION, + "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s", + exitAnim, enterAnim, Debug.getCallers(8)); + mScreenFrozenLock.acquire(); + // Apply launch power mode to reduce screen frozen time because orientation change may + // relaunch activity and redraw windows. This may also help speed up user switching. + mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); + + mDisplayFrozen = true; + mDisplayFreezeTime = SystemClock.elapsedRealtime(); + mLastFinishedFreezeSource = null; + + // {@link mDisplayFrozen} prevents us from freezing on multiple displays at the same time. + // As a result, we only track the display that has initially froze the screen. + mFrozenDisplayId = displayContent.getDisplayId(); + + mInputManagerCallback.freezeInputDispatchingLw(); + + if (displayContent.mAppTransition.isTransitionSet()) { + displayContent.mAppTransition.freeze(); + } + + if (PROFILE_ORIENTATION) { + File file = new File("/data/system/frozen"); + Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); + } + + mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN); + mExitAnimId = exitAnim; + mEnterAnimId = enterAnim; + + final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED + ? overrideOriginalRotation + : displayContent.getDisplayInfo().rotation; + displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent, + originalRotation)); + } + + void stopFreezingDisplayLocked() { + if (!mDisplayFrozen) { + return; + } + + final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId); + final int numOpeningApps; + final boolean waitingForConfig; + final boolean waitingForRemoteDisplayChange; + if (displayContent != null) { + numOpeningApps = displayContent.mOpeningApps.size(); + waitingForConfig = displayContent.mWaitingForConfig; + waitingForRemoteDisplayChange = displayContent.mRemoteDisplayChangeController + .isWaitingForRemoteDisplayChange(); + } else { + waitingForConfig = waitingForRemoteDisplayChange = false; + numOpeningApps = 0; + } + if (waitingForConfig || waitingForRemoteDisplayChange || mAppsFreezingScreen > 0 + || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE + || mClientFreezingScreen || numOpeningApps > 0) { + ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning " + + "waitingForConfig=%b, waitingForRemoteDisplayChange=%b, " + + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, " + + "mClientFreezingScreen=%b, mOpeningApps.size()=%d", + waitingForConfig, waitingForRemoteDisplayChange, + mAppsFreezingScreen, mWindowsFreezingScreen, + mClientFreezingScreen, numOpeningApps); + return; + } + + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStopFreezingDisplayLocked-" + + mLastFinishedFreezeSource); + doStopFreezingDisplayLocked(displayContent); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + private void doStopFreezingDisplayLocked(DisplayContent displayContent) { + ProtoLog.d(WM_DEBUG_ORIENTATION, + "stopFreezingDisplayLocked: Unfreezing now"); + + // We must make a local copy of the displayId as it can be potentially overwritten later on + // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result + // of update rotation, but we reference the frozen display after that call in this method. + mFrozenDisplayId = INVALID_DISPLAY; + mDisplayFrozen = false; + mInputManagerCallback.thawInputDispatchingLw(); + mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime); + StringBuilder sb = new StringBuilder(128); + sb.append("Screen frozen for "); + TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb); + if (mLastFinishedFreezeSource != null) { + sb.append(" due to "); + sb.append(mLastFinishedFreezeSource); + } + ProtoLog.i(WM_ERROR, "%s", sb.toString()); + mH.removeMessages(H.APP_FREEZE_TIMEOUT); + mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT); + if (PROFILE_ORIENTATION) { + Debug.stopMethodTracing(); + } + + boolean updateRotation = false; + + ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null + : displayContent.getRotationAnimation(); + if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { + ProtoLog.i(WM_DEBUG_ORIENTATION, "**** Dismissing screen rotation animation"); + DisplayInfo displayInfo = displayContent.getDisplayInfo(); + // Get rotation animation again, with new top window + if (!displayContent.getDisplayRotation().validateRotationAnimation( + mExitAnimId, mEnterAnimId, false /* forceDefault */)) { + mExitAnimId = mEnterAnimId = 0; + } + if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION, + getTransitionAnimationScaleLocked(), displayInfo.logicalWidth, + displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) { + mTransaction.apply(); + } else { + screenRotationAnimation.kill(); + displayContent.setRotationAnimation(null); + updateRotation = true; + } + } else { + if (screenRotationAnimation != null) { + screenRotationAnimation.kill(); + displayContent.setRotationAnimation(null); + } + updateRotation = true; + } + + boolean configChanged; + + // While the display is frozen we don't re-compute the orientation + // to avoid inconsistent states. However, something interesting + // could have actually changed during that time so re-evaluate it + // now to catch that. + configChanged = displayContent != null && displayContent.updateOrientation(); + + mScreenFrozenLock.release(); + + if (updateRotation && displayContent != null) { + ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation"); + configChanged |= displayContent.updateRotationUnchecked(); + } + + if (configChanged) { + displayContent.sendNewConfiguration(); + } + mAtmService.endPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); + mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN); + } + + static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps, + DisplayMetrics dm) { + if (index < tokens.length) { + String str = tokens[index]; + if (str != null && str.length() > 0) { + try { + int val = Integer.parseInt(str); + return val; + } catch (Exception e) { + } + } + } + if (defUnits == TypedValue.COMPLEX_UNIT_PX) { + return defDps; + } + int val = (int)TypedValue.applyDimension(defUnits, defDps, dm); + return val; + } + + void createWatermark() { + if (mWatermark != null) { + return; + } + + File file = new File("/system/etc/setup.conf"); + FileInputStream in = null; + DataInputStream ind = null; + try { + in = new FileInputStream(file); + ind = new DataInputStream(in); + String line = ind.readLine(); + if (line != null) { + String[] toks = line.split("%"); + if (toks != null && toks.length > 0) { + // TODO(multi-display): Show watermarks on secondary displays. + final DisplayContent displayContent = getDefaultDisplayContentLocked(); + mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics, + toks, mTransaction); + mTransaction.apply(); + } + } + } catch (FileNotFoundException e) { + } catch (IOException e) { + } finally { + if (ind != null) { + try { + ind.close(); + } catch (IOException e) { + } + } else if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + } + + @Override + public void setRecentsVisibility(boolean visible) { + if (!checkCallingPermission( + android.Manifest.permission.STATUS_BAR, "setRecentsVisibility()")) { + throw new SecurityException("Requires STATUS_BAR permission"); + } + synchronized (mGlobalLock) { + mPolicy.setRecentsVisibilityLw(visible); + } + } + + @Override + public void hideTransientBars(int displayId) { + if (!checkCallingPermission( + android.Manifest.permission.STATUS_BAR, "hideTransientBars()")) { + throw new SecurityException("Requires STATUS_BAR permission"); + } + + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.getInsetsPolicy().hideTransient(); + } else { + Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId); + } + } + } + + @Override + public void updateStaticPrivacyIndicatorBounds(int displayId, + Rect[] staticBounds) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.updatePrivacyIndicatorBounds(staticBounds); + } else { + Slog.w(TAG, "updateStaticPrivacyIndicatorBounds with invalid displayId=" + + displayId); + } + } + } + + @EnforcePermission(android.Manifest.permission.STATUS_BAR) + public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { + setNavBarVirtualKeyHapticFeedbackEnabled_enforcePermission(); + + synchronized (mGlobalLock) { + mPolicy.setNavBarVirtualKeyHapticFeedbackEnabledLw(enabled); + } + } + + @Override + public void createInputConsumer(IBinder token, String name, int displayId, + InputChannel inputChannel) { + if (!mAtmService.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission"); + } + + synchronized (mGlobalLock) { + DisplayContent display = mRoot.getDisplayContent(displayId); + if (display != null) { + display.getInputMonitor().createInputConsumer(token, name, inputChannel, + Binder.getCallingPid(), Binder.getCallingUserHandle()); + } + } + } + + @Override + public boolean destroyInputConsumer(IBinder token, int displayId) { + if (!mAtmService.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission"); + } + + synchronized (mGlobalLock) { + DisplayContent display = mRoot.getDisplayContent(displayId); + if (display != null) { + return display.getInputMonitor().destroyInputConsumer(token); + } + return false; + } + } + + @EnforcePermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) + @Override + public Region getCurrentImeTouchRegion() { + getCurrentImeTouchRegion_enforcePermission(); + synchronized (mGlobalLock) { + final Region r = new Region(); + // TODO(b/111080190): this method is only return the recent focused IME touch region, + // For Multi-Session IME, will need to add API for given display Id to + // get the right IME touch region. + for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) { + final DisplayContent displayContent = mRoot.mChildren.get(i); + if (displayContent.mInputMethodWindow != null) { + displayContent.mInputMethodWindow.getTouchableRegion(r); + return r; + } + } + return r; + } + } + + @Override + public boolean hasNavigationBar(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return false; + } + return dc.getDisplayPolicy().hasNavigationBar(); + } + } + + @Override + public void lockNow(Bundle options) { + mPolicy.lockNow(options); + } + + public void showRecentApps() { + mPolicy.showRecentApps(); + } + + @Override + public boolean isSafeModeEnabled() { + return mSafeMode; + } + + @Override + public boolean clearWindowContentFrameStats(IBinder token) { + if (!checkCallingPermission(Manifest.permission.FRAME_STATS, + "clearWindowContentFrameStats()")) { + throw new SecurityException("Requires FRAME_STATS permission"); + } + synchronized (mGlobalLock) { + WindowState windowState = mWindowMap.get(token); + if (windowState == null) { + return false; + } + WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController; + if (surfaceController == null) { + return false; + } + return surfaceController.clearWindowContentFrameStats(); + } + } + + @Override + public WindowContentFrameStats getWindowContentFrameStats(IBinder token) { + if (!checkCallingPermission(Manifest.permission.FRAME_STATS, + "getWindowContentFrameStats()")) { + throw new SecurityException("Requires FRAME_STATS permission"); + } + synchronized (mGlobalLock) { + WindowState windowState = mWindowMap.get(token); + if (windowState == null) { + return null; + } + WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController; + if (surfaceController == null) { + return null; + } + if (mTempWindowRenderStats == null) { + mTempWindowRenderStats = new WindowContentFrameStats(); + } + WindowContentFrameStats stats = mTempWindowRenderStats; + if (!surfaceController.getWindowContentFrameStats(stats)) { + return null; + } + return stats; + } + } + + private void dumpPolicyLocked(PrintWriter pw, String[] args) { + pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); + mPolicy.dump(" ", pw, args); + } + + private void dumpAnimatorLocked(PrintWriter pw, boolean dumpAll) { + pw.println("WINDOW MANAGER ANIMATOR STATE (dumpsys window animator)"); + mAnimator.dumpLocked(pw, " ", dumpAll); + } + + private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { + pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); + mRoot.dumpTokens(pw, dumpAll); + } + + + private void dumpHighRefreshRateBlacklist(PrintWriter pw) { + pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)"); + mHighRefreshRateDenylist.dump(pw); + } + + private void dumpTraceStatus(PrintWriter pw) { + pw.println("WINDOW MANAGER TRACE (dumpsys window trace)"); + pw.print(mWindowTracing.getStatus() + "\n"); + } + + private void dumpLogStatus(PrintWriter pw) { + pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)"); + if (android.tracing.Flags.perfettoProtologTracing()) { + pw.println("Deprecated legacy command. Use Perfetto commands instead."); + return; + } + ((LegacyProtoLogImpl) ProtoLog.getSingleInstance()).getStatus(); + } + + private void dumpSessionsLocked(PrintWriter pw) { + pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)"); + for (int i=0; i windows) { + pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)"); + mRoot.dumpWindowsNoHeader(pw, dumpAll, windows); + + if (!mHidingNonSystemOverlayWindows.isEmpty()) { + pw.println(); + pw.println(" Hiding System Alert Windows:"); + for (int i = mHidingNonSystemOverlayWindows.size() - 1; i >= 0; i--) { + final WindowState w = mHidingNonSystemOverlayWindows.get(i); + pw.print(" #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + if (mForceRemoves != null && !mForceRemoves.isEmpty()) { + pw.println(); + pw.println(" Windows force removing:"); + for (int i=mForceRemoves.size()-1; i>=0; i--) { + WindowState w = mForceRemoves.get(i); + pw.print(" Removing #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + if (!mDestroySurface.isEmpty()) { + pw.println(); + pw.println(" Windows waiting to destroy their surface:"); + for (int i=mDestroySurface.size()-1; i>=0; i--) { + WindowState w = mDestroySurface.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Destroy #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } + if (!mResizingWindows.isEmpty()) { + pw.println(); + pw.println(" Windows waiting to resize:"); + for (int i=mResizingWindows.size()-1; i>=0; i--) { + WindowState w = mResizingWindows.get(i); + if (windows == null || windows.contains(w)) { + pw.print(" Resizing #"); pw.print(i); pw.print(' '); + pw.print(w); + if (dumpAll) { + pw.println(":"); + w.dump(pw, " ", true); + } else { + pw.println(); + } + } + } + } + if (!mWaitingForDrawnCallbacks.isEmpty()) { + pw.println(); + pw.println(" Clients waiting for these windows to be drawn:"); + mWaitingForDrawnCallbacks.forEach((wc, callback) -> { + pw.print(" WindowContainer "); + pw.println(wc.getName()); + for (int i = wc.mWaitingForDrawn.size() - 1; i >= 0; i--) { + final WindowState win = (WindowState) wc.mWaitingForDrawn.get(i); + pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win); + } + }); + + } + pw.println(); + pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration()); + pw.print(" mHasPermanentDpad="); pw.println(mHasPermanentDpad); + mRoot.dumpTopFocusedDisplayId(pw); + mRoot.forAllDisplays(dc -> { + final int displayId = dc.getDisplayId(); + final InsetsControlTarget imeLayeringTarget = dc.getImeTarget(IME_TARGET_LAYERING); + final InputTarget imeInputTarget = dc.getImeInputTarget(); + final InsetsControlTarget imeControlTarget = dc.getImeTarget(IME_TARGET_CONTROL); + if (imeLayeringTarget != null) { + pw.print(" imeLayeringTarget in display# "); pw.print(displayId); + pw.print(' '); pw.println(imeLayeringTarget); + } + if (imeInputTarget != null) { + pw.print(" imeInputTarget in display# "); pw.print(displayId); + pw.print(' '); pw.println(imeInputTarget); + } + if (imeControlTarget != null) { + pw.print(" imeControlTarget in display# "); pw.print(displayId); + pw.print(' '); pw.println(imeControlTarget); + } + pw.print(" Minimum task size of display#"); pw.print(displayId); + pw.print(' '); pw.print(dc.mMinSizeOfResizeableTaskDp); + }); + pw.print(" mBlurEnabled="); pw.println(mBlurController.getBlurEnabled()); + pw.print(" mLastDisplayFreezeDuration="); + TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw); + if ( mLastFinishedFreezeSource != null) { + pw.print(" due to "); + pw.print(mLastFinishedFreezeSource); + } + pw.println(); + + mInputManagerCallback.dump(pw, " "); + mSnapshotController.dump(pw, " "); + + dumpAccessibilityController(pw, /* force= */ false); + + if (dumpAll) { + final WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); + if (imeWindow != null) { + pw.print(" mInputMethodWindow="); pw.println(imeWindow); + } + mWindowPlacerLocked.dump(pw, " "); + pw.print(" mSystemBooted="); pw.print(mSystemBooted); + pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled); + + mRoot.dumpLayoutNeededDisplayIds(pw); + + pw.print(" mTransactionSequence="); pw.println(mTransactionSequence); + pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen); + pw.print(" windows="); pw.print(mWindowsFreezingScreen); + pw.print(" client="); pw.print(mClientFreezingScreen); + pw.print(" apps="); pw.println(mAppsFreezingScreen); + final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); + pw.print(" mRotation="); pw.println(defaultDisplayContent.getRotation()); + pw.print(" mLastOrientation="); + pw.println(defaultDisplayContent.getLastOrientation()); + pw.print(" mWaitingForConfig="); + pw.println(defaultDisplayContent.mWaitingForConfig); + pw.print(" mWindowsInsetsChanged="); pw.println(mWindowsInsetsChanged); + mRotationWatcherController.dump(pw); + + pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled); + pw.print(" window="); pw.print(mWindowAnimationScaleSetting); + pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting); + pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting); + if (mRecentsAnimationController != null) { + pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController); + mRecentsAnimationController.dump(pw, " "); + } + } + } + + private void dumpAccessibilityController(PrintWriter pw, boolean force) { + boolean hasCallbacks = mAccessibilityController.hasCallbacks(); + if (!hasCallbacks && !force) { + return; + } + if (!hasCallbacks) { + pw.println("AccessibilityController doesn't have callbacks, but printing it anways:"); + } else { + pw.println("AccessibilityController:"); + } + mAccessibilityController.dump(pw, " "); + } + + private void dumpAccessibilityLocked(PrintWriter pw) { + dumpAccessibilityController(pw, /* force= */ true); + } + + private boolean dumpWindows(PrintWriter pw, String name, boolean dumpAll) { + final ArrayList windows = new ArrayList(); + if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) { + final boolean appsOnly = name.contains("apps"); + final boolean visibleOnly = name.contains("visible"); + synchronized (mGlobalLock) { + if (appsOnly) { + mRoot.dumpDisplayContents(pw); + } + + mRoot.forAllWindows((w) -> { + if ((!visibleOnly || w.isVisible()) + && (!appsOnly || w.mActivityRecord != null)) { + windows.add(w); + } + }, true /* traverseTopToBottom */); + } + } else { + synchronized (mGlobalLock) { + mRoot.getWindowsByName(windows, name); + } + } + + if (windows.isEmpty()) { + return false; + } + + synchronized (mGlobalLock) { + dumpWindowsLocked(pw, dumpAll, windows); + } + return true; + } + + private void dumpLastANRLocked(PrintWriter pw) { + pw.println("WINDOW MANAGER LAST ANR (dumpsys window lastanr)"); + if (mLastANRState == null) { + pw.println(" "); + } else { + pw.println(mLastANRState); + } + } + + /** + * Saves information about the state of the window manager at + * the time an ANR occurred before anything else in the system changes + * in response. + * + * @param activity The application that ANR'd, may be null. + * @param windowState The window that ANR'd, may be null. + * @param reason The reason for the ANR, may be null. + */ + void saveANRStateLocked(ActivityRecord activity, WindowState windowState, String reason) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new FastPrintWriter(sw, false, 1024); + pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date())); + if (activity != null) { + pw.println(" Application at fault: " + activity.stringName); + } + if (windowState != null) { + pw.println(" Window at fault: " + windowState.mAttrs.getTitle()); + } + if (reason != null) { + pw.println(" Reason: " + reason); + } + pw.println(); + final ArrayList relatedWindows = new ArrayList<>(); + for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { + final DisplayContent dc = mRoot.getChildAt(i); + final int displayId = dc.getDisplayId(); + final WindowState currentFocus = dc.mCurrentFocus; + final ActivityRecord focusedApp = dc.mFocusedApp; + pw.println(" Display #" + displayId + " currentFocus=" + currentFocus + + " focusedApp=" + focusedApp); + if (!dc.mWinAddedSinceNullFocus.isEmpty()) { + pw.println(" Windows added in display #" + displayId + " since null focus: " + + dc.mWinAddedSinceNullFocus); + } + if (!dc.mWinRemovedSinceNullFocus.isEmpty()) { + pw.println(" Windows removed in display #" + displayId + " since null focus: " + + dc.mWinRemovedSinceNullFocus); + } + pw.println(" Tasks in top down Z order:"); + dc.forAllTaskDisplayAreas(tda -> { + tda.dump(pw, " ", false /* dumpAll */); + }); + dc.getInputMonitor().dump(pw, " "); + pw.println(); + dc.forAllWindows(w -> { + if ((currentFocus != null && Objects.equals(w.mAttrs.packageName, + currentFocus.mAttrs.packageName)) || (focusedApp != null + && Objects.equals(w.mAttrs.packageName, focusedApp.packageName))) { + relatedWindows.add(w); + } + }, true /* traverseTopToBottom */); + } + if (windowState != null && !relatedWindows.contains(windowState)) { + relatedWindows.add(windowState); + } + mRoot.dumpWindowsNoHeader(pw, true /* dumpAll */, relatedWindows); + pw.println(); + pw.close(); + mLastANRState = sw.toString(); + + mH.removeMessages(H.RESET_ANR_MESSAGE); + mH.sendEmptyMessageDelayed(H.RESET_ANR_MESSAGE, LAST_ANR_LIFETIME_DURATION_MSECS); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + PriorityDump.dump(mPriorityDumper, fd, pw, args); + } + + @NeverCompile // Avoid size overhead of debugging code. + private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + boolean dumpAll = false; + + int opti = 0; + while (opti < args.length) { + String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + if ("-a".equals(opt)) { + dumpAll = true; + } else if ("-h".equals(opt)) { + pw.println("Window manager dump options:"); + pw.println(" [-a] [-h] [cmd] ..."); + pw.println(" cmd may be one of:"); + pw.println(" l[astanr]: last ANR information"); + pw.println(" p[policy]: policy state"); + pw.println(" a[animator]: animator state"); + pw.println(" s[essions]: active sessions"); + pw.println(" surfaces: active surfaces (debugging enabled only)"); + pw.println(" d[isplays]: active display contents"); + pw.println(" t[okens]: token list"); + pw.println(" w[indows]: window list"); + pw.println(" a11y[accessibility]: accessibility-related state"); + pw.println(" package-config: installed packages having app-specific config"); + pw.println(" trace: print trace status and write Winscope trace to file"); + pw.println(" cmd may also be a NAME to dump windows. NAME may"); + pw.println(" be a partial substring in a window name, a"); + pw.println(" Window hex object identifier, or"); + pw.println(" \"all\" for all windows, or"); + pw.println(" \"visible\" for the visible windows."); + pw.println(" \"visible-apps\" for the visible app windows."); + pw.println(" -a: include all available server state."); + pw.println(" --proto: output dump in protocol buffer format."); + return; + } else { + pw.println("Unknown argument: " + opt + "; use -h for help"); + } + } + + if (useProto) { + final ProtoOutputStream proto = new ProtoOutputStream(fd); + synchronized (mGlobalLock) { + dumpDebugLocked(proto, WindowTraceLogLevel.ALL); + } + proto.flush(); + return; + } + // Is the caller requesting to dump a particular piece of data? + if (opti < args.length) { + String cmd = args[opti]; + opti++; + if ("lastanr".equals(cmd) || "l".equals(cmd)) { + synchronized (mGlobalLock) { + dumpLastANRLocked(pw); + } + return; + } else if ("policy".equals(cmd) || "p".equals(cmd)) { + synchronized (mGlobalLock) { + dumpPolicyLocked(pw, args); + } + return; + } else if ("animator".equals(cmd) || "a".equals(cmd)) { + synchronized (mGlobalLock) { + dumpAnimatorLocked(pw, true); + } + return; + } else if ("sessions".equals(cmd) || "s".equals(cmd)) { + synchronized (mGlobalLock) { + dumpSessionsLocked(pw); + } + return; + } else if ("displays".equals(cmd) || "d".equals(cmd)) { + synchronized (mGlobalLock) { + mRoot.dumpDisplayContents(pw); + } + return; + } else if ("tokens".equals(cmd) || "t".equals(cmd)) { + synchronized (mGlobalLock) { + dumpTokensLocked(pw, true); + } + return; + } else if ("windows".equals(cmd) || "w".equals(cmd)) { + synchronized (mGlobalLock) { + dumpWindowsLocked(pw, true, null); + } + return; + } else if ("accessibility".equals(cmd) || "a11y".equals(cmd)) { + synchronized (mGlobalLock) { + dumpAccessibilityLocked(pw); + } + return; + } else if ("all".equals(cmd)) { + synchronized (mGlobalLock) { + dumpWindowsLocked(pw, true, null); + } + return; + } else if ("containers".equals(cmd)) { + synchronized (mGlobalLock) { + mRoot.dumpChildrenNames(pw, ""); + pw.println(" "); + mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */); + } + return; + } else if ("trace".equals(cmd)) { + dumpTraceStatus(pw); + return; + } else if ("logging".equals(cmd)) { + dumpLogStatus(pw); + return; + } else if ("refresh".equals(cmd)) { + dumpHighRefreshRateBlacklist(pw); + return; + } else if ("constants".equals(cmd)) { + mConstants.dump(pw); + return; + } else if ("package-config".equals(cmd)) { + mAtmService.dumpInstalledPackagesConfig(pw); + return; + } else { + // Dumping a single name? + if (!dumpWindows(pw, cmd, dumpAll)) { + pw.println("Bad window command, or no windows match: " + cmd); + pw.println("Use -h for help."); + } + return; + } + } + + synchronized (mGlobalLock) { + pw.println(); + final String separator = "---------------------------------------------------------" + + "----------------------"; + if (dumpAll) { + pw.println(separator); + } + dumpLastANRLocked(pw); + pw.println(); + if (dumpAll) { + pw.println(separator); + } + dumpPolicyLocked(pw, args); + pw.println(); + if (dumpAll) { + pw.println(separator); + } + dumpAnimatorLocked(pw, dumpAll); + pw.println(); + if (dumpAll) { + pw.println(separator); + } + dumpSessionsLocked(pw); + pw.println(); + if (dumpAll) { + pw.println(separator); + } + if (dumpAll) { + pw.println(separator); + } + mRoot.dumpDisplayContents(pw); + pw.println(); + if (dumpAll) { + pw.println(separator); + } + dumpTokensLocked(pw, dumpAll); + pw.println(); + if (dumpAll) { + pw.println(separator); + } + dumpWindowsLocked(pw, dumpAll, null); + if (dumpAll) { + pw.println(separator); + } + dumpTraceStatus(pw); + if (dumpAll) { + pw.println(separator); + } + dumpLogStatus(pw); + if (dumpAll) { + pw.println(separator); + } + dumpHighRefreshRateBlacklist(pw); + if (dumpAll) { + pw.println(separator); + } + mAtmService.dumpInstalledPackagesConfig(pw); + if (dumpAll) { + pw.println(separator); + } + mConstants.dump(pw); + if (dumpAll) { + pw.println(separator); + } + mSystemPerformanceHinter.dump(pw, ""); + mTrustedPresentationListenerController.dump(pw); + mSensitiveContentPackages.dump(pw); + mScreenRecordingCallbackController.dump(pw); + } + } + + // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection). + @Override + public void monitor() { + synchronized (mGlobalLock) { } + } + + // There is an inherent assumption that this will never return null. + // TODO(multi-display): Inspect all the call-points of this method to see if they make sense to + // support non-default display. + DisplayContent getDefaultDisplayContentLocked() { + return mRoot.getDisplayContent(DEFAULT_DISPLAY); + } + + public void onOverlayChanged() { + // Post to display thread so it can get the latest display info. + mH.post(() -> { + synchronized (mGlobalLock) { + mAtmService.deferWindowLayout(); + mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().onOverlayChanged()); + mAtmService.continueWindowLayout(); + } + }); + } + + @Override + public Object getWindowManagerLock() { + return mGlobalLock; + } + + @Override + public int getDockedStackSide() { + return 0; + } + + void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) { + synchronized (mGlobalLock) { + mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays; + mRoot.updateDisplayImePolicyCache(); + } + } + + @VisibleForTesting + void setIsPc(boolean isPc) { + synchronized (mGlobalLock) { + mIsPc = isPc; + } + } + + static int dipToPixel(int dip, DisplayMetrics displayMetrics) { + return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); + } + + public void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener) { + if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, + "registerPinnedTaskListener()")) { + return; + } + if (!mAtmService.mSupportsPictureInPicture) { + return; + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + displayContent.getPinnedTaskController().registerPinnedTaskListener(listener); + } + } + + @Override + public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) { + enforceRegisterWindowManagerListenersPermission("requestAppKeyboardShortcuts"); + + WindowState focusedWindow = getFocusedWindow(); + if (focusedWindow == null || focusedWindow.mClient == null) { + notifyReceiverWithEmptyBundle(receiver); + return; + } + try { + focusedWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId); + } catch (RemoteException e) { + notifyReceiverWithEmptyBundle(receiver); + } + } + + @Override + public void requestImeKeyboardShortcuts(IResultReceiver receiver, int deviceId) { + enforceRegisterWindowManagerListenersPermission("requestImeKeyboardShortcuts"); + + WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); + if (imeWindow == null || imeWindow.mClient == null) { + notifyReceiverWithEmptyBundle(receiver); + return; + } + try { + imeWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId); + } catch (RemoteException e) { + notifyReceiverWithEmptyBundle(receiver); + } + } + + private void enforceRegisterWindowManagerListenersPermission(String message) { + mContext.enforceCallingOrSelfPermission(REGISTER_WINDOW_MANAGER_LISTENERS, message); + } + + private static void notifyReceiverWithEmptyBundle(IResultReceiver receiver) { + try { + receiver.send(0, Bundle.EMPTY); + } catch (RemoteException e) { + ProtoLog.e(WM_ERROR, "unable to call receiver for empty keyboard shortcuts"); + } + } + + @Override + public void getStableInsets(int displayId, Rect outInsets) throws RemoteException { + synchronized (mGlobalLock) { + getStableInsetsLocked(displayId, outInsets); + } + } + + /** This is used when there's no app info available and shall return the system default.*/ + void getStableInsetsLocked(int displayId, Rect outInsets) { + outInsets.setEmpty(); + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc != null) { + final DisplayInfo di = dc.getDisplayInfo(); + outInsets.set(dc.getDisplayPolicy().getDecorInsetsInfo( + di.rotation, di.logicalWidth, di.logicalHeight).mConfigInsets); + } + } + + // The mouse position tracker will be obsolete after the Pointer Icon Refactor. + // TODO(b/293587049): Remove after the refactoring is fully rolled out. + @Nullable + final MousePositionTracker mMousePositionTracker = + com.android.input.flags.Flags.enablePointerChoreographer() ? null + : new MousePositionTracker(); + + private static class MousePositionTracker implements PointerEventListener { + private boolean mLatestEventWasMouse; + private float mLatestMouseX; + private float mLatestMouseY; + + /** + * The display that the pointer (mouse cursor) is currently shown on. This is updated + * directly by InputManagerService when the pointer display changes. + */ + private int mPointerDisplayId = INVALID_DISPLAY; + + /** + * Update the mouse cursor position as a result of a mouse movement. + * @return true if the position was successfully updated, false otherwise. + */ + boolean updatePosition(int displayId, float x, float y) { + synchronized (this) { + mLatestEventWasMouse = true; + + if (displayId != mPointerDisplayId) { + // The display of the position update does not match the display on which the + // mouse pointer is shown, so do not update the position. + return false; + } + mLatestMouseX = x; + mLatestMouseY = y; + return true; + } + } + + void setPointerDisplayId(int displayId) { + synchronized (this) { + mPointerDisplayId = displayId; + } + } + + @Override + public void onPointerEvent(MotionEvent motionEvent) { + if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) { + updatePosition(motionEvent.getDisplayId(), motionEvent.getRawX(), + motionEvent.getRawY()); + } else { + synchronized (this) { + mLatestEventWasMouse = false; + } + } + } + }; + + void updatePointerIcon(IWindow client) { + if (mMousePositionTracker == null) { + return; + } + int pointerDisplayId; + float mouseX, mouseY; + + synchronized(mMousePositionTracker) { + if (!mMousePositionTracker.mLatestEventWasMouse) { + return; + } + mouseX = mMousePositionTracker.mLatestMouseX; + mouseY = mMousePositionTracker.mLatestMouseY; + pointerDisplayId = mMousePositionTracker.mPointerDisplayId; + } + + synchronized (mGlobalLock) { + if (mDragDropController.dragDropActiveLocked()) { + // Drag cursor overrides the app cursor. + return; + } + WindowState callingWin = windowForClientLocked(null, client, false); + if (callingWin == null) { + ProtoLog.w(WM_ERROR, "Bad requesting window %s", client); + return; + } + final DisplayContent displayContent = callingWin.getDisplayContent(); + if (displayContent == null) { + return; + } + if (pointerDisplayId != displayContent.getDisplayId()) { + // Do not let the pointer icon be updated by a window on a different display. + return; + } + WindowState windowUnderPointer = + displayContent.getTouchableWinAtPointLocked(mouseX, mouseY); + if (windowUnderPointer != callingWin) { + return; + } + try { + windowUnderPointer.mClient.updatePointerIcon( + windowUnderPointer.translateToWindowX(mouseX), + windowUnderPointer.translateToWindowY(mouseY)); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "unable to update pointer icon"); + } + } + } + + void restorePointerIconLocked(DisplayContent displayContent, float latestX, float latestY) { + if (mMousePositionTracker == null) { + return; + } + // Mouse position tracker has not been getting updates while dragging, update it now. + if (!mMousePositionTracker.updatePosition( + displayContent.getDisplayId(), latestX, latestY)) { + // The mouse position could not be updated, so ignore this request. + return; + } + + WindowState windowUnderPointer = + displayContent.getTouchableWinAtPointLocked(latestX, latestY); + if (windowUnderPointer != null) { + try { + windowUnderPointer.mClient.updatePointerIcon( + windowUnderPointer.translateToWindowX(latestX), + windowUnderPointer.translateToWindowY(latestY)); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "unable to restore pointer icon"); + } + } else { + mContext.getSystemService(InputManager.class) + .setPointerIconType(PointerIcon.TYPE_DEFAULT); + } + } + void setMousePointerDisplayId(int displayId) { + if (mMousePositionTracker == null) { + return; + } + mMousePositionTracker.setPointerDisplayId(displayId); + } + + /** + * Update a tap exclude region in the window identified by the provided id. Touches down on this + * region will not: + *

    + *
  1. Switch focus to this window.
  2. + *
  3. Move the display of this window to top.
  4. + *
  5. Send the touch events to this window.
  6. + *
+ * Passing an invalid region will remove the area from the exclude region of this window. + */ + void updateTapExcludeRegion(IWindow client, Region region) { + synchronized (mGlobalLock) { + final WindowState callingWin = windowForClientLocked(null, client, false); + if (callingWin == null) { + ProtoLog.w(WM_ERROR, "Bad requesting window %s", client); + return; + } + callingWin.updateTapExcludeRegion(region); + } + } + + /** + * Forwards a scroll capture request to the appropriate window, if available. + * + * @param displayId the display for the request + * @param behindClient token for a window, used to filter the search to windows behind it + * @param taskId specifies the id of a task the result must belong to or -1 to match any task + * @param listener to receive the response + */ + public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId, + IScrollCaptureResponseListener listener) { + if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + final long token = Binder.clearCallingIdentity(); + try { + ScrollCaptureResponse.Builder responseBuilder = new ScrollCaptureResponse.Builder(); + synchronized (mGlobalLock) { + DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + ProtoLog.e(WM_ERROR, + "Invalid displayId for requestScrollCapture: %d", displayId); + responseBuilder.setDescription(String.format("bad displayId: %d", displayId)); + listener.onScrollCaptureResponse(responseBuilder.build()); + return; + } + WindowState topWindow = null; + if (behindClient != null) { + topWindow = windowForClientLocked(null, behindClient, /* throwOnError*/ false); + } + WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId); + if (targetWindow == null) { + responseBuilder.setDescription("findScrollCaptureTargetWindow returned null"); + listener.onScrollCaptureResponse(responseBuilder.build()); + return; + } + try { + // Forward to the window for handling, which will respond using the callback. + targetWindow.mClient.requestScrollCapture(listener); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, + "requestScrollCapture: caught exception dispatching to window." + + "token=%s", targetWindow.mClient.asBinder()); + responseBuilder.setWindowTitle(targetWindow.getName()); + responseBuilder.setPackageName(targetWindow.getOwningPackage()); + responseBuilder.setDescription(String.format("caught exception: %s", e)); + listener.onScrollCaptureResponse(responseBuilder.build()); + } + } + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, + "requestScrollCapture: caught exception dispatching callback: %s", e); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public int getWindowingMode(int displayId) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getWindowingMode()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, + "Attempted to get windowing mode of a display that does not exist: %d", + displayId); + return WindowConfiguration.WINDOWING_MODE_UNDEFINED; + } + return mDisplayWindowSettings.getWindowingModeLocked(displayContent); + } + } + + @Override + public void setWindowingMode(int displayId, int mode) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setWindowingMode()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, + "Attempted to set windowing mode to a display that does not exist: %d", + displayId); + return; + } + + int lastWindowingMode = displayContent.getWindowingMode(); + mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode); + + displayContent.reconfigureDisplayLocked(); + + if (lastWindowingMode != displayContent.getWindowingMode()) { + // reconfigure won't detect this change in isolation because the windowing mode + // is already set on the display, so fire off a new config now. + displayContent.sendNewConfiguration(); + // Now that all configurations are updated, execute pending transitions. + displayContent.executeAppTransition(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public @RemoveContentMode int getRemoveContentMode(int displayId) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getRemoveContentMode()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, + "Attempted to get remove mode of a display that does not exist: %d", + displayId); + return REMOVE_CONTENT_MODE_UNDEFINED; + } + return mDisplayWindowSettings.getRemoveContentModeLocked(displayContent); + } + } + + @Override + public void setRemoveContentMode(int displayId, @RemoveContentMode int mode) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setRemoveContentMode()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, + "Attempted to set remove mode to a display that does not exist: %d", + displayId); + return; + } + + mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode); + displayContent.reconfigureDisplayLocked(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean shouldShowWithInsecureKeyguard(int displayId) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowWithInsecureKeyguard()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to get flag of a display that does not exist: %d", + displayId); + return false; + } + return mDisplayWindowSettings.shouldShowWithInsecureKeyguardLocked(displayContent); + } + } + + @Override + public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, + "setShouldShowWithInsecureKeyguard()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to set flag to a display that does not exist: " + + "%d", displayId); + return; + } + + mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent, + shouldShow); + + displayContent.reconfigureDisplayLocked(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean shouldShowSystemDecors(int displayId) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowSystemDecors()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to get system decors flag of a display that does " + + "not exist: %d", displayId); + return false; + } + return displayContent.supportsSystemDecorations(); + } + } + + @Override + public void setShouldShowSystemDecors(int displayId, boolean shouldShow) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to set system decors flag to a display that " + + "does not exist: %d", displayId); + return; + } + if (!displayContent.isTrusted()) { + throw new SecurityException("Attempted to set system decors flag to an " + + "untrusted virtual display: " + displayId); + } + + mDisplayWindowSettings.setShouldShowSystemDecorsLocked(displayContent, shouldShow); + + displayContent.reconfigureDisplayLocked(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public @DisplayImePolicy int getDisplayImePolicy(int displayId) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getDisplayImePolicy()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + final Map displayImePolicyCache = mDisplayImePolicyCache; + if (!displayImePolicyCache.containsKey(displayId)) { + ProtoLog.w(WM_ERROR, + "Attempted to get IME policy of a display that does not exist: %d", + displayId); + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; + } + return displayImePolicyCache.get(displayId); + } + + @Override + public void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setDisplayImePolicy()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to set IME policy to a display" + + " that does not exist: %d", displayId); + return; + } + if (!displayContent.isTrusted()) { + throw new SecurityException("Attempted to set IME policy to an untrusted " + + "virtual display: " + displayId); + } + + mDisplayWindowSettings.setDisplayImePolicy(displayContent, imePolicy); + + displayContent.reconfigureDisplayLocked(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) + throws RemoteException { + if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) { + throw new SecurityException( + "Requires REGISTER_WINDOW_MANAGER_LISTENERS permission"); + } + mPolicy.registerShortcutKey(shortcutCode, shortcutKeyReceiver); + } + + private final class LocalService extends WindowManagerInternal { + + @Override + public AccessibilityControllerInternal getAccessibilityController() { + return AccessibilityController.getAccessibilityControllerInternal( + WindowManagerService.this); + } + + @Override + public void clearSnapshotCache() { + synchronized (mGlobalLock) { + mTaskSnapshotController.clearSnapshotCache(); + } + } + + @Override + public void requestTraversalFromDisplayManager() { + synchronized (mGlobalLock) { + requestTraversal(); + } + } + + @Override + public void onDisplayManagerReceivedDeviceState(int deviceState) { + mH.post(() -> { + synchronized (mGlobalLock) { + mRoot.onDisplayManagerReceivedDeviceState(deviceState); + } + }); + } + + @Override + public void setMagnificationSpec(int displayId, MagnificationSpec spec) { + synchronized (mGlobalLock) { + if (mAccessibilityController.hasCallbacks()) { + mAccessibilityController.setMagnificationSpec(displayId, spec); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + } + + @Override + public void setFullscreenMagnificationActivated(int displayId, boolean activated) { + synchronized (mGlobalLock) { + if (mAccessibilityController.hasCallbacks()) { + mAccessibilityController + .setFullscreenMagnificationActivated(displayId, activated); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + } + + @Override + public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) { + synchronized (mGlobalLock) { + if (mAccessibilityController.hasCallbacks()) { + mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + } + + @Override + public boolean setMagnificationCallbacks(int displayId, + @Nullable MagnificationCallbacks callbacks) { + synchronized (mGlobalLock) { + return mAccessibilityController.setMagnificationCallbacks(displayId, callbacks); + } + } + + @Override + public void setWindowsForAccessibilityCallback(int displayId, + WindowsForAccessibilityCallback callback) { + synchronized (mGlobalLock) { + mAccessibilityController.setWindowsForAccessibilityCallback(displayId, callback); + } + } + + @Override + public void setInputFilter(IInputFilter filter) { + mInputManager.setInputFilter(filter); + } + + @Override + public IBinder getFocusedWindowToken() { + synchronized (mGlobalLock) { + return mAccessibilityController.getFocusedWindowToken(); + } + } + + // TODO (b/229837707): Delete this method after changing the solution. + @Override + public IBinder getFocusedWindowTokenFromWindowStates() { + synchronized (mGlobalLock) { + final WindowState windowState = getFocusedWindowLocked(); + if (windowState != null) { + return windowState.mClient.asBinder(); + } + return null; + } + } + + @Override + public void moveDisplayToTopIfAllowed(int displayId) { + WindowManagerService.this.moveDisplayToTopIfAllowed(displayId); + } + + @Override + public void requestWindowFocus(IBinder windowToken) { + synchronized (mGlobalLock) { + final InputTarget inputTarget = + WindowManagerService.this.getInputTargetFromWindowTokenLocked(windowToken); + WindowManagerService.this.onPointerDownOutsideFocusLocked(inputTarget); + } + } + + @Override + public boolean isKeyguardLocked() { + return WindowManagerService.this.isKeyguardLocked(); + } + + @Override + public boolean isKeyguardShowingAndNotOccluded() { + return WindowManagerService.this.isKeyguardShowingAndNotOccluded(); + } + + @Override + public boolean isKeyguardSecure(@UserIdInt int userId) { + return mPolicy.isKeyguardSecure(userId); + } + + @Override + public void showGlobalActions() { + WindowManagerService.this.showGlobalActions(); + } + + @Override + public void getWindowFrame(IBinder token, Rect outBounds) { + synchronized (mGlobalLock) { + WindowState windowState = mWindowMap.get(token); + if (windowState != null) { + outBounds.set(windowState.getFrame()); + } else { + outBounds.setEmpty(); + } + } + } + + @Override + public Pair getWindowTransformationMatrixAndMagnificationSpec( + IBinder token) { + return mAccessibilityController + .getWindowTransformationMatrixAndMagnificationSpec(token); + } + + @Override + public void waitForAllWindowsDrawn(Message message, long timeout, int displayId) { + Objects.requireNonNull(message.getTarget()); + final WindowContainer container = displayId == INVALID_DISPLAY + ? mRoot : mRoot.getDisplayContent(displayId); + if (container == null) { + // The waiting container doesn't exist, no need to wait to run the callback. Run and + // return; + message.sendToTarget(); + return; + } + boolean allWindowsDrawn = false; + synchronized (mGlobalLock) { + container.waitForAllWindowsDrawn(); + mWindowPlacerLocked.requestTraversal(); + mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container); + if (container.mWaitingForDrawn.isEmpty()) { + allWindowsDrawn = true; + } else { + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + for (int i = 0; i < container.mWaitingForDrawn.size(); i++) { + traceStartWaitingForWindowDrawn(container.mWaitingForDrawn.get(i)); + } + } + + mWaitingForDrawnCallbacks.put(container, message); + mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout); + checkDrawnWindowsLocked(); + } + } + if (allWindowsDrawn) { + message.sendToTarget(); + } + } + + @Override + public void setForcedDisplaySize(int displayId, int width, int height) { + WindowManagerService.this.setForcedDisplaySize(displayId, width, height); + } + + @Override + public void clearForcedDisplaySize(int displayId) { + WindowManagerService.this.clearForcedDisplaySize(displayId); + } + + @Override + public void addWindowToken(IBinder token, int type, int displayId, + @Nullable Bundle options) { + WindowManagerService.this.addWindowToken(token, type, displayId, options); + } + + @Override + public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit, + int displayId) { + WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit, + displayId); + } + + @Override + public void moveWindowTokenToDisplay(IBinder binder, int displayId) { + WindowManagerService.this.moveWindowTokenToDisplay(binder, displayId); + } + + // TODO(multi-display): currently only used by PWM to notify keyguard transitions as well + // forwarding it to SystemUI for synchronizing status and navigation bar animations. + @Override + public void registerAppTransitionListener(AppTransitionListener listener) { + synchronized (mGlobalLock) { + getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener); + mAtmService.getTransitionController().registerLegacyListener(listener); + } + } + + @Override + public void registerTaskSystemBarsListener(TaskSystemBarsListener listener) { + synchronized (mGlobalLock) { + mTaskSystemBarsListenerController.registerListener(listener); + } + } + + @Override + public void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener) { + synchronized (mGlobalLock) { + mTaskSystemBarsListenerController.unregisterListener(listener); + } + } + + @Override + public void registerKeyguardExitAnimationStartListener( + KeyguardExitAnimationStartListener listener) { + synchronized (mGlobalLock) { + getDefaultDisplayContentLocked().mAppTransition + .registerKeygaurdExitAnimationStartListener(listener); + } + } + + @Override + public void reportPasswordChanged(int userId) { + mKeyguardDisableHandler.updateKeyguardEnabled(userId); + } + + @Override + public int getInputMethodWindowVisibleHeight(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + return dc.getInputMethodWindowVisibleHeight(); + } + } + + @Override + public void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed) { + mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed); + } + + @Override + public void updateInputMethodTargetWindow(@NonNull IBinder imeToken, + @NonNull IBinder imeTargetWindowToken) { + // TODO (b/34628091): Use this method to address the window animation issue. + if (DEBUG_INPUT_METHOD) { + Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken + + " imeTargetWindowToken=" + imeTargetWindowToken); + } + synchronized (mGlobalLock) { + InputTarget imeTarget = + getInputTargetFromWindowTokenLocked(imeTargetWindowToken); + if (imeTarget != null) { + imeTarget.getDisplayContent().updateImeInputAndControlTarget(imeTarget); + } + } + } + + @Override + public boolean isHardKeyboardAvailable() { + synchronized (mGlobalLock) { + return mHardKeyboardAvailable; + } + } + + @Override + public void setOnHardKeyboardStatusChangeListener( + OnHardKeyboardStatusChangeListener listener) { + synchronized (mGlobalLock) { + mHardKeyboardStatusChangeListener = listener; + } + } + + @Override + public void computeWindowsForAccessibility(int displayId) { + mAccessibilityController.performComputeChangedWindowsNot(displayId, true); + } + + @Override + public void setVr2dDisplayId(int vr2dDisplayId) { + if (DEBUG_DISPLAY) { + Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId); + } + synchronized (mGlobalLock) { + mVr2dDisplayId = vr2dDisplayId; + } + } + + @Override + public void registerDragDropControllerCallback(IDragDropCallback callback) { + mDragDropController.registerCallback(callback); + } + + @Override + public void lockNow() { + WindowManagerService.this.lockNow(null); + } + + @Override + public int getWindowOwnerUserId(IBinder token) { + synchronized (mGlobalLock) { + WindowState window = mWindowMap.get(token); + if (window != null) { + return window.mShowUserId; + } + return UserHandle.USER_NULL; + } + } + + @Override + public void setWallpaperShowWhenLocked(IBinder binder, boolean showWhenLocked) { + synchronized (mGlobalLock) { + final WindowToken token = mRoot.getWindowToken(binder); + if (token == null || token.asWallpaperToken() == null) { + ProtoLog.w(WM_ERROR, + "setWallpaperShowWhenLocked: non-existent wallpaper token: %s", binder); + return; + } + token.asWallpaperToken().setShowWhenLocked(showWhenLocked); + } + } + + @Override + public void setWallpaperCropHints(IBinder binder, SparseArray cropHints) { + synchronized (mGlobalLock) { + final WindowToken token = mRoot.getWindowToken(binder); + if (token == null || token.asWallpaperToken() == null) { + ProtoLog.w(WM_ERROR, + "setWallpaperCropHints: non-existent wallpaper token: %s", binder); + return; + } + token.asWallpaperToken().setCropHints(cropHints); + } + } + + @Override + public void setWallpaperCropUtils(WallpaperCropUtils wallpaperCropUtils) { + mRoot.getDisplayContent(DEFAULT_DISPLAY).mWallpaperController + .setWallpaperCropUtils(wallpaperCropUtils); + } + + @Override + public boolean isUidFocused(int uid) { + synchronized (mGlobalLock) { + for (int i = mRoot.getChildCount() - 1; i >= 0; i--) { + final DisplayContent displayContent = mRoot.getChildAt(i); + if (displayContent.mCurrentFocus != null + && uid == displayContent.mCurrentFocus.getOwningUid()) { + return true; + } + } + return false; + } + } + + @Override + public @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken, + int uid, int pid, int displayId) { + if (displayId == Display.INVALID_DISPLAY) { + return ImeClientFocusResult.INVALID_DISPLAY_ID; + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent(); + InputTarget target = getInputTargetFromWindowTokenLocked(windowToken); + if (target == null) { + return ImeClientFocusResult.NOT_IME_TARGET_WINDOW; + } + final int tokenDisplayId = target.getDisplayContent().getDisplayId(); + if (tokenDisplayId != displayId) { + Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch." + + " from client: " + displayId + + " from window: " + tokenDisplayId); + return ImeClientFocusResult.DISPLAY_ID_MISMATCH; + } + if (displayContent == null + || displayContent.getDisplayId() != displayId + || !displayContent.hasAccess(uid)) { + return ImeClientFocusResult.INVALID_DISPLAY_ID; + } + + if (target.isInputMethodClientFocus(uid, pid)) { + return ImeClientFocusResult.HAS_IME_FOCUS; + } + // Okay, how about this... what is the current focus? + // It seems in some cases we may not have moved the IM + // target window, such as when it was in a pop-up window, + // so let's also look at the current focus. (An example: + // go to Gmail, start searching so the keyboard goes up, + // press home. Sometimes the IME won't go down.) + // Would be nice to fix this more correctly, but it's + // way at the end of a release, and this should be good enough. + final WindowState currentFocus = displayContent.mCurrentFocus; + if (currentFocus != null && currentFocus.mSession.mUid == uid + && currentFocus.mSession.mPid == pid) { + return currentFocus.canBeImeTarget() ? ImeClientFocusResult.HAS_IME_FOCUS + : ImeClientFocusResult.NOT_IME_TARGET_WINDOW; + } + } + return ImeClientFocusResult.NOT_IME_TARGET_WINDOW; + } + + @Override + public void showImePostLayout(IBinder imeTargetWindowToken, + @NonNull ImeTracker.Token statsToken) { + synchronized (mGlobalLock) { + InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken); + if (imeTarget == null) { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); + return; + } + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); + + Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0); + final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget(); + imeTarget = controlTarget.getWindow(); + // If InsetsControlTarget doesn't have a window, it's using remoteControlTarget + // which is controlled by default display + final DisplayContent dc = imeTarget != null + ? imeTarget.getDisplayContent() : getDefaultDisplayContentLocked(); + dc.getInsetsStateController().getImeSourceProvider() + .scheduleShowImePostLayout(controlTarget, statsToken); + } + } + + @Override + public void hideIme(IBinder imeTargetWindowToken, int displayId, + @NonNull ImeTracker.Token statsToken) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.hideIme"); + synchronized (mGlobalLock) { + WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); + ProtoLog.d(WM_DEBUG_IME, "hideIme target: %s ", imeTarget); + DisplayContent dc = mRoot.getDisplayContent(displayId); + if (imeTarget != null) { + imeTarget = imeTarget.getImeControlTarget().getWindow(); + if (imeTarget != null) { + dc = imeTarget.getDisplayContent(); + } + // If there was a pending IME show(), reset it as IME has been + // requested to be hidden. + dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout(); + } + if (dc != null && dc.getImeTarget(IME_TARGET_CONTROL) != null) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); + ProtoLog.d(WM_DEBUG_IME, "hideIme Control target: %s ", + dc.getImeTarget(IME_TARGET_CONTROL)); + dc.getImeTarget(IME_TARGET_CONTROL).hideInsets(WindowInsets.Type.ime(), + true /* fromIme */, statsToken); + } else { + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET); + } + if (dc != null) { + dc.getInsetsStateController().getImeSourceProvider().setImeShowing(false); + } + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + @Override + public boolean isUidAllowedOnDisplay(int displayId, int uid) { + if (displayId == Display.DEFAULT_DISPLAY) { + return true; + } + if (displayId == Display.INVALID_DISPLAY) { + return false; + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + return displayContent != null && displayContent.hasAccess(uid); + } + } + + @Override + public int getDisplayIdForWindow(IBinder windowToken) { + synchronized (mGlobalLock) { + final WindowState window = mWindowMap.get(windowToken); + if (window != null) { + return window.getDisplayContent().getDisplayId(); + } + return Display.INVALID_DISPLAY; + } + } + + @Override + public int getTopFocusedDisplayId() { + synchronized (mGlobalLock) { + return mRoot.getTopFocusedDisplayContent().getDisplayId(); + } + } + + @Override + public Context getTopFocusedDisplayUiContext() { + synchronized (mGlobalLock) { + return mRoot.getTopFocusedDisplayContent().getDisplayUiContext(); + } + } + + @Override + public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType, + boolean supported) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mDisplayWindowSettings.setHomeSupportedOnDisplayLocked( + displayUniqueId, displayType, supported); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public boolean isHomeSupportedOnDisplay(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + ProtoLog.w(WM_ERROR, "Attempted to get home support flag of a display that " + + "does not exist: %d", displayId); + return false; + } + return displayContent.isHomeSupported(); + } + } + + @Override + public void clearDisplaySettings(String displayUniqueId, int displayType) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mDisplayWindowSettings.clearDisplaySettings(displayUniqueId, displayType); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public @DisplayImePolicy int getDisplayImePolicy(int displayId) { + return WindowManagerService.this.getDisplayImePolicy(displayId); + } + + @Override + public void addRefreshRateRangeForPackage(@NonNull String packageName, + float minRefreshRate, float maxRefreshRate) { + synchronized (mGlobalLock) { + mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy() + .addRefreshRateRangeForPackage( + packageName, minRefreshRate, maxRefreshRate)); + } + } + + @Override + public void removeRefreshRateRangeForPackage(@NonNull String packageName) { + synchronized (mGlobalLock) { + mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy() + .removeRefreshRateRangeForPackage(packageName)); + } + } + + @Override + public boolean isTouchOrFaketouchDevice() { + synchronized (mGlobalLock) { + if (mIsTouchDevice && !mIsFakeTouchDevice) { + throw new IllegalStateException( + "touchscreen supported device must report faketouch."); + } + return mIsFakeTouchDevice; + } + } + + @Override + public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) { + return mKeyInterceptionInfoForToken.get(inputToken); + } + + @Override + public void setAccessibilityIdToSurfaceMetadata( + IBinder windowToken, int accessibilityWindowId) { + synchronized (mGlobalLock) { + final WindowState state = mWindowMap.get(windowToken); + if (state == null) { + Slog.w(TAG, "Cannot find window which accessibility connection is added to"); + return; + } + mTransaction.setMetadata(state.mSurfaceControl, + SurfaceControl.METADATA_ACCESSIBILITY_ID, accessibilityWindowId).apply(); + } + } + + @Override + public String getWindowName(@NonNull IBinder binder) { + synchronized (mGlobalLock) { + final WindowState w = mWindowMap.get(binder); + return w != null ? w.getName() : null; + } + } + + @Override + public ImeTargetInfo onToggleImeRequested(boolean show, IBinder focusedToken, + IBinder requestToken, int displayId) { + final String focusedWindowName; + final String requestWindowName; + final String imeControlTargetName; + final String imeLayerTargetName; + final String imeSurfaceParentName; + synchronized (mGlobalLock) { + + //ccy liutianzuo + //增加一个焦点回调方法到目标位置 + if(mFocusChangeListener != null){ + mFocusChangeListener.onFocusChangeListener(show); + } + //end ccy + + final WindowState focusedWin = mWindowMap.get(focusedToken); + focusedWindowName = focusedWin != null ? focusedWin.getName() : "null"; + final WindowState requestWin = mWindowMap.get(requestToken); + requestWindowName = requestWin != null ? requestWin.getName() : "null"; + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc != null) { + final InsetsControlTarget controlTarget = dc.getImeTarget(IME_TARGET_CONTROL); + if (controlTarget != null) { + final WindowState w = InsetsControlTarget.asWindowOrNull(controlTarget); + imeControlTargetName = w != null ? w.getName() : controlTarget.toString(); + } else { + imeControlTargetName = "null"; + } + final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING); + imeLayerTargetName = target != null ? target.getWindow().getName() : "null"; + final SurfaceControl imeParent = dc.mInputMethodSurfaceParent; + imeSurfaceParentName = imeParent != null ? imeParent.toString() : "null"; + if (show) { + dc.onShowImeRequested(); + } + } else { + imeControlTargetName = imeLayerTargetName = imeSurfaceParentName = "no-display"; + } + } + return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName, + imeLayerTargetName, imeSurfaceParentName); + } + + @Override + public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) { + return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken); + } + + @Override + public void addTrustedTaskOverlay(int taskId, + SurfaceControlViewHost.SurfacePackage overlay) { + if (overlay == null) { + throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId); + } + synchronized (mGlobalLock) { + if (overlay.getSurfaceControl() == null + || !overlay.getSurfaceControl().isValid()) { + throw new IllegalArgumentException( + "Invalid overlay surfacecontrol passed in for task=" + taskId); + } + final Task task = mRoot.getRootTask(taskId); + if (task == null) { + throw new IllegalArgumentException("no task with taskId" + taskId); + } + task.addTrustedOverlay(overlay, task.getTopVisibleAppMainWindow()); + } + } + + @Override + public void removeTrustedTaskOverlay(int taskId, + SurfaceControlViewHost.SurfacePackage overlay) { + if (overlay == null) { + throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId); + } + synchronized (mGlobalLock) { + if (overlay.getSurfaceControl() == null + || !overlay.getSurfaceControl().isValid()) { + throw new IllegalArgumentException( + "Invalid overlay surfacecontrol passed in for task=" + taskId); + } + final Task task = mRoot.getRootTask(taskId); + if (task == null) { + throw new IllegalArgumentException("no task with taskId" + taskId); + } + task.removeTrustedOverlay(overlay); + } + } + + @Override + public SurfaceControl getHandwritingSurfaceForDisplay(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + Slog.e(TAG, "Failed to create a handwriting surface on display: " + + displayId + " - DisplayContent not found."); + return null; + } + final SurfaceControl inputOverlay = dc.getInputOverlayLayer(); + if (inputOverlay == null) { + Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId + + " - Input overlay layer is not initialized."); + return null; + } + // TODO(b/210039666): Use a method like add/removeDisplayOverlay if available. + return makeSurfaceBuilder(dc.getSession()) + .setContainerLayer() + .setName("IME Handwriting Surface") + .setCallsite("getHandwritingSurfaceForDisplay") + .setParent(inputOverlay) + .build(); + } + } + + @Override + public boolean isPointInsideWindow(@NonNull IBinder windowToken, int displayId, + float displayX, float displayY) { + synchronized (mGlobalLock) { + final WindowState w = mWindowMap.get(windowToken); + if (w == null || w.getDisplayId() != displayId) { + return false; + } + + return w.getBounds().contains((int) displayX, (int) displayY); + } + } + + @Override + public boolean setContentRecordingSession( + @Nullable ContentRecordingSession incomingSession) { + synchronized (mGlobalLock) { + // Allow the controller to handle teardown of a non-task session. + if (incomingSession == null + || incomingSession.getContentToRecord() != RECORD_CONTENT_TASK) { + mContentRecordingController.setContentRecordingSessionLocked(incomingSession, + WindowManagerService.this); + return true; + } + // For a task session, find the activity identified by the launch cookie. + final WindowContainerInfo wci = getTaskWindowContainerInfoForLaunchCookie( + incomingSession.getTokenToRecord()); + if (wci == null) { + Slog.w(TAG, "Handling a new recording session; unable to find the " + + "WindowContainerToken"); + return false; + } + // Replace the launch cookie in the session details with the task's + // WindowContainerToken. + incomingSession.setTokenToRecord(wci.getToken().asBinder()); + // Also replace the UNKNOWN target UID with the actual UID. + incomingSession.setTargetUid(wci.getUid()); + mContentRecordingController.setContentRecordingSessionLocked(incomingSession, + WindowManagerService.this); + return true; + } + } + + @Override + public SurfaceControl getA11yOverlayLayer(int displayId) { + synchronized (mGlobalLock) { + DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc != null) { + return dc.getA11yOverlayLayer(); + } + } + return null; + } + + @Override + public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs, + ScreenCapture.ScreenCaptureListener listener) { + WindowManagerService.this.captureDisplay(displayId, captureArgs, listener); + } + + @Override + public boolean hasNavigationBar(int displayId) { + return WindowManagerService.this.hasNavigationBar(displayId); + } + + @Override + public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) { + synchronized (mGlobalLock) { + mImeTargetChangeListener = listener; + } + } + + @Override + public void setOrientationRequestPolicy(boolean respected, + int[] fromOrientations, int[] toOrientations) { + synchronized (mGlobalLock) { + WindowManagerService.this.setOrientationRequestPolicy(respected, + fromOrientations, toOrientations); + } + } + + @Override + public @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken) { + InputTarget inputTarget = WindowManagerService.this.getInputTargetFromToken(inputToken); + return inputTarget == null ? null : inputTarget.getWindowToken(); + } + + @Override + public void addBlockScreenCaptureForApps(ArraySet packageInfos) { + synchronized (mGlobalLock) { + boolean modified = + mSensitiveContentPackages.addBlockScreenCaptureForApps(packageInfos); + if (modified) { + WindowManagerService.this.refreshScreenCaptureDisabled(); + } + } + } + + @Override + public void removeBlockScreenCaptureForApps(ArraySet packageInfos) { + synchronized (mGlobalLock) { + boolean modified = + mSensitiveContentPackages.removeBlockScreenCaptureForApps(packageInfos); + if (modified) { + WindowManagerService.this.refreshScreenCaptureDisabled(); + } + } + } + + @Override + public void clearBlockedApps() { + synchronized (mGlobalLock) { + boolean modified = mSensitiveContentPackages.clearBlockedApps(); + if (modified) { + WindowManagerService.this.refreshScreenCaptureDisabled(); + } + } + } + + @Override + public boolean moveFocusToTopEmbeddedWindowIfNeeded() { + synchronized (mGlobalLock) { + final WindowState focusedWindow = getFocusedWindow(); + if (focusedWindow == null) { + return false; + } + + if (moveFocusToTopEmbeddedWindow(focusedWindow)) { + // Sync the input transactions to ensure the input focus updates as well. + syncInputTransactions(false); + return true; + } + + return false; + } + } + + @Override + public ScreenCapture.ScreenshotHardwareBuffer takeAssistScreenshot() { + // WMS.takeAssistScreenshot takes care of the locking. + return WindowManagerService.this.takeAssistScreenshot(); + } + } + + private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy { + + @Override + public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) { + synchronized (mGlobalLock) { + final WindowState imeTargetWindow = mWindowMap.get(imeTarget); + if (imeTargetWindow == null) { + return false; + } + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to show ime screenshot"); + return false; + } + + dc.showImeScreenshot(imeTargetWindow); + return true; + } + } + @Override + public boolean removeImeScreenshot(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + Slog.w(TAG, "Invalid displayId:" + displayId + + ", fail to remove ime screenshot"); + return false; + } + dc.removeImeSurfaceImmediately(); + } + return true; + } + } + + void registerAppFreezeListener(AppFreezeListener listener) { + if (!mAppFreezeListeners.contains(listener)) { + mAppFreezeListeners.add(listener); + } + } + + void unregisterAppFreezeListener(AppFreezeListener listener) { + mAppFreezeListeners.remove(listener); + } + + /** Called to inform window manager if non-Vr UI shoul be disabled or not. */ + public void disableNonVrUi(boolean disable) { + synchronized (mGlobalLock) { + // Allow alert window notifications to be shown if non-vr UI is enabled. + final boolean showAlertWindowNotifications = !disable; + if (showAlertWindowNotifications == mShowAlertWindowNotifications) { + return; + } + mShowAlertWindowNotifications = showAlertWindowNotifications; + + for (int i = mSessions.size() - 1; i >= 0; --i) { + final Session s = mSessions.valueAt(i); + s.setShowingAlertWindowNotificationAllowed(mShowAlertWindowNotifications); + } + } + } + + boolean hasWideColorGamutSupport() { + return mHasWideColorGamutSupport && + SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1; + } + + boolean hasHdrSupport() { + return mHasHdrSupport && hasWideColorGamutSupport(); + } + + void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) { + if (!win.hideNonSystemOverlayWindowsWhenVisible() + && !mHidingNonSystemOverlayWindows.contains(win)) { + return; + } + final boolean systemAlertWindowsHidden = !mHidingNonSystemOverlayWindows.isEmpty(); + if (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible()) { + if (!mHidingNonSystemOverlayWindows.contains(win)) { + mHidingNonSystemOverlayWindows.add(win); + } + } else { + mHidingNonSystemOverlayWindows.remove(win); + } + + final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty(); + + if (systemAlertWindowsHidden == hideSystemAlertWindows) { + return; + } + + mRoot.forAllWindows((w) -> { + w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); + }, false /* traverseTopToBottom */); + } + + /** Called from Accessibility Controller to apply magnification spec */ + public void applyMagnificationSpecLocked(int displayId, MagnificationSpec spec) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.applyMagnificationSpec(spec); + } + } + + SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) { + return mSurfaceControlFactory.apply(s); + } + + /** + * Called when the state of lock task mode changes. This should be used to disable immersive + * mode confirmation. + * + * @param lockTaskState the new lock task mode state. One of + * {@link ActivityManager#LOCK_TASK_MODE_NONE}, + * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, + * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. + */ + void onLockTaskStateChanged(int lockTaskState) { + // TODO: pass in displayId to determine which display the lock task state changed + synchronized (mGlobalLock) { + mRoot.forAllDisplayPolicies(p -> p.onLockTaskStateChangedLw(lockTaskState)); + } + } + + @Override + public void syncInputTransactions(boolean waitForAnimations) { + final long token = Binder.clearCallingIdentity(); + try { + if (waitForAnimations) { + waitForAnimationsToComplete(); + } + + // Collect all input transactions from all displays to make sure we could sync all input + // windows at same time. + final SurfaceControl.Transaction t = mTransactionFactory.get(); + synchronized (mGlobalLock) { + mWindowPlacerLocked.performSurfacePlacementIfScheduled(); + mRoot.forAllDisplays(displayContent -> + displayContent.getInputMonitor().updateInputWindowsImmediately(t)); + } + + CountDownLatch countDownLatch = new CountDownLatch(1); + t.addWindowInfosReportedListener(countDownLatch::countDown).apply(); + countDownLatch.await(SYNC_INPUT_TRANSACTIONS_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException exception) { + Slog.e(TAG_WM, "Exception thrown while waiting for window infos to be reported", + exception); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Wait until all container animations and surface operations behalf of WindowManagerService + * complete. + */ + private void waitForAnimationsToComplete() { + synchronized (mGlobalLock) { + long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; + // This could prevent if there is no container animation, we still have to apply the + // pending transaction and exit waiting. + mAnimator.mNotifyWhenNoAnimation = true; + boolean animateStarting = false; + while (timeoutRemaining > 0) { + // Waiting until all starting windows has finished animating. + animateStarting = !mAtmService.getTransitionController().isShellTransitionsEnabled() + && mRoot.forAllActivities(ActivityRecord::hasStartingWindow); + boolean isAnimating = mAnimator.isAnimationScheduled() + || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL) + || animateStarting; + if (!isAnimating) { + // isAnimating is a legacy transition query and will be removed, so also add + // a check for whether this is in a shell-transition when not using legacy. + if (!mAtmService.getTransitionController().inTransition()) { + break; + } + } + long startTime = System.currentTimeMillis(); + try { + mGlobalLock.wait(timeoutRemaining); + } catch (InterruptedException e) { + } + timeoutRemaining -= (System.currentTimeMillis() - startTime); + } + mAnimator.mNotifyWhenNoAnimation = false; + + WindowContainer animatingContainer; + animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN, + ANIMATION_TYPE_ALL); + if (mAnimator.isAnimationScheduled() || animatingContainer != null || animateStarting) { + Slog.w(TAG, "Timed out waiting for animations to complete," + + " animatingContainer=" + animatingContainer + + " animationType=" + SurfaceAnimator.animationTypeToString( + animatingContainer != null + ? animatingContainer.mSurfaceAnimator.getAnimationType() + : SurfaceAnimator.ANIMATION_TYPE_NONE) + + " animateStarting=" + animateStarting); + } + } + } + + void onAnimationFinished() { + synchronized (mGlobalLock) { + mGlobalLock.notifyAll(); + } + } + + private void onPointerDownOutsideFocusLocked(InputTarget t) { + if (t == null || !t.receiveFocusFromTapOutside()) { + // If the window that received the input event cannot receive keys, don't move the + // display it's on to the top since that window won't be able to get focus anyway. + return; + } + if (mRecentsAnimationController != null + && mRecentsAnimationController.getTargetAppMainWindow() == t) { + // If there is an active recents animation and touched window is the target, then ignore + // the touch. The target already handles touches using its own input monitor and we + // don't want to trigger any lifecycle changes from focusing another window. + // TODO(b/186770026): We should remove this once we support multiple resumed activities + // while in overview + return; + } + final WindowState w = t.getWindowState(); + if (w != null) { + final Task task = w.getTask(); + if (task != null && w.mTransitionController.isTransientHide(task)) { + // Don't disturb transient animation by accident touch. + return; + } + } + + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s", + t); + if (mFocusedInputTarget != t && mFocusedInputTarget != null) { + mFocusedInputTarget.handleTapOutsideFocusOutsideSelf(); + } + // Trigger Activity#onUserLeaveHint() if the order change of task pauses any activities. + mAtmService.mTaskSupervisor.mUserLeaving = true; + t.handleTapOutsideFocusInsideSelf(); + mAtmService.mTaskSupervisor.mUserLeaving = false; + } + + @VisibleForTesting + void handleTaskFocusChange(Task task, ActivityRecord touchedActivity) { + if (task == null) { + return; + } + + // We ignore root home task since we don't want root home task to move to front when + // touched. Specifically, in freeform we don't want tapping on home to cause the freeform + // apps to go behind home. See b/117376413 + if (task.isActivityTypeHome()) { + // Only ignore root home task if the requested focus home Task is in the same + // TaskDisplayArea as the current focus Task. + TaskDisplayArea homeTda = task.getDisplayArea(); + WindowState curFocusedWindow = getFocusedWindow(); + if (curFocusedWindow != null && homeTda != null + && curFocusedWindow.isDescendantOf(homeTda)) { + return; + } + } + + mAtmService.setFocusedTask(task.mTaskId, touchedActivity); + } + + @VisibleForTesting + static class WindowContainerInfo { + private final int mUid; + @NonNull private final WindowContainerToken mToken; + + private WindowContainerInfo(int uid, @NonNull WindowContainerToken token) { + this.mUid = uid; + this.mToken = token; + } + + public int getUid() { + return mUid; + } + + @NonNull + public WindowContainerToken getToken() { + return mToken; + } + } + + /** + * Retrieve the {@link WindowContainerInfo} of the task that contains the activity started with + * the given launch cookie. + * + * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an + * activity + * @return a token representing the task containing the activity started with the given launch + * cookie, or {@code null} if the token couldn't be found. + */ + @VisibleForTesting + @Nullable + WindowContainerInfo getTaskWindowContainerInfoForLaunchCookie(@NonNull IBinder launchCookie) { + // Find the activity identified by the launch cookie. + final ActivityRecord targetActivity = + mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie); + if (targetActivity == null) { + Slog.w(TAG, "Unable to find the activity for this launch cookie"); + return null; + } + if (targetActivity.getTask() == null) { + Slog.w(TAG, "Unable to find the task for this launch cookie"); + return null; + } + WindowContainerToken taskWindowContainerToken = + targetActivity.getTask().mRemoteToken.toWindowContainerToken(); + if (taskWindowContainerToken == null) { + Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName()); + return null; + } + return new WindowContainerInfo(targetActivity.getUid(), taskWindowContainerToken); + } + + /** + * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY. + */ + private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) { + if ((flags & FLAG_SLIPPERY) == 0) { + return flags; + } + final int permissionResult = mContext.checkPermission( + android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, callingPid, callingUid); + if (permissionResult != PackageManager.PERMISSION_GRANTED) { + Slog.w(TAG, "Removing FLAG_SLIPPERY from '" + windowName + + "' because it doesn't have ALLOW_SLIPPERY_TOUCHES permission"); + return flags & ~FLAG_SLIPPERY; + } + return flags; + } + + /** + * You need MONITOR_INPUT permission to be able to set INPUT_FEATURE_SPY. + */ + private int sanitizeSpyWindow(int inputFeatures, String windowName, int callingUid, + int callingPid) { + if ((inputFeatures & INPUT_FEATURE_SPY) == 0) { + return inputFeatures; + } + final int permissionResult = mContext.checkPermission( + permission.MONITOR_INPUT, callingPid, callingUid); + if (permissionResult != PackageManager.PERMISSION_GRANTED) { + throw new IllegalArgumentException("Cannot use INPUT_FEATURE_SPY from '" + windowName + + "' because it doesn't the have MONITOR_INPUT permission"); + } + return inputFeatures; + } + + /** + * Assigns an InputChannel to a SurfaceControl and configures it to receive + * touch input according to it's on-screen geometry. + * + * Used by WindowlessWindowManager to enable input on SurfaceControl embedded + * views. + */ + void grantInputChannel(Session session, int callingUid, int callingPid, int displayId, + SurfaceControl surface, IBinder clientToken, + @Nullable InputTransferToken hostInputTransferToken, int flags, int privateFlags, + int inputFeatures, int type, IBinder windowToken, InputTransferToken inputTransferToken, + String inputHandleName, InputChannel outInputChannel) { + final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type); + final InputApplicationHandle applicationHandle; + final String name; + Objects.requireNonNull(outInputChannel); + synchronized (mGlobalLock) { + WindowState hostWindowState = hostInputTransferToken != null + ? mInputToWindowMap.get(hostInputTransferToken.mToken) : null; + EmbeddedWindowController.EmbeddedWindow win = + new EmbeddedWindowController.EmbeddedWindow(session, this, clientToken, + hostWindowState, callingUid, callingPid, sanitizedType, displayId, + inputTransferToken, inputHandleName, (flags & FLAG_NOT_FOCUSABLE) == 0); + win.openInputChannel(outInputChannel); + mEmbeddedWindowController.add(outInputChannel.getToken(), win); + applicationHandle = win.getApplicationHandle(); + name = win.toString(); + } + + updateInputChannel(outInputChannel.getToken(), callingUid, callingPid, displayId, surface, + name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType, + null /* region */, clientToken); + } + + @Override + public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken, + @NonNull InputTransferToken transferToToken) { + Objects.requireNonNull(transferFromToken); + Objects.requireNonNull(transferToToken); + + final long identity = Binder.clearCallingIdentity(); + boolean didTransfer; + try { + synchronized (mGlobalLock) { + // If the transferToToken exists in the input to window map, it means the request + // is to transfer from embedded to host. Otherwise, the transferToToken + // represents an embedded window so transfer from host to embedded. + WindowState windowStateTo = mInputToWindowMap.get(transferToToken.mToken); + if (windowStateTo != null) { + didTransfer = mEmbeddedWindowController.transferToHost(transferFromToken, + windowStateTo); + } else { + WindowState windowStateFrom = mInputToWindowMap.get(transferFromToken.mToken); + didTransfer = mEmbeddedWindowController.transferToEmbedded(windowStateFrom, + transferToToken); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + return didTransfer; + } + + private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, + int displayId, SurfaceControl surface, String name, + InputApplicationHandle applicationHandle, int flags, + int privateFlags, int inputFeatures, int type, Region region, IBinder clientToken) { + final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); + h.token = channelToken; + h.setWindowToken(clientToken); + h.name = name; + + flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid); + inputFeatures = sanitizeSpyWindow(inputFeatures, name, callingUid, callingPid); + + final int sanitizedLpFlags = + (flags & (FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE)) + | LayoutParams.FLAG_NOT_TOUCH_MODAL; + h.layoutParamsType = type; + h.layoutParamsFlags = sanitizedLpFlags; + + // Do not allow any input features to be set without sanitizing them first. + h.inputConfig = InputConfigAdapter.getInputConfigFromWindowParams( + type, sanitizedLpFlags, inputFeatures); + + + if ((flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0) { + h.inputConfig |= InputConfig.NOT_FOCUSABLE; + } + + h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; + h.ownerUid = callingUid; + h.ownerPid = callingPid; + + if (region == null) { + h.replaceTouchableRegionWithCrop(null); + } else { + h.touchableRegion.set(region); + h.replaceTouchableRegionWithCrop = false; + + // Task managers may need to receive input events around task layers to resize tasks. + final int permissionResult = mContext.checkPermission( + permission.MANAGE_ACTIVITY_TASKS, callingPid, callingUid); + if (permissionResult != PackageManager.PERMISSION_GRANTED) { + h.setTouchableRegionCrop(surface); + } + } + + final SurfaceControl.Transaction t = mTransactionFactory.get(); + // Check private trusted overlay flag to set trustedOverlay field of input window handle. + h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0); + t.setInputWindowInfo(surface, h); + t.apply(); + t.close(); + surface.release(); + } + + /** + * Updates the flags on an existing surface's input channel. This assumes the surface provided + * is the one associated with the provided input-channel. If this isn't the case, behavior + * is undefined. + */ + void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, + int flags, int privateFlags, int inputFeatures, Region region) { + final InputApplicationHandle applicationHandle; + final String name; + final EmbeddedWindowController.EmbeddedWindow win; + synchronized (mGlobalLock) { + win = mEmbeddedWindowController.get(channelToken); + if (win == null) { + Slog.e(TAG, "Couldn't find window for provided channelToken."); + return; + } + name = win.toString(); + applicationHandle = win.getApplicationHandle(); + win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0); + } + + updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, + applicationHandle, flags, privateFlags, inputFeatures, win.mWindowType, region, + win.mClient); + } + + /** + * Move focus to the top embedded window if possible. + */ + boolean moveFocusToTopEmbeddedWindow(@NonNull WindowState focusedWindow) { + final TaskFragment taskFragment = focusedWindow.getTaskFragment(); + if (taskFragment == null) { + // Skip if not an Activity window. + return false; + } + + if (!Flags.embeddedActivityBackNavFlag()) { + // Skip if flag is not enabled. + return false; + } + + final ActivityRecord topActivity = + taskFragment.getTask().topRunningActivity(true /* focusableOnly */); + if (topActivity == null || topActivity == focusedWindow.mActivityRecord) { + // Skip if the focused activity is already the top-most activity on the Task. + return false; + } + + if (!topActivity.isEmbedded()) { + // Skip if the top activity is not embedded + return false; + } + + final TaskFragment topTaskFragment = topActivity.getTaskFragment(); + if (topTaskFragment.isIsolatedNav() + && taskFragment.getAdjacentTaskFragment() == topTaskFragment) { + // Skip if the top TaskFragment is adjacent to current focus and is set to isolated nav. + return false; + } + + moveFocusToActivity(topActivity); + return !focusedWindow.isFocused(); + } + + boolean moveFocusToAdjacentWindow(@NonNull WindowState fromWin, @FocusDirection int direction) { + if (!fromWin.isFocused()) { + return false; + } + final TaskFragment fromFragment = fromWin.getTaskFragment(); + if (fromFragment == null) { + return false; + } + final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment(); + if (adjacentFragment == null || adjacentFragment.asTask() != null) { + // Don't move the focus to another task. + return false; + } + if (adjacentFragment.isIsolatedNav()) { + // Don't move the focus if the adjacent TF is isolated navigation. + return false; + } + final Rect fromBounds = fromFragment.getBounds(); + final Rect adjacentBounds = adjacentFragment.getBounds(); + switch (direction) { + case View.FOCUS_LEFT: + if (adjacentBounds.left >= fromBounds.left) { + return false; + } + break; + case View.FOCUS_UP: + if (adjacentBounds.top >= fromBounds.top) { + return false; + } + break; + case View.FOCUS_RIGHT: + if (adjacentBounds.right <= fromBounds.right) { + return false; + } + break; + case View.FOCUS_DOWN: + if (adjacentBounds.bottom <= fromBounds.bottom) { + return false; + } + break; + case View.FOCUS_BACKWARD: + case View.FOCUS_FORWARD: + // These are not absolute directions. Skip checking the bounds. + break; + default: + return false; + } + final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity( + true /* focusableOnly */); + if (topRunningActivity == null) { + return false; + } + moveFocusToActivity(topRunningActivity); + return !fromWin.isFocused(); + } + + @VisibleForTesting + void moveFocusToActivity(@NonNull ActivityRecord activity) { + moveDisplayToTopInternal(activity.getDisplayId()); + handleTaskFocusChange(activity.getTask(), activity); + } + + /** Return whether layer tracing is enabled */ + public boolean isLayerTracing() { + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "isLayerTracing()")) { + throw new SecurityException("Requires DUMP permission"); + } + + final long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + Parcel reply = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + reply = Parcel.obtain(); + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + sf.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, data, reply, 0 /* flags */); + return reply.readBoolean(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get layer tracing"); + } finally { + if (data != null) { + data.recycle(); + } + if (reply != null) { + reply.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + return false; + } + + /** Enable or disable layer tracing */ + public void setLayerTracing(boolean enabled) { + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "setLayerTracing()")) { + throw new SecurityException("Requires DUMP permission"); + } + + final long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(enabled ? 1 : 0); + sf.transact(/* LAYER_TRACE_CONTROL_CODE */ 1025, data, null, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set layer tracing"); + } finally { + if (data != null) { + data.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** Set layer tracing flags. */ + public void setLayerTracingFlags(int flags) { + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "setLayerTracingFlags")) { + throw new SecurityException("Requires DUMP permission"); + } + + final long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(flags); + sf.transact(1033 /* LAYER_TRACE_FLAGS_CODE */, data, null, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set layer tracing flags"); + } finally { + if (data != null) { + data.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Toggle active transaction tracing. + * Setting to true increases the buffer size for active debugging. + * Setting to false resets the buffer size and dumps the trace to file. + */ + public void setActiveTransactionTracing(boolean active) { + if (!checkCallingPermission( + android.Manifest.permission.DUMP, "setActiveTransactionTracing()")) { + throw new SecurityException("Requires DUMP permission"); + } + + final long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(active ? 1 : 0); + sf.transact(/* TRANSACTION_TRACE_CONTROL_CODE */ 1041, data, + null, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set transaction tracing"); + } finally { + if (data != null) { + data.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) { + if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + + final SurfaceControl displaySc; + synchronized (mGlobalLock) { + DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + Slog.e(TAG, "Invalid displayId " + displayId + " for mirrorDisplay"); + return false; + } + + displaySc = displayContent.getWindowingLayer(); + } + + final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc); + outSurfaceControl.copyFrom(mirror, "WMS.mirrorDisplay"); + mirror.release(); + return true; + } + + @Override + public boolean getWindowInsets(int displayId, IBinder token, InsetsState outInsetsState) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent dc = getDisplayContentOrCreate(displayId, token); + if (dc == null) { + throw new WindowManager.InvalidDisplayException("Display#" + displayId + + "could not be found!"); + } + final WindowToken winToken = dc.getWindowToken(token); + dc.getInsetsPolicy().getInsetsForWindowMetrics(winToken, outInsetsState); + return dc.getDisplayPolicy().areSystemBarsForcedConsumedLw(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public List getPossibleDisplayInfo(int displayId) { + final int callingUid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + if (!mAtmService.isCallerRecents(callingUid) + && (!multiCrop() || callingUid != SYSTEM_UID)) { + Slog.e(TAG, "Unable to verify uid for getPossibleDisplayInfo" + + " on uid " + callingUid); + return new ArrayList<>(); + } + + // Retrieve the DisplayInfo across all possible display layouts. + return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + List getPossibleDisplayInfoLocked(int displayId) { + // Retrieve the DisplayInfo for all possible rotations across all possible display + // layouts. + return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId); + } + + void grantEmbeddedWindowFocus(Session session, InputTransferToken inputTransferToken, + boolean grantFocus) { + synchronized (mGlobalLock) { + final EmbeddedWindowController.EmbeddedWindow embeddedWindow = + mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); + if (embeddedWindow == null) { + Slog.e(TAG, "Embedded window not found"); + return; + } + if (embeddedWindow.mSession != session) { + Slog.e(TAG, "Window not in session:" + session); + return; + } + IBinder inputToken = embeddedWindow.getInputChannelToken(); + if (inputToken == null) { + Slog.e(TAG, "Focus token found but input channel token not found"); + return; + } + SurfaceControl.Transaction t = mTransactionFactory.get(); + final int displayId = embeddedWindow.mDisplayId; + if (grantFocus) { + t.setFocusedWindow(inputToken, embeddedWindow.toString(), displayId).apply(); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Focus request " + embeddedWindow, "reason=grantEmbeddedWindowFocus(true)"); + } else { + // Search for a new focus target + DisplayContent displayContent = mRoot.getDisplayContent(displayId); + WindowState newFocusTarget = displayContent == null + ? null : displayContent.findFocusedWindow(); + if (newFocusTarget == null) { + t.setFocusedWindow(null, null, displayId).apply(); + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s" + + " dropped focus so setting focus to null since no candidate" + + " was found", + embeddedWindow); + return; + } + t.setFocusedWindow(newFocusTarget.mInputChannelToken, newFocusTarget.getName(), + displayId).apply(); + + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Focus request " + newFocusTarget, + "reason=grantEmbeddedWindowFocus(false)"); + } + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", + embeddedWindow, grantFocus); + } + } + + void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, + InputTransferToken inputTransferToken, boolean grantFocus) { + synchronized (mGlobalLock) { + final WindowState hostWindow = + windowForClientLocked(session, callingWindow, false /* throwOnError*/); + if (hostWindow == null) { + Slog.e(TAG, "Host window not found"); + return; + } + if (hostWindow.mInputChannel == null) { + Slog.e(TAG, "Host window does not have an input channel"); + return; + } + final EmbeddedWindowController.EmbeddedWindow embeddedWindow = + mEmbeddedWindowController.getByInputTransferToken(inputTransferToken); + if (embeddedWindow == null) { + Slog.e(TAG, "Embedded window not found"); + return; + } + if (embeddedWindow.mHostWindowState != hostWindow) { + Slog.e(TAG, "Embedded window does not belong to the host"); + return; + } + if (grantFocus) { + hostWindow.mInputWindowHandle.setFocusTransferTarget( + embeddedWindow.getInputChannelToken()); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Transfer focus request " + embeddedWindow, + "reason=grantEmbeddedWindowFocus(true)"); + } else { + hostWindow.mInputWindowHandle.setFocusTransferTarget(null); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Transfer focus request " + hostWindow, + "reason=grantEmbeddedWindowFocus(false)"); + } + DisplayContent dc = mRoot.getDisplayContent(hostWindow.getDisplayId()); + if (dc != null) { + dc.getInputMonitor().updateInputWindowsLw(true); + } + + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", + embeddedWindow, grantFocus); + } + } + + @Override + public void holdLock(IBinder token, int durationMs) { + mTestUtilityService.verifyHoldLockToken(token); + + synchronized (mGlobalLock) { + SystemClock.sleep(durationMs); + } + } + + @Override + public String[] getSupportedDisplayHashAlgorithms() { + return mDisplayHashController.getSupportedHashAlgorithms(); + } + + @Override + public VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) { + return mDisplayHashController.verifyDisplayHash(displayHash); + } + + @Override + public void setDisplayHashThrottlingEnabled(boolean enable) { + if (!checkCallingPermission(READ_FRAME_BUFFER, "setDisplayHashThrottle()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + mDisplayHashController.setDisplayHashThrottlingEnabled(enable); + } + + @Override + public boolean isTaskSnapshotSupported() { + synchronized (mGlobalLock) { + return !mTaskSnapshotController.shouldDisableSnapshots(); + } + } + + void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow, + String hashAlgorithm, RemoteCallback callback) { + final SurfaceControl displaySurfaceControl; + final Rect boundsInDisplay = new Rect(boundsInWindow); + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, window, false); + if (win == null) { + Slog.w(TAG, "Failed to generate DisplayHash. Invalid window"); + mDisplayHashController.sendDisplayHashError(callback, + DISPLAY_HASH_ERROR_MISSING_WINDOW); + return; + } + + if (win.mActivityRecord == null || !win.mActivityRecord.isState( + ActivityRecord.State.RESUMED)) { + mDisplayHashController.sendDisplayHashError(callback, + DISPLAY_HASH_ERROR_MISSING_WINDOW); + return; + } + + DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display"); + mDisplayHashController.sendDisplayHashError(callback, + DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN); + return; + } + + displaySurfaceControl = displayContent.getSurfaceControl(); + mDisplayHashController.calculateDisplayHashBoundsLocked(win, boundsInWindow, + boundsInDisplay); + + if (boundsInDisplay.isEmpty()) { + Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen"); + mDisplayHashController.sendDisplayHashError(callback, + DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN); + return; + } + } + + // A screenshot of the entire display is taken rather than just the window. This is + // because if we take a screenshot of the window, it will not include content that might + // be covering it with the same uid. We want to make sure we include content that's + // covering to ensure we get as close as possible to what the user sees + final int uid = session.mUid; + ScreenCapture.LayerCaptureArgs.Builder args = + new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl) + .setUid(uid) + .setSourceCrop(boundsInDisplay); + + mDisplayHashController.generateDisplayHash(args, boundsInWindow, hashAlgorithm, uid, + callback); + } + + boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) { + final Task imeTargetWindowTask; + synchronized (mGlobalLock) { + final WindowState imeTargetWindow = mWindowMap.get(imeTargetWindowToken); + if (imeTargetWindow == null) { + return false; + } + imeTargetWindowTask = imeTargetWindow.getTask(); + if (imeTargetWindowTask == null) { + return false; + } + if (imeTargetWindow.mActivityRecord != null + && imeTargetWindow.mActivityRecord.mLastImeShown) { + return true; + } + } + final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId, + imeTargetWindowTask.mUserId, false /* isLowResolution */, + false /* restoreFromDisk */); + return snapshot != null && snapshot.hasImeSurface(); + } + + @Override + public int getImeDisplayId() { + // TODO(b/189805422): Add a toast to notify users that IMS may get extra + // onConfigurationChanged callback when perDisplayFocus is enabled. + // Enabling perDisplayFocus means that we track focus on each display, so we don't have + // the "top focus" display and getTopFocusedDisplayContent returns the default display + // as the fallback. It leads to InputMethodService receives an extra onConfiguration + // callback when InputMethodService move from a secondary display to another display + // with the same display metrics because InputMethodService will always associate with + // the ImeContainer on the default display in onCreate and receive a configuration update + // to match default display ImeContainer and then receive another configuration update + // from attachToWindowToken. + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getTopFocusedDisplayContent(); + return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId() + : DEFAULT_DISPLAY; + } + } + + @Override + public void setTaskSnapshotEnabled(boolean enabled) { + mTaskSnapshotController.setSnapshotEnabled(enabled); + } + + @Override + public void setTaskTransitionSpec(TaskTransitionSpec spec) { + if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS, "setTaskTransitionSpec()")) { + throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission"); + } + + mTaskTransitionSpec = spec; + } + + @Override + public void clearTaskTransitionSpec() { + if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS, "clearTaskTransitionSpec()")) { + throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission"); + } + + mTaskTransitionSpec = null; + } + + @Override + @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER) + public void registerTaskFpsCallback(@IntRange(from = 0) int taskId, + ITaskFpsCallback callback) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER) + != PackageManager.PERMISSION_GRANTED) { + final int pid = Binder.getCallingPid(); + throw new SecurityException("Access denied to process: " + pid + + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER); + } + + if (mRoot.anyTaskForId(taskId) == null) { + throw new IllegalArgumentException("no task with taskId: " + taskId); + } + + mTaskFpsCallbackController.registerListener(taskId, callback); + } + + @Override + @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER) + public void unregisterTaskFpsCallback(ITaskFpsCallback callback) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER) + != PackageManager.PERMISSION_GRANTED) { + final int pid = Binder.getCallingPid(); + throw new SecurityException("Access denied to process: " + pid + + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER); + } + + mTaskFpsCallbackController.unregisterListener(callback); + } + + @Override + public Bitmap snapshotTaskForRecents(int taskId) { + if (!checkCallingPermission(READ_FRAME_BUFFER, "snapshotTaskForRecents()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + + TaskSnapshot taskSnapshot; + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + Task task = mRoot.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS); + if (task == null) { + throw new IllegalArgumentException( + "Failed to find matching task for taskId=" + taskId); + } + taskSnapshot = mTaskSnapshotController.captureSnapshot(task); + } + } finally { + Binder.restoreCallingIdentity(token); + } + + if (taskSnapshot == null || taskSnapshot.getHardwareBuffer() == null) { + return null; + } + return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(), + taskSnapshot.getColorSpace()); + } + + @Override + public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { + if (!checkCallingPermission(START_TASKS_FROM_RECENTS, "setRecentsAppBehindSystemBars()")) { + throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission"); + } + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final Task recentsApp = mRoot.getTask(task -> task.isActivityTypeHomeOrRecents() + && task.getTopVisibleActivity() != null); + if (recentsApp != null) { + recentsApp.getTask().setCanAffectSystemUiFlags(behindSystemBars); + mWindowPlacerLocked.requestTraversal(); + } + InputMethodManagerInternal.get().maybeFinishStylusHandwriting(); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Gets the background color of the letterbox. Considered invalid if the background has + * multiple colors {@link #isLetterboxBackgroundMultiColored} + */ + @Override + public int getLetterboxBackgroundColorInArgb() { + return mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb(); + } + + /** + * Whether the outer area of the letterbox has multiple colors (e.g. blurred background). + */ + @Override + public boolean isLetterboxBackgroundMultiColored() { + @LetterboxConfiguration.LetterboxBackgroundType int letterboxBackgroundType = + mLetterboxConfiguration.getLetterboxBackgroundType(); + switch (letterboxBackgroundType) { + case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING: + case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND: + case LETTERBOX_BACKGROUND_WALLPAPER: + return true; + case LETTERBOX_BACKGROUND_SOLID_COLOR: + return false; + default: + throw new AssertionError( + "Unexpected letterbox background type: " + letterboxBackgroundType); + } + } + + @Override + public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs, + ScreenCapture.ScreenCaptureListener listener) { + Slog.d(TAG, "captureDisplay"); + if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) { + throw new SecurityException("Requires READ_FRAME_BUFFER permission"); + } + + ScreenCapture.LayerCaptureArgs layerCaptureArgs = getCaptureArgs(displayId, captureArgs); + ScreenCapture.captureLayers(layerCaptureArgs, listener); + + if (Binder.getCallingUid() != SYSTEM_UID) { + // Release the SurfaceControl objects only if the caller is not in system server as no + // parcelling occurs in this case. + layerCaptureArgs.release(); + } + } + + @VisibleForTesting + ScreenCapture.LayerCaptureArgs getCaptureArgs(int displayId, + @Nullable ScreenCapture.CaptureArgs captureArgs) { + final SurfaceControl displaySurfaceControl; + synchronized (mGlobalLock) { + DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException("Trying to screenshot and invalid display: " + + displayId); + } + + displaySurfaceControl = displayContent.getSurfaceControl(); + + if (captureArgs == null) { + captureArgs = new ScreenCapture.CaptureArgs.Builder<>() + .build(); + } + + if (captureArgs.mSourceCrop.isEmpty()) { + displayContent.getBounds(mTmpRect); + mTmpRect.offsetTo(0, 0); + } else { + mTmpRect.set(captureArgs.mSourceCrop); + } + } + + return new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl, captureArgs) + .setSourceCrop(mTmpRect) + .build(); + } + + @Override + public boolean isGlobalKey(int keyCode) { + return mPolicy.isGlobalKey(keyCode); + } + + private int sanitizeWindowType(Session session, int displayId, IBinder windowToken, int type) { + // Determine whether this window type is valid for this process. + final boolean isTypeValid; + if (type == TYPE_ACCESSIBILITY_OVERLAY && windowToken != null) { + // Only accessibility services can add accessibility overlays. + // Accessibility services will have a WindowToken with type + // TYPE_ACCESSIBILITY_OVERLAY. + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + final WindowToken token = displayContent.getWindowToken(windowToken); + if (token == null) { + isTypeValid = false; + } else if (type == token.getWindowType()) { + isTypeValid = true; + } else { + isTypeValid = false; + } + } else if (!session.mCanAddInternalSystemWindow && type != 0) { + Slog.w( + TAG_WM, + "Requires INTERNAL_SYSTEM_WINDOW permission if assign type to" + + " input. New type will be 0."); + isTypeValid = false; + } else { + isTypeValid = true; + } + + if (!isTypeValid) { + return 0; + } + return type; + } + @Override + public boolean addToSurfaceSyncGroup(IBinder syncGroupToken, boolean parentSyncGroupMerge, + @Nullable ISurfaceSyncGroupCompletedListener completedListener, + AddToSurfaceSyncGroupResult outAddToSyncGroupResult) { + return mSurfaceSyncGroupController.addToSyncGroup(syncGroupToken, parentSyncGroupMerge, + completedListener, outAddToSyncGroupResult); + } + + @Override + public void markSurfaceSyncGroupReady(IBinder syncGroupToken) { + mSurfaceSyncGroupController.markSyncGroupReady(syncGroupToken); + } + + + /** + * Must be called when a screenshot is taken via hardware chord. + * + * Notifies all registered visible activities that have registered for screencapture callback, + * Returns a list of visible apps component names. + */ + @Override + public List notifyScreenshotListeners(int displayId) { + // make sure caller is SysUI. + if (!checkCallingPermission(STATUS_BAR_SERVICE, + "notifyScreenshotListeners()")) { + throw new SecurityException("Requires STATUS_BAR_SERVICE permission"); + } + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + return new ArrayList<>(); + } + ArraySet notifiedApps = new ArraySet<>(); + displayContent.forAllActivities( + (ar) -> { + if (!notifiedApps.contains(ar.mActivityComponent) && ar.isVisible() + && ar.isRegisteredForScreenCaptureCallback()) { + ar.reportScreenCaptured(); + notifiedApps.add(ar.mActivityComponent); + } + }, + true /* traverseTopToBottom */); + return List.copyOf(notifiedApps); + } + } + + @RequiresPermission(ACCESS_SURFACE_FLINGER) + @Override + public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) { + if (!checkCallingPermission(ACCESS_SURFACE_FLINGER, + "replaceDisplayContent()")) { + throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission"); + } + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + return false; + } + dc.replaceContent(sc); + return true; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void registerTrustedPresentationListener(IBinder window, + ITrustedPresentationListener listener, + TrustedPresentationThresholds thresholds, int id) { + mTrustedPresentationListenerController.registerListener(window, listener, thresholds, id); + } + + @Override + public void unregisterTrustedPresentationListener(ITrustedPresentationListener listener, + int id) { + mTrustedPresentationListenerController.unregisterListener(listener, id); + } + + @EnforcePermission(android.Manifest.permission.DETECT_SCREEN_RECORDING) + @Override + public boolean registerScreenRecordingCallback(IScreenRecordingCallback callback) { + registerScreenRecordingCallback_enforcePermission(); + return mScreenRecordingCallbackController.register(callback); + } + + @EnforcePermission(android.Manifest.permission.DETECT_SCREEN_RECORDING) + @Override + public void unregisterScreenRecordingCallback(IScreenRecordingCallback callback) { + unregisterScreenRecordingCallback_enforcePermission(); + mScreenRecordingCallbackController.unregister(callback); + } + + void onProcessActivityVisibilityChanged(int uid, boolean visible) { + mScreenRecordingCallbackController.onProcessActivityVisibilityChanged(uid, visible); + } + + /** + * Sets the listener to be called back when a cross-window drag and drop operation happens. + */ + @Override + public void setGlobalDragListener(IGlobalDragListener listener) throws RemoteException { + mAtmService.enforceTaskPermission("setUnhandledDragListener"); + synchronized (mGlobalLock) { + mDragDropController.setGlobalDragListener(listener); + } + } +} diff --git a/aosp/frameworks/base/services/java/com/android/server/SystemServer.java b/aosp/frameworks/base/services/java/com/android/server/SystemServer.java new file mode 100755 index 0000000000000000000000000000000000000000..a242cfbf77cbabf13b3f29d5af67e2fe2c2e2ce0 --- /dev/null +++ b/aosp/frameworks/base/services/java/com/android/server/SystemServer.java @@ -0,0 +1,3506 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; +import static android.os.IServiceManager.DUMP_FLAG_PROTO; +import static android.os.Process.SYSTEM_UID; +import static android.os.Process.myPid; +import static android.system.OsConstants.O_CLOEXEC; +import static android.system.OsConstants.O_RDONLY; +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG; + +import android.annotation.NonNull; +import android.annotation.StringRes; +import android.app.ActivityThread; +import android.app.AppCompatCallbacks; +import android.app.ApplicationErrorReport; +import android.app.INotificationManager; +import android.app.SystemServiceRegistry; +import android.app.admin.DevicePolicySafetyChecker; +import android.app.usage.UsageStatsManagerInternal; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.res.Configuration; +import android.content.res.Resources.Theme; +import android.credentials.CredentialManager; +import android.database.sqlite.SQLiteCompatibilityWalFlags; +import android.database.sqlite.SQLiteGlobal; +import android.graphics.GraphicsStatsService; +import android.graphics.Typeface; +import android.hardware.display.DisplayManagerInternal; +import android.net.ConnectivityManager; +import android.net.ConnectivityModuleConnector; +import android.net.NetworkStackClient; +import android.os.ArtModuleServiceManager; +import android.os.BaseBundle; +import android.os.Binder; +import android.os.Build; +import android.os.Debug; +import android.os.Environment; +import android.os.FactoryTest; +import android.os.FileUtils; +import android.os.IBinder; +import android.os.IBinderCallback; +import android.os.IIncidentManager; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.PowerManager; +import android.os.Process; +import android.os.ServiceManager; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.storage.IStorageManager; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.server.ServerProtoEnums; +import android.system.ErrnoException; +import android.system.Os; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.DisplayMetrics; +import android.util.Dumpable; +import android.util.EventLog; +import android.util.IndentingPrintWriter; +import android.util.Pair; +import android.util.Slog; +import android.util.TimeUtils; +import android.view.contentcapture.ContentCaptureManager; + +import com.android.i18n.timezone.ZoneInfoDb; +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.os.BinderInternal; +import com.android.internal.os.RuntimeInit; +import com.android.internal.policy.AttributeCache; +import com.android.internal.util.ConcurrentUtils; +import com.android.internal.util.EmergencyAffordanceManager; +import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.widget.ILockSettings; +import com.android.internal.widget.LockSettingsInternal; +import com.android.server.adaptiveauth.AdaptiveAuthService; +import com.android.server.am.ActivityManagerService; +import com.android.server.appbinding.AppBindingService; +import com.android.server.appop.AppOpMigrationHelper; +import com.android.server.appop.AppOpMigrationHelperImpl; +import com.android.server.art.ArtModuleServiceInitializer; +import com.android.server.art.DexUseManagerLocal; +import com.android.server.attention.AttentionManagerService; +import com.android.server.audio.AudioService; +import com.android.server.biometrics.AuthService; +import com.android.server.biometrics.BiometricService; +import com.android.server.biometrics.sensors.face.FaceService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.iris.IrisService; +import com.android.server.broadcastradio.BroadcastRadioService; +import com.android.server.camera.CameraServiceProxy; +import com.android.server.clipboard.ClipboardService; +import com.android.server.compat.PlatformCompat; +import com.android.server.compat.PlatformCompatNative; +import com.android.server.connectivity.PacProxyService; +import com.android.server.contentcapture.ContentCaptureManagerInternal; +import com.android.server.coverage.CoverageService; +import com.android.server.cpu.CpuMonitorService; +import com.android.server.criticalevents.CriticalEventLog; +import com.android.server.devicepolicy.DevicePolicyManagerService; +import com.android.server.devicestate.DeviceStateManagerService; +import com.android.server.display.DisplayManagerService; +import com.android.server.display.color.ColorDisplayService; +import com.android.server.dreams.DreamManagerService; +import com.android.server.emergency.EmergencyAffordanceService; +import com.android.server.flags.FeatureFlagsService; +import com.android.server.gpu.GpuService; +import com.android.server.grammaticalinflection.GrammaticalInflectionService; +import com.android.server.graphics.fonts.FontManagerService; +import com.android.server.hdmi.HdmiControlService; +import com.android.server.incident.IncidentCompanionService; +import com.android.server.input.InputManagerService; +import com.android.server.inputmethod.InputMethodManagerService; +import com.android.server.integrity.AppIntegrityManagerService; +import com.android.server.lights.LightsService; +import com.android.server.locales.LocaleManagerService; +import com.android.server.location.LocationManagerService; +import com.android.server.location.altitude.AltitudeService; +import com.android.server.logcat.LogcatManagerService; +import com.android.server.media.MediaRouterService; +import com.android.server.media.metrics.MediaMetricsManagerService; +import com.android.server.media.projection.MediaProjectionManagerService; +import com.android.server.net.NetworkManagementService; +import com.android.server.net.NetworkPolicyManagerService; +import com.android.server.net.watchlist.NetworkWatchlistService; +import com.android.server.notification.NotificationManagerService; +import com.android.server.oemlock.OemLockService; +import com.android.server.om.OverlayManagerService; +import com.android.server.os.BugreportManagerService; +import com.android.server.os.DeviceIdentifiersPolicyService; +import com.android.server.os.NativeTombstoneManagerService; +import com.android.server.os.SchedulingPolicyService; +import com.android.server.pdb.PersistentDataBlockService; +import com.android.server.people.PeopleService; +import com.android.server.permission.access.AccessCheckingService; +import com.android.server.pm.ApexManager; +import com.android.server.pm.ApexSystemServiceInfo; +import com.android.server.pm.BackgroundInstallControlService; +import com.android.server.pm.CrossProfileAppsService; +import com.android.server.pm.DataLoaderManagerService; +import com.android.server.pm.DexOptHelper; +import com.android.server.pm.DynamicCodeLoggingService; +import com.android.server.pm.Installer; +import com.android.server.pm.LauncherAppsService; +import com.android.server.pm.OtaDexoptService; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.ShortcutService; +import com.android.server.pm.UserManagerService; +import com.android.server.pm.dex.OdsignStatsLogger; +import com.android.server.pm.permission.PermissionMigrationHelper; +import com.android.server.pm.permission.PermissionMigrationHelperImpl; +import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.policy.AppOpsPolicy; +import com.android.server.policy.PermissionPolicyService; +import com.android.server.policy.PhoneWindowManager; +import com.android.server.policy.role.RoleServicePlatformHelperImpl; +import com.android.server.power.PowerManagerService; +import com.android.server.power.ShutdownThread; +import com.android.server.power.ThermalManagerService; +import com.android.server.power.hint.HintManagerService; +import com.android.server.powerstats.PowerStatsService; +import com.android.server.profcollect.ProfcollectForwardingService; +import com.android.server.recoverysystem.RecoverySystemService; +import com.android.server.resources.ResourcesManagerService; +import com.android.server.restrictions.RestrictionsManagerService; +import com.android.server.role.RoleServicePlatformHelper; +import com.android.server.rotationresolver.RotationResolverManagerService; +import com.android.server.security.AttestationVerificationManagerService; +import com.android.server.security.FileIntegrityService; +import com.android.server.security.KeyAttestationApplicationIdProviderService; +import com.android.server.security.KeyChainSystemService; +import com.android.server.security.rkp.RemoteProvisioningService; +import com.android.server.selinux.SelinuxAuditLogsService; +import com.android.server.sensorprivacy.SensorPrivacyService; +import com.android.server.sensors.SensorService; +import com.android.server.signedconfig.SignedConfigService; +import com.android.server.soundtrigger.SoundTriggerService; +import com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService; +import com.android.server.statusbar.StatusBarManagerService; +import com.android.server.storage.DeviceStorageMonitorService; +import com.android.server.telecom.TelecomLoaderService; +import com.android.server.testharness.TestHarnessModeService; +import com.android.server.textclassifier.TextClassificationManagerService; +import com.android.server.textservices.TextServicesManagerService; +import com.android.server.timedetector.NetworkTimeUpdateService; +import com.android.server.tracing.TracingServiceProxy; +import com.android.server.trust.TrustManagerService; +import com.android.server.tv.TvInputManagerService; +import com.android.server.tv.TvRemoteService; +import com.android.server.tv.interactive.TvInteractiveAppManagerService; +import com.android.server.tv.tunerresourcemanager.TunerResourceManagerService; +import com.android.server.twilight.TwilightService; +import com.android.server.uri.UriGrantsManagerService; +import com.android.server.usage.UsageStatsService; +import com.android.server.utils.TimingsTraceAndSlog; +import com.android.server.vibrator.VibratorManagerService; +import com.android.server.vr.VrManagerService; +import com.android.server.wearable.WearableSensingManagerService; +import com.android.server.webkit.WebViewUpdateService; +import com.android.server.wm.ActivityTaskManagerService; +import com.android.server.wm.WindowManagerGlobalLock; +import com.android.server.wm.WindowManagerService; +import com.android.server.RTCPushService; //ccy liutianzuo + +import dalvik.system.VMRuntime; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Timer; +import java.util.TreeSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; + +/** + * Entry point to {@code system_server}. + */ +public final class SystemServer implements Dumpable { + + private static final String TAG = "SystemServer"; + + private static final long SLOW_DISPATCH_THRESHOLD_MS = 100; + private static final long SLOW_DELIVERY_THRESHOLD_MS = 200; + + /* + * Implementation class names. TODO: Move them to a codegen class or load + * them from the build system somehow. + */ + private static final String BACKUP_MANAGER_SERVICE_CLASS = + "com.android.server.backup.BackupManagerService$Lifecycle"; + private static final String APPWIDGET_SERVICE_CLASS = + "com.android.server.appwidget.AppWidgetService"; + private static final String ARC_NETWORK_SERVICE_CLASS = + "com.android.server.arc.net.ArcNetworkService"; + private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS = + "com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService"; + private static final String ARC_SYSTEM_HEALTH_SERVICE = + "com.android.server.arc.health.ArcSystemHealthService"; + private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.voiceinteraction.VoiceInteractionManagerService"; + private static final String APP_HIBERNATION_SERVICE_CLASS = + "com.android.server.apphibernation.AppHibernationService"; + private static final String PRINT_MANAGER_SERVICE_CLASS = + "com.android.server.print.PrintManagerService"; + private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS = + "com.android.server.companion.CompanionDeviceManagerService"; + private static final String VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS = + "com.android.server.companion.virtual.VirtualDeviceManagerService"; + private static final String STATS_COMPANION_APEX_PATH = + "/apex/com.android.os.statsd/javalib/service-statsd.jar"; + private static final String SCHEDULING_APEX_PATH = + "/apex/com.android.scheduling/javalib/service-scheduling.jar"; + private static final String REBOOT_READINESS_LIFECYCLE_CLASS = + "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle"; + private static final String CONNECTIVITY_SERVICE_APEX_PATH = + "/apex/com.android.tethering/javalib/service-connectivity.jar"; + private static final String STATS_COMPANION_LIFECYCLE_CLASS = + "com.android.server.stats.StatsCompanion$Lifecycle"; + private static final String STATS_PULL_ATOM_SERVICE_CLASS = + "com.android.server.stats.pull.StatsPullAtomService"; + private static final String STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS = + "com.android.server.stats.bootstrap.StatsBootstrapAtomService$Lifecycle"; + private static final String USB_SERVICE_CLASS = + "com.android.server.usb.UsbService$Lifecycle"; + private static final String MIDI_SERVICE_CLASS = + "com.android.server.midi.MidiService$Lifecycle"; + private static final String WIFI_APEX_SERVICE_JAR_PATH = + "/apex/com.android.wifi/javalib/service-wifi.jar"; + private static final String WIFI_SERVICE_CLASS = + "com.android.server.wifi.WifiService"; + private static final String WIFI_SCANNING_SERVICE_CLASS = + "com.android.server.wifi.scanner.WifiScanningService"; + private static final String WIFI_RTT_SERVICE_CLASS = + "com.android.server.wifi.rtt.RttService"; + private static final String WIFI_AWARE_SERVICE_CLASS = + "com.android.server.wifi.aware.WifiAwareService"; + private static final String WIFI_P2P_SERVICE_CLASS = + "com.android.server.wifi.p2p.WifiP2pService"; + private static final String LOWPAN_SERVICE_CLASS = + "com.android.server.lowpan.LowpanService"; + private static final String JOB_SCHEDULER_SERVICE_CLASS = + "com.android.server.job.JobSchedulerService"; + private static final String LOCK_SETTINGS_SERVICE_CLASS = + "com.android.server.locksettings.LockSettingsService$Lifecycle"; + private static final String RESOURCE_ECONOMY_SERVICE_CLASS = + "com.android.server.tare.InternalResourceService"; + private static final String STORAGE_MANAGER_SERVICE_CLASS = + "com.android.server.StorageManagerService$Lifecycle"; + private static final String STORAGE_STATS_SERVICE_CLASS = + "com.android.server.usage.StorageStatsService$Lifecycle"; + private static final String SEARCH_MANAGER_SERVICE_CLASS = + "com.android.server.search.SearchManagerService$Lifecycle"; + private static final String THERMAL_OBSERVER_CLASS = + "com.android.clockwork.ThermalObserver"; + private static final String WEAR_CONNECTIVITY_SERVICE_CLASS = + "com.android.clockwork.connectivity.WearConnectivityService"; + private static final String WEAR_POWER_SERVICE_CLASS = + "com.android.clockwork.power.WearPowerService"; + private static final String HEALTH_SERVICE_CLASS = + "com.android.clockwork.healthservices.HealthService"; + private static final String SYSTEM_STATE_DISPLAY_SERVICE_CLASS = + "com.android.clockwork.systemstatedisplay.SystemStateDisplayService"; + private static final String WEAR_DISPLAYOFFLOAD_SERVICE_CLASS = + "com.android.clockwork.displayoffload.DisplayOffloadService"; + private static final String WEAR_MODE_SERVICE_CLASS = + "com.android.clockwork.modes.ModeManagerService"; + private static final String WEAR_DISPLAY_SERVICE_CLASS = + "com.android.clockwork.display.WearDisplayService"; + private static final String WEAR_DEBUG_SERVICE_CLASS = + "com.android.clockwork.debug.WearDebugService"; + private static final String WEAR_TIME_SERVICE_CLASS = + "com.android.clockwork.time.WearTimeService"; + private static final String WEAR_SETTINGS_SERVICE_CLASS = + "com.android.clockwork.settings.WearSettingsService"; + private static final String WRIST_ORIENTATION_SERVICE_CLASS = + "com.android.clockwork.wristorientation.WristOrientationService"; + private static final String ACCOUNT_SERVICE_CLASS = + "com.android.server.accounts.AccountManagerService$Lifecycle"; + private static final String CONTENT_SERVICE_CLASS = + "com.android.server.content.ContentService$Lifecycle"; + private static final String WALLPAPER_SERVICE_CLASS = + "com.android.server.wallpaper.WallpaperManagerService$Lifecycle"; + private static final String AUTO_FILL_MANAGER_SERVICE_CLASS = + "com.android.server.autofill.AutofillManagerService"; + private static final String CREDENTIAL_MANAGER_SERVICE_CLASS = + "com.android.server.credentials.CredentialManagerService"; + private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS = + "com.android.server.contentcapture.ContentCaptureManagerService"; + private static final String TRANSLATION_MANAGER_SERVICE_CLASS = + "com.android.server.translation.TranslationManagerService"; + private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.musicrecognition.MusicRecognitionManagerService"; + private static final String AMBIENT_CONTEXT_MANAGER_SERVICE_CLASS = + "com.android.server.ambientcontext.AmbientContextManagerService"; + private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS = + "com.android.server.systemcaptions.SystemCaptionsManagerService"; + private static final String TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS = + "com.android.server.texttospeech.TextToSpeechManagerService"; + private static final String IOT_SERVICE_CLASS = + "com.android.things.server.IoTSystemService"; + private static final String SLICE_MANAGER_SERVICE_CLASS = + "com.android.server.slice.SliceManagerService$Lifecycle"; + private static final String CAR_SERVICE_HELPER_SERVICE_CLASS = + "com.android.internal.car.CarServiceHelperService"; + private static final String TIME_DETECTOR_SERVICE_CLASS = + "com.android.server.timedetector.TimeDetectorService$Lifecycle"; + private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS = + "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle"; + private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS = + "com.android.server.timezonedetector.location.LocationTimeZoneManagerService$Lifecycle"; + private static final String GNSS_TIME_UPDATE_SERVICE_CLASS = + "com.android.server.timedetector.GnssTimeUpdateService$Lifecycle"; + private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS = + "com.android.server.accessibility.AccessibilityManagerService$Lifecycle"; + private static final String ADB_SERVICE_CLASS = + "com.android.server.adb.AdbService$Lifecycle"; + private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.speech.SpeechRecognitionManagerService"; + private static final String WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS = + "com.android.server.wallpapereffectsgeneration.WallpaperEffectsGenerationManagerService"; + private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS = + "com.android.server.appprediction.AppPredictionManagerService"; + private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS = + "com.android.server.contentsuggestions.ContentSuggestionsManagerService"; + private static final String SEARCH_UI_MANAGER_SERVICE_CLASS = + "com.android.server.searchui.SearchUiManagerService"; + private static final String SMARTSPACE_MANAGER_SERVICE_CLASS = + "com.android.server.smartspace.SmartspaceManagerService"; + private static final String DEVICE_IDLE_CONTROLLER_CLASS = + "com.android.server.DeviceIdleController"; + private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = + "com.android.server.blob.BlobStoreManagerService"; + private static final String APPSEARCH_MODULE_LIFECYCLE_CLASS = + "com.android.server.appsearch.AppSearchModule$Lifecycle"; + private static final String ISOLATED_COMPILATION_SERVICE_CLASS = + "com.android.server.compos.IsolatedCompilationService"; + private static final String ROLLBACK_MANAGER_SERVICE_CLASS = + "com.android.server.rollback.RollbackManagerService"; + private static final String ALARM_MANAGER_SERVICE_CLASS = + "com.android.server.alarm.AlarmManagerService"; + private static final String MEDIA_SESSION_SERVICE_CLASS = + "com.android.server.media.MediaSessionService"; + private static final String MEDIA_RESOURCE_MONITOR_SERVICE_CLASS = + "com.android.server.media.MediaResourceMonitorService"; + private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = + "com.android.server.ConnectivityServiceInitializer"; + private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS = + "com.android.server.NetworkStatsServiceInitializer"; + private static final String IP_CONNECTIVITY_METRICS_CLASS = + "com.android.server.connectivity.IpConnectivityMetrics"; + private static final String MEDIA_COMMUNICATION_SERVICE_CLASS = + "com.android.server.media.MediaCommunicationService"; + private static final String APP_COMPAT_OVERRIDES_SERVICE_CLASS = + "com.android.server.compat.overrides.AppCompatOverridesService$Lifecycle"; + private static final String HEALTHCONNECT_MANAGER_SERVICE_CLASS = + "com.android.server.healthconnect.HealthConnectManagerService"; + private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService"; + private static final String GAME_MANAGER_SERVICE_CLASS = + "com.android.server.app.GameManagerService$Lifecycle"; + private static final String ENHANCED_CONFIRMATION_SERVICE_CLASS = + "com.android.ecm.EnhancedConfirmationService"; + + private static final String UWB_APEX_SERVICE_JAR_PATH = + "/apex/com.android.uwb/javalib/service-uwb.jar"; + private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService"; + private static final String BLUETOOTH_APEX_SERVICE_JAR_PATH = + "/apex/com.android.btservices/javalib/service-bluetooth.jar"; + private static final String BLUETOOTH_SERVICE_CLASS = + "com.android.server.bluetooth.BluetoothService"; + private static final String SAFETY_CENTER_SERVICE_CLASS = + "com.android.safetycenter.SafetyCenterService"; + + private static final String SDK_SANDBOX_MANAGER_SERVICE_CLASS = + "com.android.server.sdksandbox.SdkSandboxManagerService$Lifecycle"; + private static final String AD_SERVICES_MANAGER_SERVICE_CLASS = + "com.android.server.adservices.AdServicesManagerService$Lifecycle"; + private static final String ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS = + "com.android.server.ondevicepersonalization." + + "OnDevicePersonalizationSystemService$Lifecycle"; + private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS = + "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle"; + private static final String DEVICE_LOCK_SERVICE_CLASS = + "com.android.server.devicelock.DeviceLockService"; + private static final String DEVICE_LOCK_APEX_PATH = + "/apex/com.android.devicelock/javalib/service-devicelock.jar"; + + private static final String PROFILING_SERVICE_LIFECYCLE_CLASS = + "android.os.profiling.ProfilingService$Lifecycle"; + private static final String PROFILING_SERVICE_JAR_PATH = + "/apex/com.android.profiling/javalib/service-profiling.jar"; + + private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; + + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; + + private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file"; + private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map"; + + // maximum number of binder threads used for system_server + // will be higher than the system default + private static final int sMaxBinderThreads = 31; + + /** + * Default theme used by the system context. This is used to style system-provided dialogs, such + * as the Power Off dialog, and other visual content. + */ + private static final int DEFAULT_SYSTEM_THEME = + com.android.internal.R.style.Theme_DeviceDefault_System; + + private final int mFactoryTestMode; + private Timer mProfilerSnapshotTimer; + + private Context mSystemContext; + private SystemServiceManager mSystemServiceManager; + + // TODO: remove all of these references by improving dependency resolution and boot phases + private PowerManagerService mPowerManagerService; + private ActivityManagerService mActivityManagerService; + private WindowManagerGlobalLock mWindowManagerGlobalLock; + private WebViewUpdateService mWebViewUpdateService; + private DisplayManagerService mDisplayManagerService; + private PackageManagerService mPackageManagerService; + private PackageManager mPackageManager; + private ContentResolver mContentResolver; + private EntropyMixer mEntropyMixer; + private DataLoaderManagerService mDataLoaderManagerService; + private long mIncrementalServiceHandle = 0; + + private boolean mFirstBoot; + private final int mStartCount; + private final boolean mRuntimeRestart; + private final long mRuntimeStartElapsedTime; + private final long mRuntimeStartUptime; + + private static final String START_HIDL_SERVICES = "StartHidlServices"; + private static final String START_SENSOR_MANAGER_SERVICE = "StartISensorManagerService"; + private static final String START_BLOB_STORE_SERVICE = "startBlobStoreManagerService"; + + private static final String SYSPROP_START_COUNT = "sys.system_server.start_count"; + private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed"; + private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime"; + + private Future mZygotePreload; + + private final SystemServerDumper mDumper = new SystemServerDumper(); + + /** + * The pending WTF to be logged into dropbox. + */ + private static LinkedList> sPendingWtfs; + + /** Start the IStats services. This is a blocking call and can take time. */ + private static native void startIStatsService(); + + /** Start the ISensorManager service. This is a blocking call and can take time. */ + private static native void startISensorManagerService(); + + /** + * Start the memtrack proxy service. + */ + private static native void startMemtrackProxyService(); + + /** + * Start all HIDL services that are run inside the system server. This may take some time. + */ + private static native void startHidlServices(); + + /** + * Mark this process' heap as profileable. Only for debug builds. + */ + private static native void initZygoteChildHeapProfiling(); + + private static final String SYSPROP_FDTRACK_ENABLE_THRESHOLD = + "persist.sys.debug.fdtrack_enable_threshold"; + private static final String SYSPROP_FDTRACK_ABORT_THRESHOLD = + "persist.sys.debug.fdtrack_abort_threshold"; + private static final String SYSPROP_FDTRACK_INTERVAL = + "persist.sys.debug.fdtrack_interval"; + + private static int getMaxFd() { + FileDescriptor fd = null; + try { + fd = Os.open("/dev/null", O_RDONLY | O_CLOEXEC, 0); + return fd.getInt$(); + } catch (ErrnoException ex) { + Slog.e("System", "Failed to get maximum fd: " + ex); + } finally { + if (fd != null) { + try { + Os.close(fd); + } catch (ErrnoException ex) { + // If Os.close threw, something went horribly wrong. + throw new RuntimeException(ex); + } + } + } + + return Integer.MAX_VALUE; + } + + private static native void fdtrackAbort(); + + private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/"); + private static final int MAX_HEAP_DUMPS = 2; + + /** + * Dump system_server's heap. + * + * For privacy reasons, these aren't automatically pulled into bugreports: + * they must be manually pulled by the user. + */ + private static void dumpHprof() { + // hprof dumps are rather large, so ensure we don't fill the disk by generating + // hundreds of these that will live forever. + TreeSet existingTombstones = new TreeSet<>(); + for (File file : HEAP_DUMP_PATH.listFiles()) { + if (!file.isFile()) { + continue; + } + if (!file.getName().startsWith("fdtrack-")) { + continue; + } + existingTombstones.add(file); + } + if (existingTombstones.size() >= MAX_HEAP_DUMPS) { + for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) { + // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place. + existingTombstones.pollLast(); + } + for (File file : existingTombstones) { + if (!file.delete()) { + Slog.w("System", "Failed to clean up hprof " + file); + } + } + } + + try { + String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof"; + Debug.dumpHprofData(filename); + } catch (IOException ex) { + Slog.e("System", "Failed to dump fdtrack hprof", ex); + } + } + + /** + * Spawn a thread that monitors for fd leaks. + */ + private static void spawnFdLeakCheckThread() { + final int enableThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ENABLE_THRESHOLD, 1600); + final int abortThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ABORT_THRESHOLD, 3000); + final int checkInterval = SystemProperties.getInt(SYSPROP_FDTRACK_INTERVAL, 120); + + new Thread(() -> { + boolean enabled = false; + long nextWrite = 0; + + while (true) { + int maxFd = getMaxFd(); + if (maxFd > enableThreshold) { + // Do a manual GC to clean up fds that are hanging around as garbage. + System.gc(); + System.runFinalization(); + maxFd = getMaxFd(); + } + + if (maxFd > enableThreshold && !enabled) { + Slog.i("System", "fdtrack enable threshold reached, enabling"); + FrameworkStatsLog.write(FrameworkStatsLog.FDTRACK_EVENT_OCCURRED, + FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__ENABLED, + maxFd); + + System.loadLibrary("fdtrack"); + enabled = true; + } else if (maxFd > abortThreshold) { + Slog.i("System", "fdtrack abort threshold reached, dumping and aborting"); + FrameworkStatsLog.write(FrameworkStatsLog.FDTRACK_EVENT_OCCURRED, + FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__ABORTING, + maxFd); + + dumpHprof(); + fdtrackAbort(); + } else { + // Limit this to once per hour. + long now = SystemClock.elapsedRealtime(); + if (now > nextWrite) { + nextWrite = now + 60 * 60 * 1000; + FrameworkStatsLog.write(FrameworkStatsLog.FDTRACK_EVENT_OCCURRED, + enabled ? FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__ENABLED + : FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__DISABLED, + maxFd); + } + } + + try { + Thread.sleep(checkInterval * 1000); + } catch (InterruptedException ex) { + continue; + } + } + }).start(); + } + + /** + * Start native Incremental Service and get its handle. + */ + private static native long startIncrementalService(); + + /** + * Inform Incremental Service that system is ready. + */ + private static native void setIncrementalServiceSystemReady(long incrementalServiceHandle); + + /** + * The main entry point from zygote. + */ + public static void main(String[] args) { + new SystemServer().run(); + } + + public SystemServer() { + // Check for factory test mode. + mFactoryTestMode = FactoryTest.getMode(); + + // Record process start information. + mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1; + mRuntimeStartElapsedTime = SystemClock.elapsedRealtime(); + mRuntimeStartUptime = SystemClock.uptimeMillis(); + Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime, + mRuntimeStartElapsedTime, mRuntimeStartUptime); + + // Remember if it's runtime restart or reboot. + mRuntimeRestart = mStartCount > 1; + } + + @Override + public String getDumpableName() { + return SystemServer.class.getSimpleName(); + } + + @Override + public void dump(PrintWriter pw, String[] args) { + pw.printf("Runtime restart: %b\n", mRuntimeRestart); + pw.printf("Start count: %d\n", mStartCount); + pw.print("Runtime start-up time: "); + TimeUtils.formatDuration(mRuntimeStartUptime, pw); pw.println(); + pw.print("Runtime start-elapsed time: "); + TimeUtils.formatDuration(mRuntimeStartElapsedTime, pw); pw.println(); + } + + /** + * Service used to dump {@link SystemServer} state that is not associated with any service. + * + *

To dump all services: + * + *

adb shell dumpsys system_server_dumper
+ * + *

To get a list of all services: + * + *

adb shell dumpsys system_server_dumper --list
+ * + *

To dump a specific service (use {@code --list} above to get service names): + * + *

adb shell dumpsys system_server_dumper --name NAME
+ */ + private final class SystemServerDumper extends Binder { + + @GuardedBy("mDumpables") + private final ArrayMap mDumpables = new ArrayMap<>(4); + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + final boolean hasArgs = args != null && args.length > 0; + + synchronized (mDumpables) { + if (hasArgs && "--list".equals(args[0])) { + final int dumpablesSize = mDumpables.size(); + for (int i = 0; i < dumpablesSize; i++) { + pw.println(mDumpables.keyAt(i)); + } + return; + } + + if (hasArgs && "--name".equals(args[0])) { + if (args.length < 2) { + pw.println("Must pass at least one argument to --name"); + return; + } + final String name = args[1]; + final Dumpable dumpable = mDumpables.get(name); + if (dumpable == null) { + pw.printf("No dumpable named %s\n", name); + return; + } + + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + // Strip --name DUMPABLE from args + final String[] actualArgs = Arrays.copyOfRange(args, 2, args.length); + dumpable.dump(ipw, actualArgs); + } + return; + } + + final int dumpablesSize = mDumpables.size(); + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + for (int i = 0; i < dumpablesSize; i++) { + final Dumpable dumpable = mDumpables.valueAt(i); + ipw.printf("%s:\n", dumpable.getDumpableName()); + ipw.increaseIndent(); + dumpable.dump(ipw, args); + ipw.decreaseIndent(); + ipw.println(); + } + } + } + } + + private void addDumpable(@NonNull Dumpable dumpable) { + synchronized (mDumpables) { + mDumpables.put(dumpable.getDumpableName(), dumpable); + } + } + } + + private void run() { + TimingsTraceAndSlog t = new TimingsTraceAndSlog(); + try { + t.traceBegin("InitBeforeStartServices"); + + // Record the process start information in sys props. + SystemProperties.set(SYSPROP_START_COUNT, String.valueOf(mStartCount)); + SystemProperties.set(SYSPROP_START_ELAPSED, String.valueOf(mRuntimeStartElapsedTime)); + SystemProperties.set(SYSPROP_START_UPTIME, String.valueOf(mRuntimeStartUptime)); + + EventLog.writeEvent(EventLogTags.SYSTEM_SERVER_START, + mStartCount, mRuntimeStartUptime, mRuntimeStartElapsedTime); + + // Set the device's time zone (a system property) if it is not set or is invalid. + SystemTimeZone.initializeTimeZoneSettingsIfRequired(); + + // If the system has "persist.sys.language" and friends set, replace them with + // "persist.sys.locale". Note that the default locale at this point is calculated + // using the "-Duser.locale" command line flag. That flag is usually populated by + // AndroidRuntime using the same set of system properties, but only the system_server + // and system apps are allowed to set them. + // + // NOTE: Most changes made here will need an equivalent change to + // core/jni/AndroidRuntime.cpp + if (!SystemProperties.get("persist.sys.language").isEmpty()) { + final String languageTag = Locale.getDefault().toLanguageTag(); + + SystemProperties.set("persist.sys.locale", languageTag); + SystemProperties.set("persist.sys.language", ""); + SystemProperties.set("persist.sys.country", ""); + SystemProperties.set("persist.sys.localevar", ""); + } + + // The system server should never make non-oneway calls + Binder.setWarnOnBlocking(true); + // The system server should always load safe labels + PackageItemInfo.forceSafeLabels(); + + // Default to FULL within the system server. + SQLiteGlobal.sDefaultSyncMode = SQLiteGlobal.SYNC_MODE_FULL; + + // Deactivate SQLiteCompatibilityWalFlags until settings provider is initialized + SQLiteCompatibilityWalFlags.init(null); + + // Here we go! + Slog.i(TAG, "Entered the Android system server!"); + final long uptimeMillis = SystemClock.elapsedRealtime(); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis); + if (!mRuntimeRestart) { + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog + .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_INIT_START, + uptimeMillis); + } + + // In case the runtime switched since last boot (such as when + // the old runtime was removed in an OTA), set the system + // property so that it is in sync. We can't do this in + // libnativehelper's JniInvocation::Init code where we already + // had to fallback to a different runtime because it is + // running as root and we need to be the system user to set + // the property. http://b/11463182 + SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary()); + + // Mmmmmm... more memory! + VMRuntime.getRuntime().clearGrowthLimit(); + + // Some devices rely on runtime fingerprint generation, so make sure + // we've defined it before booting further. + Build.ensureFingerprintProperty(); + + // Within the system server, it is an error to access Environment paths without + // explicitly specifying a user. + Environment.setUserRequired(true); + + // Within the system server, any incoming Bundles should be defused + // to avoid throwing BadParcelableException. + BaseBundle.setShouldDefuse(true); + + // Within the system server, when parceling exceptions, include the stack trace + Parcel.setStackTraceParceling(true); + + // Ensure binder calls into the system always run at foreground priority. + BinderInternal.disableBackgroundScheduling(true); + + // Increase the number of binder threads in system_server + BinderInternal.setMaxThreads(sMaxBinderThreads); + + // Prepare the main looper thread (this thread). + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_FOREGROUND); + android.os.Process.setCanSelfBackground(false); + Looper.prepareMainLooper(); + Looper.getMainLooper().setSlowLogThresholdMs( + SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); + + SystemServiceRegistry.sEnableServiceNotFoundWtf = true; + + // Initialize native services. + System.loadLibrary("android_servers"); + + // Allow heap / perf profiling. + initZygoteChildHeapProfiling(); + + // Debug builds - spawn a thread to monitor for fd leaks. + if (Build.IS_DEBUGGABLE) { + spawnFdLeakCheckThread(); + } + + // Check whether we failed to shut down last time we tried. + // This call may not return. + performPendingShutdown(); + + // Initialize the system context. + createSystemContext(); + + // Call per-process mainline module initialization. + ActivityThread.initializeMainlineModules(); + + // Sets the dumper service + ServiceManager.addService("system_server_dumper", mDumper); + mDumper.addDumpable(this); + + // Create the system service manager. + mSystemServiceManager = new SystemServiceManager(mSystemContext); + mSystemServiceManager.setStartInfo(mRuntimeRestart, + mRuntimeStartElapsedTime, mRuntimeStartUptime); + mDumper.addDumpable(mSystemServiceManager); + + LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); + // Prepare the thread pool for init tasks that can be parallelized + SystemServerInitThreadPool tp = SystemServerInitThreadPool.start(); + mDumper.addDumpable(tp); + + // Lazily load the pre-installed system font map in SystemServer only if we're not doing + // the optimized font loading in the FontManagerService. + if (!com.android.text.flags.Flags.useOptimizedBoottimeFontLoading() + && Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + Slog.i(TAG, "Loading pre-installed system font map."); + Typeface.loadPreinstalledSystemFontMap(); + } + + // Attach JVMTI agent if this is a debuggable build and the system property is set. + if (Build.IS_DEBUGGABLE) { + // Property is of the form "library_path=parameters". + String jvmtiAgent = SystemProperties.get("persist.sys.dalvik.jvmtiagent"); + if (!jvmtiAgent.isEmpty()) { + int equalIndex = jvmtiAgent.indexOf('='); + String libraryPath = jvmtiAgent.substring(0, equalIndex); + String parameterList = + jvmtiAgent.substring(equalIndex + 1, jvmtiAgent.length()); + // Attach the agent. + try { + Debug.attachJvmtiAgent(libraryPath, parameterList, null); + } catch (Exception e) { + Slog.e("System", "*************************************************"); + Slog.e("System", "********** Failed to load jvmti plugin: " + jvmtiAgent); + } + } + } + } finally { + t.traceEnd(); // InitBeforeStartServices + } + + // Setup the default WTF handler + RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf); + + // Start services. + try { + t.traceBegin("StartServices"); + startBootstrapServices(t); + startCoreServices(t); + startOtherServices(t); + startApexServices(t); + // Only update the timeout after starting all the services so that we use + // the default timeout to start system server. + updateWatchdogTimeout(t); + CriticalEventLog.getInstance().logSystemServerStarted(); + } catch (Throwable ex) { + Slog.e("System", "******************************************"); + Slog.e("System", "************ Failure starting system services", ex); + throw ex; + } finally { + t.traceEnd(); // StartServices + } + + StrictMode.initVmDefaults(null); + + if (!mRuntimeRestart && !isFirstBootOrUpgrade()) { + final long uptimeMillis = SystemClock.elapsedRealtime(); + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_READY, + uptimeMillis); + final long maxUptimeMillis = 60 * 1000; + if (uptimeMillis > maxUptimeMillis) { + Slog.wtf(SYSTEM_SERVER_TIMING_TAG, + "SystemServer init took too long. uptimeMillis=" + uptimeMillis); + } + } + + // Set binder transaction callback after starting system services + Binder.setTransactionCallback(new IBinderCallback() { + @Override + public void onTransactionError(int pid, int code, int flags, int err) { + mActivityManagerService.frozenBinderTransactionDetected(pid, code, flags, err); + } + }); + + // Loop forever. + Looper.loop(); + throw new RuntimeException("Main thread loop unexpectedly exited"); + } + + private static boolean isValidTimeZoneId(String timezoneProperty) { + return timezoneProperty != null + && !timezoneProperty.isEmpty() + && ZoneInfoDb.getInstance().hasTimeZone(timezoneProperty); + } + + private boolean isFirstBootOrUpgrade() { + return mPackageManagerService.isFirstBoot() || mPackageManagerService.isDeviceUpgrading(); + } + + private void reportWtf(String msg, Throwable e) { + Slog.w(TAG, "***********************************************"); + Slog.wtf(TAG, "BOOT FAILURE " + msg, e); + } + + private void performPendingShutdown() { + final String shutdownAction = SystemProperties.get( + ShutdownThread.SHUTDOWN_ACTION_PROPERTY, ""); + if (shutdownAction != null && shutdownAction.length() > 0) { + boolean reboot = (shutdownAction.charAt(0) == '1'); + + final String reason; + if (shutdownAction.length() > 1) { + reason = shutdownAction.substring(1, shutdownAction.length()); + } else { + reason = null; + } + + // If it's a pending reboot into recovery to apply an update, + // always make sure uncrypt gets executed properly when needed. + // If '/cache/recovery/block.map' hasn't been created, stop the + // reboot which will fail for sure, and get a chance to capture a + // bugreport when that's still feasible. (Bug: 26444951) + if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { + File packageFile = new File(UNCRYPT_PACKAGE_FILE); + if (packageFile.exists()) { + String filename = null; + try { + filename = FileUtils.readTextFile(packageFile, 0, null); + } catch (IOException e) { + Slog.e(TAG, "Error reading uncrypt package file", e); + } + + if (filename != null && filename.startsWith("/data")) { + if (!new File(BLOCK_MAP_FILE).exists()) { + Slog.e(TAG, "Can't find block map file, uncrypt failed or " + + "unexpected runtime restart?"); + return; + } + } + } + } + Runnable runnable = new Runnable() { + @Override + public void run() { + ShutdownThread.rebootOrShutdown(null, reboot, reason); + } + }; + + // ShutdownThread must run on a looper capable of displaying the UI. + Message msg = Message.obtain(UiThread.getHandler(), runnable); + msg.setAsynchronous(true); + UiThread.getHandler().sendMessage(msg); + + } + } + + private void createSystemContext() { + ActivityThread activityThread = ActivityThread.systemMain(); + mSystemContext = activityThread.getSystemContext(); + mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); + + final Context systemUiContext = activityThread.getSystemUiContext(); + systemUiContext.setTheme(DEFAULT_SYSTEM_THEME); + } + + /** + * Starts the small tangle of critical services that are needed to get the system off the + * ground. These services have complex mutual dependencies which is why we initialize them all + * in one place here. Unless your service is also entwined in these dependencies, it should be + * initialized in one of the other functions. + */ + private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startBootstrapServices"); + + t.traceBegin("ArtModuleServiceInitializer"); + // This needs to happen before DexUseManagerLocal init. We do it here to avoid colliding + // with a GC. ArtModuleServiceInitializer is a class from a separate dex file + // "service-art.jar", so referencing it involves the class linker. The class linker and the + // GC are mutually exclusive (b/263486535). Therefore, we do this here to force trigger the + // class linker earlier. If we did this later, especially after PackageManagerService init, + // the class linker would be consistently blocked by a GC because PackageManagerService + // allocates a lot of memory and almost certainly triggers a GC. + ArtModuleServiceInitializer.setArtModuleServiceManager(new ArtModuleServiceManager()); + t.traceEnd(); + + // Start the watchdog as early as possible so we can crash the system server + // if we deadlock during early boot + t.traceBegin("StartWatchdog"); + final Watchdog watchdog = Watchdog.getInstance(); + watchdog.start(); + mDumper.addDumpable(watchdog); + t.traceEnd(); + + Slog.i(TAG, "Reading configuration..."); + final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig"; + t.traceBegin(TAG_SYSTEM_CONFIG); + SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG); + t.traceEnd(); + + // Platform compat service is used by ActivityManagerService, PackageManagerService, and + // possibly others in the future. b/135010838. + t.traceBegin("PlatformCompat"); + PlatformCompat platformCompat = new PlatformCompat(mSystemContext); + ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat); + ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE, + new PlatformCompatNative(platformCompat)); + AppCompatCallbacks.install(new long[0]); + t.traceEnd(); + + // FileIntegrityService responds to requests from apps and the system. It needs to run after + // the source (i.e. keystore) is ready, and before the apps (or the first customer in the + // system) run. + t.traceBegin("StartFileIntegrityService"); + mSystemServiceManager.startService(FileIntegrityService.class); + t.traceEnd(); + + // Wait for installd to finish starting up so that it has a chance to + // create critical directories such as /data/user with the appropriate + // permissions. We need this to complete before we initialize other services. + t.traceBegin("StartInstaller"); + Installer installer = mSystemServiceManager.startService(Installer.class); + t.traceEnd(); + + // In some cases after launching an app we need to access device identifiers, + // therefore register the device identifier policy before the activity manager. + t.traceBegin("DeviceIdentifiersPolicyService"); + mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class); + t.traceEnd(); + + // Starts a service for reading runtime flag overrides, and keeping processes + // in sync with one another. + t.traceBegin("StartFeatureFlagsService"); + mSystemServiceManager.startService(FeatureFlagsService.class); + t.traceEnd(); + + // Uri Grants Manager. + t.traceBegin("UriGrantsManagerService"); + mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartPowerStatsService"); + // Tracks rail data to be used for power statistics. + mSystemServiceManager.startService(PowerStatsService.class); + t.traceEnd(); + + t.traceBegin("StartIStatsService"); + startIStatsService(); + t.traceEnd(); + + // Start MemtrackProxyService before ActivityManager, so that early calls + // to Memtrack::getMemory() don't fail. + t.traceBegin("MemtrackProxyService"); + startMemtrackProxyService(); + t.traceEnd(); + + // Start AccessCheckingService which provides new implementation for permission and app op. + t.traceBegin("StartAccessCheckingService"); + LocalServices.addService(PermissionMigrationHelper.class, + new PermissionMigrationHelperImpl()); + LocalServices.addService(AppOpMigrationHelper.class, + new AppOpMigrationHelperImpl()); + mSystemServiceManager.startService(AccessCheckingService.class); + t.traceEnd(); + + // Activity manager runs the show. + t.traceBegin("StartActivityManager"); + // TODO: Might need to move after migration to WM. + ActivityTaskManagerService atm = mSystemServiceManager.startService( + ActivityTaskManagerService.Lifecycle.class).getService(); + mActivityManagerService = ActivityManagerService.Lifecycle.startService( + mSystemServiceManager, atm); + mActivityManagerService.setSystemServiceManager(mSystemServiceManager); + mActivityManagerService.setInstaller(installer); + mWindowManagerGlobalLock = atm.getGlobalLock(); + t.traceEnd(); + + // Data loader manager service needs to be started before package manager + t.traceBegin("StartDataLoaderManagerService"); + mDataLoaderManagerService = mSystemServiceManager.startService( + DataLoaderManagerService.class); + t.traceEnd(); + + // Incremental service needs to be started before package manager + t.traceBegin("StartIncrementalService"); + mIncrementalServiceHandle = startIncrementalService(); + t.traceEnd(); + + // Power manager needs to be started early because other services need it. + // Native daemons may be watching for it to be registered so it must be ready + // to handle incoming binder calls immediately (including being able to verify + // the permissions for those calls). + t.traceBegin("StartPowerManager"); + mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class); + t.traceEnd(); + + t.traceBegin("StartThermalManager"); + mSystemServiceManager.startService(ThermalManagerService.class); + t.traceEnd(); + + t.traceBegin("StartHintManager"); + mSystemServiceManager.startService(HintManagerService.class); + t.traceEnd(); + + // Now that the power manager has been started, let the activity manager + // initialize power management features. + t.traceBegin("InitPowerManagement"); + mActivityManagerService.initPowerManagement(); + t.traceEnd(); + + // Bring up recovery system in case a rescue party needs a reboot + t.traceBegin("StartRecoverySystemService"); + mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class); + t.traceEnd(); + + // Now that we have the bare essentials of the OS up and running, take + // note that we just booted, which might send out a rescue party if + // we're stuck in a runtime restart loop. + RescueParty.registerHealthObserver(mSystemContext); + PackageWatchdog.getInstance(mSystemContext).noteBoot(); + + // Manages LEDs and display backlight so we need it to bring up the display. + t.traceBegin("StartLightsService"); + mSystemServiceManager.startService(LightsService.class); + t.traceEnd(); + + t.traceBegin("StartDisplayOffloadService"); + // Package manager isn't started yet; need to use SysProp not hardware feature + if (SystemProperties.getBoolean("config.enable_display_offload", false)) { + mSystemServiceManager.startService(WEAR_DISPLAYOFFLOAD_SERVICE_CLASS); + } + t.traceEnd(); + + // Display manager is needed to provide display metrics before package manager + // starts up. + t.traceBegin("StartDisplayManager"); + mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class); + t.traceEnd(); + + // We need the default display before we can initialize the package manager. + t.traceBegin("WaitForDisplay"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + t.traceEnd(); + + // Start the package manager. + if (!mRuntimeRestart) { + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog + .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_START, + SystemClock.elapsedRealtime()); + } + + t.traceBegin("StartDomainVerificationService"); + DomainVerificationService domainVerificationService = new DomainVerificationService( + mSystemContext, SystemConfig.getInstance(), platformCompat); + mSystemServiceManager.startService(domainVerificationService); + t.traceEnd(); + + t.traceBegin("StartPackageManagerService"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain"); + mPackageManagerService = PackageManagerService.main( + mSystemContext, installer, domainVerificationService, + mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); + } + + mFirstBoot = mPackageManagerService.isFirstBoot(); + mPackageManager = mSystemContext.getPackageManager(); + t.traceEnd(); + + t.traceBegin("DexUseManagerLocal"); + // DexUseManagerLocal needs to be loaded after PackageManagerLocal has been registered, but + // before PackageManagerService starts processing binder calls to notifyDexLoad. + LocalManagerRegistry.addManager( + DexUseManagerLocal.class, DexUseManagerLocal.createInstance(mSystemContext)); + t.traceEnd(); + + if (!mRuntimeRestart && !isFirstBootOrUpgrade()) { + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog + .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_READY, + SystemClock.elapsedRealtime()); + } + // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename + // A/B artifacts after boot, before anything else might touch/need them. + boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", false); + if (!disableOtaDexopt) { + t.traceBegin("StartOtaDexOptService"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("moveab"); + OtaDexoptService.main(mSystemContext, mPackageManagerService); + } catch (Throwable e) { + reportWtf("starting OtaDexOptService", e); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("moveab"); + t.traceEnd(); + } + } + + if (Build.IS_ARC) { + t.traceBegin("StartArcSystemHealthService"); + mSystemServiceManager.startService(ARC_SYSTEM_HEALTH_SERVICE); + t.traceEnd(); + } + + t.traceBegin("StartUserManagerService"); + mSystemServiceManager.startService(UserManagerService.LifeCycle.class); + t.traceEnd(); + + // Initialize attribute cache used to cache resources from packages. + t.traceBegin("InitAttributerCache"); + AttributeCache.init(mSystemContext); + t.traceEnd(); + + // Set up the Application instance for the system process and get started. + t.traceBegin("SetSystemProcess"); + mActivityManagerService.setSystemProcess(); + t.traceEnd(); + + // The package receiver depends on the activity service in order to get registered. + platformCompat.registerPackageReceiver(mSystemContext); + + // Complete the watchdog setup with an ActivityManager instance and listen for reboots + // Do this only after the ActivityManagerService is properly started as a system process + t.traceBegin("InitWatchdog"); + watchdog.init(mSystemContext, mActivityManagerService); + t.traceEnd(); + + // DisplayManagerService needs to setup android.display scheduling related policies + // since setSystemProcess() would have overridden policies due to setProcessGroup + mDisplayManagerService.setupSchedulerPolicies(); + + // Manages Overlay packages + t.traceBegin("StartOverlayManagerService"); + mSystemServiceManager.startService(new OverlayManagerService(mSystemContext)); + t.traceEnd(); + + // Manages Resources packages + t.traceBegin("StartResourcesManagerService"); + ResourcesManagerService resourcesService = new ResourcesManagerService(mSystemContext); + resourcesService.setActivityManagerService(mActivityManagerService); + mSystemServiceManager.startService(resourcesService); + t.traceEnd(); + + t.traceBegin("StartSensorPrivacyService"); + mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext)); + t.traceEnd(); + + if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) { + // DisplayManager needs the overlay immediately. + mActivityManagerService.updateSystemUiContext(); + LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged(); + } + + // The sensor service needs access to package manager service, app ops + // service, and permissions service, therefore we start it after them. + t.traceBegin("StartSensorService"); + mSystemServiceManager.startService(SensorService.class); + t.traceEnd(); + t.traceEnd(); // startBootstrapServices + } + + /** + * Starts some essential services that are not tangled up in the bootstrap process. + */ + private void startCoreServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startCoreServices"); + + // Service for system config + t.traceBegin("StartSystemConfigService"); + mSystemServiceManager.startService(SystemConfigService.class); + t.traceEnd(); + + t.traceBegin("StartBatteryService"); + // Tracks the battery level. Requires LightService. + mSystemServiceManager.startService(BatteryService.class); + t.traceEnd(); + + // Tracks application usage stats. + t.traceBegin("StartUsageService"); + mSystemServiceManager.startService(UsageStatsService.class); + mActivityManagerService.setUsageStatsManager( + LocalServices.getService(UsageStatsManagerInternal.class)); + t.traceEnd(); + + // Tracks whether the updatable WebView is in a ready state and watches for update installs. + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { + t.traceBegin("StartWebViewUpdateService"); + mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); + t.traceEnd(); + } + + // Tracks and caches the device state. + t.traceBegin("StartCachedDeviceStateService"); + mSystemServiceManager.startService(CachedDeviceStateService.class); + t.traceEnd(); + + // Tracks cpu time spent in binder calls + t.traceBegin("StartBinderCallsStatsService"); + mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class); + t.traceEnd(); + + // Tracks time spent in handling messages in handlers. + t.traceBegin("StartLooperStatsService"); + mSystemServiceManager.startService(LooperStatsService.Lifecycle.class); + t.traceEnd(); + + // Manages apk rollbacks. + t.traceBegin("StartRollbackManagerService"); + mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // Tracks native tombstones. + t.traceBegin("StartNativeTombstoneManagerService"); + mSystemServiceManager.startService(NativeTombstoneManagerService.class); + t.traceEnd(); + + // Service to capture bugreports. + t.traceBegin("StartBugreportManagerService"); + mSystemServiceManager.startService(BugreportManagerService.class); + t.traceEnd(); + + // Service for GPU and GPU driver. + t.traceBegin("GpuService"); + mSystemServiceManager.startService(GpuService.class); + t.traceEnd(); + + // Handles system process requests for remotely provisioned keys & data. + t.traceBegin("StartRemoteProvisioningService"); + mSystemServiceManager.startService(RemoteProvisioningService.class); + t.traceEnd(); + + // TODO(b/277600174): Start CpuMonitorService on all builds and not just on debuggable + // builds once the Android JobScheduler starts using this service. + if (Build.IS_DEBUGGABLE || Build.IS_ENG) { + // Service for CPU monitor. + t.traceBegin("CpuMonitorService"); + mSystemServiceManager.startService(CpuMonitorService.class); + t.traceEnd(); + } + + t.traceEnd(); // startCoreServices + } + + /** + * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized. + */ + private void startOtherServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startOtherServices"); + mSystemServiceManager.updateOtherServicesStartIndex(); + + final Context context = mSystemContext; + DynamicSystemService dynamicSystem = null; + IStorageManager storageManager = null; + NetworkManagementService networkManagement = null; + VpnManagerService vpnManager = null; + VcnManagementService vcnManagement = null; + NetworkPolicyManagerService networkPolicy = null; + WindowManagerService wm = null; + NetworkTimeUpdateService networkTimeUpdater = null; + InputManagerService inputManager = null; + TelephonyRegistry telephonyRegistry = null; + ConsumerIrService consumerIr = null; + MmsServiceBroker mmsService = null; + HardwarePropertiesManagerService hardwarePropertiesService = null; + PacProxyService pacProxyService = null; + + boolean disableSystemTextClassifier = SystemProperties.getBoolean( + "config.disable_systemtextclassifier", false); + + boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", + false); + boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", + false); + + boolean isWatch = context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH); + + boolean isArc = context.getPackageManager().hasSystemFeature( + "org.chromium.arc"); + + boolean isTv = context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LEANBACK); + + boolean enableVrService = context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE); + + // For debugging RescueParty + if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) { + throw new RuntimeException(); + } + + try { + final String SECONDARY_ZYGOTE_PRELOAD = "SecondaryZygotePreload"; + // We start the preload ~1s before the webview factory preparation, to + // ensure that it completes before the 32 bit relro process is forked + // from the zygote. In the event that it takes too long, the webview + // RELRO process will block, but it will do so without holding any locks. + mZygotePreload = SystemServerInitThreadPool.submit(() -> { + try { + Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD); + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD); + String[] abis32 = Build.SUPPORTED_32_BIT_ABIS; + if (abis32.length > 0 && !Process.ZYGOTE_PROCESS.preloadDefault(abis32[0])) { + Slog.e(TAG, "Unable to preload default resources for secondary"); + } + traceLog.traceEnd(); + } catch (Exception ex) { + Slog.e(TAG, "Exception preloading default resources", ex); + } + }, SECONDARY_ZYGOTE_PRELOAD); + + t.traceBegin("StartKeyAttestationApplicationIdProviderService"); + ServiceManager.addService("sec_key_att_app_id_provider", + new KeyAttestationApplicationIdProviderService(context)); + t.traceEnd(); + + t.traceBegin("StartKeyChainSystemService"); + mSystemServiceManager.startService(KeyChainSystemService.class); + t.traceEnd(); + + t.traceBegin("StartBinaryTransparencyService"); + mSystemServiceManager.startService(BinaryTransparencyService.class); + t.traceEnd(); + + t.traceBegin("StartSchedulingPolicyService"); + ServiceManager.addService("scheduling_policy", new SchedulingPolicyService()); + t.traceEnd(); + + // TelecomLoader hooks into classes with defined HFP logic, + // so check for either telephony or microphone. + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELECOM) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + t.traceBegin("StartTelecomLoaderService"); + mSystemServiceManager.startService(TelecomLoaderService.class); + t.traceEnd(); + } + + t.traceBegin("StartTelephonyRegistry"); + telephonyRegistry = new TelephonyRegistry( + context, new TelephonyRegistry.ConfigurationProvider()); + ServiceManager.addService("telephony.registry", telephonyRegistry); + t.traceEnd(); + + t.traceBegin("StartEntropyMixer"); + mEntropyMixer = new EntropyMixer(context); + t.traceEnd(); + + mContentResolver = context.getContentResolver(); + + // The AccountManager must come before the ContentService + t.traceBegin("StartAccountManagerService"); + mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartContentService"); + mSystemServiceManager.startService(CONTENT_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("InstallSystemProviders"); + mActivityManagerService.getContentProviderHelper().installSystemProviders(); + // Device configuration used to be part of System providers + mSystemServiceManager.startService(UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS); + // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags + SQLiteCompatibilityWalFlags.reset(); + t.traceEnd(); + + // Records errors and logs, for example wtf() + // Currently this service indirectly depends on SettingsProvider so do this after + // InstallSystemProviders. + t.traceBegin("StartDropBoxManager"); + mSystemServiceManager.startService(DropBoxManagerService.class); + t.traceEnd(); + + if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) { + t.traceBegin("StartEnhancedConfirmationService"); + mSystemServiceManager.startService(ENHANCED_CONFIRMATION_SERVICE_CLASS); + t.traceEnd(); + } + + // Grants default permissions and defines roles + t.traceBegin("StartRoleManagerService"); + LocalManagerRegistry.addManager(RoleServicePlatformHelper.class, + new RoleServicePlatformHelperImpl(mSystemContext)); + mSystemServiceManager.startService(ROLE_SERVICE_CLASS); + t.traceEnd(); + + if (!isTv) { + t.traceBegin("StartVibratorManagerService"); + mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class); + t.traceEnd(); + } + + t.traceBegin("StartDynamicSystemService"); + dynamicSystem = new DynamicSystemService(context); + ServiceManager.addService("dynamic_system", dynamicSystem); + t.traceEnd(); + + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) { + t.traceBegin("StartConsumerIrService"); + consumerIr = new ConsumerIrService(context); + ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr); + t.traceEnd(); + } + + // TODO(aml-jobscheduler): Think about how to do it properly. + t.traceBegin("StartResourceEconomy"); + mSystemServiceManager.startService(RESOURCE_ECONOMY_SERVICE_CLASS); + t.traceEnd(); + + // TODO(aml-jobscheduler): Think about how to do it properly. + t.traceBegin("StartAlarmManagerService"); + mSystemServiceManager.startService(ALARM_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartInputManagerService"); + inputManager = new InputManagerService(context); + t.traceEnd(); + + t.traceBegin("DeviceStateManagerService"); + mSystemServiceManager.startService(DeviceStateManagerService.class); + t.traceEnd(); + + if (!disableCameraService) { + t.traceBegin("StartCameraServiceProxy"); + mSystemServiceManager.startService(CameraServiceProxy.class); + t.traceEnd(); + } + + t.traceBegin("StartWindowManagerService"); + // WMS needs sensor service ready + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE); + wm = WindowManagerService.main(context, inputManager, !mFirstBoot, + new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); + ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, + DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); + ServiceManager.addService(Context.INPUT_SERVICE, inputManager, + /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); + t.traceEnd(); + + //ccy liutianzuo + t.traceBegin("StartRTCPushService"); + ServiceManager.addService("screenrtc_server", new RTCPushService(mSystemContext, wm, null, inputManager)); + t.traceEnd(); + //ccy end + + t.traceBegin("SetWindowManagerService"); + mActivityManagerService.setWindowManager(wm); + t.traceEnd(); + + t.traceBegin("WindowManagerServiceOnInitReady"); + wm.onInitReady(); + t.traceEnd(); + + // Start receiving calls from SensorManager services. Start in a separate thread + // because it need to connect to SensorManager. This has to start + // after PHASE_WAIT_FOR_SENSOR_SERVICE is done. + SystemServerInitThreadPool.submit(() -> { + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(START_SENSOR_MANAGER_SERVICE); + startISensorManagerService(); + traceLog.traceEnd(); + }, START_SENSOR_MANAGER_SERVICE); + + SystemServerInitThreadPool.submit(() -> { + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(START_HIDL_SERVICES); + startHidlServices(); + traceLog.traceEnd(); + }, START_HIDL_SERVICES); + + if (!isWatch && enableVrService) { + t.traceBegin("StartVrManagerService"); + mSystemServiceManager.startService(VrManagerService.class); + t.traceEnd(); + } + + t.traceBegin("StartInputManager"); + inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); + inputManager.start(); + t.traceEnd(); + + // TODO: Use service dependencies instead. + t.traceBegin("DisplayManagerWindowManagerAndInputReady"); + mDisplayManagerService.windowManagerAndInputReady(); + t.traceEnd(); + + if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) { + Slog.i(TAG, "No Bluetooth Service (factory test)"); + } else if (!context.getPackageManager().hasSystemFeature + (PackageManager.FEATURE_BLUETOOTH)) { + Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)"); + } else { + t.traceBegin("StartBluetoothService"); + mSystemServiceManager.startServiceFromJar(BLUETOOTH_SERVICE_CLASS, + BLUETOOTH_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + t.traceBegin("IpConnectivityMetrics"); + mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS); + t.traceEnd(); + + t.traceBegin("NetworkWatchlistService"); + mSystemServiceManager.startService(NetworkWatchlistService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("PinnerService"); + mSystemServiceManager.startService(PinnerService.class); + t.traceEnd(); + + if (Build.IS_DEBUGGABLE && ProfcollectForwardingService.enabled()) { + t.traceBegin("ProfcollectForwardingService"); + mSystemServiceManager.startService(ProfcollectForwardingService.class); + t.traceEnd(); + } + + t.traceBegin("SignedConfigService"); + SignedConfigService.registerUpdateReceiver(mSystemContext); + t.traceEnd(); + + t.traceBegin("AppIntegrityService"); + mSystemServiceManager.startService(AppIntegrityManagerService.class); + t.traceEnd(); + + t.traceBegin("StartLogcatManager"); + mSystemServiceManager.startService(LogcatManagerService.class); + t.traceEnd(); + + } catch (Throwable e) { + Slog.e("System", "******************************************"); + Slog.e("System", "************ Failure starting core service"); + throw e; + } + + // Before things start rolling, be sure we have decided whether + // we are in safe mode. + final boolean safeMode = wm.detectSafeMode(); + if (safeMode) { + // If yes, immediately turn on the global setting for airplane mode. + // Note that this does not send broadcasts at this stage because + // subsystems are not yet up. We will send broadcasts later to ensure + // all listeners have the chance to react with special handling. + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 1); + } else if (context.getResources().getBoolean(R.bool.config_autoResetAirplaneMode)) { + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0); + } + + StatusBarManagerService statusBar = null; + INotificationManager notification = null; + CountryDetectorService countryDetector = null; + ILockSettings lockSettings = null; + MediaRouterService mediaRouter = null; + + // Bring up services needed for UI. + if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) { + t.traceBegin("StartInputMethodManagerLifecycle"); + String immsClassName = context.getResources().getString( + R.string.config_deviceSpecificInputMethodManagerService); + if (immsClassName.isEmpty()) { + mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class); + } else { + try { + Slog.i(TAG, "Starting custom IMMS: " + immsClassName); + mSystemServiceManager.startService(immsClassName); + } catch (Throwable e) { + reportWtf("starting " + immsClassName, e); + } + } + t.traceEnd(); + + t.traceBegin("StartAccessibilityManagerService"); + try { + mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting Accessibility Manager", e); + } + t.traceEnd(); + } + + t.traceBegin("MakeDisplayReady"); + try { + wm.displayReady(); + } catch (Throwable e) { + reportWtf("making display ready", e); + } + t.traceEnd(); + + if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) { + if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) { + t.traceBegin("StartStorageManagerService"); + try { + /* + * NotificationManagerService is dependant on StorageManagerService, + * (for media / usb notifications) so we must start StorageManagerService first. + */ + mSystemServiceManager.startService(STORAGE_MANAGER_SERVICE_CLASS); + storageManager = IStorageManager.Stub.asInterface( + ServiceManager.getService("mount")); + } catch (Throwable e) { + reportWtf("starting StorageManagerService", e); + } + t.traceEnd(); + + t.traceBegin("StartStorageStatsService"); + try { + mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting StorageStatsService", e); + } + t.traceEnd(); + } + } + + // We start this here so that we update our configuration to set watch or television + // as appropriate. + t.traceBegin("StartUiModeManager"); + mSystemServiceManager.startService(UiModeManagerService.class); + t.traceEnd(); + + t.traceBegin("StartLocaleManagerService"); + try { + mSystemServiceManager.startService(LocaleManagerService.class); + } catch (Throwable e) { + reportWtf("starting LocaleManagerService service", e); + } + t.traceEnd(); + + t.traceBegin("StartGrammarInflectionService"); + try { + mSystemServiceManager.startService(GrammaticalInflectionService.class); + } catch (Throwable e) { + reportWtf("starting GrammarInflectionService service", e); + } + t.traceEnd(); + + t.traceBegin("StartAppHibernationService"); + mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("ArtManagerLocal"); + DexOptHelper.initializeArtManagerLocal(context, mPackageManagerService); + t.traceEnd(); + + t.traceBegin("UpdatePackagesIfNeeded"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("dexopt"); + mPackageManagerService.updatePackagesIfNeeded(); + } catch (Throwable e) { + reportWtf("update packages", e); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("dexopt"); + } + t.traceEnd(); + + t.traceBegin("PerformFstrimIfNeeded"); + try { + mPackageManagerService.performFstrimIfNeeded(); + } catch (Throwable e) { + reportWtf("performing fstrim", e); + } + t.traceEnd(); + + final DevicePolicyManagerService.Lifecycle dpms; + if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) { + dpms = null; + } else { + t.traceBegin("StartLockSettingsService"); + try { + mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS); + lockSettings = ILockSettings.Stub.asInterface( + ServiceManager.getService("lock_settings")); + } catch (Throwable e) { + reportWtf("starting LockSettingsService service", e); + } + t.traceEnd(); + + final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals(""); + if (hasPdb) { + t.traceBegin("StartPersistentDataBlock"); + mSystemServiceManager.startService(PersistentDataBlockService.class); + t.traceEnd(); + } + + if (Build.IS_ARC && SystemProperties.getInt("ro.boot.dev_mode", 0) == 1) { + t.traceBegin("StartArcPersistentDataBlock"); + mSystemServiceManager.startService(ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartTestHarnessMode"); + mSystemServiceManager.startService(TestHarnessModeService.class); + t.traceEnd(); + + if (hasPdb || OemLockService.isHalPresent()) { + // Implementation depends on pdb or the OemLock HAL + t.traceBegin("StartOemLockService"); + mSystemServiceManager.startService(OemLockService.class); + t.traceEnd(); + } + + t.traceBegin("StartDeviceIdleController"); + mSystemServiceManager.startService(DEVICE_IDLE_CONTROLLER_CLASS); + t.traceEnd(); + + // Always start the Device Policy Manager, so that the API is compatible with + // API8. + t.traceBegin("StartDevicePolicyManager"); + dpms = mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartStatusBarManagerService"); + try { + statusBar = new StatusBarManagerService(context); + statusBar.publishGlobalActionsProvider(); + ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar, false, + DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO); + } catch (Throwable e) { + reportWtf("starting StatusBarManagerService", e); + } + t.traceEnd(); + + if (deviceHasConfigString(context, + R.string.config_defaultMusicRecognitionService)) { + t.traceBegin("StartMusicRecognitionManagerService"); + mSystemServiceManager.startService(MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, + "MusicRecognitionManagerService not defined by OEM or disabled by flag"); + } + + startContentCaptureService(context, t); + startAttentionService(context, t); + startRotationResolverService(context, t); + startSystemCaptionsManagerService(context, t); + startTextToSpeechManagerService(context, t); + startWearableSensingService(t); + + if (deviceHasConfigString( + context, R.string.config_defaultAmbientContextDetectionService)) { + t.traceBegin("StartAmbientContextService"); + mSystemServiceManager.startService(AMBIENT_CONTEXT_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "AmbientContextManagerService not defined by OEM or disabled by flag"); + } + + // System Speech Recognition Service + t.traceBegin("StartSpeechRecognitionManagerService"); + mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // App prediction manager service + if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) { + t.traceBegin("StartAppPredictionService"); + mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "AppPredictionService not defined by OEM"); + } + + // Content suggestions manager service + if (deviceHasConfigString(context, R.string.config_defaultContentSuggestionsService)) { + t.traceBegin("StartContentSuggestionsService"); + mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "ContentSuggestionsService not defined by OEM"); + } + + // Search UI manager service + if (deviceHasConfigString(context, R.string.config_defaultSearchUiService)) { + t.traceBegin("StartSearchUiService"); + mSystemServiceManager.startService(SEARCH_UI_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + // Smartspace manager service + if (deviceHasConfigString(context, R.string.config_defaultSmartspaceService)) { + t.traceBegin("StartSmartspaceService"); + mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "SmartspaceManagerService not defined by OEM or disabled by flag"); + } + + t.traceBegin("InitConnectivityModuleConnector"); + try { + ConnectivityModuleConnector.getInstance().init(context); + } catch (Throwable e) { + reportWtf("initializing ConnectivityModuleConnector", e); + } + t.traceEnd(); + + t.traceBegin("InitNetworkStackClient"); + try { + NetworkStackClient.getInstance().init(); + } catch (Throwable e) { + reportWtf("initializing NetworkStackClient", e); + } + t.traceEnd(); + + t.traceBegin("StartNetworkManagementService"); + try { + networkManagement = NetworkManagementService.create(context); + ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement); + } catch (Throwable e) { + reportWtf("starting NetworkManagement Service", e); + } + t.traceEnd(); + + t.traceBegin("StartFontManagerService"); + mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode)); + t.traceEnd(); + + t.traceBegin("StartTextServicesManager"); + mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); + t.traceEnd(); + + if (!disableSystemTextClassifier) { + t.traceBegin("StartTextClassificationManagerService"); + mSystemServiceManager + .startService(TextClassificationManagerService.Lifecycle.class); + t.traceEnd(); + } + + t.traceBegin("StartNetworkScoreService"); + mSystemServiceManager.startService(NetworkScoreService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartNetworkStatsService"); + // This has to be called before NetworkPolicyManager because NetworkPolicyManager + // needs to take NetworkStatsService to initialize. + mSystemServiceManager.startServiceFromJar(NETWORK_STATS_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); + t.traceEnd(); + + t.traceBegin("StartNetworkPolicyManagerService"); + try { + networkPolicy = new NetworkPolicyManagerService(context, mActivityManagerService, + networkManagement); + ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); + } catch (Throwable e) { + reportWtf("starting NetworkPolicy Service", e); + } + t.traceEnd(); + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI)) { + // Wifi Service must be started first for wifi-related services. + if (!isArc) { + t.traceBegin("StartWifi"); + mSystemServiceManager.startServiceFromJar( + WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + t.traceBegin("StartWifiScanning"); + mSystemServiceManager.startServiceFromJar( + WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + } + + // ARC - ArcNetworkService registers the ARC network stack and replaces the + // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network + // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875). + if (isArc) { + t.traceBegin("StartArcNetworking"); + mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_RTT)) { + t.traceBegin("StartRttService"); + mSystemServiceManager.startServiceFromJar( + WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_AWARE)) { + t.traceBegin("StartWifiAware"); + mSystemServiceManager.startServiceFromJar( + WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_DIRECT)) { + t.traceBegin("StartWifiP2P"); + mSystemServiceManager.startServiceFromJar( + WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LOWPAN)) { + t.traceBegin("StartLowpan"); + mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartPacProxyService"); + try { + pacProxyService = new PacProxyService(context); + ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService); + } catch (Throwable e) { + reportWtf("starting PacProxyService", e); + } + t.traceEnd(); + + t.traceBegin("StartConnectivityService"); + // This has to be called after NetworkManagementService, NetworkStatsService + // and NetworkPolicyManager because ConnectivityService needs to take these + // services to initialize. + mSystemServiceManager.startServiceFromJar(CONNECTIVITY_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); + networkPolicy.bindConnectivityManager(); + t.traceEnd(); + + t.traceBegin("StartSecurityStateManagerService"); + try { + ServiceManager.addService(Context.SECURITY_STATE_SERVICE, + new SecurityStateManagerService(context)); + } catch (Throwable e) { + reportWtf("starting SecurityStateManagerService", e); + } + t.traceEnd(); + + t.traceBegin("StartVpnManagerService"); + try { + vpnManager = VpnManagerService.create(context); + ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager); + } catch (Throwable e) { + reportWtf("starting VPN Manager Service", e); + } + t.traceEnd(); + + t.traceBegin("StartVcnManagementService"); + try { + vcnManagement = VcnManagementService.create(context); + ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); + } + t.traceEnd(); + + t.traceBegin("StartSystemUpdateManagerService"); + try { + ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE, + new SystemUpdateManagerService(context)); + } catch (Throwable e) { + reportWtf("starting SystemUpdateManagerService", e); + } + t.traceEnd(); + + t.traceBegin("StartUpdateLockService"); + try { + ServiceManager.addService(Context.UPDATE_LOCK_SERVICE, + new UpdateLockService(context)); + } catch (Throwable e) { + reportWtf("starting UpdateLockService", e); + } + t.traceEnd(); + + t.traceBegin("StartNotificationManager"); + mSystemServiceManager.startService(NotificationManagerService.class); + SystemNotificationChannels.removeDeprecated(context); + SystemNotificationChannels.createAll(context); + notification = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + t.traceEnd(); + + t.traceBegin("StartDeviceMonitor"); + mSystemServiceManager.startService(DeviceStorageMonitorService.class); + t.traceEnd(); + + t.traceBegin("StartTimeDetectorService"); + try { + mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting TimeDetectorService service", e); + } + t.traceEnd(); + + t.traceBegin("StartLocationManagerService"); + mSystemServiceManager.startService(LocationManagerService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartCountryDetectorService"); + try { + countryDetector = new CountryDetectorService(context); + ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector); + } catch (Throwable e) { + reportWtf("starting Country Detector", e); + } + t.traceEnd(); + + t.traceBegin("StartTimeZoneDetectorService"); + try { + mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting TimeZoneDetectorService service", e); + } + t.traceEnd(); + + t.traceBegin("StartAltitudeService"); + try { + mSystemServiceManager.startService(AltitudeService.Lifecycle.class); + } catch (Throwable e) { + reportWtf("starting AltitudeService service", e); + } + t.traceEnd(); + + t.traceBegin("StartLocationTimeZoneManagerService"); + try { + mSystemServiceManager.startService(LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting LocationTimeZoneManagerService service", e); + } + t.traceEnd(); + + if (context.getResources().getBoolean(R.bool.config_enableGnssTimeUpdateService)) { + t.traceBegin("StartGnssTimeUpdateService"); + try { + mSystemServiceManager.startService(GNSS_TIME_UPDATE_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting GnssTimeUpdateService service", e); + } + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartSearchManagerService"); + try { + mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting Search Service", e); + } + t.traceEnd(); + } + + if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) { + t.traceBegin("StartWallpaperManagerService"); + mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.i(TAG, "Wallpaper service disabled by config"); + } + + // WallpaperEffectsGeneration manager service + if (deviceHasConfigString(context, + R.string.config_defaultWallpaperEffectsGenerationService)) { + t.traceBegin("StartWallpaperEffectsGenerationService"); + mSystemServiceManager.startService( + WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartAudioService"); + if (!isArc) { + mSystemServiceManager.startService(AudioService.Lifecycle.class); + } else { + String className = context.getResources() + .getString(R.string.config_deviceSpecificAudioService); + try { + mSystemServiceManager.startService(className + "$Lifecycle"); + } catch (Throwable e) { + reportWtf("starting " + className, e); + } + } + t.traceEnd(); + + t.traceBegin("StartSoundTriggerMiddlewareService"); + mSystemServiceManager.startService(SoundTriggerMiddlewareService.Lifecycle.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) { + t.traceBegin("StartBroadcastRadioService"); + mSystemServiceManager.startService(BroadcastRadioService.class); + t.traceEnd(); + } + + if (!isTv) { + t.traceBegin("StartDockObserver"); + mSystemServiceManager.startService(DockObserver.class); + t.traceEnd(); + } + + if (isWatch) { + t.traceBegin("StartThermalObserver"); + mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS); + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartWiredAccessoryManager"); + try { + // Listen for wired headset changes + inputManager.setWiredAccessoryCallbacks( + new WiredAccessoryManager(context, inputManager)); + } catch (Throwable e) { + reportWtf("starting WiredAccessoryManager", e); + } + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) { + // Start MIDI Manager service + t.traceBegin("StartMidiManager"); + mSystemServiceManager.startService(MIDI_SERVICE_CLASS); + t.traceEnd(); + } + + // Start ADB Debugging Service + t.traceBegin("StartAdbService"); + try { + mSystemServiceManager.startService(ADB_SERVICE_CLASS); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting AdbService"); + } + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST) + || mPackageManager.hasSystemFeature( + PackageManager.FEATURE_USB_ACCESSORY) + || Build.IS_EMULATOR) { + // Manage USB host and device support + t.traceBegin("StartUsbService"); + mSystemServiceManager.startService(USB_SERVICE_CLASS); + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartSerialService"); + mSystemServiceManager.startService(SerialService.Lifecycle.class); + t.traceEnd(); + } + + t.traceBegin("StartHardwarePropertiesManagerService"); + try { + hardwarePropertiesService = new HardwarePropertiesManagerService(context); + ServiceManager.addService(Context.HARDWARE_PROPERTIES_SERVICE, + hardwarePropertiesService); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e); + } + t.traceEnd(); + + if (!isWatch) { + t.traceBegin("StartTwilightService"); + mSystemServiceManager.startService(TwilightService.class); + t.traceEnd(); + } + + t.traceBegin("StartColorDisplay"); + mSystemServiceManager.startService(ColorDisplayService.class); + t.traceEnd(); + + // TODO(aml-jobscheduler): Think about how to do it properly. + t.traceBegin("StartJobScheduler"); + mSystemServiceManager.startService(JOB_SCHEDULER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartSoundTrigger"); + mSystemServiceManager.startService(SoundTriggerService.class); + t.traceEnd(); + + t.traceBegin("StartTrustManager"); + mSystemServiceManager.startService(TrustManagerService.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { + t.traceBegin("StartBackupManager"); + mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS) + || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) { + t.traceBegin("StartAppWidgetService"); + mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS); + t.traceEnd(); + } + + // We need to always start this service, regardless of whether the + // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care + // of initializing various settings. It will internally modify its behavior + // based on that feature. + t.traceBegin("StartVoiceRecognitionManager"); + mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) { + t.traceBegin("StartGestureLauncher"); + mSystemServiceManager.startService(GestureLauncherService.class); + t.traceEnd(); + } + t.traceBegin("StartSensorNotification"); + mSystemServiceManager.startService(SensorNotificationService.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXT_HUB)) { + t.traceBegin("StartContextHubSystemService"); + mSystemServiceManager.startService(ContextHubSystemService.class); + t.traceEnd(); + } + + t.traceBegin("StartDiskStatsService"); + try { + ServiceManager.addService("diskstats", new DiskStatsService(context)); + } catch (Throwable e) { + reportWtf("starting DiskStats Service", e); + } + t.traceEnd(); + + t.traceBegin("RuntimeService"); + try { + ServiceManager.addService("runtime", new RuntimeService(context)); + } catch (Throwable e) { + reportWtf("starting RuntimeService", e); + } + t.traceEnd(); + + if (!isWatch && !disableNetworkTime) { + t.traceBegin("StartNetworkTimeUpdateService"); + try { + networkTimeUpdater = new NetworkTimeUpdateService(context); + ServiceManager.addService("network_time_update_service", networkTimeUpdater); + } catch (Throwable e) { + reportWtf("starting NetworkTimeUpdate service", e); + } + t.traceEnd(); + } + + t.traceBegin("CertBlacklister"); + try { + CertBlacklister blacklister = new CertBlacklister(context); + } catch (Throwable e) { + reportWtf("starting CertBlacklister", e); + } + t.traceEnd(); + + if (EmergencyAffordanceManager.ENABLED) { + // EmergencyMode service + t.traceBegin("StartEmergencyAffordanceService"); + mSystemServiceManager.startService(EmergencyAffordanceService.class); + t.traceEnd(); + } + + t.traceBegin(START_BLOB_STORE_SERVICE); + mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode) + t.traceBegin("StartDreamManager"); + mSystemServiceManager.startService(DreamManagerService.class); + t.traceEnd(); + + t.traceBegin("AddGraphicsStatsService"); + ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE, + new GraphicsStatsService(context)); + t.traceEnd(); + + if (CoverageService.ENABLED) { + t.traceBegin("AddCoverageService"); + ServiceManager.addService(CoverageService.COVERAGE_SERVICE, new CoverageService()); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) { + t.traceBegin("StartPrintManager"); + mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartAttestationVerificationService"); + mSystemServiceManager.startService(AttestationVerificationManagerService.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) { + t.traceBegin("StartCompanionDeviceManager"); + mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (context.getResources().getBoolean(R.bool.config_enableVirtualDeviceManager)) { + t.traceBegin("StartVirtualDeviceManager"); + mSystemServiceManager.startService(VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartRestrictionManager"); + mSystemServiceManager.startService(RestrictionsManagerService.class); + t.traceEnd(); + + t.traceBegin("StartMediaSessionService"); + mSystemServiceManager.startService(MEDIA_SESSION_SERVICE_CLASS); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) { + t.traceBegin("StartHdmiControlService"); + mSystemServiceManager.startService(HdmiControlService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + t.traceBegin("StartTvInteractiveAppManager"); + mSystemServiceManager.startService(TvInteractiveAppManagerService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + t.traceBegin("StartTvInputManager"); + mSystemServiceManager.startService(TvInputManagerService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TUNER)) { + t.traceBegin("StartTunerResourceManager"); + mSystemServiceManager.startService(TunerResourceManagerService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { + t.traceBegin("StartMediaResourceMonitor"); + mSystemServiceManager.startService(MEDIA_RESOURCE_MONITOR_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + t.traceBegin("StartTvRemoteService"); + mSystemServiceManager.startService(TvRemoteService.class); + t.traceEnd(); + } + + t.traceBegin("StartMediaRouterService"); + try { + mediaRouter = new MediaRouterService(context); + ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter); + } catch (Throwable e) { + reportWtf("starting MediaRouterService", e); + } + t.traceEnd(); + + final boolean hasFeatureFace + = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE); + final boolean hasFeatureIris + = mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS); + final boolean hasFeatureFingerprint + = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); + + if (hasFeatureFace) { + t.traceBegin("StartFaceSensor"); + final FaceService faceService = + mSystemServiceManager.startService(FaceService.class); + t.traceEnd(); + } + + if (hasFeatureIris) { + t.traceBegin("StartIrisSensor"); + mSystemServiceManager.startService(IrisService.class); + t.traceEnd(); + } + + if (hasFeatureFingerprint) { + t.traceBegin("StartFingerprintSensor"); + final FingerprintService fingerprintService = + mSystemServiceManager.startService(FingerprintService.class); + t.traceEnd(); + } + + // Start this service after all biometric sensor services are started. + t.traceBegin("StartBiometricService"); + mSystemServiceManager.startService(BiometricService.class); + t.traceEnd(); + + t.traceBegin("StartAuthService"); + mSystemServiceManager.startService(AuthService.class); + t.traceEnd(); + + if (android.adaptiveauth.Flags.enableAdaptiveAuth()) { + t.traceBegin("StartAdaptiveAuthService"); + mSystemServiceManager.startService(AdaptiveAuthService.class); + t.traceEnd(); + } + + if (!isWatch) { + // We don't run this on watches as there are no plans to use the data logged + // on watch devices. + t.traceBegin("StartDynamicCodeLoggingService"); + try { + DynamicCodeLoggingService.schedule(context); + } catch (Throwable e) { + reportWtf("starting DynamicCodeLoggingService", e); + } + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartPruneInstantAppsJobService"); + try { + PruneInstantAppsJobService.schedule(context); + } catch (Throwable e) { + reportWtf("StartPruneInstantAppsJobService", e); + } + t.traceEnd(); + } + + t.traceBegin("StartSelinuxAuditLogsService"); + try { + SelinuxAuditLogsService.schedule(context); + } catch (Throwable e) { + reportWtf("starting SelinuxAuditLogsService", e); + } + t.traceEnd(); + + // LauncherAppsService uses ShortcutService. + t.traceBegin("StartShortcutServiceLifecycle"); + mSystemServiceManager.startService(ShortcutService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartLauncherAppsService"); + mSystemServiceManager.startService(LauncherAppsService.class); + t.traceEnd(); + + t.traceBegin("StartCrossProfileAppsService"); + mSystemServiceManager.startService(CrossProfileAppsService.class); + t.traceEnd(); + + t.traceBegin("StartPeopleService"); + mSystemServiceManager.startService(PeopleService.class); + t.traceEnd(); + + t.traceBegin("StartMediaMetricsManager"); + mSystemServiceManager.startService(MediaMetricsManagerService.class); + t.traceEnd(); + + t.traceBegin("StartBackgroundInstallControlService"); + mSystemServiceManager.startService(BackgroundInstallControlService.class); + t.traceEnd(); + } + + t.traceBegin("StartMediaProjectionManager"); + mSystemServiceManager.startService(MediaProjectionManagerService.class); + t.traceEnd(); + + if (isWatch) { + // Must be started before services that depend it, e.g. WearConnectivityService + t.traceBegin("StartWearPowerService"); + mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartHealthService"); + mSystemServiceManager.startService(HEALTH_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartSystemStateDisplayService"); + mSystemServiceManager.startService(SYSTEM_STATE_DISPLAY_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearConnectivityService"); + mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearDisplayService"); + mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS); + t.traceEnd(); + + if (Build.IS_DEBUGGABLE) { + t.traceBegin("StartWearDebugService"); + mSystemServiceManager.startService(WEAR_DEBUG_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartWearTimeService"); + mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearSettingsService"); + mSystemServiceManager.startService(WEAR_SETTINGS_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearModeService"); + mSystemServiceManager.startService(WEAR_MODE_SERVICE_CLASS); + t.traceEnd(); + + boolean enableWristOrientationService = SystemProperties.getBoolean( + "config.enable_wristorientation", false); + if (enableWristOrientationService) { + t.traceBegin("StartWristOrientationService"); + mSystemServiceManager.startService(WRIST_ORIENTATION_SERVICE_CLASS); + t.traceEnd(); + } + } + + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) { + t.traceBegin("StartSliceManagerService"); + mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) { + t.traceBegin("StartIoTSystemService"); + mSystemServiceManager.startService(IOT_SERVICE_CLASS); + t.traceEnd(); + } + + // Statsd helper + t.traceBegin("StartStatsCompanion"); + mSystemServiceManager.startServiceFromJar( + STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH); + t.traceEnd(); + + // Reboot Readiness + t.traceBegin("StartRebootReadinessManagerService"); + mSystemServiceManager.startServiceFromJar( + REBOOT_READINESS_LIFECYCLE_CLASS, SCHEDULING_APEX_PATH); + t.traceEnd(); + + // Statsd pulled atoms + t.traceBegin("StartStatsPullAtomService"); + mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS); + t.traceEnd(); + + // Log atoms to statsd from bootstrap processes. + t.traceBegin("StatsBootstrapAtomService"); + mSystemServiceManager.startService(STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS); + t.traceEnd(); + + // Incidentd and dumpstated helper + t.traceBegin("StartIncidentCompanionService"); + mSystemServiceManager.startService(IncidentCompanionService.class); + t.traceEnd(); + + // SdkSandboxManagerService + t.traceBegin("StarSdkSandboxManagerService"); + mSystemServiceManager.startService(SDK_SANDBOX_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // AdServicesManagerService (PP API service) + t.traceBegin("StartAdServicesManagerService"); + mSystemServiceManager.startService(AD_SERVICES_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // OnDevicePersonalizationSystemService + t.traceBegin("StartOnDevicePersonalizationSystemService"); + mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS); + t.traceEnd(); + + // Profiling + if (android.server.Flags.telemetryApisService()) { + t.traceBegin("StartProfilingCompanion"); + mSystemServiceManager.startServiceFromJar(PROFILING_SERVICE_LIFECYCLE_CLASS, + PROFILING_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (safeMode) { + mActivityManagerService.enterSafeMode(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + // MMS service broker + t.traceBegin("StartMmsService"); + mmsService = mSystemServiceManager.startService(MmsServiceBroker.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOFILL)) { + t.traceBegin("StartAutoFillService"); + mSystemServiceManager.startService(AUTO_FILL_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CREDENTIALS)) { + boolean credentialManagerEnabled = + DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CREDENTIAL, + CredentialManager.DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, true); + if (credentialManagerEnabled) { + if(isWatch && + !android.credentials.flags.Flags.wearCredentialManagerEnabled()) { + Slog.d(TAG, "CredentialManager disabled on wear."); + } else { + t.traceBegin("StartCredentialManagerService"); + mSystemServiceManager.startService(CREDENTIAL_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + } else { + Slog.d(TAG, "CredentialManager disabled."); + } + } + + // Translation manager service + if (deviceHasConfigString(context, R.string.config_defaultTranslationService)) { + t.traceBegin("StartTranslationManagerService"); + mSystemServiceManager.startService(TRANSLATION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "TranslationService not defined by OEM"); + } + + // NOTE: ClipboardService depends on ContentCapture and Autofill + t.traceBegin("StartClipboardService"); + mSystemServiceManager.startService(ClipboardService.class); + t.traceEnd(); + + t.traceBegin("AppServiceManager"); + mSystemServiceManager.startService(AppBindingService.Lifecycle.class); + t.traceEnd(); + + // Perfetto TracingServiceProxy + t.traceBegin("startTracingServiceProxy"); + mSystemServiceManager.startService(TracingServiceProxy.class); + t.traceEnd(); + + // It is now time to start up the app processes... + + t.traceBegin("MakeLockSettingsServiceReady"); + if (lockSettings != null) { + try { + lockSettings.systemReady(); + } catch (Throwable e) { + reportWtf("making Lock Settings Service ready", e); + } + } + t.traceEnd(); + + // Needed by DevicePolicyManager for initialization + t.traceBegin("StartBootPhaseLockSettingsReady"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_LOCK_SETTINGS_READY); + t.traceEnd(); + + // Create initial user if needed, which should be done early since some system services rely + // on it in their setup, but likely needs to be done after LockSettingsService is ready. + final HsumBootUserInitializer hsumBootUserInitializer = + HsumBootUserInitializer.createInstance( + mActivityManagerService, mPackageManagerService, mContentResolver, + context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin)); + if (hsumBootUserInitializer != null) { + t.traceBegin("HsumBootUserInitializer.init"); + hsumBootUserInitializer.init(t); + t.traceEnd(); + } + + CommunalProfileInitializer communalProfileInitializer = null; + if (UserManager.isCommunalProfileEnabled()) { + t.traceBegin("CommunalProfileInitializer.init"); + communalProfileInitializer = + new CommunalProfileInitializer(mActivityManagerService); + communalProfileInitializer.init(t); + t.traceEnd(); + } else { + t.traceBegin("CommunalProfileInitializer.removeCommunalProfileIfPresent"); + CommunalProfileInitializer.removeCommunalProfileIfPresent(); + t.traceEnd(); + } + + t.traceBegin("StartBootPhaseSystemServicesReady"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_SYSTEM_SERVICES_READY); + t.traceEnd(); + + t.traceBegin("MakeWindowManagerServiceReady"); + try { + wm.systemReady(); + } catch (Throwable e) { + reportWtf("making Window Manager Service ready", e); + } + t.traceEnd(); + + t.traceBegin("RegisterLogMteState"); + try { + LogMteState.register(context); + } catch (Throwable e) { + reportWtf("RegisterLogMteState", e); + } + t.traceEnd(); + + // Emit any pending system_server WTFs + synchronized (SystemService.class) { + if (sPendingWtfs != null) { + mActivityManagerService.schedulePendingSystemServerWtfs(sPendingWtfs); + sPendingWtfs = null; + } + } + + if (safeMode) { + mActivityManagerService.showSafeModeOverlay(); + } + + // Update the configuration for this context by hand, because we're going + // to start using it before the config change done in wm.systemReady() will + // propagate to it. + final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY); + DisplayMetrics metrics = new DisplayMetrics(); + context.getDisplay().getMetrics(metrics); + context.getResources().updateConfiguration(config, metrics); + + // The system context's theme may be configuration-dependent. + final Theme systemTheme = context.getTheme(); + if (systemTheme.getChangingConfigurations() != 0) { + systemTheme.rebase(); + } + + // Permission policy service + t.traceBegin("StartPermissionPolicyService"); + mSystemServiceManager.startService(PermissionPolicyService.class); + t.traceEnd(); + + t.traceBegin("MakePackageManagerServiceReady"); + mPackageManagerService.systemReady(); + t.traceEnd(); + + t.traceBegin("MakeDisplayManagerServiceReady"); + try { + // TODO: use boot phase and communicate this flag some other way + mDisplayManagerService.systemReady(safeMode); + } catch (Throwable e) { + reportWtf("making Display Manager Service ready", e); + } + t.traceEnd(); + + mSystemServiceManager.setSafeMode(safeMode); + + // Start device specific services + t.traceBegin("StartDeviceSpecificServices"); + final String[] classes = mSystemContext.getResources().getStringArray( + R.array.config_deviceSpecificSystemServices); + for (final String className : classes) { + t.traceBegin("StartDeviceSpecificServices " + className); + try { + mSystemServiceManager.startService(className); + } catch (Throwable e) { + reportWtf("starting " + className, e); + } + t.traceEnd(); + } + t.traceEnd(); + + t.traceBegin("GameManagerService"); + mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) { + t.traceBegin("UwbService"); + mSystemServiceManager.startServiceFromJar(UWB_SERVICE_CLASS, UWB_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + t.traceBegin("StartBootPhaseDeviceSpecificServicesReady"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); + t.traceEnd(); + + t.traceBegin("StartSafetyCenterService"); + mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("AppSearchModule"); + mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS); + t.traceEnd(); + + if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) { + t.traceBegin("IsolatedCompilationService"); + mSystemServiceManager.startService(ISOLATED_COMPILATION_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartMediaCommunicationService"); + mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("AppCompatOverridesService"); + mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("HealthConnectManagerService"); + mSystemServiceManager.startService(HEALTHCONNECT_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_LOCK)) { + t.traceBegin("DeviceLockService"); + mSystemServiceManager.startServiceFromJar(DEVICE_LOCK_SERVICE_CLASS, + DEVICE_LOCK_APEX_PATH); + t.traceEnd(); + } + + if (android.permission.flags.Flags.sensitiveNotificationAppProtection() + || android.view.flags.Flags.sensitiveContentAppProtection()) { + t.traceBegin("StartSensitiveContentProtectionManager"); + mSystemServiceManager.startService(SensitiveContentProtectionManagerService.class); + t.traceEnd(); + } + + // These are needed to propagate to the runnable below. + final NetworkManagementService networkManagementF = networkManagement; + final NetworkPolicyManagerService networkPolicyF = networkPolicy; + final CountryDetectorService countryDetectorF = countryDetector; + final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; + final InputManagerService inputManagerF = inputManager; + final TelephonyRegistry telephonyRegistryF = telephonyRegistry; + final MediaRouterService mediaRouterF = mediaRouter; + final MmsServiceBroker mmsServiceF = mmsService; + final VpnManagerService vpnManagerF = vpnManager; + final VcnManagementService vcnManagementF = vcnManagement; + final WindowManagerService windowManagerF = wm; + final ConnectivityManager connectivityF = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + + // We now tell the activity manager it is okay to run third party + // code. It will call back into us once it has gotten to the state + // where third party code can really run (but before it has actually + // started launching the initial applications), for us to complete our + // initialization. + mActivityManagerService.systemReady(() -> { + Slog.i(TAG, "Making services ready"); + t.traceBegin("StartActivityManagerReadyPhase"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY); + t.traceEnd(); + t.traceBegin("StartObservingNativeCrashes"); + try { + mActivityManagerService.startObservingNativeCrashes(); + } catch (Throwable e) { + reportWtf("observing native crashes", e); + } + t.traceEnd(); + + t.traceBegin("RegisterAppOpsPolicy"); + try { + mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy(mSystemContext)); + } catch (Throwable e) { + reportWtf("registering app ops policy", e); + } + t.traceEnd(); + + // No dependency on Webview preparation in system server. But this should + // be completed before allowing 3rd party + final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; + Future webviewPrep = null; + if (mWebViewUpdateService != null) { + webviewPrep = SystemServerInitThreadPool.submit(() -> { + Slog.i(TAG, WEBVIEW_PREPARATION); + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(WEBVIEW_PREPARATION); + ConcurrentUtils.waitForFutureNoInterrupt(mZygotePreload, "Zygote preload"); + mZygotePreload = null; + mWebViewUpdateService.prepareWebViewInSystemServer(); + traceLog.traceEnd(); + }, WEBVIEW_PREPARATION); + } + + boolean isAutomotive = mPackageManager + .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + if (isAutomotive) { + t.traceBegin("StartCarServiceHelperService"); + final SystemService cshs = mSystemServiceManager + .startService(CAR_SERVICE_HELPER_SERVICE_CLASS); + if (cshs instanceof Dumpable) { + mDumper.addDumpable((Dumpable) cshs); + } + if (cshs instanceof DevicePolicySafetyChecker) { + dpms.setDevicePolicySafetyChecker((DevicePolicySafetyChecker) cshs); + } + t.traceEnd(); + } + + if (isWatch) { + t.traceBegin("StartWearService"); + String wearServiceComponentNameString = + context.getString(R.string.config_wearServiceComponent); + + if (!TextUtils.isEmpty(wearServiceComponentNameString)) { + ComponentName wearServiceComponentName = ComponentName.unflattenFromString( + wearServiceComponentNameString); + + if (wearServiceComponentName != null) { + Intent intent = new Intent(); + intent.setComponent(wearServiceComponentName); + intent.addFlags(Intent.FLAG_DIRECT_BOOT_AUTO); + context.startServiceAsUser(intent, UserHandle.SYSTEM); + } else { + Slog.d(TAG, "Null wear service component name."); + } + } + t.traceEnd(); + } + + // Enable airplane mode in safe mode. setAirplaneMode() cannot be called + // earlier as it sends broadcasts to other services. + // TODO: This may actually be too late if radio firmware already started leaking + // RF before the respective services start. However, fixing this requires changes + // to radio firmware and interfaces. + if (safeMode) { + t.traceBegin("EnableAirplaneModeInSafeMode"); + try { + connectivityF.setAirplaneMode(true); + } catch (Throwable e) { + reportWtf("enabling Airplane Mode during Safe Mode bootup", e); + } + t.traceEnd(); + } + t.traceBegin("MakeNetworkManagementServiceReady"); + try { + if (networkManagementF != null) { + networkManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making Network Managment Service ready", e); + } + CountDownLatch networkPolicyInitReadySignal = null; + if (networkPolicyF != null) { + networkPolicyInitReadySignal = networkPolicyF + .networkScoreAndNetworkManagementServiceReady(); + } + t.traceEnd(); + t.traceBegin("MakeConnectivityServiceReady"); + try { + if (connectivityF != null) { + connectivityF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making Connectivity Service ready", e); + } + t.traceEnd(); + t.traceBegin("MakeVpnManagerServiceReady"); + try { + if (vpnManagerF != null) { + vpnManagerF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VpnManagerService ready", e); + } + t.traceEnd(); + t.traceBegin("MakeVcnManagementServiceReady"); + try { + if (vcnManagementF != null) { + vcnManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VcnManagementService ready", e); + } + t.traceEnd(); + t.traceBegin("MakeNetworkPolicyServiceReady"); + try { + if (networkPolicyF != null) { + networkPolicyF.systemReady(networkPolicyInitReadySignal); + } + } catch (Throwable e) { + reportWtf("making Network Policy Service ready", e); + } + t.traceEnd(); + + // Wait for all packages to be prepared + mPackageManagerService.waitForAppDataPrepared(); + + // It is now okay to let the various system services start their + // third party code... + t.traceBegin("PhaseThirdPartyAppsCanStart"); + // confirm webview completion before starting 3rd party + if (webviewPrep != null) { + ConcurrentUtils.waitForFutureNoInterrupt(webviewPrep, WEBVIEW_PREPARATION); + } + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START); + t.traceEnd(); + + if (hsumBootUserInitializer != null) { + t.traceBegin("HsumBootUserInitializer.systemRunning"); + hsumBootUserInitializer.systemRunning(t); + t.traceEnd(); + } + + t.traceBegin("StartNetworkStack"); + try { + // Note : the network stack is creating on-demand objects that need to send + // broadcasts, which means it currently depends on being started after + // ActivityManagerService.mSystemReady and ActivityManagerService.mProcessesReady + // are set to true. Be careful if moving this to a different place in the + // startup sequence. + NetworkStackClient.getInstance().start(); + } catch (Throwable e) { + reportWtf("starting Network Stack", e); + } + t.traceEnd(); + + t.traceBegin("StartTethering"); + try { + // TODO: hide implementation details, b/146312721. + ConnectivityModuleConnector.getInstance().startModuleService( + TETHERING_CONNECTOR_CLASS, + PERMISSION_MAINLINE_NETWORK_STACK, service -> { + ServiceManager.addService(Context.TETHERING_SERVICE, service, + false /* allowIsolated */, + DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + }); + } catch (Throwable e) { + reportWtf("starting Tethering", e); + } + t.traceEnd(); + + t.traceBegin("MakeCountryDetectionServiceReady"); + try { + if (countryDetectorF != null) { + countryDetectorF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying CountryDetectorService running", e); + } + t.traceEnd(); + t.traceBegin("MakeNetworkTimeUpdateReady"); + try { + if (networkTimeUpdaterF != null) { + networkTimeUpdaterF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying NetworkTimeService running", e); + } + t.traceEnd(); + t.traceBegin("MakeInputManagerServiceReady"); + try { + // TODO(BT) Pass parameter to input manager + if (inputManagerF != null) { + inputManagerF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying InputManagerService running", e); + } + t.traceEnd(); + t.traceBegin("MakeTelephonyRegistryReady"); + try { + if (telephonyRegistryF != null) { + telephonyRegistryF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying TelephonyRegistry running", e); + } + t.traceEnd(); + t.traceBegin("MakeMediaRouterServiceReady"); + try { + if (mediaRouterF != null) { + mediaRouterF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying MediaRouterService running", e); + } + t.traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + t.traceBegin("MakeMmsServiceReady"); + try { + if (mmsServiceF != null) mmsServiceF.systemRunning(); + } catch (Throwable e) { + reportWtf("Notifying MmsService running", e); + } + t.traceEnd(); + } + + t.traceBegin("IncidentDaemonReady"); + try { + // TODO: Switch from checkService to getService once it's always + // in the build and should reliably be there. + final IIncidentManager incident = IIncidentManager.Stub.asInterface( + ServiceManager.getService(Context.INCIDENT_SERVICE)); + if (incident != null) { + incident.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying incident daemon running", e); + } + t.traceEnd(); + + if (mIncrementalServiceHandle != 0) { + t.traceBegin("MakeIncrementalServiceReady"); + setIncrementalServiceSystemReady(mIncrementalServiceHandle); + t.traceEnd(); + } + + t.traceBegin("OdsignStatsLogger"); + try { + OdsignStatsLogger.triggerStatsWrite(); + } catch (Throwable e) { + reportWtf("Triggering OdsignStatsLogger", e); + } + t.traceEnd(); + }, t); + + t.traceBegin("LockSettingsThirdPartyAppsStarted"); + LockSettingsInternal lockSettingsInternal = + LocalServices.getService(LockSettingsInternal.class); + if (lockSettingsInternal != null) { + lockSettingsInternal.onThirdPartyAppsStarted(); + } + t.traceEnd(); + + t.traceBegin("StartSystemUI"); + try { + startSystemUi(context, windowManagerF); + } catch (Throwable e) { + reportWtf("starting System UI", e); + } + t.traceEnd(); + + t.traceEnd(); // startOtherServices + } + + /** + * Starts system services defined in apexes. + * + *

Apex services must be the last category of services to start. No other service must be + * starting after this point. This is to prevent unnecessary stability issues when these apexes + * are updated outside of OTA; and to avoid breaking dependencies from system into apexes. + */ + private void startApexServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startApexServices"); + // TODO(b/192880996): get the list from "android" package, once the manifest entries + // are migrated to system manifest. + List services = ApexManager.getInstance().getApexSystemServices(); + for (ApexSystemServiceInfo info : services) { + String name = info.getName(); + String jarPath = info.getJarPath(); + t.traceBegin("starting " + name); + if (TextUtils.isEmpty(jarPath)) { + mSystemServiceManager.startService(name); + } else { + mSystemServiceManager.startServiceFromJar(name, jarPath); + } + t.traceEnd(); + } + + // make sure no other services are started after this point + mSystemServiceManager.sealStartedServices(); + + t.traceEnd(); // startApexServices + } + + private void updateWatchdogTimeout(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("UpdateWatchdogTimeout"); + Watchdog.getInstance().registerSettingsObserver(mSystemContext); + t.traceEnd(); + } + + private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) { + String serviceName = context.getString(resId); + return !TextUtils.isEmpty(serviceName); + } + + private void startSystemCaptionsManagerService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + if (!deviceHasConfigString(context, R.string.config_defaultSystemCaptionsManagerService)) { + Slog.d(TAG, "SystemCaptionsManagerService disabled because resource is not overlaid"); + return; + } + + t.traceBegin("StartSystemCaptionsManagerService"); + mSystemServiceManager.startService(SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + private void startTextToSpeechManagerService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + t.traceBegin("StartTextToSpeechManagerService"); + mSystemServiceManager.startService(TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + private void startContentCaptureService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + // First check if it was explicitly enabled by DeviceConfig + boolean explicitlyEnabled = false; + String settings = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); + if (settings != null && !settings.equalsIgnoreCase("default")) { + explicitlyEnabled = Boolean.parseBoolean(settings); + if (explicitlyEnabled) { + Slog.d(TAG, "ContentCaptureService explicitly enabled by DeviceConfig"); + } else { + Slog.d(TAG, "ContentCaptureService explicitly disabled by DeviceConfig"); + return; + } + } + + // Then check if OEM overlaid the resource that defines the service. + if (!explicitlyEnabled) { + if (!deviceHasConfigString(context, R.string.config_defaultContentCaptureService)) { + Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); + return; + } + if (!deviceHasConfigString(context, R.string.config_defaultContentProtectionService)) { + Slog.d( + TAG, + "ContentProtectionService disabled because resource is not overlaid," + + " ContentCaptureService still enabled"); + } + } + + t.traceBegin("StartContentCaptureService"); + mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS); + + ContentCaptureManagerInternal ccmi = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (ccmi != null && mActivityManagerService != null) { + mActivityManagerService.setContentCaptureManager(ccmi); + } + + t.traceEnd(); + } + + private void startAttentionService(@NonNull Context context, @NonNull TimingsTraceAndSlog t) { + if (!AttentionManagerService.isServiceConfigured(context)) { + Slog.d(TAG, "AttentionService is not configured on this device"); + return; + } + + t.traceBegin("StartAttentionManagerService"); + mSystemServiceManager.startService(AttentionManagerService.class); + t.traceEnd(); + } + + private void startRotationResolverService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + if (!RotationResolverManagerService.isServiceConfigured(context)) { + Slog.d(TAG, "RotationResolverService is not configured on this device"); + return; + } + + t.traceBegin("StartRotationResolverService"); + mSystemServiceManager.startService(RotationResolverManagerService.class); + t.traceEnd(); + + } + + private void startWearableSensingService(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startWearableSensingService"); + mSystemServiceManager.startService(WearableSensingManagerService.class); + t.traceEnd(); + } + + private static void startSystemUi(Context context, WindowManagerService windowManager) { + PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + Intent intent = new Intent(); + intent.setComponent(pm.getSystemUiServiceComponent()); + intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); + //Slog.d(TAG, "Starting service: " + intent); + context.startServiceAsUser(intent, UserHandle.SYSTEM); + windowManager.onSystemUiStarted(); + } + + /** + * Handle the serious errors during early system boot, used by {@link Log} via + * {@link com.android.internal.os.RuntimeInit}. + */ + private static boolean handleEarlySystemWtf(final IBinder app, final String tag, boolean system, + final ApplicationErrorReport.ParcelableCrashInfo crashInfo, int immediateCallerPid) { + final String processName = "system_server"; + final int myPid = myPid(); + + com.android.server.am.EventLogTags.writeAmWtf(UserHandle.getUserId(SYSTEM_UID), myPid, + processName, -1, tag, crashInfo.exceptionMessage); + + FrameworkStatsLog.write(FrameworkStatsLog.WTF_OCCURRED, SYSTEM_UID, tag, processName, + myPid, ServerProtoEnums.SYSTEM_SERVER); + + synchronized (SystemServer.class) { + if (sPendingWtfs == null) { + sPendingWtfs = new LinkedList<>(); + } + sPendingWtfs.add(new Pair<>(tag, crashInfo)); + } + return false; + } + +} diff --git a/aosp/frameworks/base/services/proguard.flags b/aosp/frameworks/base/services/proguard.flags new file mode 100755 index 0000000000000000000000000000000000000000..eeb01f030bd21f9126bd04d30926c6588afc11d2 --- /dev/null +++ b/aosp/frameworks/base/services/proguard.flags @@ -0,0 +1,137 @@ +# TODO(b/210510433): Refine and optimize this configuration. Note that this +# configuration is only used when `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA=true`. + +# Preserve line number information for debugging stack traces. +-keepattributes SourceFile,LineNumberTable + +# Allows making private and protected methods/fields public as part of +# optimization. This enables inlining of trivial getter/setter methods. +-allowaccessmodification + +# Process entrypoint +-keep class com.android.server.SystemServer { + public static void main(java.lang.String[]); +} + +# APIs referenced by dependent JAR files and modules +# TODO(b/300514883): Pull @SystemApi keep rules from system-api.pro. +-keep interface android.annotation.SystemApi +-keep @android.annotation.SystemApi class * { + public protected *; +} +-keepclasseswithmembers class * { + @android.annotation.SystemApi *; +} +# Also ensure nested classes are kept. This is overly conservative, but handles +# cases where such classes aren't explicitly marked @SystemApi. +-if @android.annotation.SystemApi class * +-keep public class <1>$** { + public protected *; +} + +# Derivatives of SystemService and other services created via reflection +-keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService { + public ; +} +-keep,allowoptimization,allowaccessmodification class * extends com.android.server.devicepolicy.BaseIDevicePolicyManager { + public (...); +} +-keep,allowoptimization,allowaccessmodification class com.android.server.wallpaper.WallpaperManagerService { + public (...); +} + +# Accessed from com.android.compos APEX +-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog { + public static void write(...); +} +#ccy keep begin +-keep class org.webrtc.** { *; } +-keep public class org.webrtc.** { *; } +-keep public class org.mediasoup.droid.** { *; } +#ccy keep end +# Various classes subclassed in or referenced via JNI in ethernet-service +-keep public class android.net.** { *; } +-keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; } +-keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; } +-keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.display.feature.DisplayManagerFlags { *; } +-keep,allowoptimization,allowaccessmodification class android.app.admin.flags.FeatureFlagsImpl { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; } +-keep,allowaccessmodification class android.app.admin.flags.Flags { *; } + +# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing) +-keep public class com.android.server.utils.Slogf { *; } + +# Referenced in wear-service +# HIDL interfaces +-keep public class android.hidl.base.** { *; } +-keep public class android.hidl.manager.** { *; } +-keep public class com.android.server.wm.WindowManagerInternal { *; } + +# Notification extractors +# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/res/values/config.xml. +-keep,allowoptimization,allowaccessmodification public class com.android.server.notification.** implements com.android.server.notification.NotificationSignalExtractor + +# OEM provided DisplayAreaPolicy.Provider defined in frameworks/base/core/res/res/values/config.xml. +-keep,allowoptimization,allowaccessmodification class com.android.server.wm.** implements com.android.server.wm.DisplayAreaPolicy$Provider + +# JNI keep rules +# The global keep rule for native methods allows stripping of such methods if they're unreferenced +# in Java. However, because system_server explicitly registers these methods from native code, +# stripping them in Java can cause runtime issues. As such, conservatively keep all such methods in +# system_server subpackages as long as the containing class is also kept or referenced. +-keepclassmembers class com.android.server.** { + native ; +} +# TODO(b/210510433): Revisit and fix with @Keep, or consider auto-generating from +# frameworks/base/services/core/jni/onload.cpp. +-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.BroadcastRadioService { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.Convert { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.Tuner { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.broadcastradio.hal1.TunerCallback { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssConfiguration$HalInterfaceVersion { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.pm.PackageManagerShellCommandDataLoader { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$RuntimeSensorCallback { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.ExternalCaptureStateTracker { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.storage.AppFuseBridge { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.tv.TvInputHal { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbAlsaJackDetector { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbAlsaMidiDevice { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorController$OnVibrationCompleteListener { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.vibrator.VibratorManagerService$OnSyncedVibrationCompleteListener { *; } +-keepclasseswithmembers,allowoptimization,allowaccessmodification class com.android.server.** { + *** *FromNative(...); +} +-keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService { + ; +} +-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager { + *** usbDeviceRemoved(...); + *** usbDeviceAdded(...); +} +-keep,allowoptimization,allowaccessmodification class **.*NativeWrapper* { *; } + +# Miscellaneous reflection keep rules +# TODO(b/210510433): Revisit and fix with @Keep. +-keep,allowoptimization,allowaccessmodification class com.android.server.usage.AppStandbyController { + public (...); +} +-keep,allowoptimization,allowaccessmodification class android.hardware.usb.gadget.** { *; } + +# Needed when optimizations enabled +# TODO(b/210510433): Revisit and fix with @Keep. +-keep,allowoptimization,allowaccessmodification class com.android.server.SystemService { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.SystemService$TargetUser { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.usage.StorageStatsManagerLocal { *; } +-keep,allowoptimization,allowaccessmodification class com.android.internal.util.** { *; } +-keep,allowoptimization,allowaccessmodification class android.os.** { *; } + +# CoverageService guards optional jacoco class references with a runtime guard, so we can safely +# suppress build-time warnings. +-dontwarn org.jacoco.agent.rt.* diff --git a/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java new file mode 100755 index 0000000000000000000000000000000000000000..372c5576c378635837d622348a229cf91b279b57 --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntRange; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import com.android.telephony.Rlog; + +import java.util.Objects; + +import android.os.SystemProperties; + + +/** + * Signal strength related information. + */ +public final class CellSignalStrengthCdma extends CellSignalStrength implements Parcelable { + + private static final String LOG_TAG = "CellSignalStrengthCdma"; + private static final boolean DBG = false; + + private int mCdmaDbm; // This value is the RSSI value + private int mCdmaEcio; // This value is the Ec/Io + private int mEvdoDbm; // This value is the EVDO RSSI value + private int mEvdoEcio; // This value is the EVDO Ec/Io + private int mEvdoSnr; // Valid values are 0-8. 8 is the highest signal to noise ratio + private int mLevel; + + /** @hide */ + public CellSignalStrengthCdma() { + setDefaultValues(); + } + + /** + * SignalStrength constructor for input from the HAL. + * + * Note that values received from the HAL require coersion to be compatible here. All values + * reported through IRadio are the negative of the actual values (which results in a positive + * input to this method. + * + *

Note that this HAL is inconsistent with UMTS-based radio techs as the value indicating + * that a field is unreported is negative, rather than a large(r) positive number. + *

Also note that to keep the public-facing methods of this class consistent with others, + * unreported values are coerced to {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} + * rather than left as -1, which is a departure from SignalStrength, which is stuck with the + * values it currently reports. + * + * @param cdmaDbm CDMA signal strength value or CellInfo.UNAVAILABLE if invalid. + * @param cdmaEcio CDMA pilot/noise ratio or CellInfo.UNAVAILABLE if invalid. + * @param evdoDbm negative of the EvDO signal strength value or CellInfo.UNAVAILABLE if invalid. + * @param evdoEcio negative of the EvDO pilot/noise ratio or CellInfo.UNAVAILABLE if invalid. + * @param evdoSnr an SNR value 0..8 or CellInfo.UNVAILABLE if invalid. + * @hide + */ + public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, + int evdoSnr) { + mCdmaDbm = inRangeOrUnavailable(cdmaDbm, -120, 0); + mCdmaEcio = inRangeOrUnavailable(cdmaEcio, -160, 0); + mEvdoDbm = inRangeOrUnavailable(evdoDbm, -120, 0); + mEvdoEcio = inRangeOrUnavailable(evdoEcio, -160, 0); + mEvdoSnr = inRangeOrUnavailable(evdoSnr, 0, 8); + + updateLevel(null, null); + } + + /** @hide */ + public CellSignalStrengthCdma(android.hardware.radio.V1_0.CdmaSignalStrength cdma, + android.hardware.radio.V1_0.EvdoSignalStrength evdo) { + // Convert from HAL values as part of construction. + this(-cdma.dbm, -cdma.ecio, -evdo.dbm, -evdo.ecio, evdo.signalNoiseRatio); + } + + /** @hide */ + public CellSignalStrengthCdma(CellSignalStrengthCdma s) { + copyFrom(s); + } + + /** @hide */ + protected void copyFrom(CellSignalStrengthCdma s) { + mCdmaDbm = s.mCdmaDbm; + mCdmaEcio = s.mCdmaEcio; + mEvdoDbm = s.mEvdoDbm; + mEvdoEcio = s.mEvdoEcio; + mEvdoSnr = s.mEvdoSnr; + mLevel = s.mLevel; + } + + /** @hide */ + @Override + public CellSignalStrengthCdma copy() { + return new CellSignalStrengthCdma(this); + } + + /** @hide */ + @Override + public void setDefaultValues() { + mCdmaDbm = CellInfo.UNAVAILABLE; + mCdmaEcio = CellInfo.UNAVAILABLE; + mEvdoDbm = CellInfo.UNAVAILABLE; + mEvdoEcio = CellInfo.UNAVAILABLE; + mEvdoSnr = CellInfo.UNAVAILABLE; + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + + /** {@inheritDoc} */ + @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + //return mLevel; + } + + /** @hide */ + @Override + public void updateLevel(PersistableBundle cc, ServiceState ss) { + int cdmaLevel = getCdmaLevel(); + int evdoLevel = getEvdoLevel(); + if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { + /* We don't know evdo, use cdma */ + mLevel = getCdmaLevel(); + } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { + /* We don't know cdma, use evdo */ + mLevel = getEvdoLevel(); + } else { + /* We know both, use the lowest level */ + mLevel = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel; + } + } + + /** + * Get the 1xRTT Level in (Android) ASU. + * + * There is no standard definition of ASU for CDMA; however, Android defines it as the + * the lesser of the following two results (for 1xRTT): + * + * + * + * + * + * + * + * + * + * + *
RSSI Range (dBm)ASU Value
-75..16
-82..-768
-90..-834
-95..-912
-100..-961
..-10199
+ * + * + * + * + * + * + * + * + * + * + *
Ec/Io Range (dB)ASU Value
-90..16
-100..-918
-115..-1014
-130..-1162
--150..-1311
..-15199
+ * @return 1xRTT Level in Android ASU {1,2,4,8,16,99} + */ + @Override + public int getAsuLevel() { + final int cdmaDbm = getCdmaDbm(); + final int cdmaEcio = getCdmaEcio(); + int cdmaAsuLevel; + int ecioAsuLevel; + + if (cdmaDbm == CellInfo.UNAVAILABLE) cdmaAsuLevel = 99; + else if (cdmaDbm >= -75) cdmaAsuLevel = 16; + else if (cdmaDbm >= -82) cdmaAsuLevel = 8; + else if (cdmaDbm >= -90) cdmaAsuLevel = 4; + else if (cdmaDbm >= -95) cdmaAsuLevel = 2; + else if (cdmaDbm >= -100) cdmaAsuLevel = 1; + else cdmaAsuLevel = 99; + + // Ec/Io are in dB*10 + if (cdmaEcio == CellInfo.UNAVAILABLE) ecioAsuLevel = 99; + else if (cdmaEcio >= -90) ecioAsuLevel = 16; + else if (cdmaEcio >= -100) ecioAsuLevel = 8; + else if (cdmaEcio >= -115) ecioAsuLevel = 4; + else if (cdmaEcio >= -130) ecioAsuLevel = 2; + else if (cdmaEcio >= -150) ecioAsuLevel = 1; + else ecioAsuLevel = 99; + + int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel; + if (DBG) log("getAsuLevel=" + level); + return level; + } + + /** + * Get cdma as level 0..4 + */ + public int getCdmaLevel() { + final int cdmaDbm = getCdmaDbm(); + final int cdmaEcio = getCdmaEcio(); + int levelDbm; + int levelEcio; + + if (cdmaDbm == CellInfo.UNAVAILABLE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT; + else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD; + else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE; + else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR; + else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + + // Ec/Io are in dB*10 + if (cdmaEcio == CellInfo.UNAVAILABLE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT; + else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD; + else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE; + else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR; + else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + + int level = (levelDbm < levelEcio) ? levelDbm : levelEcio; + if (DBG) log("getCdmaLevel=" + level); + return level; + } + + /** + * Get Evdo as level 0..4 + */ + public int getEvdoLevel() { + int evdoDbm = getEvdoDbm(); + int evdoSnr = getEvdoSnr(); + int levelEvdoDbm; + int levelEvdoSnr; + + if (evdoDbm == CellInfo.UNAVAILABLE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT; + else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD; + else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE; + else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR; + else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + + if (evdoSnr == CellInfo.UNAVAILABLE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT; + else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD; + else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE; + else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR; + else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + + int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr; + if (DBG) log("getEvdoLevel=" + level); + return level; + } + + /** + * Get the EVDO Level in (Android) ASU. + * + * There is no standard definition of ASU for CDMA; however, Android defines it as the + * the lesser of the following two results (for EVDO): + * + * + * + * + * + * + * + * + * + * + *
RSSI Range (dBm)ASU Value
-65..16
-75..-668
-85..-764
-95..-862
-105..-961
..-10699
+ * + * + * + * + * + * + * + * + * + * + *
SNR Range (unitless)ASU Value
7..16
68
54
3..42
1..21
099
+ * + * @return EVDO Level in Android ASU {1,2,4,8,16,99} + * + * @hide + */ + public int getEvdoAsuLevel() { + int evdoDbm = getEvdoDbm(); + int evdoSnr = getEvdoSnr(); + int levelEvdoDbm; + int levelEvdoSnr; + + if (evdoDbm >= -65) levelEvdoDbm = 16; + else if (evdoDbm >= -75) levelEvdoDbm = 8; + else if (evdoDbm >= -85) levelEvdoDbm = 4; + else if (evdoDbm >= -95) levelEvdoDbm = 2; + else if (evdoDbm >= -105) levelEvdoDbm = 1; + else levelEvdoDbm = 99; + + if (evdoSnr >= 7) levelEvdoSnr = 16; + else if (evdoSnr >= 6) levelEvdoSnr = 8; + else if (evdoSnr >= 5) levelEvdoSnr = 4; + else if (evdoSnr >= 3) levelEvdoSnr = 2; + else if (evdoSnr >= 1) levelEvdoSnr = 1; + else levelEvdoSnr = 99; + + int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr; + if (DBG) log("getEvdoAsuLevel=" + level); + return level; + } + + /** + * Get the signal strength as dBm + * + * @return min(CDMA RSSI, EVDO RSSI) of the measured cell. + */ + @Override + public int getDbm() { + int cdmaDbm = getCdmaDbm(); + int evdoDbm = getEvdoDbm(); + + // Use the lower value to be conservative + return (cdmaDbm < evdoDbm) ? cdmaDbm : evdoDbm; + } + + /** + * Get the CDMA RSSI value in dBm + */ + public int getCdmaDbm() { + return mCdmaDbm; + } + + /** @hide */ + public void setCdmaDbm(int cdmaDbm) { + mCdmaDbm = cdmaDbm; + } + + /** + * Get the CDMA Ec/Io value in dB*10 + */ + public int getCdmaEcio() { + return mCdmaEcio; + } + + /** @hide */ + public void setCdmaEcio(int cdmaEcio) { + mCdmaEcio = cdmaEcio; + } + + /** + * Get the EVDO RSSI value in dBm + */ + public int getEvdoDbm() { + return mEvdoDbm; + } + + /** @hide */ + public void setEvdoDbm(int evdoDbm) { + mEvdoDbm = evdoDbm; + } + + /** + * Get the EVDO Ec/Io value in dB*10 + */ + public int getEvdoEcio() { + return mEvdoEcio; + } + + /** @hide */ + public void setEvdoEcio(int evdoEcio) { + mEvdoEcio = evdoEcio; + } + + /** + * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest. + */ + public int getEvdoSnr() { + return mEvdoSnr; + } + + /** @hide */ + public void setEvdoSnr(int evdoSnr) { + mEvdoSnr = evdoSnr; + } + + @Override + public int hashCode() { + return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr, mLevel); + } + + private static final CellSignalStrengthCdma sInvalid = new CellSignalStrengthCdma(); + + /** @hide */ + @Override + public boolean isValid() { + return !this.equals(sInvalid); + } + + @Override + public boolean equals (Object o) { + CellSignalStrengthCdma s; + if (!(o instanceof CellSignalStrengthCdma)) return false; + s = (CellSignalStrengthCdma) o; + + return mCdmaDbm == s.mCdmaDbm + && mCdmaEcio == s.mCdmaEcio + && mEvdoDbm == s.mEvdoDbm + && mEvdoEcio == s.mEvdoEcio + && mEvdoSnr == s.mEvdoSnr + && mLevel == s.mLevel; + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return "CellSignalStrengthCdma:" + + " cdmaDbm=" + mCdmaDbm + + " cdmaEcio=" + mCdmaEcio + + " evdoDbm=" + mEvdoDbm + + " evdoEcio=" + mEvdoEcio + + " evdoSnr=" + mEvdoSnr + + " level=" + mLevel; + } + + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(Parcel dest, int flags) { + if (DBG) log("writeToParcel(Parcel, int): " + toString()); + dest.writeInt(mCdmaDbm); + dest.writeInt(mCdmaEcio); + dest.writeInt(mEvdoDbm); + dest.writeInt(mEvdoEcio); + dest.writeInt(mEvdoSnr); + dest.writeInt(mLevel); + } + + /** + * Construct a SignalStrength object from the given parcel + * where the TYPE_CDMA token is already been processed. + */ + private CellSignalStrengthCdma(Parcel in) { + // CdmaDbm, CdmaEcio, EvdoDbm and EvdoEcio are written into + // the parcel as positive values. + // Need to convert into negative values unless the value is invalid + mCdmaDbm = in.readInt(); + mCdmaEcio = in.readInt(); + mEvdoDbm = in.readInt(); + mEvdoEcio = in.readInt(); + mEvdoSnr = in.readInt(); + mLevel = in.readInt(); + if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString()); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + @SuppressWarnings("hiding") + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CellSignalStrengthCdma createFromParcel(Parcel in) { + return new CellSignalStrengthCdma(in); + } + + @Override + public CellSignalStrengthCdma[] newArray(int size) { + return new CellSignalStrengthCdma[size]; + } + }; + + /** + * log + */ + private static void log(String s) { + Rlog.w(LOG_TAG, s); + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java new file mode 100755 index 0000000000000000000000000000000000000000..de06b7d8ff04ae2b91b40b1a32c87be789c53fca --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntRange; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import com.android.telephony.Rlog; + +import java.util.Objects; + +import android.os.SystemProperties; + + +/** + * GSM signal strength related information. + */ +public final class CellSignalStrengthGsm extends CellSignalStrength implements Parcelable { + + private static final String LOG_TAG = "CellSignalStrengthGsm"; + private static final boolean DBG = false; + + private static final int GSM_RSSI_MAX = -51; + private static final int GSM_RSSI_GREAT = -89; + private static final int GSM_RSSI_GOOD = -97; + private static final int GSM_RSSI_MODERATE = -103; + private static final int GSM_RSSI_POOR = -107; + private static final int GSM_RSSI_MIN = -113; + + private static final int[] sRssiThresholds = new int[] { + GSM_RSSI_POOR, GSM_RSSI_MODERATE, GSM_RSSI_GOOD, GSM_RSSI_GREAT}; + + private int mRssi; // in dBm [-113, -51] or UNAVAILABLE + @UnsupportedAppUsage + private int mBitErrorRate; // bit error rate (0-7, 99) TS 27.007 8.5 or UNAVAILABLE + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown + private int mLevel; + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public CellSignalStrengthGsm() { + setDefaultValues(); + } + + /** @hide */ + public CellSignalStrengthGsm(int rssi, int ber, int ta) { + mRssi = inRangeOrUnavailable(rssi, GSM_RSSI_MIN, GSM_RSSI_MAX); + mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99); + mTimingAdvance = inRangeOrUnavailable(ta, 0, 219); + updateLevel(null, null); + } + + /** @hide */ + public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) { + // Convert from HAL values as part of construction. + this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance); + + if (mRssi == CellInfo.UNAVAILABLE) { + setDefaultValues(); + } + } + + /** @hide */ + public CellSignalStrengthGsm(CellSignalStrengthGsm s) { + copyFrom(s); + } + + /** @hide */ + protected void copyFrom(CellSignalStrengthGsm s) { + mRssi = s.mRssi; + mBitErrorRate = s.mBitErrorRate; + mTimingAdvance = s.mTimingAdvance; + mLevel = s.mLevel; + } + + /** @hide */ + @Override + public CellSignalStrengthGsm copy() { + return new CellSignalStrengthGsm(this); + } + + /** @hide */ + @Override + public void setDefaultValues() { + mRssi = CellInfo.UNAVAILABLE; + mBitErrorRate = CellInfo.UNAVAILABLE; + mTimingAdvance = CellInfo.UNAVAILABLE; + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + + /** {@inheritDoc} */ + @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + //return mLevel; + } + + /** @hide */ + @Override + public void updateLevel(PersistableBundle cc, ServiceState ss) { + int[] rssiThresholds; + if (cc == null) { + rssiThresholds = sRssiThresholds; + } else { + rssiThresholds = cc.getIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY); + if (rssiThresholds == null || rssiThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) { + rssiThresholds = sRssiThresholds; + } + } + int level = NUM_SIGNAL_STRENGTH_THRESHOLDS; + if (mRssi < GSM_RSSI_MIN || mRssi > GSM_RSSI_MAX) { + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + return; + } + while (level > 0 && mRssi < rssiThresholds[level - 1]) level--; + mLevel = level; + } + + /** + * Get the GSM timing advance between 0..219 symbols (normally 0..63). + *

{@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no RR + * connection. Refer to 3GPP 45.010 Sec 5.8. + * + * @return the current GSM timing advance, if available. + */ + public int getTimingAdvance() { + return mTimingAdvance; + } + + /** + * Get the signal strength as dBm. + * + * @return the RSSI of the measured cell. + */ + @Override + public int getDbm() { + return mRssi; + } + + /** + * Get the RSSI in ASU. + * + * Asu is calculated based on 3GPP RSSI. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @return RSSI in ASU 0..31, 99, or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + @Override + public int getAsuLevel() { + return getAsuFromRssiDbm(mRssi); + } + + /** + * Return the Received Signal Strength Indicator. + * + * @return the RSSI in dBm (-113, -51) or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + public int getRssi() { + return mRssi; + } + + /** + * Return the Bit Error Rate. + * + * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + public int getBitErrorRate() { + return mBitErrorRate; + } + + @Override + public int hashCode() { + return Objects.hash(mRssi, mBitErrorRate, mTimingAdvance); + } + + private static final CellSignalStrengthGsm sInvalid = new CellSignalStrengthGsm(); + + /** @hide */ + @Override + public boolean isValid() { + return !this.equals(sInvalid); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof CellSignalStrengthGsm)) return false; + CellSignalStrengthGsm s = (CellSignalStrengthGsm) o; + + return mRssi == s.mRssi + && mBitErrorRate == s.mBitErrorRate + && mTimingAdvance == s.mTimingAdvance + && mLevel == s.mLevel; + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return "CellSignalStrengthGsm:" + + " rssi=" + mRssi + + " ber=" + mBitErrorRate + + " mTa=" + mTimingAdvance + + " mLevel=" + mLevel; + } + + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(Parcel dest, int flags) { + if (DBG) log("writeToParcel(Parcel, int): " + toString()); + dest.writeInt(mRssi); + dest.writeInt(mBitErrorRate); + dest.writeInt(mTimingAdvance); + dest.writeInt(mLevel); + } + + /** + * Construct a SignalStrength object from the given parcel + * where the token is already been processed. + */ + private CellSignalStrengthGsm(Parcel in) { + mRssi = in.readInt(); + mBitErrorRate = in.readInt(); + mTimingAdvance = in.readInt(); + mLevel = in.readInt(); + if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString()); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + @SuppressWarnings("hiding") + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CellSignalStrengthGsm createFromParcel(Parcel in) { + return new CellSignalStrengthGsm(in); + } + + @Override + public CellSignalStrengthGsm[] newArray(int size) { + return new CellSignalStrengthGsm[size]; + } + }; + + /** + * log + */ + private static void log(String s) { + Rlog.w(LOG_TAG, s); + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java new file mode 100755 index 0000000000000000000000000000000000000000..41a29c02664dafe0b3e253aa59804aab42a1d1ff --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntRange; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import com.android.telephony.Rlog; + +import java.util.Arrays; +import java.util.Objects; + +import android.os.SystemProperties; + + +/** + * LTE signal strength related information. + */ +public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable { + + private static final String LOG_TAG = "CellSignalStrengthLte"; + private static final boolean DBG = false; + + /** + * Indicates the unknown or undetectable RSSI value in ASU. + * + * Reference: TS 27.007 8.5 - Signal quality +CSQ + */ + private static final int SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN = 99; + /** + * Indicates the maximum valid RSSI value in ASU. + * + * Reference: TS 27.007 8.5 - Signal quality +CSQ + */ + private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE = 31; + /** + * Indicates the minimum valid RSSI value in ASU. + * + * Reference: TS 27.007 8.5 - Signal quality +CSQ + */ + private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE = 0; + + private static final int MAX_LTE_RSRP = -44; + private static final int MIN_LTE_RSRP = -140; + + /** + * Indicates RSRP is considered for {@link #getLevel()} and reported from modem. + * + * @hide + */ + public static final int USE_RSRP = 1 << 0; + /** + * Indicates RSRQ is considered for {@link #getLevel()} and reported from modem. + * + * @hide + */ + public static final int USE_RSRQ = 1 << 1; + /** + * Indicates RSSNR is considered for {@link #getLevel()} and reported from modem. + * + * @hide + */ + public static final int USE_RSSNR = 1 << 2; + + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) + private int mSignalStrength; // To be removed + private int mRssi; + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) + private int mRsrp; + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) + private int mRsrq; + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) + private int mRssnr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * Range [1, 6]. + */ + private int mCqiTableIndex; + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) + private int mCqi; + @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P) + private int mTimingAdvance; + private int mLevel; + + /** + * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP), + * Reference Signal Received Quality (RSRQ), and/or Reference Signal Signal to Noise Ratio + * (RSSNR) for the number of LTE signal bars. If multiple measures are set, the parameter + * whose signal level value is smallest is used to indicate the signal level. + * + * RSRP = 1 << 0, + * RSRQ = 1 << 1, + * RSSNR = 1 << 2, + * + * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1). + * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply. + */ + private int mParametersUseForLevel; + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public CellSignalStrengthLte() { + setDefaultValues(); + } + + /** + * Construct a cell signal strength + * + * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE} + * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE} + * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE} + * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE} + * @param cqiTableIndex [1, 6], {@link CellInfo#UNAVAILABLE} + * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE} + * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE} + * + */ + /** @hide */ + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqiTableIndex, + int cqi, int timingAdvance) { + mRssi = inRangeOrUnavailable(rssi, -113, -51); + mSignalStrength = mRssi; + mRsrp = inRangeOrUnavailable(rsrp, -140, -43); + mRsrq = inRangeOrUnavailable(rsrq, -34, 3); + mRssnr = inRangeOrUnavailable(rssnr, -20, 30); + mCqiTableIndex = inRangeOrUnavailable(cqiTableIndex, 1, 6); + mCqi = inRangeOrUnavailable(cqi, 0, 15); + mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282); + updateLevel(null, null); + } + + /** + * Construct a cell signal strength + * + * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE} + * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE} + * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE} + * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE} + * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE} + * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE} + * + */ + /** @hide */ + public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi, + int timingAdvance) { + this(rssi, rsrp, rsrq, rssnr, CellInfo.UNAVAILABLE, cqi, timingAdvance); + } + + /** @hide */ + public CellSignalStrengthLte(CellSignalStrengthLte s) { + copyFrom(s); + } + + /** @hide */ + protected void copyFrom(CellSignalStrengthLte s) { + mSignalStrength = s.mSignalStrength; + mRssi = s.mRssi; + mRsrp = s.mRsrp; + mRsrq = s.mRsrq; + mRssnr = s.mRssnr; + mCqiTableIndex = s.mCqiTableIndex; + mCqi = s.mCqi; + mTimingAdvance = s.mTimingAdvance; + mLevel = s.mLevel; + mParametersUseForLevel = s.mParametersUseForLevel; + } + + /** @hide */ + @Override + public CellSignalStrengthLte copy() { + return new CellSignalStrengthLte(this); + } + + /** @hide */ + @Override + public void setDefaultValues() { + mSignalStrength = CellInfo.UNAVAILABLE; + mRssi = CellInfo.UNAVAILABLE; + mRsrp = CellInfo.UNAVAILABLE; + mRsrq = CellInfo.UNAVAILABLE; + mRssnr = CellInfo.UNAVAILABLE; + mCqiTableIndex = CellInfo.UNAVAILABLE; + mCqi = CellInfo.UNAVAILABLE; + mTimingAdvance = CellInfo.UNAVAILABLE; + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + mParametersUseForLevel = USE_RSRP; + } + + /** {@inheritDoc} */ + @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + //return mLevel; + } + + // Lifted from Default carrier configs and max range of RSRP + private static final int[] sRsrpThresholds = new int[] { + -115, /* SIGNAL_STRENGTH_POOR */ + -105, /* SIGNAL_STRENGTH_MODERATE */ + -95, /* SIGNAL_STRENGTH_GOOD */ + -85 /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of RSRQ + private static final int[] sRsrqThresholds = new int[] { + -19, /* SIGNAL_STRENGTH_POOR */ + -17, /* SIGNAL_STRENGTH_MODERATE */ + -14, /* SIGNAL_STRENGTH_GOOD */ + -12 /* SIGNAL_STRENGTH_GREAT */ + }; + // Lifted from Default carrier configs and max range of RSSNR + private static final int[] sRssnrThresholds = new int[] { + -3, /* SIGNAL_STRENGTH_POOR */ + 1, /* SIGNAL_STRENGTH_MODERATE */ + 5, /* SIGNAL_STRENGTH_GOOD */ + 13 /* SIGNAL_STRENGTH_GREAT */ + }; + private static final int sRsrpBoost = 0; + + /** + * Checks if the given parameter type is considered to use for {@link #getLevel()}. + * + * Note: if multiple parameter types are considered, the smaller level for one of the + * parameters would be returned by {@link #getLevel()} + * + * @param parameterType bitwise OR of {@link #USE_RSRP}, {@link #USE_RSRQ}, + * {@link #USE_RSSNR} + * @return {@code true} if the level is calculated based on the given parameter type; + * {@code false} otherwise. + */ + private boolean isLevelForParameter(int parameterType) { + return (parameterType & mParametersUseForLevel) == parameterType; + } + + /** @hide */ + @Override + public void updateLevel(PersistableBundle cc, ServiceState ss) { + int[] rsrpThresholds, rsrqThresholds, rssnrThresholds; + boolean rsrpOnly; + if (cc == null) { + mParametersUseForLevel = USE_RSRP; + rsrpThresholds = sRsrpThresholds; + rsrqThresholds = sRsrqThresholds; + rssnrThresholds = sRssnrThresholds; + rsrpOnly = false; + } else { + if (ss != null && ss.isUsingNonTerrestrialNetwork()) { + if (DBG) log("updateLevel: from NTN_LTE"); + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT); + rsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY); + rsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY); + rssnrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY); + } else { + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT); + rsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY); + rsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY); + rssnrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY); + } + if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds; + if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds; + if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds; + if (DBG) { + Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel); + Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + + Arrays.toString(rsrpThresholds)); + Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + + Arrays.toString(rsrqThresholds)); + Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + + Arrays.toString(rssnrThresholds)); + } + rsrpOnly = cc.getBoolean( + CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false); + } + + int rsrpBoost = 0; + if (ss != null) { + rsrpBoost = ss.getArfcnRsrpBoost(); + } + + int rsrp = inRangeOrUnavailable(mRsrp + rsrpBoost, MIN_LTE_RSRP, MAX_LTE_RSRP); + + if (rsrpOnly) { + int level = updateLevelWithMeasure(rsrp, rsrpThresholds); + if (DBG) log("updateLevel() - rsrp = " + level); + if (level != SignalStrength.INVALID) { + mLevel = level; + return; + } + } + + int rsrpLevel = SignalStrength.INVALID; + int rsrqLevel = SignalStrength.INVALID; + int rssnrLevel = SignalStrength.INVALID; + + if (isLevelForParameter(USE_RSRP)) { + rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds); + if (DBG) { + Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel); + } + } + if (isLevelForParameter(USE_RSRQ)) { + rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds); + if (DBG) { + Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel); + } + } + if (isLevelForParameter(USE_RSSNR)) { + rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds); + if (DBG) { + Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel); + } + } + // Apply the smaller value among three levels of three measures. + mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel); + + if (mLevel == SignalStrength.INVALID) { + int rssiLevel; + if (mRssi > -51) { + rssiLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } else if (mRssi >= -89) { + rssiLevel = SIGNAL_STRENGTH_GREAT; + } else if (mRssi >= -97) { + rssiLevel = SIGNAL_STRENGTH_GOOD; + } else if (mRssi >= -103) { + rssiLevel = SIGNAL_STRENGTH_MODERATE; + } else if (mRssi >= -113) { + rssiLevel = SIGNAL_STRENGTH_POOR; + } else { + rssiLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:" + rssiLevel); + mLevel = rssiLevel; + } + } + + /** + * Update level with corresponding measure and thresholds. + * + * @param measure corresponding signal measure + * @param thresholds corresponding signal thresholds + * @return level of the signal strength + */ + private int updateLevelWithMeasure(int measure, int[] thresholds) { + int level; + if (measure == CellInfo.UNAVAILABLE) { + level = SignalStrength.INVALID; + } else if (measure >= thresholds[3]) { + level = SIGNAL_STRENGTH_GREAT; + } else if (measure >= thresholds[2]) { + level = SIGNAL_STRENGTH_GOOD; + } else if (measure >= thresholds[1]) { + level = SIGNAL_STRENGTH_MODERATE; + } else if (measure >= thresholds[0]) { + level = SIGNAL_STRENGTH_POOR; + } else { + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + return level; + } + + /** + * Get reference signal received quality + * + * @return the RSRQ if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + public int getRsrq() { + return mRsrq; + } + + /** + * Get Received Signal Strength Indication (RSSI) in dBm + * + * The value range is [-113, -51] inclusively or {@link CellInfo#UNAVAILABLE} if unavailable. + * + * Reference: TS 27.007 8.5 Signal quality +CSQ + * + * @return the RSSI if available or {@link CellInfo#UNAVAILABLE} if unavailable. + */ + public int getRssi() { + return mRssi; + } + + /** + * Get reference signal signal-to-noise ratio in dB + * Range: -20 dB to +30 dB. + * + * @return the RSSNR if available or + * {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable. + */ + public int getRssnr() { + return mRssnr; + } + + /** + * Get reference signal received power in dBm + * Range: -140 dBm to -43 dBm. + * + * @return the RSRP of the measured cell or {@link CellInfo#UNAVAILABLE} if + * unavailable. + */ + public int getRsrp() { + return mRsrp; + } + + /** + * Get table index for channel quality indicator + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * @return the CQI table index if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + @IntRange(from = 1, to = 6) + public int getCqiTableIndex() { + return mCqiTableIndex; + } + + /** + * Get channel quality indicator + * + * Reference: 3GPP TS 136.213 section 7.2.3. + * + * @return the CQI if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + @IntRange(from = 0, to = 15) + public int getCqi() { + return mCqi; + } + + /** + * Get signal strength in dBm + * + * @return the RSRP of the measured cell. + */ + @Override + public int getDbm() { + return mRsrp; + } + + /** + * Get the RSRP in ASU. + * + * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @return RSCP in ASU 0..97, 255, or UNAVAILABLE + */ + @Override + public int getAsuLevel() { + int lteAsuLevel = 99; + int lteDbm = mRsrp; + if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99; + else if (lteDbm <= -140) lteAsuLevel = 0; + else if (lteDbm >= -43) lteAsuLevel = 97; + else lteAsuLevel = lteDbm + 140; + if (DBG) log("Lte Asu level: "+lteAsuLevel); + return lteAsuLevel; + } + + /** + * Get the timing advance value for LTE, as a value in range of 0..1282. + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no + * active RRC connection. Refer to 3GPP 36.213 Sec 4.2.3 + * + * @return the LTE timing advance if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + public int getTimingAdvance() { + return mTimingAdvance; + } + + @Override + public int hashCode() { + return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqiTableIndex, mCqi, mTimingAdvance, + mLevel); + } + + private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte(); + + /** @hide */ + @Override + public boolean isValid() { + return !this.equals(sInvalid); + } + + @Override + public boolean equals (Object o) { + CellSignalStrengthLte s; + + if (!(o instanceof CellSignalStrengthLte)) return false; + s = (CellSignalStrengthLte) o; + + return mRssi == s.mRssi + && mRsrp == s.mRsrp + && mRsrq == s.mRsrq + && mRssnr == s.mRssnr + && mCqiTableIndex == s.mCqiTableIndex + && mCqi == s.mCqi + && mTimingAdvance == s.mTimingAdvance + && mLevel == s.mLevel; + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return "CellSignalStrengthLte:" + + " rssi=" + mRssi + + " rsrp=" + mRsrp + + " rsrq=" + mRsrq + + " rssnr=" + mRssnr + + " cqiTableIndex=" + mCqiTableIndex + + " cqi=" + mCqi + + " ta=" + mTimingAdvance + + " level=" + mLevel + + " parametersUseForLevel=" + mParametersUseForLevel; + } + + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(Parcel dest, int flags) { + if (DBG) log("writeToParcel(Parcel, int): " + toString()); + dest.writeInt(mRssi); + // Need to multiply rsrp and rsrq by -1 + // to ensure consistency when reading values written here + // unless the values are invalid + dest.writeInt(mRsrp); + dest.writeInt(mRsrq); + dest.writeInt(mRssnr); + dest.writeInt(mCqiTableIndex); + dest.writeInt(mCqi); + dest.writeInt(mTimingAdvance); + dest.writeInt(mLevel); + } + + /** + * Construct a SignalStrength object from the given parcel + * where the token is already been processed. + */ + private CellSignalStrengthLte(Parcel in) { + mRssi = in.readInt(); + mSignalStrength = mRssi; + mRsrp = in.readInt(); + mRsrq = in.readInt(); + mRssnr = in.readInt(); + mCqiTableIndex = in.readInt(); + mCqi = in.readInt(); + mTimingAdvance = in.readInt(); + mLevel = in.readInt(); + if (DBG) log("CellSignalStrengthLte(Parcel): " + toString()); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + @SuppressWarnings("hiding") + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CellSignalStrengthLte createFromParcel(Parcel in) { + return new CellSignalStrengthLte(in); + } + + @Override + public CellSignalStrengthLte[] newArray(int size) { + return new CellSignalStrengthLte[size]; + } + }; + + /** + * log + */ + private static void log(String s) { + Rlog.w(LOG_TAG, s); + } + + /** @hide */ + public static int convertRssnrUnitFromTenDbToDB(int rssnr) { + return (int) Math.floor((float) rssnr / 10); + } + + /** @hide */ + public static int convertRssiAsuToDBm(int rssiAsu) { + if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) { + return CellInfo.UNAVAILABLE; + } + if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE + || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) { + Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu); + return CellInfo.UNAVAILABLE; + } + return -113 + (2 * rssiAsu); + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthNr.java b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthNr.java new file mode 100755 index 0000000000000000000000000000000000000000..d8229df79b99eb5eec3d1d94463caa59e5b71d3a --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import com.android.telephony.Rlog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + + +import android.os.SystemProperties; + + +/** + * 5G NR signal strength related information. + */ +public final class CellSignalStrengthNr extends CellSignalStrength implements Parcelable { + /** + * The value is used to indicate that the asu level is unknown. + * Reference: 3GPP TS 27.007 section 8.69. + * @hide + */ + public static final int UNKNOWN_ASU_LEVEL = 99; + + private static final boolean VDBG = false; + + private static final String TAG = "CellSignalStrengthNr"; + + // Lifted from Default carrier configs and max range of SSRSRP + // Boundaries: [-156 dB, -31 dB] + private int[] mSsRsrpThresholds = new int[] { + -110, /* SIGNAL_STRENGTH_POOR */ + -90, /* SIGNAL_STRENGTH_MODERATE */ + -80, /* SIGNAL_STRENGTH_GOOD */ + -65, /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of SSRSRQ + // Boundaries: [-43 dB, 20 dB] + private int[] mSsRsrqThresholds = new int[] { + -31, /* SIGNAL_STRENGTH_POOR */ + -19, /* SIGNAL_STRENGTH_MODERATE */ + -7, /* SIGNAL_STRENGTH_GOOD */ + 6 /* SIGNAL_STRENGTH_GREAT */ + }; + + // Lifted from Default carrier configs and max range of SSSINR + // Boundaries: [-23 dB, 40 dB] + private int[] mSsSinrThresholds = new int[] { + -5, /* SIGNAL_STRENGTH_POOR */ + 5, /* SIGNAL_STRENGTH_MODERATE */ + 15, /* SIGNAL_STRENGTH_GOOD */ + 30 /* SIGNAL_STRENGTH_GREAT */ + }; + + /** + * Indicates SSRSRP is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSRSRP = 1 << 0; + /** + * Indicates SSRSRQ is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSRSRQ = 1 << 1; + /** + * Indicates SSSINR is considered for {@link #getLevel()} and reporting from modem. + * + * @hide + */ + public static final int USE_SSSINR = 1 << 2; + + /** + * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), + * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference + * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the + * parameter whose value is smallest is used to indicate the signal bar. + * + * @hide + */ + @IntDef(flag = true, prefix = { "USE_" }, value = { + USE_SSRSRP, + USE_SSRSRQ, + USE_SSSINR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SignalLevelAndReportCriteriaSource {} + + private int mCsiRsrp; + private int mCsiRsrq; + private int mCsiSinr; + /** + * CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [1, 3]. + */ + private int mCsiCqiTableIndex; + /** + * CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * Range [0, 15] for each CQI. + */ + private List mCsiCqiReport; + private int mSsRsrp; + private int mSsRsrq; + private int mSsSinr; + private int mLevel; + + /** + * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP), + * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference + * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the + * parameter whose value is smallest is used to indicate the signal bar. + * + * SSRSRP = 1 << 0, + * SSRSRQ = 1 << 1, + * SSSINR = 1 << 2, + * + * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2). + * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply. + */ + private int mParametersUseForLevel; + + /** + * Timing advance value for a one way trip from cell to device in microseconds. + * Approximate distance is calculated using 300m/us * timingAdvance. + * + * Reference: 3GPP TS 36.213 section 4.2.3. + * + * Range: [0, 1282] + */ + private int mTimingAdvance; + + /** @hide */ + public CellSignalStrengthNr() { + setDefaultValues(); + } + + /** + * @param csiRsrp CSI reference signal received power. + * @param csiRsrq CSI reference signal received quality. + * @param csiSinr CSI signal-to-noise and interference ratio. + * @param csiCqiTableIndex CSI CSI channel quality indicator (CQI) table index. + * @param csiCqiReport CSI channel quality indicators (CQI) for all subbands. + * @param ssRsrp SS reference signal received power. + * @param ssRsrq SS reference signal received quality. + * @param ssSinr SS signal-to-noise and interference ratio. + * @param timingAdvance Timing advance. + * @hide + */ + public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex, + List csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr, int timingAdvance) { + mCsiRsrp = inRangeOrUnavailable(csiRsrp, -156, -31); + mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3); + mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23); + mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3); + mCsiCqiReport = csiCqiReport.stream() + .map(cqi -> inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15)) + .collect(Collectors.toList()); + mSsRsrp = inRangeOrUnavailable(ssRsrp, -156, -31); + mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20); + mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40); + mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282); + updateLevel(null, null); + } + + /** + * @param csiRsrp CSI reference signal received power. + * @param csiRsrq CSI reference signal received quality. + * @param csiSinr CSI signal-to-noise and interference ratio. + * @param ssRsrp SS reference signal received power. + * @param ssRsrq SS reference signal received quality. + * @param ssSinr SS signal-to-noise and interference ratio. + * @hide + */ + public CellSignalStrengthNr( + int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) { + this(csiRsrp, csiRsrq, csiSinr, CellInfo.UNAVAILABLE, Collections.emptyList(), + ssRsrp, ssRsrq, ssSinr, CellInfo.UNAVAILABLE); + } + + /** + * Flip sign cell strength value when taking in the value from hal + * @param val cell strength value + * @return flipped value + * @hide + */ + public static int flip(int val) { + return val != CellInfo.UNAVAILABLE ? -val : val; + } + + /** + * Reference: 3GPP TS 38.133 10.1.6.1. + * Range: -156 dBm to -31 dBm. + * @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported + * value. + */ + public int getSsRsrp() { + return mSsRsrp; + } + + /** + * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10 + * Range: -43 dB to 20 dB. + * @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported + * value. + */ + public int getSsRsrq() { + return mSsRsrq; + } + + /** + * Reference: 3GPP TS 38.215 Sec 5.1.*, 3GPP TS 38.133 10.1.16.1 + * Range: -23 dB to 40 dB + * @return SS signal-to-noise and interference ratio, {@link CellInfo#UNAVAILABLE} means + * unreported value. + */ + public int getSsSinr() { + return mSsSinr; + } + + /** + * Reference: 3GPP TS 38.133 10.1.6.1. + * Range: -156 dBm to -31 dBm. + * @return CSI reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported + * value. + */ + public int getCsiRsrp() { + return mCsiRsrp; + } + + /** + * Reference: 3GPP TS 38.215. + * Range: -20 dB to -3 dB. + * @return CSI reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported + * value. + */ + public int getCsiRsrq() { + return mCsiRsrq; + } + + /** + * Reference: 3GPP TS 38.215 Sec 5.1.*, 3GPP TS 38.133 10.1.16.1 + * Range: -23 dB to 23 dB + * @return CSI signal-to-noise and interference ratio, {@link CellInfo#UNAVAILABLE} means + * unreported value. + */ + public int getCsiSinr() { + return mCsiSinr; + } + + /** + * Return CSI channel quality indicator (CQI) table index. There are multiple CQI tables. + * The definition of CQI in each table is different. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * @return the CQI table index if available or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + @IntRange(from = 1, to = 3) + public int getCsiCqiTableIndex() { + return mCsiCqiTableIndex; + } + /** + * Return a list of CSI channel quality indicators (CQI) for all subbands. + * + * If the CQI report is for the entire wideband, a single CQI index is provided. + * If the CQI report is for all subbands, one CQI index is provided for each subband, + * in ascending order of subband index. + * If CQI is not available, the CQI report is empty. + * + * Reference: 3GPP TS 138.214 section 5.2.2.1. + * + * @return the CQIs for all subbands if available or empty list if unavailable. + */ + @NonNull + @IntRange(from = 0, to = 15) + public List getCsiCqiReport() { + return mCsiCqiReport; + } + + /** + * Get the timing advance value for a one way trip from cell to device for NR in microseconds. + * {@link android.telephony.CellInfo#UNAVAILABLE} is reported when there is no + * active RRC connection. + * + * Reference: 3GPP TS 36.213 section 4.2.3. + * Range: 0 us to 1282 us. + * + * @return the NR timing advance if available or + * {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable. + */ + @IntRange(from = 0, to = 1282) + public int getTimingAdvanceMicros() { + return mTimingAdvance; + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCsiRsrp); + dest.writeInt(mCsiRsrq); + dest.writeInt(mCsiSinr); + dest.writeInt(mCsiCqiTableIndex); + dest.writeList(mCsiCqiReport); + dest.writeInt(mSsRsrp); + dest.writeInt(mSsRsrq); + dest.writeInt(mSsSinr); + dest.writeInt(mLevel); + dest.writeInt(mTimingAdvance); + } + + private CellSignalStrengthNr(Parcel in) { + mCsiRsrp = in.readInt(); + mCsiRsrq = in.readInt(); + mCsiSinr = in.readInt(); + mCsiCqiTableIndex = in.readInt(); + mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); + mSsRsrp = in.readInt(); + mSsRsrq = in.readInt(); + mSsSinr = in.readInt(); + mLevel = in.readInt(); + mTimingAdvance = in.readInt(); + } + + /** @hide */ + @Override + public void setDefaultValues() { + mCsiRsrp = CellInfo.UNAVAILABLE; + mCsiRsrq = CellInfo.UNAVAILABLE; + mCsiSinr = CellInfo.UNAVAILABLE; + mCsiCqiTableIndex = CellInfo.UNAVAILABLE; + mCsiCqiReport = Collections.emptyList(); + mSsRsrp = CellInfo.UNAVAILABLE; + mSsRsrq = CellInfo.UNAVAILABLE; + mSsSinr = CellInfo.UNAVAILABLE; + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + mParametersUseForLevel = USE_SSRSRP; + mTimingAdvance = CellInfo.UNAVAILABLE; + } + + /** {@inheritDoc} */ + @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + //return mLevel; + } + + /** + * Checks if the given parameter type is considered to use for {@link #getLevel()}. + * + * Note: if multiple parameter types are considered, the smaller level for one of the + * parameters would be returned by {@link #getLevel()} + * + * @param parameterType bitwise OR of {@link #USE_SSRSRP}, {@link #USE_SSRSRQ}, + * {@link #USE_SSSINR} + * @return {@code true} if the level is calculated based on the given parameter type; + * {@code false} otherwise. + * + */ + private boolean isLevelForParameter(@SignalLevelAndReportCriteriaSource int parameterType) { + return (parameterType & mParametersUseForLevel) == parameterType; + } + + /** @hide */ + @Override + public void updateLevel(PersistableBundle cc, ServiceState ss) { + if (cc == null) { + mParametersUseForLevel = USE_SSRSRP; + } else { + mParametersUseForLevel = cc.getInt( + CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP); + mSsRsrpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY); + if (VDBG) { + Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + + Arrays.toString(mSsRsrpThresholds)); + } + mSsRsrqThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY); + if (VDBG) { + Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + + Arrays.toString(mSsRsrqThresholds)); + } + mSsSinrThresholds = cc.getIntArray( + CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY); + if (VDBG) { + Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + + Arrays.toString(mSsSinrThresholds)); + } + } + int ssRsrpLevel = SignalStrength.INVALID; + int ssRsrqLevel = SignalStrength.INVALID; + int ssSinrLevel = SignalStrength.INVALID; + if (isLevelForParameter(USE_SSRSRP)) { + int rsrpBoost = 0; + if (ss != null) { + rsrpBoost = ss.getArfcnRsrpBoost(); + } + ssRsrpLevel = updateLevelWithMeasure(mSsRsrp + rsrpBoost, mSsRsrpThresholds); + if (VDBG) { + Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel); + } + } + if (isLevelForParameter(USE_SSRSRQ)) { + ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds); + if (VDBG) { + Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel); + } + } + if (isLevelForParameter(USE_SSSINR)) { + ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds); + if (VDBG) { + Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel); + } + } + // Apply the smaller value among three levels of three measures. + mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel); + } + + /** + * Update level with corresponding measure and thresholds. + * + * @param measure corresponding signal measure + * @param thresholds corresponding signal thresholds + * @return level of the signal strength + */ + private int updateLevelWithMeasure(int measure, int[] thresholds) { + int level; + if (measure == CellInfo.UNAVAILABLE) { + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } else if (measure >= thresholds[3]) { + level = SIGNAL_STRENGTH_GREAT; + } else if (measure >= thresholds[2]) { + level = SIGNAL_STRENGTH_GOOD; + } else if (measure >= thresholds[1]) { + level = SIGNAL_STRENGTH_MODERATE; + } else if (measure >= thresholds[0]) { + level = SIGNAL_STRENGTH_POOR; + } else { + level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + return level; + } + + /** + * Get the RSRP in ASU. + * + * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @return RSRP in ASU 0..97, 255, or UNAVAILABLE + */ + @Override + public int getAsuLevel() { + int asuLevel; + int nrDbm = getDbm(); + if (nrDbm == CellInfo.UNAVAILABLE) { + asuLevel = UNKNOWN_ASU_LEVEL; + } else if (nrDbm <= -140) { + asuLevel = 0; + } else if (nrDbm >= -43) { + asuLevel = 97; + } else { + asuLevel = nrDbm + 140; + } + return asuLevel; + } + + /** + * Get the SS-RSRP as dBm value -140..-44dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + @Override + public int getDbm() { + return mSsRsrp; + } + + /** @hide */ + public CellSignalStrengthNr(CellSignalStrengthNr s) { + mCsiRsrp = s.mCsiRsrp; + mCsiRsrq = s.mCsiRsrq; + mCsiSinr = s.mCsiSinr; + mCsiCqiTableIndex = s.mCsiCqiTableIndex; + mCsiCqiReport = s.mCsiCqiReport; + mSsRsrp = s.mSsRsrp; + mSsRsrq = s.mSsRsrq; + mSsSinr = s.mSsSinr; + mLevel = s.mLevel; + mParametersUseForLevel = s.mParametersUseForLevel; + mTimingAdvance = s.mTimingAdvance; + } + + /** @hide */ + @Override + public CellSignalStrengthNr copy() { + return new CellSignalStrengthNr(this); + } + + @Override + public int hashCode() { + return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mCsiCqiTableIndex, + mCsiCqiReport, mSsRsrp, mSsRsrq, mSsSinr, mLevel, mTimingAdvance); + } + + private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr(); + + /** @hide */ + @Override + public boolean isValid() { + return !this.equals(sInvalid); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CellSignalStrengthNr) { + CellSignalStrengthNr o = (CellSignalStrengthNr) obj; + return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr + && mCsiCqiTableIndex == o.mCsiCqiTableIndex + && mCsiCqiReport.equals(o.mCsiCqiReport) + && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr + && mLevel == o.mLevel && mTimingAdvance == o.mTimingAdvance; + } + return false; + } + + @Override + public String toString() { + return new StringBuilder() + .append(TAG + ":{") + .append(" csiRsrp = " + mCsiRsrp) + .append(" csiRsrq = " + mCsiRsrq) + .append(" csiCqiTableIndex = " + mCsiCqiTableIndex) + .append(" csiCqiReport = " + mCsiCqiReport) + .append(" ssRsrp = " + mSsRsrp) + .append(" ssRsrq = " + mSsRsrq) + .append(" ssSinr = " + mSsSinr) + .append(" level = " + mLevel) + .append(" parametersUseForLevel = " + mParametersUseForLevel) + .append(" timingAdvance = " + mTimingAdvance) + .append(" }") + .toString(); + } + + /** Implement the Parcelable interface */ + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CellSignalStrengthNr createFromParcel(Parcel in) { + return new CellSignalStrengthNr(in); + } + + @Override + public CellSignalStrengthNr[] newArray(int size) { + return new CellSignalStrengthNr[size]; + } + }; +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthTdscdma.java new file mode 100755 index 0000000000000000000000000000000000000000..2518b9d4cf5895c88084fdc4d7d25a5ef710d1ee --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthTdscdma.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import com.android.telephony.Rlog; + +import java.util.Objects; + +import android.os.SystemProperties; + + +/** + * Tdscdma signal strength related information. + * + * This class provides signal strength and signal quality information for the TD-SCDMA air + * interface. For more information see 3gpp 25.225. + */ +public final class CellSignalStrengthTdscdma extends CellSignalStrength implements Parcelable { + + private static final String LOG_TAG = "CellSignalStrengthTdscdma"; + private static final boolean DBG = false; + + // These levels are arbitrary but carried over from SignalStrength.java for consistency. + private static final int TDSCDMA_RSCP_MAX = -24; + private static final int TDSCDMA_RSCP_GREAT = -49; + private static final int TDSCDMA_RSCP_GOOD = -73; + private static final int TDSCDMA_RSCP_MODERATE = -97; + private static final int TDSCDMA_RSCP_POOR = -110; + private static final int TDSCDMA_RSCP_MIN = -120; + + + private int mRssi; // in dBm [-113, -51], CellInfo.UNAVAILABLE + + private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or + // CellInfo.UNAVAILABLE if unknown + private int mRscp; // Pilot Power in dBm [-120, -24] or CellInfo.UNAVAILABLE + // CellInfo.UNAVAILABLE if unknown + + private int mLevel; + + /** @hide */ + public CellSignalStrengthTdscdma() { + setDefaultValues(); + } + + /** + * @param rssi in dBm [-113, -51] or UNAVAILABLE + * @param ber [0-7], 99 or UNAVAILABLE + * @param rscp in dBm [-120, -24] or UNAVAILABLE + * + * @hide + */ + public CellSignalStrengthTdscdma(int rssi, int ber, int rscp) { + mRssi = inRangeOrUnavailable(rssi, -113, -51); + mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99); + mRscp = inRangeOrUnavailable(rscp, -120, -24); + updateLevel(null, null); + } + + /** @hide */ + public CellSignalStrengthTdscdma(CellSignalStrengthTdscdma s) { + copyFrom(s); + } + + /** @hide */ + protected void copyFrom(CellSignalStrengthTdscdma s) { + mRssi = s.mRssi; + mBitErrorRate = s.mBitErrorRate; + mRscp = s.mRscp; + mLevel = s.mLevel; + } + + /** @hide */ + @Override + public CellSignalStrengthTdscdma copy() { + return new CellSignalStrengthTdscdma(this); + } + + /** @hide */ + @Override + public void setDefaultValues() { + mRssi = CellInfo.UNAVAILABLE; + mBitErrorRate = CellInfo.UNAVAILABLE; + mRscp = CellInfo.UNAVAILABLE; + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + + + /** {@inheritDoc} */ + @Override + @IntRange(from = 0, to = 4) + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + //return mLevel; + } + + /** @hide */ + @Override + public void updateLevel(PersistableBundle cc, ServiceState ss) { + if (mRscp > TDSCDMA_RSCP_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + else if (mRscp >= TDSCDMA_RSCP_GREAT) mLevel = SIGNAL_STRENGTH_GREAT; + else if (mRscp >= TDSCDMA_RSCP_GOOD) mLevel = SIGNAL_STRENGTH_GOOD; + else if (mRscp >= TDSCDMA_RSCP_MODERATE) mLevel = SIGNAL_STRENGTH_MODERATE; + else if (mRscp >= TDSCDMA_RSCP_POOR) mLevel = SIGNAL_STRENGTH_POOR; + else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + + /** + * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + @Override + public int getDbm() { + return mRscp; + } + + /** + * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + public int getRscp() { + return mRscp; + } + + /** + * Get the RSSI as dBm value -113..-51dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + * + * @hide + */ + public int getRssi() { + return mRssi; + } + + /** + * Get the BER as an ASU value 0..7, 99, or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + * @hide + */ + public int getBitErrorRate() { + return mBitErrorRate; + } + + /** + * Get the RSCP in ASU. + * + * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @return RSCP in ASU 0..96, 255, or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + @Override + public int getAsuLevel() { + if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp); + // For historical reasons, if RSCP is unavailable, this API will very incorrectly return + // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+ + if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi); + return getAsuFromRscpDbm(CellInfo.UNAVAILABLE); + } + + @Override + public int hashCode() { + return Objects.hash(mRssi, mBitErrorRate, mRscp, mLevel); + } + + private static final CellSignalStrengthTdscdma sInvalid = new CellSignalStrengthTdscdma(); + + /** @hide */ + @Override + public boolean isValid() { + return !this.equals(sInvalid); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof CellSignalStrengthTdscdma)) return false; + CellSignalStrengthTdscdma s = (CellSignalStrengthTdscdma) o; + + return mRssi == s.mRssi + && mBitErrorRate == s.mBitErrorRate + && mRscp == s.mRscp + && mLevel == s.mLevel; + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return "CellSignalStrengthTdscdma:" + + " rssi=" + mRssi + + " ber=" + mBitErrorRate + + " rscp=" + mRscp + + " level=" + mLevel; + } + + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(Parcel dest, int flags) { + if (DBG) log("writeToParcel(Parcel, int): " + toString()); + dest.writeInt(mRssi); + dest.writeInt(mBitErrorRate); + dest.writeInt(mRscp); + dest.writeInt(mLevel); + } + + /** + * Construct a SignalStrength object from the given parcel + * where the token is already been processed. + */ + private CellSignalStrengthTdscdma(Parcel in) { + mRssi = in.readInt(); + mBitErrorRate = in.readInt(); + mRscp = in.readInt(); + mLevel = in.readInt(); + if (DBG) log("CellSignalStrengthTdscdma(Parcel): " + toString()); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + @SuppressWarnings("hiding") + @NonNull + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public @NonNull CellSignalStrengthTdscdma createFromParcel(Parcel in) { + return new CellSignalStrengthTdscdma(in); + } + + @Override + public @NonNull CellSignalStrengthTdscdma[] newArray(int size) { + return new CellSignalStrengthTdscdma[size]; + } + }; + + /** + * log + */ + private static void log(String s) { + Rlog.w(LOG_TAG, s); + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthWcdma.java new file mode 100755 index 0000000000000000000000000000000000000000..aeb6578df7edf97c28854b553065a0093fb10ede --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntRange; +import android.annotation.StringDef; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.text.TextUtils; + +import com.android.telephony.Rlog; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +import android.os.SystemProperties; + + +/** + * Wcdma signal strength related information. + */ +public final class CellSignalStrengthWcdma extends CellSignalStrength implements Parcelable { + + private static final String LOG_TAG = "CellSignalStrengthWcdma"; + private static final boolean DBG = false; + + private static final int WCDMA_RSSI_MAX = -51; + private static final int WCDMA_RSSI_GREAT = -77; + private static final int WCDMA_RSSI_GOOD = -87; + private static final int WCDMA_RSSI_MODERATE = -97; + private static final int WCDMA_RSSI_POOR = -107; + private static final int WCDMA_RSSI_MIN = -113; + + private static final int[] sRssiThresholds = new int[]{ + WCDMA_RSSI_POOR, WCDMA_RSSI_MODERATE, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT}; + + private static final int WCDMA_RSCP_MAX = -24; + private static final int WCDMA_RSCP_GREAT = -85; + private static final int WCDMA_RSCP_GOOD = -95; + private static final int WCDMA_RSCP_MODERATE = -105; + private static final int WCDMA_RSCP_POOR = -115; + private static final int WCDMA_RSCP_MIN = -120; + + private static final int[] sRscpThresholds = new int[] { + WCDMA_RSCP_POOR, WCDMA_RSCP_MODERATE, WCDMA_RSCP_GOOD, WCDMA_RSCP_GREAT}; + + // TODO: Because these are used as values in CarrierConfig, they should be exposed somehow. + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef({LEVEL_CALCULATION_METHOD_RSSI, LEVEL_CALCULATION_METHOD_RSCP}) + public @interface LevelCalculationMethod {} + /** @hide */ + public static final String LEVEL_CALCULATION_METHOD_RSSI = "rssi"; + /** @hide */ + public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp"; + + // Default to RSSI for backwards compatibility with older devices + private static final String DEFAULT_LEVEL_CALCULATION_METHOD = LEVEL_CALCULATION_METHOD_RSSI; + + private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown + + @UnsupportedAppUsage + private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or + // CellInfo.UNAVAILABLE if unknown + private int mRscp; // in dBm [-120, -24] + private int mEcNo; // range -24, 1, CellInfo.UNAVAILABLE if unknown + private int mLevel; + + /** @hide */ + public CellSignalStrengthWcdma() { + setDefaultValues(); + } + + /** @hide */ + public CellSignalStrengthWcdma(int rssi, int ber, int rscp, int ecno) { + mRssi = inRangeOrUnavailable(rssi, WCDMA_RSSI_MIN, WCDMA_RSSI_MAX); + mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99); + mRscp = inRangeOrUnavailable(rscp, -120, -24); + mEcNo = inRangeOrUnavailable(ecno, -24, 1); + updateLevel(null, null); + } + + /** @hide */ + public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) { + copyFrom(s); + } + + /** @hide */ + protected void copyFrom(CellSignalStrengthWcdma s) { + mRssi = s.mRssi; + mBitErrorRate = s.mBitErrorRate; + mRscp = s.mRscp; + mEcNo = s.mEcNo; + mLevel = s.mLevel; + } + + /** @hide */ + @Override + public CellSignalStrengthWcdma copy() { + return new CellSignalStrengthWcdma(this); + } + + /** @hide */ + @Override + public void setDefaultValues() { + mRssi = CellInfo.UNAVAILABLE; + mBitErrorRate = CellInfo.UNAVAILABLE; + mRscp = CellInfo.UNAVAILABLE; + mEcNo = CellInfo.UNAVAILABLE; + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + + /** {@inheritDoc} */ + @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + //return mLevel; + } + + /** @hide */ + @Override + public void updateLevel(PersistableBundle cc, ServiceState ss) { + String calcMethod; + int[] rscpThresholds; + + if (cc == null) { + calcMethod = DEFAULT_LEVEL_CALCULATION_METHOD; + rscpThresholds = sRscpThresholds; + } else { + // TODO: abstract this entire thing into a series of functions + calcMethod = cc.getString( + CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, + DEFAULT_LEVEL_CALCULATION_METHOD); + if (TextUtils.isEmpty(calcMethod)) calcMethod = DEFAULT_LEVEL_CALCULATION_METHOD; + rscpThresholds = cc.getIntArray( + CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY); + if (rscpThresholds == null || rscpThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) { + rscpThresholds = sRscpThresholds; + } + } + + int level = NUM_SIGNAL_STRENGTH_THRESHOLDS; + switch (calcMethod) { + case LEVEL_CALCULATION_METHOD_RSCP: + if (mRscp < WCDMA_RSCP_MIN || mRscp > WCDMA_RSCP_MAX) { + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + return; + } + while (level > 0 && mRscp < rscpThresholds[level - 1]) level--; + mLevel = level; + return; + default: + loge("Invalid Level Calculation Method for CellSignalStrengthWcdma = " + + calcMethod); + /** fall through */ + case LEVEL_CALCULATION_METHOD_RSSI: + if (mRssi < WCDMA_RSSI_MIN || mRssi > WCDMA_RSSI_MAX) { + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + return; + } + while (level > 0 && mRssi < sRssiThresholds[level - 1]) level--; + mLevel = level; + return; + } + } + + /** + * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + */ + @Override + public int getDbm() { + if (mRscp != CellInfo.UNAVAILABLE) return mRscp; + return mRssi; + } + + /** + * Get the RSCP in ASU. + * + * Asu is calculated based on 3GPP RSCP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @return RSCP in ASU 0..96, 255, or UNAVAILABLE + */ + @Override + public int getAsuLevel() { + if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp); + // For historical reasons, if RSCP is unavailable, this API will very incorrectly return + // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+ + if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi); + return getAsuFromRscpDbm(CellInfo.UNAVAILABLE); + } + + /** + * Get the RSSI as dBm + * + * @hide + */ + public int getRssi() { + return mRssi; + } + + /** + * Get the RSCP as dBm + * + * @hide + */ + public int getRscp() { + return mRscp; + } + + /** + * Get the Ec/No (Energy per chip over the noise spectral density) as dB. + * + * Reference: TS 25.133 Section 9.1.2.3 + * + * @return the Ec/No of the measured cell in the range [-24, 1] or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable + */ + public int getEcNo() { + return mEcNo; + } + + /** + * Return the Bit Error Rate + * + * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE. + * @hide + */ + public int getBitErrorRate() { + return mBitErrorRate; + } + + @Override + public int hashCode() { + return Objects.hash(mRssi, mBitErrorRate, mRscp, mEcNo, mLevel); + } + + private static final CellSignalStrengthWcdma sInvalid = new CellSignalStrengthWcdma(); + + /** @hide */ + @Override + public boolean isValid() { + return !this.equals(sInvalid); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof CellSignalStrengthWcdma)) return false; + CellSignalStrengthWcdma s = (CellSignalStrengthWcdma) o; + + return mRssi == s.mRssi + && mBitErrorRate == s.mBitErrorRate + && mRscp == s.mRscp + && mEcNo == s.mEcNo + && mLevel == s.mLevel; + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return "CellSignalStrengthWcdma:" + + " ss=" + mRssi + + " ber=" + mBitErrorRate + + " rscp=" + mRscp + + " ecno=" + mEcNo + + " level=" + mLevel; + } + + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(Parcel dest, int flags) { + if (DBG) log("writeToParcel(Parcel, int): " + toString()); + dest.writeInt(mRssi); + dest.writeInt(mBitErrorRate); + dest.writeInt(mRscp); + dest.writeInt(mEcNo); + dest.writeInt(mLevel); + } + + /** + * Construct a SignalStrength object from the given parcel + * where the token is already been processed. + */ + private CellSignalStrengthWcdma(Parcel in) { + mRssi = in.readInt(); + mBitErrorRate = in.readInt(); + mRscp = in.readInt(); + mEcNo = in.readInt(); + mLevel = in.readInt(); + if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString()); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + @SuppressWarnings("hiding") + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CellSignalStrengthWcdma createFromParcel(Parcel in) { + return new CellSignalStrengthWcdma(in); + } + + @Override + public CellSignalStrengthWcdma[] newArray(int size) { + return new CellSignalStrengthWcdma[size]; + } + }; + + /** + * log warning + */ + private static void log(String s) { + Rlog.w(LOG_TAG, s); + } + + /** + * log error + */ + private static void loge(String s) { + Rlog.e(LOG_TAG, s); + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/SignalStrength.java b/aosp/frameworks/base/telephony/java/android/telephony/SignalStrength.java new file mode 100755 index 0000000000000000000000000000000000000000..77ae2c1a41f3546fdd478dad7dfd881564b103ad --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/SignalStrength.java @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.NonNull; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.os.SystemClock; + +import com.android.telephony.Rlog; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import android.util.Log; +import java.util.Random; + +import android.os.SystemProperties; + + + +/** + * Contains phone signal strength related information. + */ +public class SignalStrength implements Parcelable { + + private static final String LOG_TAG = "SignalStrength"; + private static final boolean DBG = false; + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = + CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0 + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static final int SIGNAL_STRENGTH_POOR = + CellSignalStrength.SIGNAL_STRENGTH_POOR; // = 1 + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static final int SIGNAL_STRENGTH_MODERATE = + CellSignalStrength.SIGNAL_STRENGTH_MODERATE; // = 2 + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static final int SIGNAL_STRENGTH_GOOD = + CellSignalStrength.SIGNAL_STRENGTH_GOOD; // = 3 + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static final int SIGNAL_STRENGTH_GREAT = + CellSignalStrength.SIGNAL_STRENGTH_GREAT; // = 4 + /** @hide */ + @UnsupportedAppUsage + public static final int NUM_SIGNAL_STRENGTH_BINS = 5; + + /** + * Indicates the invalid measures of signal strength. + * + * For example, this can be returned by {@link #getEvdoDbm()} or {@link #getCdmaDbm()} + */ + public static final int INVALID = Integer.MAX_VALUE; + + // Timestamp of SignalStrength since boot + // Effectively final. Timestamp is set during construction of SignalStrength + private long mTimestampMillis; + + private boolean mLteAsPrimaryInNrNsa = true; + + CellSignalStrengthCdma mCdma; + CellSignalStrengthGsm mGsm; + CellSignalStrengthWcdma mWcdma; + CellSignalStrengthTdscdma mTdscdma; + CellSignalStrengthLte mLte; + CellSignalStrengthNr mNr; + + /** + * This constructor is used to create SignalStrength with default + * values. + * + * @hide + */ + @UnsupportedAppUsage + public SignalStrength() { + this(new CellSignalStrengthCdma(), new CellSignalStrengthGsm(), + new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(), + new CellSignalStrengthLte(), new CellSignalStrengthNr()); + } + + /** + * Constructor with all fields present + * + * @hide + */ + public SignalStrength( + @NonNull CellSignalStrengthCdma cdma, + @NonNull CellSignalStrengthGsm gsm, + @NonNull CellSignalStrengthWcdma wcdma, + @NonNull CellSignalStrengthTdscdma tdscdma, + @NonNull CellSignalStrengthLte lte, + @NonNull CellSignalStrengthNr nr) { + mCdma = cdma; + mGsm = gsm; + mWcdma = wcdma; + mTdscdma = tdscdma; + mLte = lte; + mNr = nr; + mTimestampMillis = SystemClock.elapsedRealtime(); + } + + private CellSignalStrength getPrimary() { + // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing + // newer faster RATs for default/for display purposes. + + if (mLteAsPrimaryInNrNsa) { + if (mLte.isValid()) return mLte; + } + if (mNr.isValid()) return mNr; + if (mLte.isValid()) return mLte; + if (mCdma.isValid()) return mCdma; + if (mTdscdma.isValid()) return mTdscdma; + if (mWcdma.isValid()) return mWcdma; + if (mGsm.isValid()) return mGsm; + return mLte; + } + + /** + * Returns a List of CellSignalStrength Components of this SignalStrength Report. + * + * Use this API to access underlying + * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more + * granular information about the SignalStrength report. Only valid (non-empty) + * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed, + * and the list may contain more than one instance of a CellSignalStrength type. + * + * @return a List of CellSignalStrength or an empty List if there are no valid measurements. + * + * @see android.telephony.CellSignalStrength + * @see android.telephony.CellSignalStrengthNr + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.CellSignalStrengthTdscdma + * @see android.telephony.CellSignalStrengthWcdma + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.CellSignalStrengthGsm + */ + @NonNull public List getCellSignalStrengths() { + return getCellSignalStrengths(CellSignalStrength.class); + } + + /** + * Returns a List of CellSignalStrength Components of this SignalStrength Report. + * + * Use this API to access underlying + * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more + * granular information about the SignalStrength report. Only valid (non-empty) + * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed, + * and the list may contain more than one instance of a CellSignalStrength type. + * + * @param clazz a class type that extends + * {@link android.telephony.CellSignalStrength CellSignalStrength} to filter possible + * return values. + * @return a List of CellSignalStrength or an empty List if there are no valid measurements. + * + * @see android.telephony.CellSignalStrength + * @see android.telephony.CellSignalStrengthNr + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.CellSignalStrengthTdscdma + * @see android.telephony.CellSignalStrengthWcdma + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.CellSignalStrengthGsm + */ + @NonNull public List getCellSignalStrengths( + @NonNull Class clazz) { + List cssList = new ArrayList<>(2); // Usually have 2 or fewer elems + if (mLte.isValid() && clazz.isAssignableFrom(CellSignalStrengthLte.class)) { + cssList.add((T) mLte); + } + if (mCdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthCdma.class)) { + cssList.add((T) mCdma); + } + if (mTdscdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthTdscdma.class)) { + cssList.add((T) mTdscdma); + } + if (mWcdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthWcdma.class)) { + cssList.add((T) mWcdma); + } + if (mGsm.isValid() && clazz.isAssignableFrom(CellSignalStrengthGsm.class)) { + cssList.add((T) mGsm); + } + if (mNr.isValid() && clazz.isAssignableFrom(CellSignalStrengthNr.class)) { + cssList.add((T) mNr); + } + return cssList; + } + + /** @hide */ + public void updateLevel(PersistableBundle cc, ServiceState ss) { + if (cc != null) { + mLteAsPrimaryInNrNsa = cc.getBoolean( + CarrierConfigManager.KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true); + } + mCdma.updateLevel(cc, ss); + mGsm.updateLevel(cc, ss); + mWcdma.updateLevel(cc, ss); + mTdscdma.updateLevel(cc, ss); + mLte.updateLevel(cc, ss); + mNr.updateLevel(cc, ss); + } + + /** + * This constructor is used to create a copy of an existing SignalStrength object. + * + * @param s Source SignalStrength + */ + public SignalStrength(@NonNull SignalStrength s) { + copyFrom(s); + } + + /** + * @hide + */ + @UnsupportedAppUsage + protected void copyFrom(SignalStrength s) { + mCdma = new CellSignalStrengthCdma(s.mCdma); + mGsm = new CellSignalStrengthGsm(s.mGsm); + mWcdma = new CellSignalStrengthWcdma(s.mWcdma); + mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma); + mLte = new CellSignalStrengthLte(s.mLte); + mNr = new CellSignalStrengthNr(s.mNr); + mTimestampMillis = s.getTimestampMillis(); + } + + /** + * Construct a SignalStrength object from the given parcel. + * + * @hide + */ + @UnsupportedAppUsage + public SignalStrength(Parcel in) { + if (DBG) log("Size of signalstrength parcel:" + in.dataSize()); + + mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader(), android.telephony.CellSignalStrengthCdma.class); + mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader(), android.telephony.CellSignalStrengthGsm.class); + mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader(), android.telephony.CellSignalStrengthWcdma.class); + mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader(), android.telephony.CellSignalStrengthTdscdma.class); + mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthLte.class); + mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthNr.class); + mTimestampMillis = in.readLong(); + } + + /** + * {@link Parcelable#writeToParcel} + */ + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mCdma, flags); + out.writeParcelable(mGsm, flags); + out.writeParcelable(mWcdma, flags); + out.writeParcelable(mTdscdma, flags); + out.writeParcelable(mLte, flags); + out.writeParcelable(mNr, flags); + out.writeLong(mTimestampMillis); + } + + /** + * @return timestamp in milliseconds since boot for {@link SignalStrength}. + * This timestamp reports the approximate time that the signal was measured and reported + * by the modem. It can be used to compare the recency of {@link SignalStrength} instances. + */ + @ElapsedRealtimeLong + public long getTimestampMillis() { + return mTimestampMillis; + } + + /** + * {@link Parcelable#describeContents} + */ + public int describeContents() { + return 0; + } + + /** + * {@link Parcelable.Creator} + * + */ + public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator<>() { + public SignalStrength createFromParcel(Parcel in) { + return new SignalStrength(in); + } + + public SignalStrength[] newArray(int size) { + return new SignalStrength[size]; + } + }; + + /** + * Get the GSM RSSI in ASU. + * + * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @return RSSI in ASU 0..31, 99, or UNAVAILABLE + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthGsm#getAsuLevel}. + * @see android.telephony.CellSignalStrengthGsm + * @see android.telephony.SignalStrength#getCellSignalStrengths + */ + @Deprecated + public int getGsmSignalStrength() { + return mGsm.getAsuLevel(); + } + + /** + * Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthGsm#getBitErrorRate}. + * + * @see android.telephony.CellSignalStrengthGsm + * @see android.telephony.SignalStrength#getCellSignalStrengths() + */ + @Deprecated + public int getGsmBitErrorRate() { + return mGsm.getBitErrorRate(); + } + + /** + * Get the CDMA RSSI value in dBm + * + * @return the CDMA RSSI value or {@link #INVALID} if invalid + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getCdmaDbm}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + */ + @Deprecated + public int getCdmaDbm() { + return mCdma.getCdmaDbm(); + } + + /** + * Get the CDMA Ec/Io value in dB*10 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getCdmaEcio}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + */ + @Deprecated + public int getCdmaEcio() { + return mCdma.getCdmaEcio(); + } + + /** + * Get the EVDO RSSI value in dBm + * + * @return the EVDO RSSI value or {@link #INVALID} if invalid + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getEvdoDbm}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + */ + @Deprecated + public int getEvdoDbm() { + return mCdma.getEvdoDbm(); + } + + /** + * Get the EVDO Ec/Io value in dB*10 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getEvdoEcio}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + */ + @Deprecated + public int getEvdoEcio() { + return mCdma.getEvdoEcio(); + } + + /** + * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest. + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getEvdoSnr}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + */ + @Deprecated + public int getEvdoSnr() { + return mCdma.getEvdoSnr(); + } + + /** + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getRssi}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteSignalStrength() { + return mLte.getRssi(); + } + + /** + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getRsrp}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteRsrp() { + return mLte.getRsrp(); + } + + /** + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getRsrq}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteRsrq() { + return mLte.getRsrq(); + } + + /** + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getRssnr}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteRssnr() { + return mLte.getRssnr(); + } + + /** + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getCqi}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteCqi() { + return mLte.getCqi(); + } + + /** + * Retrieve an abstract level value for the overall signal strength. + * + * @return a single integer from 0 to 4 representing the general signal quality. + * This may take into account many different radio technology inputs. + * 0 represents very poor signal strength + * while 4 represents a very strong signal strength. + */ + public int getLevel() { + //ccynice for sim signalLevel + return SystemProperties.getInt("persist.sim.signalLevel",4); + /* + int level = getPrimary().getLevel(); + if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) { + loge("Invalid Level " + level + ", this=" + this); + return SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + } + return getPrimary().getLevel(); + */ + } + + /** + * Get the signal level as an asu value with a range dependent on the underlying technology. + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrength#getAsuLevel}. Because the levels vary by technology, + * this method is misleading and should not be used. + * @see android.telephony.CellSignalStrength + * @see android.telephony.SignalStrength#getCellSignalStrengths + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getAsuLevel() { + return getPrimary().getAsuLevel(); + } + + /** + * Get the signal strength as dBm + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrength#getDbm()}. Because the levels vary by technology, + * this method is misleading and should not be used. + * @see android.telephony.CellSignalStrength + * @see android.telephony.SignalStrength#getCellSignalStrengths + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getDbm() { + return getPrimary().getDbm(); + } + + /** + * Get Gsm signal strength as dBm + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthGsm#getDbm}. + * + * @see android.telephony.CellSignalStrengthGsm + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getGsmDbm() { + return mGsm.getDbm(); + } + + /** + * Get gsm as level 0..4 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthGsm#getLevel}. + * + * @see android.telephony.CellSignalStrengthGsm + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getGsmLevel() { + return mGsm.getLevel(); + } + + /** + * Get the gsm signal level as an asu value between 0..31, 99 is unknown + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthGsm#getAsuLevel}. + * + * @see android.telephony.CellSignalStrengthGsm + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getGsmAsuLevel() { + return mGsm.getAsuLevel(); + } + + /** + * Get cdma as level 0..4 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getLevel}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getCdmaLevel() { + return mCdma.getLevel(); + } + + /** + * Get the cdma signal level as an asu value between 0..31, 99 is unknown + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getAsuLevel}. Since there is no definition of + * ASU for CDMA, the resultant value is Android-specific and is not recommended + * for use. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getCdmaAsuLevel() { + return mCdma.getAsuLevel(); + } + + /** + * Get Evdo as level 0..4 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getEvdoLevel}. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getEvdoLevel() { + return mCdma.getEvdoLevel(); + } + + /** + * Get the evdo signal level as an asu value between 0..31, 99 is unknown + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthCdma#getEvdoAsuLevel}. Since there is no definition of + * ASU for EvDO, the resultant value is Android-specific and is not recommended + * for use. + * + * @see android.telephony.CellSignalStrengthCdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getEvdoAsuLevel() { + return mCdma.getEvdoAsuLevel(); + } + + /** + * Get LTE as dBm + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getDbm}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteDbm() { + return mLte.getRsrp(); + } + + /** + * Get LTE as level 0..4 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getLevel}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteLevel() { + return mLte.getLevel(); + } + + /** + * Get the LTE signal level as an asu value between 0..97, 99 is unknown + * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthLte#getAsuLevel}. + * + * @see android.telephony.CellSignalStrengthLte + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getLteAsuLevel() { + return mLte.getAsuLevel(); + } + + /** + * @return true if this is for GSM + * + * @deprecated This method returns true if there are any 3gpp type SignalStrength elements in + * this SignalStrength report or if the report contains no valid SignalStrength + * information. Instead callers should use + * {@link android.telephony.SignalStrength#getCellSignalStrengths + * getCellSignalStrengths()} to determine which types of information are contained + * in the SignalStrength report. + */ + @Deprecated + public boolean isGsm() { + return !(getPrimary() instanceof CellSignalStrengthCdma); + } + + /** + * @return get TD-SCDMA dBm + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthTdscdma#getDbm}. + * + * @see android.telephony.CellSignalStrengthTdscdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getTdScdmaDbm() { + return mTdscdma.getRscp(); + } + + /** + * Get TD-SCDMA as level 0..4 + * Range : 25 to 120 + * INT_MAX: 0x7FFFFFFF denotes invalid value + * Reference: 3GPP TS 25.123, section 9.1.1.1 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthTdscdma#getLevel}. + * + * @see android.telephony.CellSignalStrengthTdscdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getTdScdmaLevel() { + return mTdscdma.getLevel(); + } + + /** + * Get the TD-SCDMA signal level as an asu value. + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthTdscdma#getAsuLevel}. + * + * @see android.telephony.CellSignalStrengthTdscdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getTdScdmaAsuLevel() { + return mTdscdma.getAsuLevel(); + } + + /** + * Gets WCDMA RSCP as a dBm value between -120 and -24, as defined in TS 27.007 8.69. + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthWcdma#getRscp}. + * + * @see android.telephony.CellSignalStrengthWcdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + public int getWcdmaRscp() { + return mWcdma.getRscp(); + } + + /** + * Get the WCDMA signal level as an ASU value between 0-96, 255 is unknown + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthWcdma#getAsuLevel}. + * + * @see android.telephony.CellSignalStrengthWcdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + public int getWcdmaAsuLevel() { + /* + * 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * 0 -120 dBm or less + * 1 -119 dBm + * 2...95 -118... -25 dBm + * 96 -24 dBm or greater + * 255 not known or not detectable + */ + return mWcdma.getAsuLevel(); + } + + /** + * Gets WCDMA signal strength as a dBm value between -120 and -24, as defined in TS 27.007 8.69. + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthWcdma#getDbm}. + * + * @see android.telephony.CellSignalStrengthWcdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + public int getWcdmaDbm() { + return mWcdma.getDbm(); + } + + /** + * Get WCDMA as level 0..4 + * + * @deprecated this information should be retrieved from + * {@link CellSignalStrengthWcdma#getDbm}. + * + * @see android.telephony.CellSignalStrengthWcdma + * @see android.telephony.SignalStrength#getCellSignalStrengths() + * @hide + */ + @Deprecated + public int getWcdmaLevel() { + return mWcdma.getLevel(); + } + + /** + * @return hash code + */ + @Override + public int hashCode() { + return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mNr); + } + + /** + * @return true if the signal strengths are the same + */ + @Override + public boolean equals (Object o) { + if (!(o instanceof SignalStrength)) return false; + + SignalStrength s = (SignalStrength) o; + + return mCdma.equals(s.mCdma) + && mGsm.equals(s.mGsm) + && mWcdma.equals(s.mWcdma) + && mTdscdma.equals(s.mTdscdma) + && mLte.equals(s.mLte) + && mNr.equals(s.mNr); + } + + /** + * @return string representation. + */ + @Override + public String toString() { + return new StringBuilder().append("SignalStrength:{") + .append("mCdma=").append(mCdma) + .append(",mGsm=").append(mGsm) + .append(",mWcdma=").append(mWcdma) + .append(",mTdscdma=").append(mTdscdma) + .append(",mLte=").append(mLte) + .append(",mNr=").append(mNr) + .append(",primary=").append(getPrimary().getClass().getSimpleName()) + .append("}") + .toString(); + } + + /** + * Set intent notifier Bundle based on SignalStrength + * + * @param m intent notifier Bundle + * + * @deprecated this method relies on non-stable implementation details, and full access to + * internal storage is available via {@link #getCellSignalStrengths()}. + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public void fillInNotifierBundle(Bundle m) { + m.putParcelable("Cdma", mCdma); + m.putParcelable("Gsm", mGsm); + m.putParcelable("Wcdma", mWcdma); + m.putParcelable("Tdscdma", mTdscdma); + m.putParcelable("Lte", mLte); + m.putParcelable("Nr", mNr); + } + + /** + * log warning + */ + private static void log(String s) { + Rlog.w(LOG_TAG, s); + } + + /** + * log error + */ + private static void loge(String s) { + Rlog.e(LOG_TAG, s); + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/SubscriptionManager.java b/aosp/frameworks/base/telephony/java/android/telephony/SubscriptionManager.java new file mode 100755 index 0000000000000000000000000000000000000000..05353670773cf5ab9ecc9a8ab8204cf7bb2455cd --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/SubscriptionManager.java @@ -0,0 +1,4975 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED; +import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.ColorInt; +import android.annotation.DurationMillisLong; +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresFeature; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressAutoDoc; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.app.PendingIntent; +import android.app.PropertyInvalidatedCache; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.NetworkCapabilities; +import android.net.NetworkPolicyManager; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.ParcelUuid; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Telephony.SimInfo; +import android.telephony.euicc.EuiccManager; +import android.telephony.ims.ImsMmTelManager; +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; +import android.util.LruCache; +import android.util.Pair; + +import com.android.internal.telephony.ISetOpportunisticDataCallback; +import com.android.internal.telephony.ISub; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.flags.Flags; +import com.android.internal.telephony.util.HandlerExecutor; +import com.android.internal.util.FunctionalUtils; +import com.android.internal.util.Preconditions; +import com.android.telephony.Rlog; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * Subscription manager provides the mobile subscription information that are associated with the + * calling user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U) + * and below can see all subscriptions as it does today. + * + *

For example, if we have + *

    + *
  • Subscription 1 associated with personal profile. + *
  • Subscription 2 associated with work profile. + *
+ * Then for SDK 35+, if the caller identity is personal profile, then + * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa. + * + */ +@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) +@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) +public class SubscriptionManager { + private static final String LOG_TAG = "SubscriptionManager"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** An invalid subscription identifier */ + public static final int INVALID_SUBSCRIPTION_ID = -1; + + /** Base value for placeholder SUBSCRIPTION_ID's. */ + /** @hide */ + public static final int PLACEHOLDER_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1; + + /** An invalid phone identifier */ + /** @hide */ + public static final int INVALID_PHONE_INDEX = -1; + + /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */ + public static final int INVALID_SIM_SLOT_INDEX = -1; + + /** Indicates the default subscription ID in Telephony. */ + public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE; + + /** + * Indicates the default phone id. + * @hide + */ + public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE; + + /** Indicates the default slot index. */ + /** @hide */ + public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE; + + /** Minimum possible subid that represents a subscription */ + /** @hide */ + public static final int MIN_SUBSCRIPTION_ID_VALUE = 0; + + /** Maximum possible subid that represents a subscription */ + /** @hide */ + public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1; + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static final Uri CONTENT_URI = SimInfo.CONTENT_URI; + + /** The IPC cache key shared by all subscription manager service cacheable properties. */ + private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY = + "cache_key.telephony.subscription_manager_service"; + + /** @hide */ + public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings"; + + /** @hide */ + public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME = + "restoreSimSpecificSettings"; + + /** + * The key of the boolean flag indicating whether restoring subscriptions actually changes + * the subscription database or not. + * + * @hide + */ + public static final String RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED = + "restoreSimSpecificSettingsDatabaseUpdated"; + + /** + * Key to the backup & restore data byte array in the Bundle that is returned by {@link + * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link + * #restoreAllSimSpecificSettingsFromBackup(byte[])}. + * + * @hide + */ + public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA"; + + private static final int MAX_CACHE_SIZE = 4; + + private static class VoidPropertyInvalidatedCache + extends PropertyInvalidatedCache { + private final FunctionalUtils.ThrowingFunction mInterfaceMethod; + private final String mCacheKeyProperty; + private final T mDefaultValue; + + VoidPropertyInvalidatedCache( + FunctionalUtils.ThrowingFunction subscriptionInterfaceMethod, + String cacheKeyProperty, + T defaultValue) { + super(MAX_CACHE_SIZE, cacheKeyProperty); + mInterfaceMethod = subscriptionInterfaceMethod; + mCacheKeyProperty = cacheKeyProperty; + mDefaultValue = defaultValue; + } + + @Override + public T recompute(Void query) { + // This always throws on any error. The exceptions must be handled outside + // the cache. + try { + return mInterfaceMethod.applyOrThrow(TelephonyManager.getSubscriptionService()); + } catch (Exception re) { + throw new RuntimeException(re); + } + } + + @Override + public T query(Void query) { + T result = mDefaultValue; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = super.query(query); + } + } catch (Exception ex) { + Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty); + } + + if (VDBG) logd("recomputing " + mCacheKeyProperty + ", result = " + result); + return result; + } + } + + private static class IntegerPropertyInvalidatedCache + extends PropertyInvalidatedCache { + private final FunctionalUtils.ThrowingBiFunction mInterfaceMethod; + private final String mCacheKeyProperty; + private final T mDefaultValue; + + IntegerPropertyInvalidatedCache( + FunctionalUtils.ThrowingBiFunction subscriptionInterfaceMethod, + String cacheKeyProperty, + T defaultValue) { + super(MAX_CACHE_SIZE, cacheKeyProperty); + mInterfaceMethod = subscriptionInterfaceMethod; + mCacheKeyProperty = cacheKeyProperty; + mDefaultValue = defaultValue; + } + + @Override + public T recompute(Integer query) { + // This always throws on any error. The exceptions must be handled outside + // the cache. + try { + return mInterfaceMethod.applyOrThrow( + TelephonyManager.getSubscriptionService(), query); + } catch (Exception re) { + throw new RuntimeException(re); + } + } + + @Override + public T query(Integer query) { + T result = mDefaultValue; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = super.query(query); + } + } catch (Exception ex) { + Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty); + } + + if (VDBG) logd("recomputing " + mCacheKeyProperty + ", result = " + result); + return result; + } + } + + private static IntegerPropertyInvalidatedCache sGetDefaultSubIdCacheAsUser = + new IntegerPropertyInvalidatedCache<>(ISub::getDefaultSubIdAsUser, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + + private static VoidPropertyInvalidatedCache sGetDefaultDataSubIdCache = + new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + + private static IntegerPropertyInvalidatedCache sGetDefaultSmsSubIdCacheAsUser = + new IntegerPropertyInvalidatedCache<>(ISub::getDefaultSmsSubIdAsUser, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + + private static VoidPropertyInvalidatedCache sGetActiveDataSubscriptionIdCache = + new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + + private static IntegerPropertyInvalidatedCache sGetSlotIndexCache = + new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SIM_SLOT_INDEX); + + private static IntegerPropertyInvalidatedCache sGetSubIdCache = + new IntegerPropertyInvalidatedCache<>(ISub::getSubId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + + private static IntegerPropertyInvalidatedCache sGetPhoneIdCache = + new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_PHONE_INDEX); + + /** + * Generates a content {@link Uri} used to receive updates on simInfo change + * on the given subscriptionId + * @param subscriptionId the subscriptionId to receive updates on + * @return the Uri used to observe carrier identity changes + * @hide + */ + public static Uri getUriForSubscriptionId(int subscriptionId) { + return Uri.withAppendedPath(CONTENT_URI, String.valueOf(subscriptionId)); + } + + /** + * A content {@link Uri} used to receive updates on wfc enabled user setting. + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription wfc enabled {@link ImsMmTelManager#isVoWiFiSettingEnabled()} + * while your app is running. You can also use a {@link android.app.job.JobService} + * to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc"); + + /** + * A content {@link Uri} used to receive updates on advanced calling user setting + * + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription advanced calling enabled + * {@link ImsMmTelManager#isAdvancedCallingSettingEnabled()} while your app is running. + * You can also use a {@link android.app.job.JobService} to ensure your app is notified of + * changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * + * @see ImsMmTelManager#isAdvancedCallingSettingEnabled() + * + * @hide + */ + @NonNull + @SystemApi + public static final Uri ADVANCED_CALLING_ENABLED_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "advanced_calling"); + + /** + * A content {@link Uri} used to receive updates on wfc mode setting. + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription wfc mode {@link ImsMmTelManager#getVoWiFiModeSetting()} + * while your app is running. You can also use a {@link android.app.job.JobService} to ensure + * your app is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri WFC_MODE_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc_mode"); + + /** + * A content {@link Uri} used to receive updates on wfc roaming mode setting. + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription wfc roaming mode {@link ImsMmTelManager#getVoWiFiRoamingModeSetting()} + * while your app is running. You can also use a {@link android.app.job.JobService} + * to ensure your app is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri WFC_ROAMING_MODE_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "wfc_roaming_mode"); + + /** + * A content {@link Uri} used to receive updates on vt(video telephony over IMS) enabled + * setting. + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription vt enabled {@link ImsMmTelManager#isVtSettingEnabled()} + * while your app is running. You can also use a {@link android.app.job.JobService} to ensure + * your app is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri VT_ENABLED_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "vt_enabled"); + + /** + * A content {@link Uri} used to receive updates on wfc roaming enabled setting. + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription wfc roaming enabled {@link ImsMmTelManager#isVoWiFiRoamingSettingEnabled()} + * while your app is running. You can also use a {@link android.app.job.JobService} to ensure + * your app is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "wfc_roaming_enabled"); + + + /** + * A content {@link uri} used to call the appropriate backup or restore method for sim-specific + * settings + *

+ * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link + * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call. + * @hide + */ + @NonNull + public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "backup_and_restore"); + + /** + * A content {@link uri} used to notify contentobservers listening to siminfo restore during + * SuW. + * @hide + */ + @NonNull + public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore"); + + /** + * A content {@link Uri} used to receive updates on cross sim enabled user setting. + *

+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription cross sim calling enabled + * {@link ImsMmTelManager#isCrossSimCallingEnabled()} + * while your app is running. You can also use a {@link android.app.job.JobService} + * to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri CROSS_SIM_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, + SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED); + + /** + * TelephonyProvider unique key column name is the subscription id. + *

Type: TEXT (String)

+ */ + /** @hide */ + public static final String UNIQUE_KEY_SUBSCRIPTION_ID = + SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID; + + /** + * TelephonyProvider column name for a unique identifier for the subscription within the + * specific subscription type. For example, it contains SIM ICC Identifier subscriptions + * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices. + *

Type: TEXT (String)

+ */ + /** @hide */ + public static final String ICC_ID = SimInfo.COLUMN_ICC_ID; + + /** + * TelephonyProvider column name for user SIM_SlOT_INDEX + *

Type: INTEGER (int)

+ */ + /** @hide */ + public static final String SIM_SLOT_INDEX = SimInfo.COLUMN_SIM_SLOT_INDEX; + + /** SIM is not inserted */ + /** @hide */ + public static final int SIM_NOT_INSERTED = SimInfo.SIM_NOT_INSERTED; + + /** + * The slot-index for Bluetooth Remote-SIM subscriptions + * @hide + */ + public static final int SLOT_INDEX_FOR_REMOTE_SIM_SUB = INVALID_SIM_SLOT_INDEX; + + /** + * TelephonyProvider column name Subscription-type. + *

Type: INTEGER (int)

{@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM Subscriptions, + * {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions. + * Default value is 0. + */ + /** @hide */ + public static final String SUBSCRIPTION_TYPE = SimInfo.COLUMN_SUBSCRIPTION_TYPE; + + /** + * TelephonyProvider column name for last used TP - message Reference + *

Type: INTEGER (int)

with -1 as default value + * TP - Message Reference valid range [0 - 255] + * @hide + */ + public static final String TP_MESSAGE_REF = SimInfo.COLUMN_TP_MESSAGE_REF; + + /** + * TelephonyProvider column name enabled_mobile_data_policies. + * A list of mobile data policies, each of which represented by an integer and joint by ",". + * + * Default value is empty string. + * @hide + */ + public static final String ENABLED_MOBILE_DATA_POLICIES = + SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SUBSCRIPTION_TYPE_"}, + value = { + SUBSCRIPTION_TYPE_LOCAL_SIM, + SUBSCRIPTION_TYPE_REMOTE_SIM}) + public @interface SubscriptionType {} + + /** + * This constant is to designate a subscription as a Local-SIM Subscription. + *

A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the + * device. + *

+ */ + public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM; + + /** + * This constant is to designate a subscription as a Remote-SIM Subscription. + *

+ * A Remote-SIM subscription is for a SIM on a phone connected to this device via some + * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription can + * be used for SMS, Voice and data by proxying data through the connected device. + * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs. + *

+ * + *

+ * A Remote-SIM is available only as long the phone stays connected to this device. + * When the phone disconnects, Remote-SIM subscription is removed from this device and is + * no longer known. All data associated with the subscription, such as stored SMS, call logs, + * contacts etc, are removed from this device. + *

+ * + *

+ * If the phone re-connects to this device, a new Remote-SIM subscription is created for + * the phone. The Subscription Id associated with the new subscription is different from + * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the + * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM that + * was never seen before. + *

+ */ + public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = SimInfo.SUBSCRIPTION_TYPE_REMOTE_SIM; + + /** + * TelephonyProvider column name for user displayed name. + *

Type: TEXT (String)

+ */ + /** @hide */ + public static final String DISPLAY_NAME = SimInfo.COLUMN_DISPLAY_NAME; + + /** + * TelephonyProvider column name for the service provider name for the SIM. + *

Type: TEXT (String)

+ */ + /** @hide */ + public static final String CARRIER_NAME = SimInfo.COLUMN_CARRIER_NAME; + + /** + * Default name resource + * @hide + */ + public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName; + + /** + * TelephonyProvider column name for source of the user displayed name. + *

Type: INT (int)

with one of the NAME_SOURCE_XXXX values below + * + * @hide + */ + public static final String NAME_SOURCE = SimInfo.COLUMN_NAME_SOURCE; + + /** + * The name_source is unknown. (for initialization) + * @hide + */ + public static final int NAME_SOURCE_UNKNOWN = SimInfo.NAME_SOURCE_UNKNOWN; + + /** + * The name_source is from the carrier id. + * @hide + */ + public static final int NAME_SOURCE_CARRIER_ID = SimInfo.NAME_SOURCE_CARRIER_ID; + + /** + * The name_source is from SIM EF_SPN. + * @hide + */ + public static final int NAME_SOURCE_SIM_SPN = SimInfo.NAME_SOURCE_SIM_SPN; + + /** + * The name_source is from user input + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public static final int NAME_SOURCE_USER_INPUT = SimInfo.NAME_SOURCE_USER_INPUT; + + /** + * The name_source is carrier (carrier app, carrier config, etc.) + * @hide + */ + public static final int NAME_SOURCE_CARRIER = SimInfo.NAME_SOURCE_CARRIER; + + /** + * The name_source is from SIM EF_PNN. + * @hide + */ + public static final int NAME_SOURCE_SIM_PNN = SimInfo.NAME_SOURCE_SIM_PNN; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"NAME_SOURCE_"}, + value = { + NAME_SOURCE_UNKNOWN, + NAME_SOURCE_CARRIER_ID, + NAME_SOURCE_SIM_SPN, + NAME_SOURCE_USER_INPUT, + NAME_SOURCE_CARRIER, + NAME_SOURCE_SIM_PNN + }) + public @interface SimDisplayNameSource {} + + /** + * Device status is not shared to a remote party. + */ + public static final int D2D_SHARING_DISABLED = 0; + + /** + * Device status is shared with all numbers in the user's contacts. + */ + public static final int D2D_SHARING_ALL_CONTACTS = 1; + + /** + * Device status is shared with all selected contacts. + */ + public static final int D2D_SHARING_SELECTED_CONTACTS = 2; + + /** + * Device status is shared whenever possible. + */ + public static final int D2D_SHARING_ALL = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"D2D_SHARING_"}, + value = { + D2D_SHARING_DISABLED, + D2D_SHARING_ALL_CONTACTS, + D2D_SHARING_SELECTED_CONTACTS, + D2D_SHARING_ALL + }) + public @interface DeviceToDeviceStatusSharingPreference {} + + /** + * TelephonyProvider column name for device to device sharing status. + *

Type: INTEGER (int)

+ */ + public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING; + + /** + * TelephonyProvider column name for contacts information that allow device to device sharing. + *

Type: TEXT (String)

+ */ + public static final String D2D_STATUS_SHARING_SELECTED_CONTACTS = + SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS; + + /** + * TelephonyProvider column name for the color of a SIM. + *

Type: INTEGER (int)

+ */ + /** @hide */ + public static final String HUE = SimInfo.COLUMN_COLOR; + + /** + * TelephonyProvider column name for the phone number of a SIM. + *

Type: TEXT (String)

+ */ + /** @hide */ + public static final String NUMBER = SimInfo.COLUMN_NUMBER; + + /** + * TelephonyProvider column name for whether data roaming is enabled. + *

Type: INTEGER (int)

+ */ + /** @hide */ + public static final String DATA_ROAMING = SimInfo.COLUMN_DATA_ROAMING; + + /** Indicates that data roaming is enabled for a subscription */ + public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE; + + /** Indicates that data roaming is disabled for a subscription */ + public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"DATA_ROAMING_"}, + value = { + DATA_ROAMING_ENABLE, + DATA_ROAMING_DISABLE + }) + public @interface DataRoamingMode {} + + /** + * TelephonyProvider column name for subscription carrier id. + * @see TelephonyManager#getSimCarrierId() + *

Type: INTEGER (int)

+ * @hide + */ + public static final String CARRIER_ID = SimInfo.COLUMN_CARRIER_ID; + + /** + * @hide A comma-separated list of EHPLMNs associated with the subscription + *

Type: TEXT (String)

+ */ + public static final String EHPLMNS = SimInfo.COLUMN_EHPLMNS; + + /** + * @hide A comma-separated list of HPLMNs associated with the subscription + *

Type: TEXT (String)

+ */ + public static final String HPLMNS = SimInfo.COLUMN_HPLMNS; + + /** + * TelephonyProvider column name for the MCC associated with a SIM, stored as a string. + *

Type: TEXT (String)

+ * @hide + */ + public static final String MCC_STRING = SimInfo.COLUMN_MCC_STRING; + + /** + * TelephonyProvider column name for the MNC associated with a SIM, stored as a string. + *

Type: TEXT (String)

+ * @hide + */ + public static final String MNC_STRING = SimInfo.COLUMN_MNC_STRING; + + /** + * TelephonyProvider column name for the MCC associated with a SIM. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String MCC = SimInfo.COLUMN_MCC; + + /** + * TelephonyProvider column name for the MNC associated with a SIM. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String MNC = SimInfo.COLUMN_MNC; + + /** + * TelephonyProvider column name for the iso country code associated with a SIM. + *

Type: TEXT (String)

+ * @hide + */ + public static final String ISO_COUNTRY_CODE = SimInfo.COLUMN_ISO_COUNTRY_CODE; + + /** + * TelephonyProvider column name for whether a subscription is embedded (that is, present on an + * eSIM). + *

Type: INTEGER (int), 1 for embedded or 0 for non-embedded. + * @hide + */ + public static final String IS_EMBEDDED = SimInfo.COLUMN_IS_EMBEDDED; + + /** + * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the + * current enabled profile on the card, while for eUICC card it is the EID of the card. + *

Type: TEXT (String)

+ * @hide + */ + public static final String CARD_ID = SimInfo.COLUMN_CARD_ID; + + /** + * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from + * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1. + *

TYPE: BLOB + * @hide + */ + public static final String ACCESS_RULES = SimInfo.COLUMN_ACCESS_RULES; + + /** + * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from + * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs. + * Only present if there are access rules in CarrierConfigs + *

TYPE: BLOB + * @hide + */ + public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = + SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS; + + /** + * TelephonyProvider column name identifying whether an embedded subscription is on a removable + * card. Such subscriptions are marked inaccessible as soon as the current card is removed. + * Otherwise, they will remain accessible unless explicitly deleted. Only present if + * {@link #IS_EMBEDDED} is 1. + *

TYPE: INTEGER (int), 1 for removable or 0 for non-removable. + * @hide + */ + public static final String IS_REMOVABLE = SimInfo.COLUMN_IS_REMOVABLE; + + /** + * TelephonyProvider column name for extreme threat in CB settings + * @hide + */ + public static final String CB_EXTREME_THREAT_ALERT = + SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT; + + /** + * TelephonyProvider column name for severe threat in CB settings + *@hide + */ + public static final String CB_SEVERE_THREAT_ALERT = SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT; + + /** + * TelephonyProvider column name for amber alert in CB settings + *@hide + */ + public static final String CB_AMBER_ALERT = SimInfo.COLUMN_CB_AMBER_ALERT; + + /** + * TelephonyProvider column name for emergency alert in CB settings + *@hide + */ + public static final String CB_EMERGENCY_ALERT = SimInfo.COLUMN_CB_EMERGENCY_ALERT; + + /** + * TelephonyProvider column name for alert sound duration in CB settings + *@hide + */ + public static final String CB_ALERT_SOUND_DURATION = + SimInfo.COLUMN_CB_ALERT_SOUND_DURATION; + + /** + * TelephonyProvider column name for alert reminder interval in CB settings + *@hide + */ + public static final String CB_ALERT_REMINDER_INTERVAL = + SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL; + + /** + * TelephonyProvider column name for enabling vibrate in CB settings + *@hide + */ + public static final String CB_ALERT_VIBRATE = SimInfo.COLUMN_CB_ALERT_VIBRATE; + + /** + * TelephonyProvider column name for enabling alert speech in CB settings + *@hide + */ + public static final String CB_ALERT_SPEECH = SimInfo.COLUMN_CB_ALERT_SPEECH; + + /** + * TelephonyProvider column name for ETWS test alert in CB settings + *@hide + */ + public static final String CB_ETWS_TEST_ALERT = SimInfo.COLUMN_CB_ETWS_TEST_ALERT; + + /** + * TelephonyProvider column name for enable channel50 alert in CB settings + *@hide + */ + public static final String CB_CHANNEL_50_ALERT = SimInfo.COLUMN_CB_CHANNEL_50_ALERT; + + /** + * TelephonyProvider column name for CMAS test alert in CB settings + *@hide + */ + public static final String CB_CMAS_TEST_ALERT = SimInfo.COLUMN_CB_CMAS_TEST_ALERT; + + /** + * TelephonyProvider column name for Opt out dialog in CB settings + *@hide + */ + public static final String CB_OPT_OUT_DIALOG = SimInfo.COLUMN_CB_OPT_OUT_DIALOG; + + /** + * TelephonyProvider column name for enable Volte. + * + * If this setting is not initialized (set to -1) then we use the Carrier Config value + * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. + *@hide + */ + public static final String ENHANCED_4G_MODE_ENABLED = + SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED; + + /** + * TelephonyProvider column name for enable VT (Video Telephony over IMS) + *@hide + */ + public static final String VT_IMS_ENABLED = SimInfo.COLUMN_VT_IMS_ENABLED; + + /** + * TelephonyProvider column name for enable Wifi calling + *@hide + */ + public static final String WFC_IMS_ENABLED = SimInfo.COLUMN_WFC_IMS_ENABLED; + + /** + * TelephonyProvider column name for Wifi calling mode + *@hide + */ + public static final String WFC_IMS_MODE = SimInfo.COLUMN_WFC_IMS_MODE; + + /** + * TelephonyProvider column name for Wifi calling mode in roaming + *@hide + */ + public static final String WFC_IMS_ROAMING_MODE = SimInfo.COLUMN_WFC_IMS_ROAMING_MODE; + + /** + * TelephonyProvider column name for enable Wifi calling in roaming + *@hide + */ + public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED; + + /** + * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this + * subscription. + * @hide + */ + public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED; + + /** + * Determines if the user has enabled cross SIM calling for this subscription. + * + * @hide + */ + public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED; + + /** + * TelephonyProvider column name for whether a subscription is opportunistic, that is, + * whether the network it connects to is limited in functionality or coverage. + * For example, CBRS. + *

Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic. + * @hide + */ + public static final String IS_OPPORTUNISTIC = SimInfo.COLUMN_IS_OPPORTUNISTIC; + + /** + * TelephonyProvider column name for group ID. Subscriptions with same group ID + * are considered bundled together, and should behave as a single subscription at + * certain scenarios. + * + * @hide + */ + public static final String GROUP_UUID = SimInfo.COLUMN_GROUP_UUID; + + /** + * TelephonyProvider column name for group owner. It's the package name who created + * the subscription group. + * + * @hide + */ + public static final String GROUP_OWNER = SimInfo.COLUMN_GROUP_OWNER; + + /** + * TelephonyProvider column name for the profile class of a subscription + * Only present if {@link #IS_EMBEDDED} is 1. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS; + + /** + * TelephonyProvider column name for the port index of the active UICC port. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String PORT_INDEX = SimInfo.COLUMN_PORT_INDEX; + + /** + * TelephonyProvider column name for VoIMS opt-in status. + * + *

Type: INTEGER (int)

+ * @hide + */ + public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS; + + /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String NR_ADVANCED_CALLING_ENABLED = + SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED; + + /** + * Profile class of the subscription + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "PROFILE_CLASS_" }, value = { + SimInfo.PROFILE_CLASS_TESTING, + SimInfo.PROFILE_CLASS_PROVISIONING, + SimInfo.PROFILE_CLASS_OPERATIONAL, + SimInfo.PROFILE_CLASS_UNSET, + }) + public @interface ProfileClass {} + + /** + * A testing profile can be pre-loaded or downloaded onto + * the eUICC and provides connectivity to test equipment + * for the purpose of testing the device and the eUICC. It + * is not intended to store any operator credentials. + * @hide + */ + @SystemApi + public static final int PROFILE_CLASS_TESTING = SimInfo.PROFILE_CLASS_TESTING; + + /** + * A provisioning profile is pre-loaded onto the eUICC and + * provides connectivity to a mobile network solely for the + * purpose of provisioning profiles. + * @hide + */ + @SystemApi + public static final int PROFILE_CLASS_PROVISIONING = SimInfo.PROFILE_CLASS_PROVISIONING; + + /** + * An operational profile can be pre-loaded or downloaded + * onto the eUICC and provides services provided by the + * operator. + * @hide + */ + @SystemApi + public static final int PROFILE_CLASS_OPERATIONAL = SimInfo.PROFILE_CLASS_OPERATIONAL; + + /** + * The profile class is unset. This occurs when profile class + * info is not available. The subscription either has no profile + * metadata or the profile metadata did not encode profile class. + * @hide + */ + @SystemApi + public static final int PROFILE_CLASS_UNSET = SimInfo.PROFILE_CLASS_UNSET; + + /** + * Default profile class + * @hide + */ + @SystemApi + @Deprecated + public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_UNSET; + + /** + * IMSI (International Mobile Subscriber Identity). + *

Type: TEXT

+ * @hide + */ + //TODO: add @SystemApi + public static final String IMSI = SimInfo.COLUMN_IMSI; + + /** + * Whether uicc applications is set to be enabled or disabled. By default it's enabled. + * @hide + */ + public static final String UICC_APPLICATIONS_ENABLED = SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED; + + /** + * Indicate which network type is allowed. + * @hide + */ + public static final String ALLOWED_NETWORK_TYPES = + SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS; + + /** + * TelephonyProvider column name for user handle associated with a sim. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String USER_HANDLE = SimInfo.COLUMN_USER_HANDLE; + + /** + * TelephonyProvider column name for satellite enabled. + * By default, it's disabled. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String SATELLITE_ENABLED = SimInfo.COLUMN_SATELLITE_ENABLED; + + /** + * TelephonyProvider column name for satellite attach enabled for carrier. The value of this + * column is set based on user settings. + * By default, it's enabled. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String SATELLITE_ATTACH_ENABLED_FOR_CARRIER = + SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER; + + /** + * TelephonyProvider column name to identify eSIM profile of a non-terrestrial network. + * By default, it's disabled. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String IS_NTN = SimInfo.COLUMN_IS_NTN; + + /** + * TelephonyProvider column name to identify service capabilities. + * Disabled by default. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String SERVICE_CAPABILITIES = SimInfo.COLUMN_SERVICE_CAPABILITIES; + + /** + * TelephonyProvider column name to identify eSIM's transfer status. + * By default, it's disabled. + *

Type: INTEGER (int)

+ * @hide + */ + public static final String TRANSFER_STATUS = SimInfo.COLUMN_TRANSFER_STATUS; + + /** + * TelephonyProvider column name for satellite entitlement status. The value of this column is + * set based on entitlement query result for satellite configuration. + * By default, it's disabled. + *

Type: INTEGER (int)

+ * + * @hide + */ + public static final String SATELLITE_ENTITLEMENT_STATUS = + SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS; + + /** + * TelephonyProvider column name for satellite entitlement plmns. The value of this column is + * set based on entitlement query result for satellite configuration. + * By default, it's empty. + *

Type: TEXT

+ * + * @hide + */ + public static final String SATELLITE_ENTITLEMENT_PLMNS = + SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"USAGE_SETTING_"}, + value = { + USAGE_SETTING_UNKNOWN, + USAGE_SETTING_DEFAULT, + USAGE_SETTING_VOICE_CENTRIC, + USAGE_SETTING_DATA_CENTRIC}) + public @interface UsageSetting {} + + /** + * The usage setting is unknown. + * + * This will be the usage setting returned on devices that do not support querying the + * or setting the usage setting. + * + * It may also be provided by a carrier that wishes to provide a value to avoid making any + * settings changes. + */ + public static final int USAGE_SETTING_UNKNOWN = -1; + + /** + * Subscription uses the default setting. + * + * The value is based upon device capability and the other properties of the subscription. + * + * Most subscriptions will default to voice-centric when in a phone. + * + * An opportunistic subscription will default to data-centric. + * + * @see SubscriptionInfo#isOpportunistic + */ + public static final int USAGE_SETTING_DEFAULT = 0; + + /** + * This subscription is forced to voice-centric mode + * + *

Refer to voice-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3. + * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221 + * Annex A. + * + *

Devices that support {@link PackageManager#FEATURE_TELEPHONY_CALLING} and support usage + * setting configuration must support setting this value via + * {@link CarrierConfigManager#KEY_CELLULAR_USAGE_SETTING_INT}. + */ + public static final int USAGE_SETTING_VOICE_CENTRIC = 1; + + /** + * This subscription is forced to data-centric mode + * + *

Refer to data-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3. + * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221 + * Annex A. + * + *

Devices that support {@link PackageManager#FEATURE_TELEPHONY_DATA} and support usage + * setting configuration must support setting this value via. + * {@link CarrierConfigManager#KEY_CELLULAR_USAGE_SETTING_INT}. + */ + public static final int USAGE_SETTING_DATA_CENTRIC = 2; + + /** + * Indicate the preferred usage setting for the subscription. + * + * 0 - Default - If the value has not been explicitly set, it will be "default" + * 1 - Voice-centric + * 2 - Data-centric + * + * @hide + */ + public static final String USAGE_SETTING = SimInfo.COLUMN_USAGE_SETTING; + + /** + * Broadcast Action: The user has changed one of the default subs related to + * data, phone calls, or sms

+ * + * TODO: Change to a listener + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String SUB_DEFAULT_CHANGED_ACTION = + "android.intent.action.SUB_DEFAULT_CHANGED"; + + /** + * Broadcast Action: The default subscription has changed. This has the following + * extra values:

+ * The {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default subscription index + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED + = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED"; + + /** + * Broadcast Action: The default sms subscription has changed. This has the following + * extra values:

+ * {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default sms + * subscription index + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED + = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"; + + /** + * Activity Action: Display UI for managing the billing relationship plans + * between a carrier and a specific subscriber. + *

+ * Carrier apps are encouraged to implement this activity, and the OS will + * provide an affordance to quickly enter this activity, typically via + * Settings. This affordance will only be shown when the carrier app is + * actively providing subscription plan information via + * {@link #setSubscriptionPlans(int, List)}. + *

+ * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription + * the user is interested in. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS + = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; + + /** + * Broadcast Action: Request a refresh of the billing relationship plans + * between a carrier and a specific subscriber. + *

+ * Carrier apps are encouraged to implement this receiver, and the OS will + * provide an affordance to request a refresh. This affordance will only be + * shown when the carrier app is actively providing subscription plan + * information via {@link #setSubscriptionPlans(int, List)}. + *

+ * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription + * the user is interested in. + *

+ * Receivers should protect themselves by checking that the sender holds the + * {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS + = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS"; + + /** + * Broadcast Action: The billing relationship plans between a carrier and a + * specific subscriber has changed. + *

+ * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription + * changed. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) + public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED + = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED"; + + /** + * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and + * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription + * which has changed. + */ + public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; + + /** + * Integer extra to specify SIM slot index. + */ + public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; + + /** + * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102), + * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application. + * + *

The availability and accuracy of the number depends on the carrier. + * The number may be updated by over-the-air update to UICC applications + * from the carrier, or by other means with physical access to the SIM. + */ + public static final int PHONE_NUMBER_SOURCE_UICC = 1; + + /** + * A source of phone number: provided by an app that has carrier privilege. + * + *

The number is intended to be set by a carrier app knowing the correct number + * which is, for example, different from the number in {@link #PHONE_NUMBER_SOURCE_UICC UICC} + * for some reason. + * The number is not available until a carrier app sets one via + * {@link #setCarrierPhoneNumber(int, String)}. + * The app can update the number with the same API should the number change. + */ + public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; + + /** + * A source of phone number: provided by IMS (IP Multimedia Subsystem) implementation. + * When IMS service is registered (as indicated by + * {@link android.telephony.ims.RegistrationManager.RegistrationCallback#onRegistered(int)}) + * the IMS implementation may return P-Associated-Uri SIP headers (RFC 3455). The URIs + * are the user’s public user identities known to the network (see 3GPP TS 24.229 5.4.1.2), + * and the phone number is typically one of them (see “global number” in 3GPP TS 23.003 13.4). + * + *

This source provides the phone number from the last IMS registration. + * IMS registration may happen on every device reboot or other network condition changes. + * The number will be updated should the associated URI change after an IMS registration. + */ + public static final int PHONE_NUMBER_SOURCE_IMS = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"PHONE_NUMBER_SOURCE"}, + value = { + PHONE_NUMBER_SOURCE_UICC, + PHONE_NUMBER_SOURCE_CARRIER, + PHONE_NUMBER_SOURCE_IMS, + }) + public @interface PhoneNumberSource {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SERVICE_CAPABILITY"}, + value = { + SERVICE_CAPABILITY_VOICE, + SERVICE_CAPABILITY_SMS, + SERVICE_CAPABILITY_DATA, + }) + public @interface ServiceCapability { + } + + /** + * Represents a value indicating the voice calling capabilities of a subscription. + * + *

This value identifies whether the subscription supports various voice calling services. + * These services can include circuit-switched (CS) calling, packet-switched (PS) IMS (IP + * Multimedia Subsystem) calling, and over-the-top (OTT) calling options. + * + *

Note: The availability of emergency calling services is not solely dependent on this + * voice capability. Emergency services may be accessible even if the subscription lacks + * standard voice capabilities. However, the device's ability to support emergency calls + * can be influenced by its inherent voice capabilities, as determined by + * {@link TelephonyManager#isDeviceVoiceCapable()}. + * + * @see TelephonyManager#isDeviceVoiceCapable() + */ + @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) + public static final int SERVICE_CAPABILITY_VOICE = 1; + + /** + * Represents a value indicating the SMS capabilities of a subscription. + * + *

This value identifies whether the subscription supports various sms services. + * These services can include circuit-switched (CS) SMS, packet-switched (PS) IMS (IP + * Multimedia Subsystem) SMS, and over-the-top (OTT) SMS options. + * + *

Note: The availability of emergency SMS services is not solely dependent on this + * sms capability. Emergency services may be accessible even if the subscription lacks + * standard sms capabilities. However, the device's ability to support emergency sms + * can be influenced by its inherent sms capabilities, as determined by + * {@link TelephonyManager#isDeviceSmsCapable()}. + * + * @see TelephonyManager#isDeviceSmsCapable() + */ + @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) + public static final int SERVICE_CAPABILITY_SMS = 2; + + /** + * Represents a value indicating the data calling capabilities of a subscription. + */ + @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) + public static final int SERVICE_CAPABILITY_DATA = 3; + + /** + * Maximum value of service capabilities supported so far. + * @hide + */ + public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA; + + /** + * Bitmask for {@code SERVICE_CAPABILITY_VOICE}. + * @hide + */ + public static final int SERVICE_CAPABILITY_VOICE_BITMASK = + serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE); + + /** + * Bitmask for {@code SERVICE_CAPABILITY_SMS}. + * @hide + */ + public static final int SERVICE_CAPABILITY_SMS_BITMASK = + serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS); + + /** + * Bitmask for {@code SERVICE_CAPABILITY_DATA}. + * @hide + */ + public static final int SERVICE_CAPABILITY_DATA_BITMASK = + serviceCapabilityToBitmask(SERVICE_CAPABILITY_DATA); + + private final Context mContext; + + /** + * In order to prevent the overflow of the heap size due to an indiscriminate increase in the + * cache, the heap size of the resource cache is set sufficiently large. + */ + private static final int MAX_RESOURCE_CACHE_ENTRY_COUNT = 1_000; + + /** + * Cache of Resources that has been created in getResourcesForSubId. Key contains package name, + * and Configuration of Resources. If more than the maximum number of resources are stored in + * this cache, the least recently used Resources will be removed to maintain the maximum size. + */ + private static final LruCache, Resources> sResourcesCache = + new LruCache<>(MAX_RESOURCE_CACHE_ENTRY_COUNT); + + + /** + * The profile has not been transferred or converted to an eSIM. + * @hide + */ + @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION) + @SystemApi + public static final int TRANSFER_STATUS_NONE = 0; + + /** + * The existing profile of the old device has been transferred to an eSIM of the new device. + * @hide + */ + @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION) + @SystemApi + public static final int TRANSFER_STATUS_TRANSFERRED_OUT = 1; + + /** + * The existing profile of the same device has been converted to an eSIM of the same device + * @hide + */ + @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION) + @SystemApi + public static final int TRANSFER_STATUS_CONVERTED = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"TRANSFER_STATUS"}, + value = { + TRANSFER_STATUS_NONE, + TRANSFER_STATUS_TRANSFERRED_OUT, + TRANSFER_STATUS_CONVERTED, + }) + public @interface TransferStatus {} + + + /** + * A listener class for monitoring changes to {@link SubscriptionInfo} records. + *

+ * Override the onSubscriptionsChanged method in the object that extends this + * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)} + * to register your listener and to unregister invoke + * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)} + *

+ * Permissions android.Manifest.permission.READ_PHONE_STATE is required + * for #onSubscriptionsChanged to be invoked. + */ + public static class OnSubscriptionsChangedListener { + + /** + * After {@link Build.VERSION_CODES#Q}, it is no longer necessary to instantiate a + * Handler inside of the OnSubscriptionsChangedListener in all cases, so it will only + * be done for callers that do not supply an Executor. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long LAZY_INITIALIZE_SUBSCRIPTIONS_CHANGED_HANDLER = 278814050L; + + /** + * For backwards compatibility reasons, stashes the Looper associated with the thread + * context in which this listener was created. + */ + private final Looper mCreatorLooper; + + /** + * @hide + */ + public Looper getCreatorLooper() { + return mCreatorLooper; + } + + /** + * Create an OnSubscriptionsChangedListener. + * + * For callers targeting {@link Build.VERSION_CODES#P} or earlier, this can only be called + * on a thread that already has a prepared Looper. Callers targeting Q or later should + * subsequently use {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * Executor, OnSubscriptionsChangedListener)}. + * + * On OS versions prior to {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} callers should + * assume that this call will fail if invoked on a thread that does not already have a + * prepared looper. + */ + public OnSubscriptionsChangedListener() { + mCreatorLooper = Looper.myLooper(); + if (mCreatorLooper == null + && !Compatibility.isChangeEnabled( + LAZY_INITIALIZE_SUBSCRIPTIONS_CHANGED_HANDLER)) { + // matches the implementation of Handler + throw new RuntimeException( + "Can't create handler inside thread " + + Thread.currentThread() + + " that has not called Looper.prepare()"); + } + } + + /** + * Allow a listener to be created with a custom looper + * @param looper the non-null Looper that the underlining handler should run on + * @hide + */ + public OnSubscriptionsChangedListener(@NonNull Looper looper) { + Objects.requireNonNull(looper); + mCreatorLooper = looper; + } + + /** + * Callback invoked when there is any change to any SubscriptionInfo, as well as once on + * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically + * this method would invoke {@link #getActiveSubscriptionInfoList} + */ + public void onSubscriptionsChanged() { + if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN"); + } + + /** + * Callback invoked when {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * Executor, OnSubscriptionsChangedListener)} or + * {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * OnSubscriptionsChangedListener)} fails to complete due to the + * {@link Context#TELEPHONY_REGISTRY_SERVICE} being unavailable. + * @hide + */ + public void onAddListenerFailed() { + Rlog.w(LOG_TAG, "onAddListenerFailed not overridden"); + } + + private void log(String s) { + Rlog.d(LOG_TAG, s); + } + } + + /** + * {@code true} if the SubscriptionManager instance should see all subscriptions regardless its + * association with particular user profile. + * + *

It only applies to Android SDK 35(V) and above. For Android SDK 34(U) and below, the + * caller can see all subscription across user profiles as it does today today even if it's + * {@code false}. + */ + private final boolean mIsForAllUserProfiles; + + /** @hide */ + @UnsupportedAppUsage + public SubscriptionManager(Context context) { + this(context, false /*isForAllUserProfiles*/); + } + + /** Constructor */ + private SubscriptionManager(Context context, boolean isForAllUserProfiles) { + if (DBG) { + logd("SubscriptionManager created " + + (isForAllUserProfiles ? "for all user profile" : "")); + } + mIsForAllUserProfiles = isForAllUserProfiles; + mContext = context; + } + + private NetworkPolicyManager getNetworkPolicyManager() { + return (NetworkPolicyManager) mContext + .getSystemService(Context.NETWORK_POLICY_SERVICE); + } + + /** + * @deprecated developers should always obtain references directly from + * {@link Context#getSystemService(Class)}. + */ + @Deprecated + public static SubscriptionManager from(Context context) { + return (SubscriptionManager) context + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + } + + /** + * Register for changes to the list of active {@link SubscriptionInfo} records or to the + * individual records themselves. When a change occurs the onSubscriptionsChanged method of + * the listener will be invoked immediately if there has been a notification. The + * onSubscriptionChanged method will also be triggered once initially when calling this + * function. The callback will be invoked on the looper specified in the listener's constructor. + * + * @param listener an instance of {@link OnSubscriptionsChangedListener} with + * onSubscriptionsChanged overridden. + * + * @deprecated Will get exception if the parameter listener is not initialized with a Looper. + * Use {@link #addOnSubscriptionsChangedListener(Executor, OnSubscriptionsChangedListener)}. + */ + @Deprecated + public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { + if (listener == null) return; + + Looper looper = listener.getCreatorLooper(); + if (looper == null) { + throw new RuntimeException( + "Can't create handler inside thread " + Thread.currentThread() + + " that has not called Looper.prepare()"); + } + + addOnSubscriptionsChangedListener(new HandlerExecutor(new Handler(looper)), listener); + } + + /** + * Register for changes to the list of {@link SubscriptionInfo} records or to the + * individual records (active or inactive) themselves. When a change occurs, the + * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} method of + * the listener will be invoked immediately. The + * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} method will also be invoked + * once initially when calling this method. + * + * @param listener an instance of {@link OnSubscriptionsChangedListener} with + * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} overridden. + * @param executor the executor that will execute callbacks. + */ + public void addOnSubscriptionsChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnSubscriptionsChangedListener listener) { + String pkgName = mContext != null ? mContext.getOpPackageName() : ""; + if (DBG) { + logd("register OnSubscriptionsChangedListener pkgName=" + pkgName + + " listener=" + listener); + } + // We use the TelephonyRegistry as it runs in the system and thus is always + // available. + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.addOnSubscriptionsChangedListener(listener, + executor); + } else { + // If the telephony registry isn't available, we will inform the caller on their + // listener that it failed so they can try to re-register. + loge("addOnSubscriptionsChangedListener: pkgname=" + pkgName + " failed to be added " + + " due to TELEPHONY_REGISTRY_SERVICE being unavailable."); + executor.execute(() -> listener.onAddListenerFailed()); + } + } + + /** + * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary + * as the listener will automatically be unregistered if an attempt to invoke the listener + * fails. + * + * @param listener that is to be unregistered. + */ + public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { + if (listener == null) return; + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + if (DBG) { + logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug + + " listener=" + listener); + } + // We use the TelephonyRegistry as it runs in the system and thus is always + // available. + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.removeOnSubscriptionsChangedListener(listener); + } + } + + /** + * A listener class for monitoring changes to {@link SubscriptionInfo} records of opportunistic + * subscriptions. + *

+ * Override the onOpportunisticSubscriptionsChanged method in the object that extends this + * or {@link #addOnOpportunisticSubscriptionsChangedListener( + * Executor, OnOpportunisticSubscriptionsChangedListener)} + * to register your listener and to unregister invoke + * {@link #removeOnOpportunisticSubscriptionsChangedListener( + * OnOpportunisticSubscriptionsChangedListener)} + *

+ * Permissions android.Manifest.permission.READ_PHONE_STATE is required + * for #onOpportunisticSubscriptionsChanged to be invoked. + */ + public static class OnOpportunisticSubscriptionsChangedListener { + /** + * Callback invoked when there is any change to any SubscriptionInfo. Typically + * this method would invoke {@link #getActiveSubscriptionInfoList} + */ + public void onOpportunisticSubscriptionsChanged() { + if (DBG) log("onOpportunisticSubscriptionsChanged: NOT OVERRIDDEN"); + } + + private void log(String s) { + Rlog.d(LOG_TAG, s); + } + } + + /** + * Register for changes to the list of opportunistic subscription records or to the + * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged + * method of the listener will be invoked immediately if there has been a notification. + * + * @param listener an instance of {@link OnOpportunisticSubscriptionsChangedListener} with + * onOpportunisticSubscriptionsChanged overridden. + */ + public void addOnOpportunisticSubscriptionsChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnOpportunisticSubscriptionsChangedListener listener) { + if (executor == null || listener == null) { + return; + } + + String pkgName = mContext != null ? mContext.getOpPackageName() : ""; + if (DBG) { + logd("register addOnOpportunisticSubscriptionsChangedListener pkgName=" + pkgName + + " listener=" + listener); + } + + // We use the TelephonyRegistry as it runs in the system and thus is always + // available. + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.addOnOpportunisticSubscriptionsChangedListener( + listener, executor); + } + } + + /** + * Unregister the {@link OnOpportunisticSubscriptionsChangedListener} that is currently + * listening opportunistic subscriptions change. This is not strictly necessary + * as the listener will automatically be unregistered if an attempt to invoke the listener + * fails. + * + * @param listener that is to be unregistered. + */ + public void removeOnOpportunisticSubscriptionsChangedListener( + @NonNull OnOpportunisticSubscriptionsChangedListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + if (DBG) { + logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug=" + + pkgForDebug + " listener=" + listener); + } + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.removeOnOpportunisticSubscriptionsChangedListener(listener); + } + } + + /** + * Get the active SubscriptionInfo with the input subId. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @param subId The unique SubscriptionInfo key in database. + * @return SubscriptionInfo, maybe null if its not active. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public SubscriptionInfo getActiveSubscriptionInfo(int subId) { + if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId); + if (!isValidSubscriptionId(subId)) { + if (DBG) { + logd("[getActiveSubscriptionInfo]- invalid subId"); + } + return null; + } + + SubscriptionInfo subInfo = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + // ignore it + } + + //ccynice for sim + List list = getFakeSubscriptionInfoList(); + if(list != null && !list.isEmpty()) + return list.get(0); + + return subInfo; + } + + /** + * Gets an active SubscriptionInfo {@link SubscriptionInfo} associated with the Sim IccId. + * + * @param iccId the IccId of SIM card + * @return SubscriptionInfo, maybe null if its not active + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @Nullable + @SystemApi + public SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String iccId) { + if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId); + if (iccId == null) { + logd("[getActiveSubscriptionInfoForIccIndex]- null iccid"); + return null; + } + + SubscriptionInfo result = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + // ignore it + } + + //ccynice for sim + List list = getFakeSubscriptionInfoList(); + if(list != null && !list.isEmpty()) + return list.get(0); + + return result; + } + + /** + * Get the active SubscriptionInfo associated with the slotIndex + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @param slotIndex the slot which the subscription is inserted + * @return SubscriptionInfo, maybe null if its not active + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) { + if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex); + if (!isValidSlotIndex(slotIndex)) { + logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIndex"); + return null; + } + + SubscriptionInfo result = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + // ignore it + } + //ccynice for sim + List list = getFakeSubscriptionInfoList(); + if(list != null && !list.isEmpty()) + return list.get(0); + + return result; + } + + /** + * Get all subscription info records from SIMs that are inserted now or previously inserted. + * + *

+ * If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission, + * {@link SubscriptionInfo#getNumber()} will return empty string. + * If the caller does not have {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER}, + * {@link SubscriptionInfo#getIccId()} will return an empty string, and + * {@link SubscriptionInfo#getGroupUuid()} will return {@code null}. + * + *

+ * The carrier app will only get the list of subscriptions that it has carrier privilege on, + * but will have non-stripped {@link SubscriptionInfo} in the list. + * + * @return List of all {@link SubscriptionInfo} records from SIMs that are inserted or + * previously inserted. Sorted by {@link SubscriptionInfo#getSimSlotIndex()}, then + * {@link SubscriptionInfo#getSubscriptionId()}. + * + * @throws SecurityException if callers do not hold the required permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @NonNull + @RequiresPermission(anyOf = { + Manifest.permission.READ_PHONE_STATE, + "carrier privileges", + }) + public List getAllSubscriptionInfoList() { + List result = null; + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getAllSubInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + // ignore it + } + + if (result == null) { + result = Collections.emptyList(); + } + +//ccynice + if(result==null||result.size()<1) + result = getFakeSubscriptionInfoList(); + + + return result; + } + + /** + * Get the SubscriptionInfo(s) of the currently active SIM(s) associated with the current caller + * user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U) + * and below can see all subscriptions as it does today. + * + *

For example, if we have + *

    + *
  • Subscription 1 associated with personal profile. + *
  • Subscription 2 associated with work profile. + *
+ * Then for SDK 35+, if the caller identity is personal profile, then this will return + * subscription 1 only and vice versa. + * + *

Returned records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by + * {@link SubscriptionInfo#getSubscriptionId}. Beginning with Android SDK 35, this method will + * never return null. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @return a list of the active {@link SubscriptionInfo} that is visible to the caller. If + * an empty list or null is returned, then there are no active subscriptions that + * are visible to the caller. If the number of active subscriptions available to + * any caller changes, then this change will be indicated by + * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public @Nullable List getActiveSubscriptionInfoList() { + //ccynice + return getActiveSubscriptionInfoList(/* userVisibleonly */true); + + /*List activeList = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag(), mIsForAllUserProfiles); + } + } catch (RemoteException ex) { + // ignore it + } + + if (activeList != null) { + activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo)) + .collect(Collectors.toList()); + } else { + activeList = Collections.emptyList(); + } + return activeList;*/ + } + + /** + * Get both hidden and visible SubscriptionInfo(s) of the currently active SIM(s). + * The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} + * then by {@link SubscriptionInfo#getSubscriptionId}. + * + * Hidden subscriptions refer to those are not meant visible to the users. + * For example, an opportunistic subscription that is grouped with other + * subscriptions should remain invisible to users as they are only functionally + * supplementary to primary ones. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible + * to the calling app are returned. + * + * @return Sorted list of the currently available {@link SubscriptionInfo} + * records on the device. + * This is similar to {@link #getActiveSubscriptionInfoList} except that it will return + * both active and hidden SubscriptionInfos. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + public @NonNull List getCompleteActiveSubscriptionInfoList() { + return getActiveSubscriptionInfoList(/* userVisibleonly */ false); + } + + /** + * Create a new subscription manager instance that can see all subscriptions across + * user profiles. + * + * The permission check for accessing all subscriptions will be enforced upon calling the + * individual APIs linked below. + * + * @return a SubscriptionManager that can see all subscriptions regardless its user profile + * association. + * + * @see #getActiveSubscriptionInfoList + * @see #getActiveSubscriptionInfoCount + * @see UserHandle + */ + @FlaggedApi(Flags.FLAG_ENFORCE_SUBSCRIPTION_USER_FILTER) + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) + @NonNull public SubscriptionManager createForAllUserProfiles() { + return new SubscriptionManager(mContext, true/*isForAllUserProfiles*/); + } + + /** + * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly + * is true, it will filter out the hidden subscriptions. + * + * @hide + */ + public @NonNull List getActiveSubscriptionInfoList(boolean userVisibleOnly) { + List activeList = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag(), true /*isForAllUserProfiles*/); + } + } catch (RemoteException ex) { + // ignore it + } + +//ccynice + if(activeList==null||activeList.size()<1) + activeList = getFakeSubscriptionInfoList(); + if (activeList == null || activeList.isEmpty()) { + return Collections.emptyList(); + } else if (userVisibleOnly) { + return activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo)) + .collect(Collectors.toList()); + } else { + return activeList; + } + } + + /** + * Gets the SubscriptionInfo(s) of all available subscriptions, if any. + * + *

Available subscriptions include active ones (those with a non-negative + * {@link SubscriptionInfo#getSimSlotIndex()}) as well as inactive but installed embedded + * subscriptions. + * + *

The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by + * {@link SubscriptionInfo#getSubscriptionId}. + * + * @return Sorted list of the current {@link SubscriptionInfo} records available on the + * device. + *

    + *
  • + * If null is returned the current state is unknown but if a + * {@link OnSubscriptionsChangedListener} has been registered + * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future. + *
  • + * If the list is empty then there are no {@link SubscriptionInfo} records currently available. + *
  • + * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex} + * then by {@link SubscriptionInfo#getSubscriptionId}. + *
+ * + *

+ * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required + * for #getAvailableSubscriptionInfoList to be invoked. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + public @Nullable List getAvailableSubscriptionInfoList() { + List result = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + // ignore it + } + //ccynice + if(result==null||result.size()<1) + result = getFakeSubscriptionInfoList(); + return (result == null) ? Collections.emptyList() : result; + } + + /** + * Gets the SubscriptionInfo(s) of all embedded subscriptions accessible to the calling app, if + * any. + * + *

Only those subscriptions for which the calling app has carrier privileges per the + * subscription metadata, if any, will be included in the returned list. + * + *

The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by + * {@link SubscriptionInfo#getSubscriptionId}. + * + * @return Sorted list of the current embedded {@link SubscriptionInfo} records available on the + * device which are accessible to the caller. + *

    + *
  • + * If null is returned the current state is unknown but if a + * {@link OnSubscriptionsChangedListener} has been registered + * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future. + *
  • + * If the list is empty then there are no {@link SubscriptionInfo} records currently available. + *
  • + * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex} + * then by {@link SubscriptionInfo#getSubscriptionId}. + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. + */ + public @Nullable List getAccessibleSubscriptionInfoList() { + List result = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName()); + } + } catch (RemoteException ex) { + // ignore it + } + + //ccynice + if(result==null||result.size()<1) + result = getFakeSubscriptionInfoList(); + return (result == null) ? Collections.emptyList() : result; + } + + /** + * Request a refresh of the platform cache of profile information for the eUICC which + * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}. + * + *

Should be called by the EuiccService implementation whenever this information changes due + * to an operation done outside the scope of a request initiated by the platform to the + * EuiccService. There is no need to refresh for downloads, deletes, or other operations that + * were made through the EuiccService. + * + *

Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * + * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. + * @hide + */ + @SystemApi + public void requestEmbeddedSubscriptionInfoListRefresh() { + int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc(); + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); + } + } catch (RemoteException ex) { + logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed."); + } + } + + /** + * Request a refresh of the platform cache of profile information for the eUICC with the given + * {@code cardId}. + * + *

Should be called by the EuiccService implementation whenever this information changes due + * to an operation done outside the scope of a request initiated by the platform to the + * EuiccService. There is no need to refresh for downloads, deletes, or other operations that + * were made through the EuiccService. + * + *

Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. + * + * @param cardId the card ID of the eUICC. + * + * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. + * @hide + */ + @SystemApi + public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId); + } + } catch (RemoteException ex) { + logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed."); + } + } + + /** + * Get the active subscription count associated with the current caller user profile for + * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as + * it does today. + * + * @return The current number of active subscriptions. + * + * @see #getActiveSubscriptionInfoList() + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public int getActiveSubscriptionInfoCount() { + int result = 0; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), + mContext.getAttributionTag(), mIsForAllUserProfiles); + } + } catch (RemoteException ex) { + // ignore it + } + + return result; + } + + /** + * @return the maximum number of active subscriptions that will be returned by + * {@link #getActiveSubscriptionInfoList} and the value returned by + * {@link #getActiveSubscriptionInfoCount}. + */ + public int getActiveSubscriptionInfoCountMax() { + int result = 0; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getActiveSubInfoCountMax(); + } + } catch (RemoteException ex) { + // ignore it + } + + return result; + } + + /** + * Add a new SubscriptionInfo to SubscriptionInfo database if needed + * @param iccId the IccId of the SIM card + * @param slotIndex the slot which the SIM is inserted + * @return the URL of the newly created row or the updated row + * @hide + */ + public Uri addSubscriptionInfoRecord(String iccId, int slotIndex) { + if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotIndex:" + slotIndex); + if (iccId == null) { + logd("[addSubscriptionInfoRecord]- null iccId"); + } + if (!isValidSlotIndex(slotIndex)) { + logd("[addSubscriptionInfoRecord]- invalid slotIndex"); + } + + addSubscriptionInfoRecord(iccId, null, slotIndex, SUBSCRIPTION_TYPE_LOCAL_SIM); + + // FIXME: Always returns null? + return null; + + } + + /** + * Add a new SubscriptionInfo to SubscriptionInfo database if needed + * @param uniqueId This is the unique identifier for the subscription within the + * specific subscription type. + * @param displayName human-readable name of the device the subscription corresponds to. + * @param slotIndex the slot assigned to this subscription. It is ignored for subscriptionType + * of {@link #SUBSCRIPTION_TYPE_REMOTE_SIM}. + * @param subscriptionType the {@link #SUBSCRIPTION_TYPE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void addSubscriptionInfoRecord(@NonNull String uniqueId, @Nullable String displayName, + int slotIndex, @SubscriptionType int subscriptionType) { + if (VDBG) { + logd("[addSubscriptionInfoRecord]+ uniqueId:" + uniqueId + + ", displayName:" + displayName + ", slotIndex:" + slotIndex + + ", subscriptionType: " + subscriptionType); + } + if (uniqueId == null) { + Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- uniqueId is null"); + return; + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub == null) { + Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null"); + return; + } + int result = iSub.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType); + if (result < 0) { + Log.e(LOG_TAG, "Adding of subscription didn't succeed: error = " + result); + } else { + logd("successfully added new subscription"); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** + * Remove subscription info record from the subscription database. + * + * @param uniqueId This is the unique identifier for the subscription within the specific + * subscription type. + * @param subscriptionType the type of subscription to be removed. + * + * @throws NullPointerException if {@code uniqueId} is {@code null}. + * @throws SecurityException if callers do not hold the required permission. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void removeSubscriptionInfoRecord(@NonNull String uniqueId, + @SubscriptionType int subscriptionType) { + if (VDBG) { + logd("[removeSubscriptionInfoRecord]+ uniqueId:" + uniqueId + + ", subscriptionType: " + subscriptionType); + } + if (uniqueId == null) { + Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- uniqueId is null"); + return; + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub == null) { + Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null"); + return; + } + boolean result = iSub.removeSubInfo(uniqueId, subscriptionType); + if (!result) { + Log.e(LOG_TAG, "Removal of subscription didn't succeed"); + } else { + logd("successfully removed subscription"); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** + * Set SIM icon tint color for subscription ID + * @param tint the RGB value of icon tint color of the SIM + * @param subId the unique subscription ID in database + * @return the number of records updated + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public int setIconTint(@ColorInt int tint, int subId) { + if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId); + return setSubscriptionPropertyHelper(subId, "setIconTint", + (iSub)-> iSub.setIconTint(subId, tint) + ); + } + + /** + * Set the display name for a subscription ID + * @param displayName the display name of SIM card + * @param subId the unique Subscritpion ID in database + * @param nameSource SIM display name source + * @return the number of records updated or < 0 if invalid subId + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public int setDisplayName(@Nullable String displayName, int subId, + @SimDisplayNameSource int nameSource) { + if (VDBG) { + logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId + + " nameSource:" + nameSource); + } + return setSubscriptionPropertyHelper(subId, "setDisplayName", + (iSub)-> iSub.setDisplayNameUsingSrc(displayName, subId, nameSource) + ); + } + + /** + * Set phone number by subId + * @param number the phone number of the SIM + * @param subId the unique SubscriptionInfo index in database + * @return the number of records updated + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public int setDisplayNumber(String number, int subId) { + if (number == null) { + logd("[setDisplayNumber]- fail"); + return -1; + } + return setSubscriptionPropertyHelper(subId, "setDisplayNumber", + (iSub)-> iSub.setDisplayNumber(number, subId) + ); + } + + /** + * Set data roaming by simInfo index + * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming + * @param subId the unique SubscriptionInfo index in database + * @return the number of records updated + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public int setDataRoaming(int roaming, int subId) { + if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId); + return setSubscriptionPropertyHelper(subId, "setDataRoaming", + (iSub)->iSub.setDataRoaming(roaming, subId) + ); + } + + /** + * Get slotIndex associated with the subscription. + * + * @param subscriptionId the unique SubscriptionInfo index in database + * @return slotIndex as a positive integer or {@link #INVALID_SIM_SLOT_INDEX} if the supplied + * subscriptionId doesn't have an associated slot index. + */ + public static int getSlotIndex(int subscriptionId) { + return sGetSlotIndexCache.query(subscriptionId); + } + + /** + * Get an array of subscription ids for the specified logical SIM slot Index. The maximum size + * of the array is 1. This API was mistakenly designed to return multiple subscription ids, + * which is not possible in the current Android telephony architecture. + * + * @param slotIndex The logical SIM slot index. + * + * @return Subscription id of the active subscription on the specified logical SIM slot index. + * If SIM is absent on the slot, a single element array of {@link #INVALID_SUBSCRIPTION_ID} will + * be returned. {@code null} if the provided {@code slotIndex} is not valid. + * + * @deprecated Use {@link #getSubscriptionId(int)} instead. + */ + @Deprecated + @Nullable + public int[] getSubscriptionIds(int slotIndex) { + if (!isValidSlotIndex(slotIndex)) { + return null; + } + return new int[]{getSubscriptionId(slotIndex)}; + } + + /** + * Get an array of subscription ids for the specified logical SIM slot Index. The maximum size + * of the array is 1. This API was mistakenly designed to return multiple subscription ids, + * which is not possible in the current Android telephony architecture. + * + * @param slotIndex The logical SIM slot index. + * + * @return Subscription id of the active subscription on the specified logical SIM slot index. + * If SIM is absent on the slot, a single element array of {@link #INVALID_SUBSCRIPTION_ID} will + * be returned. {@code null} if the provided {@code slotIndex} is not valid. + * + * @deprecated Use {@link #getSubscriptionId(int)} instead. + * @hide + */ + @Deprecated + public static int[] getSubId(int slotIndex) { + if (!isValidSlotIndex(slotIndex)) { + return null; + } + return new int[]{getSubscriptionId(slotIndex)}; + } + + /** + * Get the subscription id for specified logical SIM slot index. + * + * @param slotIndex The logical SIM slot index. + * @return The subscription id. {@link #INVALID_SUBSCRIPTION_ID} if SIM is absent. + */ + public static int getSubscriptionId(int slotIndex) { + if (!isValidSlotIndex(slotIndex)) { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + + return sGetSubIdCache.query(slotIndex); + } + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static int getPhoneId(int subId) { + return sGetPhoneIdCache.query(subId); + } + + private static void logd(String msg) { + Rlog.d(LOG_TAG, msg); + } + + private static void loge(String msg) { + Rlog.e(LOG_TAG, msg); + } + + /** + * Returns the system's default subscription id. + * + * For a voice capable device, it will return getDefaultVoiceSubscriptionId. + * For a data only device, it will return the getDefaultDataSubscriptionId. + * May return an INVALID_SUBSCRIPTION_ID on error. + * + * @return the "system" default subscription id. + */ + public static int getDefaultSubscriptionId() { + return sGetDefaultSubIdCacheAsUser.query(Process.myUserHandle().getIdentifier()); + } + + /** + * Returns the system's default voice subscription id. + * + * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID. + * + * @return the default voice subscription Id. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + public static int getDefaultVoiceSubscriptionId() { + int subId = INVALID_SUBSCRIPTION_ID; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + subId = iSub.getDefaultVoiceSubIdAsUser(Process.myUserHandle().getIdentifier()); + } + } catch (RemoteException ex) { + // ignore it + } + + if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId); + + //ccynice for sim keep fake sim id alike + return SystemProperties.getInt("sim.id", subId); + //return subId; + } + + /** + * Sets the system's default voice subscription id. + * + * On a data-only device, this is a no-op. + * + * May throw a {@link RuntimeException} if the provided subscription id is equal to + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} + * + * @param subscriptionId A valid subscription ID to set as the system default, or + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDefaultVoiceSubscriptionId(int subscriptionId) { + if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId); + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setDefaultVoiceSubId(subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** + * Same as {@link #setDefaultVoiceSubscriptionId(int)}, but preserved for backwards + * compatibility. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + public void setDefaultVoiceSubId(int subId) { + setDefaultVoiceSubscriptionId(subId); + } + + /** + * Return the SubscriptionInfo for default voice subscription. + * + * Will return null on data only devices, or on error. + * + * @return the SubscriptionInfo for the default voice subscription. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public SubscriptionInfo getDefaultVoiceSubscriptionInfo() { + return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId()); + } + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static int getDefaultVoicePhoneId() { + return getPhoneId(getDefaultVoiceSubscriptionId()); + } + + /** + * Returns the system's default SMS subscription id. + * + * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID. + * + * @return the default SMS subscription Id. + */ + public static int getDefaultSmsSubscriptionId() { + //ccynice for sim keep fake sim id alike + return SystemProperties.getInt("sim.id", -1); + //return sGetDefaultSmsSubIdCacheAsUser.query(Process.myUserHandle().getIdentifier()); + } + + /** + * Set the subscription which will be used by default for SMS, with the subscription which + * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied + * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}). + * + * @param subscriptionId the supplied subscription ID + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setDefaultSmsSubId(int subscriptionId) { + if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId); + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setDefaultSmsSubId(subscriptionId); + } + } catch (RemoteException ex) { + ex.rethrowFromSystemServer(); + } + } + + /** + * Returns the system's default data subscription id. + * + * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID. + * + * @return the default data subscription Id. + */ + public static int getDefaultDataSubscriptionId() { + return sGetDefaultDataSubIdCache.query(null); + } + + /** + * Set the subscription which will be used by default for data, with the subscription which + * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied + * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}). + * + * @param subscriptionId the supplied subscription ID + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setDefaultDataSubId(int subscriptionId) { + if (VDBG) logd("setDataSubscription sub id = " + subscriptionId); + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setDefaultDataSubId(subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** + * Return the SubscriptionInfo for default data subscription. + * + * Will return null on voice only devices, or on error. + * + * @return the SubscriptionInfo for the default data subscription. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @UnsupportedAppUsage + public SubscriptionInfo getDefaultDataSubscriptionInfo() { + return getActiveSubscriptionInfo(getDefaultDataSubscriptionId()); + } + + /** + * Check if the supplied subscription ID is valid. + * + *

A valid subscription ID is not necessarily an active subscription ID + * (see {@link #isActiveSubscriptionId(int)}) or an usable subscription ID + * (see {@link #isUsableSubscriptionId(int)}). Unless specifically noted, subscription + * APIs work with a valid subscription ID. + * + * @param subscriptionId The subscription ID. + * @return {@code true} if the supplied subscriptionId is valid; {@code false} otherwise. + */ + public static boolean isValidSubscriptionId(int subscriptionId) { + return subscriptionId > INVALID_SUBSCRIPTION_ID; + } + + /** + * Check if the supplied subscription ID is usable. + * + *

A usable subscription ID is a valid subscription ID, but not necessarily an active + * subscription ID (see {@link #isActiveSubscriptionId(int)}). Some subscription APIs + * require a usable subscription ID, and this is noted in their documentation; otherwise, a + * subscription ID does not need to be usable for subscription functions, only valid. + * + * @param subscriptionId the subscription ID + * @return {@code true} if the subscription ID is usable; {@code false} otherwise. + */ + public static boolean isUsableSubscriptionId(int subscriptionId) { + return isUsableSubIdValue(subscriptionId); + } + + /** + * @return true if subId is an usable subId value else false. A + * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static boolean isUsableSubIdValue(int subId) { + return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE; + } + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public static boolean isValidSlotIndex(int slotIndex) { + return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getActiveModemCount(); + } + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static boolean isValidPhoneId(int phoneId) { + return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount(); + } + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) { + int subId = SubscriptionManager.getSubscriptionId(phoneId); + if (isValidSubscriptionId(subId)) { + putPhoneIdAndSubIdExtra(intent, phoneId, subId); + } else { + logd("putPhoneIdAndSubIdExtra: no valid subs"); + intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); + intent.putExtra(EXTRA_SLOT_INDEX, phoneId); + } + } + + /** @hide */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) { + if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId); + intent.putExtra(EXTRA_SLOT_INDEX, phoneId); + intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); + putSubscriptionIdExtra(intent, subId); + } + + /** + * Get visible subscription Id(s) of the currently active SIM(s). + * + * @return the list of subId's that are active, + * is never null but the length may be 0. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NonNull int[] getActiveSubscriptionIdList() { + return getActiveSubscriptionIdList(/* visibleOnly */ true); + } + + /** + * Get both hidden and visible subscription Id(s) of the currently active SIM(s). + * + * Hidden subscriptions refer to those are not meant visible to the users. + * For example, an opportunistic subscription that is grouped with other + * subscriptions should remain invisible to users as they are only functionally + * supplementary to primary ones. + * + * @return the list of subId's that are active, + * is never null but the length may be 0. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NonNull int[] getCompleteActiveSubscriptionIdList() { + return getActiveSubscriptionIdList(/* visibleOnly */false); + } + + /** + * @return a non-null list of subId's that are active. + * + * @hide + */ + public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + int[] subId = iSub.getActiveSubIdList(visibleOnly); + if (subId != null) return subId; + } + } catch (RemoteException ex) { + // ignore it + } + + return new int[0]; + } + + /** + * Returns true if the device is considered roaming on the current + * network for a subscription. + *

+ * Availability: Only when user registered to a network. + * + * @param subId The subscription ID + * @return true if the network for the subscription is roaming, false otherwise + */ + public boolean isNetworkRoaming(int subId) { + final int phoneId = getPhoneId(subId); + if (phoneId < 0) { + // What else can we do? + return false; + } + return TelephonyManager.getDefault().isNetworkRoaming(subId); + } + + /** + * Set a field in the subscription database. Note not all fields are supported. + * + * @param subscriptionId Subscription Id of Subscription. + * @param columnName Column name in the database. Note not all fields are supported. + * @param value Value to store in the database. + * + * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not + * exposed. + * @throws SecurityException if callers do not hold the required permission. + * + * @see android.provider.Telephony.SimInfo for all the columns. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public static void setSubscriptionProperty(int subscriptionId, @NonNull String columnName, + @NonNull String value) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setSubscriptionProperty(subscriptionId, columnName, value); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** + * Serialize list of contacts uri to string + * @hide + */ + public static String serializeUriLists(List uris) { + List contacts = new ArrayList<>(); + for (Uri uri : uris) { + contacts.add(uri.toString()); + } + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(contacts); + oos.flush(); + return Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT); + } catch (IOException e) { + logd("serializeUriLists IO exception"); + } + return ""; + } + + /** + * Get specific field in string format from the subscription info database. + * + * @param context The calling context. + * @param subscriptionId Subscription id of the subscription. + * @param columnName Column name in subscription database. + * + * @return Value in string format associated with {@code subscriptionId} and {@code columnName} + * from the database. Empty string if the {@code subscriptionId} is invalid (for backward + * compatible). + * + * @throws IllegalArgumentException if the field is not exposed. + * + * @see android.provider.Telephony.SimInfo for all the columns. + * + * @hide + */ + @NonNull + @RequiresPermission(anyOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "carrier privileges", + }) + private static String getStringSubscriptionProperty(@NonNull Context context, + int subscriptionId, @NonNull String columnName) { + String resultValue = null; + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + resultValue = iSub.getSubscriptionProperty(subscriptionId, columnName, + context.getOpPackageName(), context.getAttributionTag()); + } + } catch (RemoteException ex) { + // ignore it + } + return TextUtils.emptyIfNull(resultValue); + } + + /** + * Get specific field in {@code boolean} format from the subscription info database. + * + * @param subscriptionId Subscription id of the subscription. + * @param columnName Column name in subscription database. + * @param defaultValue Default value in case not found or error. + * @param context The calling context. + * + * @return Value in {@code boolean} format associated with {@code subscriptionId} and + * {@code columnName} from the database, or {@code defaultValue} if not found or error. + * + * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not + * exposed. + * + * @see android.provider.Telephony.SimInfo for all the columns. + * + * @hide + */ + @RequiresPermission(anyOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "carrier privileges", + }) + public static boolean getBooleanSubscriptionProperty(int subscriptionId, + @NonNull String columnName, boolean defaultValue, @NonNull Context context) { + String result = getStringSubscriptionProperty(context, subscriptionId, columnName); + if (!result.isEmpty()) { + try { + return Integer.parseInt(result) == 1; + } catch (NumberFormatException err) { + logd("getBooleanSubscriptionProperty NumberFormat exception"); + } + } + return defaultValue; + } + + /** + * Get specific field in {@code integer} format from the subscription info database. + * + * @param subscriptionId Subscription id of the subscription. + * @param columnName Column name in subscription database. + * @param defaultValue Default value in case not found or error. + * @param context The calling context. + * + * @return Value in {@code integer} format associated with {@code subscriptionId} and + * {@code columnName} from the database, or {@code defaultValue} if not found or error. + * + * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not + * exposed. + * + * @see android.provider.Telephony.SimInfo for all the columns. + * + * @hide + */ + @RequiresPermission(anyOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "carrier privileges", + }) + public static int getIntegerSubscriptionProperty(int subscriptionId, @NonNull String columnName, + int defaultValue, @NonNull Context context) { + String result = getStringSubscriptionProperty(context, subscriptionId, columnName); + if (!result.isEmpty()) { + try { + return Integer.parseInt(result); + } catch (NumberFormatException err) { + logd("getIntegerSubscriptionProperty NumberFormat exception"); + } + } + return defaultValue; + } + + /** + * Get specific field in {@code long} format from the subscription info database. + * + * @param subscriptionId Subscription id of the subscription. + * @param columnName Column name in subscription database. + * @param defaultValue Default value in case not found or error. + * @param context The calling context. + * + * @return Value in {@code long} format associated with {@code subscriptionId} and + * {@code columnName} from the database, or {@code defaultValue} if not found or error. + * + * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not + * exposed. + * + * @see android.provider.Telephony.SimInfo for all the columns. + * + * @hide + */ + @RequiresPermission(anyOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "carrier privileges", + }) + public static long getLongSubscriptionProperty(int subscriptionId, @NonNull String columnName, + long defaultValue, @NonNull Context context) { + String result = getStringSubscriptionProperty(context, subscriptionId, columnName); + if (!result.isEmpty()) { + try { + return Long.parseLong(result); + } catch (NumberFormatException err) { + logd("getLongSubscriptionProperty NumberFormat exception"); + } + } + return defaultValue; + } + + /** + * Returns the {@link Resources} from the given {@link Context} for the MCC/MNC associated with + * the subscription. If the subscription ID is invalid, the base resources are returned instead. + * + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param context Context object + * @param subId Subscription Id of Subscription whose resources are required + * @return Resources associated with Subscription. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @NonNull + @SystemApi + public static Resources getResourcesForSubId(@NonNull Context context, int subId) { + return getResourcesForSubId(context, subId, false); + } + + /** + * Returns the resources associated with Subscription. + * @param context Context object + * @param subId Subscription Id of Subscription who's resources are required + * @param useRootLocale if root locale should be used. Localized locale is used if false. + * @return Resources associated with Subscription. + * @hide + */ + @NonNull + public static Resources getResourcesForSubId(Context context, int subId, + boolean useRootLocale) { + // Check if the Resources already exists in the cache based on the given context. Find a + // Resource that match Configuration. + Pair cacheKey = null; + if (isValidSubscriptionId(subId)) { + Configuration configurationKey = + new Configuration(context.getResources().getConfiguration()); + if (useRootLocale) { + configurationKey.setLocale(Locale.ROOT); + } + cacheKey = Pair.create(context.getPackageName(), configurationKey); + synchronized (sResourcesCache) { + Resources cached = sResourcesCache.get(cacheKey); + if (cached != null) { + // Cache hit. Use cached Resources. + return cached; + } + } + } + + final SubscriptionInfo subInfo = + SubscriptionManager.from(context).getActiveSubscriptionInfo(subId); + + Configuration overrideConfig = new Configuration(); + if (subInfo != null) { + overrideConfig.mcc = subInfo.getMcc(); + overrideConfig.mnc = subInfo.getMnc(); + if (overrideConfig.mnc == 0) { + overrideConfig.mnc = Configuration.MNC_ZERO; + cacheKey = null; + } + } else { + cacheKey = null; + } + + if (useRootLocale) { + overrideConfig.setLocale(Locale.ROOT); + } + + // Create new context with new configuration so that we can avoid modifying the passed in + // context. + // Note that if the original context configuration changes, the resources here will also + // change for all values except those overridden by newConfig (e.g. if the device has an + // orientation change). + Context newContext = context.createConfigurationContext(overrideConfig); + Resources res = newContext.getResources(); + + if (cacheKey != null) { + synchronized (sResourcesCache) { + // Save the newly created Resources in the resource cache. + sResourcesCache.put(cacheKey, res); + } + } + return res; + } + + /** + * Checks if the supplied subscription ID corresponds to a subscription which is actively in + * use on the device. An active subscription ID is a valid and usable subscription ID. + * + * @param subscriptionId the subscription ID. + * @return {@code true} if the supplied subscription ID corresponds to an active subscription; + * {@code false} if it does not correspond to an active subscription; or throw a + * SecurityException if the caller hasn't got the right permission. + *i + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public boolean isActiveSubscriptionId(int subscriptionId) { + return isActiveSubId(subscriptionId); + } + + /** + * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription + * and the SIM providing the subscription is present in a slot and in "LOADED" state. + * @hide + */ + @UnsupportedAppUsage + public boolean isActiveSubId(int subId) { + //ccynice for sim + boolean state = SystemProperties.getBoolean("sim.state", true); + return state; + /* + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.isActiveSubId(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + } + return false;*/ + } + + /** + * Get the description of the billing relationship plan between a carrier + * and a specific subscriber. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this relationship applies to + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + */ + public @NonNull List getSubscriptionPlans(int subId) { + SubscriptionPlan[] subscriptionPlans = + getNetworkPolicyManager().getSubscriptionPlans(subId, mContext.getOpPackageName()); + return subscriptionPlans == null + ? Collections.emptyList() : Arrays.asList(subscriptionPlans); + } + + /** + * Set the description of the billing relationship plan between a carrier + * and a specific subscriber. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this relationship applies to. An empty list + * may be sent to clear any existing plans. + * @param plans the list of plans. The first plan is always the primary and + * most important plan. Any additional plans are secondary and + * may not be displayed or used by decision making logic. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + * @throws IllegalArgumentException if plans don't meet the requirements + * defined in {@link SubscriptionPlan}. + * @deprecated use {@link #setSubscriptionPlans(int, List, long)} instead. + */ + @Deprecated + public void setSubscriptionPlans(int subId, @NonNull List plans) { + setSubscriptionPlans(subId, plans, 0); + } + + /** + * Set the description of the billing relationship plan between a carrier + * and a specific subscriber. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this relationship applies to. An empty list + * may be sent to clear any existing plans. + * @param plans the list of plans. The first plan is always the primary and + * most important plan. Any additional plans are secondary and + * may not be displayed or used by decision making logic. + * @param expirationDurationMillis the duration after which the subscription plans + * will be automatically cleared, or {@code 0} to leave the plans until + * explicitly cleared, or the next reboot, whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + * @throws IllegalArgumentException if plans don't meet the requirements + * defined in {@link SubscriptionPlan}. + */ + public void setSubscriptionPlans(int subId, @NonNull List plans, + @DurationMillisLong long expirationDurationMillis) { + getNetworkPolicyManager().setSubscriptionPlans(subId, + plans.toArray(new SubscriptionPlan[0]), expirationDurationMillis, + mContext.getOpPackageName()); + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered unmetered. This will be reflected + * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this override applies to. + * @param overrideUnmetered set if the billing relationship should be + * considered unmetered. + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + */ + public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, + @DurationMillisLong long expirationDurationMillis) { + setSubscriptionOverrideUnmetered(subId, overrideUnmetered, + TelephonyManager.getAllNetworkTypes(), expirationDurationMillis); + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered unmetered. This will be reflected + * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this override applies to. + * @param overrideUnmetered set if the billing relationship should be + * considered unmetered. + * @param networkTypes the network types this override applies to. If no + * network types are specified, override values will be ignored. + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + */ + public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, + @NonNull @Annotation.NetworkType int[] networkTypes, + @DurationMillisLong long expirationDurationMillis) { + final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0; + getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED, + overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName()); + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered congested. This will cause the + * device to delay certain network requests when possible, such as developer + * jobs that are willing to run in a flexible time window. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this override applies to. + * @param overrideCongested set if the subscription should be considered + * congested. + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + */ + public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, + @DurationMillisLong long expirationDurationMillis) { + setSubscriptionOverrideCongested(subId, overrideCongested, + TelephonyManager.getAllNetworkTypes(), expirationDurationMillis); + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered congested. This will cause the + * device to delay certain network requests when possible, such as developer + * jobs that are willing to run in a flexible time window. + *

+ * This method is only accessible to the following narrow set of apps: + *

    + *
  • The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + *
  • The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + *
+ * + * @param subId the subscriber this override applies to. + * @param overrideCongested set if the subscription should be considered congested. + * @param networkTypes the network types this override applies to. If no network types are + * specified, override values will be ignored. + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the requested state until explicitly + * cleared, or the next reboot, whichever happens first. + * + * @throws SecurityException if the caller doesn't meet the requirements outlined above. + */ + public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, + @NonNull @Annotation.NetworkType int[] networkTypes, + @DurationMillisLong long expirationDurationMillis) { + final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0; + getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED, + overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName()); + } + + /** + * Checks whether the app with the given context is authorized to manage the given subscription + * according to its metadata. + * + * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns + * true). To check for permissions for non-embedded subscription as well, + * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. + * + * @param info The subscription to check. + * @return whether the app is authorized to manage this subscription per its metadata. + * @see android.telephony.TelephonyManager#hasCarrierPrivileges + */ + public boolean canManageSubscription(SubscriptionInfo info) { + return canManageSubscription(info, mContext.getPackageName()); + } + + /** + * Checks whether the given app is authorized to manage the given subscription. An app can only + * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the + * {@link android.telephony.SubscriptionInfo} with the access status. + * + * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns + * true). To check for permissions for non-embedded subscription as well, + * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. + * + * @param info The subscription to check. + * @param packageName Package name of the app to check. + * + * @return whether the app is authorized to manage this subscription per its access rules. + * @see android.telephony.TelephonyManager#hasCarrierPrivileges + * @hide + */ + @SystemApi + public boolean canManageSubscription(@NonNull SubscriptionInfo info, + @NonNull String packageName) { + if (info == null || info.getAccessRules() == null || packageName == null) { + return false; + } + PackageManager packageManager = mContext.getPackageManager(); + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNING_CERTIFICATES); + } catch (PackageManager.NameNotFoundException e) { + logd("Unknown package: " + packageName); + return false; + } + for (UiccAccessRule rule : info.getAccessRules()) { + if (rule.getCarrierPrivilegeStatus(packageInfo) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + } + return false; + } + + /** + * Set which subscription is preferred for cellular data. + * It's also usually the subscription we set up internet connection on. + * + * PreferredData overwrites user setting of default data subscription. And it's used + * by AlternativeNetworkService or carrier apps to switch primary and CBRS + * subscription dynamically in multi-SIM devices. + * + * @param subId which subscription is preferred to for cellular data. If it's + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means + * it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * is used to determine which modem is preferred. + * @param needValidation whether Telephony will wait until the network is validated by + * connectivity service before switching data to it. More details see + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED}. + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * Pass null if don't care about the result. + * + * @throws IllegalStateException when subscription manager service is not available. + * @throws SecurityException when clients do not have MODIFY_PHONE_STATE permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setPreferredDataSubscriptionId(int subId, boolean needValidation, + @Nullable @CallbackExecutor Executor executor, @Nullable + @TelephonyManager.SetOpportunisticSubscriptionResult Consumer callback) { + if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub == null) { + throw new IllegalStateException("subscription manager service is null."); + } + + ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { + @Override + public void onComplete(int result) { + if (executor == null || callback == null) { + return; + } + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + callback.accept(result); + }); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub); + } catch (RemoteException ex) { + loge("setPreferredDataSubscriptionId RemoteException=" + ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get which subscription is preferred for cellular data. + * It's also usually the subscription we set up internet connection on. + * + * PreferredData overwrites user setting of default data subscription. And it's used + * by AlternativeNetworkService or carrier apps to switch primary and CBRS + * subscription dynamically in multi-SIM devices. + * + * @return preferred subscription id for cellular data. {@link DEFAULT_SUBSCRIPTION_ID} if + * there's no prefered subscription. + * + * @hide + * + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getPreferredDataSubscriptionId() { + int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + preferredSubId = iSub.getPreferredDataSubscriptionId(); + } + } catch (RemoteException ex) { + // ignore it + } + + return preferredSubId; + } + + /** + * Return opportunistic subscriptions that can be visible to the caller. + * Opportunistic subscriptions are for opportunistic networks, which are cellular + * networks with limited capabilities and coverage, for example, CBRS. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @return the list of opportunistic subscription info. If none exists, an empty list. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public @NonNull List getOpportunisticSubscriptions() { + String contextPkg = mContext != null ? mContext.getOpPackageName() : ""; + String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null; + List subInfoList = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, + contextAttributionTag); + } + } catch (RemoteException ex) { + // ignore it + } + + if (subInfoList == null) { + subInfoList = new ArrayList<>(); + } + +//ccynice + if(subInfoList==null||subInfoList.size()<1) + subInfoList = getFakeSubscriptionInfoList(); + + return subInfoList; + } + + /** + * Switch to a certain subscription + * + * @param subId sub id + * @param callbackIntent pending intent that will be sent after operation is done. + * + * @deprecated this API is a duplicate of {@link EuiccManager#switchToSubscription(int, + * PendingIntent)} and does not support Multiple Enabled Profile(MEP). Apps should use + * {@link EuiccManager#switchToSubscription(int, PendingIntent)} or + * {@link EuiccManager#switchToSubscription(int, int, PendingIntent)} instead. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. + */ + @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC) + @Deprecated + public void switchToSubscription(int subId, @NonNull PendingIntent callbackIntent) { + Preconditions.checkNotNull(callbackIntent, "callbackIntent cannot be null"); + EuiccManager euiccManager = new EuiccManager(mContext); + euiccManager.switchToSubscription(subId, callbackIntent); + } + + /** + * Set whether a subscription is opportunistic, that is, whether the network it connects + * to has limited coverage. For example, CBRS. Setting a subscription opportunistic has + * following impacts: + * 1) Even if it's active, it will be dormant most of the time. The modem will not try + * to scan or camp until it knows an available network is nearby to save power. + * 2) Telephony relies on system app or carrier input to notify nearby available networks. + * See {@link TelephonyManager#updateAvailableNetworks(List, Executor, Consumer)} + * for more information. + * 3) In multi-SIM devices, when the network is nearby and camped, system may automatically + * switch internet data between it and default data subscription, based on carrier + * recommendation and its signal strength and metered-ness, etc. + * + * + * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier + * privilege permission of the subscription. + * + * @param opportunistic whether it’s opportunistic subscription. + * @param subId the unique SubscriptionInfo index in database + * @return {@code true} if the operation is succeed, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean setOpportunistic(boolean opportunistic, int subId) { + if (VDBG) logd("[setOpportunistic]+ opportunistic:" + opportunistic + " subId:" + subId); + return setSubscriptionPropertyHelper(subId, "setOpportunistic", + (iSub)-> iSub.setOpportunistic( + opportunistic, subId, mContext.getOpPackageName())) == 1; + } + + /** + * Inform SubscriptionManager that subscriptions in the list are bundled + * as a group. It can be multiple primary (non-opportunistic) subscriptions, + * or one or more primary plus one or more opportunistic subscriptions. + * + * This API will always create a new immutable group and assign group UUID to all the + * subscriptions, regardless whether they are in a group already or not. + * + * Grouped subscriptions will have below behaviors: + * 1) They will share the same user settings. + * 2) The opportunistic subscriptions in the group is considered invisible and will not + * return from {@link #getActiveSubscriptionInfoList()}, unless caller has carrier + * privilege permission of the subscriptions. + * 3) The opportunistic subscriptions in the group can't be active by itself. If all other + * non-opportunistic ones are deactivated (unplugged or disabled in Settings), + * the opportunistic ones will be deactivated automatically. + * + * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * permission or had carrier privilege permission on the subscriptions: + * {@link TelephonyManager#hasCarrierPrivileges()} or + * {@link #canManageSubscription(SubscriptionInfo)} + * + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + * @throws IllegalArgumentException if any of the subscriptions in the list doesn't exist. + * @throws IllegalStateException if Telephony service is in bad state. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @param subIdList list of subId that will be in the same group + * @return groupUUID a UUID assigned to the subscription group. + * + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public @NonNull ParcelUuid createSubscriptionGroup(@NonNull List subIdList) { + Preconditions.checkNotNull(subIdList, "can't create group for null subId list"); + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + if (VDBG) { + logd("[createSubscriptionGroup]"); + } + + ParcelUuid groupUuid = null; + int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + loge("createSubscriptionGroup RemoteException " + ex); + ex.rethrowAsRuntimeException(); + } + + return groupUuid; + } + + /** + * Add a list of subscriptions into a group. + * See {@link #createSubscriptionGroup(List)} for more details. + * + * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * permission or had carrier privilege permission on the subscriptions: + * {@link TelephonyManager#hasCarrierPrivileges()} or + * {@link #canManageSubscription(SubscriptionInfo)} + * + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist. + * @throws IllegalStateException if Telephony service is in bad state. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @param subIdList list of subId that need adding into the group + * @param groupUuid the groupUuid the subscriptions are being added to. + * + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void addSubscriptionsIntoGroup(@NonNull List subIdList, + @NonNull ParcelUuid groupUuid) { + Preconditions.checkNotNull(subIdList, "subIdList can't be null."); + Preconditions.checkNotNull(groupUuid, "groupUuid can't be null."); + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + if (VDBG) { + logd("[addSubscriptionsIntoGroup]"); + } + + int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + loge("addSubscriptionsIntoGroup RemoteException " + ex); + ex.rethrowAsRuntimeException(); + } + } + + private boolean isSystemProcess() { + return Process.myUid() == Process.SYSTEM_UID; + } + + /** + * Remove a list of subscriptions from their subscription group. + * + * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * permission or has carrier privilege permission on all of the subscriptions provided in + * {@code subIdList}. + * + * @param subIdList list of subId that need removing from their groups. + * @param groupUuid The UUID of the subscription group. + * + * @throws SecurityException if the caller doesn't meet the requirements outlined above. + * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the + * specified group. + * @throws IllegalStateException if Telephony service is in bad state. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @see #createSubscriptionGroup(List) + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void removeSubscriptionsFromGroup(@NonNull List subIdList, + @NonNull ParcelUuid groupUuid) { + Preconditions.checkNotNull(subIdList, "subIdList can't be null."); + Preconditions.checkNotNull(groupUuid, "groupUuid can't be null."); + String callingPackage = mContext != null ? mContext.getOpPackageName() : ""; + if (VDBG) { + logd("[removeSubscriptionsFromGroup]"); + } + + int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray(); + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, callingPackage); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + loge("removeSubscriptionsFromGroup RemoteException " + ex); + ex.rethrowAsRuntimeException(); + } + } + + /** + * Get subscriptionInfo list of subscriptions that are in the same group of given subId. + * + * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE} + * or carrier privilege permission on the subscription. + * {@link TelephonyManager#hasCarrierPrivileges()} + * + *

Starting with API level 33, the caller also needs permission to access device identifiers + * to get the list of subscriptions associated with a group UUID. + * This method can be invoked if one of the following requirements is met: + *

    + *
  • If the app has carrier privilege permission. + * {@link TelephonyManager#hasCarrierPrivileges()} + *
  • If the app has {@link android.Manifest.permission#READ_PHONE_STATE} permission and + * access to device identifiers. + *
+ * + * @throws IllegalStateException if Telephony service is in bad state. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @param groupUuid of which list of subInfo will be returned. + * @return list of subscriptionInfo that belong to the same group, including the given + * subscription itself. It will return an empty list if no subscription belongs to the group. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public @NonNull List getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid) { + Preconditions.checkNotNull(groupUuid, "groupUuid can't be null"); + String contextPkg = mContext != null ? mContext.getOpPackageName() : ""; + String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null; + if (VDBG) { + logd("[getSubscriptionsInGroup]+ groupUuid:" + groupUuid); + } + + List result = null; + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, + contextAttributionTag); + } else { + if (!isSystemProcess()) { + throw new IllegalStateException("telephony service is null."); + } + } + } catch (RemoteException ex) { + loge("removeSubscriptionsFromGroup RemoteException " + ex); + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + + //ccynice + if(result==null||result.size()<1) + result = getFakeSubscriptionInfoList(); + + // TODO(b/296125268) Really this method should throw, but it's common enough that for + // system callers it's worth having a little magic for the system process until it's + // made safer. + if (result == null) result = Collections.emptyList(); + + return result; + } + + /** + * Whether a subscription is visible to API caller. If it's a bundled opportunistic + * subscription, it should be hidden anywhere in Settings, dialer, status bar etc. + * Exception is if caller owns carrier privilege, in which case they will + * want to see their own hidden subscriptions. + * + * @param info the subscriptionInfo to check against. + * + * @return {@code true} if this subscription should be visible to the API caller. + * + * @hide + */ + public boolean isSubscriptionVisible(SubscriptionInfo info) { + if (info == null) return false; + // If subscription is NOT grouped opportunistic subscription, it's visible. + if (info.getGroupUuid() == null || !info.isOpportunistic()) return true; + + // If the caller is the carrier app and owns the subscription, it should be visible + // to the caller. + boolean hasCarrierPrivilegePermission = TelephonyManager.from(mContext) + .hasCarrierPrivileges(info.getSubscriptionId()) + || canManageSubscription(info); + return hasCarrierPrivilegePermission; + } + + /** + * Return a list of subscriptions that are available and visible to the user. + * Used by Settings app to show a list of subscriptions for user to pick. + * + *

+ * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required + * for getSelectableSubscriptionInfoList to be invoked. + * @return list of user selectable subscriptions. + * + * @hide + */ + public @Nullable List getSelectableSubscriptionInfoList() { + List availableList = getAvailableSubscriptionInfoList(); + if (availableList == null) { + return null; + } else { + // Multiple subscriptions in a group should only have one representative. + // It should be the current active primary subscription if any, or any + // primary subscription. + List selectableList = new ArrayList<>(); + Map groupMap = new HashMap<>(); + + for (SubscriptionInfo info : availableList) { + // Grouped opportunistic subscriptions are considered invisible + // to users so they should never be returned. + if (info.getGroupUuid() != null && info.isOpportunistic()) continue; + + ParcelUuid groupUuid = info.getGroupUuid(); + if (groupUuid == null) { + // Doesn't belong to any group. Add in the list. + selectableList.add(info); + } else if (!groupMap.containsKey(groupUuid) + || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX + && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) { + // If it belongs to a group that has never been recorded or it's the current + // active subscription, add it in the list. + selectableList.remove(groupMap.get(groupUuid)); + selectableList.add(info); + groupMap.put(groupUuid, info); + } + + } + return selectableList; + } + } + + /** + * Enable or disable a subscription. This method is same as + * {@link #setUiccApplicationsEnabled(int, boolean)}. + * + * @param subscriptionId Subscription to be enabled or disabled. + * @param enable whether user is turning it on or off. + * + * @return whether the operation is successful. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean setSubscriptionEnabled(int subscriptionId, boolean enable) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setUiccApplicationsEnabled(enable, subscriptionId); + } + } catch (RemoteException ex) { + return false; + } + return true; + } + + /** + * Set uicc applications being enabled or disabled. + * The value will be remembered on the subscription and will be applied whenever it's present. + * If the subscription in currently present, it will also apply the setting to modem + * immediately (the setting in the modem will not change until the modem receives and responds + * to the request, but typically this should only take a few seconds. The user visible setting + * available from SubscriptionInfo.areUiccApplicationsEnabled() will be updated + * immediately.) + * + * @param subscriptionId which subscription to operate on. + * @param enabled whether uicc applications are enabled or disabled. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setUiccApplicationsEnabled(int subscriptionId, boolean enabled) { + if (VDBG) { + logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled); + } + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setUiccApplicationsEnabled(enabled, subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** + * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM. + * + * Physical SIM refers non-euicc, or aka non-programmable SIM. + * + * It provides whether a physical SIM card can be disabled without taking it out, which is done + * via {@link #setSubscriptionEnabled(int, boolean)} API. + * + * @return whether can disable subscriptions on physical SIMs. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean canDisablePhysicalSubscription() { + if (VDBG) { + logd("canDisablePhysicalSubscription"); + } + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.canDisablePhysicalSubscription(); + } + } catch (RemoteException ex) { + // ignore it + } + + return false; + } + + /** + * Check if the subscription is currently active in any slot. + * + * @param subscriptionId The subscription id. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isSubscriptionEnabled(int subscriptionId) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.isSubscriptionEnabled(subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + + return false; + } + + /** + * Set the device to device status sharing user preference for a subscription id. The setting + * app uses this method to indicate with whom they wish to share device to device status + * information. + * + * @param subscriptionId The subscription id. + * @param sharing The status sharing preference. + * + * @throws SecurityException if the caller doesn't have permissions required. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDeviceToDeviceStatusSharingPreference(int subscriptionId, + @DeviceToDeviceStatusSharingPreference int sharing) { + if (VDBG) { + logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + + subscriptionId); + } + setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus", + (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subscriptionId)); + } + + /** + * Returns the user-chosen device to device status sharing preference + * @param subscriptionId Subscription id of subscription + * @return The device to device status sharing preference + * + * @throws SecurityException if the caller doesn't have permissions required. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference( + int subscriptionId) { + if (VDBG) { + logd("[getDeviceToDeviceStatusSharing] + subId: " + subscriptionId); + } + return getIntegerSubscriptionProperty(subscriptionId, D2D_STATUS_SHARING, + D2D_SHARING_DISABLED, mContext); + } + + /** + * Set the list of contacts that allow device to device status sharing for a subscription id. + * The setting app uses this method to indicate with whom they wish to share device to device + * status information. + * + * @param subscriptionId The subscription id. + * @param contacts The list of contacts that allow device to device status sharing. + * + * @throws SecurityException if the caller doesn't have permissions required. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDeviceToDeviceStatusSharingContacts(int subscriptionId, + @NonNull List contacts) { + String contactString = serializeUriLists(contacts); + if (VDBG) { + logd("[setDeviceToDeviceStatusSharingContacts] + contacts: " + contactString + + " subId: " + subscriptionId); + } + setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus", + (iSub)->iSub.setDeviceToDeviceStatusSharingContacts(serializeUriLists(contacts), + subscriptionId)); + } + + /** + * Get the list of contacts that allow device to device status sharing. + * + * @param subscriptionId Subscription id. + * + * @return The list of contacts that allow device to device status sharing. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + public @NonNull List getDeviceToDeviceStatusSharingContacts(int subscriptionId) { + String result = getStringSubscriptionProperty(mContext, subscriptionId, + D2D_STATUS_SHARING_SELECTED_CONTACTS); + if (result != null) { + try { + byte[] b = Base64.decode(result, Base64.DEFAULT); + ByteArrayInputStream bis = new ByteArrayInputStream(b); + ObjectInputStream ois = new ObjectInputStream(bis); + List contacts = ArrayList.class.cast(ois.readObject()); + List uris = new ArrayList<>(); + for (String contact : contacts) { + uris.add(Uri.parse(contact)); + } + return uris; + } catch (IOException e) { + logd("getDeviceToDeviceStatusSharingContacts IO exception"); + } catch (ClassNotFoundException e) { + logd("getDeviceToDeviceStatusSharingContacts ClassNotFound exception"); + } + } + return new ArrayList<>(); + } + + /** + * Get the active subscription id by logical SIM slot index. + * + * @param slotIndex The logical SIM slot index. + * @return The active subscription id. + * + * @throws IllegalArgumentException if the provided slot index is invalid. + * @throws SecurityException if callers do not hold the required permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getEnabledSubscriptionId(int slotIndex) { + int subId = INVALID_SUBSCRIPTION_ID; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + subId = iSub.getEnabledSubscriptionId(slotIndex); + } + } catch (RemoteException ex) { + // ignore it + } + + if (VDBG) logd("getEnabledSubscriptionId, subId = " + subId); + return subId; + } + + private interface CallISubMethodHelper { + int callMethod(ISub iSub) throws RemoteException; + } + + private int setSubscriptionPropertyHelper(int subId, String methodName, + CallISubMethodHelper helper) { + if (!isValidSubscriptionId(subId)) { + logd("[" + methodName + "]" + "- fail"); + return -1; + } + + int result = 0; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + result = helper.callMethod(iSub); + } + } catch (RemoteException ex) { + // ignore it + } + + return result; + } + + /** + * Get active data subscription id. Active data subscription refers to the subscription + * currently chosen to provide cellular internet connection to the user. This may be + * different from {@link #getDefaultDataSubscriptionId()}. + * + * @return Active data subscription id if any is chosen, or {@link #INVALID_SUBSCRIPTION_ID} if + * not. + * + * @see TelephonyCallback.ActiveDataSubscriptionIdListener + */ + public static int getActiveDataSubscriptionId() { + return sGetActiveDataSubscriptionIdCache.query(null); + } + + /** + * Helper method that puts a subscription id on an intent with the constants: + * PhoneConstant.SUBSCRIPTION_KEY and SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX. + * Both constants are used to support backwards compatibility. Once we know we got all places, + * we can remove PhoneConstants.SUBSCRIPTION_KEY. + * @param intent Intent to put sub id on. + * @param subId SubscriptionId to put on intent. + * + * @hide + */ + public static void putSubscriptionIdExtra(Intent intent, int subId) { + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); + intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); + } + + /** @hide */ + public static void invalidateSubscriptionManagerServiceCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY); + } + + /** + * Allows a test process to disable client-side caching operations. + * + * @hide + */ + public static void disableCaching() { + sGetDefaultSubIdCacheAsUser.disableLocal(); + sGetDefaultDataSubIdCache.disableLocal(); + sGetActiveDataSubscriptionIdCache.disableLocal(); + sGetDefaultSmsSubIdCacheAsUser.disableLocal(); + sGetSlotIndexCache.disableLocal(); + sGetSubIdCache.disableLocal(); + sGetPhoneIdCache.disableLocal(); + } + + /** + * Clears all process-local binder caches. + * + * @hide */ + public static void clearCaches() { + sGetDefaultSubIdCacheAsUser.clear(); + sGetDefaultDataSubIdCache.clear(); + sGetActiveDataSubscriptionIdCache.clear(); + sGetDefaultSmsSubIdCacheAsUser.clear(); + sGetSlotIndexCache.clear(); + sGetSubIdCache.clear(); + sGetPhoneIdCache.clear(); + } + + /** + * Called to retrieve SIM-specific settings data to be backed up. + * + * @return data in byte[] to be backed up. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public byte[] getAllSimSpecificSettingsForBackup() { + Bundle bundle = mContext.getContentResolver().call( + SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, + GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null); + return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA); + } + + /** + * Called during setup wizard restore flow to attempt to restore the backed up sim-specific + * configs to device for all existing SIMs in the subscription database {@link SimInfo}. + * Internally, it will store the backup data in an internal file. This file will persist on + * device for device's lifetime and will be used later on when a SIM is inserted to restore that + * specific SIM's settings. End result is subscription database is modified to match any backed + * up configs for the appropriate inserted SIMs. + * + *

+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any + * {@link SimInfo} entry is updated as the result of this method call. + * + * @param data with the sim specific configs to be backed up. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.restoreAllSimSpecificSettingsFromBackup(data); + } else { + throw new IllegalStateException("subscription service unavailable."); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + } + + /** + * Returns the phone number for the given {@code subscriptionId} and {@code source}, + * or an empty string if not available. + * + *

General apps that need to know the phone number should use {@link #getPhoneNumber(int)} + * instead. This API may be suitable specific apps that needs to know the phone number from + * a specific source. For example, a carrier app needs to know exactly what's on + * {@link #PHONE_NUMBER_SOURCE_UICC UICC} and decide if the previously set phone number + * of source {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} should be updated. + * + *

The API provides no guarantees of what format the number is in: the format can vary + * depending on the {@code source} and the network etc. Programmatic parsing should be done + * cautiously, for example, after formatting the number to a consistent format with + * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}. + * + *

Note the assumption is that one subscription (which usually means one SIM) has + * only one phone number. The multiple sources backup each other so hopefully at least one + * is available. For example, for a carrier that doesn't typically set phone numbers + * on {@link #PHONE_NUMBER_SOURCE_UICC UICC}, the source {@link #PHONE_NUMBER_SOURCE_IMS IMS} + * may provide one. Or, a carrier may decide to provide the phone number via source + * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} if neither source UICC nor IMS is available. + * + *

The availability and correctness of the phone number depends on the underlying source + * and the network etc. Additional verification is needed to use this number for + * security-related or other sensitive scenarios. + * + * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID} + * for the default one. + * @param source the source of the phone number, one of the PHONE_NUMBER_SOURCE_* constants. + * + * @return the phone number, or an empty string if not available. + * + * @throws IllegalArgumentException if {@code source} is invalid. + * @throws IllegalStateException if the telephony process is not currently available. + * @throws SecurityException if the caller doesn't have permissions required. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @see #PHONE_NUMBER_SOURCE_UICC + * @see #PHONE_NUMBER_SOURCE_CARRIER + * @see #PHONE_NUMBER_SOURCE_IMS + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_NUMBERS, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "carrier privileges", + }) + @NonNull + public String getPhoneNumber(int subscriptionId, @PhoneNumberSource int source) { + if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) { + subscriptionId = getDefaultSubscriptionId(); + } + if (source != PHONE_NUMBER_SOURCE_UICC + && source != PHONE_NUMBER_SOURCE_CARRIER + && source != PHONE_NUMBER_SOURCE_IMS) { + throw new IllegalArgumentException("invalid source " + source); + } + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.getPhoneNumber(subscriptionId, source, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } else { + throw new IllegalStateException("subscription service unavailable."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Returns the phone number for the given {@code subId}, or an empty string if + * not available. + * + *

This API is suitable for general apps that needs to know the phone number. + * For specific apps that needs to know the phone number provided by a specific source, + * {@link #getPhoneNumber(int, int)} may be suitable. + * + *

This API is built up on {@link #getPhoneNumber(int, int)}, but picks + * from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER} + * > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}. + * + *

The API provides no guarantees of what format the number is in: the format can vary + * depending on the underlying source and the network etc. Programmatic parsing should be done + * cautiously, for example, after formatting the number to a consistent format with + * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}. + * + *

The availability and correctness of the phone number depends on the underlying source + * and the network etc. Additional verification is needed to use this number for + * security-related or other sensitive scenarios. + * + * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID} + * for the default one. + * @return the phone number, or an empty string if not available. + * + * @throws IllegalStateException if the telephony process is not currently available. + * @throws SecurityException if the caller doesn't have permissions required. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @see #getPhoneNumber(int, int) + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_NUMBERS, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "carrier privileges", + }) + @NonNull + public String getPhoneNumber(int subscriptionId) { + if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) { + subscriptionId = getDefaultSubscriptionId(); + } + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.getPhoneNumberFromFirstAvailableSource(subscriptionId, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } else { + throw new IllegalStateException("subscription service unavailable."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Sets the phone number for the given {@code subId} for source + * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier}. + * Sets an empty string to remove the previously set phone number. + * + *

The API is suitable for carrier apps to provide a phone number, for example when + * it's not possible to update {@link #PHONE_NUMBER_SOURCE_UICC UICC} directly. + * + *

It's recommended that the phone number is formatted to well-known formats, + * for example, by {@link PhoneNumberUtils} {@code formatNumber*} methods. + * + * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID} + * for the default one. + * @param number the phone number, or an empty string to remove the previously set number. + * @throws IllegalStateException if the telephony process is not currently available. + * @throws NullPointerException if {@code number} is {@code null}. + * @throws SecurityException if the caller doesn't have permissions required. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission("carrier privileges") + public void setCarrierPhoneNumber(int subscriptionId, @NonNull String number) { + if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) { + subscriptionId = getDefaultSubscriptionId(); + } + if (number == null) { + throw new NullPointerException("invalid number null"); + } + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setPhoneNumber(subscriptionId, PHONE_NUMBER_SOURCE_CARRIER, number, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } else { + throw new IllegalStateException("subscription service unavailable."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Set the preferred usage setting. + * + * The cellular usage setting is a switch which controls the mode of operation for the cellular + * radio to either require or not require voice service. It is not managed via Android’s + * Settings. + * + * @param subscriptionId the subId of the subscription. + * @param usageSetting the requested usage setting. + * + * @throws IllegalStateException if a specific mode or setting the mode is not supported on a + * particular device. + * + *

Requires {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * or that the calling app has CarrierPrivileges for the given subscription. + * + * Note: This method will not allow the setting of USAGE_SETTING_UNKNOWN. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + void setUsageSetting(int subscriptionId, @UsageSetting int usageSetting) { + if (VDBG) logd("[setUsageSetting]+ setting:" + usageSetting + " subId:" + subscriptionId); + setSubscriptionPropertyHelper(subscriptionId, "setUsageSetting", + (iSub)-> iSub.setUsageSetting( + usageSetting, subscriptionId, mContext.getOpPackageName())); + } + + /** + * Convert phone number source to string. + * + * @param source The phone name source. + * + * @return The phone name source in string format. + * + * @hide + */ + @NonNull + public static String phoneNumberSourceToString(@PhoneNumberSource int source) { + switch (source) { + case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC: return "UICC"; + case SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER: return "CARRIER"; + case SubscriptionManager.PHONE_NUMBER_SOURCE_IMS: return "IMS"; + default: + return "UNKNOWN(" + source + ")"; + } + } + + /** + * Convert display name source to string. + * + * @param source The display name source. + * @return The display name source in string format. + * + * @hide + */ + @NonNull + public static String displayNameSourceToString( + @SubscriptionManager.SimDisplayNameSource int source) { + switch (source) { + case SubscriptionManager.NAME_SOURCE_UNKNOWN: return "UNKNOWN"; + case SubscriptionManager.NAME_SOURCE_CARRIER_ID: return "CARRIER_ID"; + case SubscriptionManager.NAME_SOURCE_SIM_SPN: return "SIM_SPN"; + case SubscriptionManager.NAME_SOURCE_USER_INPUT: return "USER_INPUT"; + case SubscriptionManager.NAME_SOURCE_CARRIER: return "CARRIER"; + case SubscriptionManager.NAME_SOURCE_SIM_PNN: return "SIM_PNN"; + default: + return "UNKNOWN(" + source + ")"; + } + } + + /** + * Convert subscription type to string. + * + * @param type The subscription type. + * @return The subscription type in string format. + * + * @hide + */ + @NonNull + public static String subscriptionTypeToString(@SubscriptionManager.SubscriptionType int type) { + switch (type) { + case SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM: return "LOCAL_SIM"; + case SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM: return "REMOTE_SIM"; + default: + return "UNKNOWN(" + type + ")"; + } + } + + /** + * Convert usage setting to string. + * + * @param usageSetting Usage setting. + * @return The usage setting in string format. + * + * @hide + */ + @NonNull + public static String usageSettingToString(@SubscriptionManager.UsageSetting int usageSetting) { + switch (usageSetting) { + case SubscriptionManager.USAGE_SETTING_UNKNOWN: return "UNKNOWN"; + case SubscriptionManager.USAGE_SETTING_DEFAULT: return "DEFAULT"; + case SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC: return "VOICE_CENTRIC"; + case SubscriptionManager.USAGE_SETTING_DATA_CENTRIC: return "DATA_CENTRIC"; + default: + return "UNKNOWN(" + usageSetting + ")"; + } + } + + /** + * Set userHandle for a subscription. + * + * Used to set an association between a subscription and a user on the device so that voice + * calling and SMS from that subscription can be associated with that user. + * Data services are always shared between users on the device. + * + * @param subscriptionId the subId of the subscription. + * @param userHandle the userHandle associated with the subscription. + * Pass {@code null} user handle to clear the association. + * + * @throws IllegalArgumentException if subscription is invalid. + * @throws SecurityException if the caller doesn't have permissions required. + * @throws IllegalStateException if subscription service is not available. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) + public void setSubscriptionUserHandle(int subscriptionId, @Nullable UserHandle userHandle) { + if (!isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("[setSubscriptionUserHandle]: " + + "Invalid subscriptionId: " + subscriptionId); + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setSubscriptionUserHandle(userHandle, subscriptionId); + } else { + throw new IllegalStateException("[setSubscriptionUserHandle]: " + + "subscription service unavailable"); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + } + + /** + * Get UserHandle of this subscription. + * + * Used to get user handle associated with this subscription. + * + * @param subscriptionId the subId of the subscription. + * @return userHandle associated with this subscription + * or {@code null} if subscription is not associated with any user. + * + * @throws IllegalArgumentException if subscription is invalid. + * @throws SecurityException if the caller doesn't have permissions required. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) + public @Nullable UserHandle getSubscriptionUserHandle(int subscriptionId) { + if (!isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("[getSubscriptionUserHandle]: " + + "Invalid subscriptionId: " + subscriptionId); + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.getSubscriptionUserHandle(subscriptionId); + } else { + Log.e(LOG_TAG, "[getSubscriptionUserHandle]: subscription service unavailable"); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return null; + } + + /** + * Check if subscription and user are associated with each other. + * + * @param subscriptionId the subId of the subscription + * @param userHandle user handle of the user + * @return {@code true} if subscription is associated with user + * else {@code false} if subscription is not associated with user. + * + * @throws IllegalArgumentException if subscription doesn't exist. + * @throws SecurityException if the caller doesn't have permissions required. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) + public boolean isSubscriptionAssociatedWithUser(int subscriptionId, + @NonNull UserHandle userHandle) { + if (!isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("[isSubscriptionAssociatedWithUser]: " + + "Invalid subscriptionId: " + subscriptionId); + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.isSubscriptionAssociatedWithUser(subscriptionId, userHandle); + } else { + Log.e(LOG_TAG, "[isSubscriptionAssociatedWithUser]: subscription service " + + "unavailable"); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Returns whether the given subscription is associated with the calling user. + * + * @param subscriptionId the subscription ID of the subscription + * @return {@code true} if the subscription is associated with the user that the current process + * is running in; {@code false} otherwise. + * + * @throws IllegalArgumentException if subscription doesn't exist. + * @throws SecurityException if the caller doesn't have permissions required. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @FlaggedApi(Flags.FLAG_SUBSCRIPTION_USER_ASSOCIATION_QUERY) + public boolean isSubscriptionAssociatedWithUser(int subscriptionId) { + if (!isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("[isSubscriptionAssociatedWithCallingUser]: " + + "Invalid subscriptionId: " + subscriptionId); + } + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.isSubscriptionAssociatedWithCallingUser(subscriptionId); + } else { + throw new IllegalStateException("subscription service unavailable."); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Get list of subscriptions associated with user. + * + * @param userHandle user handle of the user + * @return list of subscriptionInfo associated with the user. + * + * @throws SecurityException if the caller doesn't have permissions required. + * @throws IllegalStateException if subscription service is not available. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) + public @NonNull List getSubscriptionInfoListAssociatedWithUser( + @NonNull UserHandle userHandle) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + return iSub.getSubscriptionInfoListAssociatedWithUser(userHandle); + } else { + Log.e(LOG_TAG, "[getSubscriptionInfoListAssociatedWithUser]: " + + "subscription service unavailable"); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return new ArrayList<>(); + } + + /** + * @return the bitmasks combination of all service capabilities. + * @hide + */ + public static int getAllServiceCapabilityBitmasks() { + return SERVICE_CAPABILITY_VOICE_BITMASK | SERVICE_CAPABILITY_SMS_BITMASK + | SERVICE_CAPABILITY_DATA_BITMASK; + } + + /** + * @return The set of service capability from a bitmask combined one. + * @hide + */ + @NonNull + @ServiceCapability + public static Set getServiceCapabilitiesSet(int combinedServiceCapabilities) { + Set capabilities = new HashSet<>(); + for (int i = SERVICE_CAPABILITY_VOICE; i <= SERVICE_CAPABILITY_MAX; i++) { + final int capabilityBitmask = serviceCapabilityToBitmask(i); + if ((combinedServiceCapabilities & capabilityBitmask) == capabilityBitmask) { + capabilities.add(i); + } + } + return Collections.unmodifiableSet(capabilities); + } + + /** + * @return The service capability bitmask from a {@link ServiceCapability} value. + * @hide + */ + public static int serviceCapabilityToBitmask(@ServiceCapability int capability) { + return 1 << (capability - 1); + } + + /** + * Set the transfer status of the subscriptionInfo of the subId. + * @param subscriptionId The unique SubscriptionInfo key in database. + * @param status The transfer status to change. + * + * + * @hide + */ + @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION) + @SystemApi + @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) + public void setTransferStatus(int subscriptionId, @TransferStatus int status) { + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + iSub.setTransferStatus(subscriptionId, status); + } + } catch (RemoteException ex) { + logd("setTransferStatus for subId = " + subscriptionId + " failed."); + throw ex.rethrowFromSystemServer(); + } + } + + //ccynice for sim + private List getFakeSubscriptionInfoList(){ + List result = new ArrayList<>(); + + int simSlotIndex =SystemProperties.getInt("persist.sm.simSlotIndex",-1); + if(simSlotIndex==0){ + int id =SystemProperties.getInt("persist.sm.id",0); + String iccId =SystemProperties.get("persist.sm.iccId",""); + String displayName =SystemProperties.get("persist.sm.displayName",""); + String carrierName =SystemProperties.get("persist.sm.carrierName",""); + + int nameSource =SystemProperties.getInt("persist.sm.nameSource",0); + int iconTint =SystemProperties.getInt("persist.sm.iconTint",0); + String number =SystemProperties.get("persist.sm.number",""); + int roaming =SystemProperties.getInt("persist.sm.roaming",0); + //icon + + String mcc =SystemProperties.get("persist.sm.mcc",""); + String mnc =SystemProperties.get("persist.sm.mnc",""); + String countryIso =SystemProperties.get("persist.sm.countryIso",""); + boolean isEmbedded =SystemProperties.getBoolean("persist.sm.isEmbedded",false); + //nativeAccessRules + + String cardString =SystemProperties.get("persist.sm.cardString",""); + int cardId =SystemProperties.getInt("persist.sm.cardId",0); + boolean isOpportunistic =SystemProperties.getBoolean("persist.sm.isOpportunistic",false); + String groupUUID =SystemProperties.get("persist.sm.groupUUID",""); + boolean isGroupDisabled =SystemProperties.getBoolean("persist.sm.isGroupDisabled",false); + + int carrierId =SystemProperties.getInt("persist.sm.carrierId",0); + int profileClass =SystemProperties.getInt("persist.sm.profileClass",0); + int subType =SystemProperties.getInt("persist.sm.subType",0); + String groupOwner =SystemProperties.get("persist.sm.groupOwner",""); + //carrierConfigAccessRules + boolean areUiccApplicationsEnabled =SystemProperties.getBoolean("persist.sm.areUiccApplicationsEnabled",true); + + result.add(new SubscriptionInfo(id,iccId,simSlotIndex,carrierName,carrierName, + nameSource,iconTint,number,roaming,null, + mcc,mcc,countryIso,isEmbedded,null, + cardString,cardId,isOpportunistic,groupUUID.isEmpty()?null:groupUUID,isGroupDisabled, + carrierId,profileClass,subType,groupOwner.isEmpty()?null:groupOwner, + null,areUiccApplicationsEnabled)); + + } + + int simSlotIndex2 =SystemProperties.getInt("persist.sm.simSlotIndex2",-1); + if(simSlotIndex2==1){ + int id =SystemProperties.getInt("persist.sm.id2",0); + String iccId =SystemProperties.get("persist.sm.iccId2",""); + String displayName =SystemProperties.get("persist.sm.displayName2",""); + String carrierName =SystemProperties.get("persist.sm.carrierName2",""); + + int nameSource =SystemProperties.getInt("persist.sm.nameSource2",0); + int iconTint =SystemProperties.getInt("persist.sm.iconTint2",0); + String number =SystemProperties.get("persist.sm.number2",""); + int roaming =SystemProperties.getInt("persist.sm.roaming2",0); + //icon + + String mcc =SystemProperties.get("persist.sm.mcc2",""); + String mnc =SystemProperties.get("persist.sm.mnc2",""); + String countryIso =SystemProperties.get("persist.sm.countryIso2",""); + boolean isEmbedded =SystemProperties.getBoolean("persist.sm.isEmbedded2",false); + //nativeAccessRules + + String cardString =SystemProperties.get("persist.sm.cardString2",""); + int cardId =SystemProperties.getInt("persist.sm.cardId2",0); + boolean isOpportunistic =SystemProperties.getBoolean("persist.sm.isOpportunistic2",false); + String groupUUID =SystemProperties.get("persist.sm.groupUUID2",""); + boolean isGroupDisabled =SystemProperties.getBoolean("persist.sm.isGroupDisabled2",false); + + int carrierId =SystemProperties.getInt("persist.sm.carrierId2",0); + int profileClass =SystemProperties.getInt("persist.sm.profileClass2",0); + int subType =SystemProperties.getInt("persist.sm.subType2",0); + String groupOwner =SystemProperties.get("persist.sm.groupOwner2",""); + //carrierConfigAccessRules + boolean areUiccApplicationsEnabled =SystemProperties.getBoolean("persist.sm.areUiccApplicationsEnabled2",true); + + result.add(new SubscriptionInfo(id,iccId,simSlotIndex2,carrierName,carrierName, + nameSource,iconTint,number,roaming,null, + mcc,mcc,countryIso,isEmbedded,null, + cardString,cardId,isOpportunistic,groupUUID.isEmpty()?null:groupUUID,isGroupDisabled, + carrierId,profileClass,subType,groupOwner.isEmpty()?null:groupOwner, + null,areUiccApplicationsEnabled)); + } + return result; + } +} diff --git a/aosp/frameworks/base/telephony/java/android/telephony/TelephonyManager.java b/aosp/frameworks/base/telephony/java/android/telephony/TelephonyManager.java new file mode 100755 index 0000000000000000000000000000000000000000..c4fd12db11c1c39df422c6ca033f777ca93304bb --- /dev/null +++ b/aosp/frameworks/base/telephony/java/android/telephony/TelephonyManager.java @@ -0,0 +1,19391 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import static android.content.Context.TELECOM_SERVICE; +import static android.provider.Telephony.Carriers.DPC_URI; +import static android.provider.Telephony.Carriers.INVALID_APN_ID; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.Manifest; +import android.annotation.BytesLong; +import android.annotation.CallbackExecutor; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.LongDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresFeature; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.StringDef; +import android.annotation.SuppressAutoDoc; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.WorkerThread; +import android.app.PendingIntent; +import android.app.PropertyInvalidatedCache; +import android.app.role.RoleManager; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextParams; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.OutcomeReceiver; +import android.os.ParcelFileDescriptor; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.SystemProperties; +import android.os.WorkSource; +import android.provider.Settings.SettingNotFoundException; +import android.service.carrier.CarrierIdentifier; +import android.service.carrier.CarrierService; +import android.sysprop.TelephonyProperties; +import android.telecom.Call; +import android.telecom.CallScreeningService; +import android.telecom.Connection; +import android.telecom.InCallService; +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import android.telephony.Annotation.ApnType; +import android.telephony.Annotation.CallState; +import android.telephony.Annotation.CarrierPrivilegeStatus; +import android.telephony.Annotation.NetworkType; +import android.telephony.Annotation.RadioPowerState; +import android.telephony.Annotation.SimActivationState; +import android.telephony.Annotation.ThermalMitigationResult; +import android.telephony.Annotation.UiccAppType; +import android.telephony.Annotation.UiccAppTypeExt; +import android.telephony.CallForwardingInfo.CallForwardingReason; +import android.telephony.VisualVoicemailService.VisualVoicemailTask; +import android.telephony.data.ApnSetting; +import android.telephony.data.ApnSetting.MvnoType; +import android.telephony.data.NetworkSlicingConfig; +import android.telephony.emergency.EmergencyNumber; +import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; +import android.telephony.gba.UaSecurityProtocolIdentifier; +import android.telephony.ims.ImsMmTelManager; +import android.telephony.ims.aidl.IImsConfig; +import android.telephony.ims.aidl.IImsRegistration; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; +import com.android.internal.telephony.CellNetworkScanResult; +import com.android.internal.telephony.IBooleanConsumer; +import com.android.internal.telephony.ICallForwardingInfoCallback; +import com.android.internal.telephony.IIntegerConsumer; +import com.android.internal.telephony.INumberVerificationCallback; +import com.android.internal.telephony.IOns; +import com.android.internal.telephony.IPhoneSubInfo; +import com.android.internal.telephony.ISetOpportunisticDataCallback; +import com.android.internal.telephony.ISms; +import com.android.internal.telephony.ISub; +import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.IUpdateAvailableNetworksCallback; +import com.android.internal.telephony.IccLogicalChannelRequest; +import com.android.internal.telephony.OperatorInfo; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.flags.Flags; +import com.android.telephony.Rlog; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Provides access to information about the telephony services on + * the device. Applications can use the methods in this class to + * determine telephony services and states, as well as to access some + * types of subscriber information. Applications can also register + * a listener to receive notification of telephony state changes. + *

+ * The returned TelephonyManager will use the default subscription for all calls. + * To call an API for a specific subscription, use {@link #createForSubscriptionId(int)}. e.g. + * + * telephonyManager = defaultSubTelephonyManager.createForSubscriptionId(subId); + * + *

+ * Note that access to some telephony information is + * permission-protected. Your application cannot access the protected + * information unless it has the appropriate permissions declared in + * its manifest file. Where permissions apply, they are noted in the + * the methods through which you access the protected information. + * + *

TelephonyManager is intended for use on devices that implement + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices + * that do not implement this feature, the behavior is not reliable. + */ +@SystemService(Context.TELEPHONY_SERVICE) +@RequiresFeature(PackageManager.FEATURE_TELEPHONY) +public class TelephonyManager { + private static final String TAG = "TelephonyManager"; + + private TelephonyRegistryManager mTelephonyRegistryMgr; + /** + * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and + * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L; + + /** + * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)} + * into the ResultReceiver Bundle. + * @hide + */ + public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity"; + + /** @hide */ + public static final String EXCEPTION_RESULT_KEY = "exception"; + + /** + * The process name of the Phone app as well as many other apps that use this process name, such + * as settings and vendor components. + * @hide + */ + public static final String PHONE_PROCESS_NAME = "com.android.phone"; + + /** + * The allowed states of Wi-Fi calling. + * + * @hide + */ + public interface WifiCallingChoices { + /** Always use Wi-Fi calling */ + static final int ALWAYS_USE = 0; + /** Ask the user whether to use Wi-Fi on every call */ + static final int ASK_EVERY_TIME = 1; + /** Never use Wi-Fi calling */ + static final int NEVER_USE = 2; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"NETWORK_SELECTION_MODE_"}, + value = { + NETWORK_SELECTION_MODE_UNKNOWN, + NETWORK_SELECTION_MODE_AUTO, + NETWORK_SELECTION_MODE_MANUAL}) + public @interface NetworkSelectionMode {} + + public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; + public static final int NETWORK_SELECTION_MODE_AUTO = 1; + public static final int NETWORK_SELECTION_MODE_MANUAL = 2; + + /** + * Reasons for Radio being powered off. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RADIO_POWER_REASON_"}, + value = { + RADIO_POWER_REASON_USER, + RADIO_POWER_REASON_THERMAL, + RADIO_POWER_REASON_CARRIER, + RADIO_POWER_REASON_NEARBY_DEVICE}) + public @interface RadioPowerReason {} + + /** + * This reason is used when users want to turn off radio, e.g., users turn on airplane mode. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_USER = 0; + /** + * This reason is used when radio needs to be turned off due to thermal. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_THERMAL = 1; + /** + * This reason is used when carriers want to turn off radio. A privileged app can request to + * turn off radio via the system service + * {@link com.android.carrierdefaultapp.CaptivePortalLoginActivity}, which subsequently calls + * the system APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_CARRIER = 2; + /** + * Used to reduce power on a battery-constrained device when Telephony services are available + * via a paired device which is nearby. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_NEARBY_DEVICE = 3; + + /** The otaspMode passed to PhoneStateListener#onOtaspChanged */ + /** @hide */ + static public final int OTASP_UNINITIALIZED = 0; + /** @hide */ + static public final int OTASP_UNKNOWN = 1; + /** @hide */ + static public final int OTASP_NEEDED = 2; + /** @hide */ + static public final int OTASP_NOT_NEEDED = 3; + /* OtaUtil has conflict enum 4: OtaUtils.OTASP_FAILURE_SPC_RETRIES */ + /** @hide */ + static public final int OTASP_SIM_UNPROVISIONED = 5; + + /** + * Used in carrier Wi-Fi for IMSI + IMPI encryption, this indicates a public key that's + * available for use in ePDG links. + * + * @hide + */ + @SystemApi + static public final int KEY_TYPE_EPDG = 1; + + /** + * Used in carrier Wi-Fi for IMSI + IMPI encryption, this indicates a public key that's + * available for use in WLAN links. + * + * @hide + */ + @SystemApi + static public final int KEY_TYPE_WLAN = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"KEY_TYPE_"}, value = {KEY_TYPE_EPDG, KEY_TYPE_WLAN}) + public @interface KeyType {} + + /** + * No Single Radio Voice Call Continuity (SRVCC) handover is active. + * See TS 23.216 for more information. + * @hide + */ + @SystemApi + public static final int SRVCC_STATE_HANDOVER_NONE = -1; + + /** + * Single Radio Voice Call Continuity (SRVCC) handover has been started on the network. + * See TS 23.216 for more information. + * @hide + */ + @SystemApi + public static final int SRVCC_STATE_HANDOVER_STARTED = 0; + + /** + * Ongoing Single Radio Voice Call Continuity (SRVCC) handover has successfully completed. + * See TS 23.216 for more information. + * @hide + */ + @SystemApi + public static final int SRVCC_STATE_HANDOVER_COMPLETED = 1; + + /** + * Ongoing Single Radio Voice Call Continuity (SRVCC) handover has failed. + * See TS 23.216 for more information. + * @hide + */ + @SystemApi + public static final int SRVCC_STATE_HANDOVER_FAILED = 2; + + /** + * Ongoing Single Radio Voice Call Continuity (SRVCC) handover has been canceled. + * See TS 23.216 for more information. + * @hide + */ + @SystemApi + public static final int SRVCC_STATE_HANDOVER_CANCELED = 3; + + /** + * Convert srvcc handover state to string. + * + * @param state The srvcc handover state. + * @return The srvcc handover state in string format. + * + * @hide + */ + public static @NonNull String srvccStateToString(int state) { + switch (state) { + case TelephonyManager.SRVCC_STATE_HANDOVER_NONE: + return "NONE"; + case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED: + return "STARTED"; + case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED: + return "COMPLETED"; + case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: + return "FAILED"; + case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED: + return "CANCELED"; + default: + return "UNKNOWN(" + state + ")"; + } + } + + /** + * A UICC card identifier used if the device does not support the operation. + * For example, {@link #getCardIdForDefaultEuicc()} returns this value if the device has no + * eUICC, or the eUICC cannot be read. + */ + public static final int UNSUPPORTED_CARD_ID = -1; + + /** + * A UICC card identifier used before the UICC card is loaded. See + * {@link #getCardIdForDefaultEuicc()} and {@link UiccCardInfo#getCardId()}. + *

+ * Note that once the UICC card is loaded, the card ID may become {@link #UNSUPPORTED_CARD_ID}. + */ + public static final int UNINITIALIZED_CARD_ID = -2; + + /** + * Default port index for a UICC. + * + * On physical SIM cards the only available port is 0. + * See {@link android.telephony.UiccPortInfo} for more information on ports. + * + * See {@link android.telephony.euicc.EuiccManager#isSimPortAvailable(int)} for information on + * how portIndex is used on eUICCs. + */ + public static final int DEFAULT_PORT_INDEX = 0; + + /** @hide */ + public static final int INVALID_PORT_INDEX = -1; + + /** @hide */ + public static final String PROPERTY_ENABLE_NULL_CIPHER_TOGGLE = "enable_null_cipher_toggle"; + + /** + * To apply the enforcement telephony feature and API + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) + public static final long ENABLE_FEATURE_MAPPING = 297989574L; + + private final Context mContext; + private final int mSubId; + @UnsupportedAppUsage + private SubscriptionManager mSubscriptionManager; + private TelephonyScanManager mTelephonyScanManager; + + /** Cached service handles, cleared by resetServiceHandles() at death */ + private static final Object sCacheLock = new Object(); + + /** @hide */ + private static boolean sServiceHandleCacheEnabled = true; + + @GuardedBy("sCacheLock") + private static ITelephony sITelephony; + @GuardedBy("sCacheLock") + private static IPhoneSubInfo sIPhoneSubInfo; + @GuardedBy("sCacheLock") + private static ISub sISub; + @GuardedBy("sCacheLock") + private static ISms sISms; + @GuardedBy("sCacheLock") + private static final DeathRecipient sServiceDeath = new DeathRecipient(); + + /** + * Cache key for a {@link PropertyInvalidatedCache} which maps from {@link PhoneAccountHandle} + * to subscription Id. The cache is initialized in {@code PhoneInterfaceManager}'s constructor + * when {@link PropertyInvalidatedCache#invalidateCache(String)} is called. + * The cache is cleared from {@code TelecomAccountRegistry#tearDown} when all phone accounts are + * removed from Telecom. + * @hide + */ + public static final String CACHE_KEY_PHONE_ACCOUNT_TO_SUBID = + "cache_key.telephony.phone_account_to_subid"; + private static final int CACHE_MAX_SIZE = 4; + + /** + * A {@link PropertyInvalidatedCache} which lives in an app's {@link TelephonyManager} instance. + * Caches any queries for a mapping between {@link PhoneAccountHandle} and {@code subscription + * id}. The cache may be invalidated from Telephony when phone account re-registration takes + * place. + */ + private PropertyInvalidatedCache mPhoneAccountHandleToSubIdCache = + new PropertyInvalidatedCache(CACHE_MAX_SIZE, + CACHE_KEY_PHONE_ACCOUNT_TO_SUBID) { + @Override + public Integer recompute(PhoneAccountHandle phoneAccountHandle) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getSubIdForPhoneAccountHandle(phoneAccountHandle, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + }; + + /** Enum indicating multisim variants + * DSDS - Dual SIM Dual Standby + * DSDA - Dual SIM Dual Active + * TSTS - Triple SIM Triple Standby + **/ + /** @hide */ + @UnsupportedAppUsage(implicitMember = + "values()[Landroid/telephony/TelephonyManager$MultiSimVariants;") + public enum MultiSimVariants { + @UnsupportedAppUsage + DSDS, + @UnsupportedAppUsage + DSDA, + @UnsupportedAppUsage + TSTS, + @UnsupportedAppUsage + UNKNOWN + }; + + /** @hide */ + @UnsupportedAppUsage + public TelephonyManager(Context context) { + this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + } + + /** @hide */ + @UnsupportedAppUsage + public TelephonyManager(Context context, int subId) { + mSubId = subId; + mContext = mergeAttributionAndRenouncedPermissions(context.getApplicationContext(), + context); + mSubscriptionManager = SubscriptionManager.from(mContext); + } + + /** @hide */ + @UnsupportedAppUsage + private TelephonyManager() { + mContext = null; + mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + + private static TelephonyManager sInstance = new TelephonyManager(); + + /** @hide + /* @deprecated - use getSystemService as described above */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static TelephonyManager getDefault() { + return sInstance; + } + + // This method takes the Application context and adds the attributionTag + // and renouncedPermissions from the given context. + private Context mergeAttributionAndRenouncedPermissions(Context to, Context from) { + Context contextToReturn = from; + if (to != null) { + if (!Objects.equals(from.getAttributionTag(), to.getAttributionTag())) { + contextToReturn = to.createAttributionContext(from.getAttributionTag()); + } else { + contextToReturn = to; + } + + Set renouncedPermissions = + from.getAttributionSource().getRenouncedPermissions(); + if (!renouncedPermissions.isEmpty()) { + if (to.getParams() != null) { + contextToReturn = contextToReturn.createContext( + new ContextParams.Builder(to.getParams()) + .setRenouncedPermissions(renouncedPermissions).build()); + } else { + contextToReturn = contextToReturn.createContext( + new ContextParams.Builder() + .setRenouncedPermissions(renouncedPermissions).build()); + } + } + } + return contextToReturn; + } + + private String getOpPackageName() { + // For legacy reasons the TelephonyManager has API for getting + // a static instance with no context set preventing us from + // getting the op package name. As a workaround we do a best + // effort and get the context from the current activity thread. + if (mContext != null) { + return mContext.getOpPackageName(); + } else { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + try { + return telephony.getCurrentPackageName(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + } + + private String getAttributionTag() { + // For legacy reasons the TelephonyManager has API for getting + // a static instance with no context set preventing us from + // getting the attribution tag. + if (mContext != null) { + return mContext.getAttributionTag(); + } + return null; + } + + private Set getRenouncedPermissions() { + // For legacy reasons the TelephonyManager has API for getting + // a static instance with no context set preventing us from + // getting the attribution source. + if (mContext != null) { + return mContext.getAttributionSource().getRenouncedPermissions(); + } + return Collections.emptySet(); + } + + /** + * Post a runnable to the BackgroundThread. + * + * Used to invoke user callbacks without calling into the caller's executor from the caller's + * calling thread context, for example to provide asynchronous error information that is + * generated locally (not over a binder thread). + * + *

This is not necessary unless you are invoking caller's code asynchronously from within + * the caller's thread context. + * + * @param r a runnable. + */ + private static void runOnBackgroundThread(@NonNull Runnable r) { + try { + BackgroundThread.getExecutor().execute(r); + } catch (RejectedExecutionException e) { + throw new IllegalStateException( + "Failed to post a callback from the caller's thread context.", e); + } + } + + /** + * Returns the multi SIM variant + * Returns DSDS for Dual SIM Dual Standby + * Returns DSDA for Dual SIM Dual Active + * Returns TSTS for Triple SIM Triple Standby + * Returns UNKNOWN for others + */ + /** {@hide} */ + @UnsupportedAppUsage + public MultiSimVariants getMultiSimConfiguration() { + String mSimConfig = + TelephonyProperties.multi_sim_config().orElse(""); + if (mSimConfig.equals("dsds")) { + return MultiSimVariants.DSDS; + } else if (mSimConfig.equals("dsda")) { + return MultiSimVariants.DSDA; + } else if (mSimConfig.equals("tsts")) { + return MultiSimVariants.TSTS; + } else { + return MultiSimVariants.UNKNOWN; + } + } + + /** + * Returns the number of phones available. + * Returns 0 if none of voice, sms, data is not supported + * Returns 1 for Single standby mode (Single SIM functionality). + * Returns 2 for Dual standby mode (Dual SIM functionality). + * Returns 3 for Tri standby mode (Tri SIM functionality). + * @deprecated Use {@link #getActiveModemCount} instead. + */ + @Deprecated + public int getPhoneCount() { + return getActiveModemCount(); + } + + /** + * Returns the number of logical modems currently configured to be activated. + * + * Returns 0 if none of voice, sms, data is not supported + * Returns 1 for Single standby mode (Single SIM functionality). + * Returns 2 for Dual standby mode (Dual SIM functionality). + * Returns 3 for Tri standby mode (Tri SIM functionality). + */ + public int getActiveModemCount() { + int modemCount = 1; + switch (getMultiSimConfiguration()) { + case UNKNOWN: + modemCount = 1; + // check for voice and data support, 0 if not supported + if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) { + modemCount = 0; + } + break; + case DSDS: + case DSDA: + modemCount = 2; + break; + case TSTS: + modemCount = 3; + break; + } + return modemCount; + } + + /** + * Return how many logical modem can be potentially active simultaneously, in terms of hardware + * capability. + * It might return different value from {@link #getActiveModemCount}. For example, for a + * dual-SIM capable device operating in single SIM mode (only one logical modem is turned on), + * {@link #getActiveModemCount} returns 1 while this API returns 2. + */ + public int getSupportedModemCount() { + return TelephonyProperties.max_active_modems().orElse(getActiveModemCount()); + } + + /** + * Gets the maximum number of SIMs that can be active, based on the device's multisim + * configuration. + * @return 1 for single-SIM, DSDS, and TSTS devices. 2 for DSDA devices. + * @hide + */ + @SystemApi + public int getMaxNumberOfSimultaneouslyActiveSims() { + switch (getMultiSimConfiguration()) { + case UNKNOWN: + case DSDS: + case TSTS: + return 1; + case DSDA: + return 2; + } + return 1; + } + + /** {@hide} */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static TelephonyManager from(Context context) { + return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + } + + /** + * Create a new TelephonyManager object pinned to the given subscription ID. + * + * @return a TelephonyManager that uses the given subId for all calls. + */ + public TelephonyManager createForSubscriptionId(int subId) { + // Don't reuse any TelephonyManager objects. + return new TelephonyManager(mContext, subId); + } + + /** + * Create a new TelephonyManager object pinned to the subscription ID associated with the given + * phone account. + * + * @return a TelephonyManager that uses the given phone account for all calls, or {@code null} + * if the phone account does not correspond to a valid subscription ID. + */ + @Nullable + public TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { + int subId = getSubscriptionId(phoneAccountHandle); + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + return null; + } + return new TelephonyManager(mContext, subId); + } + + /** {@hide} */ + @UnsupportedAppUsage + public boolean isMultiSimEnabled() { + return getPhoneCount() > 1; + } + + private static final int MAXIMUM_CALL_COMPOSER_PICTURE_SIZE = 80000; + + /** + * Indicates the maximum size of the call composure picture. + * + * Pictures sent via + * {@link #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver)} + * or {@link #uploadCallComposerPicture(Path, String, Executor, OutcomeReceiver)} must not + * exceed this size, or an error will be returned via the callback in those methods. + * + * @return Maximum file size in bytes. + */ + public static @BytesLong long getMaximumCallComposerPictureSize() { + return MAXIMUM_CALL_COMPOSER_PICTURE_SIZE; + } + + // + // Broadcast Intent actions + // + + /** + * Broadcast intent action indicating that the call state + * on the device has changed. + * + *

+ * The {@link #EXTRA_STATE} extra indicates the new call state. + * If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second + * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outgoing + * calls as a String. + *

+ * If the receiving app has + * {@link android.Manifest.permission#READ_CALL_LOG} and + * {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the + * broadcast twice; one with the {@link #EXTRA_INCOMING_NUMBER} populated with the phone number, + * and another with it blank. Due to the nature of broadcasts, you cannot assume the order + * in which these broadcasts will arrive, however you are guaranteed to receive two in this + * case. Apps which are interested in the {@link #EXTRA_INCOMING_NUMBER} can ignore the + * broadcasts where {@link #EXTRA_INCOMING_NUMBER} is not present in the extras (e.g. where + * {@link Intent#hasExtra(String)} returns {@code false}). + *

+ * This was a {@link android.content.Context#sendStickyBroadcast sticky} + * broadcast in version 1.0, but it is no longer sticky. + * Instead, use {@link #getCallState} to synchronously query the current call state. + * + * @see #EXTRA_STATE + * @see #EXTRA_INCOMING_NUMBER + * @see #getCallState + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final String ACTION_PHONE_STATE_CHANGED = + "android.intent.action.PHONE_STATE"; + + /** + * The Phone app sends this intent when a user opts to respond-via-message during an incoming + * call. By default, the device's default SMS app consumes this message and sends a text message + * to the caller. A third party app can also provide this functionality by consuming this Intent + * with a {@link android.app.Service} and sending the message using its own messaging system. + *

The intent contains a URI (available from {@link android.content.Intent#getData}) + * describing the recipient, using either the {@code sms:}, {@code smsto:}, {@code mms:}, + * or {@code mmsto:} URI schema. Each of these URI schema carry the recipient information the + * same way: the path part of the URI contains the recipient's phone number or a comma-separated + * set of phone numbers if there are multiple recipients. For example, {@code + * smsto:2065551234}.

+ * + *

The intent may also contain extras for the message text (in {@link + * android.content.Intent#EXTRA_TEXT}) and a message subject + * (in {@link android.content.Intent#EXTRA_SUBJECT}).

+ * + *

Note: + * The intent-filter that consumes this Intent needs to be in a {@link android.app.Service} + * that requires the + * permission {@link android.Manifest.permission#SEND_RESPOND_VIA_MESSAGE}.

+ *

For example, the service that receives this intent can be declared in the manifest file + * with an intent filter like this:

+ *
+     * <!-- Service that delivers SMS messages received from the phone "quick response" -->
+     * <service android:name=".HeadlessSmsSendService"
+     *          android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
+     *          android:exported="true" >
+     *   <intent-filter>
+     *     <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+     *     <category android:name="android.intent.category.DEFAULT" />
+     *     <data android:scheme="sms" />
+     *     <data android:scheme="smsto" />
+     *     <data android:scheme="mms" />
+     *     <data android:scheme="mmsto" />
+     *   </intent-filter>
+     * </service>
+ *

+ * Output: nothing. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String ACTION_RESPOND_VIA_MESSAGE = + "android.intent.action.RESPOND_VIA_MESSAGE"; + + /** + * The emergency dialer may choose to present activities with intent filters for this + * action as emergency assistance buttons that launch the activity when clicked. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_EMERGENCY_ASSISTANCE = + "android.telephony.action.EMERGENCY_ASSISTANCE"; + + /** + * A boolean meta-data value indicating whether the voicemail settings should be hidden in the + * call settings page launched by + * {@link android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS}. + * Dialer implementations (see {@link android.telecom.TelecomManager#getDefaultDialerPackage()}) + * which would also like to manage voicemail settings should set this meta-data to {@code true} + * in the manifest registration of their application. + * + * @see android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS + * @see #ACTION_CONFIGURE_VOICEMAIL + * @see #EXTRA_HIDE_PUBLIC_SETTINGS + */ + public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = + "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"; + + /** + * Open the voicemail settings activity to make changes to voicemail configuration. + * + *

+ * The {@link #EXTRA_PHONE_ACCOUNT_HANDLE} extra indicates which {@link PhoneAccountHandle} to + * configure voicemail. + * The {@link #EXTRA_HIDE_PUBLIC_SETTINGS} hides settings the dialer will modify through public + * API if set. + * + * @see #EXTRA_PHONE_ACCOUNT_HANDLE + * @see #EXTRA_HIDE_PUBLIC_SETTINGS + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_CONFIGURE_VOICEMAIL = + "android.telephony.action.CONFIGURE_VOICEMAIL"; + + /** + * The boolean value indicating whether the voicemail settings activity launched by {@link + * #ACTION_CONFIGURE_VOICEMAIL} should hide settings accessible through public API. This is + * used by dialer implementations which provides their own voicemail settings UI, but still + * needs to expose device specific voicemail settings to the user. + * + * @see #ACTION_CONFIGURE_VOICEMAIL + * @see #METADATA_HIDE_VOICEMAIL_SETTINGS_MENU + */ + public static final String EXTRA_HIDE_PUBLIC_SETTINGS = + "android.telephony.extra.HIDE_PUBLIC_SETTINGS"; + + /** + * @hide + */ + public static final boolean EMERGENCY_ASSISTANCE_ENABLED = true; + + /** + * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast + * for a String containing the new call state. + * + *

+ * Retrieve with + * {@link android.content.Intent#getStringExtra(String)}. + * + * @see #EXTRA_STATE_IDLE + * @see #EXTRA_STATE_RINGING + * @see #EXTRA_STATE_OFFHOOK + */ + public static final String EXTRA_STATE = PhoneConstants.STATE_KEY; + + /** + * Value used with {@link #EXTRA_STATE} corresponding to + * {@link #CALL_STATE_IDLE}. + */ + public static final String EXTRA_STATE_IDLE = PhoneConstants.State.IDLE.toString(); + + /** + * Value used with {@link #EXTRA_STATE} corresponding to + * {@link #CALL_STATE_RINGING}. + */ + public static final String EXTRA_STATE_RINGING = PhoneConstants.State.RINGING.toString(); + + /** + * Value used with {@link #EXTRA_STATE} corresponding to + * {@link #CALL_STATE_OFFHOOK}. + */ + public static final String EXTRA_STATE_OFFHOOK = PhoneConstants.State.OFFHOOK.toString(); + + /** + * Extra key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast + * for a String containing the incoming or outgoing phone number. + *

+ * This extra is only populated for receivers of the {@link #ACTION_PHONE_STATE_CHANGED} + * broadcast which have been granted the {@link android.Manifest.permission#READ_CALL_LOG} and + * {@link android.Manifest.permission#READ_PHONE_STATE} permissions. + *

+ * For incoming calls, the phone number is only guaranteed to be populated when the + * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_RINGING}. + * If the incoming caller is from an unknown number, the extra will be populated with an empty + * string. + * For outgoing calls, the phone number is only guaranteed to be populated when the + * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_OFFHOOK}. + *

+ * Retrieve with + * {@link android.content.Intent#getStringExtra(String)}. + *

+ * + * @deprecated Companion apps for wearable devices should use the {@link InCallService} API + * to retrieve the phone number for calls instead. Apps performing call screening should use + * the {@link CallScreeningService} API instead. + */ + @Deprecated + public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; + + /** + * Broadcast intent action indicating that call disconnect cause has changed. + * + *

+ * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause. + * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause. + * + *

+ * Requires the READ_PRECISE_PHONE_STATE permission. + * + * @see #EXTRA_DISCONNECT_CAUSE + * @see #EXTRA_PRECISE_DISCONNECT_CAUSE + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED = + "android.intent.action.CALL_DISCONNECT_CAUSE"; + + /** + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the disconnect cause. + * + * @see DisconnectCause + * + *

+ * Retrieve with + * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}. + * + * @deprecated Should use the {@link TelecomManager#EXTRA_DISCONNECT_CAUSE} instead. + * @hide + */ + @Deprecated + public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause"; + + /** + * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and + * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer + * containing the disconnect cause provided by the RIL. + * + * @see PreciseDisconnectCause + * + *

+ * Retrieve with + * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}. + * + * @hide + */ + public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause"; + + /** + * Broadcast intent action for letting the default dialer to know to show voicemail + * notification. + * + *

+ * The {@link #EXTRA_PHONE_ACCOUNT_HANDLE} extra indicates which {@link PhoneAccountHandle} the + * voicemail is received on. + * The {@link #EXTRA_NOTIFICATION_COUNT} extra indicates the total numbers of unheard + * voicemails. + * The {@link #EXTRA_VOICEMAIL_NUMBER} extra indicates the voicemail number if available. + * The {@link #EXTRA_CALL_VOICEMAIL_INTENT} extra is a {@link android.app.PendingIntent} that + * will call the voicemail number when sent. This extra will be empty if the voicemail number + * is not set, and {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} will be set instead. + * The {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} extra is a + * {@link android.app.PendingIntent} that will launch the voicemail settings. This extra is only + * available when the voicemail number is not set. + * The {@link #EXTRA_IS_REFRESH} extra indicates whether the notification is a refresh or a new + * notification. + * + * @see #EXTRA_PHONE_ACCOUNT_HANDLE + * @see #EXTRA_NOTIFICATION_COUNT + * @see #EXTRA_VOICEMAIL_NUMBER + * @see #EXTRA_CALL_VOICEMAIL_INTENT + * @see #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT + * @see #EXTRA_IS_REFRESH + */ + public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = + "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION"; + + /** + * The extra used with an {@link #ACTION_CONFIGURE_VOICEMAIL} and + * {@link #ACTION_SHOW_VOICEMAIL_NOTIFICATION} {@code Intent} to specify the + * {@link PhoneAccountHandle} the configuration or notification is for. + *

+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}. + */ + public static final String EXTRA_PHONE_ACCOUNT_HANDLE = + "android.telephony.extra.PHONE_ACCOUNT_HANDLE"; + + /** + * The number of voice messages associated with the notification. + */ + public static final String EXTRA_NOTIFICATION_COUNT = + "android.telephony.extra.NOTIFICATION_COUNT"; + + /** + * The voicemail number. + */ + public static final String EXTRA_VOICEMAIL_NUMBER = + "android.telephony.extra.VOICEMAIL_NUMBER"; + + /** + * The intent to call voicemail. + */ + public static final String EXTRA_CALL_VOICEMAIL_INTENT = + "android.telephony.extra.CALL_VOICEMAIL_INTENT"; + + /** + * The intent to launch voicemail settings. + */ + public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = + "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT"; + + /** + * Boolean value representing whether the {@link + * TelephonyManager#ACTION_SHOW_VOICEMAIL_NOTIFICATION} is new or a refresh of an existing + * notification. Notification refresh happens after reboot or connectivity changes. The user has + * already been notified for the voicemail so it should not alert the user, and should not be + * shown again if the user has dismissed it. + */ + public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH"; + + /** + * {@link android.telecom.Connection} event used to indicate that an IMS call has be + * successfully handed over from WIFI to LTE. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE = + "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE"; + + /** + * {@link android.telecom.Connection} event used to indicate that an IMS call has be + * successfully handed over from LTE to WIFI. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI = + "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI"; + + /** + * {@link android.telecom.Connection} event used to indicate that an IMS call failed to be + * handed over from LTE to WIFI. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_HANDOVER_TO_WIFI_FAILED = + "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED"; + + /** + * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to + * audio because the data limit was reached. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_DOWNGRADE_DATA_LIMIT_REACHED = + "android.telephony.event.EVENT_DOWNGRADE_DATA_LIMIT_REACHED"; + + /** + * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to + * audio because the data was disabled. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_DOWNGRADE_DATA_DISABLED = + "android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED"; + + /** + * {@link android.telecom.Connection} event used to indicate that the InCall UI should notify + * the user when an international call is placed while on WFC only. + *

+ * Used when the carrier config value + * {@link CarrierConfigManager#KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL} is true, the device + * is on WFC (VoLTE not available) and an international number is dialed. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC = + "android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC"; + + /** + * {@link android.telecom.Connection} event used to indicate that an outgoing call has been + * forwarded to another number. + *

+ * Sent in response to an IMS supplementary service notification indicating the call has been + * forwarded. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to be null when this connection event is used. + * @hide + */ + public static final String EVENT_CALL_FORWARDED = + "android.telephony.event.EVENT_CALL_FORWARDED"; + + /** + * {@link android.telecom.Connection} event used to indicate that a supplementary service + * notification has been received. + *

+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}. + * The {@link Bundle} parameter is expected to include the following extras: + *

    + *
  • {@link #EXTRA_NOTIFICATION_TYPE} - the notification type.
  • + *
  • {@link #EXTRA_NOTIFICATION_CODE} - the notification code.
  • + *
  • {@link #EXTRA_NOTIFICATION_MESSAGE} - human-readable message associated with the + * supplementary service notification.
  • + *
+ * @hide + */ + public static final String EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION = + "android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION"; + + /** + * Event reported from the Telephony stack to indicate that the {@link Connection} is not + * able to find any network and likely will not get connected. Upon receiving this event, + * the dialer app should start the app included in the extras bundle of this event if satellite + * is provisioned. + *

+ * The dialer app receives this event via + * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. + *

+ * The {@link Bundle} parameter is guaranteed to include the following extras if the below + * conditions are met: + *

    + *
  • {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE} - the recommending handover + * type.
  • + *
  • {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT} - the {@link PendingIntent} + * which will be launched by the Dialer app when receiving this connection event.
  • + *
+ *

+ * If the device is connected to satellite via carrier within the hysteresis time defined by + * the carrier config + * {@link CarrierConfigManager#KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT}, the component of + * the {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT} will be set to the default SMS + * app. + *

+ * Otherwise, if the overlay config {@code config_oem_enabled_satellite_handover_app} is + * present, the app defined by this config will be used as the component of the + * {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT}. If this overlay config is empty, + * {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT} will not be included in the event + * {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE}. + */ + @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + public static final String EVENT_DISPLAY_EMERGENCY_MESSAGE = + "android.telephony.event.DISPLAY_EMERGENCY_MESSAGE"; + + /** + * Integer extra key used with {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE} which indicates + * the type of handover from emergency call to satellite messaging. + *

+ * Will be either + * android.telephony.satellite.SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS + * or + * android.telephony.satellite.SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 + *

+ * Set in the extras for the {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE} connection event. + */ + @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + public static final String EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE = + "android.telephony.extra.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE"; + + /** + * Extra key used with the {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE} for a {@link PendingIntent} + * which will be launched by the Dialer app. + */ + @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + public static final String EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT = + "android.telephony.extra.EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT"; + + /** + * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates + * the type of supplementary service notification which occurred. + * Will be either + * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_1} + * or + * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_2} + *

+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event. + * @hide + */ + public static final String EXTRA_NOTIFICATION_TYPE = + "android.telephony.extra.NOTIFICATION_TYPE"; + + /** + * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates + * the supplementary service notification which occurred. + *

+ * Depending on the {@link #EXTRA_NOTIFICATION_TYPE}, the code will be one of the {@code CODE_*} + * codes defined in {@link com.android.internal.telephony.gsm.SuppServiceNotification}. + *

+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event. + * @hide + */ + public static final String EXTRA_NOTIFICATION_CODE = + "android.telephony.extra.NOTIFICATION_CODE"; + + /** + * {@link CharSequence} extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} + * which contains a human-readable message which can be displayed to the user for the + * supplementary service notification. + *

+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event. + * @hide + */ + public static final String EXTRA_NOTIFICATION_MESSAGE = + "android.telephony.extra.NOTIFICATION_MESSAGE"; + + /* Visual voicemail protocols */ + + /** + * The OMTP protocol. + */ + public static final String VVM_TYPE_OMTP = "vvm_type_omtp"; + + /** + * A flavor of OMTP protocol with a different mobile originated (MO) format + */ + public static final String VVM_TYPE_CVVM = "vvm_type_cvvm"; + + /** + * Key in bundle returned by {@link #getVisualVoicemailPackageName()}, indicating whether visual + * voicemail was enabled or disabled by the user. If the user never explicitly changed this + * setting, this key will not exist. + * + * @see #getVisualVoicemailSettings() + * @hide + */ + @SystemApi + public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = + "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; + + /** + * Key in bundle returned by {@link #getVisualVoicemailPackageName()}, indicating the voicemail + * access PIN scrambled during the auto provisioning process. The user is expected to reset + * their PIN if this value is not {@code null}. + * + * @see #getVisualVoicemailSettings() + * @hide + */ + @SystemApi + public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = + "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; + + /** + * Broadcast action to be received by Broadcast receivers. + * + * Indicates multi-SIM configuration is changed. For example, it changed + * from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode. + * + * It doesn't indicate how many subscriptions are actually active, or which states SIMs are, + * or that all steps during multi-SIM change are done. To know those information you still need + * to listen to SIM_STATE changes or active subscription changes. + * + * See extra of {@link #EXTRA_ACTIVE_SIM_SUPPORTED_COUNT} for updated value. + */ + public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = + "android.telephony.action.MULTI_SIM_CONFIG_CHANGED"; + + + /** + * The number of active SIM supported by current multi-SIM config. It's not related to how many + * SIM/subscriptions are currently active. + * + * Same value will be returned by {@link #getActiveModemCount()}. + * + * For single SIM mode, it's 1. + * For DSDS or DSDA mode, it's 2. + * For triple-SIM mode, it's 3. + * + * Extra of {@link #ACTION_MULTI_SIM_CONFIG_CHANGED}. + * + * type: integer + */ + public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = + "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT"; + + /** + * @hide + */ + public static final String USSD_RESPONSE = "USSD_RESPONSE"; + + /** + * USSD return code success. + * @hide + */ + public static final int USSD_RETURN_SUCCESS = 100; + + /** + * Failed code returned when the mobile network has failed to complete a USSD request. + *

+ * Returned via {@link TelephonyManager.UssdResponseCallback#onReceiveUssdResponseFailed( + * TelephonyManager, String, int)}. + */ + public static final int USSD_RETURN_FAILURE = -1; + + /** + * Failure code returned when a USSD request has failed to execute because the Telephony + * service is unavailable. + *

+ * Returned via {@link TelephonyManager.UssdResponseCallback#onReceiveUssdResponseFailed( + * TelephonyManager, String, int)}. + */ + public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; + + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which leaves the roaming + * mode set to the radio default or to the user's preference if they've indicated one. + */ + public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which only permits + * connections on home networks. + */ + public static final int CDMA_ROAMING_MODE_HOME = 0; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on + * affiliated networks. + */ + public static final int CDMA_ROAMING_MODE_AFFILIATED = 1; + /** + * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on + * any network. + */ + public static final int CDMA_ROAMING_MODE_ANY = 2; + + /** @hide */ + @IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = { + CDMA_ROAMING_MODE_RADIO_DEFAULT, + CDMA_ROAMING_MODE_HOME, + CDMA_ROAMING_MODE_AFFILIATED, + CDMA_ROAMING_MODE_ANY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CdmaRoamingMode{} + + /** + * An unknown carrier id. It could either be subscription unavailable or the subscription + * carrier cannot be recognized. Unrecognized carriers here means + * {@link #getSimOperator() MCC+MNC} cannot be identified. + */ + public static final int UNKNOWN_CARRIER_ID = -1; + + /** + * An unknown carrier id list version. + * @hide + */ + @TestApi + public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; + + /** + * Broadcast Action: The subscription carrier identity has changed. + * This intent could be sent on the following events: + *

    + *
  • Subscription absent. Carrier identity could change from a valid id to + * {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
  • + *
  • Subscription loaded. Carrier identity could change from + * {@link TelephonyManager#UNKNOWN_CARRIER_ID} to a valid id.
  • + *
  • The subscription carrier is recognized after a remote update.
  • + *
+ * The intent will have the following extra values: + *
    + *
  • {@link #EXTRA_CARRIER_ID} The up-to-date carrier id of the current subscription id. + *
  • + *
  • {@link #EXTRA_CARRIER_NAME} The up-to-date carrier name of the current subscription. + *
  • + *
  • {@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier + * identity. + *
  • + *
+ *

This is a protected intent that can only be sent by the system. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = + "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED"; + + /** + * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates + * the updated carrier id returned by {@link TelephonyManager#getSimCarrierId()}. + *

Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or + * the carrier cannot be identified. + */ + public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID"; + + /** + * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which + * indicates the updated carrier name of the current subscription. + * @see TelephonyManager#getSimCarrierIdName() + *

Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID}, + * usually the brand name of the subsidiary (e.g. T-Mobile). + */ + public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME"; + + /** + * Broadcast Action: The subscription specific carrier identity has changed. + * + * A specific carrier ID returns the fine-grained carrier ID of the current subscription. + * It can represent the fact that a carrier may be in effect an aggregation of other carriers + * (ie in an MVNO type scenario) where each of these specific carriers which are used to make + * up the actual carrier service may have different carrier configurations. + * A specific carrier ID could also be used, for example, in a scenario where a carrier requires + * different carrier configuration for different service offering such as a prepaid plan. + * + * the specific carrier ID would be used for configuration purposes, but apps wishing to know + * about the carrier itself should use the regular carrier ID returned by + * {@link #getSimCarrierId()}. + * + *

Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be + * sent on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} while its also + * possible to be sent without {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} when + * specific carrier ID changes while carrier ID remains the same. + * e.g, the same subscription switches to different IMSI could potentially change its + * specific carrier ID while carrier id remains the same. + * @see #getSimSpecificCarrierId() + * @see #getSimCarrierId() + * + * The intent will have the following extra values: + *

    + *
  • {@link #EXTRA_SPECIFIC_CARRIER_ID} The up-to-date specific carrier id of the + * current subscription. + *
  • + *
  • {@link #EXTRA_SPECIFIC_CARRIER_NAME} The up-to-date name of the specific carrier id. + *
  • + *
  • {@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier + * identity. + *
  • + *
+ *

This is a protected intent that can only be sent by the system. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED = + "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED"; + + /** + * An int extra used with {@link #ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED} which + * indicates the updated specific carrier id returned by + * {@link TelephonyManager#getSimSpecificCarrierId()}. Note, its possible specific carrier id + * changes while {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same + * e.g, when subscription switch to different IMSIs. + *

Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or + * the carrier cannot be identified. + */ + public static final String EXTRA_SPECIFIC_CARRIER_ID = + "android.telephony.extra.SPECIFIC_CARRIER_ID"; + + /** + * An string extra used with {@link #ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED} + * which indicates the updated specific carrier name returned by + * {@link TelephonyManager#getSimSpecificCarrierIdName()}. + *

it's a user-facing name of the specific carrier id {@link #EXTRA_SPECIFIC_CARRIER_ID} + * e.g, Tracfone-AT&T + */ + public static final String EXTRA_SPECIFIC_CARRIER_NAME = + "android.telephony.extra.SPECIFIC_CARRIER_NAME"; + + /** + * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the + * subscription which has changed; or in general whenever a subscription ID needs specified. + */ + public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID"; + + /** + * Broadcast Action: The Service Provider string(s) have been updated. Activities or + * services that use these strings should update their display. + * + *

The intent will have the following extra values: + *

+ *
{@link #EXTRA_SHOW_PLMN}
+ *
Boolean that indicates whether the PLMN should be shown.
+ *
{@link #EXTRA_PLMN}
+ *
The operator name of the registered network, as a string.
+ *
{@link #EXTRA_SHOW_SPN}
+ *
Boolean that indicates whether the SPN should be shown.
+ *
{@link #EXTRA_SPN}
+ *
The service provider name, as a string.
+ *
{@link #EXTRA_DATA_SPN}
+ *
The service provider name for data service, as a string.
+ *
+ * + * Note that {@link #EXTRA_SHOW_PLMN} may indicate that {@link #EXTRA_PLMN} should be displayed, + * even though the value for {@link #EXTRA_PLMN} is null. This can happen, for example, if the + * phone has not registered to a network yet. In this case the receiver may substitute an + * appropriate placeholder string (eg, "No service"). + * + * It is recommended to display {@link #EXTRA_PLMN} before / above {@link #EXTRA_SPN} if + * both are displayed. + * + *

Note: this is a protected intent that can only be sent by the system. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SERVICE_PROVIDERS_UPDATED = + "android.telephony.action.SERVICE_PROVIDERS_UPDATED"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * whether the PLMN should be shown. + * @hide + */ + public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the operator name of the registered network. + * @hide + */ + public static final String EXTRA_PLMN = "android.telephony.extra.PLMN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * whether the PLMN should be shown. + * @hide + */ + public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the service provider name. + * @hide + */ + public static final String EXTRA_SPN = "android.telephony.extra.SPN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the service provider name for data service. + * @hide + */ + public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN"; + + /** + * Broadcast intent action indicating that when data stall recovery is attempted by Telephony, + * intended for report every data stall recovery step attempted. + * + *

+ * The {@link #EXTRA_RECOVERY_ACTION} extra indicates the action associated with the data + * stall recovery. + * The phone id where the data stall recovery is attempted. + * + *

+ * Requires the READ_PHONE_STATE permission. + * + *

+ * This is a protected intent that can only be sent by the system. + * + * @see #EXTRA_RECOVERY_ACTION + * + * @hide + */ + // TODO(b/78370030) : Restrict this to system applications only + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final String ACTION_DATA_STALL_DETECTED = + "android.intent.action.DATA_STALL_DETECTED"; + + /** + * A service action that identifies + * a {@link android.service.carrier.CarrierMessagingClientService} subclass in the + * AndroidManifest.xml. + * + *

See {@link android.service.carrier.CarrierMessagingClientService} for the details. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = + "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; + + /** + * An int extra used with {@link #ACTION_DATA_STALL_DETECTED} to indicate the + * action associated with the data stall recovery. + * + * @see #ACTION_DATA_STALL_DETECTED + * + * @hide + */ + public static final String EXTRA_RECOVERY_ACTION = "recoveryAction"; + + private static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000; + + /** + * Intent sent when an error occurs that debug tools should log and possibly take further + * action such as capturing vendor-specific logs. + * + * A privileged application that reads these events should take appropriate vendor-specific + * action to record the event and collect further information to assist in analysis, debugging, + * and resolution of any associated issue. + * + *

This event should not be used for generic logging or diagnostic monitoring purposes and + * should generally be sent at a low rate. Instead, this mechanism should be used for the + * framework to notify a debugging application that an event (such as a bug) has occured + * within the framework if that event should trigger the collection and preservation of other + * more detailed device state for debugging. + * + *

At most one application can receive these events and should register a receiver in + * in the application manifest. For performance reasons, if no application to receive these + * events is detected at boot, then these events will not be sent. + * + *

Each event will include an {@link EXTRA_ANOMALY_ID} that will uniquely identify the + * event that has occurred. Each event will be sent to the diagnostic monitor only once per + * boot cycle (as another optimization). + * + * @see #EXTRA_ANOMALY_ID + * @see #EXTRA_ANOMALY_DESCRIPTION + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final String ACTION_ANOMALY_REPORTED = + "android.telephony.action.ANOMALY_REPORTED"; + + /** + * An arbitrary ParcelUuid which should be consistent for each occurrence of a DebugEvent. + * + * This field must be included in all {@link ACTION_ANOMALY_REPORTED} events. + * + * @see #ACTION_ANOMALY_REPORTED + * @hide + */ + @SystemApi + public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID"; + + /** + * A freeform string description of the Anomaly. + * + * This field is optional for all {@link ACTION_ANOMALY_REPORTED}s, as a guideline should not + * exceed 80 characters, and should be as short as possible to convey the essence of the event. + * + * @see #ACTION_ANOMALY_REPORTED + * @hide + */ + @SystemApi + public static final String EXTRA_ANOMALY_DESCRIPTION = + "android.telephony.extra.ANOMALY_DESCRIPTION"; + + /** + * Broadcast intent sent to indicate primary (non-opportunistic) subscription list has changed. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED = + "android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED"; + + /** + * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED} + * to indicate what type of SIM selection is needed. + * + * @hide + */ + public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = + "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE"; + + /** @hide */ + @IntDef({ + EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE, + EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA, + EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE, + EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS, + EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DefaultSubscriptionSelectType{} + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate there's no need to re-select any default subscription. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate there's a need to select default data subscription. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate there's a need to select default voice call subscription. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate there's a need to select default sms subscription. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate user to decide whether current SIM should be preferred for all + * data / voice / sms. {@link #EXTRA_SUBSCRIPTION_ID} will specified to indicate + * which subscription should be the default subscription. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate that default subscription for data/sms/voice is now determined, that + * it should dismiss any dialog or pop-ups that is asking user to select default sub. + * This is used when, for example, opportunistic subscription is configured. At that + * time the primary becomes default sub there's no need to ask user to select anymore. + * @hide + */ + public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS = 5; + + /** + * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED} + * to indicate if the SIM combination in DSDS has limitation or compatible issue. + * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios. + * + * @hide + */ + public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = + "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE"; + + /** @hide */ + @IntDef({ + EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE, + EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SimCombinationWarningType{} + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate there's no SIM combination warning. + * @hide + */ + public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate two active SIMs are both CDMA hence there might be functional limitation. + * @hide + */ + public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; + + /** + * String intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED} + * to indicate what's the name of SIM combination it has limitation or compatible issue. + * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the + * name will be "operator1 & operator2". + * + * @hide + */ + public static final String EXTRA_SIM_COMBINATION_NAMES = + "android.telephony.extra.SIM_COMBINATION_NAMES"; + + /** + *

Broadcast Action: The emergency callback mode is changed. + *

    + *
  • EXTRA_PHONE_IN_ECM_STATE - A boolean value,true=phone in ECM, + * false=ECM off
  • + *
+ *

+ * You can not receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, + * android.content.IntentFilter) Context.registerReceiver()}. + * + *

This is a protected intent that can only be sent by the system. + * + * @see #EXTRA_PHONE_IN_ECM_STATE + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @SuppressLint("ActionValue") + public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = + "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED"; + + + /** + * Extra included in {@link #ACTION_EMERGENCY_CALLBACK_MODE_CHANGED}. + * Indicates whether the phone is in an emergency phone state. + * + * @hide + */ + @SystemApi + public static final String EXTRA_PHONE_IN_ECM_STATE = + "android.telephony.extra.PHONE_IN_ECM_STATE"; + + /** + * Broadcast action sent when a data connection is redirected with validation failure. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values: + *

    + *
  • {@link #EXTRA_APN_TYPE}
  • An integer indicating the apn type.
    + *
  • {@link #EXTRA_REDIRECTION_URL}
  • A string indicating the redirection url
    + *
  • {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
  • + *
    The subscription ID on which the validation failure happened.
    + *
+ *

This is a protected intent that can only be sent by the system.

+ */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = + "android.telephony.action.CARRIER_SIGNAL_REDIRECTED"; + + /** + * Broadcast action sent when a data connection setup fails. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values: + *
    + *
  • {@link #EXTRA_APN_TYPE}
  • An integer indicating the apn type.
    + *
  • {@link #EXTRA_DATA_FAIL_CAUSE}
  • A integer indicating the data fail cause.
    + *
  • {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
  • + *
    The subscription ID on which the data setup failure happened.
    + *
+ *

This is a protected intent that can only be sent by the system.

+ */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = + "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + + /** + * Broadcast action sent when a PCO value becomes available from the modem. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values:

+ *
    + *
  • {@link #EXTRA_APN_TYPE}
  • An integer indicating the apn type.
    + *
  • {@link #EXTRA_APN_PROTOCOL}
  • An integer indicating the protocol of the apn + * connection
    + *
  • {@link #EXTRA_PCO_ID}
  • An integer indicating the PCO id for the data.
    + *
  • {@link #EXTRA_PCO_VALUE}
  • A byte array of PCO data read from modem.
    + *
  • {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
  • + *
    The subscription ID for which the PCO info was received.
    + *
+ *

This is a protected intent that can only be sent by the system.

+ */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = + "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE"; + + /** + * Broadcast action sent when the availability of the system default network changes. + * + * @see ConnectivityManager#registerDefaultNetworkCallback(ConnectivityManager.NetworkCallback) + * + * This action is intended for carrier apps to set/reset carrier actions. It is only sent to the + * carrier apps specified in the carrier config for the subscription ID attached to this intent. + * + * The intent will have the following extra values:

+ *
    + *
  • {@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}
  • + *
    {@code true} if the default network is now available, {@code false} otherwise.
    + *
  • {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
  • + *
    The subscription ID on which the default network availability changed.
    + *
+ *

This is a protected intent that can only be sent by the system.

+ */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = + "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + + /** + * Broadcast action sent when carrier apps should reset their internal state. + * + * Sent when certain events such as turning on/off mobile data, removing the SIM, etc. require + * carrier apps to reset their state. + * + * This action is intended to signal carrier apps to perform cleanup operations. It is only sent + * to the carrier apps specified in the carrier config for the subscription ID attached to + * this intent. + * + * The intent will have the following extra values:

+ *
    + *
  • {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
  • + *
    The subscription ID for which state should be reset.
    + *
+ *

This is a protected intent that can only be sent by the system.

+ */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CARRIER_SIGNAL_RESET = + "android.telephony.action.CARRIER_SIGNAL_RESET"; + + /** + * String extra containing the redirection URL sent with + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}. + */ + public static final String EXTRA_REDIRECTION_URL = "android.telephony.extra.REDIRECTION_URL"; + + /** + * An integer extra containing the data fail cause. + * + * Sent with {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. See {@link DataFailCause} + * for a list of possible values. + */ + public static final String EXTRA_DATA_FAIL_CAUSE = "android.telephony.extra.DATA_FAIL_CAUSE"; + + /** + * An integer extra containing the APN type. + * + * Sent with the {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}, and {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} + * broadcasts. + * See the {@code TYPE_} constants in {@link ApnSetting} for a list of possible values. + */ + public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE"; + + /** + * An integer extra containing the protocol of the apn connection. + * + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. + * See the {@code PROTOCOL_*} constants in {@link ApnSetting} for a list of possible values. + */ + public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL"; + + /** + * An integer extra indicating the ID for the PCO data. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. + */ + public static final String EXTRA_PCO_ID = "android.telephony.extra.PCO_ID"; + + /** + * A byte array extra containing PCO data read from the modem. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. + */ + public static final String EXTRA_PCO_VALUE = "android.telephony.extra.PCO_VALUE"; + + /** + * A boolean extra indicating the availability of the default network. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcast. + */ + public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = + "android.telephony.extra.DEFAULT_NETWORK_AVAILABLE"; + + /** + *

Broadcast Action: The emergency call state is changed. + *

    + *
  • EXTRA_PHONE_IN_EMERGENCY_CALL - A boolean value, true if phone in emergency + * call, false otherwise
  • + *
+ *

+ * You can not receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, + * android.content.IntentFilter) Context.registerReceiver()}. + * + *

This is a protected intent that can only be sent by the system. + * + * @see #EXTRA_PHONE_IN_EMERGENCY_CALL + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @SuppressLint("ActionValue") + public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = + "android.intent.action.EMERGENCY_CALL_STATE_CHANGED"; + + + /** + * Extra included in {@link #ACTION_EMERGENCY_CALL_STATE_CHANGED}. + * It indicates whether the phone is making an emergency call. + * + * @hide + */ + @SystemApi + public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = + "android.telephony.extra.PHONE_IN_EMERGENCY_CALL"; + + /** + *

Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms + *

. + * This is to pop up a notice to show user that the phone is in emergency callback mode + * and data calls and outgoing sms are blocked. + * + *

This is a protected intent that can only be sent by the system. + * + * @hide + */ + @SystemApi + public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = + "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS"; + + /** + * Broadcast Action: The default data subscription has changed in a multi-SIM device. + * This has the following extra values:

+ *
    + *
  • subscription - A int, the current data default subscription.
  • + *
+ * + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = + "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED"; + + /** + * Broadcast Action: The default voice subscription has changed in a mult-SIm device. + * This has the following extra values:

+ *
    + *
  • subscription - A int, the current voice default subscription.
  • + *
+ * + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = + "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED"; + + /** + * Broadcast Action: This triggers a client initiated OMA-DM session to the OMA server. + *

+ * Open Mobile Alliance (OMA) Device Management (DM). + * + * This intent is used by the system components to trigger OMA-DM + * + * @hide + */ + @SystemApi + @SuppressLint("ActionValue") + public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = + "com.android.omadm.service.CONFIGURATION_UPDATE"; + + /** + * Activity action: Show setting to reset mobile networks. + * + *

On devices with a settings activity to reset mobile networks, the activity should be + * launched without additional permissions. + * + *

On some devices, this settings activity may not exist. Callers should ensure that this + * case is appropriately handled. + */ + @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS) + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = + "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; + + // + // + // Device Info + // + // + + /** + * Returns the software version number for the device, for example, + * the IMEI/SV for GSM phones. Return null if the software version is + * not available. + *

+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + @Nullable + public String getDeviceSoftwareVersion() { + return getDeviceSoftwareVersion(getSlotIndex()); + } + + /** + * Returns the software version number for the device, for example, + * the IMEI/SV for GSM phones. Return null if the software version is + * not available. + *

+ * Requires Permission: READ_PHONE_STATE. + * + * @param slotIndex of which deviceID is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + @Nullable + public String getDeviceSoftwareVersion(int slotIndex) { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + try { + return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the unique device ID, for example, the IMEI for GSM and the MEID + * or ESN for CDMA phones. Return null if device ID is not available. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any + * active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns + * MEID for CDMA. + */ + @Deprecated + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public String getDeviceId() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the unique device ID of a subscription, for example, the IMEI for + * GSM and the MEID for CDMA phones. Return null if device ID is not available. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any + * active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @param slotIndex of which deviceID is returned + * + * @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns + * MEID for CDMA. + */ + @Deprecated + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public String getDeviceId(int slotIndex) { + // FIXME this assumes phoneId == slotIndex + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not + * available. + * + * See {@link #getImei(int)} for details on the required permissions and behavior + * when the caller does not hold sufficient permissions. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) + public String getImei() { + return getImei(getSlotIndex()); + } + + /** + * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not + * available. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any + * active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
  • If the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @param slotIndex of which IMEI is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) + public String getImei(int slotIndex) { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + try { + return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not + * available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) + @Nullable + public String getTypeAllocationCode() { + return getTypeAllocationCode(getSlotIndex()); + } + + /** + * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not + * available. + * + * @param slotIndex of which Type Allocation Code is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) + @Nullable + public String getTypeAllocationCode(int slotIndex) { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + try { + return telephony.getTypeAllocationCodeForSlot(slotIndex); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any + * active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getMeid() { + return getMeid(getSlotIndex()); + } + + /** + * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any + * active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @param slotIndex of which MEID is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getMeid(int slotIndex) { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + try { + String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(), + getAttributionTag()); + if (TextUtils.isEmpty(meid)) { + Log.d(TAG, "getMeid: return null because MEID is not available"); + return null; + } + return meid; + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not + * available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + @Nullable + public String getManufacturerCode() { + return getManufacturerCode(getSlotIndex()); + } + + /** + * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not + * available. + * + * @param slotIndex of which Type Allocation Code is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + @Nullable + public String getManufacturerCode(int slotIndex) { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + try { + return telephony.getManufacturerCodeForSlot(slotIndex); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the Network Access Identifier (NAI). Return null if NAI is not available. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getNai() { + return getNaiBySubscriberId(getSubId()); + } + + private String getNaiBySubscriberId(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Rlog.v(TAG, "Nai = " + nai); + } + return nai; + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * Returns the current location of the device. + *

+ * If there is only one radio in the device and that radio has an LTE connection, + * this method will return null. The implementation must not to try add LTE + * identifiers into the existing cdma/gsm classes. + *

+ * @return Current location of the device or null if not available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * + * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public CellLocation getCellLocation() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + Rlog.d(TAG, "getCellLocation returning null because telephony is null"); + return null; + } + + CellIdentity cellIdentity = telephony.getCellLocation(mContext.getOpPackageName(), + mContext.getAttributionTag()); + CellLocation cl = cellIdentity.asCellLocation(); + if (cl == null || cl.isEmpty()) { + Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or" + + " phone type doesn't match CellLocation type"); + return null; + } + + return cl; + } catch (RemoteException ex) { + Rlog.d(TAG, "getCellLocation returning null due to RemoteException " + ex); + return null; + } + } + + /** + * Returns the neighboring cell information of the device. + * + * @return List of NeighboringCellInfo or null if info unavailable. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @removed + * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information + * from NeighboringCellInfo, including LTE cell information. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public List getNeighboringCellInfo() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getNeighboringCellInfo(mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** No phone radio. */ + public static final int PHONE_TYPE_NONE = PhoneConstants.PHONE_TYPE_NONE; + /** Phone radio is GSM. */ + public static final int PHONE_TYPE_GSM = PhoneConstants.PHONE_TYPE_GSM; + /** Phone radio is CDMA. */ + public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA; + /** Phone is via SIP. */ + public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP; + + /** + * Phone is via IMS. + * + * @hide + */ + public static final int PHONE_TYPE_IMS = PhoneConstants.PHONE_TYPE_IMS; + + /** + * Phone is via Third Party. + * + * @hide + */ + public static final int PHONE_TYPE_THIRD_PARTY = PhoneConstants.PHONE_TYPE_THIRD_PARTY; + + /** + * Returns the current phone type. + * TODO: This is a last minute change and hence hidden. + * + * @see #PHONE_TYPE_NONE + * @see #PHONE_TYPE_GSM + * @see #PHONE_TYPE_CDMA + * @see #PHONE_TYPE_SIP + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + * {@hide} + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public int getCurrentPhoneType() { + return getCurrentPhoneType(getSubId()); + } + + /** + * Returns a constant indicating the device phone type for a subscription. + * + * @see #PHONE_TYPE_NONE + * @see #PHONE_TYPE_GSM + * @see #PHONE_TYPE_CDMA + * + * @param subId for which phone type is returned + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public int getCurrentPhoneType(int subId) { + int phoneId; + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + // if we don't have any sims, we don't have subscriptions, but we + // still may want to know what type of phone we've got. + phoneId = 0; + } else { + phoneId = SubscriptionManager.getPhoneId(subId); + } + + return getCurrentPhoneTypeForSlot(phoneId); + } + + /** + * See getCurrentPhoneType. + * + * @hide + */ + public int getCurrentPhoneTypeForSlot(int slotIndex) { + try{ + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getActivePhoneTypeForSlot(slotIndex); + } else { + // This can happen when the ITelephony interface is not up yet. + return getPhoneTypeFromProperty(slotIndex); + } + } catch (RemoteException ex) { + // This shouldn't happen in the normal case, as a backup we + // read from the system property. + return getPhoneTypeFromProperty(slotIndex); + } catch (NullPointerException ex) { + // This shouldn't happen in the normal case, as a backup we + // read from the system property. + return getPhoneTypeFromProperty(slotIndex); + } + } + + /** + * Returns a constant indicating the device phone type. This + * indicates the type of radio used to transmit voice calls. + * + * @see #PHONE_TYPE_NONE + * @see #PHONE_TYPE_GSM + * @see #PHONE_TYPE_CDMA + * @see #PHONE_TYPE_SIP + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public int getPhoneType() { + if (!isVoiceCapable()) { + return PHONE_TYPE_NONE; + } + return getCurrentPhoneType(); + } + + /** {@hide} */ + @UnsupportedAppUsage + private int getPhoneTypeFromProperty(int phoneId) { + Integer type = getTelephonyProperty( + phoneId, TelephonyProperties.current_active_phone(), null); + if (type != null) return type; + return getPhoneTypeFromNetworkType(phoneId); + } + + /** {@hide} */ + private int getPhoneTypeFromNetworkType(int phoneId) { + // When the system property CURRENT_ACTIVE_PHONE, has not been set, + // use the system property for default network type. + // This is a fail safe, and can only happen at first boot. + Integer mode = getTelephonyProperty(phoneId, TelephonyProperties.default_network(), null); + if (mode != null) { + return TelephonyManager.getPhoneType(mode); + } + return TelephonyManager.PHONE_TYPE_NONE; + } + + /** + * This function returns the type of the phone, depending + * on the network mode. + * + * @param networkMode + * @return Phone Type + * + * @hide + */ + @UnsupportedAppUsage + public static int getPhoneType(int networkMode) { + switch(networkMode) { + case RILConstants.NETWORK_MODE_CDMA: + case RILConstants.NETWORK_MODE_CDMA_NO_EVDO: + case RILConstants.NETWORK_MODE_EVDO_NO_CDMA: + return PhoneConstants.PHONE_TYPE_CDMA; + + case RILConstants.NETWORK_MODE_WCDMA_PREF: + case RILConstants.NETWORK_MODE_GSM_ONLY: + case RILConstants.NETWORK_MODE_WCDMA_ONLY: + case RILConstants.NETWORK_MODE_GSM_UMTS: + case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA: + case RILConstants.NETWORK_MODE_LTE_WCDMA: + case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: + case RILConstants.NETWORK_MODE_TDSCDMA_ONLY: + case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA: + case RILConstants.NETWORK_MODE_LTE_TDSCDMA: + case RILConstants.NETWORK_MODE_TDSCDMA_GSM: + case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM: + case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA: + case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA: + case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: + case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + return PhoneConstants.PHONE_TYPE_GSM; + + // Use CDMA Phone for the global mode including CDMA + case RILConstants.NETWORK_MODE_GLOBAL: + case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO: + case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + return PhoneConstants.PHONE_TYPE_CDMA; + + case RILConstants.NETWORK_MODE_LTE_ONLY: + if (TelephonyProperties.lte_on_cdma_device().orElse( + PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) { + return PhoneConstants.PHONE_TYPE_CDMA; + } else { + return PhoneConstants.PHONE_TYPE_GSM; + } + default: + return PhoneConstants.PHONE_TYPE_GSM; + } + } + + /** + * @return The max value for the timeout passed in {@link #requestNumberVerification}. + * @hide + */ + @SystemApi + public static long getMaxNumberVerificationTimeoutMillis() { + return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS; + } + + // + // + // Current Network + // + // + + /** + * Returns the alphabetic name of current registered operator. + *

+ * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public String getNetworkOperatorName() { + return getNetworkOperatorName(getSubId()); + } + + /** + * Returns the alphabetic name of current registered operator + * for a particular subscription. + *

+ * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + * @param subId + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getNetworkOperatorName(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + return getTelephonyProperty(phoneId, TelephonyProperties.operator_alpha(), ""); + } + + /** + * Returns the numeric name (MCC+MNC) of current registered operator. + *

+ * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public String getNetworkOperator() { + return getNetworkOperatorForPhone(getPhoneId()); + } + + /** + * Returns the numeric name (MCC+MNC) of current registered operator + * for a particular subscription. + *

+ * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + * + * @param subId + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getNetworkOperator(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + return getNetworkOperatorForPhone(phoneId); + } + + /** + * Returns the numeric name (MCC+MNC) of current registered operator + * for a particular subscription. + *

+ * Availability: Only when user is registered to a network. Result may be + * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if + * on a CDMA network). + * + * @param phoneId + * @hide + **/ + @UnsupportedAppUsage + public String getNetworkOperatorForPhone(int phoneId) { + return getTelephonyProperty(phoneId, TelephonyProperties.operator_numeric(), ""); + } + + + /** + * Returns the network specifier of the subscription ID pinned to the TelephonyManager. The + * network specifier is used by {@link + * android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to create a {@link + * android.net.NetworkRequest} that connects through the subscription. + * + * @see android.net.NetworkRequest.Builder#setNetworkSpecifier(String) + * @see #createForSubscriptionId(int) + * @see #createForPhoneAccountHandle(PhoneAccountHandle) + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public String getNetworkSpecifier() { + return String.valueOf(getSubId()); + } + + /** + * Returns the carrier config of the subscription ID pinned to the TelephonyManager. If an + * invalid subscription ID is pinned to the TelephonyManager, the returned config will contain + * default values. + * + *

This method may take several seconds to complete, so it should only be called from a + * worker thread. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @see CarrierConfigManager#getConfigForSubId(int) + * @see #createForSubscriptionId(int) + * @see #createForPhoneAccountHandle(PhoneAccountHandle) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @WorkerThread + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public PersistableBundle getCarrierConfig() { + CarrierConfigManager carrierConfigManager = mContext + .getSystemService(CarrierConfigManager.class); + return carrierConfigManager.getConfigForSubId(getSubId()); + } + + /** + * Returns true if the device is considered roaming on the current + * network, for GSM purposes. + *

+ * Availability: Only when user registered to a network. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isNetworkRoaming() { + return isNetworkRoaming(getSubId()); + } + + /** + * Returns true if the device is considered roaming on the current + * network for a subscription. + *

+ * Availability: Only when user registered to a network. + * + * @param subId + * @hide + */ + @UnsupportedAppUsage + public boolean isNetworkRoaming(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + return getTelephonyProperty(phoneId, TelephonyProperties.operator_is_roaming(), false); + } + + /** + * Returns the ISO-3166-1 alpha-2 country code equivalent of the MCC (Mobile Country Code) of + * the current registered operator or the cell nearby, if available. + * + * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine + * if on a CDMA network). + *

+ * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not + * available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public String getNetworkCountryIso() { + return getNetworkCountryIso(getSlotIndex()); + } + + /** + * Returns the ISO-3166-1 alpha-2 country code equivalent of the MCC (Mobile Country Code) of + * the current registered operator or the cell nearby, if available. This is same as + * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for + * accessing network country info from the SIM slot that does not have SIM inserted. + * + * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine + * if on a CDMA network). + *

+ * + * @param slotIndex the SIM slot index to get network country ISO. + * + * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not + * available. + * + * @throws IllegalArgumentException when the slotIndex is invalid. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + @NonNull + public String getNetworkCountryIso(int slotIndex) { + try { + if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX + && !SubscriptionManager.isValidSlotIndex(slotIndex)) { + throw new IllegalArgumentException("invalid slot index " + slotIndex); + } + + ITelephony telephony = getITelephony(); + if (telephony == null) return ""; + return telephony.getNetworkCountryIsoForPhone(slotIndex); + } catch (RemoteException ex) { + return ""; + } + } + + /** + * @hide + * @deprecated Use {@link #getNetworkCountryIso(int)} instead. + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, + publicAlternatives = "Use {@link #getNetworkCountryIso(int)} instead.") + public String getNetworkCountryIsoForPhone(int phoneId) { + return getNetworkCountryIso(phoneId); + } + + /* + * When adding a network type to the list below, make sure to add the correct icon to + * MobileSignalController.mapIconSets() as well as NETWORK_TYPES + * Do not add negative types. + */ + /** Network type is unknown */ + public static final int NETWORK_TYPE_UNKNOWN = TelephonyProtoEnums.NETWORK_TYPE_UNKNOWN; // = 0. + /** Current network is GPRS */ + public static final int NETWORK_TYPE_GPRS = TelephonyProtoEnums.NETWORK_TYPE_GPRS; // = 1. + /** Current network is EDGE */ + public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2. + /** Current network is UMTS */ + public static final int NETWORK_TYPE_UMTS = TelephonyProtoEnums.NETWORK_TYPE_UMTS; // = 3. + /** Current network is CDMA: Either IS95A or IS95B*/ + public static final int NETWORK_TYPE_CDMA = TelephonyProtoEnums.NETWORK_TYPE_CDMA; // = 4. + /** Current network is EVDO revision 0*/ + public static final int NETWORK_TYPE_EVDO_0 = TelephonyProtoEnums.NETWORK_TYPE_EVDO_0; // = 5. + /** Current network is EVDO revision A*/ + public static final int NETWORK_TYPE_EVDO_A = TelephonyProtoEnums.NETWORK_TYPE_EVDO_A; // = 6. + /** Current network is 1xRTT*/ + public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7. + /** Current network is HSDPA */ + public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8. + /** Current network is HSUPA */ + public static final int NETWORK_TYPE_HSUPA = TelephonyProtoEnums.NETWORK_TYPE_HSUPA; // = 9. + /** Current network is HSPA */ + public static final int NETWORK_TYPE_HSPA = TelephonyProtoEnums.NETWORK_TYPE_HSPA; // = 10. + /** + * Current network is iDen + * @deprecated Legacy network type no longer being used starting in Android U. + */ + @Deprecated + public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11. + /** Current network is EVDO revision B*/ + public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12. + /** Current network is LTE */ + public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13. + /** Current network is eHRPD */ + public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14. + /** Current network is HSPA+ */ + public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15. + /** Current network is GSM */ + public static final int NETWORK_TYPE_GSM = TelephonyProtoEnums.NETWORK_TYPE_GSM; // = 16. + /** Current network is TD_SCDMA */ + public static final int NETWORK_TYPE_TD_SCDMA = + TelephonyProtoEnums.NETWORK_TYPE_TD_SCDMA; // = 17. + /** Current network is IWLAN */ + public static final int NETWORK_TYPE_IWLAN = TelephonyProtoEnums.NETWORK_TYPE_IWLAN; // = 18. + /** Current network is LTE_CA {@hide} */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19. + /** + * Current network is NR (New Radio) 5G. + * This will only be returned for 5G SA. + * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}. + */ + public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20. + + private static final @NetworkType int[] NETWORK_TYPES = { + NETWORK_TYPE_GPRS, + NETWORK_TYPE_EDGE, + NETWORK_TYPE_UMTS, + NETWORK_TYPE_CDMA, + NETWORK_TYPE_EVDO_0, + NETWORK_TYPE_EVDO_A, + NETWORK_TYPE_1xRTT, + NETWORK_TYPE_HSDPA, + NETWORK_TYPE_HSUPA, + NETWORK_TYPE_HSPA, + NETWORK_TYPE_IDEN, + NETWORK_TYPE_EVDO_B, + NETWORK_TYPE_LTE, + NETWORK_TYPE_EHRPD, + NETWORK_TYPE_HSPAP, + NETWORK_TYPE_GSM, + NETWORK_TYPE_TD_SCDMA, + NETWORK_TYPE_IWLAN, + NETWORK_TYPE_LTE_CA, + NETWORK_TYPE_NR + }; + + /** + * Returns an array of all valid network types. + * + * @return An integer array containing all valid network types in no particular order. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static @NonNull @NetworkType int[] getAllNetworkTypes() { + return NETWORK_TYPES.clone(); + } + + /** + * Return the current data network type. + * + * @deprecated use {@link #getDataNetworkType()} + * @return the NETWORK_TYPE_xxxx for current data connection. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NetworkType int getNetworkType() { + return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); + } + + /** + * Returns a constant indicating the radio technology (network type) + * currently in use on the device for a subscription. + * @return the network type + * + * @param subId for which network type is returned + * + * @see #NETWORK_TYPE_UNKNOWN + * @see #NETWORK_TYPE_GPRS + * @see #NETWORK_TYPE_EDGE + * @see #NETWORK_TYPE_UMTS + * @see #NETWORK_TYPE_HSDPA + * @see #NETWORK_TYPE_HSUPA + * @see #NETWORK_TYPE_HSPA + * @see #NETWORK_TYPE_CDMA + * @see #NETWORK_TYPE_EVDO_0 + * @see #NETWORK_TYPE_EVDO_A + * @see #NETWORK_TYPE_EVDO_B + * @see #NETWORK_TYPE_1xRTT + * @see #NETWORK_TYPE_IDEN + * @see #NETWORK_TYPE_LTE + * @see #NETWORK_TYPE_EHRPD + * @see #NETWORK_TYPE_HSPAP + * @see #NETWORK_TYPE_NR + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getNetworkType(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } else { + // This can happen when the ITelephony interface is not up yet. + return NETWORK_TYPE_UNKNOWN; + } + } catch (RemoteException ex) { + // This shouldn't happen in the normal case + return NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return NETWORK_TYPE_UNKNOWN; + } + } + + /** + * Returns a constant indicating the radio technology (network type) + * currently in use on the device for data transmission. + * + * If this object has been created with {@link #createForSubscriptionId}, applies to the given + * subId. Otherwise, applies to {@link SubscriptionManager#getActiveDataSubscriptionId()}. + * + * Note: Before {@link SubscriptionManager#getActiveDataSubscriptionId()} was introduced in API + * level 30, it was applied to {@link SubscriptionManager#getDefaultDataSubscriptionId()} which + * may be different now from {@link SubscriptionManager#getActiveDataSubscriptionId()}, e.g. + * when opportunistic network is providing cellular internet connection to the user. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link #hasCarrierPrivileges}). + * + * @return the network type + * + * @see #NETWORK_TYPE_UNKNOWN + * @see #NETWORK_TYPE_GPRS + * @see #NETWORK_TYPE_EDGE + * @see #NETWORK_TYPE_UMTS + * @see #NETWORK_TYPE_HSDPA + * @see #NETWORK_TYPE_HSUPA + * @see #NETWORK_TYPE_HSPA + * @see #NETWORK_TYPE_CDMA + * @see #NETWORK_TYPE_EVDO_0 + * @see #NETWORK_TYPE_EVDO_A + * @see #NETWORK_TYPE_EVDO_B + * @see #NETWORK_TYPE_1xRTT + * @see #NETWORK_TYPE_IDEN + * @see #NETWORK_TYPE_LTE + * @see #NETWORK_TYPE_EHRPD + * @see #NETWORK_TYPE_HSPAP + * @see #NETWORK_TYPE_NR + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NetworkType int getDataNetworkType() { + return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); + } + + /** + * Returns a constant indicating the radio technology (network type) + * currently in use on the device for data transmission for a subscription + * @return the network type + * + * @param subId for which network type is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getDataNetworkType(int subId) { + try{ + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } else { + // This can happen when the ITelephony interface is not up yet. + return NETWORK_TYPE_UNKNOWN; + } + } catch(RemoteException ex) { + // This shouldn't happen in the normal case + return NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return NETWORK_TYPE_UNKNOWN; + } + } + + /** + * Returns the NETWORK_TYPE_xxxx for voice + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public @NetworkType int getVoiceNetworkType() { + return getVoiceNetworkType(getSubId()); + } + + /** + * Returns the NETWORK_TYPE_xxxx for voice for a subId + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public int getVoiceNetworkType(int subId) { + try{ + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } else { + // This can happen when the ITelephony interface is not up yet. + return NETWORK_TYPE_UNKNOWN; + } + } catch(RemoteException ex) { + // This shouldn't happen in the normal case + return NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return NETWORK_TYPE_UNKNOWN; + } + } + + /** + * Returns a string representation of the radio technology (network type) + * currently in use on the device. + * @return the name of the radio technology + * + * @hide pending API council review + */ + @UnsupportedAppUsage + public String getNetworkTypeName() { + return getNetworkTypeName(getNetworkType()); + } + + /** + * Returns a string representation of the radio technology (network type) + * currently in use on the device. + * @param subId for which network type is returned + * @return the name of the radio technology + * + */ + /** {@hide} */ + @UnsupportedAppUsage + public static String getNetworkTypeName(@NetworkType int type) { + switch (type) { + case NETWORK_TYPE_GPRS: + return "GPRS"; + case NETWORK_TYPE_EDGE: + return "EDGE"; + case NETWORK_TYPE_UMTS: + return "UMTS"; + case NETWORK_TYPE_HSDPA: + return "HSDPA"; + case NETWORK_TYPE_HSUPA: + return "HSUPA"; + case NETWORK_TYPE_HSPA: + return "HSPA"; + case NETWORK_TYPE_CDMA: + return "CDMA"; + case NETWORK_TYPE_EVDO_0: + return "CDMA - EvDo rev. 0"; + case NETWORK_TYPE_EVDO_A: + return "CDMA - EvDo rev. A"; + case NETWORK_TYPE_EVDO_B: + return "CDMA - EvDo rev. B"; + case NETWORK_TYPE_1xRTT: + return "CDMA - 1xRTT"; + case NETWORK_TYPE_LTE: + return "LTE"; + case NETWORK_TYPE_EHRPD: + return "CDMA - eHRPD"; + case NETWORK_TYPE_IDEN: + return "iDEN"; + case NETWORK_TYPE_HSPAP: + return "HSPA+"; + case NETWORK_TYPE_GSM: + return "GSM"; + case NETWORK_TYPE_TD_SCDMA: + return "TD_SCDMA"; + case NETWORK_TYPE_IWLAN: + return "IWLAN"; + case NETWORK_TYPE_LTE_CA: + return "LTE_CA"; + case NETWORK_TYPE_NR: + return "NR"; + case NETWORK_TYPE_UNKNOWN: + return "UNKNOWN"; + default: + return "UNKNOWN(" + type + ")"; + } + } + + /** + * Returns the bitmask for a given technology (network type) + * @param networkType for which bitmask is returned + * @return the network type bitmask + * {@hide} + */ + public static @NetworkTypeBitMask long getBitMaskForNetworkType(@NetworkType int networkType) { + switch(networkType) { + case NETWORK_TYPE_GSM: + return NETWORK_TYPE_BITMASK_GSM; + case NETWORK_TYPE_GPRS: + return NETWORK_TYPE_BITMASK_GPRS; + case NETWORK_TYPE_EDGE: + return NETWORK_TYPE_BITMASK_EDGE; + case NETWORK_TYPE_CDMA: + return NETWORK_TYPE_BITMASK_CDMA; + case NETWORK_TYPE_1xRTT: + return NETWORK_TYPE_BITMASK_1xRTT; + case NETWORK_TYPE_EVDO_0: + return NETWORK_TYPE_BITMASK_EVDO_0; + case NETWORK_TYPE_EVDO_A: + return NETWORK_TYPE_BITMASK_EVDO_A; + case NETWORK_TYPE_EVDO_B: + return NETWORK_TYPE_BITMASK_EVDO_B; + case NETWORK_TYPE_EHRPD: + return NETWORK_TYPE_BITMASK_EHRPD; + case NETWORK_TYPE_HSUPA: + return NETWORK_TYPE_BITMASK_HSUPA; + case NETWORK_TYPE_HSDPA: + return NETWORK_TYPE_BITMASK_HSDPA; + case NETWORK_TYPE_HSPA: + return NETWORK_TYPE_BITMASK_HSPA; + case NETWORK_TYPE_HSPAP: + return NETWORK_TYPE_BITMASK_HSPAP; + case NETWORK_TYPE_UMTS: + return NETWORK_TYPE_BITMASK_UMTS; + case NETWORK_TYPE_TD_SCDMA: + return NETWORK_TYPE_BITMASK_TD_SCDMA; + case NETWORK_TYPE_LTE: + case NETWORK_TYPE_LTE_CA: + return NETWORK_TYPE_BITMASK_LTE; + case NETWORK_TYPE_NR: + return NETWORK_TYPE_BITMASK_NR; + case NETWORK_TYPE_IWLAN: + return NETWORK_TYPE_BITMASK_IWLAN; + case NETWORK_TYPE_IDEN: + return NETWORK_TYPE_BITMASK_IDEN; + default: + return NETWORK_TYPE_BITMASK_UNKNOWN; + } + } + + // + // + // SIM Card + // + // + + /** @hide */ + @IntDef(prefix = {"SIM_STATE_"}, + value = { + SIM_STATE_UNKNOWN, + SIM_STATE_ABSENT, + SIM_STATE_PIN_REQUIRED, + SIM_STATE_PUK_REQUIRED, + SIM_STATE_NETWORK_LOCKED, + SIM_STATE_READY, + SIM_STATE_NOT_READY, + SIM_STATE_PERM_DISABLED, + SIM_STATE_CARD_IO_ERROR, + SIM_STATE_CARD_RESTRICTED, + SIM_STATE_LOADED, + SIM_STATE_PRESENT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SimState {} + + /** + * SIM card state: Unknown. Signifies that the SIM is in transition + * between states. For example, when the user inputs the SIM pin + * under PIN_REQUIRED state, a query for sim status returns + * this state before turning to SIM_STATE_READY. + * + * These are the ordinal value of IccCardConstants.State. + */ + + public static final int SIM_STATE_UNKNOWN = TelephonyProtoEnums.SIM_STATE_UNKNOWN; // 0 + /** SIM card state: no SIM card is available in the device */ + public static final int SIM_STATE_ABSENT = TelephonyProtoEnums.SIM_STATE_ABSENT; // 1 + /** SIM card state: Locked: requires the user's SIM PIN to unlock */ + public static final int SIM_STATE_PIN_REQUIRED = + TelephonyProtoEnums.SIM_STATE_PIN_REQUIRED; // 2 + /** SIM card state: Locked: requires the user's SIM PUK to unlock */ + public static final int SIM_STATE_PUK_REQUIRED = + TelephonyProtoEnums.SIM_STATE_PUK_REQUIRED; // 3 + /** SIM card state: Locked: requires a network PIN to unlock */ + public static final int SIM_STATE_NETWORK_LOCKED = + TelephonyProtoEnums.SIM_STATE_NETWORK_LOCKED; // 4 + /** SIM card state: Ready */ + public static final int SIM_STATE_READY = TelephonyProtoEnums.SIM_STATE_READY; // 5 + /** SIM card state: SIM Card is NOT READY */ + public static final int SIM_STATE_NOT_READY = TelephonyProtoEnums.SIM_STATE_NOT_READY; // 6 + /** SIM card state: SIM Card Error, permanently disabled */ + public static final int SIM_STATE_PERM_DISABLED = + TelephonyProtoEnums.SIM_STATE_PERM_DISABLED; // 7 + /** SIM card state: SIM Card Error, present but faulty */ + public static final int SIM_STATE_CARD_IO_ERROR = + TelephonyProtoEnums.SIM_STATE_CARD_IO_ERROR; // 8 + /** SIM card state: SIM Card restricted, present but not usable due to + * carrier restrictions. + */ + public static final int SIM_STATE_CARD_RESTRICTED = + TelephonyProtoEnums.SIM_STATE_CARD_RESTRICTED; // 9 + /** + * SIM card state: Loaded: SIM card applications have been loaded + * @hide + */ + @SystemApi + public static final int SIM_STATE_LOADED = TelephonyProtoEnums.SIM_STATE_LOADED; // 10 + /** + * SIM card state: SIM Card is present + * @hide + */ + @SystemApi + public static final int SIM_STATE_PRESENT = TelephonyProtoEnums.SIM_STATE_PRESENT; // 11 + + /** + * Extra included in {@link #ACTION_SIM_CARD_STATE_CHANGED} and + * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state. + * + * @hide + */ + @SystemApi + public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; + + /** + * Broadcast Action: The sim card state has changed. + * The intent will have the following extra values:

+ *
+ *
{@link #EXTRA_SIM_STATE}
+ *
The sim card state. One of: + *
+ *
{@link #SIM_STATE_ABSENT}
+ *
SIM card not found
+ *
{@link #SIM_STATE_CARD_IO_ERROR}
+ *
SIM card IO error
+ *
{@link #SIM_STATE_CARD_RESTRICTED}
+ *
SIM card is restricted
+ *
{@link #SIM_STATE_PRESENT}
+ *
SIM card is present
+ *
+ *
+ *
+ * + *

Requires the READ_PRIVILEGED_PHONE_STATE permission. + * + *

The current state can also be queried using {@link #getSimCardState()}. + * + *

This is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_CARD_STATE_CHANGED = + "android.telephony.action.SIM_CARD_STATE_CHANGED"; + + /** + * Broadcast Action: The sim application state has changed. + * The intent will have the following extra values:

+ *
+ *
{@link #EXTRA_SIM_STATE}
+ *
The sim application state. One of: + *
+ *
{@link #SIM_STATE_NOT_READY}
+ *
SIM card applications not ready
+ *
{@link #SIM_STATE_PIN_REQUIRED}
+ *
SIM card PIN locked
+ *
{@link #SIM_STATE_PUK_REQUIRED}
+ *
SIM card PUK locked
+ *
{@link #SIM_STATE_NETWORK_LOCKED}
+ *
SIM card network locked
+ *
{@link #SIM_STATE_PERM_DISABLED}
+ *
SIM card permanently disabled due to PUK failures
+ *
{@link #SIM_STATE_LOADED}
+ *
SIM card data loaded
+ *
+ *
+ *
+ * + *

Requires the READ_PRIVILEGED_PHONE_STATE permission. + * + *

The current state can also be queried using + * {@link #getSimApplicationState()}. + * + *

This is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = + "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; + + /** + * Broadcast Action: Status of the SIM slots on the device has changed. + * + *

Requires the READ_PRIVILEGED_PHONE_STATE permission. + * + *

The status can be queried using + * {@link #getUiccSlotsInfo()} + * + *

This is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_SLOT_STATUS_CHANGED = + "android.telephony.action.SIM_SLOT_STATUS_CHANGED"; + + /** + * Broadcast Action: A debug code has been entered in the dialer. + *

+ * This intent is broadcast by the system and OEM telephony apps may need to receive these + * broadcasts. And it requires the sender to be default dialer or has carrier privileges + * (see {@link #hasCarrierPrivileges}). + *

+ * These "secret codes" are used to activate developer menus by dialing certain codes. + * And they are of the form {@code *#*##*#*}. The intent will have the data + * URI: {@code android_secret_code://}. It is possible that a manifest + * receiver would be woken up even if it is not currently running. + *

+ * It is supposed to replace {@link android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION} + * in the next Android version. + * Before that both of these two actions will be broadcast. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE"; + + /** + * This API is used to check if there is an ICC card present in the device. + * + * An ICC card is a smart card that contains a subscriber identity module (SIM) and is used + * to identify and authenticate users to a mobile network. + * + * Note: In case of embedded SIM there is an ICC card always present irrespective + * of whether an active SIM profile is present or not so this API would always return true. + * + * @return true if a ICC card is present. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean hasIccCard() { + return hasIccCard(getSlotIndex()); + } + + /** + * @return true if a ICC card is present for a subscription + * + * @param slotIndex for which icc card presence is checked + */ + /** {@hide} */ + // FIXME Input argument slotIndex should be of type int + @UnsupportedAppUsage + public boolean hasIccCard(int slotIndex) { + + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return false; + return telephony.hasIccCardUsingSlotIndex(slotIndex); + } catch (RemoteException ex) { + // Assume no ICC card if remote exception which shouldn't happen + return false; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return false; + } + } + + /** + * Returns a constant indicating the state of the default SIM card. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_READY + * @see #SIM_STATE_NOT_READY + * @see #SIM_STATE_PERM_DISABLED + * @see #SIM_STATE_CARD_IO_ERROR + * @see #SIM_STATE_CARD_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @SimState int getSimState() { + int simState = getSimStateIncludingLoaded(); + if (simState == SIM_STATE_LOADED) { + simState = SIM_STATE_READY; + } + return simState; + } + + private @SimState int getSimStateIncludingLoaded() { + int slotIndex = getSlotIndex(); + // slotIndex may be invalid due to sim being absent. In that case query all slots to get + // sim state + if (slotIndex < 0) { + // query for all slots and return absent if all sim states are absent, otherwise + // return unknown + for (int i = 0; i < getPhoneCount(); i++) { + int simState = getSimState(i); + if (simState != SIM_STATE_ABSENT) { + Rlog.d(TAG, "getSimState: default sim:" + slotIndex + ", sim state for " + + "slotIndex=" + i + " is " + simState + ", return state as unknown"); + return SIM_STATE_UNKNOWN; + } + } + Rlog.d(TAG, "getSimState: default sim:" + slotIndex + ", all SIMs absent, return " + + "state as absent"); + return SIM_STATE_ABSENT; + } + return getSimStateForSlotIndex(slotIndex); + } + + /** + * Returns a constant indicating the state of the default SIM card. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_CARD_IO_ERROR + * @see #SIM_STATE_CARD_RESTRICTED + * @see #SIM_STATE_PRESENT + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @SimState int getSimCardState() { + int simState = getSimState(); + return getSimCardStateFromSimState(simState); + } + + /** + * Returns a constant indicating the state of the device SIM card in a physical slot. + * + * @param physicalSlotIndex physical slot index + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_CARD_IO_ERROR + * @see #SIM_STATE_CARD_RESTRICTED + * @see #SIM_STATE_PRESENT + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated instead use {@link #getSimCardState(int, int)} + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @Deprecated + public @SimState int getSimCardState(int physicalSlotIndex) { + int activePort = getFirstActivePortIndex(physicalSlotIndex); + int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, activePort)); + return getSimCardStateFromSimState(simState); + } + + /** + * Returns a constant indicating the state of the device SIM card in a physical slot and + * port index. + * + * @param physicalSlotIndex physical slot index + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_CARD_IO_ERROR + * @see #SIM_STATE_CARD_RESTRICTED + * @see #SIM_STATE_PRESENT + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @SimState int getSimCardState(int physicalSlotIndex, int portIndex) { + int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, portIndex)); + return getSimCardStateFromSimState(simState); + } + /** + * Converts SIM state to SIM card state. + * @param simState + * @return SIM card state + */ + private @SimState int getSimCardStateFromSimState(int simState) { + switch (simState) { + case SIM_STATE_UNKNOWN: + case SIM_STATE_ABSENT: + case SIM_STATE_CARD_IO_ERROR: + case SIM_STATE_CARD_RESTRICTED: + return simState; + default: + return SIM_STATE_PRESENT; + } + } + + /** + * Converts a physical slot index to logical slot index. + * @param physicalSlotIndex physical slot index + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * @return logical slot index + */ + private int getLogicalSlotIndex(int physicalSlotIndex, int portIndex) { + UiccSlotInfo[] slotInfos = getUiccSlotsInfo(); + if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length + && slotInfos[physicalSlotIndex] != null) { + for (UiccPortInfo portInfo : slotInfos[physicalSlotIndex].getPorts()) { + if (portInfo.getPortIndex() == portIndex) { + return portInfo.getLogicalSlotIndex(); + } + } + } + + return SubscriptionManager.INVALID_SIM_SLOT_INDEX; + } + + /** + * Returns a constant indicating the state of the card applications on the default SIM card. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_NOT_READY + * @see #SIM_STATE_PERM_DISABLED + * @see #SIM_STATE_LOADED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @SimState int getSimApplicationState() { + int simState = getSimStateIncludingLoaded(); + return getSimApplicationStateFromSimState(simState); + } + + /** + * Returns a constant indicating the state of the card applications on the device SIM card in + * a physical slot. + * + * @param physicalSlotIndex physical slot index + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_NOT_READY + * @see #SIM_STATE_PERM_DISABLED + * @see #SIM_STATE_LOADED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated instead use {@link #getSimApplicationState(int, int)} + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @Deprecated + public @SimState int getSimApplicationState(int physicalSlotIndex) { + int activePort = getFirstActivePortIndex(physicalSlotIndex); + int simState = getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex, activePort)); + return getSimApplicationStateFromSimState(simState); + } + + /** + * Returns a constant indicating the state of the card applications on the device SIM card in + * a physical slot. + * + * @param physicalSlotIndex physical slot index + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_NOT_READY + * @see #SIM_STATE_PERM_DISABLED + * @see #SIM_STATE_LOADED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @SimState int getSimApplicationState(int physicalSlotIndex, int portIndex) { + int simState = getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex, portIndex)); + return getSimApplicationStateFromSimState(simState); + } + + /** + * Converts SIM state to SIM application state. + * @param simState + * @return SIM application state + */ + private @SimState int getSimApplicationStateFromSimState(int simState) { + switch (simState) { + case SIM_STATE_UNKNOWN: + case SIM_STATE_ABSENT: + case SIM_STATE_CARD_IO_ERROR: + case SIM_STATE_CARD_RESTRICTED: + return SIM_STATE_UNKNOWN; + case SIM_STATE_READY: + // Ready is not a valid state anymore. The state that is broadcast goes from + // NOT_READY to either LOCKED or LOADED. + return SIM_STATE_NOT_READY; + default: + return simState; + } + } + + + /** + * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present + * on the UICC card. + * + * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * + * @param appType the uicc app type like {@link APPTYPE_CSIM} + * @return true if the specified type of application in UICC CARD or false if no uicc or error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean isApplicationOnUicc(@UiccAppType int appType) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isApplicationOnUicc(getSubId(), appType); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isApplicationOnUicc", e); + } + return false; + } + + /** + * Returns a constant indicating the state of the device SIM card in a logical slot. + * + * @param slotIndex logical slot index + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_READY + * @see #SIM_STATE_NOT_READY + * @see #SIM_STATE_PERM_DISABLED + * @see #SIM_STATE_CARD_IO_ERROR + * @see #SIM_STATE_CARD_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @SimState int getSimState(int slotIndex) { + int simState = getSimStateForSlotIndex(slotIndex); + if (simState == SIM_STATE_LOADED) { + simState = SIM_STATE_READY; + } + return simState; + } + + /** + * Returns the MCC+MNC (mobile country code + mobile network code) of the + * provider of the SIM. 5 or 6 decimal digits. + *

+ * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getSimOperator() { + return getSimOperatorNumeric(); + } + + /** + * Returns the MCC+MNC (mobile country code + mobile network code) of the + * provider of the SIM. 5 or 6 decimal digits. + *

+ * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + * + * @param subId for which SimOperator is returned + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getSimOperator(int subId) { + return getSimOperatorNumeric(subId); + } + + /** + * Returns the MCC+MNC (mobile country code + mobile network code) of the + * provider of the SIM. 5 or 6 decimal digits. + *

+ * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getSimOperatorNumeric() { + int subId = mSubId; + if (!SubscriptionManager.isUsableSubIdValue(subId)) { + subId = SubscriptionManager.getDefaultDataSubscriptionId(); + if (!SubscriptionManager.isUsableSubIdValue(subId)) { + subId = SubscriptionManager.getDefaultSmsSubscriptionId(); + if (!SubscriptionManager.isUsableSubIdValue(subId)) { + subId = SubscriptionManager.getDefaultVoiceSubscriptionId(); + if (!SubscriptionManager.isUsableSubIdValue(subId)) { + subId = SubscriptionManager.getDefaultSubscriptionId(); + } + } + } + } + return getSimOperatorNumeric(subId); + } + + /** + * Returns the MCC+MNC (mobile country code + mobile network code) of the + * provider of the SIM for a particular subscription. 5 or 6 decimal digits. + *

+ * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + * + * @param subId for which SimOperator is returned + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getSimOperatorNumeric(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + return getSimOperatorNumericForPhone(phoneId); + } + + /** + * Returns the MCC+MNC (mobile country code + mobile network code) of the + * provider of the SIM for a particular subscription. 5 or 6 decimal digits. + *

+ * + * @param phoneId for which SimOperator is returned + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getSimOperatorNumericForPhone(int phoneId) { + return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_numeric(), ""); + } + + /** + * Returns the Service Provider Name (SPN). + *

+ * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getSimOperatorName() { + return getSimOperatorNameForPhone(getPhoneId()); + } + + /** + * Returns the Service Provider Name (SPN). + *

+ * Availability: SIM state must be {@link #SIM_STATE_READY} + * + * @see #getSimState + * + * @param subId for which SimOperatorName is returned + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getSimOperatorName(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + return getSimOperatorNameForPhone(phoneId); + } + + /** + * Returns the Service Provider Name (SPN). + * + * @hide + */ + @UnsupportedAppUsage + public String getSimOperatorNameForPhone(int phoneId) { + return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_alpha(), ""); + } + + /** + * Returns the ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code. + *

+ * The ISO-3166-1 alpha-2 country code is provided in lowercase 2 character format. + * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string is not + * available. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getSimCountryIso() { + return getSimCountryIsoForPhone(getPhoneId()); + } + + /** + * Returns the ISO country code equivalent for the SIM provider's country code. + * + * @param subId for which SimCountryIso is returned + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public static String getSimCountryIso(int subId) { + int phoneId = SubscriptionManager.getPhoneId(subId); + return getSimCountryIsoForPhone(phoneId); + } + + /** + * Returns the ISO country code equivalent for the SIM provider's country code. + * + * @hide + */ + @UnsupportedAppUsage + public static String getSimCountryIsoForPhone(int phoneId) { + return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_iso_country(), ""); + } + + /** + * Returns the serial number of the SIM, if applicable. Return null if it is + * unavailable. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getSimSerialNumber() { + return getSimSerialNumber(getSubId()); + } + + /** + * Returns the serial number for the given subscription, if applicable. Return null if it is + * unavailable. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @param subId for which Sim Serial number is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @UnsupportedAppUsage + public String getSimSerialNumber(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Return if the current radio can support both 3GPP and 3GPP2 radio technologies at the same + * time. This is also known as global mode, which includes LTE, CDMA, EvDo and GSM/WCDMA. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time + * {@code false} if not supported or unknown + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isLteCdmaEvdoGsmWcdmaEnabled() { + return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE; + } + + /** + * Return if the current radio is LTE on CDMA for Subscription. This + * is a tri-state return value as for a period of time + * the mode may be unknown. + * + * @param subId for which radio is LTE on CDMA is returned + * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE} + * or {@link PhoneConstants#LTE_ON_CDMA_TRUE} + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @UnsupportedAppUsage + public int getLteOnCdmaMode(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return PhoneConstants.LTE_ON_CDMA_UNKNOWN; + return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + // Assume no ICC card if remote exception which shouldn't happen + return PhoneConstants.LTE_ON_CDMA_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return PhoneConstants.LTE_ON_CDMA_UNKNOWN; + } + } + + /** + * Get the card ID of the default eUICC card. If the eUICCs have not yet been loaded, returns + * {@link #UNINITIALIZED_CARD_ID}. If there is no eUICC or the device does not support card IDs + * for eUICCs, returns {@link #UNSUPPORTED_CARD_ID}. + * + *

The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are + * unique to a device, and always refer to the same UICC or eUICC card unless the device goes + * through a factory reset. + * + * @return card ID of the default eUICC card, if loaded. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC) + public int getCardIdForDefaultEuicc() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + return UNINITIALIZED_CARD_ID; + } + return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName()); + } catch (RemoteException e) { + return UNINITIALIZED_CARD_ID; + } + } + + /** + * Gets information about currently inserted UICCs and eUICCs. + *

+ * Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *

+ * If the caller has carrier priviliges on any active subscription, then they have permission to + * get simple information like the card ID ({@link UiccCardInfo#getCardId()}), whether the card + * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the physical slot index where the card is + * inserted ({@link UiccCardInfo#getPhysicalSlotIndex()}. + *

+ * To get private information such as the EID ({@link UiccCardInfo#getEid()}) or ICCID + * ({@link UiccCardInfo#getIccId()}), the caller must have carrier priviliges on that specific + * UICC or eUICC card. + *

+ * See {@link UiccCardInfo} for more details on the kind of information available. + * + * @return a list of UiccCardInfo objects, representing information on the currently inserted + * UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if + * the caller does not have adequate permissions for that card. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @NonNull + public List getUiccCardsInfo() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + Log.e(TAG, "Error in getUiccCardsInfo: unable to connect to Telephony service."); + return new ArrayList(); + } + return telephony.getUiccCardsInfo(mContext.getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error in getUiccCardsInfo: " + e); + return new ArrayList(); + } + } + + /** + * Gets all the UICC slots. The objects in the array can be null if the slot info is not + * available, which is possible between phone process starting and getting slot info from modem. + * + * @return UiccSlotInfo array. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public UiccSlotInfo[] getUiccSlotsInfo() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + return null; + } + return telephony.getUiccSlotsInfo(mContext.getOpPackageName()); + } catch (RemoteException e) { + return null; + } + } + + /** + * Test method to reload the UICC profile. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void refreshUiccProfile() { + try { + ITelephony telephony = getITelephony(); + telephony.refreshUiccProfile(mSubId); + } catch (RemoteException ex) { + Rlog.w(TAG, "RemoteException", ex); + } + } + + /** + * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. For + * example, passing the physicalSlots array [1, 0] means mapping the first item 1, which is + * physical slot index 1, to the logical slot 0; and mapping the second item 0, which is + * physical slot index 0, to the logical slot 1. The index of the array means the index of the + * logical slots. + * + * @param physicalSlots The content of the array represents the physical slot index. The array + * size should be same as {@link #getUiccSlotsInfo()}. + * @return boolean Return true if the switch succeeds, false if the switch fails. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)} + */ + // TODO: once integrating the HAL changes we can convert int[] to List and + // converge API's in ITelephony.aidl and PhoneInterfaceManager + + @SystemApi + @Deprecated + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean switchSlots(int[] physicalSlots) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + return false; + } + return telephony.switchSlots(physicalSlots); + } catch (RemoteException e) { + return false; + } + } + + /** + * @param slotMapping Logical to physical slot and port mapping. + * @return {@code true} if slotMapping is valid. + * @return {@code false} if slotMapping is invalid. + * + * slotMapping is invalid if there are different entries (physical slot + port) mapping to the + * same logical slot or if there are same {physical slot + port} mapping to the different + * logical slot + * @hide + */ + private static boolean isSlotMappingValid(@NonNull Collection slotMapping) { + // Grouping the collection by logicalSlotIndex, finding different entries mapping to the + // same logical slot + Map> slotMappingInfo = slotMapping.stream().collect( + Collectors.groupingBy(UiccSlotMapping::getLogicalSlotIndex)); + for (Map.Entry> entry : slotMappingInfo.entrySet()) { + List logicalSlotMap = entry.getValue(); + if (logicalSlotMap.size() > 1) { + // duplicate logicalSlotIndex found + return false; + } + } + + // Grouping the collection by physical slot and port, finding same entries mapping to the + // different logical slot + Map, List> slotMapInfos = slotMapping.stream().collect( + Collectors.groupingBy( + slot -> Arrays.asList(slot.getPhysicalSlotIndex(), slot.getPortIndex()))); + for (Map.Entry, List> entry : slotMapInfos.entrySet()) { + List portAndPhysicalSlotList = entry.getValue(); + if (portAndPhysicalSlotList.size() > 1) { + // duplicate pair of portIndex and physicalSlotIndex found + return false; + } + } + return true; + } + /** + * Maps the logical slots to physical slots and ports. Mapping is specified from + * {@link UiccSlotMapping} which consist of both physical slot index and port index. + * Logical slot is the slot that is seen by modem. Physical slot is the actual physical slot. + * Port index is the index (enumerated value) for the associated port available on the SIM. + * Each physical slot can have multiple ports if + * {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP} is supported. + * + * Example: no. of logical slots 1 and physical slots 2 do not support MEP, each physical slot + * has one port: + * The only logical slot (index 0) can be mapped to first physical slot (value 0), port(index + * 0) or + * second physical slot(value 1), port (index 0), while the other physical slot remains unmapped + * and inactive. + * slotMapping[0] = UiccSlotMapping{0 //port index, 0 //physical slot, 0 //logical slot} or + * slotMapping[0] = UiccSlotMapping{0 //port index, 1 //physical slot, 0 //logical slot} + * + * Example no. of logical slots 2 and physical slots 2 supports MEP with 2 ports available: + * Each logical slot must be mapped to a port (physical slot and port combination). + * First logical slot (index 0) can be mapped to physical slot 1 and the second logical slot + * can be mapped to either port from physical slot 2. + * + * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{0, 1, 1} or + * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} + * + * or the other way around, the second logical slot(index 1) can be mapped to physical slot 1 + * and the first logical slot can be mapped to either port from physical slot 2. + * + * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{0, 1, 1} or + * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 1, 1} + * + * another possible mapping is each logical slot maps to each port of physical slot 2 and there + * is no active logical modem mapped to physical slot 1. + * + * slotMapping[0] = UiccSlotMapping{0, 1, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} or + * slotMapping[0] = UiccSlotMapping{1, 1, 0} and slotMapping[1] = UiccSlotMapping{0, 1, 1} + * + * @param slotMapping Logical to physical slot and port mapping. + * @throws IllegalStateException if telephony service is null or slot mapping was sent when the + * radio in middle of a silent restart or other invalid states to handle the command + * @throws IllegalArgumentException if the caller passes in an invalid collection of + * UiccSlotMapping like duplicate data, etc + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void setSimSlotMapping(@NonNull Collection slotMapping) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (isSlotMappingValid(slotMapping)) { + boolean result = telephony.setSimSlotMapping(new ArrayList(slotMapping)); + if (!result) { + throw new IllegalStateException("setSimSlotMapping has failed"); + } + } else { + throw new IllegalArgumentException("Duplicate UiccSlotMapping data found"); + } + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * Get the mapping from logical slots to physical slots. The key of the map is the logical slot + * id and the value is the physical slots id mapped to this logical slot id. + * + * @return a map indicates the mapping from logical slots to physical slots. The size of the map + * should be {@link #getPhoneCount()} if success, otherwise return an empty map. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated use {@link #getSimSlotMapping()} instead. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @NonNull + @Deprecated + public Map getLogicalToPhysicalSlotMapping() { + Map slotMapping = new HashMap<>(); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + List simSlotsMapping = telephony.getSlotsMapping( + mContext.getOpPackageName()); + for (UiccSlotMapping slotMap : simSlotsMapping) { + slotMapping.put(slotMap.getLogicalSlotIndex(), slotMap.getPhysicalSlotIndex()); + } + } + } catch (RemoteException e) { + Log.e(TAG, "getSlotsMapping RemoteException", e); + } + return slotMapping; + } + + /** + * Get the mapping from logical slots to physical sim slots and port indexes. Initially the + * logical slot index was mapped to physical slot index, but with support for multi-enabled + * profile(MEP){@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP},logical slot is now mapped to + * port index. + * + * @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical + * slots to ports and physical slots. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @NonNull + public Collection getSimSlotMapping() { + List slotMap; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + slotMap = telephony.getSlotsMapping(mContext.getOpPackageName()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + return slotMap; + } + // + // + // Subscriber Info + // + // + + /** + * Returns the unique subscriber ID, for example, the IMSI for a GSM phone. + * Return null if it is unavailable. + * + *

Starting with API level 29, persistent device identifiers are guarded behind additional + * restrictions, and apps are recommended to use resettable identifiers (see Best practices for unique identifiers). This + * method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
  • If the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. + *
+ * + *

If the calling app does not meet one of these requirements then this method will behave + * as follows: + * + *

    + *
  • If the calling app's target SDK is API level 28 or lower and the app has the + * READ_PHONE_STATE permission then null is returned.
  • + *
  • If the calling app's target SDK is API level 28 or lower and the app does not have + * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or + * higher, then a SecurityException is thrown.
  • + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getSubscriberId() { + return getSubscriberId(getSubId()); + } + + /** + * Returns the unique subscriber ID, for example, the IMSI for a GSM phone + * for a subscription. + * Return null if it is unavailable. + * + * See {@link #getSubscriberId()} for details on the required permissions and behavior + * when the caller does not hold sufficient permissions. + * + * @param subId whose subscriber id is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getSubscriberId(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns carrier specific information that will be used to encrypt the IMSI and IMPI, + * including the public key and the key identifier; or {@code null} if not available. + *

+ * For a multi-sim device, the dafault data sim is used if not specified. + *

+ * Requires Permission: READ_PRIVILEGED_PHONE_STATE. + * + * @param keyType whether the key is being used for EPDG or WLAN. Valid values are + * {@link #KEY_TYPE_EPDG} or {@link #KEY_TYPE_WLAN}. + * @return ImsiEncryptionInfo Carrier specific information that will be used to encrypt the + * IMSI and IMPI. This includes the public key and the key identifier. This information + * will be stored in the device keystore. {@code null} will be returned when no key is + * found, and the carrier does not require a key. + * @throws IllegalArgumentException when an invalid key is found or when key is required but + * not found. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @Nullable + public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) { + Rlog.e(TAG,"IMSI error: Subscriber Info is null"); + return null; + } + int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId()); + if (keyType != KEY_TYPE_EPDG && keyType != KEY_TYPE_WLAN) { + throw new IllegalArgumentException("IMSI error: Invalid key type"); + } + ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption( + subId, keyType, mContext.getOpPackageName()); + if (imsiEncryptionInfo == null && isImsiEncryptionRequired(subId, keyType)) { + Rlog.e(TAG, "IMSI error: key is required but not found"); + throw new IllegalArgumentException("IMSI error: key is required but not found"); + } + return imsiEncryptionInfo; + } catch (RemoteException ex) { + Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex); + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex); + } + return null; + } + + /** + * Resets the carrier keys used to encrypt the IMSI and IMPI. + *

+ * This involves 2 steps: + * 1. Delete the keys from the database. + * 2. Send an intent to download new Certificates. + *

+ * For a multi-sim device, the dafault data sim is used if not specified. + *

+ * Requires Permission: MODIFY_PHONE_STATE. + * + * @see #getCarrierInfoForImsiEncryption + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + public void resetCarrierKeysForImsiEncryption() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) { + throw new RuntimeException("IMSI error: Subscriber Info is null"); + } + int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId()); + info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName()); + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getCarrierInfoForImsiEncryption RemoteException" + ex); + } + } + + /** + * @param keyAvailability bitmask that defines the availabilty of keys for a type. + * @param keyType the key type which is being checked. (WLAN, EPDG) + * @return true if the digit at position keyType is 1, else false. + * @hide + */ + private static boolean isKeyEnabled(int keyAvailability, @KeyType int keyType) { + int returnValue = (keyAvailability >> (keyType - 1)) & 1; + return (returnValue == 1) ? true : false; + } + + /** + * If Carrier requires Imsi to be encrypted. + * @hide + */ + private boolean isImsiEncryptionRequired(int subId, @KeyType int keyType) { + CarrierConfigManager configManager = + (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); + if (configManager == null) { + return false; + } + PersistableBundle pb = configManager.getConfigForSubId(subId); + if (pb == null) { + return false; + } + int keyAvailability = pb.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); + return isKeyEnabled(keyAvailability, keyType); + } + + /** + * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI. + * This includes the public key and the key identifier. This information will be stored in the + * device keystore. + *

+ * Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * @param imsiEncryptionInfo which includes the Key Type, the Public Key + * (java.security.PublicKey) and the Key Identifier.and the Key Identifier. + * The keyIdentifier Attribute value pair that helps a server locate + * the private key to decrypt the permanent identity. This field is + * optional and if it is present then it’s always separated from encrypted + * permanent identity with “,”. Key identifier AVP is presented in ASCII string + * with “name=value” format. + * @hide + */ + public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) return; + info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(), + imsiEncryptionInfo); + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return; + } catch (RemoteException ex) { + Rlog.e(TAG, "setCarrierInfoForImsiEncryption RemoteException", ex); + return; + } + } + + /** + * Exception that may be supplied to the callback in {@link #uploadCallComposerPicture} if + * something goes awry. + */ + public static class CallComposerException extends Exception { + /** + * Used internally only, signals success of the upload to the carrier. + * @hide + */ + public static final int SUCCESS = -1; + /** + * Indicates that an unknown error was encountered when uploading the call composer picture. + * + * Clients that encounter this error should retry the upload. + */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Indicates that the phone process died or otherwise became unavailable while uploading the + * call composer picture. + * + * Clients that encounter this error should retry the upload. + */ + public static final int ERROR_REMOTE_END_CLOSED = 1; + + /** + * Indicates that the file or stream supplied exceeds the size limit defined in + * {@link #getMaximumCallComposerPictureSize()}. + * + * Clients that encounter this error should retry the upload after reducing the size of the + * picture. + */ + public static final int ERROR_FILE_TOO_LARGE = 2; + + /** + * Indicates that the device failed to authenticate with the carrier when uploading the + * picture. + * + * Clients that encounter this error should not retry the upload unless a reboot or radio + * reset has been performed in the interim. + */ + public static final int ERROR_AUTHENTICATION_FAILED = 3; + + /** + * Indicates that the {@link InputStream} passed to {@link #uploadCallComposerPicture} + * was closed. + * + * The caller should retry if this error is encountered, and be sure to not close the stream + * before the callback is called this time. + */ + public static final int ERROR_INPUT_CLOSED = 4; + + /** + * Indicates that an {@link IOException} was encountered while reading the picture. + * + * The offending {@link IOException} will be available via {@link #getIOException()}. + * Clients should use the contents of the exception to determine whether a retry is + * warranted. + */ + public static final int ERROR_IO_EXCEPTION = 5; + + /** + * Indicates that the device is currently not connected to a network that's capable of + * reaching a carrier's RCS servers. + * + * Clients should prompt the user to remedy the issue by moving to an area with better + * signal, by connecting to a different network, or to retry at another time. + */ + public static final int ERROR_NETWORK_UNAVAILABLE = 6; + + /** @hide */ + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_UNKNOWN, + ERROR_REMOTE_END_CLOSED, + ERROR_FILE_TOO_LARGE, + ERROR_AUTHENTICATION_FAILED, + ERROR_INPUT_CLOSED, + ERROR_IO_EXCEPTION, + ERROR_NETWORK_UNAVAILABLE, + }) + + @Retention(RetentionPolicy.SOURCE) + public @interface CallComposerError {} + + private final int mErrorCode; + private final IOException mIOException; + + public CallComposerException(@CallComposerError int errorCode, + @Nullable IOException ioException) { + mErrorCode = errorCode; + mIOException = ioException; + } + + /** + * Fetches the error code associated with this exception. + * @return An error code. + */ + public @CallComposerError int getErrorCode() { + return mErrorCode; + } + + /** + * Fetches the {@link IOException} that caused the error. + */ + // Follows the naming of IOException + @SuppressLint("AcronymName") + public @Nullable IOException getIOException() { + return mIOException; + } + } + + /** @hide */ + public static final String KEY_CALL_COMPOSER_PICTURE_HANDLE = "call_composer_picture_handle"; + + /** + * Uploads a picture to the carrier network for use with call composer. + * + * @see #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver) + * @param pictureToUpload Path to a local file containing the picture to upload. + * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg) + * @param executor The {@link Executor} on which the {@code pictureToUpload} file will be read + * from disk, as well as on which {@code callback} will be called. + * @param callback A callback called when the upload operation terminates, either in success + * or in error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void uploadCallComposerPicture(@NonNull Path pictureToUpload, + @NonNull String contentType, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver callback) { + Objects.requireNonNull(pictureToUpload); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + // Do the role check now so that we can quit early if needed -- there's an additional + // permission check on the other side of the binder call as well. + RoleManager rm = mContext.getSystemService(RoleManager.class); + if (!rm.isRoleHeld(RoleManager.ROLE_DIALER)) { + throw new SecurityException("You must hold RoleManager.ROLE_DIALER to do this"); + } + + executor.execute(() -> { + try { + if (Looper.getMainLooper().isCurrentThread()) { + Log.w(TAG, "Uploading call composer picture on main thread!" + + " hic sunt dracones!"); + } + long size = Files.size(pictureToUpload); + if (size > getMaximumCallComposerPictureSize()) { + callback.onError(new CallComposerException( + CallComposerException.ERROR_FILE_TOO_LARGE, null)); + return; + } + InputStream fileStream = Files.newInputStream(pictureToUpload); + try { + uploadCallComposerPicture(fileStream, contentType, executor, + new OutcomeReceiver() { + @Override + public void onResult(ParcelUuid result) { + try { + fileStream.close(); + } catch (IOException e) { + // ignore + Log.e(TAG, "Error closing file input stream when" + + " uploading call composer pic"); + } + callback.onResult(result); + } + + @Override + public void onError(CallComposerException error) { + try { + fileStream.close(); + } catch (IOException e) { + // ignore + Log.e(TAG, "Error closing file input stream when" + + " uploading call composer pic"); + } + callback.onError(error); + } + }); + } catch (Exception e) { + Log.e(TAG, "Got exception calling into stream-version of" + + " uploadCallComposerPicture: " + e); + try { + fileStream.close(); + } catch (IOException e1) { + // ignore + Log.e(TAG, "Error closing file input stream when uploading" + + " call composer pic"); + } + } + } catch (IOException e) { + Log.e(TAG, "IOException when uploading call composer pic:" + e); + callback.onError( + new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e)); + } + }); + + } + + /** + * Uploads a picture to the carrier network for use with call composer. + * + * This method allows a dialer app to upload a picture to the carrier network that can then + * later be attached to an outgoing call. In order to attach the picture to a call, use the + * {@link ParcelUuid} returned from {@code callback} upon successful upload as the value to + * {@link TelecomManager#EXTRA_OUTGOING_PICTURE}. + * + * This functionality is only available to the app filling the {@link RoleManager#ROLE_DIALER} + * role on the device. + * + * This functionality is only available when + * {@link CarrierConfigManager#KEY_SUPPORTS_CALL_COMPOSER_BOOL} is set to {@code true} in the + * bundle returned from {@link #getCarrierConfig()}. + * + * @param pictureToUpload An {@link InputStream} that supplies the bytes representing the + * picture to upload. The client bears responsibility for closing this + * stream after {@code callback} is called with success or failure. + * + * Additionally, if the stream supplies more bytes than the return value + * of {@link #getMaximumCallComposerPictureSize()}, the upload will be + * aborted and the callback will be called with an exception containing + * {@link CallComposerException#ERROR_FILE_TOO_LARGE}. + * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg). The list + * of acceptable content types can be found at 3GPP TS 26.141 sections + * 4.2 and 4.3. + * @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be + * read, as well as on which the callback will be called. + * @param callback A callback called when the upload operation terminates, either in success + * or in error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload, + @NonNull String contentType, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver callback) { + Objects.requireNonNull(pictureToUpload); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("Telephony service not available."); + } + + ParcelFileDescriptor writeFd; + ParcelFileDescriptor readFd; + try { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe(); + writeFd = pipe[1]; + readFd = pipe[0]; + } catch (IOException e) { + executor.execute(() -> callback.onError( + new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e))); + return; + } + + OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd); + + try { + telephony.uploadCallComposerPicture(getSubId(), mContext.getOpPackageName(), + contentType, readFd, new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + if (resultCode != CallComposerException.SUCCESS) { + executor.execute(() -> callback.onError( + new CallComposerException(resultCode, null))); + return; + } + ParcelUuid resultUuid = + result.getParcelable(KEY_CALL_COMPOSER_PICTURE_HANDLE, android.os.ParcelUuid.class); + if (resultUuid == null) { + Log.e(TAG, "Got null uuid without an error" + + " while uploading call composer pic"); + executor.execute(() -> callback.onError( + new CallComposerException( + CallComposerException.ERROR_UNKNOWN, null))); + return; + } + executor.execute(() -> callback.onResult(resultUuid)); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Remote exception uploading call composer pic:" + e); + e.rethrowAsRuntimeException(); + } + + executor.execute(() -> { + if (Looper.getMainLooper().isCurrentThread()) { + Log.w(TAG, "Uploading call composer picture on main thread!" + + " hic sunt dracones!"); + } + + int totalBytesRead = 0; + byte[] buffer = new byte[16 * 1024]; + try { + while (true) { + int numRead; + try { + numRead = pictureToUpload.read(buffer); + } catch (IOException e) { + Log.e(TAG, "IOException reading from input while uploading pic: " + e); + // Most likely, this was because the stream was closed. We have no way to + // tell though. + callback.onError(new CallComposerException( + CallComposerException.ERROR_INPUT_CLOSED, e)); + try { + writeFd.closeWithError("input closed"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + + if (numRead < 0) { + break; + } + + totalBytesRead += numRead; + if (totalBytesRead > getMaximumCallComposerPictureSize()) { + Log.e(TAG, "Read too many bytes from call composer pic stream: " + + totalBytesRead); + try { + callback.onError(new CallComposerException( + CallComposerException.ERROR_FILE_TOO_LARGE, null)); + writeFd.closeWithError("too large"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + + try { + output.write(buffer, 0, numRead); + } catch (IOException e) { + callback.onError(new CallComposerException( + CallComposerException.ERROR_REMOTE_END_CLOSED, e)); + try { + writeFd.closeWithError("remote end closed"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + } + } finally { + try { + output.close(); + } catch (IOException e) { + // Ignore -- we might've already closed it. + } + } + }); + } + + /** + * Returns the Group Identifier Level1 for a GSM phone. + * Return null if it is unavailable. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getGroupIdLevel1() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the Group Identifier Level1 for a GSM phone for a particular subscription. + * Return null if it is unavailable. + * + * @param subId whose subscriber id is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public String getGroupIdLevel1(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the phone number string for line 1, for example, the MSISDN + * for a GSM phone for a particular subscription. Return null if it is unavailable. + *

+ * The default SMS app can also use this. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_SMS READ_SMS}, + * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, + * that the caller is the default SMS app, + * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}) + * for any API level. + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * for apps targeting SDK API level 29 and below. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead. + */ + @Deprecated + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.READ_PHONE_NUMBERS + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getLine1Number() { + return getLine1Number(getSubId()); + } + + /** + * Returns the phone number string for line 1, for example, the MSISDN + * for a GSM phone for a particular subscription. Return null if it is unavailable. + *

+ * The default SMS app can also use this. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_SMS READ_SMS}, + * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, + * that the caller is the default SMS app, + * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}) + * for any API level. + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * for apps targeting SDK API level 29 and below. + * + * @param subId whose phone number for line 1 is returned + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.READ_PHONE_NUMBERS + }) + @UnsupportedAppUsage + public String getLine1Number(int subId) { + String number = null; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + if (number != null) { + return number; + } + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Set the line 1 phone number string and its alphatag for the current ICCID + * for display purpose only, for example, displayed in Phone Status. It won't + * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null + * value. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param alphaTag alpha-tagging of the dailing nubmer + * @param number The dialing number + * @return true if the operation was executed correctly. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead. + */ + @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean setLine1NumberForDisplay(String alphaTag, String number) { + return setLine1NumberForDisplay(getSubId(), alphaTag, number); + } + + /** + * Set the line 1 phone number string and its alphatag for the current ICCID + * for display purpose only, for example, displayed in Phone Status. It won't + * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null + * value. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId the subscriber that the alphatag and dialing number belongs to. + * @param alphaTag alpha-tagging of the dailing nubmer + * @param number The dialing number + * @return true if the operation was executed correctly. + * @hide + */ + public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) { + try { + // This API is deprecated; call the new API to allow smooth migartion. + // The new API doesn't accept null so convert null to empty string. + mSubscriptionManager.setCarrierPhoneNumber(subId, (number == null ? "" : number)); + + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.setLine1NumberForDisplayForSubscriber(subId, alphaTag, number); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return false; + } + + /** + * Returns the alphabetic identifier associated with the line 1 number. + * Return null if it is unavailable. + * @hide + * nobody seems to call this. + */ + @UnsupportedAppUsage + @TestApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public String getLine1AlphaTag() { + return getLine1AlphaTag(getSubId()); + } + + /** + * Returns the alphabetic identifier associated with the line 1 number + * for a subscription. + * Return null if it is unavailable. + * @param subId whose alphabetic identifier associated with line 1 is returned + * nobody seems to call this. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public String getLine1AlphaTag(int subId) { + String alphaTag = null; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + alphaTag = telephony.getLine1AlphaTagForDisplay(subId, + getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + if (alphaTag != null) { + return alphaTag; + } + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Return the set of subscriber IDs that should be considered "merged together" for data usage + * purposes. This is commonly {@code null} to indicate no merging is required. Any returned + * subscribers are sorted in a deterministic order. + *

+ * The returned set of subscriber IDs will include the subscriber ID corresponding to this + * TelephonyManager's subId. + * + * This is deprecated and {@link #getMergedImsisFromGroup()} should be used for data + * usage merging purpose. + * TODO: remove this API. + * + * @hide + */ + @UnsupportedAppUsage + @Deprecated + public @Nullable String[] getMergedSubscriberIds() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Return the set of IMSIs that should be considered "merged together" for data usage + * purposes. This API merges IMSIs based on subscription grouping: IMSI of those in the same + * group will all be returned. + * Return the current IMSI if there is no subscription group, see + * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group, + * otherwise return an empty array if there is a failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @NonNull String[] getMergedImsisFromGroup() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + String[] mergedImsisFromGroup = + telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName()); + if (mergedImsisFromGroup != null) { + return mergedImsisFromGroup; + } + } + } catch (RemoteException ex) { + } + return new String[0]; + } + + /** + * Returns the MSISDN string for a GSM phone. Return null if it is unavailable. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_SMS READ_SMS}, + * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, + * that the caller is the default SMS app, + * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}) + * for any API level. + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * for apps targeting SDK API level 29 and below. + * + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.READ_PHONE_NUMBERS + }) + @UnsupportedAppUsage + public String getMsisdn() { + return getMsisdn(getSubId()); + } + + /** + * Returns the MSISDN string for a GSM phone. Return null if it is unavailable. + * + * @param subId for which msisdn is returned + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_SMS READ_SMS}, + * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, + * that the caller is the default SMS app, + * or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}) + * for any API level. + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * for apps targeting SDK API level 29 and below. + * + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_SMS, + android.Manifest.permission.READ_PHONE_NUMBERS + }) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public String getMsisdn(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getMsisdnForSubscriber(subId, getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the voice mail number. Return null if it is unavailable. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public String getVoiceMailNumber() { + return getVoiceMailNumber(getSubId()); + } + + /** + * Returns the voice mail number for a subscription. + * Return null if it is unavailable. + * @param subId whose voice mail number is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public String getVoiceMailNumber(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Sets the voice mail number. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param alphaTag The alpha tag to display. + * @param number The voicemail number. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean setVoiceMailNumber(String alphaTag, String number) { + return setVoiceMailNumber(getSubId(), alphaTag, number); + } + + /** + * Sets the voicemail number for the given subscriber. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription id. + * @param alphaTag The alpha tag to display. + * @param number The voicemail number. + * @hide + */ + public boolean setVoiceMailNumber(int subId, String alphaTag, String number) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.setVoiceMailNumber(subId, alphaTag, number); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return false; + } + + /** + * Enables or disables the visual voicemail client for a phone account. + * + *

Requires that the calling app is the default dialer, or has carrier privileges (see + * {@link #hasCarrierPrivileges}), or has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param phoneAccountHandle the phone account to change the client state + * @param enabled the new state of the client + * @hide + * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should + * be implemented instead. + */ + @SystemApi + @Deprecated + @SuppressLint("RequiresPermission") + public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){ + } + + /** + * Returns whether the visual voicemail client is enabled. + * + * @param phoneAccountHandle the phone account to check for. + * @return {@code true} when the visual voicemail client is enabled for this client + * @hide + * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should + * be implemented instead. + */ + @SystemApi + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressLint("RequiresPermission") + public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){ + return false; + } + + /** + * Returns an opaque bundle of settings formerly used by the visual voicemail client for the + * subscription ID pinned to the TelephonyManager, or {@code null} if the subscription ID is + * invalid. This method allows the system dialer to migrate settings out of the pre-O visual + * voicemail client in telephony. + * + *

Requires the caller to be the system dialer. + * + * @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL + * @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @SuppressLint("RequiresPermission") + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + @Nullable + public Bundle getVisualVoicemailSettings(){ + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony + .getVisualVoicemailSettings(mContext.getOpPackageName(), mSubId); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Returns the package responsible of processing visual voicemail for the subscription ID pinned + * to the TelephonyManager. Returns {@code null} when there is no package responsible for + * processing visual voicemail for the subscription. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @see #createForSubscriptionId(int) + * @see #createForPhoneAccountHandle(PhoneAccountHandle) + * @see VisualVoicemailService + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @Nullable + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public String getVisualVoicemailPackageName() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(), + getAttributionTag(), getSubId()); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Set the visual voicemail SMS filter settings for the subscription ID pinned + * to the TelephonyManager. + * When the filter is enabled, {@link + * VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be + * called when a SMS matching the settings is received. Caller must be the default dialer, + * system dialer, or carrier visual voicemail app. + * + * @param settings The settings for the filter, or {@code null} to disable the filter. + * + * @see TelecomManager#getDefaultDialerPackage() + * @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) { + if (settings == null) { + disableVisualVoicemailSmsFilter(mSubId); + } else { + enableVisualVoicemailSmsFilter(mSubId, settings); + } + } + + /** + * Send a visual voicemail SMS. The caller must be the current default dialer. + * A {@link VisualVoicemailService} uses this method to send a command via SMS to the carrier's + * visual voicemail server. Some examples for carriers using the OMTP standard include + * activating and deactivating visual voicemail, or requesting the current visual voicemail + * provisioning status. See the OMTP Visual Voicemail specification for more information on the + * format of these SMS messages. + * + *

Requires Permission: + * {@link android.Manifest.permission#SEND_SMS SEND_SMS} + * + * @param number The destination number. + * @param port The destination port for data SMS, or 0 for text SMS. + * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream. + * @param sentIntent The sent intent passed to the {@link SmsManager} + * + * @throws SecurityException if the caller is not the current default dialer + * + * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent) + * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void sendVisualVoicemailSms(String number, int port, String text, + PendingIntent sentIntent) { + sendVisualVoicemailSmsForSubscriber(mSubId, number, port, text, sentIntent); + } + + /** + * Enables the visual voicemail SMS filter for a phone account. When the filter is + * enabled, Incoming SMS messages matching the OMTP VVM SMS interface will be redirected to the + * visual voicemail client with + * {@link android.provider.VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED}. + * + *

This takes effect only when the caller is the default dialer. The enabled status and + * settings persist through default dialer changes, but the filter will only honor the setting + * set by the current default dialer. + * + * + * @param subId The subscription id of the phone account. + * @param settings The settings for the filter. + */ + /** @hide */ + public void enableVisualVoicemailSmsFilter(int subId, + VisualVoicemailSmsFilterSettings settings) { + if(settings == null){ + throw new IllegalArgumentException("Settings cannot be null"); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.enableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId, + settings); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * Disables the visual voicemail SMS filter for a phone account. + * + *

This takes effect only when the caller is the default dialer. The enabled status and + * settings persist through default dialer changes, but the filter will only honor the setting + * set by the current default dialer. + */ + /** @hide */ + public void disableVisualVoicemailSmsFilter(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.disableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * @returns the settings of the visual voicemail SMS filter for a phone account, or {@code null} + * if the filter is disabled. + * + *

This takes effect only when the caller is the default dialer. The enabled status and + * settings persist through default dialer changes, but the filter will only honor the setting + * set by the current default dialer. + */ + /** @hide */ + @Nullable + public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony + .getVisualVoicemailSmsFilterSettings(mContext.getOpPackageName(), subId); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + + return null; + } + + /** + * @returns the settings of the visual voicemail SMS filter for a phone account set by the + * current active visual voicemail client, or {@code null} if the filter is disabled. + * + *

Requires the calling app to have READ_PRIVILEGED_PHONE_STATE permission. + */ + /** @hide */ + @Nullable + public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getActiveVisualVoicemailSmsFilterSettings(subId); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + + return null; + } + + /** + * Send a visual voicemail SMS. The IPC caller must be the current default dialer. + * + * @param phoneAccountHandle The account to send the SMS with. + * @param number The destination number. + * @param port The destination port for data SMS, or 0 for text SMS. + * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream. + * @param sentIntent The sent intent passed to the {@link SmsManager} + * + * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent) + * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent) + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.SEND_SMS) + public void sendVisualVoicemailSmsForSubscriber(int subId, String number, int port, + String text, PendingIntent sentIntent) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.sendVisualVoicemailSmsForSubscriber( + mContext.getOpPackageName(), mContext.getAttributionTag(), subId, number, + port, text, sentIntent); + } + } catch (RemoteException ex) { + } + } + + /** + * Initial SIM activation state, unknown. Not set by any carrier apps. + * @hide + */ + @SystemApi + public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0; + + /** + * indicate SIM is under activation procedure now. + * intermediate state followed by another state update with activation procedure result: + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * @hide + */ + @SystemApi + public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; + + /** + * Indicate SIM has been successfully activated with full service + * @hide + */ + @SystemApi + public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; + + /** + * Indicate SIM has been deactivated by the carrier so that service is not available + * and requires activation service to enable services. + * Carrier apps could be signalled to set activation state to deactivated if detected + * deactivated sim state and set it back to activated after successfully run activation service. + * @hide + */ + @SystemApi + public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; + + /** + * Restricted state indicate SIM has been activated but service are restricted. + * note this is currently available for data activation state. For example out of byte sim. + * @hide + */ + @SystemApi + public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4; + + /** + * Sets the voice activation state + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param activationState The voice activation state + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING} + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setVoiceActivationState(@SimActivationState int activationState) { + setVoiceActivationState(getSubId(), activationState); + } + + /** + * Sets the voice activation state for the given subscriber. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription id. + * @param activationState The voice activation state of the given subscriber. + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setVoiceActivationState(int subId, @SimActivationState int activationState) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.setVoiceActivationState(subId, activationState); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * Sets the data activation state + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param activationState The data activation state + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setDataActivationState(@SimActivationState int activationState) { + setDataActivationState(getSubId(), activationState); + } + + /** + * Sets the data activation state for the given subscriber. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription id. + * @param activationState The data activation state of the given subscriber. + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setDataActivationState(int subId, @SimActivationState int activationState) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.setDataActivationState(subId, activationState); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * Returns the voice activation state + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return voiceActivationState + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public @SimActivationState int getVoiceActivationState() { + return getVoiceActivationState(getSubId()); + } + + /** + * Returns the voice activation state for the given subscriber. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription id. + * + * @return voiceActivationState for the given subscriber + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @SimActivationState int getVoiceActivationState(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.getVoiceActivationState(subId, getOpPackageName()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return SIM_ACTIVATION_STATE_UNKNOWN; + } + + /** + * Returns the data activation state + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return dataActivationState for the given subscriber + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public @SimActivationState int getDataActivationState() { + return getDataActivationState(getSubId()); + } + + /** + * Returns the data activation state for the given subscriber. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription id. + * + * @return dataActivationState for the given subscriber + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @SimActivationState int getDataActivationState(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.getDataActivationState(subId, getOpPackageName()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return SIM_ACTIVATION_STATE_UNKNOWN; + } + + /** + * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages + * but the count is unknown. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public int getVoiceMessageCount() { + return getVoiceMessageCount(getSubId()); + } + + /** + * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does + * not have the READ_PHONE_STATE permission. + * @param subId whose voice message count is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public int getVoiceMessageCount(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return 0; + return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + return 0; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return 0; + } + } + + /** + * Retrieves the alphabetic identifier associated with the voice + * mail number. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public String getVoiceMailAlphaTag() { + return getVoiceMailAlphaTag(getSubId()); + } + + /** + * Retrieves the alphabetic identifier associated with the voice + * mail number for a subscription. + * @param subId whose alphabetic identifier associated with the + * voice mail number is returned + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public String getVoiceMailAlphaTag(int subId) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Send the special dialer code. The IPC caller must be the current default dialer or have + * carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param inputCode The special dialer code to send + * + * @throws SecurityException if the caller does not have carrier privileges or is not the + * current default dialer + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void sendDialerSpecialCode(String inputCode) { + try { + final ITelephony telephony = getITelephony(); + if (telephony == null) { + return; + } + telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode); + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#sendDialerSpecialCode RemoteException" + ex); + } + } + + /** + * Returns the IMS private user identity (IMPI) that was loaded from the ISIM. + * @return the IMPI, or null if not present or not loaded + * @hide + * @deprecated use {@link #getImsPrivateUserIdentity()} + */ + @UnsupportedAppUsage + public String getIsimImpi() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + //get the Isim Impi based on subId + return info.getIsimImpi(getSubId()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the IMS private user identity (IMPI) of the subscription that was loaded from the + * ISIM records {@link #APPTYPE_ISIM}. This value is fetched from the Elementary file EF_IMPI. + * The contents of the file is a Ip Multimedia Service Private User Identity of the user + * as defined in the section 4.2.2 of 3GPP TS 131 103. + * + * @return IMPI (IMS private user identity) of type string. + * @throws IllegalStateException in case the ISIM has’t been loaded + * @throws SecurityException if the caller does not have the required permission/privileges + * @hide + */ + @NonNull + @RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getImsPrivateUserIdentity() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) { + Rlog.e(TAG, "getImsPrivateUserIdentity(): IPhoneSubInfo instance is NULL"); + throw new RuntimeException("IMPI error: Subscriber Info is null"); + } + return info.getImsPrivateUserIdentity(getSubId(), getOpPackageName(), + getAttributionTag()); + } catch (RemoteException | NullPointerException | IllegalArgumentException ex) { + Rlog.e(TAG, "getImsPrivateUserIdentity() Exception = " + ex); + throw new RuntimeException(ex.getMessage()); + } + } + + /** + * Returns the IMS home network domain name that was loaded from the ISIM {@see #APPTYPE_ISIM}. + * @return the IMS domain name. Returns {@code null} if ISIM hasn't been loaded or IMS domain + * hasn't been loaded or isn't present on the ISIM. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @Nullable + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getIsimDomain() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + //get the Isim Domain based on subId + return info.getIsimDomain(getSubId()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the IMS public user identities (IMPU) that were loaded from the ISIM. + * @return an array of IMPU strings, with one IMPU per string, or null if + * not present or not loaded + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated use {@link #getImsPublicUserIdentities()} + */ + @UnsupportedAppUsage + @Nullable + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public String[] getIsimImpu() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + //get the Isim Impu based on subId + return info.getIsimImpu(getSubId()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the IMS public user identities (IMPU) of the subscription that was loaded from the + * ISIM records {@link #APPTYPE_ISIM}. This value is fetched from the Elementary file EF_IMPU. + * The contents of the file are Ip Multimedia Service Public User Identities of the user + * as defined in the section 4.2.4 of 3GPP TS 131 103. It contains one or more records. + * + * @return List of public user identities of type android.net.Uri or empty list if + * EF_IMPU is not available. + * @throws IllegalStateException in case the ISIM hasn’t been loaded + * @throws SecurityException if the caller does not have the required permission/privilege + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @NonNull + @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_NUMBERS, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public List getImsPublicUserIdentities() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) { + throw new RuntimeException("IMPU error: Subscriber Info is null"); + } + return info.getImsPublicUserIdentities(getSubId(), getOpPackageName(), + getAttributionTag()); + } catch (IllegalArgumentException | NullPointerException ex) { + Rlog.e(TAG, "getImsPublicUserIdentities Exception = " + ex); + } catch (RemoteException ex) { + Rlog.e(TAG, "getImsPublicUserIdentities Exception = " + ex); + ex.rethrowAsRuntimeException(); + } + return Collections.EMPTY_LIST; + } + + /** + * Device call state: No activity. + */ + public static final int CALL_STATE_IDLE = 0; + /** + * Device call state: Ringing. A new call arrived and is + * ringing or waiting. In the latter case, another call is + * already active. + */ + public static final int CALL_STATE_RINGING = 1; + /** + * Device call state: Off-hook. At least one call exists + * that is dialing, active, or on hold, and no calls are ringing + * or waiting. + */ + public static final int CALL_STATE_OFFHOOK = 2; + + /** + * Returns the state of all calls on the device. + *

+ * This method considers not only calls in the Telephony stack, but also calls via other + * {@link android.telecom.ConnectionService} implementations. + *

+ * Note: The call state returned via this method may differ from what is reported by + * {@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers + * Telephony (mobile) calls. + *

+ * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications + * targeting API level 31+. + * + * @return the current call state. + * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a + * specific telephony subscription (which allows carrier privileged apps), + * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or + * {@link TelecomManager#isInCall()}, which supplies an aggregate "in call" state for the entire + * device. + */ + @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) + @Deprecated + public @CallState int getCallState() { + if (mContext != null) { + TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); + if (telecomManager != null) { + return telecomManager.getCallState(); + } + } + return CALL_STATE_IDLE; + } + + /** + * Retrieve the call state for a specific subscription that was specified when this + * TelephonyManager instance was created. + *

Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the calling + * application has carrier privileges (see {@link #hasCarrierPrivileges}). + * @see TelephonyManager#createForSubscriptionId(int) + * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle) + * @return The call state of the subscription associated with this TelephonyManager instance. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public @CallState int getCallStateForSubscription() { + return getCallState(getSubId()); + } + + /** + * Returns the Telephony call state for calls on a specific subscription. + *

+ * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()} + * considers the state of calls from other {@link android.telecom.ConnectionService} + * implementations. + *

+ * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications + * targeting API level 31+ or that the calling application has carrier privileges + * (see {@link #hasCarrierPrivileges()}). + * + * @param subId the subscription to check call state for. + * @hide + */ + @UnsupportedAppUsage + @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true) + public @CallState int getCallState(int subId) { + ITelephony telephony = getITelephony(); + if (telephony == null) { + return CALL_STATE_IDLE; + } + try { + return telephony.getCallStateForSubscription(subId, mContext.getPackageName(), + mContext.getAttributionTag()); + } catch (RemoteException e) { + return CALL_STATE_IDLE; + } + } + + /** Data connection activity: No traffic. */ + public static final int DATA_ACTIVITY_NONE = 0x00000000; + /** Data connection activity: Currently receiving IP PPP traffic. */ + public static final int DATA_ACTIVITY_IN = 0x00000001; + /** Data connection activity: Currently sending IP PPP traffic. */ + public static final int DATA_ACTIVITY_OUT = 0x00000002; + /** Data connection activity: Currently both sending and receiving + * IP PPP traffic. */ + public static final int DATA_ACTIVITY_INOUT = DATA_ACTIVITY_IN | DATA_ACTIVITY_OUT; + /** + * Data connection is active, but physical link is down + */ + public static final int DATA_ACTIVITY_DORMANT = 0x00000004; + + /** + * Returns a constant indicating the type of activity on a data connection + * (cellular). + * + * @see #DATA_ACTIVITY_NONE + * @see #DATA_ACTIVITY_IN + * @see #DATA_ACTIVITY_OUT + * @see #DATA_ACTIVITY_INOUT + * @see #DATA_ACTIVITY_DORMANT + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public int getDataActivity() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return DATA_ACTIVITY_NONE; + return telephony.getDataActivityForSubId( + getSubId(SubscriptionManager.getActiveDataSubscriptionId())); + } catch (RemoteException ex) { + // the phone process is restarting. + return DATA_ACTIVITY_NONE; + } catch (NullPointerException ex) { + // the phone process is restarting. + return DATA_ACTIVITY_NONE; + } + } + + /** @hide */ + @IntDef(prefix = {"DATA_"}, value = { + DATA_UNKNOWN, + DATA_DISCONNECTED, + DATA_CONNECTING, + DATA_CONNECTED, + DATA_SUSPENDED, + DATA_DISCONNECTING, + DATA_HANDOVER_IN_PROGRESS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DataState{} + + /** Data connection state: Unknown. Used before we know the state. */ + public static final int DATA_UNKNOWN = -1; + /** Data connection state: Disconnected. IP traffic not available. */ + public static final int DATA_DISCONNECTED = 0; + /** Data connection state: Currently setting up a data connection. */ + public static final int DATA_CONNECTING = 1; + /** Data connection state: Connected. IP traffic should be available. */ + public static final int DATA_CONNECTED = 2; + /** Data connection state: Suspended. The connection is up, but IP + * traffic is temporarily unavailable. For example, in a 2G network, + * data activity may be suspended when a voice call arrives. */ + public static final int DATA_SUSPENDED = 3; + /** + * Data connection state: Disconnecting. + * + * IP traffic may be available but will cease working imminently. + */ + public static final int DATA_DISCONNECTING = 4; + + /** + * Data connection state: Handover in progress. The connection is being transited from cellular + * network to IWLAN, or from IWLAN to cellular network. + */ + public static final int DATA_HANDOVER_IN_PROGRESS = 5; + + /** + * Used for checking if the SDK version for {@link TelephonyManager#getDataState} is above Q. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long GET_DATA_STATE_R_VERSION = 148534348L; + + /** + * Returns a constant indicating the current data connection state + * (cellular). + * + * @see #DATA_DISCONNECTED + * @see #DATA_CONNECTING + * @see #DATA_CONNECTED + * @see #DATA_SUSPENDED + * @see #DATA_DISCONNECTING + * @see #DATA_HANDOVER_IN_PROGRESS + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public int getDataState() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return DATA_DISCONNECTED; + int state = telephony.getDataStateForSubId( + getSubId(SubscriptionManager.getActiveDataSubscriptionId())); + if (state == TelephonyManager.DATA_DISCONNECTING + && !Compatibility.isChangeEnabled(GET_DATA_STATE_R_VERSION)) { + return TelephonyManager.DATA_CONNECTED; + } + + return state; + } catch (RemoteException ex) { + // the phone process is restarting. + return DATA_DISCONNECTED; + } catch (NullPointerException ex) { + return DATA_DISCONNECTED; + } + } + + private static ITelephony getITelephony() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (!sServiceHandleCacheEnabled) { + return ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); + } + + if (sITelephony == null) { + ITelephony temp = ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sITelephony == null && temp != null) { + try { + sITelephony = temp; + sITelephony.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sITelephony = null; + } + } + } + } + return sITelephony; + } + + private IOns getIOns() { + return IOns.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getOpportunisticNetworkServiceRegisterer() + .get()); + } + + // + // + // PhoneStateListener + // + // + + /** + * Registers a listener object to receive notification of changes + * in specified telephony states. + *

+ * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony + * state of interest in the events argument. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the listener object and passes the current (updated) + * values. + *

+ * To un-register a listener, pass the listener object and set the events argument to + * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0). + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds, + * pass a separate listener object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you must + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of listeners will cause system + * instability. If a process has registered too many listeners without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more listeners. + * + * @param listener The {@link PhoneStateListener} object to register + * (or unregister) + * @param events The telephony state(s) of interest to the listener, + * as a bitwise-OR combination of {@link PhoneStateListener} + * LISTEN_ flags. + * @deprecated Use {@link #registerTelephonyCallback(Executor, TelephonyCallback)}. + */ + @Deprecated + public void listen(PhoneStateListener listener, int events) { + if (mContext == null) return; + boolean notifyNow = (getITelephony() != null); + TelephonyRegistryManager telephonyRegistry = + (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistry != null) { + Set renouncedPermissions = getRenouncedPermissions(); + boolean renounceFineLocationAccess = renouncedPermissions + .contains(Manifest.permission.ACCESS_FINE_LOCATION); + boolean renounceCoarseLocationAccess = renouncedPermissions + .contains(Manifest.permission.ACCESS_COARSE_LOCATION); + telephonyRegistry.listenFromListener(mSubId, renounceFineLocationAccess, + renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag(), + listener, events, notifyNow); + } else { + Rlog.w(TAG, "telephony registry not ready."); + } + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERI_"}, value = { + ERI_ON, + ERI_OFF, + ERI_FLASH + }) + public @interface EriIconIndex {} + + /** + * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_ON = 0; + + /** + * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_OFF = 1; + + /** + * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_FLASH = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERI_ICON_MODE_"}, value = { + ERI_ICON_MODE_NORMAL, + ERI_ICON_MODE_FLASH + }) + public @interface EriIconMode {} + + /** + * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that + * the ERI icon should be displayed normally. + * + * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 + * @hide + */ + public static final int ERI_ICON_MODE_NORMAL = 0; + + /** + * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that + * the ERI icon should be flashing. + * + * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 + * @hide + */ + public static final int ERI_ICON_MODE_FLASH = 1; + + /** + * Returns the CDMA ERI icon display number. The number is assigned by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers. + * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() { + return getCdmaEriIconIndex(getSubId()); + } + + /** + * Returns the CDMA ERI icon index to display for a subscription. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @UnsupportedAppUsage + public @EriIconIndex int getCdmaEriIconIndex(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return -1; + return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + // the phone process is restarting. + return -1; + } catch (NullPointerException ex) { + return -1; + } + } + + /** + * Returns the CDMA ERI icon mode for a subscription. + * 0 - ON + * 1 - FLASHING + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @UnsupportedAppUsage + public @EriIconMode int getCdmaEriIconMode(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return -1; + return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + // the phone process is restarting. + return -1; + } catch (NullPointerException ex) { + return -1; + } + } + + /** + * Returns the CDMA ERI text, + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public String getCdmaEriText() { + return getCdmaEriText(getSubId()); + } + + /** + * Returns the CDMA ERI text, of a subscription + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @UnsupportedAppUsage + public String getCdmaEriText(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + // the phone process is restarting. + return null; + } catch (NullPointerException ex) { + return null; + } + } + + // TODO(b/316183370): replace all @code with @link in javadoc after feature is released + /** + * @return true if the current device is "voice capable". + *

+ * "Voice capable" means that this device supports circuit-switched + * (i.e. voice) phone calls over the telephony network, and is allowed + * to display the in-call UI while a cellular voice call is active. + * This will be false on "data only" devices which can't make voice + * calls and don't support any in-call UI. + *

+ * Note: the meaning of this flag is subtly different from the + * PackageManager.FEATURE_TELEPHONY system feature, which is available + * on any device with a telephony radio, even if the device is + * data-only. + * @deprecated Replaced by {@code #isDeviceVoiceCapable()}. Starting from Android 15, voice + * capability may also be overridden by carriers for a given subscription. For voice capable + * device (when {@code #isDeviceVoiceCapable} return {@code true}), caller should check for + * subscription-level voice capability as well. See {@code #isDeviceVoiceCapable} for details. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + @Deprecated + public boolean isVoiceCapable() { + if (mContext == null) return true; + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); + } + + /** + * @return true if the current device is "voice capable". + *

+ * "Voice capable" means that this device supports circuit-switched or IMS packet switched + * (i.e. voice) phone calls over the telephony network, and is allowed to display the in-call + * UI while a cellular voice call is active. This will be false on "data only" devices which + * can't make voice calls and don't support any in-call UI. + *

+ * Note: the meaning of this flag is subtly different from the PackageManager + * .FEATURE_TELEPHONY system feature, which is available on any device with a telephony + * radio, even if the device is data-only. + *

+ * Starting from Android 15, voice capability may also be overridden by carrier for a given + * subscription on a voice capable device. To check if a subscription is "voice capable", + * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if + * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} is included. + * + * @see SubscriptionInfo#getServiceCapabilities() + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) + public boolean isDeviceVoiceCapable() { + return isVoiceCapable(); + } + + /** + * @return true if the current device supports sms service. + *

+ * If true, this means that the device supports both sending and + * receiving sms via the telephony network. + *

+ * Note: Voicemail waiting sms, cell broadcasting sms, and MMS are + * disabled when device doesn't support sms. + * @deprecated Replaced by {@code #isDeviceSmsCapable()}. Starting from Android 15, SMS + * capability may also be overridden by carriers for a given subscription. For SMS capable + * device (when {@code #isDeviceSmsCapable} return {@code true}), caller should check for + * subscription-level SMS capability as well. See {@code #isDeviceSmsCapable} for details. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public boolean isSmsCapable() { + if (mContext == null) return true; + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_sms_capable); + } + + /** + * @return true if the current device supports SMS service. + *

+ * If true, this means that the device supports both sending and + * receiving SMS via the telephony network. + *

+ * Note: Voicemail waiting SMS, cell broadcasting SMS, and MMS are + * disabled when device doesn't support SMS. + *

+ * Starting from Android 15, SMS capability may also be overridden by carriers for a given + * subscription on an SMS capable device. To check if a subscription is "SMS capable", + * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if + * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS} is included. + * + * @see SubscriptionInfo#getServiceCapabilities() + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) + public boolean isDeviceSmsCapable() { + return isSmsCapable(); + } + + /** + * Requests all available cell information from all radios on the device including the + * camped/registered, serving, and neighboring cells. + * + *

The response can include one or more {@link android.telephony.CellInfoGsm CellInfoGsm}, + * {@link android.telephony.CellInfoCdma CellInfoCdma}, + * {@link android.telephony.CellInfoTdscdma CellInfoTdscdma}, + * {@link android.telephony.CellInfoLte CellInfoLte}, and + * {@link android.telephony.CellInfoWcdma CellInfoWcdma} objects, in any combination. + * It is typical to see instances of one or more of any these in the list. In addition, zero + * or more of the returned objects may be considered registered; that is, their + * {@link android.telephony.CellInfo#isRegistered CellInfo.isRegistered()} + * methods may return true, indicating that the cell is being used or would be used for + * signaling communication if necessary. + * + *

Beginning with {@link android.os.Build.VERSION_CODES#Q Android Q}, + * if this API results in a change of the cached CellInfo, that change will be reported via + * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}. + * + *

Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no + * longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps + * will receive the latest cached results, which may not be current. Apps targeting + * {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated + * CellInfo should call + * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}; + * however, in all cases, updates will be rate-limited and are not guaranteed. To determine the + * recency of CellInfo data, callers should check + * {@link android.telephony.CellInfo#getTimeStamp CellInfo#getTimeStamp()}. + * + *

This method returns valid data for devices with + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases + * where only partial information is available for a particular CellInfo entry, unavailable + * fields will be reported as {@link android.telephony.CellInfo#UNAVAILABLE}. All reported + * cells will include at least a valid set of technology-specific identification info and a + * power level measurement. + * + *

This method is preferred over using {@link + * android.telephony.TelephonyManager#getCellLocation getCellLocation()}. + * + * @return List of {@link android.telephony.CellInfo}; null if cell + * information is unavailable. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public List getAllCellInfo() { + //ccynice for fake cellinfo + List cellInfos = new ArrayList(); + String cellInfo_list_str = SystemProperties.get("persist.sm.allCellInfo","460,00,7394564,0,34053,-59,-65,-7,26,10,0"); + if(!cellInfo_list_str.isEmpty()) + { + String[] parts = cellInfo_list_str.split(","); + if(parts!=null && parts.length==11){ + try { + int mcc = Integer.parseInt(parts[0]); + int mnc = Integer.parseInt(parts[1]); + int ci = Integer.parseInt(parts[2]); + int pci = Integer.parseInt(parts[3]); + int tac = Integer.parseInt(parts[4]); + + int rssi = Integer.parseInt(parts[5]); + int rsrp = Integer.parseInt(parts[6]); + int rsrq = Integer.parseInt(parts[7]); + int rssnr = Integer.parseInt(parts[8]); + int cqi = Integer.parseInt(parts[9]); + int timingAdvance = Integer.parseInt(parts[10]); + + CellInfoLte mCellInfoLte = new CellInfoLte(); + CellIdentityLte mCellIdentityLte = new CellIdentityLte( mcc, mnc, ci, pci, tac); + CellSignalStrengthLte mCellSignalStrengthLte = new CellSignalStrengthLte( rssi, rsrp, rsrq, rssnr, cqi, timingAdvance); + + + CellConfigLte mCellConfig = new CellConfigLte(); + mCellInfoLte.setCellIdentity(mCellIdentityLte); + mCellInfoLte.setCellSignalStrength(mCellSignalStrengthLte); + mCellInfoLte.setCellConfig(mCellConfig); + + cellInfos.add(mCellInfoLte); + + }catch (NumberFormatException e) { + Log.e(TAG, "persist.sm.allCellInfo 字符串不是有效的整数!"); + } + } + } + + + return cellInfos; +/* + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getAllCellInfo(getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null;*/ + } + + /** Callback for providing asynchronous {@link CellInfo} on request */ + public abstract static class CellInfoCallback { + /** + * Success response to + * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}. + * + * Invoked when there is a response to + * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()} + * to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty + * list will be provided. If an error occurs, null will be provided unless the onError + * callback is overridden. + * + * @param cellInfo a list of {@link CellInfo} or an empty list. + * + * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()} + */ + public abstract void onCellInfo(@NonNull List cellInfo); + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERROR_"}, value = {ERROR_TIMEOUT, ERROR_MODEM_ERROR}) + public @interface CellInfoCallbackError {} + + /** + * The system timed out waiting for a response from the Radio. + */ + public static final int ERROR_TIMEOUT = 1; + + /** + * The modem returned a failure. + */ + public static final int ERROR_MODEM_ERROR = 2; + + /** + * Error response to + * {@link TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}. + * + * Invoked when an error condition prevents updated {@link CellInfo} from being fetched + * and returned from the modem. Callers of requestCellInfoUpdate() should override this + * function to receive detailed status information in the event of an error. By default, + * this function will invoke onCellInfo() with null. + * + * @param errorCode an error code indicating the type of failure. + * @param detail a Throwable object with additional detail regarding the failure if + * available, otherwise null. + */ + public void onError(@CellInfoCallbackError int errorCode, @Nullable Throwable detail) { + // By default, simply invoke the success callback with an empty list. + onCellInfo(new ArrayList()); + } + }; + + /** + * Used for checking if the target SDK version for the current process is S or above. + * + *

Applies to the following methods: + * {@link #requestCellInfoUpdate}, + * {@link #setPreferredOpportunisticDataSubscription}, + * {@link #updateAvailableNetworks}, + * requestNumberVerification(), + * setSimPowerStateForSlot(), + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long NULL_TELEPHONY_THROW_NO_CB = 182185642L; + + /** + * Requests all available cell information from the current subscription for observed + * camped/registered, serving, and neighboring cells. + * + *

Any available results from this request will be provided by calls to + * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()} + * for each active subscription. + * + *

This method returns valid data for devices with + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices + * that do not implement this feature, the behavior is not reliable. + * + * @param executor the executor on which callback will be invoked. + * @param callback a callback to receive CellInfo. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void requestCellInfoUpdate( + @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + + telephony.requestCellInfoUpdate( + getSubId(), + new ICellInfoCallback.Stub() { + @Override + public void onCellInfo(List cellInfo) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCellInfo(cellInfo)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onError(int errorCode, String exceptionName, String message) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onError( + errorCode, + createThrowableByClassName(exceptionName, message))); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }, getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + runOnBackgroundThread(() -> executor.execute( + () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex))); + } + } + + /** + * Requests all available cell information from the current subscription for observed + * camped/registered, serving, and neighboring cells. + * + *

Any available results from this request will be provided by calls to + * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()} + * for each active subscription. + * + *

This method returns valid data for devices with + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices + * that do not implement this feature, the behavior is not reliable. + * + * @param workSource the requestor to whom the power consumption for this should be attributed. + * @param executor the executor on which callback will be invoked. + * @param callback a callback to receive CellInfo. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.MODIFY_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void requestCellInfoUpdate(@NonNull WorkSource workSource, + @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + + telephony.requestCellInfoUpdateWithWorkSource( + getSubId(), + new ICellInfoCallback.Stub() { + @Override + public void onCellInfo(List cellInfo) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCellInfo(cellInfo)); + } finally { + Binder.restoreCallingIdentity(identity); + } + + } + + @Override + public void onError(int errorCode, String exceptionName, String message) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onError( + errorCode, + createThrowableByClassName(exceptionName, message))); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }, getOpPackageName(), getAttributionTag(), workSource); + } catch (RemoteException ex) { + runOnBackgroundThread(() -> executor.execute( + () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex))); + } + } + + private static Throwable createThrowableByClassName(String className, String message) { + if (className == null) { + return null; + } + try { + Class c = Class.forName(className); + return (Throwable) c.getConstructor(String.class).newInstance(message); + } catch (ReflectiveOperationException | ClassCastException e) { + } + return new RuntimeException(className + ": " + message); + } + + /** + * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged + * PhoneStateListener.onCellInfoChanged} will be invoked. + *

+ * The default, 0, means invoke onCellInfoChanged when any of the reported + * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue + * A onCellInfoChanged. + *

+ * @param rateInMillis the rate + * + * @hide + */ + public void setCellInfoListRate(int rateInMillis, int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.setCellInfoListRate(rateInMillis, subId); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * Returns the MMS user agent. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public String getMmsUserAgent() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getMmsUserAgent(getSubId()); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Returns the MMS user agent profile URL. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public String getMmsUAProfUrl() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getMmsUAProfUrl(getSubId()); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Get the first active portIndex from the corresponding physical slot index. + * @param physicalSlotIndex physical slot index + * @return first active port index or INVALID_PORT_INDEX if no port is active + */ + private int getFirstActivePortIndex(int physicalSlotIndex) { + UiccSlotInfo[] slotInfos = getUiccSlotsInfo(); + if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length + && slotInfos[physicalSlotIndex] != null) { + Optional result = slotInfos[physicalSlotIndex].getPorts().stream() + .filter(portInfo -> portInfo.isActive()).findFirst(); + if (result.isPresent()) { + return result.get().getPortIndex(); + } + } + return INVALID_PORT_INDEX; + } + + /** + * Opens a logical channel to the ICC card. + * + * Input parameters equivalent to TS 27.007 AT+CCHO command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param AID Application id. See ETSI 102.221 and 101.220. + * @return an IccOpenLogicalChannelResponse object. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)} + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @Deprecated + public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) { + return iccOpenLogicalChannel(getSubId(), AID, -1); + } + + /** + * Opens a logical channel to the ICC card using the physical slot index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index. + * + * This operation wraps two APDU instructions: + *

    + *
  • MANAGE CHANNEL to open a logical channel
  • + *
  • SELECT the given {@code AID} using the given {@code p2}
  • + *
+ * + * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08, + * and 0x0C are guaranteed to be supported. + * + * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be + * considered an error and the channel shall not be opened. + * + * Input parameters equivalent to TS 27.007 AT+CCHO command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param slotIndex the physical slot index of the ICC card + * @param aid Application id. See ETSI 102.221 and 101.220. + * @param p2 P2 parameter (described in ISO 7816-4). + * @return an IccOpenLogicalChannelResponse object. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), + * instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)} + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @Nullable + @Deprecated + public IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int slotIndex, + @Nullable String aid, int p2) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + IccLogicalChannelRequest request = new IccLogicalChannelRequest(); + request.slotIndex = slotIndex; + request.portIndex = getFirstActivePortIndex(slotIndex); + request.aid = aid; + request.p2 = p2; + request.callingPackage = getOpPackageName(); + request.binder = new Binder(); + return telephony.iccOpenLogicalChannel(request); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Opens a logical channel to the ICC card using the physical slot index and port index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index and port index. + * + * This operation wraps two APDU instructions: + *

    + *
  • MANAGE CHANNEL to open a logical channel
  • + *
  • SELECT the given {@code AID} using the given {@code p2}
  • + *
+ * + * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08, + * and 0x0C are guaranteed to be supported. + * + * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be + * considered an error and the channel shall not be opened. + * + * Input parameters equivalent to TS 27.007 AT+CCHO command. + * + * @param slotIndex the physical slot index of the ICC card + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * @param aid Application id. See ETSI 102.221 and 101.220. + * @param p2 P2 parameter (described in ISO 7816-4). + * @return an IccOpenLogicalChannelResponse object. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @NonNull + public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex, + int portIndex, @Nullable String aid, int p2) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + IccLogicalChannelRequest request = new IccLogicalChannelRequest(); + request.slotIndex = slotIndex; + request.portIndex = portIndex; + request.aid = aid; + request.p2 = p2; + request.callingPackage = getOpPackageName(); + request.binder = new Binder(); + return telephony.iccOpenLogicalChannel(request); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Opens a logical channel to the ICC card. + * + * This operation wraps two APDU instructions: + *
    + *
  • MANAGE CHANNEL to open a logical channel
  • + *
  • SELECT the given {@code AID} using the given {@code p2}
  • + *
+ * + * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08, + * and 0x0C are guaranteed to be supported. + * + * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be + * considered an error and the channel shall not be opened. + * + * Input parameters equivalent to TS 27.007 AT+CCHO command. + * + * It is strongly recommended that callers of this should firstly create a new TelephonyManager + * instance by calling {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so + * can result in unpredictable and detrimental behavior like callers can end up talking to the + * wrong SIM card. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param AID Application id. See ETSI 102.221 and 101.220. + * @param p2 P2 parameter (described in ISO 7816-4). + * @return an IccOpenLogicalChannelResponse object. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) { + return iccOpenLogicalChannel(getSubId(), AID, p2); + } + + /** + * Opens a logical channel to the ICC card. + * + * This operation wraps two APDU instructions: + *

    + *
  • MANAGE CHANNEL to open a logical channel
  • + *
  • SELECT the given {@code AID} using the given {@code p2}
  • + *
+ * + * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08, + * and 0x0C are guaranteed to be supported. + * + * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be + * considered an error and the channel shall not be opened. + * + * Input parameters equivalent to TS 27.007 AT+CCHO command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param AID Application id. See ETSI 102.221 and 101.220. + * @param p2 P2 parameter (described in ISO 7816-4). + * @return an IccOpenLogicalChannelResponse object. + * @hide + */ + public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + IccLogicalChannelRequest request = new IccLogicalChannelRequest(); + request.subId = subId; + request.callingPackage = getOpPackageName(); + request.aid = AID; + request.p2 = p2; + request.binder = new Binder(); + return telephony.iccOpenLogicalChannel(request); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Closes a previously opened logical channel to the ICC card using the physical slot index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index. + * + * Input parameters equivalent to TS 27.007 AT+CCHC command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param slotIndex the physical slot index of the ICC card + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @return true if the channel was closed successfully. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), + * instead use {@link #iccCloseLogicalChannelByPort(int, int, int)} + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @Deprecated + public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + IccLogicalChannelRequest request = new IccLogicalChannelRequest(); + request.slotIndex = slotIndex; + request.portIndex = getFirstActivePortIndex(slotIndex); + request.channel = channel; + return telephony.iccCloseLogicalChannel(request); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } catch (IllegalStateException ex) { + Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex); + } + return false; + } + + /** + * Closes a previously opened logical channel to the ICC card using the physical slot index and + * port index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index and port index. + * + * Input parameters equivalent to TS 27.007 AT+CCHC command. + * + * @param slotIndex the physical slot index of the ICC card + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * + * @throws IllegalStateException if the Telephony process is not currently available or modem + * currently can't process this command. + * @throws IllegalArgumentException if invalid arguments are passed. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + IccLogicalChannelRequest request = new IccLogicalChannelRequest(); + request.slotIndex = slotIndex; + request.portIndex = portIndex; + request.channel = channel; + telephony.iccCloseLogicalChannel(request); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Closes a previously opened logical channel to the ICC card. + * + * Input parameters equivalent to TS 27.007 AT+CCHC command. + * It is strongly recommended that callers of this API should firstly create + * new TelephonyManager instance by calling + * {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so can result in + * unpredictable and detrimental behavior like callers can end up talking to the wrong SIM card. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @return true if the channel was closed successfully. + * @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean iccCloseLogicalChannel(int channel) { + try { + return iccCloseLogicalChannel(getSubId(), channel); + } catch (IllegalStateException ex) { + Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex); + } + return false; + } + + /** + * Closes a previously opened logical channel to the ICC card. + * + * Input parameters equivalent to TS 27.007 AT+CCHC command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @return true if the channel was closed successfully. + * @hide + */ + public boolean iccCloseLogicalChannel(int subId, int channel) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + IccLogicalChannelRequest request = new IccLogicalChannelRequest(); + request.subId = subId; + request.channel = channel; + return telephony.iccCloseLogicalChannel(request); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } catch (IllegalStateException ex) { + Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex); + } + return false; + } + + /** + * Transmit an APDU to the ICC card over a logical channel using the physical slot index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index. + * + * Input parameters equivalent to TS 27.007 AT+CGLA command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param slotIndex the physical slot index of the ICC card + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at the end, or null if + * there is an issue connecting to the Telephony service. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), + * instead use + * {@link #iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, String)} + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @Nullable + @Deprecated + public String iccTransmitApduLogicalChannelBySlot(int slotIndex, int channel, int cla, + int instruction, int p1, int p2, int p3, @Nullable String data) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.iccTransmitApduLogicalChannelByPort(slotIndex, + getFirstActivePortIndex(slotIndex), channel, cla, instruction, + p1, p2, p3, data); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Transmit an APDU to the ICC card over a logical channel using the physical slot index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index. + * + * Input parameters equivalent to TS 27.007 AT+CGLA command. + * + * @param slotIndex the physical slot index of the ICC card + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at the end, or null if + * there is an issue connecting to the Telephony service. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @NonNull + public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel, + int cla, int instruction, int p1, int p2, int p3, @Nullable String data) { + String response; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + response = telephony.iccTransmitApduLogicalChannelByPort(slotIndex, portIndex, + channel, cla, instruction, p1, p2, p3, data); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + return response; + } + + /** + * Transmit an APDU to the ICC card over a logical channel. + * + * Input parameters equivalent to TS 27.007 AT+CGLA command. + * + * It is strongly recommended that callers of this API should firstly create a new + * TelephonyManager instance by calling + * {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so can result in + * unpredictable and detrimental behavior like callers can end up talking to the wrong SIM card. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at + * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String iccTransmitApduLogicalChannel(int channel, int cla, + int instruction, int p1, int p2, int p3, String data) { + return iccTransmitApduLogicalChannel(getSubId(), channel, cla, + instruction, p1, p2, p3, data); + } + + /** + * Transmit an APDU to the ICC card over a logical channel. + * + * Input parameters equivalent to TS 27.007 AT+CGLA command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param channel is the channel id to be closed as returned by a successful + * iccOpenLogicalChannel. + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at + * the end. + * @hide + */ + public String iccTransmitApduLogicalChannel(int subId, int channel, int cla, + int instruction, int p1, int p2, int p3, String data) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.iccTransmitApduLogicalChannel(subId, channel, cla, + instruction, p1, p2, p3, data); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return ""; + } + + /** + * Transmit an APDU to the ICC card over the basic channel using the physical slot index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index. + * + * Input parameters equivalent to TS 27.007 AT+CSIM command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param slotIndex the physical slot index of the ICC card to target + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at + * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP), + * instead use + * {@link #iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, String)} + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @NonNull + @Deprecated + public String iccTransmitApduBasicChannelBySlot(int slotIndex, int cla, int instruction, int p1, + int p2, int p3, @Nullable String data) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.iccTransmitApduBasicChannelByPort(slotIndex, + getFirstActivePortIndex(slotIndex), getOpPackageName(), + cla, instruction, p1, p2, p3, data); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Transmit an APDU to the ICC card over the basic channel using the physical slot index. + * + * Use this method when no subscriptions are available on the SIM and the operation must be + * performed using the physical slot index. + * + * Input parameters equivalent to TS 27.007 AT+CSIM command. + * + * @param slotIndex the physical slot index of the ICC card to target + * @param portIndex The port index is an enumeration of the ports available on the UICC. + * Use {@link UiccPortInfo#getPortIndex()} to get portIndex. + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at + * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + @NonNull + public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla, + int instruction, int p1, int p2, int p3, @Nullable String data) { + String response; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + response = telephony.iccTransmitApduBasicChannelByPort(slotIndex, portIndex, + getOpPackageName(), cla, instruction, p1, p2, p3, data); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + throw ex.rethrowAsRuntimeException(); + } + return response; + } + /** + * Transmit an APDU to the ICC card over the basic channel. + * + * Input parameters equivalent to TS 27.007 AT+CSIM command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at + * the end. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String iccTransmitApduBasicChannel(int cla, + int instruction, int p1, int p2, int p3, String data) { + return iccTransmitApduBasicChannel(getSubId(), cla, + instruction, p1, p2, p3, data); + } + + /** + * Transmit an APDU to the ICC card over the basic channel. + * + * Input parameters equivalent to TS 27.007 AT+CSIM command. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param cla Class of the APDU command. + * @param instruction Instruction of the APDU command. + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU + * is sent to the SIM. + * @param data Data to be sent with the APDU. + * @return The APDU response from the ICC card with the status appended at + * the end. + * @hide + */ + public String iccTransmitApduBasicChannel(int subId, int cla, + int instruction, int p1, int p2, int p3, String data) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.iccTransmitApduBasicChannel(subId, getOpPackageName(), cla, + instruction, p1, p2, p3, data); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return ""; + } + + /** + * Returns the response APDU for a command APDU sent through SIM_IO. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param fileID + * @param command + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. + * @param filePath + * @return The APDU response. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3, + String filePath) { + return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath); + } + + /** + * Returns the response APDU for a command APDU sent through SIM_IO. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param fileID + * @param command + * @param p1 P1 value of the APDU command. + * @param p2 P2 value of the APDU command. + * @param p3 P3 value of the APDU command. + * @param filePath + * @return The APDU response. + * @hide + */ + public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2, + int p3, String filePath) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.iccExchangeSimIO(subId, fileID, command, p1, p2, p3, filePath); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; + } + + /** + * Send ENVELOPE to the SIM and return the response. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param content String containing SAT/USAT response in hexadecimal + * format starting with command tag. See TS 102 223 for + * details. + * @return The APDU response from the ICC card in hexadecimal format + * with the last 4 bytes being the status word. If the command fails, + * returns an empty string. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String sendEnvelopeWithStatus(String content) { + return sendEnvelopeWithStatus(getSubId(), content); + } + + /** + * Send ENVELOPE to the SIM and return the response. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param content String containing SAT/USAT response in hexadecimal + * format starting with command tag. See TS 102 223 for + * details. + * @return The APDU response from the ICC card in hexadecimal format + * with the last 4 bytes being the status word. If the command fails, + * returns an empty string. + * @hide + */ + public String sendEnvelopeWithStatus(int subId, String content) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.sendEnvelopeWithStatus(subId, content); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return ""; + } + + /** + * Read one of the NV items defined in com.android.internal.telephony.RadioNVItems. + * Used for device configuration by some CDMA operators. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param itemID the ID of the item to read. + * @return the NV item as a String, or null on any failure. + * + * @hide + */ + @UnsupportedAppUsage + public String nvReadItem(int itemID) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.nvReadItem(itemID); + } catch (RemoteException ex) { + Rlog.e(TAG, "nvReadItem RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "nvReadItem NPE", ex); + } + return ""; + } + + /** + * Write one of the NV items defined in com.android.internal.telephony.RadioNVItems. + * Used for device configuration by some CDMA operators. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param itemID the ID of the item to read. + * @param itemValue the value to write, as a String. + * @return true on success; false on any failure. + * + * @hide + */ + public boolean nvWriteItem(int itemID, String itemValue) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.nvWriteItem(itemID, itemValue); + } catch (RemoteException ex) { + Rlog.e(TAG, "nvWriteItem RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "nvWriteItem NPE", ex); + } + return false; + } + + /** + * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage. + * Used for device configuration by some CDMA operators. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param preferredRoamingList byte array containing the new PRL. + * @return true on success; false on any failure. + * + * @hide + */ + public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.nvWriteCdmaPrl(preferredRoamingList); + } catch (RemoteException ex) { + Rlog.e(TAG, "nvWriteCdmaPrl RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "nvWriteCdmaPrl NPE", ex); + } + return false; + } + + /** + * Perform the specified type of NV config reset. The radio will be taken offline + * and the device must be rebooted after the operation. Used for device + * configuration by some CDMA operators. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and + * {@link #resetRadioConfig()} for reset type 3 (b/116476729) + * + * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset + * @return true on success; false on any failure. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public boolean nvResetConfig(int resetType) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (resetType == 1 /*1: reload NV reset */) { + return telephony.rebootModem(getSlotIndex()); + } else if (resetType == 3 /*3: factory NV reset */) { + return telephony.resetModemConfig(getSlotIndex()); + } else { + Rlog.e(TAG, "nvResetConfig unsupported reset type"); + } + } + } catch (RemoteException ex) { + Rlog.e(TAG, "nvResetConfig RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "nvResetConfig NPE", ex); + } + return false; + } + + /** + * Rollback modem configurations to factory default except some config which are in allowlist. + * Used for device configuration by some carriers. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return {@code true} on success; {@code false} on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean resetRadioConfig() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.resetModemConfig(getSlotIndex()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "resetRadioConfig RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "resetRadioConfig NPE", ex); + } + return false; + } + + /** + * Generate a radio modem reset. Used for device configuration by some carriers. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return {@code true} on success; {@code false} on any failure. + * + * @deprecated Using {@link #rebootModem()} instead. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean rebootRadio() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.rebootModem(getSlotIndex()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "rebootRadio RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "rebootRadio NPE", ex); + } + return false; + } + + /** + * Generate a radio modem reset. Used for device configuration by some carriers. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws RuntimeException + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void rebootModem() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("telephony service is null."); + } + telephony.rebootModem(getSlotIndex()); + } catch (RemoteException ex) { + Rlog.e(TAG, "rebootRadio RemoteException", ex); + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Return an appropriate subscription ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the provided + * subscription ID is returned. Otherwise, the default subscription ID will be returned. + * + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int getSubscriptionId() { + return getSubId(); + } + + /** + * Return an appropriate subscription ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the provided + * subscription ID is returned. Otherwise, the default subscription ID will be returned. + * + */ + private int getSubId() { + if (SubscriptionManager.isUsableSubIdValue(mSubId)) { + return mSubId; + } + return SubscriptionManager.getDefaultSubscriptionId(); + } + + /** + * Return an appropriate subscription ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the provided + * subId is returned. Otherwise, the preferred subId which is based on caller's context is + * returned. + * {@see SubscriptionManager#getDefaultDataSubscriptionId()} + * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()} + * {@see SubscriptionManager#getDefaultSmsSubscriptionId()} + */ + @UnsupportedAppUsage + private int getSubId(int preferredSubId) { + if (SubscriptionManager.isUsableSubIdValue(mSubId)) { + return mSubId; + } + return preferredSubId; + } + + /** + * Return an appropriate phone ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the phoneId + * associated with the provided subId is returned. Otherwise, the default phoneId associated + * with the default subId will be returned. + */ + private int getPhoneId() { + return SubscriptionManager.getPhoneId(getSubId()); + } + + /** + * Return an appropriate phone ID for any situation. + * + * If this object has been created with {@link #createForSubscriptionId}, then the phoneId + * associated with the provided subId is returned. Otherwise, return the phoneId associated + * with the preferred subId based on caller's context. + * {@see SubscriptionManager#getDefaultDataSubscriptionId()} + * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()} + * {@see SubscriptionManager#getDefaultSmsSubscriptionId()} + */ + @UnsupportedAppUsage + private int getPhoneId(int preferredSubId) { + return SubscriptionManager.getPhoneId(getSubId(preferredSubId)); + } + + /** + * Return an appropriate slot index for any situation. + * + * if this object has been created with {@link #createForSubscriptionId}, then the slot index + * associated with the provided subId is returned. Otherwise, return the slot index associated + * with the default subId. + * If SIM is not inserted, return default SIM slot index. + * + * {@hide} + */ + @VisibleForTesting + @UnsupportedAppUsage + public int getSlotIndex() { + int slotIndex = SubscriptionManager.getSlotIndex(getSubId()); + if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) { + slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX; + } + return slotIndex; + } + + /** + * Request that the next incoming call from a number matching {@code range} be intercepted. + * + * This API is intended for OEMs to provide a service for apps to verify the device's phone + * number. When called, the Telephony stack will store the provided {@link PhoneNumberRange} and + * intercept the next incoming call from a number that lies within the range, within a timeout + * specified by {@code timeoutMillis}. + * + * If such a phone call is received, the caller will be notified via + * {@link NumberVerificationCallback#onCallReceived(String)} on the provided {@link Executor}. + * If verification fails for any reason, the caller will be notified via + * {@link NumberVerificationCallback#onVerificationFailed(int)} + * on the provided {@link Executor}. + * + * In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this + * API must also be listed in the device configuration as an authorized app in + * {@code packages/services/Telephony/res/values/config.xml} under the + * {@code platform_number_verification_package} key. + * + * @hide + * @param range The range of phone numbers the caller expects a phone call from. + * @param timeoutMillis The amount of time to wait for such a call, or the value of + * {@link #getMaxNumberVerificationTimeoutMillis()}, whichever is lesser. + * @param executor The {@link Executor} that callbacks should be executed on. + * @param callback The callback to use for delivering results. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis, + @NonNull @CallbackExecutor Executor executor, + @NonNull NumberVerificationCallback callback) { + if (executor == null) { + throw new NullPointerException("Executor must be non-null"); + } + if (callback == null) { + throw new NullPointerException("Callback must be non-null"); + } + + INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() { + @Override + public void onCallReceived(String phoneNumber) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> + callback.onCallReceived(phoneNumber)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onVerificationFailed(int reason) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> + callback.onVerificationFailed(reason)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + + telephony.requestNumberVerification(range, timeoutMillis, internalCallback, + getOpPackageName()); + } catch (RemoteException ex) { + Rlog.e(TAG, "requestNumberVerification RemoteException", ex); + runOnBackgroundThread(() -> executor.execute( + () -> callback.onVerificationFailed( + NumberVerificationCallback.REASON_UNSPECIFIED))); + } + } + + /** + * Inserts or updates a list property. Expands the list if its length is not enough. + */ + private static List updateTelephonyProperty(List prop, int phoneId, T value) { + List ret = new ArrayList<>(prop); + while (ret.size() <= phoneId) ret.add(null); + ret.set(phoneId, value); + return ret; + } + /** + * Convenience function for retrieving a value from the secure settings + * value list as an integer. Note that internally setting values are + * always stored as strings; this function converts the string to an + * integer for you. + *

+ * This version does not take a default value. If the setting has not + * been set, or the string value is not a number, + * it throws {@link SettingNotFoundException}. + * + * @param cr The ContentResolver to access. + * @param name The name of the setting to retrieve. + * @param index The index of the list + * + * @throws SettingNotFoundException Thrown if a setting by the given + * name can't be found or the setting value is not an integer. + * + * @return The value at the given index of settings. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static int getIntAtIndex(android.content.ContentResolver cr, + String name, int index) + throws android.provider.Settings.SettingNotFoundException { + String v = android.provider.Settings.Global.getString(cr, name); + if (v != null) { + String valArray[] = v.split(","); + if ((index >= 0) && (index < valArray.length) && (valArray[index] != null)) { + try { + return Integer.parseInt(valArray[index]); + } catch (NumberFormatException e) { + //Log.e(TAG, "Exception while parsing Integer: ", e); + } + } + } + throw new android.provider.Settings.SettingNotFoundException(name); + } + + /** + * Convenience function for updating settings value as coma separated + * integer values. This will either create a new entry in the table if the + * given name does not exist, or modify the value of the existing row + * with that name. Note that internally setting values are always + * stored as strings, so this function converts the given value to a + * string before storing it. + * + * @param cr The ContentResolver to access. + * @param name The name of the setting to modify. + * @param index The index of the list + * @param value The new value for the setting to be added to the list. + * @return true if the value was set, false on database errors + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static boolean putIntAtIndex(android.content.ContentResolver cr, + String name, int index, int value) { + String data = ""; + String valArray[] = null; + String v = android.provider.Settings.Global.getString(cr, name); + + if (index == Integer.MAX_VALUE) { + throw new IllegalArgumentException("putIntAtIndex index == MAX_VALUE index=" + index); + } + if (index < 0) { + throw new IllegalArgumentException("putIntAtIndex index < 0 index=" + index); + } + if (v != null) { + valArray = v.split(","); + } + + // Copy the elements from valArray till index + for (int i = 0; i < index; i++) { + String str = ""; + if ((valArray != null) && (i < valArray.length)) { + str = valArray[i]; + } + data = data + str + ","; + } + + data = data + value; + + // Copy the remaining elements from valArray if any. + if (valArray != null) { + for (int i = index+1; i < valArray.length; i++) { + data = data + "," + valArray[i]; + } + } + return android.provider.Settings.Global.putString(cr, name, data); + } + + /** + * Gets a per-phone telephony property from a property name. + * + * @hide + */ + @UnsupportedAppUsage + public static String getTelephonyProperty(int phoneId, String property, String defaultVal) { + String propVal = null; + String prop = SystemProperties.get(property); + if ((prop != null) && (prop.length() > 0)) { + String values[] = prop.split(","); + if ((phoneId >= 0) && (phoneId < values.length) && (values[phoneId] != null)) { + propVal = values[phoneId]; + } + } + return propVal == null ? defaultVal : propVal; + } + + /** + * Gets a typed per-phone telephony property from a schematized list property. + */ + private static T getTelephonyProperty(int phoneId, List prop, T defaultValue) { + T ret = null; + if (phoneId >= 0 && phoneId < prop.size()) ret = prop.get(phoneId); + return ret != null ? ret : defaultValue; + } + + /** + * Gets a global telephony property. + * + * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are + * per-phone. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public static String getTelephonyProperty(String property, String defaultVal) { + String propVal = SystemProperties.get(property); + return TextUtils.isEmpty(propVal) ? defaultVal : propVal; + } + + /** @hide */ + @UnsupportedAppUsage + public int getSimCount() { + // FIXME Need to get it from Telephony Dev Controller when that gets implemented! + // and then this method shouldn't be used at all! + return getPhoneCount(); + } + + /** + * Returns the IMS Service Table (IST) that was loaded from the ISIM. + * + * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table. + * + * @return IMS Service Table or null if not present or not loaded + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @Nullable + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getIsimIst() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + //get the Isim Ist based on subId + return info.getIsimIst(getSubId()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** + * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM. + * @return an array of PCSCF strings with one PCSCF per string, or null if + * not present or not loaded + * @hide + */ + @UnsupportedAppUsage + public String[] getIsimPcscf() { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + //get the Isim Pcscf based on subId + return info.getIsimPcscf(getSubId()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** UICC application type is unknown or not specified */ + public static final int APPTYPE_UNKNOWN = PhoneConstants.APPTYPE_UNKNOWN; + /** UICC application type is SIM */ + public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM; + /** UICC application type is USIM */ + public static final int APPTYPE_USIM = PhoneConstants.APPTYPE_USIM; + /** UICC application type is RUIM */ + public static final int APPTYPE_RUIM = PhoneConstants.APPTYPE_RUIM; + /** UICC application type is CSIM */ + public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM; + /** UICC application type is ISIM */ + public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM; + + // authContext (parameter P2) when doing UICC challenge, + // per 3GPP TS 31.102 (Section 7.1.2) + /** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */ + public static final int AUTHTYPE_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM; + /** Authentication type for UICC challenge is EAP AKA. See RFC 4187 for details. */ + public static final int AUTHTYPE_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA; + /** + * Authentication type for GBA Bootstrap Challenge. + * Pass this authentication type into the {@link #getIccAuthentication} API to perform a GBA + * Bootstrap challenge (BSF), with {@code data} (generated according to the procedure defined in + * 3GPP 33.220 Section 5.3.2 step.4) in base64 encoding. + * This method will return the Bootstrapping response in base64 encoding when ICC authentication + * is completed. + * Ref 3GPP 33.220 Section 5.3.2. + */ + public static final int AUTHTYPE_GBA_BOOTSTRAP = PhoneConstants.AUTH_CONTEXT_GBA_BOOTSTRAP; + /** + * Authentication type for GBA Network Application Functions (NAF) key External Challenge. + * Pass this authentication type into the {@link #getIccAuthentication} API to perform a GBA + * Network Applications Functions (NAF) key External challenge using the NAF_ID parameter + * as the {@code data} in base64 encoding. + * This method will return the Ks_Ext_Naf key in base64 encoding when ICC authentication + * is completed. + * Ref 3GPP 33.220 Section 5.3.2. + */ + public static final int AUTHTYPE_GBA_NAF_KEY_EXTERNAL = + PhoneConstants.AUTHTYPE_GBA_NAF_KEY_EXTERNAL; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + AUTHTYPE_EAP_SIM, + AUTHTYPE_EAP_AKA, + AUTHTYPE_GBA_BOOTSTRAP, + AUTHTYPE_GBA_NAF_KEY_EXTERNAL + }) + public @interface AuthType {} + + /** + * Returns the response of authentication for the default subscription. + * Returns null if the authentication hasn't been successful + * + *

Requires one of the following permissions: + *

    + *
  • READ_PRIVILEGED_PHONE_STATE + *
  • the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *
  • the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. + *
+ * + * @param appType the icc application type, like {@link #APPTYPE_USIM} + * @param authType the authentication type, any one of {@link #AUTHTYPE_EAP_AKA} or + * {@link #AUTHTYPE_EAP_SIM} or {@link #AUTHTYPE_GBA_BOOTSTRAP} or + * {@link #AUTHTYPE_GBA_NAF_KEY_EXTERNAL} + * @param data authentication challenge data, base64 encoded. + * See 3GPP TS 31.102 7.1.2 for more details. + * @return the response of authentication. This value will be null in the following cases: + * Authentication error, incorrect MAC + * Authentication error, security context not supported + * Key freshness failure + * Authentication error, no memory space available + * Authentication error, no memory space available in EFMUK + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not + // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since + // it's not public API. + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getIccAuthentication(int appType,@AuthType int authType, String data) { + return getIccAuthentication(getSubId(), appType, authType, data); + } + + /** + * Returns the response of USIM Authentication for specified subId. + * Returns null if the authentication hasn't been successful + * + *

See {@link #getIccAuthentication(int, int, String)} for details on the required + * permissions. + * + * @param subId subscription ID used for authentication + * @param appType the icc application type, like {@link #APPTYPE_USIM} + * @param authType the authentication type, any one of {@link #AUTHTYPE_EAP_AKA} or + * {@link #AUTHTYPE_EAP_SIM} or {@link #AUTHTYPE_GBA_BOOTSTRAP} or + * {@link #AUTHTYPE_GBA_NAF_KEY_EXTERNAL} + * @param data authentication challenge data, base64 encoded. + * See 3GPP TS 31.102 7.1.2 for more details. + * @return the response of authentication. This value will be null in the following cases only + * (see 3GPP TS 31.102 7.3.1): + * Authentication error, incorrect MAC + * Authentication error, security context not supported + * Key freshness failure + * Authentication error, no memory space available + * Authentication error, no memory space available in EFMUK + * @hide + */ + @UnsupportedAppUsage + public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) + return null; + return info.getIccSimChallengeResponse(subId, appType, authType, data, + getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone starts + return null; + } + } + + /** + * Returns an array of Forbidden PLMNs from the USIM App + * Returns null if the query fails. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return an array of forbidden PLMNs or null if not available + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String[] getForbiddenPlmns() { + return getForbiddenPlmns(getSubId(), APPTYPE_USIM); + } + + /** + * Returns an array of Forbidden PLMNs from the specified SIM App + * Returns null if the query fails. + * + * @param subId subscription ID used for authentication + * @param appType the icc application type, like {@link #APPTYPE_USIM} + * @return fplmns an array of forbidden PLMNs + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public String[] getForbiddenPlmns(int subId, int appType) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone starts + return null; + } + } + + /** + * Replace the contents of the forbidden PLMN SIM file with the provided values. + * Passing an empty list will clear the contents of the EFfplmn file. + * If the provided list is shorter than the size of EFfplmn, then the list will be padded + * up to the file size with 'FFFFFF'. (required by 3GPP TS 31.102 spec 4.2.16) + * If the list is longer than the size of EFfplmn, then the file will be written from the + * beginning of the list up to the file size. + * + *

Requires Permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE + * MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param fplmns a list of PLMNs to be forbidden. + * + * @return number of PLMNs that were successfully written to the SIM FPLMN list. + * This may be less than the number of PLMNs passed in where the SIM file does not have enough + * room for all of the values passed in. Return -1 in the event of an unexpected failure + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int setForbiddenPlmns(@NonNull List fplmns) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) return -1; + return telephony.setForbiddenPlmns( + getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage()); + } catch (NullPointerException ex) { + // This could happen before phone starts + Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage()); + } + return -1; + } + + /** + * Fetches the sim service table from the EFUST/EFIST based on the application type + * {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}. The return value is hexaString format + * representing X bytes (x >= 1). Each bit of every byte indicates which optional services + * are available for the given application type. + * The USIM service table EF is described in as per Section 4.2.8 of 3GPP TS 31.102. + * The ISIM service table EF is described in as per Section 4.2.7 of 3GPP TS 31.103. + * The list of services mapped to the exact nth byte of response as mentioned in Section 4.2 + * .7 of 3GPP TS 31.103. Eg. Service n°1: Local Phone Book, Service n°2: Fixed Dialling + * Numbers (FDN) - Bit 1 and 2 of the 1st Byte represent service Local Phone Book and Fixed + * Dialling Numbers (FDN)respectively. The coding format for each service type should be + * interpreted as bit = 1: service available;bit = 0:service not available. + * + * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}. + * @return HexString represents sim service table else null. + * @throws SecurityException if the caller does not have the required permission/privileges + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + + @Nullable + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getSimServiceTable(int appType) { + try { + IPhoneSubInfo info = getSubscriberInfoService(); + if (info == null) { + Rlog.e(TAG, "getSimServiceTable(): IPhoneSubInfo is null"); + return null; + } + //Fetches the sim service table based on subId and appType + if (appType == APPTYPE_ISIM) { + return info.getIsimIst(getSubId()); + } else if ((appType == APPTYPE_USIM)) { + return info.getSimServiceTable(getSubId(), APPTYPE_USIM); + } else { + return null; + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getSimServiceTable(): RemoteException=" + ex.getMessage()); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getSimServiceTable(): NullPointerException=" + ex.getMessage()); + } + return null; + } + + /** + * Resets the {@link android.telephony.ims.ImsService} associated with the specified sim slot. + * Used by diagnostic apps to force the IMS stack to be disabled and re-enabled in an effort to + * recover from scenarios where the {@link android.telephony.ims.ImsService} gets in to a bad + * state. + * + * @param slotIndex the sim slot to reset the IMS stack on. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. + * @hide */ + @SystemApi + @WorkerThread + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) + public void resetIms(int slotIndex) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.resetIms(slotIndex); + } + } catch (RemoteException e) { + Rlog.e(TAG, "toggleImsOnOff, RemoteException: " + + e.getMessage()); + } + } + + /** + * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability + * status updates, if not already enabled. + * @hide + */ + public void enableIms(int slotId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.enableIms(slotId); + } + } catch (RemoteException e) { + Rlog.e(TAG, "enableIms, RemoteException: " + + e.getMessage()); + } + } + + /** + * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature + * status updates to disabled. + * @hide + */ + public void disableIms(int slotId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.disableIms(slotId); + } + } catch (RemoteException e) { + Rlog.e(TAG, "disableIms, RemoteException: " + + e.getMessage()); + } + } + + /** + * @return the {@IImsRegistration} interface that corresponds with the slot index and feature. + * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for. + * @param feature An integer indicating the feature that we wish to get the ImsRegistration for. + * Corresponds to features defined in ImsFeature. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getImsRegistration(slotIndex, feature); + } + } catch (RemoteException e) { + Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage()); + } + return null; + } + + /** + * @return the {@IImsConfig} interface that corresponds with the slot index and feature. + * @param slotIndex The SIM slot corresponding to the ImsService ImsConfig is active for. + * @param feature An integer indicating the feature that we wish to get the ImsConfig for. + * Corresponds to features defined in ImsFeature. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable IImsConfig getImsConfig(int slotIndex, int feature) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getImsConfig(slotIndex, feature); + } + } catch (RemoteException e) { + Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage()); + } + return null; + } + + /** + * Set IMS registration state on all active subscriptions. + *

+ * Use {@link android.telephony.ims.stub.ImsRegistrationImplBase#onRegistered} and + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered} to set Ims + * registration state instead. + * + * @param registered whether ims is registered + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setImsRegistrationState(final boolean registered) { + try { + final ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.setImsRegistrationState(registered); + } catch (final RemoteException e) { + } + } + + /** @hide */ + @IntDef(prefix = { "NETWORK_MODE_" }, value = { + NETWORK_MODE_WCDMA_PREF, + NETWORK_MODE_GSM_ONLY, + NETWORK_MODE_WCDMA_ONLY, + NETWORK_MODE_GSM_UMTS, + NETWORK_MODE_CDMA_EVDO, + NETWORK_MODE_CDMA_NO_EVDO, + NETWORK_MODE_EVDO_NO_CDMA, + NETWORK_MODE_GLOBAL, + NETWORK_MODE_LTE_CDMA_EVDO, + NETWORK_MODE_LTE_GSM_WCDMA, + NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA, + NETWORK_MODE_LTE_ONLY, + NETWORK_MODE_LTE_WCDMA, + NETWORK_MODE_TDSCDMA_ONLY, + NETWORK_MODE_TDSCDMA_WCDMA, + NETWORK_MODE_LTE_TDSCDMA, + NETWORK_MODE_TDSCDMA_GSM, + NETWORK_MODE_LTE_TDSCDMA_GSM, + NETWORK_MODE_TDSCDMA_GSM_WCDMA, + NETWORK_MODE_LTE_TDSCDMA_WCDMA, + NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA, + NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA, + NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA, + NETWORK_MODE_NR_ONLY, + NETWORK_MODE_NR_LTE, + NETWORK_MODE_NR_LTE_CDMA_EVDO, + NETWORK_MODE_NR_LTE_GSM_WCDMA, + NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA, + NETWORK_MODE_NR_LTE_WCDMA, + NETWORK_MODE_NR_LTE_TDSCDMA, + NETWORK_MODE_NR_LTE_TDSCDMA_GSM, + NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA, + NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA, + NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrefNetworkMode{} + + /** + * Preferred network mode is GSM/WCDMA (WCDMA preferred). + * @hide + */ + public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF; + + /** + * Preferred network mode is GSM only. + * @hide + */ + public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY; + + /** + * Preferred network mode is WCDMA only. + * @hide + */ + public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY; + + /** + * Preferred network mode is GSM/WCDMA (auto mode, according to PRL). + * @hide + */ + public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS; + + /** + * Preferred network mode is CDMA and EvDo (auto mode, according to PRL). + * @hide + */ + public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA; + + /** + * Preferred network mode is CDMA only. + * @hide + */ + public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO; + + /** + * Preferred network mode is EvDo only. + * @hide + */ + public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA; + + /** + * Preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL). + * @hide + */ + public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL; + + /** + * Preferred network mode is LTE, CDMA and EvDo. + * @hide + */ + public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO; + + /** + * Preferred network mode is LTE, GSM/WCDMA. + * @hide + */ + public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA; + + /** + * Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA. + * @hide + */ + public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA; + + /** + * Preferred network mode is LTE Only. + * @hide + */ + public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY; + + /** + * Preferred network mode is LTE/WCDMA. + * @hide + */ + public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA; + + /** + * Preferred network mode is TD-SCDMA only. + * @hide + */ + public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY; + + /** + * Preferred network mode is TD-SCDMA and WCDMA. + * @hide + */ + public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA; + + /** + * Preferred network mode is TD-SCDMA and LTE. + * @hide + */ + public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA; + + /** + * Preferred network mode is TD-SCDMA and GSM. + * @hide + */ + public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM; + + /** + * Preferred network mode is TD-SCDMA,GSM and LTE. + * @hide + */ + public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM; + + /** + * Preferred network mode is TD-SCDMA, GSM/WCDMA. + * @hide + */ + public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = + RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA; + + /** + * Preferred network mode is TD-SCDMA, WCDMA and LTE. + * @hide + */ + public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA; + + /** + * Preferred network mode is TD-SCDMA, GSM/WCDMA and LTE. + * @hide + */ + public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA; + + /** + * Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA. + * @hide + */ + public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA; + /** + * Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo. + * @hide + */ + public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA; + + /** + * Preferred network mode is NR 5G only. + * @hide + */ + public static final int NETWORK_MODE_NR_ONLY = RILConstants.NETWORK_MODE_NR_ONLY; + + /** + * Preferred network mode is NR 5G, LTE. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE = RILConstants.NETWORK_MODE_NR_LTE; + + /** + * Preferred network mode is NR 5G, LTE, CDMA and EvDo. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO = + RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO; + + /** + * Preferred network mode is NR 5G, LTE, GSM and WCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_GSM_WCDMA = + RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA; + + /** + * Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA; + + /** + * Preferred network mode is NR 5G, LTE and WCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_WCDMA = RILConstants.NETWORK_MODE_NR_LTE_WCDMA; + + /** + * Preferred network mode is NR 5G, LTE and TDSCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_TDSCDMA = RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA; + + /** + * Preferred network mode is NR 5G, LTE, TD-SCDMA and GSM. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM = + RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM; + + /** + * Preferred network mode is NR 5G, LTE, TD-SCDMA, WCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA = + RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA; + + /** + * Preferred network mode is NR 5G, LTE, TD-SCDMA, GSM and WCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = + RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA; + + /** + * Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA. + * @hide + */ + public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA; + + /** + * The default preferred network mode constant. + * + *

This constant is used in case of nothing is set in + * TelephonyProperties#default_network(). + * + * @hide + */ + public static final int DEFAULT_PREFERRED_NETWORK_MODE = + RILConstants.PREFERRED_NETWORK_MODE; + + /** + * Get the preferred network type. + * Used for device configuration by some CDMA operators. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return the preferred network type. + * @hide + * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead. + */ + @Deprecated + @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)) + @UnsupportedAppUsage + public @PrefNetworkMode int getPreferredNetworkType(int subId) { + return RadioAccessFamily.getNetworkTypeFromRaf((int) getAllowedNetworkTypesBitmask()); + } + + /** + * Get the preferred network type bitmask. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return The bitmask of preferred network types. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() { + return getAllowedNetworkTypesBitmask(); + } + + /** + * Get the allowed network type bitmask. + * Note that the device can only register on the network of {@link NetworkTypeBitmask} + * (except for emergency call cases). + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return The bitmask of allowed network types. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NetworkTypeBitMask long getAllowedNetworkTypesBitmask() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return (long) telephony.getAllowedNetworkTypesBitmask(getSubId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getAllowedNetworkTypesBitmask RemoteException", ex); + } + return 0; + } + + /** + * Get the allowed network types by carriers. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return the allowed network type bitmask + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + * @deprecated Use {@link #getAllowedNetworkTypesForReason} instead. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + @SystemApi + @Deprecated + public @NetworkTypeBitMask long getAllowedNetworkTypes() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getAllowedNetworkTypesForReason(getSubId(), + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getAllowedNetworkTypes RemoteException", ex); + } + return -1; + } + + /** + * Sets the network selection mode to automatic. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void setNetworkSelectionModeAutomatic() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setNetworkSelectionModeAutomatic(getSubId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setNetworkSelectionModeAutomatic NPE", ex); + } + } + + /** + * Perform a radio scan and return the list of available networks. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Note that this scan can take a long time (sometimes minutes) to happen. + * + *

Requires Permissions: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier + * privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * + * @return {@link CellNetworkScanResult} with the status + * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of + * {@link com.android.internal.telephony.OperatorInfo} if it's available. Otherwise, the failure + * caused will be included in the result. + * + * @hide + */ + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) + public CellNetworkScanResult getAvailableNetworks() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName(), + getAttributionTag()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getAvailableNetworks RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getAvailableNetworks NPE", ex); + } + return new CellNetworkScanResult( + CellNetworkScanResult.STATUS_UNKNOWN_ERROR, null /* OperatorInfo */); + } + + /** + * Request a network scan. + * + * This method is asynchronous, so the network scan results will be returned by callback. + * The returned NetworkScan will contain a callback method which can be used to stop the scan. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * + * If the system-wide location switch is off, apps may still call this API, with the + * following constraints: + *

    + *
  1. The app must hold the {@code android.permission.NETWORK_SCAN} permission.
  2. + *
  3. The app must not supply any specific bands or channels to scan.
  4. + *
  5. The app must only specify MCC/MNC pairs that are + * associated to a SIM in the device.
  6. + *
  7. Returned results will have no meaningful info other than signal strength + * and MCC/MNC info.
  8. + *
+ * + * @param request Contains all the RAT with bands/channels that need to be scanned. + * @param executor The executor through which the callback should be invoked. Since the scan + * request may trigger multiple callbacks and they must be invoked in the same order as + * they are received by the platform, the user should provide an executor which executes + * tasks one at a time in serial order. + * @param callback Returns network scan results or errors. + * @return A NetworkScan obj which contains a callback which can be used to stop the scan. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public NetworkScan requestNetworkScan( + NetworkScanRequest request, Executor executor, + TelephonyScanManager.NetworkScanCallback callback) { + return requestNetworkScan(INCLUDE_LOCATION_DATA_FINE, request, executor, callback); + } + + /** + * Request a network scan. + * + * This method is asynchronous, so the network scan results will be returned by callback. + * The returned NetworkScan will contain a callback method which can be used to stop the scan. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION} if includeLocationData is + * set to {@link #INCLUDE_LOCATION_DATA_FINE}. + * + * If the system-wide location switch is off, apps may still call this API, with the + * following constraints: + *

    + *
  1. The app must hold the {@code android.permission.NETWORK_SCAN} permission.
  2. + *
  3. The app must not supply any specific bands or channels to scan.
  4. + *
  5. The app must only specify MCC/MNC pairs that are + * associated to a SIM in the device.
  6. + *
  7. Returned results will have no meaningful info other than signal strength + * and MCC/MNC info.
  8. + *
+ * + * @param includeLocationData Specifies if the caller would like to receive + * location related information. If this parameter is set to + * {@link #INCLUDE_LOCATION_DATA_FINE} then the application will be checked for + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission and available + * location related information received during network scan will be sent to the caller. + * @param request Contains all the RAT with bands/channels that need to be scanned. + * @param executor The executor through which the callback should be invoked. Since the scan + * request may trigger multiple callbacks and they must be invoked in the same order as + * they are received by the platform, the user should provide an executor which executes + * tasks one at a time in serial order. + * @param callback Returns network scan results or errors. + * @return A NetworkScan obj which contains a callback which can be used to stop the scan. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @Nullable NetworkScan requestNetworkScan( + @IncludeLocationData int includeLocationData, + @NonNull NetworkScanRequest request, + @NonNull Executor executor, + @NonNull TelephonyScanManager.NetworkScanCallback callback) { + synchronized (sCacheLock) { + if (mTelephonyScanManager == null) { + mTelephonyScanManager = new TelephonyScanManager(); + } + } + return mTelephonyScanManager.requestNetworkScan(getSubId(), + includeLocationData != INCLUDE_LOCATION_DATA_FINE, + request, executor, callback, + getOpPackageName(), getAttributionTag()); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * + * @deprecated + * Use {@link + * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} + * @removed + */ + @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public NetworkScan requestNetworkScan( + NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) { + return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback); + } + + /** + * Ask the radio to connect to the input network and change selection mode to manual. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param operatorNumeric the PLMN ID of the network to select. + * @param persistSelection whether the selection will persist until reboot. If true, only allows + * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume + * normal network selection next time. + * @return {@code true} on success; {@code false} on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) { + return setNetworkSelectionModeManual( + new OperatorInfo( + "" /* operatorAlphaLong */, "" /* operatorAlphaShort */, operatorNumeric), + persistSelection); + } + + /** + * Ask the radio to connect to the input network and change selection mode to manual. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param operatorNumeric the PLMN ID of the network to select. + * @param persistSelection whether the selection will persist until reboot. + * If true, only allows attaching to the selected PLMN until reboot; otherwise, + * attach to the chosen PLMN and resume normal network selection next time. + * @param ran the initial suggested radio access network type. + * If registration fails, the RAN is not available after, the RAN is not within the + * network types specified by the preferred network types, or the value is + * {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select + * the next best RAN for network registration. + * @return {@code true} on success; {@code false} on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric, + boolean persistSelection, @AccessNetworkConstants.RadioAccessNetworkType int ran) { + return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */, + "" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection); + } + + /** + * Ask the radio to connect to the input network and change selection mode to manual. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to. + * @param persistSelection whether the selection will persist until reboot. If true, only allows + * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume + * normal network selection next time. + * @return {@code true} on success; {@code true} on any failure. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean setNetworkSelectionModeManual( + OperatorInfo operatorInfo, boolean persistSelection) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setNetworkSelectionModeManual( + getSubId(), operatorInfo, persistSelection); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex); + } + return false; + } + + /** + * Get the network selection mode. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return the network selection mode. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // No support for carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NetworkSelectionMode int getNetworkSelectionMode() { + int mode = NETWORK_SELECTION_MODE_UNKNOWN; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + mode = telephony.getNetworkSelectionMode(getSubId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getNetworkSelectionMode RemoteException", ex); + } + return mode; + } + + /** + * Get the PLMN chosen for Manual Network Selection if active. + * Return empty string if in automatic selection. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link #hasCarrierPrivileges}) + * + * @return manually selected network info on success or empty string on failure + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // No support carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NonNull String getManualNetworkSelectionPlmn() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null && isManualNetworkSelectionAllowed()) { + return telephony.getManualNetworkSelectionPlmn(getSubId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex); + } + return ""; + } + + /** + * Query Telephony to see if there has recently been an emergency SMS sent to the network by the + * user and we are still within the time interval after the emergency SMS was sent that we are + * considered in Emergency SMS mode. + * + *

This mode is used by other applications to allow them to perform special functionality, + * such as allow the GNSS service to provide user location to the carrier network for emergency + * when an emergency SMS is sent. This interval is set by + * {@link CarrierConfigManager#KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT}. If + * the carrier does not support this mode, this function will always return false. + * + * @return {@code true} if this device is in emergency SMS mode, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public boolean isInEmergencySmsMode() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isInEmergencySmsMode(); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isInEmergencySmsMode RemoteException", ex); + } + return false; + } + + /** + * Set the preferred network type. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + *

+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. + * + * @param subId the id of the subscription to set the preferred network type for. + * @param networkType the preferred network type + * @return true on success; false on any failure. + * @hide + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public boolean setPreferredNetworkType(int subId, @PrefNetworkMode int networkType) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setAllowedNetworkTypesForReason(subId, + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, + RadioAccessFamily.getRafFromNetworkType(networkType)); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex); + } + return false; + } + + /** + * Set the preferred network type bitmask but if {@link #setAllowedNetworkTypes} has been set, + * only the allowed network type will set to the modem. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + *

+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. + * + * @param networkTypeBitmask The bitmask of preferred network types. + * @return true on success; false on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + @SystemApi + public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + networkTypeBitmask = checkNetworkTypeBitmask(networkTypeBitmask); + return telephony.setAllowedNetworkTypesForReason(getSubId(), + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, networkTypeBitmask); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setPreferredNetworkTypeBitmask RemoteException", ex); + } + return false; + } + + /** + * If {@link #NETWORK_TYPE_BITMASK_LTE_CA} bit is set, convert it to NETWORK_TYPE_BITMASK_LTE. + * + * @param networkTypeBitmask The networkTypeBitmask being checked + * @return The checked/converted networkTypeBitmask + */ + private long checkNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) { + if ((networkTypeBitmask & NETWORK_TYPE_BITMASK_LTE_CA) != 0) { + networkTypeBitmask ^= NETWORK_TYPE_BITMASK_LTE_CA; + networkTypeBitmask |= NETWORK_TYPE_BITMASK_LTE; + } + return networkTypeBitmask; + } + + /** + * Set the allowed network types of the device. This is for carrier or privileged apps to + * enable/disable certain network types on the device. The user preferred network types should + * be set through {@link #setPreferredNetworkTypeBitmask}. + *

+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setPreferredNetworkTypesBitmap is used instead. + * + * @param allowedNetworkTypes The bitmask of allowed network types. + * @return true on success; false on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * + * @hide + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason + * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}. + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK) + @SystemApi + public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + allowedNetworkTypes = checkNetworkTypeBitmask(allowedNetworkTypes); + return telephony.setAllowedNetworkTypesForReason(getSubId(), + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, allowedNetworkTypes); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setAllowedNetworkTypes RemoteException", ex); + } + return false; + } + + /** @hide */ + @IntDef({ + ALLOWED_NETWORK_TYPES_REASON_USER, + ALLOWED_NETWORK_TYPES_REASON_POWER, + ALLOWED_NETWORK_TYPES_REASON_CARRIER, + ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AllowedNetworkTypesReason { + } + + /** + * To indicate allowed network type change is requested by user. + */ + public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; + + /** + * To indicate allowed network type change is requested by power manager. + * Power Manger configuration won't affect the settings configured through + * other reasons and will result in allowing network types that are in both + * configurations (i.e intersection of both sets). + * + * @hide + */ + @SystemApi + public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; + + /** + * To indicate allowed network type change is requested by carrier. + * Carrier configuration won't affect the settings configured through + * other reasons and will result in allowing network types that are in both + * configurations (i.e intersection of both sets). + */ + public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; + + /** + * To indicate allowed network type change is requested by the user via the 2G toggle. + * + * @hide + */ + @SystemApi + public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; + + /** + * Set the allowed network types of the device and provide the reason triggering the allowed + * network change. + *

Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * This can be called for following reasons: + *

    + *
  1. Allowed network types control by USER + * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} + *
  2. Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} + *
+ * This API will result in allowing an intersection of allowed network types for all reasons, + * including the configuration done through other reasons. + * + * @param reason the reason the allowed network type change is taking place + * @param allowedNetworkTypes The bitmask of allowed network type + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. + * @throws SecurityException if the caller does not have the required privileges or if the + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * caller tries to use one of the following security-based reasons without + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions. + *
    + *
  1. {@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}
  2. + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK) + public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason, + @NetworkTypeBitMask long allowedNetworkTypes) { + if (!isValidAllowedNetworkTypesReason(reason)) { + throw new IllegalArgumentException("invalid AllowedNetworkTypesReason."); + } + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + allowedNetworkTypes = checkNetworkTypeBitmask(allowedNetworkTypes); + telephony.setAllowedNetworkTypesForReason(getSubId(), reason, + allowedNetworkTypes); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setAllowedNetworkTypesForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get the allowed network types for certain reason. + * + * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a + * specific reason. + *

Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param reason the reason the allowed network type change is taking place + * @return the allowed network type bitmask + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. + * @throws SecurityException if the caller does not have the required permission/privileges + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK) + public @NetworkTypeBitMask long getAllowedNetworkTypesForReason( + @AllowedNetworkTypesReason int reason) { + if (!isValidAllowedNetworkTypesReason(reason)) { + throw new IllegalArgumentException("invalid AllowedNetworkTypesReason."); + } + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getAllowedNetworkTypesForReason(getSubId(), reason); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getAllowedNetworkTypesForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return -1; + } + /** + * Verifies that the reason provided is valid. + * @hide + */ + public static boolean isValidAllowedNetworkTypesReason(@AllowedNetworkTypesReason int reason) { + switch (reason) { + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER: + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER: + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER: + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: + return true; + } + return false; + } + /** + * Get bit mask of all network types. + * + * @return bit mask of all network types + * @hide + */ + public static @NetworkTypeBitMask long getAllNetworkTypesBitmask() { + return NETWORK_STANDARDS_FAMILY_BITMASK_3GPP | NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2; + } + + /** + * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}. + * + * @param networkTypeBitmask The bitmask of allowed network types. + * @return the name of the allowed network types + * @hide + */ + public static String convertNetworkTypeBitmaskToString( + @NetworkTypeBitMask long networkTypeBitmask) { + String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR) + .filter(x -> { + return (networkTypeBitmask & getBitMaskForNetworkType(x)) + == getBitMaskForNetworkType(x); + }) + .mapToObj(x -> getNetworkTypeName(x)) + .collect(Collectors.joining("|")); + return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName; + } + + /** + * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo + * and GSM/WCDMA. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return true on success; false on any failure. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean setPreferredNetworkTypeToGlobal() { + return setPreferredNetworkTypeToGlobal(getSubId()); + } + + /** + * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo + * and GSM/WCDMA. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return true on success; false on any failure. + * @hide + */ + public boolean setPreferredNetworkTypeToGlobal(int subId) { + return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA); + } + + /** + * Check whether DUN APN is required for tethering. + *

+ * Requires Permission: MODIFY_PHONE_STATE. + * + * @return {@code true} if DUN APN is required for tethering. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isTetheringApnRequired() { + return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); + } + + /** + * Check whether DUN APN is required for tethering with subId. + * + * @param subId the id of the subscription to require tethering. + * @return {@code true} if DUN APN is required for tethering. + * @hide + */ + public boolean isTetheringApnRequired(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.isTetheringApnRequiredForSubscriber(subId); + } catch (RemoteException ex) { + Rlog.e(TAG, "hasMatchedTetherApnSetting RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "hasMatchedTetherApnSetting NPE", ex); + } + return false; + } + + + /** + * Values used to return status for hasCarrierPrivileges call. + */ + /** @hide */ @SystemApi + public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; + /** @hide */ @SystemApi + public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; + /** @hide */ @SystemApi + public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; + /** @hide */ @SystemApi + public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; + + /** + * Has the calling application been granted carrier privileges by the carrier. + * + * If any of the packages in the calling UID has carrier privileges, the + * call will return true. This access is granted by the owner of the UICC + * card and does not depend on the registered carrier. + * + * Note that this API applies to both physical and embedded subscriptions and + * is a superset of the checks done in SubscriptionManager#canManageSubscription. + * + * @return true if the app has carrier privileges. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean hasCarrierPrivileges() { + return hasCarrierPrivileges(getSubId()); + } + + /** + * Has the calling application been granted carrier privileges by the carrier. + * + * If any of the packages in the calling UID has carrier privileges, the + * call will return true. This access is granted by the owner of the UICC + * card and does not depend on the registered carrier. + * + * Note that this API applies to both physical and embedded subscriptions and + * is a superset of the checks done in SubscriptionManager#canManageSubscription. + * + * @param subId The subscription to use. + * @return true if the app has carrier privileges. + * @hide + */ + public boolean hasCarrierPrivileges(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCarrierPrivilegeStatus(subId) + == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; + } + } catch (RemoteException ex) { + Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "hasCarrierPrivileges NPE", ex); + } + return false; + } + + /** + * Override the branding for the current ICCID. + * + * Once set, whenever the SIM is present in the device, the service + * provider name (SPN) and the operator name will both be replaced by the + * brand value input. To unset the value, the same function should be + * called with a null brand value. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param brand The brand name to display/set. + * @return true if the operation was executed correctly. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean setOperatorBrandOverride(String brand) { + return setOperatorBrandOverride(getSubId(), brand); + } + + /** + * Override the branding for the current ICCID. + * + * Once set, whenever the SIM is present in the device, the service + * provider name (SPN) and the operator name will both be replaced by the + * brand value input. To unset the value, the same function should be + * called with a null brand value. + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param subId The subscription to use. + * @param brand The brand name to display/set. + * @return true if the operation was executed correctly. + * @hide + */ + public boolean setOperatorBrandOverride(int subId, String brand) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.setOperatorBrandOverride(subId, brand); + } catch (RemoteException ex) { + Rlog.e(TAG, "setOperatorBrandOverride RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setOperatorBrandOverride NPE", ex); + } + return false; + } + + /** + * Override the roaming preference for the current ICCID. + * + * Using this call, the carrier app (see #hasCarrierPrivileges) can override + * the platform's notion of a network operator being considered roaming or not. + * The change only affects the ICCID that was active when this call was made. + * + * If null is passed as any of the input, the corresponding value is deleted. + * + *

Requires that the caller have carrier privilege. See #hasCarrierPrivileges. + * + * @param gsmRoamingList - List of MCCMNCs to be considered roaming for 3GPP RATs. + * @param gsmNonRoamingList - List of MCCMNCs to be considered not roaming for 3GPP RATs. + * @param cdmaRoamingList - List of SIDs to be considered roaming for 3GPP2 RATs. + * @param cdmaNonRoamingList - List of SIDs to be considered not roaming for 3GPP2 RATs. + * @return true if the operation was executed correctly. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public boolean setRoamingOverride(List gsmRoamingList, + List gsmNonRoamingList, List cdmaRoamingList, + List cdmaNonRoamingList) { + return setRoamingOverride(getSubId(), gsmRoamingList, gsmNonRoamingList, + cdmaRoamingList, cdmaNonRoamingList); + } + + /** + * Override the roaming preference for the current ICCID. + * + * Using this call, the carrier app (see #hasCarrierPrivileges) can override + * the platform's notion of a network operator being considered roaming or not. + * The change only affects the ICCID that was active when this call was made. + * + * If null is passed as any of the input, the corresponding value is deleted. + * + *

Requires that the caller have carrier privilege. See #hasCarrierPrivileges. + * + * @param subId for which the roaming overrides apply. + * @param gsmRoamingList - List of MCCMNCs to be considered roaming for 3GPP RATs. + * @param gsmNonRoamingList - List of MCCMNCs to be considered not roaming for 3GPP RATs. + * @param cdmaRoamingList - List of SIDs to be considered roaming for 3GPP2 RATs. + * @param cdmaNonRoamingList - List of SIDs to be considered not roaming for 3GPP2 RATs. + * @return true if the operation was executed correctly. + * + * @hide + */ + public boolean setRoamingOverride(int subId, List gsmRoamingList, + List gsmNonRoamingList, List cdmaRoamingList, + List cdmaNonRoamingList) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.setRoamingOverride(subId, gsmRoamingList, gsmNonRoamingList, + cdmaRoamingList, cdmaNonRoamingList); + } catch (RemoteException ex) { + Rlog.e(TAG, "setRoamingOverride RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setRoamingOverride NPE", ex); + } + return false; + } + + /** + * Expose the rest of ITelephony to @SystemApi + */ + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getCdmaMdn() { + return getCdmaMdn(getSubId()); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getCdmaMdn(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getCdmaMdn(subId); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getCdmaMin() { + return getCdmaMin(getSubId()); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getCdmaMin(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return null; + return telephony.getCdmaMin(subId); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int checkCarrierPrivilegesForPackage(String pkgName) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.checkCarrierPrivilegesForPackage(getSubId(), pkgName); + } catch (RemoteException ex) { + Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "checkCarrierPrivilegesForPackage NPE", ex); + } + return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.checkCarrierPrivilegesForPackageAnyPhone(pkgName); + } catch (RemoteException ex) { + Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone NPE", ex); + } + return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public List getCarrierPackageNamesForIntent(Intent intent) { + return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId()); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public List getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.getCarrierPackageNamesForIntentAndPhone(intent, phoneId); + } catch (RemoteException ex) { + Rlog.e(TAG, "getCarrierPackageNamesForIntentAndPhone RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getCarrierPackageNamesForIntentAndPhone NPE", ex); + } + return null; + } + + /** + * Returns the package name that provides the {@link CarrierService} implementation for the + * current subscription, or {@code null} if no package with carrier privileges declares one. + * + *

If this object has been created with {@link #createForSubscriptionId}, then the provided + * subscription ID is used. Otherwise, the default subscription ID will be used. + * + * @return The system-selected package that provides the {@link CarrierService} implementation + * for the current subscription, or {@code null} if none is resolved + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @Nullable String getCarrierServicePackageName() { + return getCarrierServicePackageNameForLogicalSlot(getPhoneId()); + } + + /** + * Returns the package name that provides the {@link CarrierService} implementation for the + * specified {@code logicalSlotIndex}, or {@code null} if no package with carrier privileges + * declares one. + * + * @param logicalSlotIndex The slot index to fetch the {@link CarrierService} package for + * @return The system-selected package that provides the {@link CarrierService} implementation + * for the slot, or {@code null} if none is resolved + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCarrierServicePackageNameForLogicalSlot(logicalSlotIndex); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getCarrierServicePackageNameForLogicalSlot RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getCarrierServicePackageNameForLogicalSlot NPE", ex); + } + return null; + } + + /** @hide */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public List getPackagesWithCarrierPrivileges() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getPackagesWithCarrierPrivileges(getPhoneId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getPackagesWithCarrierPrivileges RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getPackagesWithCarrierPrivileges NPE", ex); + } + return Collections.EMPTY_LIST; + } + + /** + * Get the names of packages with carrier privileges for all the active subscriptions. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @NonNull + public List getCarrierPrivilegedPackagesForAllActiveSubscriptions() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getPackagesWithCarrierPrivilegesForAllPhones(); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions RemoteException", + ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions NPE", ex); + } + return Collections.EMPTY_LIST; + } + + /** + * Call composer status OFF from user setting. + */ + public static final int CALL_COMPOSER_STATUS_OFF = 0; + + /** + * Call composer status ON from user setting. + */ + public static final int CALL_COMPOSER_STATUS_ON = 1; + + /** + * Call composer status Business Only from user setting. + */ + @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_BUSINESS_CALL_COMPOSER) + public static final int CALL_COMPOSER_STATUS_BUSINESS_ONLY = 2; + + /** @hide */ + @IntDef(prefix = {"CALL_COMPOSER_STATUS_"}, + value = { + CALL_COMPOSER_STATUS_ON, + CALL_COMPOSER_STATUS_OFF, + CALL_COMPOSER_STATUS_BUSINESS_ONLY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CallComposerStatus {} + + /** + * Set the user-set status for enriched calling with call composer. + * + * @param status user-set status for enriched calling with call composer. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @throws IllegalArgumentException if requested state is invalid. + * @throws SecurityException if the caller does not have the permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setCallComposerStatus(@CallComposerStatus int status) { + if (com.android.server.telecom.flags.Flags.businessCallComposer()) { + if (status > CALL_COMPOSER_STATUS_BUSINESS_ONLY + || status < CALL_COMPOSER_STATUS_OFF) { + throw new IllegalArgumentException("requested status is invalid"); + } + } else { + if (status > CALL_COMPOSER_STATUS_ON + || status < CALL_COMPOSER_STATUS_OFF) { + throw new IllegalArgumentException("requested status is invalid"); + } + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCallComposerStatus(getSubId(), status); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#setCallComposerStatus", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get the user-set status for enriched calling with call composer. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @throws SecurityException if the caller does not have the permission. + * + * @return the user-set status for enriched calling with call composer, either of + * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public @CallComposerStatus int getCallComposerStatus() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCallComposerStatus(getSubId()); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#getCallComposerStatus", ex); + ex.rethrowFromSystemServer(); + } + return CALL_COMPOSER_STATUS_OFF; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @SuppressLint("RequiresPermission") + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void dial(String number) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.dial(number); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#dial", e); + } + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * + * @deprecated Use {@link android.telecom.TelecomManager#placeCall(Uri address, + * Bundle extras)} instead. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void call(String callingPackage, String number) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.call(callingPackage, number); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#call", e); + } + } + + /** + * @removed Use {@link android.telecom.TelecomManager#endCall()} instead. + * @hide + * @removed + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + public boolean endCall() { + return false; + } + + /** + * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead + * @hide + * @removed + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void answerRingingCall() { + // No-op + } + + /** + * @removed Use {@link android.telecom.TelecomManager#silenceRinger} instead + * @hide + */ + @Deprecated + @SystemApi + @SuppressLint("RequiresPermission") + public void silenceRinger() { + // No-op + } + + /** + * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) + public boolean isOffhook() { + TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE); + return tm.isInCall(); + } + + /** + * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) + public boolean isRinging() { + TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE); + return tm.isRinging(); + } + + /** + * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) + public boolean isIdle() { + TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE); + return !tm.isInCall(); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isRadioOn() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.isRadioOnWithFeature(getOpPackageName(), getAttributionTag()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isRadioOn", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean supplyPin(String pin) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.supplyPinForSubscriber(getSubId(), pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPinForSubscriber", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean supplyPuk(String puk, String pin) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.supplyPukForSubscriber(getSubId(), puk, pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPukForSubscriber", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @deprecated use {@link #supplyIccLockPin(String)} instead. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @Deprecated + public int[] supplyPinReportResult(String pin) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.supplyPinReportResultForSubscriber(getSubId(), pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPinReportResultForSubscriber", e); + } + return new int[0]; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @deprecated use {@link #supplyIccLockPuk(String, String)} instead. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @Deprecated + public int[] supplyPukReportResult(String puk, String pin) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyPukReportResultForSubscriber", e); + } + return new int[0]; + } + + /** + * Supplies a PIN to unlock the ICC and returns the corresponding {@link PinResult}. + * Used when the user enters their ICC unlock PIN to attempt an unlock. + * + * @param pin The user entered PIN. + * @return The result of the PIN. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @NonNull + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public PinResult supplyIccLockPin(@NonNull String pin) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + int[] result = telephony.supplyPinReportResultForSubscriber(getSubId(), pin); + return new PinResult(result[0], result[1]); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyIccLockPin", e); + e.rethrowFromSystemServer(); + } + return PinResult.getDefaultFailedResult(); + } + + /** + * Supplies a PUK and PIN to unlock the ICC and returns the corresponding {@link PinResult}. + * Used when the user enters their ICC unlock PUK and PIN to attempt an unlock. + * + * @param puk The product unlocking key. + * @param pin The user entered PIN. + * @return The result of the PUK and PIN. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @NonNull + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public PinResult supplyIccLockPuk(@NonNull String puk, @NonNull String pin) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + int[] result = telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin); + return new PinResult(result[0], result[1]); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#supplyIccLockPuk", e); + e.rethrowFromSystemServer(); + } + return PinResult.getDefaultFailedResult(); + } + + /** + * Used to notify callers of + * {@link TelephonyManager#sendUssdRequest(String, UssdResponseCallback, Handler)} when the + * network either successfully executes a USSD request, or if there was a failure while + * executing the request. + *

+ * {@link #onReceiveUssdResponse(TelephonyManager, String, CharSequence)} will be called if the + * USSD request has succeeded. + * {@link #onReceiveUssdResponseFailed(TelephonyManager, String, int)} will be called if the + * USSD request has failed. + */ + public static abstract class UssdResponseCallback { + /** + * Called when a USSD request has succeeded. The {@code response} contains the USSD + * response received from the network. The calling app can choose to either display the + * response to the user or perform some operation based on the response. + *

+ * USSD responses are unstructured text and their content is determined by the mobile network + * operator. + * + * @param telephonyManager the TelephonyManager the callback is registered to. + * @param request the USSD request sent to the mobile network. + * @param response the response to the USSD request provided by the mobile network. + **/ + public void onReceiveUssdResponse(final TelephonyManager telephonyManager, + String request, CharSequence response) {}; + + /** + * Called when a USSD request has failed to complete. + * + * @param telephonyManager the TelephonyManager the callback is registered to. + * @param request the USSD request sent to the mobile network. + * @param failureCode failure code indicating why the request failed. Will be either + * {@link TelephonyManager#USSD_RETURN_FAILURE} or + * {@link TelephonyManager#USSD_ERROR_SERVICE_UNAVAIL}. + **/ + public void onReceiveUssdResponseFailed(final TelephonyManager telephonyManager, + String request, int failureCode) {}; + } + + /** + * Sends an Unstructured Supplementary Service Data (USSD) request to the mobile network and + * informs the caller of the response via the supplied {@code callback}. + *

Carriers define USSD codes which can be sent by the user to request information such as + * the user's current data balance or minutes balance. + *

Requires permission: + * {@link android.Manifest.permission#CALL_PHONE} + * @param ussdRequest the USSD command to be executed. + * @param callback called by the framework to inform the caller of the result of executing the + * USSD request (see {@link UssdResponseCallback}). + * @param handler the {@link Handler} to run the request on. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void sendUssdRequest(String ussdRequest, + final UssdResponseCallback callback, Handler handler) { + checkNotNull(callback, "UssdResponseCallback cannot be null."); + final TelephonyManager telephonyManager = this; + + ResultReceiver wrappedCallback = new ResultReceiver(handler) { + @Override + protected void onReceiveResult(int resultCode, Bundle ussdResponse) { + Rlog.d(TAG, "USSD:" + resultCode); + checkNotNull(ussdResponse, "ussdResponse cannot be null."); + UssdResponse response = ussdResponse.getParcelable(USSD_RESPONSE, android.telephony.UssdResponse.class); + + if (resultCode == USSD_RETURN_SUCCESS) { + callback.onReceiveUssdResponse(telephonyManager, response.getUssdRequest(), + response.getReturnMessage()); + } else { + callback.onReceiveUssdResponseFailed(telephonyManager, + response.getUssdRequest(), resultCode); + } + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.handleUssdRequest(getSubId(), ussdRequest, wrappedCallback); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e); + UssdResponse response = new UssdResponse(ussdRequest, ""); + Bundle returnData = new Bundle(); + returnData.putParcelable(USSD_RESPONSE, response); + wrappedCallback.send(USSD_ERROR_SERVICE_UNAVAIL, returnData); + } + } + + /** + * Whether the device is currently on a technology (e.g. UMTS or LTE) which can support + * voice and data simultaneously. This can change based on location or network condition. + * + * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isConcurrentVoiceAndDataSupported() { + try { + ITelephony telephony = getITelephony(); + return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed( + getSubId())); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean handlePinMmi(String dialString) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.handlePinMmi(dialString); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#handlePinMmi", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean handlePinMmiForSubscriber(int subId, String dialString) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.handlePinMmiForSubscriber(subId, dialString); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#handlePinMmi", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void toggleRadioOnOff() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.toggleRadioOnOff(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#toggleRadioOnOff", e); + } + } + + /** + * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean setRadio(boolean turnOn) { + boolean result = true; + try { + if (turnOn) { + clearRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } else { + requestRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } + } catch (Exception e) { + String calledFunction = + turnOn ? "clearRadioPowerOffForReason" : "requestRadioPowerOffForReason"; + Log.e(TAG, "Error calling " + calledFunction, e); + result = false; + } + return result; + } + + /** + * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean setRadioPower(boolean turnOn) { + boolean result = true; + try { + if (turnOn) { + clearRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } else { + requestRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } + } catch (Exception e) { + String calledFunction = + turnOn ? "clearRadioPowerOffForReason" : "requestRadioPowerOffForReason"; + Log.e(TAG, "Error calling " + calledFunction, e); + result = false; + } + return result; + } + + /** + * Vote on powering off the radio for a reason. The radio will be turned on only when there is + * no reason to power it off. When any of the voters want to power it off, it will be turned + * off. In case of emergency, the radio will be turned on even if there are some reasons for + * powering it off, and these radio off votes will be cleared. + *

+ * Each API call is for one reason. However, an app can call the API multiple times for multiple + * reasons. Multiple apps can vote for the same reason but the vote of one app does not affect + * the vote of another app. + *

+ * Each app is responsible for its vote. A powering-off vote for a reason of an app will be + * maintained until it is cleared by calling {@link #clearRadioPowerOffForReason(int)} for that + * reason by the app, or an emergency call is made, or the device is rebooted. When an app + * comes backup from a crash, it needs to make sure if its vote is as expected. An app can use + * the API {@link #getRadioPowerOffReasons()} to check its votes. Votes won't be removed when + * an app crashes. + *

+ * User setting for power state is persistent across device reboots. This applies to all users, + * callers must be careful to update the off reasons when the current user changes. + * + * @param reason The reason for powering off radio. + * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission. + * @throws IllegalStateException if the Telephony service is not currently available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void requestRadioPowerOffForReason(@RadioPowerReason int reason) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (!telephony.requestRadioPowerOffForReason(getSubId(), reason)) { + throw new IllegalStateException("Telephony service is not available."); + } + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#requestRadioPowerOffForReason", e); + e.rethrowAsRuntimeException(); + } + } + + /** + * Remove the vote on powering off the radio for a reason, as requested by + * {@link requestRadioPowerOffForReason}. + * + * @param reason The reason for powering off radio. + * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission. + * @throws IllegalStateException if the Telephony service is not currently available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void clearRadioPowerOffForReason(@RadioPowerReason int reason) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (!telephony.clearRadioPowerOffForReason(getSubId(), reason)) { + throw new IllegalStateException("Telephony service is not available."); + } + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#clearRadioPowerOffForReason", e); + e.rethrowAsRuntimeException(); + } + } + + /** + * Get reasons for powering off radio of the calling app, as requested by + * {@link #requestRadioPowerOffForReason(int)}. + * + * @return Set of reasons for powering off radio of the calling app. + * @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission. + * @throws IllegalStateException if the Telephony service is not currently available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + @NonNull + public Set getRadioPowerOffReasons() { + Set result = new HashSet<>(); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + result.addAll(telephony.getRadioPowerOffReasons(getSubId(), + mContext.getOpPackageName(), mContext.getAttributionTag())); + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getRadioPowerOffReasons", e); + e.rethrowAsRuntimeException(); + } + return result; + } + + /** + * Shut down all the live radios over all the slot indexes. + * + *

To know when the radio has completed powering off, use + * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void shutdownAllRadios() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.shutdownMobileRadios(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e); + e.rethrowAsRuntimeException(); + } + } + + /** + * Check if any radio is on over all the slot indexes. + * + * @return {@code true} if any radio is on over any slot index. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isAnyRadioPoweredOn() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.needMobileRadioShutdown(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isAnyRadioPoweredOn", e); + e.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Radio explicitly powered off (e.g, airplane mode). + * @hide + */ + @SystemApi + public static final int RADIO_POWER_OFF = 0; + + /** + * Radio power is on. + * @hide + */ + @SystemApi + public static final int RADIO_POWER_ON = 1; + + /** + * Radio power unavailable (eg, modem resetting or not booted). + * @hide + */ + @SystemApi + public static final int RADIO_POWER_UNAVAILABLE = 2; + + /** + * @return current modem radio state. + * + *

Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or + * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @RadioPowerState int getRadioPowerState() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return RADIO_POWER_UNAVAILABLE; + } + + /** + * This method should not be used due to privacy and stability concerns. + * + * @hide + */ + @SystemApi + public void updateServiceLocation() { + Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()"); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean enableDataConnectivity() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.enableDataConnectivity(getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#enableDataConnectivity", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean disableDataConnectivity() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.disableDataConnectivity(getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#disableDataConnectivity", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataConnectivityPossible() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager + .getActiveDataSubscriptionId())); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isDataAllowed", e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean needsOtaServiceProvisioning() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.needsOtaServiceProvisioning(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#needsOtaServiceProvisioning", e); + } + return false; + } + + /** + * Get the mobile provisioning url that is used to launch a browser to allow users to manage + * their mobile plan. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}. + * + * TODO: The legacy design only supports single sim design. Ideally, this should support + * multi-sim design in current world. + * + * {@hide} + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @Nullable String getMobileProvisioningUrl() { + try { + final ITelephony service = getITelephony(); + if (service != null) { + return service.getMobileProvisioningUrl(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getMobileProvisioningUrl RemoteException" + ex); + } + return null; + } + + /** + * Turns mobile data on or off. + * If this object has been created with {@link #createForSubscriptionId}, applies to the given + * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param enable Whether to enable mobile data. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead. + * + */ + @Deprecated + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setDataEnabled(boolean enable) { + setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable); + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead. + */ + @SystemApi + @Deprecated + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setDataEnabled(int subId, boolean enable) { + try { + setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling setDataEnabledForReason e:" + e); + } + } + + /** + * @deprecated use {@link #isDataEnabled()} instead. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean getDataEnabled() { + return isDataEnabled(); + } + + /** + * Returns whether mobile data is enabled or not per user setting. There are other factors + * that could disable mobile data, but they are not considered here. + * + * If this object has been created with {@link #createForSubscriptionId}, applies to the given + * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires one of the following permissions: + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}, + * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or + * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app has carrier + * privileges (see {@link #hasCarrierPrivileges}). + * + *

Note that this does not take into account any data restrictions that may be present on the + * calling app. Such restrictions may be inspected with + * {@link ConnectivityManager#getRestrictBackgroundStatus}. + * + * @return true if mobile data is enabled. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, + android.Manifest.permission.MODIFY_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataEnabled() { + try { + return isDataEnabledForReason(DATA_ENABLED_REASON_USER); + } catch (IllegalStateException ise) { + // TODO(b/176163590): Remove this catch once TelephonyManager is booting safely. + Log.e(TAG, "Error calling #isDataEnabled, returning default (false).", ise); + return false; + } + } + + /** + * Returns whether mobile data roaming is enabled on the subscription. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires one of the following permissions: + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}, + * {@link android.Manifest.permission#READ_PHONE_STATE} or + * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app + * has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return {@code true} if the data roaming is enabled on the subscription, otherwise return + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * {@code false}. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataRoamingEnabled() { + boolean isDataRoamingEnabled = false; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + isDataRoamingEnabled = telephony.isDataRoamingEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isDataRoamingEnabled", e); + } + return isDataRoamingEnabled; + } + + /** + * Gets the roaming mode for CDMA phone. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @return the CDMA roaming mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * @see #CDMA_ROAMING_MODE_RADIO_DEFAULT + * @see #CDMA_ROAMING_MODE_HOME + * @see #CDMA_ROAMING_MODE_AFFILIATED + * @see #CDMA_ROAMING_MODE_ANY + * + *

Requires permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public @CdmaRoamingMode int getCdmaRoamingMode() { + int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + mode = telephony.getCdmaRoamingMode(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#getCdmaRoamingMode", ex); + ex.rethrowFromSystemServer(); + } + return mode; + } + + /** + * Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method throws an IllegalStateException. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @param mode CDMA roaming mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process or radio is not currently available, + * the device is not CDMA capable, or the request fails. + * + * @see #CDMA_ROAMING_MODE_RADIO_DEFAULT + * @see #CDMA_ROAMING_MODE_HOME + * @see #CDMA_ROAMING_MODE_AFFILIATED + * @see #CDMA_ROAMING_MODE_ANY + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public void setCdmaRoamingMode(@CdmaRoamingMode int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) { + throw new IllegalStateException("Phone does not support CDMA."); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + boolean result = telephony.setCdmaRoamingMode(getSubId(), mode); + if (!result) throw new IllegalStateException("radio is unavailable."); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#setCdmaRoamingMode", ex); + ex.rethrowFromSystemServer(); + } + } + + /** @hide */ + @IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = { + CDMA_SUBSCRIPTION_UNKNOWN, + CDMA_SUBSCRIPTION_RUIM_SIM, + CDMA_SUBSCRIPTION_NV + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CdmaSubscription{} + + /** + * Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source. + * @hide + */ + @SystemApi + public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; + + /** + * Used for CDMA subscription mode: RUIM/SIM (default) + * @hide + */ + @SystemApi + public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; + + /** + * Used for CDMA subscription mode: NV -> non-volatile memory + * @hide + */ + @SystemApi + public static final int CDMA_SUBSCRIPTION_NV = 1; + + /** + * Gets the subscription mode for CDMA phone. + * + * @return the CDMA subscription mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process or radio is not currently available. + * + * @see #CDMA_SUBSCRIPTION_UNKNOWN + * @see #CDMA_SUBSCRIPTION_RUIM_SIM + * @see #CDMA_SUBSCRIPTION_NV + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public @CdmaSubscription int getCdmaSubscriptionMode() { + int mode = CDMA_SUBSCRIPTION_RUIM_SIM; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + mode = telephony.getCdmaSubscriptionMode(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#getCdmaSubscriptionMode", ex); + ex.rethrowFromSystemServer(); + } + return mode; + } + + /** + * Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method throws an IllegalStateException. + * + * @param mode CDMA subscription mode. + * @throws SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process or radio is not currently available, + * the device is not CDMA capable, or the request fails. + * + * @see #CDMA_SUBSCRIPTION_UNKNOWN + * @see #CDMA_SUBSCRIPTION_RUIM_SIM + * @see #CDMA_SUBSCRIPTION_NV + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public void setCdmaSubscriptionMode(@CdmaSubscription int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) { + throw new IllegalStateException("Phone does not support CDMA."); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + boolean result = telephony.setCdmaSubscriptionMode(getSubId(), mode); + if (!result) throw new IllegalStateException("radio is unavailable."); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#setCdmaSubscriptionMode", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Enables/Disables the data roaming on the subscription. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier + * privileges (see {@link #hasCarrierPrivileges}). + * + * @param isEnabled {@code true} to enable mobile data roaming, otherwise disable it. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setDataRoamingEnabled(boolean isEnabled) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setDataRoamingEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), isEnabled); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setDataRoamingEnabled", e); + } + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * + * @deprecated use {@link #isDataEnabled()} instead. + * @hide + */ + @Deprecated + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean getDataEnabled(int subId) { + try { + return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling isDataEnabledForReason e:" + e); + } + return false; + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. + * @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)} + * instead. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) + public void enableVideoCalling(boolean enable) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.enableVideoCalling(enable); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#enableVideoCalling", e); + } + } + + /** + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. + * @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user + * has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to + * determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to + * determine if video calling is capable. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) + public boolean isVideoCallingEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.isVideoCallingEnabled(getOpPackageName(), getAttributionTag()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e); + } + return false; + } + + /** + * Whether the device supports configuring the DTMF tone length. + * + * @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean canChangeDtmfToneLength() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(), + getAttributionTag()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#canChangeDtmfToneLength", e); + } + return false; + } + + /** + * Whether the device is a world phone. + * + * @return {@code true} if the device is a world phone, and {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public boolean isWorldPhone() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isWorldPhone(mSubId, getOpPackageName(), getAttributionTag()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isWorldPhone", e); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e); + } + return false; + } + + /** + * @deprecated Use {@link TelecomManager#isTtySupported()} instead + * Whether the phone supports TTY mode. + * + * @return {@code true} if the device supports TTY mode, and {@code false} otherwise. + * + */ + @Deprecated + public boolean isTtyModeSupported() { + try { + TelecomManager telecomManager = null; + if (mContext != null) { + telecomManager = mContext.getSystemService(TelecomManager.class); + } + if (telecomManager != null) { + return telecomManager.isTtySupported(); + } + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling TelecomManager#isTtySupported", e); + } + return false; + } + + /** + * Determines whether the device currently supports RTT (Real-time text). Based both on carrier + * support for the feature and device firmware support. + * + * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_IMS}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS) + public boolean isRttSupported() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isRttSupported(mSubId); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isRttSupported", e); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e); + } + return false; + } + /** + * Whether the phone supports hearing aid compatibility. + * + * @return {@code true} if the device supports hearing aid compatibility, and {@code false} + * otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean isHearingAidCompatibilitySupported() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isHearingAidCompatibilitySupported(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isHearingAidCompatibilitySupported", e); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#isHearingAidCompatibilitySupported", e); + } + return false; + } + + /** + * Returns the IMS Registration Status for a particular Subscription ID. + * + * @param subId Subscription ID + * @return true if IMS status is registered, false if the IMS status is not registered or a + * RemoteException occurred. + * Use {@link ImsMmTelManager.RegistrationCallback} instead. + * @hide + */ + public boolean isImsRegistered(int subId) { + try { + return getITelephony().isImsRegistered(subId); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * Returns the IMS Registration Status for a particular Subscription ID, which is determined + * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an + * invalid subscription ID is used during creation, will the default subscription ID will be + * used. + * + * @return true if IMS status is registered, false if the IMS status is not registered or a + * RemoteException occurred. + * @see SubscriptionManager#getDefaultSubscriptionId() + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public boolean isImsRegistered() { + try { + return getITelephony().isImsRegistered(getSubId()); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * The current status of Voice over LTE for the subscription associated with this instance when + * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was + * used during creation, the default subscription ID will be used. + * @return true if Voice over LTE is available or false if it is unavailable or unknown. + * @see SubscriptionManager#getDefaultSubscriptionId() + *

+ * Use {@link ImsMmTelManager#isAvailable(int, int)} instead. + * @hide + */ + @UnsupportedAppUsage + public boolean isVolteAvailable() { + try { + return getITelephony().isAvailable(getSubId(), + MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + ImsRegistrationImplBase.REGISTRATION_TECH_LTE); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * The availability of Video Telephony (VT) for the subscription ID specified when this instance + * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was + * used during creation, the default subscription ID will be used. To query the + * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}. + * @return true if VT is available, or false if it is unavailable or unknown. + * Use {@link ImsMmTelManager#isAvailable(int, int)} instead. + * @hide + */ + @UnsupportedAppUsage + public boolean isVideoTelephonyAvailable() { + try { + return getITelephony().isVideoTelephonyAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified. + * @param subId the subscription ID. + * @return true if VoWiFi is available, or false if it is unavailable or unknown. + * Use {@link ImsMmTelManager#isAvailable(int, int)} instead. + * @hide + */ + @UnsupportedAppUsage + public boolean isWifiCallingAvailable() { + try { + return getITelephony().isWifiCallingAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * The technology that IMS is registered for for the MMTEL feature. + * @param subId subscription ID to get IMS registration technology for. + * @return The IMS registration technology that IMS is registered to for the MMTEL feature. + * Valid return results are: + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration, + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} for registration over + * other sim's internet, or + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the + * result is unavailable. + * Use {@link ImsMmTelManager.RegistrationCallback} instead. + * @hide + */ + public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() { + try { + return getITelephony().getImsRegTechnologyForMmTel(getSubId()); + } catch (RemoteException ex) { + return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; + } + } + + /** + * Set TelephonyProperties.icc_operator_numeric for the given phone. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setSimOperatorNumericForPhone(int phoneId, String numeric) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.icc_operator_numeric(), phoneId, numeric); + TelephonyProperties.icc_operator_numeric(newList); + } + } + + /** + * Set TelephonyProperties.icc_operator_alpha for the given phone. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public void setSimOperatorNameForPhone(int phoneId, String name) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.icc_operator_alpha(), phoneId, name); + TelephonyProperties.icc_operator_alpha(newList); + } + } + + /** + * Set TelephonyProperties.icc_operator_iso_country for the default phone. + * + * @hide + */ + public void setSimCountryIso(String iso) { + int phoneId = getPhoneId(); + setSimCountryIsoForPhone(phoneId, iso); + } + + /** + * Set TelephonyProperties.icc_operator_iso_country for the given phone. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public void setSimCountryIsoForPhone(int phoneId, String iso) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.icc_operator_iso_country(), phoneId, iso); + TelephonyProperties.icc_operator_iso_country(newList); + } + } + + /** + * Set TelephonyProperties.sim_state for the default phone. + * + * @hide + */ + public void setSimState(String state) { + int phoneId = getPhoneId(); + setSimStateForPhone(phoneId, state); + } + + /** + * Set TelephonyProperties.sim_state for the given phone. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public void setSimStateForPhone(int phoneId, String state) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.sim_state(), phoneId, state); + TelephonyProperties.sim_state(newList); + } + } + + /** + * Powers down the SIM. SIM must be up prior. + * @hide + */ + public static final int CARD_POWER_DOWN = 0; + + /** + * Powers up the SIM normally. SIM must be down prior. + * @hide + */ + public static final int CARD_POWER_UP = 1; + + /** + * Powers up the SIM in PASS_THROUGH mode. SIM must be down prior. + * When SIM is powered up in PASS_THOUGH mode, the modem does not send + * any command to it (for example SELECT of MF, or TERMINAL CAPABILITY), + * and the SIM card is controlled completely by Telephony sending APDUs + * directly. The SIM card state will be RIL_CARDSTATE_PRESENT and the + * number of card apps will be 0. + * No new error code is generated. Emergency calls are supported in the + * same way as if the SIM card is absent. + * The PASS_THROUGH mode is valid only for the specific card session where it + * is activated, and normal behavior occurs at the next SIM initialization, + * unless PASS_THROUGH mode is requested again. Hence, the last power-up mode + * is NOT persistent across boots. On reboot, SIM will power up normally. + * @hide + */ + public static final int CARD_POWER_UP_PASS_THROUGH = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CARD_POWER"}, + value = { + CARD_POWER_DOWN, + CARD_POWER_UP, + CARD_POWER_UP_PASS_THROUGH, + }) + public @interface SimPowerState {} + + /** + * Set SIM card power state. + * + * @param state State of SIM (power down, power up, pass through) + * @see #CARD_POWER_DOWN + * @see #CARD_POWER_UP + * @see #CARD_POWER_UP_PASS_THROUGH + * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED} + * broadcasts to determine success or failure and timeout if needed. + * + * @deprecated prefer {@link setSimPowerState(int, Executor, Consumer)}. + * There is no guarantee that SIM power changes will trigger ACTION_SIM_STATE_CHANGED on new + * devices. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@hide} + **/ + @SystemApi + @Deprecated + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void setSimPowerState(int state) { + setSimPowerStateForSlot(getSlotIndex(), state); + } + + /** + * Set SIM card power state. + * + * @param slotIndex SIM slot id + * @param state State of SIM (power down, power up, pass through) + * @see #CARD_POWER_DOWN + * @see #CARD_POWER_UP + * @see #CARD_POWER_UP_PASS_THROUGH + * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED} + * broadcasts to determine success or failure and timeout if needed. + * + * @deprecated prefer {@link setSimPowerStateForSlot(int, int, Executor, Consumer)}. + * changes will trigger ACTION_SIM_STATE_CHANGED on new devices. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@hide} + **/ + @SystemApi + @Deprecated + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void setSimPowerStateForSlot(int slotIndex, int state) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setSimPowerStateForSlot(slotIndex, state); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e); + } + } + + /** + * Set SIM card power state. + * + * @param state State of SIM (power down, power up, pass through) + * @see #CARD_POWER_DOWN + * @see #CARD_POWER_UP + * @see #CARD_POWER_UP_PASS_THROUGH + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * @see #SET_SIM_POWER_STATE_SUCCESS + * @see #SET_SIM_POWER_STATE_ALREADY_IN_STATE + * @see #SET_SIM_POWER_STATE_MODEM_ERROR + * @see #SET_SIM_POWER_STATE_SIM_ERROR + * @see #SET_SIM_POWER_STATE_NOT_SUPPORTED + * @throws IllegalArgumentException if requested SIM state is invalid + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@hide} + **/ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void setSimPowerState(@SimPowerState int state, @NonNull Executor executor, + @NonNull @SetSimPowerStateResult Consumer callback) { + setSimPowerStateForSlot(getSlotIndex(), state, executor, callback); + } + + /** + * Set SIM card power state. + * + * @param slotIndex SIM slot id + * @param state State of SIM (power down, power up, pass through) + * @see #CARD_POWER_DOWN + * @see #CARD_POWER_UP + * @see #CARD_POWER_UP_PASS_THROUGH + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * @see #SET_SIM_POWER_STATE_SUCCESS + * @see #SET_SIM_POWER_STATE_ALREADY_IN_STATE + * @see #SET_SIM_POWER_STATE_MODEM_ERROR + * @see #SET_SIM_POWER_STATE_SIM_ERROR + * @see #SET_SIM_POWER_STATE_NOT_SUPPORTED + * @throws IllegalArgumentException if requested SIM state is invalid + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@hide} + **/ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void setSimPowerStateForSlot(int slotIndex, @SimPowerState int state, + @NonNull Executor executor, + @NonNull @SetSimPowerStateResult Consumer callback) { + if (state != CARD_POWER_DOWN && state != CARD_POWER_UP + && state != CARD_POWER_UP_PASS_THROUGH) { + throw new IllegalArgumentException("requested SIM state is invalid"); + } + try { + ITelephony telephony = getITelephony(); + if (telephony == null) throw new IllegalStateException("Telephony is null."); + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> callback.accept(result))); + } + }; + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e); + runOnBackgroundThread(() -> executor.execute( + () -> callback.accept(SET_SIM_POWER_STATE_MODEM_ERROR))); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", + e); + } + } + + /** + * Set baseband version by phone id. + * + * @param phoneId for which baseband version is set + * @param version baseband version + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public void setBasebandVersionForPhone(int phoneId, String version) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.baseband_version(), phoneId, version); + TelephonyProperties.baseband_version(newList); + } + } + + /** + * Get baseband version for the default phone. + * + * @return baseband version. + * @hide + */ + public String getBasebandVersion() { + int phoneId = getPhoneId(); + return getBasebandVersionForPhone(phoneId); + } + + /** + * Get baseband version by phone id. + * + * @return baseband version. + * @hide + */ + public String getBasebandVersionForPhone(int phoneId) { + return getTelephonyProperty(phoneId, TelephonyProperties.baseband_version(), ""); + } + + /** + * Set phone type by phone id. + * + * @param phoneId for which phone type is set + * @param type phone type + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public void setPhoneType(int phoneId, int type) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.current_active_phone(), phoneId, type); + TelephonyProperties.current_active_phone(newList); + } + } + + /** + * Get OTASP number schema by phone id. + * + * @param phoneId for which OTA SP number schema is get + * @param defaultValue default value + * @return OTA SP number schema + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + return getTelephonyProperty( + phoneId, TelephonyProperties.otasp_num_schema(), defaultValue); + } + + return defaultValue; + } + + /** + * Get SMS receive capable from system property by phone id. + * + * @param phoneId for which SMS receive capable is get + * @param defaultValue default value + * @return SMS receive capable + * + * @hide + */ + public boolean getSmsReceiveCapableForPhone(int phoneId, boolean defaultValue) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + return getTelephonyProperty(phoneId, TelephonyProperties.sms_receive(), defaultValue); + } + + return defaultValue; + } + + /** + * Get SMS send capable from system property by phone id. + * + * @param phoneId for which SMS send capable is get + * @param defaultValue default value + * @return SMS send capable + * + * @hide + */ + public boolean getSmsSendCapableForPhone(int phoneId, boolean defaultValue) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + return getTelephonyProperty(phoneId, TelephonyProperties.sms_send(), defaultValue); + } + + return defaultValue; + } + + /** + * Get the component name of the default app to direct respond-via-message intent for the + * user associated with this subscription, update the cache if there is no respond-via-message + * application currently configured for this user. + * @return component name of the app and class to direct Respond Via Message intent to, or + * {@code null} if the functionality is not supported. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getDefaultRespondViaMessageApplication(getSubId(), true); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in getAndUpdateDefaultRespondViaMessageApplication: " + e); + } + return null; + } + + /** + * Get the component name of the default app to direct respond-via-message intent for the + * user associated with this subscription. + * @return component name of the app and class to direct Respond Via Message intent to, or + * {@code null} if the functionality is not supported. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public @Nullable ComponentName getDefaultRespondViaMessageApplication() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getDefaultRespondViaMessageApplication(getSubId(), false); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in getDefaultRespondViaMessageApplication: " + e); + } + return null; + } + + /** + * Set the alphabetic name of current registered operator. + * @param phoneId which phone you want to set + * @param name the alphabetic name of current registered operator. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setNetworkOperatorNameForPhone(int phoneId, String name) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.operator_alpha(), phoneId, name); + try { + TelephonyProperties.operator_alpha(newList); + } catch (IllegalArgumentException e) { //property value is longer than the byte limit + Log.e(TAG, "setNetworkOperatorNameForPhone: ", e); + + int numberOfEntries = newList.size(); + int maxOperatorLength = //save 1 byte for joiner " , " + (SystemProperties.PROP_VALUE_MAX - numberOfEntries) / numberOfEntries; + + //examine and truncate every operator and retry + for (int i = 0; i < newList.size(); i++) { + if (newList.get(i) != null) { + newList.set(i, TextUtils + .truncateStringForUtf8Storage(newList.get(i), maxOperatorLength)); + } + } + TelephonyProperties.operator_alpha(newList); + Log.e(TAG, "successfully truncated operator_alpha: " + newList); + } + } + } + + /** + * Set the numeric name (MCC+MNC) of current registered operator. + * @param phoneId for which phone type is set + * @param operator the numeric name (MCC+MNC) of current registered operator + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.operator_numeric(), phoneId, numeric); + TelephonyProperties.operator_numeric(newList); + } + } + + /** + * Set roaming state of the current network, for GSM purposes. + * @param phoneId which phone you want to set + * @param isRoaming is network in romaing state or not + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.operator_is_roaming(), phoneId, isRoaming); + TelephonyProperties.operator_is_roaming(newList); + } + } + + /** + * Set the network type currently in use on the device for data transmission. + * + * If this object has been created with {@link #createForSubscriptionId}, applies to the + * phoneId associated with the given subId. Otherwise, applies to the phoneId associated with + * {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * @param type the network type currently in use on the device for data transmission + * @hide + */ + public void setDataNetworkType(int type) { + int phoneId = getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId()); + setDataNetworkTypeForPhone(phoneId, type); + } + + /** + * Set the network type currently in use on the device for data transmission. + * @param phoneId which phone you want to set + * @param type the network type currently in use on the device for data transmission + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public void setDataNetworkTypeForPhone(int phoneId, int type) { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.data_network_type(), phoneId, + ServiceState.rilRadioTechnologyToString(type)); + TelephonyProperties.data_network_type(newList); + } + } + + /** + * Returns the subscription ID for the given phone account. + * @hide + */ + @UnsupportedAppUsage + public int getSubIdForPhoneAccount(@Nullable PhoneAccount phoneAccount) { + int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + if (phoneAccount != null + && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { + retval = getSubscriptionId(phoneAccount.getAccountHandle()); + } + return retval; + } + + /** + * Determines the {@link PhoneAccountHandle} associated with this TelephonyManager. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + *

Requires Permission android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}) + * + * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null} + * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is + * data-only or an opportunistic subscription. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public @Nullable PhoneAccountHandle getPhoneAccountHandle() { + return getPhoneAccountHandleForSubscriptionId(getSubId()); + } + + /** + * Determines the {@link PhoneAccountHandle} associated with a subscription Id. + * + * @param subscriptionId The subscription Id to check. + * @return The {@link PhoneAccountHandle} associated with a subscription Id, or {@code null} if + * there is no associated {@link PhoneAccountHandle}. + * @hide + */ + public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) { + PhoneAccountHandle returnValue = null; + try { + ITelephony service = getITelephony(); + if (service != null) { + returnValue = service.getPhoneAccountHandleForSubscriptionId(subscriptionId); + } + } catch (RemoteException e) { + } + + return returnValue; + } + + /** + * Returns the subscription ID for the given phone account handle. + * + * @param phoneAccountHandle the phone account handle for outgoing calls + * @return subscription ID for the given phone account handle; or + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} + * if not available; or throw a SecurityException if the caller doesn't have the + * permission. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int getSubscriptionId(@NonNull PhoneAccountHandle phoneAccountHandle) { + return mPhoneAccountHandleToSubIdCache.query(phoneAccountHandle); + } + + /** + * Resets telephony manager settings back to factory defaults. + * + * @hide + */ + public void factoryReset(int subId) { + try { + Log.d(TAG, "factoryReset: subId=" + subId); + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.factoryReset(subId, getOpPackageName()); + } + } catch (RemoteException e) { + } + } + + + /** + * Resets Telephony and IMS settings back to factory defaults only for the subscription + * associated with this instance. + * @see #createForSubscriptionId(int) + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public void resetSettings() { + try { + Log.d(TAG, "resetSettings: subId=" + getSubId()); + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.factoryReset(getSubId(), getOpPackageName()); + } + } catch (RemoteException e) { + } + } + + + /** + * Returns a locale based on the country and language from the SIM. Returns {@code null} if + * no locale could be derived from subscriptions. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @see Locale#toLanguageTag() + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @Nullable public Locale getSimLocale() { + try { + final ITelephony telephony = getITelephony(); + if (telephony != null) { + String languageTag = telephony.getSimLocaleForSubscriber(getSubId()); + if (!TextUtils.isEmpty(languageTag)) { + return Locale.forLanguageTag(languageTag); + } + } + } catch (RemoteException ex) { + } + return null; + } + + /** + * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}. + * @hide + */ + @SystemApi + public static class ModemActivityInfoException extends Exception { + /** Indicates that an unknown error occurred */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Indicates that the modem or phone processes are not available (such as when the device + * is in airplane mode). + */ + public static final int ERROR_PHONE_NOT_AVAILABLE = 1; + + /** + * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo} + */ + public static final int ERROR_INVALID_INFO_RECEIVED = 2; + + /** + * Indicates that the modem encountered an internal failure when processing the request + * for activity info. + */ + public static final int ERROR_MODEM_RESPONSE_ERROR = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERROR_"}, + value = { + ERROR_UNKNOWN, + ERROR_PHONE_NOT_AVAILABLE, + ERROR_INVALID_INFO_RECEIVED, + ERROR_MODEM_RESPONSE_ERROR, + }) + public @interface ModemActivityInfoError {} + + private final int mErrorCode; + + /** + * An exception with ModemActivityInfo specific error codes. + * + * @param errorCode a ModemActivityInfoError code. + */ + public ModemActivityInfoException(@ModemActivityInfoError int errorCode) { + mErrorCode = errorCode; + } + + public @ModemActivityInfoError int getErrorCode() { + return mErrorCode; + } + + @Override + public String toString() { + switch (mErrorCode) { + case ERROR_UNKNOWN: return "ERROR_UNKNOWN"; + case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE"; + case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED"; + case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR"; + default: return "UNDEFINED"; + } + } + } + + /** + * Requests the current modem activity info. + * + * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since + * the last restart of the phone process. + * + * @param callback A callback object to which the result will be delivered. If there was an + * error processing the request, {@link OutcomeReceiver#onError} will be called + * with more details about the error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + // Pass no handler into the receiver, since we're going to be trampolining the call to the + // listener onto the provided executor. + ResultReceiver wrapperResultReceiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle data) { + if (data == null) { + Log.w(TAG, "requestModemActivityInfo: received null bundle"); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + data.setDefusable(true); + if (data.containsKey(EXCEPTION_RESULT_KEY)) { + int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY); + sendErrorToListener(receivedErrorCode); + return; + } + + if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) { + Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key"); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY); + if (!(receivedResult instanceof ModemActivityInfo)) { + Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't " + + "a ModemActivityInfo."); + sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN); + return; + } + ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult; + if (!modemActivityInfo.isValid()) { + Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo"); + sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED); + return; + } + Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo); + sendResultToListener(modemActivityInfo); + } + + private void sendResultToListener(ModemActivityInfo info) { + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onResult(info))); + } + + private void sendErrorToListener(int code) { + ModemActivityInfoException e = new ModemActivityInfoException(code); + Binder.withCleanCallingIdentity(() -> + executor.execute(() -> + callback.onError(e))); + } + }; + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.requestModemActivityInfo(wrapperResultReceiver); + return; + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e); + } + executor.execute(() -> callback.onError( + new ModemActivityInfoException( + ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE))); + } + + /** + * Returns the current {@link ServiceState} information. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * If you want continuous updates of service state info, register a {@link PhoneStateListener} + * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * May return {@code null} when the subscription is inactive or when there was an error + * communicating with the phone process. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(allOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @Nullable ServiceState getServiceState() { + return getServiceState(getLocationData()); + } + + /** + * Returns the current {@link ServiceState} information. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * If you want continuous updates of service state info, register a {@link PhoneStateListener} + * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event. + * + * There's another way to renounce permissions with a custom context + * {@code AttributionSource.Builder#setRenouncedPermissions(Set)} but only for system + * apps. To avoid confusion, calling this method supersede renouncing permissions with a + * custom context. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * @param includeLocationData Specifies if the caller would like to receive + * location related information. + * May return {@code null} when the subscription is inactive or when there was an error + * communicating with the phone process. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(allOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) { + return getServiceStateForSubscriber(getSubId(), + includeLocationData != INCLUDE_LOCATION_DATA_FINE, + includeLocationData == INCLUDE_LOCATION_DATA_NONE); + } + + /** + * Returns the service state information on specified subscription. Callers require + * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information. + * + * May return {@code null} when the subscription is inactive or when there was an error + * communicating with the phone process. + * @param renounceFineLocationAccess Set this to true if the caller would not like to receive + * location related information which will be sent if the caller already possess + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission + * @param renounceCoarseLocationAccess Set this to true if the caller would not like to + * receive location related information which will be sent if the caller already possess + * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions. + */ + private ServiceState getServiceStateForSubscriber(int subId, + boolean renounceFineLocationAccess, + boolean renounceCoarseLocationAccess) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getServiceStateForSubscriber(subId, renounceFineLocationAccess, + renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e); + } catch (NullPointerException e) { + AnomalyReporter.reportAnomaly( + UUID.fromString("e2bed88e-def9-476e-bd71-3e572a8de6d1"), + "getServiceStateForSubscriber " + subId + " NPE"); + } + return null; + } + + /** + * Returns the service state information on specified subscription. Callers require + * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information. + * + * May return {@code null} when the subscription is inactive or when there was an error + * communicating with the phone process. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + public ServiceState getServiceStateForSubscriber(int subId) { + return getServiceStateForSubscriber(subId, false, false); + } + + /** + * Returns the URI for the per-account voicemail ringtone set in Phone settings. + * + * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the + * voicemail ringtone. + * @return The URI for the ringtone to play when receiving a voicemail from a specific + * PhoneAccount. May be {@code null} if no ringtone is set. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getVoicemailRingtoneUri(accountHandle); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getVoicemailRingtoneUri", e); + } + return null; + } + + /** + * Sets the per-account voicemail ringtone. + * + *

Requires that the calling app is the default dialer, or has carrier privileges (see + * {@link #hasCarrierPrivileges}, or has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the + * voicemail ringtone. + * @param uri The URI for the ringtone to play when receiving a voicemail from a specific + * PhoneAccount. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS} + * instead. + */ + @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setVoicemailRingtoneUri(getOpPackageName(), phoneAccountHandle, uri); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setVoicemailRingtoneUri", e); + } + } + + /** + * Returns whether vibration is set for voicemail notification in Phone settings. + * + * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the + * voicemail vibration setting. + * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isVoicemailVibrationEnabled(accountHandle); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e); + } + return false; + } + + /** + * Sets the per-account preference whether vibration is enabled for voicemail notifications. + * + *

Requires that the calling app is the default dialer, or has carrier privileges (see + * {@link #hasCarrierPrivileges}, or has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the + * voicemail vibration setting. + * @param enabled Whether to enable or disable vibration for voicemail notifications from a + * specific PhoneAccount. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS} + * instead. + */ + @Deprecated + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle, + boolean enabled) { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setVoicemailVibrationEnabled(getOpPackageName(), phoneAccountHandle, + enabled); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e); + } + } + + /** + * Returns carrier id of the current subscription. + *

To recognize a carrier (including MVNO) as a first-class identity, Android assigns each + * carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android + * platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in + * here + * + *

Apps which have carrier-specific configurations or business logic can use the carrier id + * as an Android platform-wide identifier for carriers. + * + * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the + * subscription is unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int getSimCarrierId() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSubscriptionCarrierId(getSubId()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return UNKNOWN_CARRIER_ID; + } + + /** + * Returns carrier id name of the current subscription. + *

Carrier id name is a user-facing name of carrier id returned by + * {@link #getSimCarrierId()}, usually the brand name of the subsidiary + * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but + * should have a single carrier name. Carrier name is not a canonical identity, + * use {@link #getSimCarrierId()} instead. + *

The returned carrier name is unlocalized. + * + * @return Carrier name of the current subscription. Return {@code null} if the subscription is + * unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @Nullable CharSequence getSimCarrierIdName() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSubscriptionCarrierName(getSubId()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return null; + } + + /** + * Returns fine-grained carrier ID of the current subscription. + * + * A specific carrier ID can represent the fact that a carrier may be in effect an aggregation + * of other carriers (ie in an MVNO type scenario) where each of these specific carriers which + * are used to make up the actual carrier service may have different carrier configurations. + * A specific carrier ID could also be used, for example, in a scenario where a carrier requires + * different carrier configuration for different service offering such as a prepaid plan. + * + * the specific carrier ID would be used for configuration purposes, but apps wishing to know + * about the carrier itself should use the regular carrier ID returned by + * {@link #getSimCarrierId()}. + * + * e.g, Tracfone SIMs could return different specific carrier ID based on IMSI from current + * subscription while carrier ID remains the same. + * + *

For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()} + *

Specific carrier ids are defined in the same way as carrier id + * here + * except each with a "parent" id linking to its top-level carrier id. + * + * @return Returns fine-grained carrier id of the current subscription. + * Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot + * be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int getSimSpecificCarrierId() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSubscriptionSpecificCarrierId(getSubId()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return UNKNOWN_CARRIER_ID; + } + + /** + * Similar like {@link #getSimCarrierIdName()}, returns user-facing name of the + * specific carrier id returned by {@link #getSimSpecificCarrierId()}. + * + * The specific carrier ID would be used for configuration purposes, but apps wishing to know + * about the carrier itself should use the regular carrier ID returned by + * {@link #getSimCarrierIdName()}. + * + *

The returned name is unlocalized. + * + * @return user-facing name of the subscription specific carrier id. Return {@code null} if the + * subscription is unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @Nullable CharSequence getSimSpecificCarrierIdName() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSubscriptionSpecificCarrierName(getSubId()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return null; + } + + /** + * Returns carrier id based on sim MCCMNC (returned by {@link #getSimOperator()}) only. + * This is used for fallback when configurations/logic for exact carrier id + * {@link #getSimCarrierId()} are not found. + * + * Android carrier id table here + * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier + * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id + * by default. After carrier id table update, a new carrier id was assigned. If apps don't + * take the update with the new id, it might be helpful to always fallback by using carrier + * id based on MCCMNC if there is no match. + * + * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the + * subscription is unavailable or the carrier cannot be identified. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int getCarrierIdFromSimMccMnc() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getCarrierIdFromMccMnc(getSlotIndex(), getSimOperator(), true); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return UNKNOWN_CARRIER_ID; + } + + /** + * Returns carrier id based on MCCMNC (returned by {@link #getSimOperator()}) only. This is + * used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()} + * are not found. + * + * Android carrier id table here + * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier + * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id + * by default. After carrier id table update, a new carrier id was assigned. If apps don't + * take the update with the new id, it might be helpful to always fallback by using carrier + * id based on MCCMNC if there is no match. + * + * @return matching carrier id from passing MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the + * subscription is unavailable or the carrier cannot be identified. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getCarrierIdFromMccMnc(String mccmnc) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc, false); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return UNKNOWN_CARRIER_ID; + } + + /** + * Return a list of certs as hex strings from loaded carrier privileges access rules. + * + * @return a list of certificates as hex strings, or an empty list if there are no certs or + * privilege rules are not loaded yet. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @NonNull + public List getCertsFromCarrierPrivilegeAccessRules() { + List certs = null; + try { + ITelephony service = getITelephony(); + if (service != null) { + certs = service.getCertsFromCarrierPrivilegeAccessRules(getSubId()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return certs == null ? Collections.emptyList() : certs; + } + + /** + * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}. + * All uicc applications are uniquely identified by application ID, represented by the hex + * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220 + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} + * + * @param appType the uicc app type. + * @return Application ID for specified app type or {@code null} if no uicc or error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @Nullable + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public String getAidForAppType(@UiccAppType int appType) { + return getAidForAppType(getSubId(), appType); + } + + /** + * same as {@link #getAidForAppType(int)} + * @hide + */ + public String getAidForAppType(int subId, int appType) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getAidForAppType(subId, appType); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getAidForAppType", e); + } + return null; + } + + /** + * Return the Electronic Serial Number. + * + * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * + * @return ESN or null if error. + * @hide + */ + public String getEsn() { + return getEsn(getSubId()); + } + + /** + * Return the Electronic Serial Number. + * + * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * + * @param subId the subscription ID that this request applies to. + * @return ESN or null if error. + * @hide + */ + public String getEsn(int subId) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getEsn(subId); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getEsn", e); + } + return null; + } + + /** + * Return the Preferred Roaming List Version + * + * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * + * @return PRLVersion or null if error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CDMA}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA) + public String getCdmaPrlVersion() { + return getCdmaPrlVersion(getSubId()); + } + + /** + * Return the Preferred Roaming List Version + * + * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * + * @param subId the subscription ID that this request applies to. + * @return PRLVersion or null if error. + * @hide + */ + public String getCdmaPrlVersion(int subId) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getCdmaPrlVersion(subId); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getCdmaPrlVersion", e); + } + return null; + } + + /** + * Get snapshot of Telephony histograms + * @return List of Telephony histograms + * Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * Or the calling app has carrier privileges. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public List getTelephonyHistograms() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getTelephonyHistograms(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getTelephonyHistograms", e); + } + return null; + } + + /** + * Set the allowed carrier list for slotIndex + * Require system privileges. In the future we may add this to carrier APIs. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * + *

This method works only on devices with {@link + * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. + * + * @deprecated use setCarrierRestrictionRules instead + * + * @return The number of carriers set successfully. Should be length of + * carrierList on success; -1 if carrierList null or on error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK) + public int setAllowedCarriers(int slotIndex, List carriers) { + if (carriers == null || !SubscriptionManager.isValidPhoneId(slotIndex)) { + return -1; + } + // Execute the method setCarrierRestrictionRules with an empty excluded list. + // If the allowed list is empty, it means that all carriers are allowed (default allowed), + // otherwise it means that only specified carriers are allowed (default not allowed). + CarrierRestrictionRules carrierRestrictionRules = CarrierRestrictionRules.newBuilder() + .setAllowedCarriers(carriers) + .setDefaultCarrierRestriction( + carriers.isEmpty() + ? CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED + : CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) + .build(); + + int result = setCarrierRestrictionRules(carrierRestrictionRules); + + // Convert result into int, as required by this method. + if (result == SET_CARRIER_RESTRICTION_SUCCESS) { + return carriers.size(); + } else { + return -1; + } + } + + /** + * The carrier restrictions were successfully set. + * @hide + */ + @SystemApi + public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0; + + /** + * The carrier restrictions were not set due to lack of support in the modem. This can happen + * if the modem does not support setting the carrier restrictions or if the configuration + * passed in the {@code setCarrierRestrictionRules} is not supported by the modem. + * @hide + */ + @SystemApi + public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; + + /** + * The setting of carrier restrictions failed. + * @hide + */ + @SystemApi + public static final int SET_CARRIER_RESTRICTION_ERROR = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_CARRIER_RESTRICTION_"}, + value = { + SET_CARRIER_RESTRICTION_SUCCESS, + SET_CARRIER_RESTRICTION_NOT_SUPPORTED, + SET_CARRIER_RESTRICTION_ERROR + }) + public @interface SetCarrierRestrictionResult {} + + /** + * The SIM power state was successfully set. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_SUCCESS = 0; + + /** + * The SIM is already in the requested power state. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_ALREADY_IN_STATE = 1; + + /** + * Failed to connect to the modem to make the power state request. This may happen if the + * modem has an error. The user may want to make the request again later. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_MODEM_ERROR = 2; + + /** + * Failed to connect to the SIM to make the power state request. This may happen if the + * SIM has been removed. The user may want to make the request again later. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_SIM_ERROR = 3; + + /** + * The modem version does not support synchronous power. + * @hide + */ + @SystemApi + public static final int SET_SIM_POWER_STATE_NOT_SUPPORTED = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_SIM_POWER_STATE_"}, + value = { + SET_SIM_POWER_STATE_SUCCESS, + SET_SIM_POWER_STATE_ALREADY_IN_STATE, + SET_SIM_POWER_STATE_MODEM_ERROR, + SET_SIM_POWER_STATE_SIM_ERROR, + SET_SIM_POWER_STATE_NOT_SUPPORTED + }) + public @interface SetSimPowerStateResult {} + + /** + * Set the allowed carrier list and the excluded carrier list indicating the priority between + * the two lists. + * Requires system privileges. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * + *

This method works only on devices with {@link + * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. + * + * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success. + * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the + * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SetCarrierRestrictionResult + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK) + public int setCarrierRestrictionRules(@NonNull CarrierRestrictionRules rules) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.setAllowedCarriers(rules); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e); + } catch (NullPointerException e) { + Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e); + } + return SET_CARRIER_RESTRICTION_ERROR; + } + + /** + * Get the allowed carrier list for slotIndex. + * Requires system privileges. + * + *

This method returns valid data on devices with {@link + * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. + * + * @deprecated Apps should use {@link getCarriersRestrictionRules} to retrieve the list of + * allowed and excliuded carriers, as the result of this API is valid only when the excluded + * list is empty. This API could return an empty list, even if some restrictions are present. + * + * @return List of {@link android.telephony.CarrierIdentifier}; empty list + * means all carriers are allowed. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK) + public List getAllowedCarriers(int slotIndex) { + if (SubscriptionManager.isValidPhoneId(slotIndex)) { + CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules(); + if (carrierRestrictionRule != null) { + return carrierRestrictionRule.getAllowedCarriers(); + } + } + return new ArrayList(0); + } + + /** + * Get the allowed carrier list and the excluded carrier list indicating the priority between + * the two lists. + * Require system privileges. In the future we may add this to carrier APIs. + * + *

This method returns valid data on devices with {@link + * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. + * + * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the + * excluded carrier list with the priority between the two lists. Returns {@code null} + * in case of error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK) + @Nullable + public CarrierRestrictionRules getCarrierRestrictionRules() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getAllowedCarriers(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e); + } catch (NullPointerException e) { + Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e); + } + return null; + } + + /** + * Carrier restriction status value is unknown, in case modem did not provide any + * information about carrier restriction status. + */ + public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0; + + /** The device is not restricted to a carrier */ + public static final int CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED = 1; + + /** The device is restricted to a carrier. */ + public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2; + + /** The device is restricted to the carrier of the calling application. */ + public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3; + + /** @hide */ + @IntDef(prefix = {"CARRIER_RESTRICTION_STATUS_"}, value = { + CARRIER_RESTRICTION_STATUS_UNKNOWN, + CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED, + CARRIER_RESTRICTION_STATUS_RESTRICTED, + CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CarrierRestrictionStatus { + } + + /** + * Get the carrier restriction status of the device. + *

To fetch the carrier restriction status of the device the calling application needs to be + * allowlisted to Android at here. + * The calling application also needs the READ_PHONE_STATE permission. + * The return value of the API is as follows. + *

    + *
  • return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER} if the caller + * and the device locked by the network are same
  • + *
  • return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED} if the caller and the + * device locked by the network are different
  • + *
  • return {@link #CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the device is + * not locked
  • + *
  • return {@link #CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device locking + * state is unavailable or radio does not supports the feature
  • + *
+ * + * @param executor The executor on which the result listener will be called. + * @param resultListener {@link Consumer} that will be called with the carrier restriction + * status result fetched from the radio + * @throws SecurityException if the caller does not have the required permission/privileges or + * if the caller is not pre-registered. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void getCarrierRestrictionStatus(@NonNull Executor executor, + @NonNull @CarrierRestrictionStatus + Consumer resultListener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(resultListener); + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(@CarrierRestrictionStatus int result) { + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(result))); + } + }; + try { + ITelephony service = getITelephony(); + if (service != null) { + service.getCarrierRestrictionStatus(internalCallback, getOpPackageName()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getCarrierRestrictionStatus: RemoteException = " + ex); + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Test API to verify carrier restriction status allow list i.e. + * packages/services/Telephony/assets/CarrierRestrictionOperatorDetails.json. + * + * @param pkgName : packaga name of the entry to verify + * @param carrierId : carrier Id of the entry + * @return {@code List} : list of registered shaIds + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public List getShaIdFromAllowList(String pkgName, int carrierId) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getShaIdFromAllowList(pkgName, carrierId); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getShaIdFromAllowList: RemoteException = " + ex); + throw ex.rethrowAsRuntimeException(); + } + return Collections.EMPTY_LIST; + } + + /** + * Used to enable or disable carrier data by the system based on carrier signalling or + * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to + * user settings, carrier data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param enabled control enable or disable carrier data. + * @see #resetAllCarrierActions() + * @deprecated use {@link #setDataEnabledForReason(int, boolean) with + * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setCarrierDataEnabled(boolean enabled) { + try { + setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling setDataEnabledForReason e:" + e); + } + } + + /** + * Carrier action to enable or disable the radio. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param enabled control enable or disable radio. + * @see #resetAllCarrierActions() + * + * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void setRadioEnabled(boolean enabled) { + if (enabled) { + clearRadioPowerOffForReason(RADIO_POWER_REASON_CARRIER); + } else { + requestRadioPowerOffForReason(RADIO_POWER_REASON_CARRIER); + } + } + + /** + * No error. Operation succeeded. + * @hide + */ + public static final int ENABLE_VONR_SUCCESS = 0; + + /** + * Radio is not available. + * @hide + */ + public static final int ENABLE_VONR_RADIO_NOT_AVAILABLE = 2; + + /** + * Internal Radio error. + * @hide + */ + public static final int ENABLE_VONR_RADIO_ERROR = 3; + + /** + * Voice over NR enable/disable request is received when system is in invalid state. + * @hide + */ + public static final int ENABLE_VONR_RADIO_INVALID_STATE = 4; + + /** + * Voice over NR enable/disable request is not supported. + * @hide + */ + public static final int ENABLE_VONR_REQUEST_NOT_SUPPORTED = 5; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"EnableVoNrResult"}, value = { + ENABLE_VONR_SUCCESS, + ENABLE_VONR_RADIO_NOT_AVAILABLE, + ENABLE_VONR_RADIO_ERROR, + ENABLE_VONR_RADIO_INVALID_STATE, + ENABLE_VONR_REQUEST_NOT_SUPPORTED}) + public @interface EnableVoNrResult {} + + /** + * Enable or disable Voice over NR (VoNR) + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param enabled enable or disable VoNR. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public @EnableVoNrResult int setVoNrEnabled(boolean enabled) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.setVoNrEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setVoNrEnabled", e); + } + + return ENABLE_VONR_RADIO_INVALID_STATE; + } + + /** + * Is Voice over NR (VoNR) enabled. + * @return true if Voice over NR (VoNR) is enabled else false. Enabled state does not mean + * voice call over NR is active or voice ove NR is available. It means the device is allowed to + * register IMS over NR. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isVoNrEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isVoNrEnabled(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isVoNrEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + /** + * Carrier action to start or stop reporting default network available events. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param report control start/stop reporting network status. + * @see #resetAllCarrierActions() + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void reportDefaultNetworkStatus(boolean report) { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.carrierActionReportDefaultNetworkStatus( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), report); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#carrierActionReportDefaultNetworkStatus", e); + } + } + + /** + * Reset all carrier actions previously set by {@link #setRadioEnabled}, + * {@link #reportDefaultNetworkStatus} and {@link #setCarrierDataEnabled}. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void resetAllCarrierActions() { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.carrierActionResetAll( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#carrierActionResetAll", e); + } + } + + /** + * Policy control of data connection. Usually used when data limit is passed. + * @param enabled True if enabling the data, otherwise disabling. + * @deprecated use {@link #setDataEnabledForReason(int, boolean) with + * reason {@link #DATA_ENABLED_REASON_POLICY}} instead. + * @hide + */ + @Deprecated + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setPolicyDataEnabled(boolean enabled) { + try { + setDataEnabledForReason(DATA_ENABLED_REASON_POLICY, enabled); + } catch (RuntimeException e) { + Log.e(TAG, "Error calling setDataEnabledForReason e:" + e); + } + } + + /** @hide */ + @IntDef({ + DATA_ENABLED_REASON_USER, + DATA_ENABLED_REASON_POLICY, + DATA_ENABLED_REASON_CARRIER, + DATA_ENABLED_REASON_THERMAL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DataEnabledReason{} + + /** @hide */ + @IntDef({ + DATA_ENABLED_REASON_UNKNOWN, + DATA_ENABLED_REASON_USER, + DATA_ENABLED_REASON_POLICY, + DATA_ENABLED_REASON_CARRIER, + DATA_ENABLED_REASON_THERMAL, + DATA_ENABLED_REASON_OVERRIDE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DataEnabledChangedReason{} + + /** + * To indicate that data was enabled or disabled due to an unknown reason. + * Note that this is not a valid reason for {@link #setDataEnabledForReason(int, boolean)} and + * is only used to indicate that data enabled was changed. + */ + public static final int DATA_ENABLED_REASON_UNKNOWN = -1; + + /** + * To indicate that user enabled or disabled data. + */ + public static final int DATA_ENABLED_REASON_USER = 0; + + /** + * To indicate that data control due to policy. Usually used when data limit is passed. + * Policy data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + */ + public static final int DATA_ENABLED_REASON_POLICY = 1; + + /** + * To indicate enable or disable carrier data by the system based on carrier signalling or + * carrier privileged apps. Carrier data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + */ + public static final int DATA_ENABLED_REASON_CARRIER = 2; + + /** + * To indicate enable or disable data by thermal service. + * Thermal data on/off won't affect user settings but will bypass the + * settings and turns off data internally if set to {@code false}. + */ + public static final int DATA_ENABLED_REASON_THERMAL = 3; + + /** + * To indicate data was enabled or disabled due to mobile data policy overrides. + * Note that this is not a valid reason for {@link #setDataEnabledForReason(int, boolean)} and + * is only used to indicate that data enabled was changed due to an override. + */ + public static final int DATA_ENABLED_REASON_OVERRIDE = 4; + + /** + * Control of data connection and provide the reason triggering the data connection control. + * This can be called for following reasons + *

    + *
  1. data limit is passed {@link #DATA_ENABLED_REASON_POLICY} + *
  2. data disabled by carrier {@link #DATA_ENABLED_REASON_CARRIER} + *
  3. data disabled by user {@link #DATA_ENABLED_REASON_USER} + *
  4. data disabled due to thermal {@link #DATA_ENABLED_REASON_THERMAL} + *
+ * If any of the reason is off, then it will result in + * bypassing user preference and result in data to be turned off. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies + * to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + * + * @param reason the reason the data enable change is taking place + * @param enabled True if enabling the data, otherwise disabling. + * + *

Requires Permission: + * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) if the reason is + * {@link #DATA_ENABLED_REASON_USER} or {@link #DATA_ENABLED_REASON_CARRIER} or the call app + * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of + * the reason. + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) { + setDataEnabledForReason(getSubId(), reason, enabled); + } + + private void setDataEnabledForReason(int subId, @DataEnabledReason int reason, + boolean enabled) { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setDataEnabledForReason(subId, reason, enabled, getOpPackageName()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#setDataEnabledForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Return whether data is enabled for certain reason . + * + * If {@link #isDataEnabledForReason} returns false, it means in data enablement for a + * specific reason is turned off. If any of the reason is off, then it will result in + * bypassing user preference and result in data to be turned off. Call + * {@link #isDataConnectionAllowed} in order to know whether + * data connection is allowed on the device. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies + * to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * @param reason the reason the data enable change is taking place + * @return whether data is enabled for a reason. + *

Requires Permission: + * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) or + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE} + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.MODIFY_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataEnabledForReason(@DataEnabledReason int reason) { + return isDataEnabledForReason(getSubId(), reason); + } + + private boolean isDataEnabledForReason(int subId, @DataEnabledReason int reason) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isDataEnabledForReason(subId, reason); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#isDataEnabledForReason RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + /** + * Get Client request stats which will contain statistical information + * on each request made by client. + * Callers require either READ_PRIVILEGED_PHONE_STATE or + * READ_PHONE_STATE to retrieve the information. + * @param subId sub id + * @return List of Client Request Stats + * @hide + */ + public List getClientRequestStats(int subId) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getClientRequestStats(getOpPackageName(), getAttributionTag(), + subId); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e); + } + + return null; + } + + /** + * Checks if phone is in emergency callback mode. + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @return true if phone is in emergency callback mode. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean getEmergencyCallbackMode() { + return getEmergencyCallbackMode(getSubId()); + } + + /** + * Check if phone is in emergency callback mode + * @return true if phone is in emergency callback mode + * @param subId the subscription ID that this action applies to. + * @hide + */ + public boolean getEmergencyCallbackMode(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + return false; + } + return telephony.getEmergencyCallbackMode(subId); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getEmergencyCallbackMode", e); + } + return false; + } + + /** + * Checks if manual network selection is allowed. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link #hasCarrierPrivileges}) + * + *

If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @return {@code true} if manual network selection is allowed, otherwise return {@code false}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // No support carrier privileges (b/72967236). + @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isManualNetworkSelectionAllowed() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isManualNetworkSelectionAllowed(getSubId()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#isManualNetworkSelectionAllowed", e); + } + return true; + } + + /** + * Get the most recently available signal strength information. + * + * Get the most recent SignalStrength information reported by the modem. Due + * to power saving this information may not always be current. + * @return the most recent cached signal strength info from the modem + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @Nullable + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public SignalStrength getSignalStrength() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSignalStrength(getSubId()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getSignalStrength", e); + } + return null; + } + + /** + * Checks whether cellular data connection is allowed in the device. + * + *

Whether cellular data connection is allowed considers all factors below: + *

    + *
  • User turned on data setting {@link #isDataEnabled}.
  • + *
  • Carrier allows data to be on.
  • + *
  • Network policy.
  • + *
  • And possibly others.
  • + *
+ * @return {@code true} if the overall data connection is allowed; {@code false} if not. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataConnectionAllowed() { + boolean retVal = false; + try { + int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId()); + ITelephony telephony = getITelephony(); + if (telephony != null) + retVal = telephony.isDataEnabled(subId); + } catch (RemoteException e) { + Log.e(TAG, "Error isDataConnectionAllowed", e); + } + return retVal; + } + + /** + * @return true if the current device is "data capable" over a radio on the device. + *

+ * "Data capable" means that this device supports packet-switched + * data connections over the telephony network. + *

+ */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataCapable() { + if (mContext == null) return true; + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_mobile_data_capable); + } + + /** + * The indication for signal strength update. + * @hide + */ + public static final int INDICATION_FILTER_SIGNAL_STRENGTH = 0x1; + + /** + * The indication for full network state update. + * @hide + */ + public static final int INDICATION_FILTER_FULL_NETWORK_STATE = 0x2; + + /** + * The indication for data call dormancy changed update. + * @hide + */ + public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED = 0x4; + + /** + * The indication for link capacity estimate update. + * @hide + */ + public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE = 0x8; + + /** + * The indication for physical channel config update. + * @hide + */ + public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG = 0x10; + + /** + * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2, + * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config + * (also any country or carrier overlays) to be loaded when using a test SIM with a call box. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * + * @deprecated + * @hide + */ + @Deprecated + @TestApi + public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1, + String gid2, String plmn, String spn) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCarrierTestOverride( + getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn, + null, null); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + } + + /** + * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2, + * plmn, spn, apn and carrier priviledge. This would be handy for, eg, forcing a particular + * carrier id, carrier's config (also any country or carrier overlays) to be loaded when using + * a test SIM with a call box. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @hide + */ + @TestApi + public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1, + String gid2, String plmn, String spn, + String carrierPriviledgeRules, String apn) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCarrierTestOverride( + getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn, + carrierPriviledgeRules, apn); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + } + + /** + * A test API to return installed carrier id list version + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @hide + */ + @UnsupportedAppUsage + @TestApi + public int getCarrierIdListVersion() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCarrierIdListVersion(getSubId()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return UNKNOWN_CARRIER_ID_LIST_VERSION; + } + + /** + * How many modems can have simultaneous data connections. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public int getNumberOfModemsWithSimultaneousDataConnections() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getNumberOfModemsWithSimultaneousDataConnections( + getSubId(), getOpPackageName(), getAttributionTag()); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return 0; + } + + /** + * Enable or disable OpportunisticNetworkService. + * + * This method should be called to enable or disable + * OpportunisticNetwork service on the device. + * + *

+ * Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @param enable enable(True) or disable(False) + * @return returns true if successfully set. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean setOpportunisticNetworkState(boolean enable) { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + boolean ret = false; + try { + IOns iOpportunisticNetworkService = getIOns(); + if (iOpportunisticNetworkService != null) { + ret = iOpportunisticNetworkService.setEnable(enable, pkgForDebug); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "enableOpportunisticNetwork RemoteException", ex); + } + + return ret; + } + + /** + * is OpportunisticNetworkService enabled + * + * This method should be called to determine if the OpportunisticNetworkService is + * enabled + * + *

+ * Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isOpportunisticNetworkEnabled() { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + boolean isEnabled = false; + + try { + IOns iOpportunisticNetworkService = getIOns(); + if (iOpportunisticNetworkService != null) { + isEnabled = iOpportunisticNetworkService.isEnabled(pkgForDebug); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "enableOpportunisticNetwork RemoteException", ex); + } + + return isEnabled; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @LongDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"}, + value = {NETWORK_TYPE_BITMASK_UNKNOWN, + NETWORK_TYPE_BITMASK_GSM, + NETWORK_TYPE_BITMASK_GPRS, + NETWORK_TYPE_BITMASK_EDGE, + NETWORK_TYPE_BITMASK_CDMA, + NETWORK_TYPE_BITMASK_1xRTT, + NETWORK_TYPE_BITMASK_EVDO_0, + NETWORK_TYPE_BITMASK_EVDO_A, + NETWORK_TYPE_BITMASK_EVDO_B, + NETWORK_TYPE_BITMASK_EHRPD, + NETWORK_TYPE_BITMASK_HSUPA, + NETWORK_TYPE_BITMASK_HSDPA, + NETWORK_TYPE_BITMASK_HSPA, + NETWORK_TYPE_BITMASK_HSPAP, + NETWORK_TYPE_BITMASK_UMTS, + NETWORK_TYPE_BITMASK_TD_SCDMA, + NETWORK_TYPE_BITMASK_LTE, + NETWORK_TYPE_BITMASK_LTE_CA, + NETWORK_TYPE_BITMASK_NR, + NETWORK_TYPE_BITMASK_IWLAN, + NETWORK_TYPE_BITMASK_IDEN + }) + public @interface NetworkTypeBitMask {} + + // 2G + /** + * network type bitmask unknown. + */ + public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; + /** + * network type bitmask indicating the support of radio tech GSM. + */ + public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1)); + /** + * network type bitmask indicating the support of radio tech GPRS. + */ + public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1)); + /** + * network type bitmask indicating the support of radio tech EDGE. + */ + public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1)); + /** + * network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B). + */ + public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1)); + /** + * network type bitmask indicating the support of radio tech 1xRTT. + */ + @SuppressLint("AllUpper") + public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1)); + // 3G + /** + * network type bitmask indicating the support of radio tech EVDO 0. + */ + public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1)); + /** + * network type bitmask indicating the support of radio tech EVDO A. + */ + public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1)); + /** + * network type bitmask indicating the support of radio tech EVDO B. + */ + public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1)); + /** + * network type bitmask indicating the support of radio tech EHRPD. + */ + public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1)); + /** + * network type bitmask indicating the support of radio tech HSUPA. + */ + public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1)); + /** + * network type bitmask indicating the support of radio tech HSDPA. + */ + public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1)); + /** + * network type bitmask indicating the support of radio tech HSPA. + */ + public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1)); + /** + * network type bitmask indicating the support of radio tech iDen. + * @hide + */ + public static final long NETWORK_TYPE_BITMASK_IDEN = (1 << (NETWORK_TYPE_IDEN - 1)); + /** + * network type bitmask indicating the support of radio tech HSPAP. + */ + public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1)); + /** + * network type bitmask indicating the support of radio tech UMTS. + */ + public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1)); + /** + * network type bitmask indicating the support of radio tech TD_SCDMA. + */ + public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1)); + // 4G + /** + * network type bitmask indicating the support of radio tech LTE. + */ + public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1)); + /** + * NOT USED; this bitmask is exposed accidentally. + * If used, will be converted to {@link #NETWORK_TYPE_BITMASK_LTE}. + * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation). + * + * @deprecated Please use {@link #NETWORK_TYPE_BITMASK_LTE} instead. Deprecated in Android U. + */ + @Deprecated + public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1)); + + /** + * network type bitmask indicating the support of radio tech NR(New Radio) 5G. + */ + public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1)); + + /** + * network type bitmask indicating the support of radio tech IWLAN. + */ + public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1)); + + /** @hide */ + public static final long NETWORK_CLASS_BITMASK_2G = NETWORK_TYPE_BITMASK_GSM + | NETWORK_TYPE_BITMASK_GPRS + | NETWORK_TYPE_BITMASK_EDGE + | NETWORK_TYPE_BITMASK_CDMA + | NETWORK_TYPE_BITMASK_1xRTT; + + /** @hide */ + public static final long NETWORK_CLASS_BITMASK_3G = NETWORK_TYPE_BITMASK_EVDO_0 + | NETWORK_TYPE_BITMASK_EVDO_A + | NETWORK_TYPE_BITMASK_EVDO_B + | NETWORK_TYPE_BITMASK_EHRPD + | NETWORK_TYPE_BITMASK_HSUPA + | NETWORK_TYPE_BITMASK_HSDPA + | NETWORK_TYPE_BITMASK_HSPA + | NETWORK_TYPE_BITMASK_HSPAP + | NETWORK_TYPE_BITMASK_UMTS + | NETWORK_TYPE_BITMASK_TD_SCDMA; + + /** @hide */ + public static final long NETWORK_CLASS_BITMASK_4G = NETWORK_TYPE_BITMASK_LTE + | NETWORK_TYPE_BITMASK_LTE_CA + | NETWORK_TYPE_BITMASK_IWLAN; + + /** @hide */ + public static final long NETWORK_CLASS_BITMASK_5G = NETWORK_TYPE_BITMASK_NR; + + /** @hide */ + public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP = NETWORK_TYPE_BITMASK_GSM + | NETWORK_TYPE_BITMASK_GPRS + | NETWORK_TYPE_BITMASK_EDGE + | NETWORK_TYPE_BITMASK_HSUPA + | NETWORK_TYPE_BITMASK_HSDPA + | NETWORK_TYPE_BITMASK_HSPA + | NETWORK_TYPE_BITMASK_HSPAP + | NETWORK_TYPE_BITMASK_UMTS + | NETWORK_TYPE_BITMASK_TD_SCDMA + | NETWORK_TYPE_BITMASK_LTE + | NETWORK_TYPE_BITMASK_LTE_CA + | NETWORK_TYPE_BITMASK_NR; + + /** @hide */ + public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2 = NETWORK_TYPE_BITMASK_CDMA + | NETWORK_TYPE_BITMASK_1xRTT + | NETWORK_TYPE_BITMASK_EVDO_0 + | NETWORK_TYPE_BITMASK_EVDO_A + | NETWORK_TYPE_BITMASK_EVDO_B + | NETWORK_TYPE_BITMASK_EHRPD; + + /** + * @return Modem supported radio access family bitmask + * + *

Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws SecurityException if the caller does not have the required permission + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NetworkTypeBitMask long getSupportedRadioAccessFamily() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName()); + } else { + // This can happen when the ITelephony interface is not up yet. + return NETWORK_TYPE_BITMASK_UNKNOWN; + } + } catch (RemoteException ex) { + // This shouldn't happen in the normal case + return NETWORK_TYPE_BITMASK_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return NETWORK_TYPE_BITMASK_UNKNOWN; + } + } + + /** + * Indicates Emergency number database version is invalid. + * + * @hide + */ + @SystemApi + public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; + + /** + * Notify Telephony for OTA emergency number database installation complete. + * + *

Requires permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + @SystemApi + public void notifyOtaEmergencyNumberDbInstalled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.notifyOtaEmergencyNumberDbInstalled(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "notifyOtaEmergencyNumberDatabaseInstalled RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + } + + /** + * Override the file path for OTA emergency number database in a file partition. + * + * @param otaParcelFileDescriptor parcelable file descriptor for OTA emergency number database. + * + *

Requires permission: + * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + @SystemApi + public void updateOtaEmergencyNumberDbFilePath( + @NonNull ParcelFileDescriptor otaParcelFileDescriptor) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "updateOtaEmergencyNumberDbFilePath RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + } + + /** + * Reset the file path to default for OTA emergency number database in a file partition. + * + *

Requires permission: + * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + @SystemApi + public void resetOtaEmergencyNumberDbFilePath() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.resetOtaEmergencyNumberDbFilePath(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "resetOtaEmergencyNumberDbFilePath RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + } + + /** + * Returns whether {@link TelephonyManager#ACTION_EMERGENCY_ASSISTANCE emergency assistance} is + * available on the device. + *

+ * Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} + * + * @return {@code true} if emergency assistance is available, {@code false} otherwise + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck") + @SystemApi + public boolean isEmergencyAssistanceEnabled() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + "isEmergencyAssistanceEnabled"); + return EMERGENCY_ASSISTANCE_ENABLED; + } + + /** + * Get the emergency assistance package name. + * + * @return the package name of the emergency assistance app. + * @throws IllegalStateException if emergency assistance is not enabled or the device is + * not voice capable. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @FlaggedApi(android.permission.flags.Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED) + @NonNull + @SystemApi + public String getEmergencyAssistancePackageName() { + if (!isEmergencyAssistanceEnabled() || !isVoiceCapable()) { + throw new IllegalStateException("isEmergencyAssistanceEnabled() is false or device" + + " not voice capable."); + } + String emergencyRole = mContext.getSystemService(RoleManager.class) + .getEmergencyRoleHolder(mContext.getUserId()); + return Objects.requireNonNull(emergencyRole, "Emergency role holder must not be null"); + } + + /** + * Get the emergency number list based on current locale, sim, default, modem and network. + * + *

In each returned list, the emergency number {@link EmergencyNumber} coming from higher + * priority sources will be located at the smaller index; the priority order of sources are: + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG} + * + *

The subscriptions which the returned list would be based on, are all the active + * subscriptions, no matter which subscription could be used to create TelephonyManager. + * + *

Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return Map including the keys as the active subscription IDs (Note: if there is no active + * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value + * as the list of {@link EmergencyNumber}; empty Map if this information is not available; + * or throw a SecurityException if the caller does not have the permission. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @NonNull + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public Map> getEmergencyNumberList() { + Map> emergencyNumberList = new HashMap<>(); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getEmergencyNumberList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "getEmergencyNumberList RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + return emergencyNumberList; + } + + /** + * Get the per-category emergency number list based on current locale, sim, default, modem + * and network. + * + *

In each returned list, the emergency number {@link EmergencyNumber} coming from higher + * priority sources will be located at the smaller index; the priority order of sources are: + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG} + * + *

The subscriptions which the returned list would be based on, are all the active + * subscriptions, no matter which subscription could be used to create TelephonyManager. + * + *

Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param categories the emergency service categories which are the bitwise-OR combination of + * the following constants: + *

    + *
  1. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}
  2. + *
  3. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE}
  4. + *
  5. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE}
  6. + *
  7. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE}
  8. + *
  9. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD}
  10. + *
  11. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE}
  12. + *
  13. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC}
  14. + *
  15. {@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC}
  16. + *
+ * @return Map including the keys as the active subscription IDs (Note: if there is no active + * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value + * as the list of {@link EmergencyNumber}; empty Map if this information is not available; + * or throw a SecurityException if the caller does not have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @NonNull + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public Map> getEmergencyNumberList( + @EmergencyServiceCategories int categories) { + Map> emergencyNumberListForCategories = new HashMap<>(); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + Map> emergencyNumberList = + telephony.getEmergencyNumberList(mContext.getOpPackageName(), + mContext.getAttributionTag()); + emergencyNumberListForCategories = + filterEmergencyNumbersByCategories(emergencyNumberList, categories); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + return emergencyNumberListForCategories; + } + + /** + * Filter emergency numbers with categories. + * + * @hide + */ + @VisibleForTesting + public Map> filterEmergencyNumbersByCategories( + Map> emergencyNumberList, + @EmergencyServiceCategories int categories) { + Map> emergencyNumberListForCategories = new HashMap<>(); + if (emergencyNumberList != null) { + for (Integer subscriptionId : emergencyNumberList.keySet()) { + List allNumbersForSub = emergencyNumberList.get( + subscriptionId); + List numbersForCategoriesPerSub = new ArrayList<>(); + for (EmergencyNumber number : allNumbersForSub) { + if (number.isInEmergencyServiceCategories(categories)) { + numbersForCategoriesPerSub.add(number); + } + } + emergencyNumberListForCategories.put( + subscriptionId, numbersForCategoriesPerSub); + } + } + return emergencyNumberListForCategories; + } + + /** + * Identifies if the supplied phone number is an emergency number that matches a known + * emergency number based on current locale, SIM card(s), Android database, modem, network, + * or defaults. + * + *

This method assumes that only dialable phone numbers are passed in; non-dialable + * numbers are not considered emergency numbers. A dialable phone number consists only + * of characters/digits identified by {@link PhoneNumberUtils#isDialable(char)}. + * + *

The subscriptions which the identification would be based on, are all the active + * subscriptions, no matter which subscription could be used to create TelephonyManager. + * + * @param number - the number to look up + * @return {@code true} if the given number is an emergency number based on current locale, + * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise. + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean isEmergencyNumber(@NonNull String number) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isEmergencyNumber(number, true); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "isEmergencyNumber RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Checks if the supplied number is an emergency number based on current locale, sim, default, + * modem and network. + * + *

Specifically, this method will return {@code true} if the specified number is an + * emergency number, *or* if the number simply starts with the same digits as any current + * emergency number. + * + *

The subscriptions which the identification would be based on, are all the active + * subscriptions, no matter which subscription could be used to create TelephonyManager. + * + *

Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param number - the number to look up + * @return {@code true} if the given number is an emergency number or it simply starts with + * the same digits of any current emergency number based on current locale, sim, modem and + * network; {@code false} if it is not; or throw an SecurityException if the caller does not + * have the required permission/privileges + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * + * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead. + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean isPotentialEmergencyNumber(@NonNull String number) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isEmergencyNumber(number, false); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Log.e(TAG, "isEmergencyNumber RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Returns the emergency number database version. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public int getEmergencyNumberDbVersion() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getEmergencyNumberDbVersion(getSubId()); + } + } catch (RemoteException ex) { + Log.e(TAG, "getEmergencyNumberDbVersion RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + return INVALID_EMERGENCY_NUMBER_DB_VERSION; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_OPPORTUNISTIC_SUB"}, value = { + SET_OPPORTUNISTIC_SUB_SUCCESS, + SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED, + SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION, + SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE, + SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION}) + public @interface SetOpportunisticSubscriptionResult {} + + /** + * No error. Operation succeeded. + */ + public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0; + + /** + * Validation failed when trying to switch to preferred subscription. + */ + public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1; + + /** + * The subscription is not valid. It must be an active opportunistic subscription. + */ + public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; + + /** + * The subscription is not valid. It must be an opportunistic subscription. + */ + public static final int SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE = 3; + + /** + * Subscription service happened remote exception. + */ + public static final int SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION = 4; + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"UPDATE_AVAILABLE_NETWORKS"}, value = { + UPDATE_AVAILABLE_NETWORKS_SUCCESS, + UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE, + UPDATE_AVAILABLE_NETWORKS_ABORTED, + UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS, + UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE, + UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL, + UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL, + UPDATE_AVAILABLE_NETWORKS_MULTIPLE_NETWORKS_NOT_SUPPORTED, + UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE, + UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION, + UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED, + UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE}) + public @interface UpdateAvailableNetworksResult {} + + /** + * No error. Operation succeeded. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_SUCCESS = 0; + + /** + * There is a unknown failure happened. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE = 1; + + /** + * The request is aborted. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_ABORTED = 2; + + /** + * The parameter passed in is invalid. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS = 3; + + /** + * No carrier privilege. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE = 4; + + /** + * Disable modem fail. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL = 5; + + /** + * Enable modem fail. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL = 6; + + /** + * Carrier app does not support multiple available networks. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_MULTIPLE_NETWORKS_NOT_SUPPORTED = 7; + + /** + * The subscription is not valid. It must be an opportunistic subscription. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE = 8; + + /** + * There is no OpportunisticNetworkService. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION = 9; + + /** + * OpportunisticNetworkService is disabled. + */ + public static final int UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED = 10; + + /** + * SIM port is not available to switch to opportunistic subscription. + * @hide + */ + public static final int UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE = 11; + + /** + * Set preferred opportunistic data subscription id. + * + * Switch internet data to preferred opportunistic data subscription id. This api + * can result in lose of internet connectivity for short period of time while internet data + * is handed over. + *

Requires that the calling app has carrier privileges on both primary and + * secondary subscriptions (see + * {@link #hasCarrierPrivileges}), or has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param subId which opportunistic subscription + * {@link SubscriptionManager#getOpportunisticSubscriptions} is preferred for cellular data. + * Pass {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} to unset the preference + * @param needValidation whether validation is needed before switch happens. + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * See the {@code SET_OPPORTUNISTIC_SUB_*} constants + * for more details. Pass null if don't care about the result. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation, + @Nullable @CallbackExecutor Executor executor, @Nullable Consumer callback) { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + try { + IOns iOpportunisticNetworkService = getIOns(); + if (iOpportunisticNetworkService == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Opportunistic Network Service is null"); + } else { + // Let the general remote exception handling catch this. + throw new RemoteException("Null Opportunistic Network Service!"); + } + } + ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { + @Override + public void onComplete(int result) { + if (executor == null || callback == null) { + return; + } + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + callback.accept(result); + }); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + + iOpportunisticNetworkService + .setPreferredDataSubscriptionId(subId, needValidation, callbackStub, + pkgForDebug); + } catch (RemoteException ex) { + Rlog.e(TAG, "setPreferredOpportunisticDataSubscription RemoteException", ex); + if (executor == null || callback == null) { + return; + } + runOnBackgroundThread(() -> executor.execute(() -> { + if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { + callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); + } else { + callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); + } + })); + } + } + + /** + * Get preferred opportunistic data subscription Id + * + *

Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}), + * or has either READ_PRIVILEGED_PHONE_STATE + * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission. + * @return subId preferred opportunistic subscription id or + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred + * subscription id + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public int getPreferredOpportunisticDataSubscription() { + String packageName = mContext != null ? mContext.getOpPackageName() : ""; + String attributionTag = mContext != null ? mContext.getAttributionTag() : null; + int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + try { + IOns iOpportunisticNetworkService = getIOns(); + if (iOpportunisticNetworkService != null) { + subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId( + packageName, attributionTag); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex); + } + return subId; + } + + /** + * Update availability of a list of networks in the current location. + * + * This api should be called to inform OpportunisticNetwork Service about the availability + * of a network at the current location. This information will be used by OpportunisticNetwork + * service to enable modem stack and to attach to the network. If an empty list is passed, + * it is assumed that no network is available and will result in disabling the modem stack + * to save power. This api do not switch internet data once network attach is completed. + * Use {@link TelephonyManager#setPreferredOpportunisticDataSubscription} + * to switch internet data after network attach is complete. + * Requires that the calling app has carrier privileges on both primary and + * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * @param availableNetworks is a list of available network information. + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void updateAvailableNetworks(@NonNull List availableNetworks, + @Nullable @CallbackExecutor Executor executor, + @UpdateAvailableNetworksResult @Nullable Consumer callback) { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + Objects.requireNonNull(availableNetworks, "availableNetworks must not be null."); + try { + IOns iOpportunisticNetworkService = getIOns(); + if (iOpportunisticNetworkService == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Opportunistic Network Service is null"); + } else { + // Let the general remote exception handling catch this. + throw new RemoteException("Null Opportunistic Network Service!"); + } + } + + IUpdateAvailableNetworksCallback callbackStub = + new IUpdateAvailableNetworksCallback.Stub() { + @Override + public void onComplete(int result) { + if (executor == null || callback == null) { + return; + } + Binder.withCleanCallingIdentity(() -> { + executor.execute(() -> callback.accept(result)); + }); + } + }; + iOpportunisticNetworkService + .updateAvailableNetworks(availableNetworks, callbackStub, pkgForDebug); + } catch (RemoteException ex) { + Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex); + if (executor == null || callback == null) { + return; + } + runOnBackgroundThread(() -> executor.execute(() -> { + if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { + callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION); + } else { + callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); + } + })); + } + } + + /** + * Enable or disable a logical modem stack. When a logical modem is disabled, the corresponding + * SIM will still be visible to the user but its mapping modem will not have any radio activity. + * For example, we will disable a modem when user or system believes the corresponding SIM + * is temporarily not needed (e.g. out of coverage), and will enable it back on when needed. + * + * Requires that the calling app has permission + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * @param slotIndex which corresponding modem will operate on. + * @param enable whether to enable or disable the modem stack. + * @return whether the operation is successful. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public boolean enableModemForSlot(int slotIndex, boolean enable) { + boolean ret = false; + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + ret = telephony.enableModemForSlot(slotIndex, enable); + } + } catch (RemoteException ex) { + Log.e(TAG, "enableModem RemoteException", ex); + } + return ret; + } + + /** + * Indicates whether or not there is a modem stack enabled for the given SIM slot. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, + * READ_PRIVILEGED_PHONE_STATE or that the calling app has carrier privileges (see + * {@link #hasCarrierPrivileges()}). + * + * @param slotIndex which slot it's checking. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY) + public boolean isModemEnabledForSlot(int slotIndex) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + } catch (RemoteException ex) { + Log.e(TAG, "enableModem RemoteException", ex); + } + return false; + } + + /** + * Broadcast intent action for network country code changes. + * + *

+ * The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current + * network returned by {@link #getNetworkCountryIso()}. + * + *

There may be a delay of several minutes before reporting that no country is detected. + * + * @see #EXTRA_NETWORK_COUNTRY + * @see #getNetworkCountryIso() + */ + public static final String ACTION_NETWORK_COUNTRY_CHANGED = + "android.telephony.action.NETWORK_COUNTRY_CHANGED"; + + /** + * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the + * the country code in ISO-3166-1 alpha-2 format. This is the same country code returned by + * {@link #getNetworkCountryIso()}. This might be an empty string when the country code is not + * available. + * + *

+ * Retrieve with {@link android.content.Intent#getStringExtra(String)}. + */ + public static final String EXTRA_NETWORK_COUNTRY = + "android.telephony.extra.NETWORK_COUNTRY"; + + /** + * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the + * last known the country code in ISO-3166-1 alpha-2 format. This might be an empty string when + * the country code was never available. The last known country code persists across reboot. + * + *

+ * Retrieve with {@link android.content.Intent#getStringExtra(String)}. + */ + public static final String EXTRA_LAST_KNOWN_NETWORK_COUNTRY = + "android.telephony.extra.LAST_KNOWN_NETWORK_COUNTRY"; + + /** + * Indicate if the user is allowed to use multiple SIM cards at the same time to register + * on the network (e.g. Dual Standby or Dual Active) when the device supports it, or if the + * usage is restricted. This API is used to prevent usage of multiple SIM card, based on + * policies of the carrier. + *

Note: the API does not prevent access to the SIM cards for operations that don't require + * access to the network. + * + * @param isMultiSimCarrierRestricted true if usage of multiple SIMs is restricted, false + * otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK) + public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setMultiSimCarrierRestriction(isMultiSimCarrierRestricted); + } + } catch (RemoteException e) { + Log.e(TAG, "setMultiSimCarrierRestriction RemoteException", e); + } + } + + /** + * The usage of multiple SIM cards at the same time to register on the network (e.g. Dual + * Standby or Dual Active) is supported. + */ + public static final int MULTISIM_ALLOWED = 0; + + /** + * The usage of multiple SIM cards at the same time to register on the network (e.g. Dual + * Standby or Dual Active) is not supported by the hardware. + */ + public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; + + /** + * The usage of multiple SIM cards at the same time to register on the network (e.g. Dual + * Standby or Dual Active) is supported by the hardware, but restricted by the carrier. + */ + public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"MULTISIM_"}, + value = { + MULTISIM_ALLOWED, + MULTISIM_NOT_SUPPORTED_BY_HARDWARE, + MULTISIM_NOT_SUPPORTED_BY_CARRIER + }) + public @interface IsMultiSimSupportedResult {} + + /** + * Returns if the usage of multiple SIM cards at the same time to register on the network + * (e.g. Dual Standby or Dual Active) is supported by the device and by the carrier. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return {@link #MULTISIM_ALLOWED} if the device supports multiple SIMs. + * {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs. + * {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the + * functionality is restricted by the carrier. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @IsMultiSimSupportedResult + public int isMultiSimSupported() { + if (getSupportedModemCount() < 2) { + return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE; + } + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isMultiSimSupported(getOpPackageName(), getAttributionTag()); + } + } catch (RemoteException e) { + Log.e(TAG, "isMultiSimSupported RemoteException", e); + } + return MULTISIM_NOT_SUPPORTED_BY_HARDWARE; + } + + /** + * Switch configs to enable multi-sim or switch back to single-sim + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * Note: with only carrier privileges, it is not allowed to switch from multi-sim + * to single-sim + * + * @param numOfSims number of live SIMs we want to switch to + * @throws android.os.RemoteException + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void switchMultiSimConfig(int numOfSims) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.switchMultiSimConfig(numOfSims); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "switchMultiSimConfig RemoteException", ex); + } + } + + /** + * Get whether making changes to modem configurations by {@link #switchMultiSimConfig(int)} will + * trigger device reboot. + * The modem configuration change refers to switching from single SIM configuration to DSDS + * or the other way around. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the + * calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return {@code true} if reboot will be triggered after making changes to modem + * configurations, otherwise return {@code false}. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean doesSwitchMultiSimConfigTriggerReboot() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(), + getOpPackageName(), getAttributionTag()); + } + } catch (RemoteException e) { + Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e); + } + return false; + } + + /** + * Retrieve the Radio HAL Version for this device. + * + * Get the HAL version for the IRadio interface for test purposes. + * + * @return a Pair of (major version, minor version) or (-1,-1) if unknown. + * + * @hide + * + * @deprecated Use {@link #getHalVersion} instead. + */ + @Deprecated + @UnsupportedAppUsage + @TestApi + public Pair getRadioHalVersion() { + return getHalVersion(HAL_SERVICE_RADIO); + } + + /** @hide */ + public static final int HAL_SERVICE_RADIO = 0; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioData + * {@link RadioDataProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_DATA = 1; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioMessaging + * {@link RadioMessagingProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_MESSAGING = 2; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioModem + * {@link RadioModemProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_MODEM = 3; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioNetwork + * {@link RadioNetworkProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_NETWORK = 4; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioSim + * {@link RadioSimProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_SIM = 5; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioVoice + * {@link RadioVoiceProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_VOICE = 6; + + /** + * HAL service type that supports the HAL APIs implementation of IRadioIms + * {@link RadioImsProxy} + * @hide + */ + @TestApi + public static final int HAL_SERVICE_IMS = 7; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"HAL_SERVICE_"}, + value = { + HAL_SERVICE_RADIO, + HAL_SERVICE_DATA, + HAL_SERVICE_MESSAGING, + HAL_SERVICE_MODEM, + HAL_SERVICE_NETWORK, + HAL_SERVICE_SIM, + HAL_SERVICE_VOICE, + HAL_SERVICE_IMS, + }) + public @interface HalService {} + + /** + * The HAL Version indicating that the version is unknown or invalid. + * @hide + */ + @TestApi + public static final Pair HAL_VERSION_UNKNOWN = new Pair(-1, -1); + + /** + * The HAL Version indicating that the version is unsupported. + * @hide + */ + @TestApi + public static final Pair HAL_VERSION_UNSUPPORTED = new Pair(-2, -2); + + /** + * Retrieve the HAL Version of a specific service for this device. + * + * Get the HAL version for a specific HAL interface for test purposes. + * + * @param halService the service id to query. + * @return a Pair of (major version, minor version), HAL_VERSION_UNKNOWN if unknown + * or HAL_VERSION_UNSUPPORTED if unsupported. + * + * @hide + */ + @TestApi + public @NonNull Pair getHalVersion(@HalService int halService) { + try { + ITelephony service = getITelephony(); + if (service != null) { + int version = service.getHalVersion(halService); + if (version != -1) { + return new Pair(version / 100, version % 100); + } + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "getHalVersion() RemoteException", e); + e.rethrowAsRuntimeException(); + } + return HAL_VERSION_UNKNOWN; + } + + /** + * Get the calling application status about carrier privileges for the subscription created + * in TelephonyManager. Used by Telephony Module for permission checking. + * + * @param uid Uid to check. + * @return any value of {@link #CARRIER_PRIVILEGE_STATUS_HAS_ACCESS}, + * {@link #CARRIER_PRIVILEGE_STATUS_NO_ACCESS}, + * {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or + * {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES} + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @CarrierPrivilegeStatus int getCarrierPrivilegeStatus(int uid) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCarrierPrivilegeStatusForUid(getSubId(), uid); + } + } catch (RemoteException ex) { + Log.e(TAG, "getCarrierPrivilegeStatus RemoteException", ex); + } + return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; + } + + /** + * Returns a list of APNs set as overrides by the device policy manager via + * {@link #addDevicePolicyOverrideApn}. + * This method must only be called from the system or phone processes. + * + * @param context Context to use. + * @return {@link List} of APNs that have been set as overrides. + * @throws {@link SecurityException} if the caller is not the system or phone process. + * @hide + */ + @TestApi + // TODO: add new permission tag indicating that this is system-only. + public @NonNull List getDevicePolicyOverrideApns(@NonNull Context context) { + try (Cursor cursor = context.getContentResolver().query(DPC_URI, null, null, null, null)) { + if (cursor == null) { + return Collections.emptyList(); + } + List apnList = new ArrayList(); + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + ApnSetting apn = ApnSetting.makeApnSetting(cursor); + apnList.add(apn); + } + return apnList; + } + } + + /** + * Used by the device policy manager to add a new override APN. + * This method must only be called from the system or phone processes. + * + * @param context Context to use. + * @param apnSetting The {@link ApnSetting} describing the new APN. + * @return An integer, corresponding to a primary key in a database, that allows the caller to + * modify the APN in the future via {@link #modifyDevicePolicyOverrideApn}, or + * {@link android.provider.Telephony.Carriers.INVALID_APN_ID} if the override operation + * failed. + * @throws {@link SecurityException} if the caller is not the system or phone process. + * @hide + */ + @TestApi + // TODO: add new permission tag indicating that this is system-only. + public int addDevicePolicyOverrideApn(@NonNull Context context, + @NonNull ApnSetting apnSetting) { + Uri resultUri = context.getContentResolver().insert(DPC_URI, apnSetting.toContentValues()); + + int resultId = INVALID_APN_ID; + if (resultUri != null) { + try { + resultId = Integer.parseInt(resultUri.getLastPathSegment()); + } catch (NumberFormatException e) { + Rlog.e(TAG, "Failed to parse inserted override APN id: " + + resultUri.getLastPathSegment()); + } + } + return resultId; + } + + /** + * Used by the device policy manager to modify an override APN. + * This method must only be called from the system or phone processes. + * + * @param context Context to use. + * @param apnId The integer key of the APN to modify, as returned by + * {@link #addDevicePolicyOverrideApn} + * @param apnSetting The {@link ApnSetting} describing the updated APN. + * @return {@code true} if successful, {@code false} otherwise. + * @throws {@link SecurityException} if the caller is not the system or phone process. + * @hide + */ + @TestApi + // TODO: add new permission tag indicating that this is system-only. + public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId, + @NonNull ApnSetting apnSetting) { + return context.getContentResolver().update( + Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)), + apnSetting.toContentValues(), null, null) > 0; + } + + /** + * Return whether data is enabled for certain APN type. This will tell if framework will accept + * corresponding network requests on a subId. + * + * {@link #isDataEnabled()} is directly associated with users' Mobile data toggle on / off. If + * {@link #isDataEnabled()} returns false, it means in general all meter-ed data are disabled. + * + * This per APN type API gives a better idea whether data is allowed on a specific APN type. + * It will return true if: + * + * 1) User data is turned on, or + * 2) APN is un-metered for this subscription, or + * 3) APN type is allowlisted. E.g. MMS is allowlisted if + * {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled. + * + * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}. + * @return whether data is enabled for a apn type. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isDataEnabledForApn(@ApnType int apnType) { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : ""; + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isDataEnabledForApn(apnType, getSubId(), pkgForDebug); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#isDataEnabledForApn RemoteException" + ex); + } + return false; + } + + /** + * Whether an APN type is metered or not. It will be evaluated with the subId associated + * with the TelephonyManager instance. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isApnMetered(@ApnType int apnType) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isApnMetered(apnType, getSubId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#isApnMetered RemoteException" + ex); + } + return true; + } + + /** + * Specify which bands modem's background scan must act on. + * If {@code specifiers} is non-empty, the scan will be restricted to the bands specified. + * Otherwise, it scans all bands. + * + * For example, CBRS is only on LTE band 48. By specifying this band, + * modem saves more power. + * + * @param specifiers which bands to scan. + * @param executor The executor to execute the callback on + * @param callback The callback that gets invoked when the radio responds to the request. Called + * with {@code true} if the request succeeded, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void setSystemSelectionChannels(@NonNull List specifiers, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer callback) { + Objects.requireNonNull(specifiers, "Specifiers must not be null."); + Objects.requireNonNull(executor, "Executor must not be null."); + Objects.requireNonNull(callback, "Callback must not be null."); + setSystemSelectionChannelsInternal(specifiers, executor, callback); + } + + /** + * Same as {@link #setSystemSelectionChannels(List, Executor, Consumer)}, but to be + * used when the caller does not need feedback on the results of the operation. + * @param specifiers which bands to scan. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void setSystemSelectionChannels(@NonNull List specifiers) { + Objects.requireNonNull(specifiers, "Specifiers must not be null."); + setSystemSelectionChannelsInternal(specifiers, null, null); + } + + + private void setSystemSelectionChannelsInternal(@NonNull List specifiers, + @Nullable @CallbackExecutor Executor executor, + @Nullable Consumer callback) { + IBooleanConsumer aidlConsumer = callback == null ? null : new IBooleanConsumer.Stub() { + @Override + public void accept(boolean result) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.accept(result)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setSystemSelectionChannels(specifiers, getSubId(), aidlConsumer); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#setSystemSelectionChannels RemoteException" + ex); + } + } + + /** + * Get which bands the modem's background scan is acting on, specified by + * {@link #setSystemSelectionChannels}. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public @NonNull List getSystemSelectionChannels() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSystemSelectionChannels(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getSystemSelectionChannels RemoteException" + ex); + } + return new ArrayList<>(); + } + + /** + * Verifies whether the input MCC/MNC and MVNO correspond to the current carrier. + * + * @param mccmnc the carrier's mccmnc that you want to match + * @param mvnoType the mvnoType that defined in {@link ApnSetting} + * @param mvnoMatchData the MVNO match data + * @return {@code true} if input mccmnc and mvno matches with data from sim operator. + * {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@hide} + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public boolean matchesCurrentSimOperator(@NonNull String mccmnc, @MvnoType int mvnoType, + @Nullable String mvnoMatchData) { + try { + if (!mccmnc.equals(getSimOperator())) { + return false; + } + ITelephony service = getITelephony(); + if (service != null) { + return service.isMvnoMatched(getSlotIndex(), mvnoType, mvnoMatchData); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#matchesCurrentSimOperator RemoteException" + ex); + } + return false; + } + + /** + * Callback to be used with {@link #getCallForwarding} + * @hide + */ + @SystemApi + public interface CallForwardingInfoCallback { + /** + * Indicates that the operation was successful. + */ + int RESULT_SUCCESS = 0; + + /** + * Indicates that setting or retrieving the call forwarding info failed with an unknown + * error. + */ + int RESULT_ERROR_UNKNOWN = 1; + + /** + * Indicates that call forwarding is not enabled because the recipient is not on a + * Fixed Dialing Number (FDN) list. + */ + int RESULT_ERROR_FDN_CHECK_FAILURE = 2; + + /** + * Indicates that call forwarding is not supported on the network at this time. + */ + int RESULT_ERROR_NOT_SUPPORTED = 3; + + /** + * Call forwarding errors + * @hide + */ + @IntDef(prefix = { "RESULT_ERROR_" }, value = { + RESULT_ERROR_UNKNOWN, + RESULT_ERROR_NOT_SUPPORTED, + RESULT_ERROR_FDN_CHECK_FAILURE + }) + @Retention(RetentionPolicy.SOURCE) + @interface CallForwardingError{ + } + /** + * Called when the call forwarding info is successfully retrieved from the network. + * @param info information about how calls are forwarded + */ + void onCallForwardingInfoAvailable(@NonNull CallForwardingInfo info); + + /** + * Called when there was an error retrieving the call forwarding information. + * @param error + */ + void onError(@CallForwardingError int error); + } + + /** + * Gets the voice call forwarding info for a given call forwarding reason. + * + * This method queries the network for the currently set call forwarding configuration for the + * provided call forwarding reason. When the network has provided its response, the result will + * be supplied via the provided {@link Executor} on the provided + * {@link CallForwardingInfoCallback}. + * + * @param callForwardingReason the call forwarding reason to query. + * @param executor The executor on which to execute the callback once the result is ready. + * @param callback The callback the results should be delivered on. + * + * @throws IllegalArgumentException if callForwardingReason is not any of + * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY}, + * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE}, + * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL} + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void getCallForwarding(@CallForwardingReason int callForwardingReason, + @NonNull Executor executor, @NonNull CallForwardingInfoCallback callback) { + if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL + || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) { + throw new IllegalArgumentException("callForwardingReason is out of range"); + } + + ICallForwardingInfoCallback internalCallback = new ICallForwardingInfoCallback.Stub() { + @Override + public void onCallForwardingInfoAvailable(CallForwardingInfo info) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> + callback.onCallForwardingInfoAvailable(info))); + } + + @Override + public void onError(int error) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> + callback.onError(error))); + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.getCallForwarding(getSubId(), callForwardingReason, internalCallback); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getCallForwarding RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + } + + /** + * Sets voice call forwarding behavior as described by the provided {@link CallForwardingInfo}. + * + * This method will enable call forwarding if the provided {@link CallForwardingInfo} returns + * {@code true} from its {@link CallForwardingInfo#isEnabled()} method, and disables call + * forwarding otherwise. + * + * If you wish to be notified about the results of this operation, provide an {@link Executor} + * and {@link Consumer} to be notified asynchronously when the operation completes. + * + * @param callForwardingInfo Info about whether calls should be forwarded and where they + * should be forwarded to. + * @param executor The executor on which the listener will be called. Must be non-null if + * {@code listener} is non-null. + * @param resultListener Asynchronous listener that'll be called when the operation completes. + * Called with {@link CallForwardingInfoCallback#RESULT_SUCCESS} if the + * operation succeeded and an error code from + * {@link CallForwardingInfoCallback} it failed. + * + * @throws IllegalArgumentException if any of the following are true for the parameter + * callForwardingInfo: + *

    + *
  • it is {@code null}.
  • + *
  • {@link CallForwardingInfo#getReason()} is not any of: + *
      + *
    • {@link CallForwardingInfo#REASON_UNCONDITIONAL}
    • + *
    • {@link CallForwardingInfo#REASON_BUSY}
    • + *
    • {@link CallForwardingInfo#REASON_NO_REPLY}
    • + *
    • {@link CallForwardingInfo#REASON_NOT_REACHABLE}
    • + *
    • {@link CallForwardingInfo#REASON_ALL}
    • + *
    • {@link CallForwardingInfo#REASON_ALL_CONDITIONAL}
    • + *
    + *
  • {@link CallForwardingInfo#getNumber()} returns {@code null} when enabling call + * forwarding
  • + *
  • {@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when + * enabling call forwarding
  • + *
+ * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo, + @Nullable @CallbackExecutor Executor executor, + @Nullable @CallForwardingInfoCallback.CallForwardingError + Consumer resultListener) { + if (callForwardingInfo == null) { + throw new IllegalArgumentException("callForwardingInfo is null"); + } + int callForwardingReason = callForwardingInfo.getReason(); + if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL + || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) { + throw new IllegalArgumentException("callForwardingReason is out of range"); + } + if (callForwardingInfo.isEnabled()) { + if (callForwardingInfo.getNumber() == null) { + throw new IllegalArgumentException("callForwarding number is null"); + } + if (callForwardingReason == CallForwardingInfo.REASON_NO_REPLY + && callForwardingInfo.getTimeoutSeconds() <= 0) { + throw new IllegalArgumentException("callForwarding timeout isn't positive"); + } + } + if (resultListener != null) { + Objects.requireNonNull(executor); + } + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> resultListener.accept(result))); + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCallForwarding(getSubId(), callForwardingInfo, internalCallback); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setCallForwarding RemoteException", ex); + ex.rethrowAsRuntimeException(); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setCallForwarding NPE", ex); + throw ex; + } + } + + /** + * Indicates that call waiting is enabled. + * + * @hide + */ + @SystemApi + public static final int CALL_WAITING_STATUS_ENABLED = 1; + + /** + * Indicates that call waiting is disabled. + * + * @hide + */ + @SystemApi + public static final int CALL_WAITING_STATUS_DISABLED = 2; + + /** + * Indicates there was an unknown error retrieving the call waiting status. + * + * @hide + */ + @SystemApi + public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; + + /** + * Indicates the call waiting is not supported on the current network. + * + * @hide + */ + @SystemApi + public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; + + /** + * Indicates the call waiting status could not be set or queried because the Fixed Dialing + * Numbers (FDN) feature is enabled. + * + * @hide + */ + @SystemApi + public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5; + + /** + * @hide + */ + @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = { + CALL_WAITING_STATUS_ENABLED, + CALL_WAITING_STATUS_DISABLED, + CALL_WAITING_STATUS_UNKNOWN_ERROR, + CALL_WAITING_STATUS_NOT_SUPPORTED, + CALL_WAITING_STATUS_FDN_CHECK_FAILURE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CallWaitingStatus { + } + + /** + * Retrieves the call waiting status of this device from the network. + * + * When call waiting is enabled, an incoming call that arrives when the user is already on + * an active call will be held in a waiting state while the user is notified instead of being + * rejected with a busy signal. + * + * @param executor The executor on which the result listener will be called. + * @param resultListener A {@link Consumer} that will be called with the result fetched + * from the network. The result will be one of: + *
    + *
  • {@link #CALL_WAITING_STATUS_ENABLED}}
  • + *
  • {@link #CALL_WAITING_STATUS_DISABLED}}
  • + *
  • {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR}}
  • + *
  • {@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}
  • + *
  • {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}
  • + *
+ * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void getCallWaitingStatus(@NonNull Executor executor, + @NonNull @CallWaitingStatus Consumer resultListener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(resultListener); + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> resultListener.accept(result))); + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.getCallWaitingStatus(getSubId(), internalCallback); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getCallWaitingStatus RemoteException", ex); + ex.rethrowAsRuntimeException(); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getCallWaitingStatus NPE", ex); + throw ex; + } + } + + /** + * Sets the call waiting status of this device with the network. + * + * If you wish to be notified about the results of this operation, provide an {@link Executor} + * and {@link Consumer} to be notified asynchronously when the operation completes. + * + * @see #getCallWaitingStatus for a description of the call waiting functionality. + * + * @param enabled {@code true} to enable; {@code false} to disable. + * @param executor The executor on which the listener will be called. Must be non-null if + * {@code listener} is non-null. + * @param resultListener Asynchronous listener that'll be called when the operation completes. + * Called with the new call waiting status (either + * {@link #CALL_WAITING_STATUS_ENABLED} or + * {@link #CALL_WAITING_STATUS_DISABLED} if the operation succeeded and + * {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or + * {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or + * {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_CALLING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public void setCallWaitingEnabled(boolean enabled, @Nullable Executor executor, + @Nullable Consumer resultListener) { + if (resultListener != null) { + Objects.requireNonNull(executor); + } + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> + Binder.withCleanCallingIdentity(() -> resultListener.accept(result))); + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCallWaitingStatus(getSubId(), enabled, internalCallback); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setCallWaitingStatus RemoteException", ex); + ex.rethrowAsRuntimeException(); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setCallWaitingStatus NPE", ex); + throw ex; + } + } + + /** + * Controls whether mobile data on the non-default SIM is allowed during a voice call. + * + * This is used for allowing data on the non-default data SIM when a voice call is placed on + * the non-default data SIM on DSDS devices. If this policy is disabled, users will not be able + * to use mobile data via the non-default data SIM during the call, which may mean no mobile + * data at all since some modem implementations disallow mobile data via the default data SIM + * during voice calls. + * If this policy is enabled, data will be temporarily enabled on the non-default data SIM + * during any voice calls. + * + * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}. + * @hide + */ + @SystemApi + public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; + + /** + * Controls whether MMS messages bypass the user-specified "mobile data" toggle. + * + * When enabled, requests for connections to the MMS APN will be accepted by telephony even if + * the user has turned "mobile data" off on this specific sim card. {@link #isDataEnabledForApn} + * will also return true for {@link ApnSetting#TYPE_MMS}. + * When disabled, the MMS APN will be governed by the same rules as all other APNs. + * + * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}. + * @hide + */ + @SystemApi + public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; + + /** + * Allow switching mobile data to the non-default SIM if the non-default SIM has better + * availability. + * + * This is used for temporarily allowing data on the non-default data SIM when on-default SIM + * has better availability on DSDS devices, where better availability means strong + * signal/connectivity. + * If this policy is enabled, data will be temporarily enabled on the non-default data SIM, + * including during any voice calls(equivalent to enabling + * {@link #MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL}). + * + * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}. + * @hide + */ + @SystemApi + public static final int MOBILE_DATA_POLICY_AUTO_DATA_SWITCH = 3; + + /** + * @hide + */ + @IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = { + MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL, + MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, + MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MobileDataPolicy { } + + /** + * Enables or disables a piece of mobile data policy. + * + * Enables or disables the mobile data policy specified in {@code policy}. See the detailed + * description of each policy constant for what they do. + * + * @param policy The data policy to enable. + * @param enabled Whether to enable or disable the policy. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setMobileDataPolicyEnabled(getSubId(), policy, enabled); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#setMobileDataPolicyEnabled RemoteException" + ex); + } + } + + /** + * Fetches the status of a piece of mobile data policy. + * + * @param policy The data policy that you want the status for. + * @return {@code true} if enabled, {@code false} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isMobileDataPolicyEnabled(getSubId(), policy); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#isMobileDataPolicyEnabled RemoteException" + ex); + } + return false; + } + + /** + * Indicates that the ICC PIN lock state or PIN was changed successfully. + * @hide + */ + public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE; + + /** + * Check whether ICC PIN lock is enabled. + * This is a sync call which returns the cached PIN enabled state. + * + * @return {@code true} if ICC PIN lock enabled, {@code false} if disabled. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + *

Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @WorkerThread + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @SystemApi + public boolean isIccLockEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isIccLockEnabled(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "isIccLockEnabled RemoteException", e); + e.rethrowFromSystemServer(); + } + return false; + } + + /** + * Enable or disable the ICC PIN lock. + * + * @param enabled "true" for locked, "false" for unlocked. + * @param pin needed to change the ICC PIN lock, aka. Pin1. + * @return the result of enabling or disabling the ICC PIN lock. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @NonNull + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public PinResult setIccLockEnabled(boolean enabled, @NonNull String pin) { + checkNotNull(pin, "setIccLockEnabled pin can't be null."); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + int result = telephony.setIccLockEnabled(getSubId(), enabled, pin); + if (result == CHANGE_ICC_LOCK_SUCCESS) { + return new PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 0); + } else if (result < 0) { + return PinResult.getDefaultFailedResult(); + } else { + return new PinResult(PinResult.PIN_RESULT_TYPE_INCORRECT, result); + } + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "setIccLockEnabled RemoteException", e); + e.rethrowFromSystemServer(); + } + return PinResult.getDefaultFailedResult(); + } + + /** + * Change the ICC lock PIN. + * + * @param oldPin is the old PIN + * @param newPin is the new PIN + * @return The result of changing the ICC lock PIN. + * @throws SecurityException if the caller doesn't have the permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @NonNull + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public PinResult changeIccLockPin(@NonNull String oldPin, @NonNull String newPin) { + checkNotNull(oldPin, "changeIccLockPin oldPin can't be null."); + checkNotNull(newPin, "changeIccLockPin newPin can't be null."); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + int result = telephony.changeIccLockPassword(getSubId(), oldPin, newPin); + if (result == CHANGE_ICC_LOCK_SUCCESS) { + return new PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 0); + } else if (result < 0) { + return PinResult.getDefaultFailedResult(); + } else { + return new PinResult(PinResult.PIN_RESULT_TYPE_INCORRECT, result); + } + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "changeIccLockPin RemoteException", e); + e.rethrowFromSystemServer(); + } + return PinResult.getDefaultFailedResult(); + } + + /** + * Called when userActivity is signalled in the power manager. + * This should only be called from system Uid. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void notifyUserActivity() { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.userActivity(); + } + } catch (RemoteException e) { + // one-way notification, if telephony is not available, it is okay to not throw + // exception here. + Log.w(TAG, "notifyUserActivity exception: " + e.getMessage()); + } + } + + /** + * No error. Operation succeeded. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS = 0; + + /** + * NR Dual connectivity enablement is not supported. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; + + /** + * Radio is not available. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE = 2; + + /** + * Internal Radio error. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; + + /** + * Currently in invalid state. Not able to process the request. + * @hide + */ + @SystemApi + public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ENABLE_NR_DUAL_CONNECTIVITY"}, value = { + ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS, + ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED, + ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE, + ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE, + ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR}) + public @interface EnableNrDualConnectivityResult {} + + /** + * Enable NR dual connectivity. Enabled state does not mean dual connectivity + * is active. It means device is allowed to connect to both primary and secondary. + * + * @hide + */ + @SystemApi + public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; + + /** + * Disable NR dual connectivity. Disabled state does not mean the secondary cell is released. + * Modem will release it only if the current bearer is released to avoid radio link failure. + * @hide + */ + @SystemApi + public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; + + /** + * Disable NR dual connectivity and force the secondary cell to be released if dual connectivity + * was active. This will result in radio link failure. + * @hide + */ + @SystemApi + public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; + + /** + * @hide + */ + @IntDef(prefix = { "NR_DUAL_CONNECTIVITY_" }, value = { + NR_DUAL_CONNECTIVITY_ENABLE, + NR_DUAL_CONNECTIVITY_DISABLE, + NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NrDualConnectivityState { + } + + /** + * Enable/Disable E-UTRA-NR Dual Connectivity. + * + * This api is supported only if + * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE}) + * returns true. + * @param nrDualConnectivityState expected NR dual connectivity state + * This can be passed following states + *

    + *
  1. Enable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_ENABLE} + *
  2. Disable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_DISABLE} + *
  3. Disable NR dual connectivity and force secondary cell to be released + * {@link #NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE} + *
+ * @return operation result. + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE) + public @EnableNrDualConnectivityResult int setNrDualConnectivityState( + @NrDualConnectivityState int nrDualConnectivityState) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setNrDualConnectivityState(getSubId(), nrDualConnectivityState); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setNrDualConnectivityState RemoteException", ex); + ex.rethrowFromSystemServer(); + } + + return ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE; + } + + /** + * Is E-UTRA-NR Dual Connectivity enabled. + * This api is supported only if + * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} + * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE}) + * returns true. + * @return true if dual connectivity is enabled else false. Enabled state does not mean dual + * connectivity is active. It means the device is allowed to connect to both primary and + * secondary cell. + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE) + public boolean isNrDualConnectivityEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isNrDualConnectivityEnabled(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isNRDualConnectivityEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + private static class DeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + resetServiceCache(); + } + } + + /** + * Reset everything in the service cache; if one handle died then they are + * all probably broken. + * @hide + */ + private static void resetServiceCache() { + synchronized (sCacheLock) { + if (sITelephony != null) { + sITelephony.asBinder().unlinkToDeath(sServiceDeath, 0); + sITelephony = null; + } + if (sISub != null) { + sISub.asBinder().unlinkToDeath(sServiceDeath, 0); + sISub = null; + SubscriptionManager.clearCaches(); + } + if (sISms != null) { + sISms.asBinder().unlinkToDeath(sServiceDeath, 0); + sISms = null; + } + if (sIPhoneSubInfo != null) { + sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0); + sIPhoneSubInfo = null; + } + } + } + + /** + * @hide + */ + static IPhoneSubInfo getSubscriberInfoService() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (!sServiceHandleCacheEnabled) { + return IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); + } + + if (sIPhoneSubInfo == null) { + IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getPhoneSubServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sIPhoneSubInfo == null && temp != null) { + try { + sIPhoneSubInfo = temp; + sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sIPhoneSubInfo = null; + } + } + } + } + return sIPhoneSubInfo; + } + + /** + * @hide + */ + static ISub getSubscriptionService() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (!sServiceHandleCacheEnabled) { + return ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); + } + + if (sISub == null) { + ISub temp = ISub.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSubscriptionServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sISub == null && temp != null) { + try { + sISub = temp; + sISub.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sISub = null; + } + } + } + } + return sISub; + } + + /** + * @hide + */ + static ISms getSmsService() { + // Keeps cache disabled until test fixes are checked into AOSP. + if (!sServiceHandleCacheEnabled) { + return ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); + } + + if (sISms == null) { + ISms temp = ISms.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getSmsServiceRegisterer() + .get()); + synchronized (sCacheLock) { + if (sISms == null && temp != null) { + try { + sISms = temp; + sISms.asBinder().linkToDeath(sServiceDeath, 0); + } catch (Exception e) { + // something has gone horribly wrong + sISms = null; + } + } + } + } + return sISms; + } + + /** + * Disables service handle caching for tests that utilize mock services. + * @hide + */ + @VisibleForTesting + public static void disableServiceHandleCaching() { + sServiceHandleCacheEnabled = false; + } + + /** + * Reenables service handle caching. + * @hide + */ + @VisibleForTesting + public static void enableServiceHandleCaching() { + sServiceHandleCacheEnabled = true; + } + + /** + * Setup sITelephony for testing. + * @hide + */ + @VisibleForTesting + public static void setupITelephonyForTest(ITelephony telephony) { + sITelephony = telephony; + } + + /** + * Setup sIPhoneSubInfo for testing. + * + * @hide + */ + @VisibleForTesting + public static void setupIPhoneSubInfoForTest(IPhoneSubInfo iPhoneSubInfo) { + synchronized (sCacheLock) { + sIPhoneSubInfo = iPhoneSubInfo; + } + } + + /** + * Setup sISub for testing. + * + * @hide + */ + @VisibleForTesting + public static void setupISubForTest(ISub iSub) { + synchronized (sCacheLock) { + sISub = iSub; + } + } + + /** + * Whether device can connect to 5G network when two SIMs are active. + * + * @hide TODO b/153669716: remove or make system API. + */ + public boolean canConnectTo5GInDsdsMode() { + ITelephony telephony = getITelephony(); + if (telephony == null) return true; + try { + return telephony.canConnectTo5GInDsdsMode(); + } catch (RemoteException ex) { + return true; + } catch (NullPointerException ex) { + return true; + } + } + + /** + * Returns a list of the equivalent home PLMNs (EF_EHPLMN) from the USIM app. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return A list of equivalent home PLMNs. Returns an empty list if EF_EHPLMN is empty or + * does not exist on the SIM card. + * + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws SecurityException if the caller doesn't have the permission. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @NonNull List getEquivalentHomePlmns() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getEquivalentHomePlmns(getSubId(), mContext.getOpPackageName(), + getAttributionTag()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#getEquivalentHomePlmns RemoteException" + ex); + } + + return Collections.emptyList(); + } + + /** + * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and + * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible. See comments + * on respective methods for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = + "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE"; + + /** + * Indicates whether {@link #setPreferredNetworkType}, {@link + * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and + * {@link #setAllowedNetworkTypesForReason} rely on + * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio + * interface. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK = + "CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK"; + + /** + * Indicates whether {@link #setNrDualConnectivityState()} and + * {@link #isNrDualConnectivityEnabled()} ()} are available. See comments + * on respective methods for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = + "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE"; + + /** + * Indicates whether a data throttling request sent with {@link #sendThermalMitigationRequest} + * is supported. See comments on {@link #sendThermalMitigationRequest} for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING = + "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING"; + + /** + * Indicates whether {@link #getNetworkSlicingConfiguration} is supported. See comments on + * respective methods for more information. + */ + public static final String CAPABILITY_SLICING_CONFIG_SUPPORTED = + "CAPABILITY_SLICING_CONFIG_SUPPORTED"; + + /** + * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on + * respective methods for more information. + * + * @hide + */ + public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED = + "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED"; + + /** + * Indicates whether modem supports handling parsed SIM phonebook records through the RIL, + * both batched reads and individual writes. + * + * @hide + */ + public static final String CAPABILITY_SIM_PHONEBOOK_IN_MODEM = + "CAPABILITY_SIM_PHONEBOOK_IN_MODEM"; + + /** + * A list of the radio interface capability values with public valid constants. + * + * Here is a related list for the systemapi-only valid constants: + * CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE + * CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK + * CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE + * CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING + * + * @hide + * TODO(b/185508047): Doc generation for mixed public/systemapi StringDefs formats badly. + */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "CAPABILITY_", value = { + CAPABILITY_SLICING_CONFIG_SUPPORTED, + CAPABILITY_SIM_PHONEBOOK_IN_MODEM, + }) + public @interface RadioInterfaceCapability {} + + /** + * Whether the device supports a given capability on the radio interface. + * + * If the capability is not in the set of radio interface capabilities, false is returned. + * + * @param capability the name of the capability to check for + * @return the availability of the capability + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public boolean isRadioInterfaceCapabilitySupported( + @NonNull @RadioInterfaceCapability String capability) { + try { + if (capability == null) return false; + + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isRadioInterfaceCapabilitySupported(capability); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "Telephony#isRadioInterfaceCapabilitySupported RemoteException" + ex); + } + return false; + } + + /** + * Indicates that the thermal mitigation request was completed successfully. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0; + + /** + * Indicates that the thermal mitigation request was not completed because of a modem error. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; + + /** + * Indicates that the thermal mitigation request was not completed because the modem is not + * available. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; + + /** + * Indicates that the thermal mitigation request could not power off the radio due to the device + * either being in an active emergency voice call, device pending an emergency call, or any + * other state that would disallow powering off of radio. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; + + /** + * Indicates that the thermal mitigation request resulted an unknown error. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4; + + /** + * Thermal mitigation request to control functionalities at modem. Thermal mitigation is done + * per-subscription. Caller must be sure to bind the TelephonyManager instance to subId by + * calling {@link #createForSubscriptionId(int)} if they want thermal mitigation on a specific + * subscription Id. Otherwise, TelephonyManager will use the default subscription. + * + * Calling this does not guarantee that the thermal mitigation action requested was done to + * completion. A thermal module should actively monitor the temperature levels and request an + * appropriate thermal mitigation action. Every action is assumed to be done 'on top of' the + * previous action, where the order of actions from least thermal mitigation to most is as + * follows: + *

    + *
  1. {@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_DATA_THROTTLING}
  2. + *
      + *
    1. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}
    2. + *
    3. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}
    4. + *
    5. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}
    6. + *
    + *
  3. {@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY}
  4. + *
  5. {@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}
  6. + *
+ * + * So, for example, requesting {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will ensure that the + * data on secondary carrier has been disabled before throttling on primary carrier. {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} will ensure that data on both + * primary and secondary have been disabled. {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF} will ensure that voice is + * disabled and that data on both primary and secondary carriers are disabled before turning + * radio off. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD} is not part of the order + * and can be used at any time during data throttling to hold onto the current level of data + * throttling. + * + *

If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}({@link + * #CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING}) returns false, then sending a {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD}, {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, or {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will result in {@link + * IllegalArgumentException} being thrown. However, on devices that do not + * support data throttling, {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING} can still be requested in + * order to undo the mitigations above it (i.e {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} and/or {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}).

+ * + *

In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of + * this API must also be listed in the device configuration as an authorized app in + * {@code packages/services/Telephony/res/values/config.xml} under the + * {@code thermal_mitigation_allowlisted_packages} key.

+ * + * @param thermalMitigationRequest Thermal mitigation request. See {@link + * ThermalMitigationRequest} for details. + * + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or + * if the device's modem does not support data throttling. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + @ThermalMitigationResult + public int sendThermalMitigationRequest( + @NonNull ThermalMitigationRequest thermalMitigationRequest) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest, + getOpPackageName()); + } + throw new IllegalStateException("telephony service is null."); + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#thermalMitigationRequest RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR; + } + + /** + * Registers a callback object to receive notification of changes in specified telephony states. + *

+ * To register a callback, pass a {@link TelephonyCallback} which implements + * interfaces of events. For example, + * FakeServiceStateCallback extends {@link TelephonyCallback} implements + * {@link TelephonyCallback.ServiceStateListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the callback object and passes the current (updated) + * values. + *

+ * Note: Be aware of the permission requirements stated on the {@link TelephonyCallback} + * listeners you implement. Your application must be granted these permissions in order to + * register a {@link TelephonyCallback} which requires them; a {@link SecurityException} will be + * thrown if you do not hold the required permissions for all {@link TelephonyCallback} + * listeners you implement. + *

+ * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple + * subIds, pass a separate callback object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you must + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of callbacks will cause system + * instability. If a process has registered too many callbacks without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more callbacks. + * + * @param executor The executor of where the callback will execute. + * @param callback The {@link TelephonyCallback} object to register. The caller should hold a + * reference to the callback. The framework only holds a weak reference. + */ + public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull TelephonyCallback callback) { + registerTelephonyCallback(getLocationData(), executor, callback); + } + + private int getLocationData() { + boolean renounceCoarseLocation = + getRenouncedPermissions().contains(Manifest.permission.ACCESS_COARSE_LOCATION); + boolean renounceFineLocation = + getRenouncedPermissions().contains(Manifest.permission.ACCESS_FINE_LOCATION); + if (renounceCoarseLocation) { + return INCLUDE_LOCATION_DATA_NONE; + } else if (renounceFineLocation) { + return INCLUDE_LOCATION_DATA_COARSE; + } else { + return INCLUDE_LOCATION_DATA_FINE; + } + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"INCLUDE_LOCATION_DATA_"}, value = { + INCLUDE_LOCATION_DATA_NONE, + INCLUDE_LOCATION_DATA_COARSE, + INCLUDE_LOCATION_DATA_FINE}) + public @interface IncludeLocationData {} + + /** + * Specifies to not include any location related data. + * + * Indicates whether the caller would not like to receive + * location related information which will be sent if the caller already possess + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the + * permissions. + */ + public static final int INCLUDE_LOCATION_DATA_NONE = 0; + + /** + * Include coarse location data. + * + * Indicates whether the caller would not like to receive + * location related information which will be sent if the caller already possess + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the + * permissions. + */ + public static final int INCLUDE_LOCATION_DATA_COARSE = 1; + + /** + * Include fine location data. + * + * Indicates whether the caller would not like to receive + * location related information which will be sent if the caller already possess + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the + * permissions. + */ + public static final int INCLUDE_LOCATION_DATA_FINE = 2; + + /** + * Registers a callback object to receive notification of changes in specified telephony states. + *

+ * To register a callback, pass a {@link TelephonyCallback} which implements + * interfaces of events. For example, + * FakeServiceStateCallback extends {@link TelephonyCallback} implements + * {@link TelephonyCallback.ServiceStateListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the callback object and passes the current (updated) + * values. + *

+ * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple + * subIds, pass a separate callback object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you must + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of callbacks will cause system + * instability. If a process has registered too many callbacks without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more callbacks. + * + *

+ * There's another way to renounce permissions with a custom context + * {@code AttributionSource.Builder#setRenouncedPermissions(Set)} but only for system + * apps. To avoid confusion, calling this method supersede renouncing permissions with a + * custom context. + * + * @param includeLocationData Specifies if the caller would like to receive + * location related information. + * @param executor The executor of where the callback will execute. + * @param callback The {@link TelephonyCallback} object to register. The caller should hold a + * reference to the callback. The framework only holds a weak reference. + */ + public void registerTelephonyCallback(@IncludeLocationData int includeLocationData, + @NonNull @CallbackExecutor Executor executor, + @NonNull TelephonyCallback callback) { + if (mContext == null) { + throw new IllegalStateException("telephony service is null."); + } + + if (executor == null || callback == null) { + throw new IllegalArgumentException("TelephonyCallback and executor must be non-null"); + } + mTelephonyRegistryMgr = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.registerTelephonyCallback( + includeLocationData != INCLUDE_LOCATION_DATA_FINE, + includeLocationData == INCLUDE_LOCATION_DATA_NONE, + executor, mSubId, getOpPackageName(), + getAttributionTag(), callback, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** + * Unregister an existing {@link TelephonyCallback}. + * + * @param callback The {@link TelephonyCallback} object to unregister. + */ + public void unregisterTelephonyCallback(@NonNull TelephonyCallback callback) { + + if (mContext == null) { + throw new IllegalStateException("telephony service is null."); + } + + if (callback.callback == null) { + return; + } + + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(), + getAttributionTag(), callback, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"GBA_FAILURE_REASON_"}, value = { + GBA_FAILURE_REASON_UNKNOWN, + GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED, + GBA_FAILURE_REASON_FEATURE_NOT_READY, + GBA_FAILURE_REASON_NETWORK_FAILURE, + GBA_FAILURE_REASON_INCORRECT_NAF_ID, + GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED}) + public @interface AuthenticationFailureReason {} + + /** +   * GBA Authentication has failed for an unknown reason. +   * + *

The caller should retry a message that failed with this response. + * @hide +   */ + @SystemApi + public static final int GBA_FAILURE_REASON_UNKNOWN = 0; + + /** +   * GBA Authentication is not supported by the carrier, SIM or android. +   * + *

Application should use other authentication mechanisms if possible. + * @hide +   */ + @SystemApi + public static final int GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED = 1; + + /** +   * GBA Authentication service is not ready for use. + * +   *

Application could try again at a later time. + * @hide +   */ + @SystemApi + public static final int GBA_FAILURE_REASON_FEATURE_NOT_READY = 2; + + /** +   * GBA Authentication has been failed by the network. + * @hide +   */ + @SystemApi + public static final int GBA_FAILURE_REASON_NETWORK_FAILURE = 3; + + /** +   * GBA Authentication has failed due to incorrect NAF URL. + * @hide +   */ + @SystemApi + public static final int GBA_FAILURE_REASON_INCORRECT_NAF_ID = 4; + + /** +   * GBA Authentication has failed due to unsupported security protocol + * @hide +   */ + @SystemApi + public static final int GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED = 5; + + /** + * The callback associated with a {@link #bootstrapAuthenticationRequest()}. + * @hide + */ + @SystemApi + public static class BootstrapAuthenticationCallback { + + /** + * Invoked when the previously requested GBA keys are available (@see + * bootstrapAuthenticationRequest()). + * @param gbaKey Ks_NAF/Ks_ext_NAF Response + * @param transactionId Bootstrapping Transaction Identifier + */ + public void onKeysAvailable(@NonNull byte[] gbaKey, @NonNull String transactionId) {} + + /** + * @param reason The reason for the authentication failure. + */ + public void onAuthenticationFailure(@AuthenticationFailureReason int reason) {} + } + + /** + * Used to get the Generic Bootstrapping Architecture authentication keys + * KsNAF/Ks_ext_NAF for a particular NAF as defined in 3GPP spec TS 33.220 for + * the specified sub id. + * + *

Application must be prepared to wait for receiving the Gba keys through the + * registered callback and not invoke the API on the main application thread. + * Application also must call the api to get the fresh key every time instead + * of caching the key. + * + * Following steps may be invoked on the API call depending on the state of the + * underlying GBA implementation: + *

    + *
  1. Resolve and bind to a Gba implementation.
  2. + *
  3. Run bootstrapping if no valid keys are available or bootstrapping is forced.
  4. + *
  5. Generate the ks_NAF/ ks_Ext_NAF to be returned via the callback.
  6. + *
+ * + *

Requires Permission: + *

    + *
  • {@link android.Manifest.permission#MODIFY_PHONE_STATE},
  • + *
  • {@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},
  • + *
  • or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).
  • + *
+ * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link + * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN} + * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain + * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts + * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME), + * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest). + * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used + * for the authentication, which may be set the same as the resource that the application is + * going to access. For example, the nafId can be + * "https://3GPP-bootstrapping@naf1.operator.com", + * "https://3GPP-bootstrapping-uicc@naf1.operator.com", + * "https://3GPP-bootstrapping-digest@naf1.operator.com", + * "ftps://3GPP-bootstrapping-digest@naf1.operator.com". + * @param securityProtocol Security protocol identifier between UE and NAF.  See + * 3GPP TS 33.220 Annex H. Application can use +   * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId}, + * {@link UaSecurityProtocolIdentifier#create3GppUaSpId}, + * to create the ua security protocol identifier as needed +  * @param forceBootStrapping true=force bootstrapping, false=do not force +  * bootstrapping. Bootstrapping shouldn't be forced unless the application sees +  * authentication errors from the server. +  * @param e The {@link Executor} that will be used to call the Gba callback. +  * @param callback A callback called on the supplied {@link Executor} that will +  * contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are +  * available and valid at the time of call and bootstrapping is not requested, + * then the callback shall be invoked with the available keys. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @WorkerThread + @RequiresPermission(anyOf = {android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public void bootstrapAuthenticationRequest( + @UiccAppTypeExt int appType, @NonNull Uri nafId, + @NonNull UaSecurityProtocolIdentifier securityProtocol, + boolean forceBootStrapping, @NonNull Executor e, + @NonNull BootstrapAuthenticationCallback callback) { + try { + ITelephony service = getITelephony(); + if (service == null) { + e.execute(() -> callback.onAuthenticationFailure( + GBA_FAILURE_REASON_FEATURE_NOT_READY)); + return; + } + service.bootstrapAuthenticationRequest( + getSubId(), appType, nafId, securityProtocol, forceBootStrapping, + new IBootstrapAuthenticationCallback.Stub() { + @Override + public void onKeysAvailable(int token, byte[] gbaKey, + String transactionId) { + final long identity = Binder.clearCallingIdentity(); + try { + e.execute(() -> callback.onKeysAvailable(gbaKey, transactionId)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onAuthenticationFailure(int token, int reason) { + final long identity = Binder.clearCallingIdentity(); + try { + e.execute(() -> callback.onAuthenticationFailure(reason)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }); + } catch (RemoteException exception) { + Log.e(TAG, "Error calling ITelephony#bootstrapAuthenticationRequest", exception); + e.execute(() -> callback.onAuthenticationFailure(GBA_FAILURE_REASON_FEATURE_NOT_READY)); + } + } + + /** + * The network type is valid or not. + * + * @param networkType The network type {@link NetworkType}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isNetworkTypeValid(@NetworkType int networkType) { + return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN && + networkType <= TelephonyManager.NETWORK_TYPE_NR; + } + + /** + * Set a {@link SignalStrengthUpdateRequest} to receive notification when signal quality + * measurements breach the specified thresholds. + * + * To be notified, set the signal strength update request and then register + * {@link TelephonyManager#listen(PhoneStateListener, int)} with + * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS}. The notification will arrive through + * {@link PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}. + * + * To stop receiving the notification over the specified thresholds, pass the same + * {@link SignalStrengthUpdateRequest} object to + * {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}. + * + * System will clean up the {@link SignalStrengthUpdateRequest} if the caller process died + * without calling {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}. + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To request for multiple subIds, + * pass a request object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * Note that the thresholds in the request will be used on a best-effort basis; the system may + * modify requests to multiplex various request sources or to optimize power consumption. The + * caller should not expect to be notified with the exactly the same thresholds. + * + * @see #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) + * + * @param request the SignalStrengthUpdateRequest to be set into the System + * + * @throws IllegalStateException if a new request is set with same subId from the same caller + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSignalStrengthUpdateRequest", e); + } + } + + /** + * Clear a {@link SignalStrengthUpdateRequest} from the system. + * + *

Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + *

If the given request was not set before, this operation is a no-op. + * + * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) + * + * @param request the SignalStrengthUpdateRequest to be cleared from the System + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.clearSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#clearSignalStrengthUpdateRequest", e); + } + } + + /** + * Gets the current phone capability. + * + * @return the PhoneCapability which describes the data connection capability of modem. + * It's used to evaluate possible phone config change, for example from single + * SIM device to multi-SIM device. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public @NonNull PhoneCapability getPhoneCapability() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getPhoneCapability(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + if (getActiveModemCount() > 1) { + return PhoneCapability.DEFAULT_DSDS_CAPABILITY; + } else { + return PhoneCapability.DEFAULT_SSSS_CAPABILITY; + } + } + + /** + * The unattended reboot was prepared successfully. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; + + /** + * The unattended reboot was prepared, but the user will need to manually + * enter the PIN code of at least one SIM card present in the device. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; + + /** + * The unattended reboot was not prepared due to a non-recoverable error. After this error, + * the client that manages the unattended reboot should not try to invoke the API again + * until the next power cycle. + * @hide + */ + @SystemApi + public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"}, + value = { + PREPARE_UNATTENDED_REBOOT_SUCCESS, + PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED, + PREPARE_UNATTENDED_REBOOT_ERROR + }) + public @interface PrepareUnattendedRebootResult {} + + /** + * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done + * shortly (e.g. within 15 seconds) after the API is invoked. + * + *

Requires Permission: + * {@link android.Manifest.permission#REBOOT} + * + * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success. + * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains + * at least one SIM card for which the user needs to manually enter the PIN + * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case + * of error. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.REBOOT) + @PrepareUnattendedRebootResult + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + public int prepareForUnattendedReboot() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.prepareForUnattendedReboot(); + } + } catch (RemoteException e) { + Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e); + e.rethrowFromSystemServer(); + } + return PREPARE_UNATTENDED_REBOOT_ERROR; + } + + /** + * Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if + * something goes awry. + */ + public static class NetworkSlicingException extends Exception { + /** + * Getting the current slicing configuration successfully. Used internally only. + * @hide + */ + public static final int SUCCESS = 0; + + /** + * The system timed out waiting for a response from the Radio. + * @hide + */ + public static final int ERROR_TIMEOUT = 1; + + /** + * The modem returned a failure. + * @hide + */ + public static final int ERROR_MODEM_ERROR = 2; + + /** @hide */ + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_TIMEOUT, + ERROR_MODEM_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkSlicingError {} + + private final int mErrorCode; + + /** @hide */ + public NetworkSlicingException(@NetworkSlicingError int errorCode) { + mErrorCode = errorCode; + } + + @Override + public String toString() { + switch (mErrorCode) { + case ERROR_TIMEOUT: return "ERROR_TIMEOUT"; + case ERROR_MODEM_ERROR: return "ERROR_MODEM_ERROR"; + default: return "UNDEFINED"; + } + } + } + + /** + * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the + * system timed out waiting for a response from the Radio. + */ + public class TimeoutException extends NetworkSlicingException { + /** @hide */ + public TimeoutException(int errorCode) { + super(errorCode); + } + } + + /** + * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the + * modem returned a failure. + */ + public class ModemErrorException extends NetworkSlicingException { + /** @hide */ + public ModemErrorException(int errorCode) { + super(errorCode); + } + } + + /** @hide */ + public static final String KEY_SLICING_CONFIG_HANDLE = "slicing_config_handle"; + + /** + * Request to get the current slicing configuration including URSP rules and + * NSSAIs (configured, allowed and rejected). + * + * This method can be invoked if one of the following requirements is met: + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + *
+ * + * This will be invalid if the device does not support + * android.telephony.TelephonyManager#CAPABILITY_SLICING_CONFIG_SUPPORTED. + * + * @param executor the executor on which callback will be invoked. + * @param callback a callback to receive the current slicing configuration. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}. + */ + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED) + @SuppressAutoDoc // No support for carrier privileges (b/72967236). + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void getNetworkSlicingConfiguration( + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("telephony service is null."); + } + telephony.getSlicingConfig(new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + if (resultCode == NetworkSlicingException.ERROR_TIMEOUT) { + executor.execute(() -> callback.onError( + new TimeoutException(resultCode))); + return; + } else if (resultCode == NetworkSlicingException.ERROR_MODEM_ERROR) { + executor.execute(() -> callback.onError( + new ModemErrorException(resultCode))); + return; + } + + NetworkSlicingConfig slicingConfig = + result.getParcelable(KEY_SLICING_CONFIG_HANDLE, android.telephony.data.NetworkSlicingConfig.class); + executor.execute(() -> callback.onResult(slicingConfig)); + } + }); + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + } + + /** + * A premium capability that boosts the network to allow for real-time interactive traffic + * by prioritizing low latency communication. + * Corresponds to {@link NetworkCapabilities#NET_CAPABILITY_PRIORITIZE_LATENCY}. + */ + public static final int PREMIUM_CAPABILITY_PRIORITIZE_LATENCY = + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY; + + /** + * Purchasable premium capabilities. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "PREMIUM_CAPABILITY_" }, value = { + PREMIUM_CAPABILITY_PRIORITIZE_LATENCY}) + public @interface PremiumCapability {} + + /** + * Returns the premium capability {@link PremiumCapability} as a String. + * + * @param capability The premium capability. + * @return The premium capability as a String. + * @hide + */ + public static String convertPremiumCapabilityToString(@PremiumCapability int capability) { + switch (capability) { + case PREMIUM_CAPABILITY_PRIORITIZE_LATENCY: + return "PRIORITIZE_LATENCY"; + default: + return "UNKNOWN (" + capability + ")"; + } + } + + /** + * Check whether the given premium capability is available for purchase from the carrier. + * If this is {@code true}, the capability can be purchased from the carrier using + * {@link #purchasePremiumCapability(int, Executor, Consumer)}. + * + * @param capability The premium capability to check. + * @return Whether the given premium capability is available to purchase. + * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + */ + @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("telephony service is null."); + } + return telephony.isPremiumCapabilityAvailableForPurchase(capability, getSubId()); + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return false; + } + + /** + * Purchase premium capability request was successful. + * Once the purchase result is successful, the network must set up a slicing configuration + * for the purchased premium capability within the timeout specified by + * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG}. + * During the setup time, subsequent attempts will return + * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP}. + * After setup is complete, subsequent attempts will return + * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED} until the boost expires. + * The expiry time is determined by the type or duration of boost purchased from the carrier, + * provided at {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING}. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS = 1; + + /** + * Purchase premium capability failed because the request is throttled. + * If purchasing premium capabilities is throttled, it will be for the amount of time + * specified by {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}. + * If displaying the performance boost notification is throttled, it will be for the amount of + * time specified by {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}. + * We will show the performance boost notification to the user up to the daily and monthly + * maximum number of times specified by + * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT} and + * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT}. + * Subsequent attempts will return the same error until the request is no longer throttled + * or throttling conditions change. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2; + + /** + * Purchase premium capability failed because it is already purchased and available. + * Subsequent attempts will return the same error until the performance boost expires. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED = 3; + + /** + * Purchase premium capability failed because a request was already made and is in progress. + * This may have been requested by either the same app or another app. + * Subsequent attempts will return the same error until the previous request completes. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS = 4; + + /** + * Purchase premium capability failed because the requesting application is not in the + * foreground. Subsequent attempts will return the same error until the requesting application + * moves to the foreground. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND = 5; + + /** + * Purchase premium capability failed because the user canceled the operation. + * Subsequent attempts will be throttled for the amount of time specified by + * {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG} + * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6; + + /** + * Purchase premium capability failed because the carrier disabled or does not support + * the capability, as specified in + * {@link CarrierConfigManager#KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY}. + * Subsequent attempts will return the same error until the carrier enables the feature. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED = 7; + + /** + * Purchase premium capability failed because the carrier app did not indicate success. + * Subsequent attempts will be throttled for the amount of time specified by + * {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG} + * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR = 8; + + /** + * Purchase premium capability failed because we did not receive a response from the user + * for the performance boost notification within the time specified by + * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG}. + * The performance boost notification will be automatically dismissed and subsequent attempts + * will be throttled for the amount of time specified by + * {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG} + * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9; + + /** + * Purchase premium capability failed because the device does not support the feature. + * Subsequent attempts will return the same error. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED = 10; + + /** + * Purchase premium capability failed because the telephony service is unavailable + * or there was an error in the phone process. + * Subsequent attempts will return the same error until request conditions are satisfied. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED = 11; + + /** + * Purchase premium capability failed because the network is not available. + * Subsequent attempts will return the same error until network conditions change. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE = 12; + + /** + * Purchase premium capability failed because the entitlement check failed. + * Subsequent attempts will be throttled for the amount of time specified by + * {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG} + * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}. + * Throttling will be reevaluated when the network is no longer congested. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED = 13; + + /** + * Purchase premium capability failed because the request was not made on the default data + * subscription, indicated by {@link SubscriptionManager#getDefaultDataSubscriptionId()}. + * Subsequent attempts will return the same error until the request is made on the default + * data subscription. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION = 14; + + /** + * Purchase premium capability was successful and is waiting for the network to setup the + * slicing configuration. If the setup is complete within the time specified by + * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG}, + * subsequent requests will return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED} + * until the purchase expires. If the setup is not complete within the time specified above, + * applications can request the premium capability again. + */ + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP = 15; + + /** + * Purchase premium capability failed because the user disabled the feature. + * Subsequent attempts will be throttled for the amount of time specified by + * {@link CarrierConfigManager + * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG} + * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}. + */ + @FlaggedApi(Flags.FLAG_SLICING_ADDITIONAL_ERROR_CODES) + public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16; + + /** + * Results of the purchase premium capability request. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "PURCHASE_PREMIUM_CAPABILITY_RESULT_" }, value = { + PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, + PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, + PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED, + PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS, + PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND, + PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED, + PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, + PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, + PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, + PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED, + PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE, + PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED, + PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION, + PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP, + PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED}) + public @interface PurchasePremiumCapabilityResult {} + + /** + * Returns the purchase result as a String. + * + * @param result The purchase premium capability result. + * @return The purchase result as a String. + * @hide + */ + public static String convertPurchaseResultToString( + @PurchasePremiumCapabilityResult int result) { + switch (result) { + case PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS: + return "SUCCESS"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED: + return "THROTTLED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED: + return "ALREADY_PURCHASED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS: + return "ALREADY_IN_PROGRESS"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND: + return "NOT_FOREGROUND"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED: + return "USER_CANCELED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED: + return "CARRIER_DISABLED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR: + return "CARRIER_ERROR"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT: + return "TIMEOUT"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED: + return "FEATURE_NOT_SUPPORTED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED: + return "REQUEST_FAILED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE: + return "NETWORK_NOT_AVAILABLE"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED: + return "ENTITLEMENT_CHECK_FAILED"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION: + return "NOT_DEFAULT_DATA_SUBSCRIPTION"; + case PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP: + return "PENDING_NETWORK_SETUP"; + default: + return "UNKNOWN (" + result + ")"; + } + } + + /** + * Purchase the given premium capability from the carrier. + * This requires user action to purchase the boost from the carrier. + * If this returns {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS} or + * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED}, applications can request + * the premium capability via {@link ConnectivityManager#requestNetwork}. + * + * @param capability The premium capability to purchase. + * @param executor The callback executor for the response. + * @param callback The result of the purchase request. + * @throws SecurityException if the caller does not hold permissions + * READ_BASIC_PHONE_STATE or INTERNET. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_DATA}. + * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid. + */ + @RequiresPermission(allOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE, + android.Manifest.permission.INTERNET}) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA) + public void purchasePremiumCapability(@PremiumCapability int capability, + @NonNull @CallbackExecutor Executor executor, + @NonNull @PurchasePremiumCapabilityResult Consumer callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + executor.execute(() -> callback.accept(result)); + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + callback.accept(PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED); + return; + } + telephony.purchasePremiumCapability(capability, internalCallback, getSubId()); + } catch (RemoteException ex) { + callback.accept(PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED); + } + } + + /** + * Get last known cell identity. + * + * If there is current registered network this value will be same as the registered cell + * identity. If the device goes out of service the previous cell identity is cached and + * will be returned. If the cache age of the Cell identity is more than 24 hours + * it will be cleared and null will be returned. + * @return last known cell identity {@CellIdentity}. + * @hide + */ + @SystemApi + @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_GET_LAST_KNOWN_CELL_IDENTITY) + @RequiresPermission(allOf = {Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) + public @Nullable CellIdentity getLastKnownCellIdentity() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("telephony service is null."); + } + return telephony.getLastKnownCellIdentity(getSubId(), getOpPackageName(), + getAttributionTag()); + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + return null; + } + + /** + * Callbacks to listen for when the set of packages with carrier privileges for a SIM changes. + * + *

Of note, when multiple callbacks are registered, they may be triggered one after another. + * The ordering of them is not guaranteed and thus should not be depend on. + * + * @hide + */ + @SystemApi + public interface CarrierPrivilegesCallback { + /** + * Called when the set of packages with carrier privileges has changed. + * + *

Of note, this callback will not be fired if a carrier triggers a SIM profile + * switch and the same set of packages remains privileged after the switch. + * + *

At registration, the callback will receive the current set of privileged packages. + * + * @param privilegedPackageNames The updated set of package names that have carrier + * privileges + * @param privilegedUids The updated set of UIDs that have carrier privileges + */ + void onCarrierPrivilegesChanged( + @NonNull Set privilegedPackageNames, @NonNull Set privilegedUids); + + /** + * Called when the {@link CarrierService} for the current user profile has changed. + * + *

This method does nothing by default. Clients that are interested in the carrier + * service change should override this method to get package name and UID info. + * + *

At registration, the callback will receive the current carrier service info. + * + *

Of note, this callback will not be fired if a carrier triggers a SIM profile + * switch and the same carrier service remains after switch. + * + * @param carrierServicePackageName package name of the {@link CarrierService}. May be + * {@code null} when no carrier service is detected. + * @param carrierServiceUid UID of the {@link CarrierService}. May be + * {@link android.os.Process#INVALID_UID} if no carrier + * service is detected. + */ + default void onCarrierServiceChanged( + @Nullable String carrierServicePackageName, int carrierServiceUid) { + // do nothing by default + } + } + + /** + * Sets a voice service state override from telecom based on the current {@link PhoneAccount}s + * registered. See {@link PhoneAccount#CAPABILITY_VOICE_CALLING_AVAILABLE}. + * + *

Currently, this API is only called to indicate over-the-top voice calling capability of + * the SIM call manager, which will get merged into {@link ServiceState#getState} and propagated + * to interested callers via {@link #getServiceState} and {@link + * TelephonyCallback.ServiceStateListener}. + * + *

If callers are truly interested in the actual device <-> tower connection status and not + * an overall "device can make voice calls" boolean, they can use {@link + * ServiceState#getNetworkRegistrationInfo} to check CS registration state. + * + * @hide + */ + @TestApi + @SystemApi + @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) + @RequiresPermission(Manifest.permission.BIND_TELECOM_CONNECTION_SERVICE) + public void setVoiceServiceStateOverride(boolean hasService) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("Telephony service is null"); + } + telephony.setVoiceServiceStateOverride(getSubId(), hasService, getOpPackageName()); + } catch (RemoteException ex) { + ex.rethrowAsRuntimeException(); + } + } + + /** + * Registers a {@link CarrierPrivilegesCallback} on the given {@code logicalSlotIndex} to + * receive callbacks when the set of packages with carrier privileges changes. The callback will + * immediately be called with the latest state. + * + * @param logicalSlotIndex The SIM slot to listen on + * @param executor The executor where {@code callback} will be invoked + * @param callback The callback to register + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void registerCarrierPrivilegesCallback( + int logicalSlotIndex, + @NonNull @CallbackExecutor Executor executor, + @NonNull CarrierPrivilegesCallback callback) { + if (mContext == null) { + throw new IllegalStateException("Telephony service is null"); + } else if (executor == null || callback == null) { + throw new IllegalArgumentException( + "CarrierPrivilegesCallback and executor must be non-null"); + } + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr == null) { + throw new IllegalStateException("Telephony registry service is null"); + } + mTelephonyRegistryMgr.addCarrierPrivilegesCallback(logicalSlotIndex, executor, callback); + } + + /** + * Unregisters an existing {@link CarrierPrivilegesCallback}. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void unregisterCarrierPrivilegesCallback(@NonNull CarrierPrivilegesCallback callback) { + if (mContext == null) { + throw new IllegalStateException("Telephony service is null"); + } else if (callback == null) { + throw new IllegalArgumentException("CarrierPrivilegesCallback must be non-null"); + } + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr == null) { + throw new IllegalStateException("Telephony registry service is null"); + } + mTelephonyRegistryMgr.removeCarrierPrivilegesCallback(callback); + } + + /** + * set removable eSIM as default eUICC. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC) + public void setRemovableEsimAsDefaultEuicc(boolean isDefault) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setRemovableEsimAsDefaultEuicc(isDefault, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in setRemovableEsimAsDefault: " + e); + } + } + + /** + * Returns whether the removable eSIM is default eUICC or not. + * + * @hide + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC) + public boolean isRemovableEsimDefaultEuicc() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isRemovableEsimDefaultEuicc(getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in isRemovableEsimDefaultEuicc: " + e); + } + return false; + } + + /** + * Returns a constant indicating the state of sim for the slot index. + * + * @param slotIndex Logical SIM slot index. + * + * @see TelephonyManager.SimState + * + * @hide + */ + @SimState + public static int getSimStateForSlotIndex(int slotIndex) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getSimStateForSlotIndex(slotIndex); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in getSimStateForSlotIndex: " + e); + } + return TelephonyManager.SIM_STATE_UNKNOWN; + } + + /** + * Captures parameters for collection of emergency + * call diagnostic data + * @hide + */ + @SystemApi + @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) + public static final class EmergencyCallDiagnosticData { + public static final class Builder { + private boolean mCollectTelecomDumpsys; + private boolean mCollectTelephonyDumpsys; + + // If this is set to a value other than -1L, then the logcat collection is enabled. + // Logcat lines with this time or greater are collected how much is collected is + // dependent on internal implementation. Time represented as milliseconds since boot. + private long mLogcatStartTimeMillis = sUnsetLogcatStartTime; + + /** + * Allows enabling of telecom dumpsys collection. + * @param collectTelecomDumpsys Determines whether telecom dumpsys should be collected. + * @return Builder instance corresponding to the configured call diagnostic params. + */ + public @NonNull Builder setTelecomDumpsysCollectionEnabled( + boolean collectTelecomDumpsys) { + mCollectTelecomDumpsys = collectTelecomDumpsys; + return this; + } + + /** + * Allows enabling of telephony dumpsys collection. + * @param collectTelephonyDumpsys Determines if telephony dumpsys should be collected. + * @return Builder instance corresponding to the configured call diagnostic params. + */ + public @NonNull Builder setTelephonyDumpsysCollectionEnabled( + boolean collectTelephonyDumpsys) { + mCollectTelephonyDumpsys = collectTelephonyDumpsys; + return this; + } + + /** + * Allows enabling of logcat (system,radio) collection. + * @param startTimeMillis Enables logcat collection as of the indicated timestamp. + * @return Builder instance corresponding to the configured call diagnostic params. + */ + public @NonNull Builder setLogcatCollectionStartTimeMillis( + @CurrentTimeMillisLong long startTimeMillis) { + mLogcatStartTimeMillis = startTimeMillis; + return this; + } + + /** + * Build the EmergencyCallDiagnosticData from the provided Builder config. + * @return {@link EmergencyCallDiagnosticData} instance from provided builder. + */ + public @NonNull EmergencyCallDiagnosticData build() { + return new EmergencyCallDiagnosticData(mCollectTelecomDumpsys, + mCollectTelephonyDumpsys, mLogcatStartTimeMillis); + } + } + + private boolean mCollectTelecomDumpsys; + private boolean mCollectTelephonyDumpsys; + private boolean mCollectLogcat; + private long mLogcatStartTimeMillis; + + private static long sUnsetLogcatStartTime = -1L; + + private EmergencyCallDiagnosticData(boolean collectTelecomDumpsys, + boolean collectTelephonyDumpsys, long logcatStartTimeMillis) { + mCollectTelecomDumpsys = collectTelecomDumpsys; + mCollectTelephonyDumpsys = collectTelephonyDumpsys; + mLogcatStartTimeMillis = logcatStartTimeMillis; + mCollectLogcat = logcatStartTimeMillis != sUnsetLogcatStartTime; + } + + public boolean isTelecomDumpsysCollectionEnabled() { + return mCollectTelecomDumpsys; + } + + public boolean isTelephonyDumpsysCollectionEnabled() { + return mCollectTelephonyDumpsys; + } + + public boolean isLogcatCollectionEnabled() { + return mCollectLogcat; + } + + public long getLogcatCollectionStartTimeMillis() + { + return mLogcatStartTimeMillis; + } + + @Override + public String toString() { + return "EmergencyCallDiagnosticData{" + + "mCollectTelecomDumpsys=" + mCollectTelecomDumpsys + + ", mCollectTelephonyDumpsys=" + mCollectTelephonyDumpsys + + ", mCollectLogcat=" + mCollectLogcat + + ", mLogcatStartTimeMillis=" + mLogcatStartTimeMillis + + '}'; + } + } + + /** + * Request telephony to persist state for debugging emergency call failures. + * + * @param dropboxTag Tag to use when persisting data to dropbox service. + * @param data Parameters controlling what is collected in the diagnostics. + * + * @hide + */ + @SystemApi + @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) + @RequiresPermission(android.Manifest.permission.DUMP) + public void persistEmergencyCallDiagnosticData(@NonNull String dropboxTag, + @NonNull EmergencyCallDiagnosticData data) { + try { + ITelephony telephony = ITelephony.Stub.asInterface( + TelephonyFrameworkInitializer + .getTelephonyServiceManager() + .getTelephonyServiceRegisterer() + .get()); + if (telephony != null) { + telephony.persistEmergencyCallDiagnosticData(dropboxTag, + data.isLogcatCollectionEnabled(), + data.getLogcatCollectionStartTimeMillis(), + data.isTelecomDumpsysCollectionEnabled(), + data.isTelephonyDumpsysCollectionEnabled()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error while persistEmergencyCallDiagnosticData: " + e); + } + } + + /** + * Set the UE's ability to accept/reject null ciphered and null integrity-protected connections. + * + * The modem is required to ignore this in case of an emergency call. + * + *

Requires permission: android.Manifest.MODIFY_PHONE_STATE

+ * + * @param enabled if null ciphered and null integrity protected connections are permitted + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support disabling null ciphers. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setNullCipherAndIntegrityEnabled(boolean enabled) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setNullCipherAndIntegrityEnabled(enabled); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setNullCipherAndIntegrityEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get the value of the global preference for null cipher and integriy enablement. + * Note: This does not return the state of the modem, only the persisted global preference. + * + *

Requires permission: android.Manifest.READ_PHONE_STATE

+ * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support disabling null ciphers. + * @hide + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public boolean isNullCipherAndIntegrityPreferenceEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isNullCipherAndIntegrityPreferenceEnabled(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isNullCipherAndIntegrityPreferenceEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return true; + } + + /** + * Enable or disable notifications sent for cellular identifier disclosure events. + * + * Disclosure events are defined as instances where a device has sent a cellular identifier + * on the Non-access stratum (NAS) before a security context is established. As a result the + * identifier is sent in the clear, which has privacy implications for the user. + * + * @param enable if notifications about disclosure events should be enabled + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support this feature. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY) + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public void setEnableCellularIdentifierDisclosureNotifications(boolean enable) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setEnableCellularIdentifierDisclosureNotifications(enable); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setEnableCellularIdentifierDisclosureNotifications RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get whether or not cellular identifier disclosure notifications are enabled. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support this feature. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY) + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public boolean isCellularIdentifierDisclosureNotificationsEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isCellularIdentifierDisclosureNotificationsEnabled(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isCellularIdentifierDisclosureNotificationsEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + /** + * Enables or disables notifications sent when cellular null cipher or integrity algorithms + * are in use by the cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public void setNullCipherNotificationsEnabled(boolean enable) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setNullCipherNotificationsEnabled(enable); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setEnableNullCipherNotifications RemoteException", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get whether notifications are enabled for null cipher or integrity algorithms in use by the + * cellular modem. + * + * @throws IllegalStateException if the Telephony process is not currently available + * @throws SecurityException if the caller does not have the required privileges + * @throws UnsupportedOperationException if the modem does not support reporting on ciphering + * and integrity algorithms in use + * @hide + */ + @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY) + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public boolean isNullCipherNotificationsEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isNullCipherNotificationsEnabled(); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isNullCipherNotificationsEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + + /** + * Get current cell broadcast message identifier ranges. + * + * @throws SecurityException if the caller does not have the required permission + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + @NonNull + public List getCellBroadcastIdRanges() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCellBroadcastIdRanges(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + ex.rethrowFromSystemServer(); + } + return new ArrayList<>(); + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CELL_BROADCAST_RESULT_"}, value = { + CELL_BROADCAST_RESULT_UNKNOWN, + CELL_BROADCAST_RESULT_SUCCESS, + CELL_BROADCAST_RESULT_UNSUPPORTED, + CELL_BROADCAST_RESULT_FAIL_CONFIG, + CELL_BROADCAST_RESULT_FAIL_ACTIVATION}) + public @interface CellBroadcastResult {} + + /** + * The result of the cell broadcast request is unknown + * @hide + */ + @SystemApi + public static final int CELL_BROADCAST_RESULT_UNKNOWN = -1; + + /** + * The cell broadcast request is successful. + * @hide + */ + @SystemApi + public static final int CELL_BROADCAST_RESULT_SUCCESS = 0; + + /** + * The cell broadcast request is not supported. + * @hide + */ + @SystemApi + public static final int CELL_BROADCAST_RESULT_UNSUPPORTED = 1; + + /** + * The cell broadcast request is failed due to the error to set config + * @hide + */ + @SystemApi + public static final int CELL_BROADCAST_RESULT_FAIL_CONFIG = 2; + + /** + * The cell broadcast request is failed due to the error to set activation + * @hide + */ + @SystemApi + public static final int CELL_BROADCAST_RESULT_FAIL_ACTIVATION = 3; + + /** + * Callback mode type + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"EMERGENCY_CALLBACK_MODE_"}, value = { + EMERGENCY_CALLBACK_MODE_CALL, + EMERGENCY_CALLBACK_MODE_SMS}) + public @interface EmergencyCallbackModeType {} + + /** + * The callback mode is due to emergency call. + * @hide + */ + public static final int EMERGENCY_CALLBACK_MODE_CALL = 1; + + /** + * The callback mode is due to emergency SMS. + * @hide + */ + public static final int EMERGENCY_CALLBACK_MODE_SMS = 2; + + /** + * The reason for changing callback mode. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"STOP_REASON_"}, + value = { + STOP_REASON_UNKNOWN, + STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED, + STOP_REASON_NORMAL_SMS_SENT, + STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED, + STOP_REASON_EMERGENCY_SMS_SENT, + STOP_REASON_TIMER_EXPIRED, + STOP_REASON_USER_ACTION, + }) + public @interface EmergencyCallbackModeStopReason {} + + /** + * unknown reason. + * @hide + */ + public static final int STOP_REASON_UNKNOWN = 0; + + /** + * The call back mode is exited due to a new normal call is originated. + * @hide + */ + public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1; + + /** + * The call back mode is exited due to a new normal SMS is originated. + * @hide + */ + public static final int STOP_REASON_NORMAL_SMS_SENT = 2; + + /** + * The call back mode is exited due to a new emergency call is originated. + * @hide + */ + public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3; + + /** + * The call back mode is exited due to a new emergency SMS is originated. + * @hide + */ + public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4; + + /** + * The call back mode is exited due to timer expiry. + * @hide + */ + public static final int STOP_REASON_TIMER_EXPIRED = 5; + + /** + * The call back mode is exited due to user action. + * @hide + */ + public static final int STOP_REASON_USER_ACTION = 6; + + /** + * Set reception of cell broadcast messages with the list of the given ranges + * + *

The ranges set previously will be overridden by the new one. Empty list + * can be used to clear the ranges. + * + * @param ranges the list of {@link CellBroadcastIdRange} to be set. + * @param executor The {@link Executor} that will be used to call the callback. + * @param callback A callback called on the supplied {@link Executor} to notify + * the result when the operation completes. + * @throws SecurityException if the caller does not have the required permission + * @throws IllegalArgumentException when the ranges are invalid. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING) + public void setCellBroadcastIdRanges(@NonNull List ranges, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer callback) { + IIntegerConsumer consumer = callback == null ? null : new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.accept(result)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCellBroadcastIdRanges(getSubId(), ranges, consumer); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + ex.rethrowFromSystemServer(); + } + } + + /** + * Returns whether the domain selection service is supported. + * + * @return {@code true} if the domain selection service is supported. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) + public boolean isDomainSelectionSupported() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isDomainSelectionSupported(); + } + } catch (RemoteException ex) { + Rlog.w(TAG, "RemoteException", ex); + } + return false; + } + + /** + * Returns the primary IMEI (International Mobile Equipment Identity) of the device as + * mentioned in GSMA TS.37. {@link #getImei(int)} returns the IMEI that belongs to the selected + * slotID whereas this API {@link #getPrimaryImei()} returns primary IMEI of the device. + * A single SIM device with only one IMEI will be set by default as primary IMEI. + * A multi-SIM device with multiple IMEIs will have one of the IMEIs set as primary as + * mentioned in GSMA TS37_2.2_REQ_8. + * + *

Requires one of the following permissions + *

    + *
  • If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this + * is a privileged permission that can only be granted to apps preloaded on the device. + *
  • If the calling app is the device owner of a fully-managed device, a profile + * owner of an organization-owned device, or their delegates (see {@link + * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}). + *
  • If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any + * active subscription. + *
  • If the calling app is the default SMS role holder (see {@link + * RoleManager#isRoleHeld(String)}). + *
  • If the calling app has been granted the + * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission. + *
+ * + * @return Primary IMEI of type string + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_GSM}. + * @throws SecurityException if the caller does not have the required permission/privileges + */ + @NonNull + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM) + public String getPrimaryImei() { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + Rlog.e(TAG, "getPrimaryImei(): IPhoneSubInfo instance is NULL"); + throw new IllegalStateException("Telephony service not available."); + } + return telephony.getPrimaryImei(getOpPackageName(), getAttributionTag()); + } catch (RemoteException ex) { + Rlog.e(TAG, "getPrimaryImei() RemoteException : " + ex); + throw ex.rethrowAsRuntimeException(); + } + } + + /** + * Convert SIM state into string. + * + * @param state SIM state. + * @return SIM state in string format. + * + * @hide + */ + @NonNull + public static String simStateToString(@SimState int state) { + switch (state) { + case TelephonyManager.SIM_STATE_UNKNOWN: + return "UNKNOWN"; + case TelephonyManager.SIM_STATE_ABSENT: + return "ABSENT"; + case TelephonyManager.SIM_STATE_PIN_REQUIRED: + return "PIN_REQUIRED"; + case TelephonyManager.SIM_STATE_PUK_REQUIRED: + return "PUK_REQUIRED"; + case TelephonyManager.SIM_STATE_NETWORK_LOCKED: + return "NETWORK_LOCKED"; + case TelephonyManager.SIM_STATE_READY: + return "READY"; + case TelephonyManager.SIM_STATE_NOT_READY: + return "NOT_READY"; + case TelephonyManager.SIM_STATE_PERM_DISABLED: + return "PERM_DISABLED"; + case TelephonyManager.SIM_STATE_CARD_IO_ERROR: + return "CARD_IO_ERROR"; + case TelephonyManager.SIM_STATE_CARD_RESTRICTED: + return "CARD_RESTRICTED"; + case TelephonyManager.SIM_STATE_LOADED: + return "LOADED"; + case TelephonyManager.SIM_STATE_PRESENT: + return "PRESENT"; + default: + return "UNKNOWN(" + state + ")"; + } + } +} diff --git a/aosp/frameworks/native/opengl/libs/GLES2/gl2.cpp b/aosp/frameworks/native/opengl/libs/GLES2/gl2.cpp new file mode 100755 index 0000000000000000000000000000000000000000..99e58ab4b6f9a3325775b29160de1470f7cb99f4 --- /dev/null +++ b/aosp/frameworks/native/opengl/libs/GLES2/gl2.cpp @@ -0,0 +1,315 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +#include "../hooks.h" +#include "../egl_impl.h" + +using namespace android; + +// ---------------------------------------------------------------------------- +// Actual GL entry-points +// ---------------------------------------------------------------------------- + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_INTERNAL_CALL +#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE +#undef CALL_GL_API_INTERNAL_DO_RETURN +#undef CALL_GL_API_RETURN + +#if USE_SLOW_BINDING + + #define API_ENTRY(_api) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + if (_c) return _c->_api(__VA_ARGS__); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE return 0; + + // This stays blank, since void functions will implicitly return, and + // all of the other functions will return 0 based on the previous macro. + #define CALL_GL_API_INTERNAL_DO_RETURN + +#elif defined(__arm__) + + #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n" + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + asm volatile( \ + GET_TLS(r12) \ + "ldr r12, [r12, %[tls]] \n" \ + "cmp r12, #0 \n" \ + "ldrne pc, [r12, %[api]] \n" \ + : \ + : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ + [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "r0", "r1", "r2", "r3", "r12" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "mov r0, #0 \n" \ + : \ + : \ + : "r0" \ + ); + + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "bx lr \n" \ + : \ + : \ + : "r0" \ + ); + +#elif defined(__aarch64__) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + asm volatile( \ + "mrs x16, tpidr_el0\n" \ + "ldr x16, [x16, %[tls]]\n" \ + "cbz x16, 1f\n" \ + "ldr x16, [x16, %[api]]\n" \ + "br x16\n" \ + "1:\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x16" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "mov w0, wzr \n" \ + : \ + : \ + : "w0" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "ret \n" \ + : \ + : \ + : \ + ); + +#elif defined(__i386__) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + __asm__ volatile( \ + "mov %%gs:0, %%eax\n" \ + "mov %P[tls](%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ + "je 1f\n" \ + "jmp *%P[api](%%eax)\n" \ + "1:\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "cc", "%eax" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + __asm__ volatile( \ + "xor %%eax, %%eax\n" \ + : \ + : \ + : "%eax" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + __asm__ volatile( \ + "ret\n" \ + : \ + : \ + : \ + ); + +#elif defined(__x86_64__) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + __asm__ volatile( \ + "mov %%fs:0, %%rax\n" \ + "mov %P[tls](%%rax), %%rax\n" \ + "test %%rax, %%rax\n" \ + "je 1f\n" \ + "jmp *%P[api](%%rax)\n" \ + "1:\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "cc", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9", \ + "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", \ + "%xmm6", "%xmm7" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + __asm__ volatile( \ + "xor %%eax, %%eax\n" \ + : \ + : \ + : "%eax" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + __asm__ volatile( \ + "retq\n" \ + : \ + : \ + : \ + ); + +#elif defined(__riscv) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + asm volatile( \ + "mv t0, tp\n" \ + "li t1, %[tls]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "beqz t0, 1f\n" \ + "li t1, %[api]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "jalr x0, t0\n" \ + "1:\n" \ + : \ + : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \ + [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \ + "a5", "t6", "t3", "t4", "t5", "t6" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "li a0, 0\n" \ + : \ + : \ + : "a0" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "ret\n" \ + : \ + : \ + : \ + ); + +#endif + +#define CALL_GL_API(_api, ...) \ + CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \ + CALL_GL_API_INTERNAL_DO_RETURN + +#define CALL_GL_API_RETURN(_api, ...) \ + CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \ + CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + CALL_GL_API_INTERNAL_DO_RETURN + +extern "C" { +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include "gl2_api.in" +#include "gl2ext_api.in" +#pragma GCC diagnostic warning "-Wunused-parameter" +} + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_INTERNAL_CALL +#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE +#undef CALL_GL_API_INTERNAL_DO_RETURN +#undef CALL_GL_API_RETURN + +/* + * glGetString() and glGetStringi() are special because we expose some + * extensions in the wrapper. Also, wrapping glGetXXX() is required because + * the value returned for GL_NUM_EXTENSIONS may have been altered by the + * injection of the additional extensions. + */ + +extern "C" { + const GLubyte * __glGetString(GLenum name); + const GLubyte * __glGetStringi(GLenum name, GLuint index); + void __glGetBooleanv(GLenum pname, GLboolean * data); + void __glGetFloatv(GLenum pname, GLfloat * data); + void __glGetIntegerv(GLenum pname, GLint * data); + void __glGetInteger64v(GLenum pname, GLint64 * data); +} + +const GLubyte * glGetString(GLenum name) { + egl_connection_t* const cnx = egl_get_connection(); + + if(name == 0x1F01){ + //ccynice for gpu info + //const char* key = "persist.sys.gpuName"; + //const char* default_str = reinterpret_cast(cnx->platform.glGetString(name)); + static char value[100]; + int len = property_get("persist.sys.gpuRender", value, "unknown"); + if(len>0 && strcmp(value, "unknown") != 0 ) + return reinterpret_cast(value); + else + return cnx->platform.glGetString(name); + }else{ + return cnx->platform.glGetString(name); + } +} + +const GLubyte * glGetStringi(GLenum name, GLuint index) { + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetStringi(name, index); +} + +void glGetBooleanv(GLenum pname, GLboolean * data) { + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetBooleanv(pname, data); +} + +void glGetFloatv(GLenum pname, GLfloat * data) { + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetFloatv(pname, data); +} + +void glGetIntegerv(GLenum pname, GLint * data) { + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetIntegerv(pname, data); +} + +void glGetInteger64v(GLenum pname, GLint64 * data) { + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetInteger64v(pname, data); +} diff --git a/aosp/frameworks/native/opengl/libs/GLES_CM/gl.cpp b/aosp/frameworks/native/opengl/libs/GLES_CM/gl.cpp new file mode 100755 index 0000000000000000000000000000000000000000..901d68ea5ef974cb66b9e505c9dc0b03cf11e7de --- /dev/null +++ b/aosp/frameworks/native/opengl/libs/GLES_CM/gl.cpp @@ -0,0 +1,340 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "../hooks.h" +#include "../egl_impl.h" + +using namespace android; + +// ---------------------------------------------------------------------------- +// extensions for the framework +// ---------------------------------------------------------------------------- + +extern "C" { +GL_API void GL_APIENTRY glColorPointerBounds(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr, GLsizei count); +GL_API void GL_APIENTRY glNormalPointerBounds(GLenum type, GLsizei stride, + const GLvoid *pointer, GLsizei count); +GL_API void GL_APIENTRY glTexCoordPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +GL_API void GL_APIENTRY glVertexPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +GL_API void GL_APIENTRY glPointSizePointerOESBounds(GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei count); +} + +void glColorPointerBounds(GLint size, GLenum type, GLsizei stride, + const GLvoid *ptr, GLsizei /*count*/) { + glColorPointer(size, type, stride, ptr); +} +void glNormalPointerBounds(GLenum type, GLsizei stride, + const GLvoid *pointer, GLsizei /*count*/) { + glNormalPointer(type, stride, pointer); +} +void glTexCoordPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei /*count*/) { + glTexCoordPointer(size, type, stride, pointer); +} +void glVertexPointerBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei /*count*/) { + glVertexPointer(size, type, stride, pointer); +} + +void GL_APIENTRY glPointSizePointerOESBounds(GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei /*count*/) { + glPointSizePointerOES(type, stride, pointer); +} + +GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei /*count*/) { + glMatrixIndexPointerOES(size, type, stride, pointer); +} + +GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, + GLsizei stride, const GLvoid *pointer, GLsizei /*count*/) { + glWeightPointerOES(size, type, stride, pointer); +} + +// ---------------------------------------------------------------------------- +// Actual GL entry-points +// ---------------------------------------------------------------------------- + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_INTERNAL_CALL +#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE +#undef CALL_GL_API_INTERNAL_DO_RETURN +#undef CALL_GL_API_RETURN + +#if USE_SLOW_BINDING + + #define API_ENTRY(_api) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \ + if (_c) return _c->_api(__VA_ARGS__); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE return 0; + + // This stays blank, since void functions will implicitly return, and + // all of the other functions will return 0 based on the previous macro. + #define CALL_GL_API_INTERNAL_DO_RETURN + +#elif defined(__arm__) + + #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n" + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + asm volatile( \ + GET_TLS(r12) \ + "ldr r12, [r12, %[tls]] \n" \ + "cmp r12, #0 \n" \ + "ldrne pc, [r12, %[api]] \n" \ + : \ + : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ + [api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "r0", "r1", "r2", "r3", "r12" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "mov r0, #0 \n" \ + : \ + : \ + : "r0" \ + ); + + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "bx lr \n" \ + : \ + : \ + : "r0" \ + ); + +#elif defined(__aarch64__) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + asm volatile( \ + "mrs x16, tpidr_el0\n" \ + "ldr x16, [x16, %[tls]]\n" \ + "cbz x16, 1f\n" \ + "ldr x16, [x16, %[api]]\n" \ + "br x16\n" \ + "1:\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x16" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "mov w0, wzr \n" \ + : \ + : \ + : "w0" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "ret \n" \ + : \ + : \ + : \ + ); + +#elif defined(__i386__) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + __asm__ volatile( \ + "mov %%gs:0, %%eax\n" \ + "mov %P[tls](%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ + "je 1f\n" \ + "jmp *%P[api](%%eax)\n" \ + "1:\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "cc", "%eax" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + __asm__ volatile( \ + "xor %%eax, %%eax\n" \ + : \ + : \ + : "%eax" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + __asm__ volatile( \ + "ret\n" \ + : \ + : \ + : \ + ); + +#elif defined(__x86_64__) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + __asm__ volatile( \ + "mov %%fs:0, %%rax\n" \ + "mov %P[tls](%%rax), %%rax\n" \ + "test %%rax, %%rax\n" \ + "je 1f\n" \ + "jmp *%P[api](%%rax)\n" \ + "1:\n" \ + : \ + : [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \ + [api] "i" (__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "cc", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9", \ + "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", \ + "%xmm6", "%xmm7" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + __asm__ volatile( \ + "xor %%eax, %%eax\n" \ + : \ + : \ + : "%eax" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + __asm__ volatile( \ + "retq\n" \ + : \ + : \ + : \ + ); + +#elif defined(__riscv) + + #define API_ENTRY(_api) __attribute__((naked,noinline)) _api + + #define CALL_GL_API_INTERNAL_CALL(_api, ...) \ + asm volatile( \ + "mv t0, tp\n" \ + "li t1, %[tls]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "beqz t0, 1f\n" \ + "li t1, %[api]\n" \ + "add t0, t0, t1\n" \ + "ld t0, 0(t0)\n" \ + "jalr x0, t0\n" \ + "1:\n" \ + : \ + : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \ + [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \ + : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \ + "a5", "t6", "t3", "t4", "t5", "t6" \ + ); + + #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + asm volatile( \ + "li a0, 0\n" \ + : \ + : \ + : "a0" \ + ); + + #define CALL_GL_API_INTERNAL_DO_RETURN \ + asm volatile( \ + "ret\n" \ + : \ + : \ + : \ + ); + +#endif + +#define CALL_GL_API(_api, ...) \ + CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \ + CALL_GL_API_INTERNAL_DO_RETURN + +#define CALL_GL_API_RETURN(_api, ...) \ + CALL_GL_API_INTERNAL_CALL(_api, __VA_ARGS__) \ + CALL_GL_API_INTERNAL_SET_RETURN_VALUE \ + CALL_GL_API_INTERNAL_DO_RETURN + +extern "C" { +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include "gl_api.in" +#include "glext_api.in" +#pragma GCC diagnostic warning "-Wunused-parameter" +} + +#undef API_ENTRY +#undef CALL_GL_API +#undef CALL_GL_API_INTERNAL_CALL +#undef CALL_GL_API_INTERNAL_SET_RETURN_VALUE +#undef CALL_GL_API_INTERNAL_DO_RETURN +#undef CALL_GL_API_RETURN + +/* + * glGetString() is special because we expose some extensions in the wrapper + */ + +extern "C" const GLubyte * __glGetString(GLenum name); + +const GLubyte * glGetString(GLenum name) { + const GLubyte * ret = egl_get_string_for_current_context(name); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + ret = _c->glGetString(name); + } + + if(name == 0x1F01){ + //ccynice for gpu info + //const char* key = "persist.sys.gpuName"; + //const char* default_str = reinterpret_cast(cnx->platform.glGetString(name)); + static char value[100]; + int len = property_get("persist.sys.gpuRender", value, "unknown"); + if(len>0 && strcmp(value, "unknown") != 0 ) + return reinterpret_cast(value); + else + return ret; + }else{ + return ret; + } +} diff --git a/aosp/libcore/ojluni/src/main/java/java/io/File.java b/aosp/libcore/ojluni/src/main/java/java/io/File.java new file mode 100755 index 0000000000000000000000000000000000000000..aa674fec39dd5d0182736982969ad423ea0d96e9 --- /dev/null +++ b/aosp/libcore/ojluni/src/main/java/java/io/File.java @@ -0,0 +1,2312 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.net.URI; +import java.net.URL; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.util.List; +import java.util.ArrayList; +import java.security.AccessController; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import sun.security.action.GetPropertyAction; + +import android.system.Os; + +// Android-added: Info about UTF-8 usage in filenames. +/** + * An abstract representation of file and directory pathnames. + * + *

User interfaces and operating systems use system-dependent pathname + * strings to name files and directories. This class presents an + * abstract, system-independent view of hierarchical pathnames. An + * abstract pathname has two components: + * + *

    + *
  1. An optional system-dependent prefix string, + * such as a disk-drive specifier, "/" for the UNIX root + * directory, or "\\\\" for a Microsoft Windows UNC pathname, and + *
  2. A sequence of zero or more string names. + *
+ * + * The first name in an abstract pathname may be a directory name or, in the + * case of Microsoft Windows UNC pathnames, a hostname. Each subsequent name + * in an abstract pathname denotes a directory; the last name may denote + * either a directory or a file. The empty abstract pathname has no + * prefix and an empty name sequence. + * + *

The conversion of a pathname string to or from an abstract pathname is + * inherently system-dependent. When an abstract pathname is converted into a + * pathname string, each name is separated from the next by a single copy of + * the default separator character. The default name-separator + * character is defined by the system property file.separator, and + * is made available in the public static fields {@link + * #separator} and {@link #separatorChar} of this class. + * When a pathname string is converted into an abstract pathname, the names + * within it may be separated by the default name-separator character or by any + * other name-separator character that is supported by the underlying system. + * + *

A pathname, whether abstract or in string form, may be either + * absolute or relative. An absolute pathname is complete in + * that no other information is required in order to locate the file that it + * denotes. A relative pathname, in contrast, must be interpreted in terms of + * information taken from some other pathname. By default the classes in the + * java.io package always resolve relative pathnames against the + * current user directory. This directory is named by the system property + * user.dir, and is typically the directory in which the Java + * virtual machine was invoked. + * + *

The parent of an abstract pathname may be obtained by invoking + * the {@link #getParent} method of this class and consists of the pathname's + * prefix and each name in the pathname's name sequence except for the last. + * Each directory's absolute pathname is an ancestor of any File + * object with an absolute abstract pathname which begins with the directory's + * absolute pathname. For example, the directory denoted by the abstract + * pathname "/usr" is an ancestor of the directory denoted by the + * pathname "/usr/local/bin". + * + *

The prefix concept is used to handle root directories on UNIX platforms, + * and drive specifiers, root directories and UNC pathnames on Microsoft Windows platforms, + * as follows: + * + *

    + * + *
  • For UNIX platforms, the prefix of an absolute pathname is always + * "/". Relative pathnames have no prefix. The abstract pathname + * denoting the root directory has the prefix "/" and an empty + * name sequence. + * + *
  • For Microsoft Windows platforms, the prefix of a pathname that contains a drive + * specifier consists of the drive letter followed by ":" and + * possibly followed by "\\" if the pathname is absolute. The + * prefix of a UNC pathname is "\\\\"; the hostname and the share + * name are the first two names in the name sequence. A relative pathname that + * does not specify a drive has no prefix. + * + *
+ * + *

Instances of this class may or may not denote an actual file-system + * object such as a file or a directory. If it does denote such an object + * then that object resides in a partition. A partition is an + * operating system-specific portion of storage for a file system. A single + * storage device (e.g. a physical disk-drive, flash memory, CD-ROM) may + * contain multiple partitions. The object, if any, will reside on the + * partition named by some ancestor of the absolute + * form of this pathname. + * + *

A file system may implement restrictions to certain operations on the + * actual file-system object, such as reading, writing, and executing. These + * restrictions are collectively known as access permissions. The file + * system may have multiple sets of access permissions on a single object. + * For example, one set may apply to the object's owner, and another + * may apply to all other users. The access permissions on an object may + * cause some methods in this class to fail. + * + *

Instances of the File class are immutable; that is, once + * created, the abstract pathname represented by a File object + * will never change. + * + *

Interoperability with {@code java.nio.file} package

+ * + *

The {@code java.nio.file} + * package defines interfaces and classes for the Java virtual machine to access + * files, file attributes, and file systems. This API may be used to overcome + * many of the limitations of the {@code java.io.File} class. + * The {@link #toPath toPath} method may be used to obtain a {@link + * Path} that uses the abstract path represented by a {@code File} object to + * locate a file. The resulting {@code Path} may be used with the {@link + * java.nio.file.Files} class to provide more efficient and extensive access to + * additional file operations, file attributes, and I/O exceptions to help + * diagnose errors when an operation on a file fails. + * + *

On Android strings are converted to UTF-8 byte sequences when sending filenames to + * the operating system, and byte sequences returned by the operating system (from the + * various {@code list} methods) are converted to strings by decoding them as UTF-8 + * byte sequences. + * + * @author unascribed + * @since JDK1.0 + */ + +public class File + implements Serializable, Comparable +{ + + /** + * The FileSystem object representing the platform's local file system. + */ + private static final FileSystem fs = DefaultFileSystem.getFileSystem(); + + /** + * This abstract pathname's normalized pathname string. A normalized + * pathname string uses the default name-separator character and does not + * contain any duplicate or redundant separators. + * + * @serial + */ + private final String path; + + /** + * Enum type that indicates the status of a file path. + */ + private static enum PathStatus { INVALID, CHECKED }; + + /** + * The flag indicating whether the file path is invalid. + */ + private transient PathStatus status = null; + + /** + * Check if the file has an invalid path. Currently, the inspection of + * a file path is very limited, and it only covers Nul character check. + * Returning true means the path is definitely invalid/garbage. But + * returning false does not guarantee that the path is valid. + * + * @return true if the file path is invalid. + */ + final boolean isInvalid() { + if (status == null) { + status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED + : PathStatus.INVALID; + } + return status == PathStatus.INVALID; + } + + /** + * The length of this abstract pathname's prefix, or zero if it has no + * prefix. + */ + private final transient int prefixLength; + + /** + * Returns the length of this abstract pathname's prefix. + * For use by FileSystem classes. + */ + int getPrefixLength() { + return prefixLength; + } + + /** + * The system-dependent default name-separator character. This field is + * initialized to contain the first character of the value of the system + * property file.separator. On UNIX systems the value of this + * field is '/'; on Microsoft Windows systems it is '\\'. + * + * @see java.lang.System#getProperty(java.lang.String) + */ + public static final char separatorChar = fs.getSeparator(); + + /** + * The system-dependent default name-separator character, represented as a + * string for convenience. This string contains a single character, namely + * {@link #separatorChar}. + */ + public static final String separator = "" + separatorChar; + + /** + * The system-dependent path-separator character. This field is + * initialized to contain the first character of the value of the system + * property path.separator. This character is used to + * separate filenames in a sequence of files given as a path list. + * On UNIX systems, this character is ':'; on Microsoft Windows systems it + * is ';'. + * + * @see java.lang.System#getProperty(java.lang.String) + */ + public static final char pathSeparatorChar = fs.getPathSeparator(); + + /** + * The system-dependent path-separator character, represented as a string + * for convenience. This string contains a single character, namely + * {@link #pathSeparatorChar}. + */ + public static final String pathSeparator = "" + pathSeparatorChar; + + + /* -- Constructors -- */ + + /** + * Internal constructor for already-normalized pathname strings. + */ + private File(String pathname, int prefixLength) { + this.path = pathname; + this.prefixLength = prefixLength; + } + + /** + * Internal constructor for already-normalized pathname strings. + * The parameter order is used to disambiguate this method from the + * public(File, String) constructor. + */ + private File(String child, File parent) { + assert parent.path != null; + assert (!parent.path.equals("")); + this.path = fs.resolve(parent.path, child); + this.prefixLength = parent.prefixLength; + } + + /** + * Creates a new File instance by converting the given + * pathname string into an abstract pathname. If the given string is + * the empty string, then the result is the empty abstract pathname. + * + * @param pathname A pathname string + * @throws NullPointerException + * If the pathname argument is null + */ + public File(String pathname) { + if (pathname == null) { + throw new NullPointerException(); + } + this.path = fs.normalize(pathname); + this.prefixLength = fs.prefixLength(this.path); + } + + /* Note: The two-argument File constructors do not interpret an empty + parent abstract pathname as the current user directory. An empty parent + instead causes the child to be resolved against the system-dependent + directory defined by the FileSystem.getDefaultParent method. On Unix + this default is "/", while on Microsoft Windows it is "\\". This is required for + compatibility with the original behavior of this class. */ + + /** + * Creates a new File instance from a parent pathname string + * and a child pathname string. + * + *

If parent is null then the new + * File instance is created as if by invoking the + * single-argument File constructor on the given + * child pathname string. + * + *

Otherwise the parent pathname string is taken to denote + * a directory, and the child pathname string is taken to + * denote either a directory or a file. If the child pathname + * string is absolute then it is converted into a relative pathname in a + * system-dependent way. If parent is the empty string then + * the new File instance is created by converting + * child into an abstract pathname and resolving the result + * against a system-dependent default directory. Otherwise each pathname + * string is converted into an abstract pathname and the child abstract + * pathname is resolved against the parent. + * + * @param parent The parent pathname string + * @param child The child pathname string + * @throws NullPointerException + * If child is null + */ + public File(String parent, String child) { + if (child == null) { + throw new NullPointerException(); + } + // BEGIN Android-changed: b/25859957, app-compat; don't substitute empty parent. + if (parent != null && !parent.isEmpty()) { + this.path = fs.resolve(fs.normalize(parent), + fs.normalize(child)); + // END Android-changed: b/25859957, app-compat; don't substitute empty parent. + } else { + this.path = fs.normalize(child); + } + this.prefixLength = fs.prefixLength(this.path); + } + + /** + * Creates a new File instance from a parent abstract + * pathname and a child pathname string. + * + *

If parent is null then the new + * File instance is created as if by invoking the + * single-argument File constructor on the given + * child pathname string. + * + *

Otherwise the parent abstract pathname is taken to + * denote a directory, and the child pathname string is taken + * to denote either a directory or a file. If the child + * pathname string is absolute then it is converted into a relative + * pathname in a system-dependent way. If parent is the empty + * abstract pathname then the new File instance is created by + * converting child into an abstract pathname and resolving + * the result against a system-dependent default directory. Otherwise each + * pathname string is converted into an abstract pathname and the child + * abstract pathname is resolved against the parent. + * + * @param parent The parent abstract pathname + * @param child The child pathname string + * @throws NullPointerException + * If child is null + */ + public File(File parent, String child) { + if (child == null) { + throw new NullPointerException(); + } + if (parent != null) { + if (parent.path.equals("")) { + this.path = fs.resolve(fs.getDefaultParent(), + fs.normalize(child)); + } else { + this.path = fs.resolve(parent.path, + fs.normalize(child)); + } + } else { + this.path = fs.normalize(child); + } + this.prefixLength = fs.prefixLength(this.path); + } + + /** + * Creates a new File instance by converting the given + * file: URI into an abstract pathname. + * + *

The exact form of a file: URI is system-dependent, hence + * the transformation performed by this constructor is also + * system-dependent. + * + *

For a given abstract pathname f it is guaranteed that + * + *

+ * new File( f.{@link #toURI() toURI}()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) + *
+ * + * so long as the original abstract pathname, the URI, and the new abstract + * pathname are all created in (possibly different invocations of) the same + * Java virtual machine. This relationship typically does not hold, + * however, when a file: URI that is created in a virtual machine + * on one operating system is converted into an abstract pathname in a + * virtual machine on a different operating system. + * + * @param uri + * An absolute, hierarchical URI with a scheme equal to + * "file", a non-empty path component, and undefined + * authority, query, and fragment components + * + * @throws NullPointerException + * If uri is null + * + * @throws IllegalArgumentException + * If the preconditions on the parameter do not hold + * + * @see #toURI() + * @see java.net.URI + * @since 1.4 + */ + public File(URI uri) { + + // Check our many preconditions + if (!uri.isAbsolute()) + throw new IllegalArgumentException("URI is not absolute"); + if (uri.isOpaque()) + throw new IllegalArgumentException("URI is not hierarchical"); + String scheme = uri.getScheme(); + if ((scheme == null) || !scheme.equalsIgnoreCase("file")) + throw new IllegalArgumentException("URI scheme is not \"file\""); + if (uri.getAuthority() != null) + throw new IllegalArgumentException("URI has an authority component"); + if (uri.getFragment() != null) + throw new IllegalArgumentException("URI has a fragment component"); + if (uri.getQuery() != null) + throw new IllegalArgumentException("URI has a query component"); + String p = uri.getPath(); + if (p.equals("")) + throw new IllegalArgumentException("URI path component is empty"); + + // Okay, now initialize + p = fs.fromURIPath(p); + if (File.separatorChar != '/') + p = p.replace('/', File.separatorChar); + this.path = fs.normalize(p); + this.prefixLength = fs.prefixLength(this.path); + } + + + /* -- Path-component accessors -- */ + + /** + * Returns the name of the file or directory denoted by this abstract + * pathname. This is just the last name in the pathname's name + * sequence. If the pathname's name sequence is empty, then the empty + * string is returned. + * + * @return The name of the file or directory denoted by this abstract + * pathname, or the empty string if this pathname's name sequence + * is empty + */ + public String getName() { + int index = path.lastIndexOf(separatorChar); + if (index < prefixLength) return path.substring(prefixLength); + return path.substring(index + 1); + } + + /** + * Returns the pathname string of this abstract pathname's parent, or + * null if this pathname does not name a parent directory. + * + *

The parent of an abstract pathname consists of the + * pathname's prefix, if any, and each name in the pathname's name + * sequence except for the last. If the name sequence is empty then + * the pathname does not name a parent directory. + * + * @return The pathname string of the parent directory named by this + * abstract pathname, or null if this pathname + * does not name a parent + */ + public String getParent() { + int index = path.lastIndexOf(separatorChar); + if (index < prefixLength) { + if ((prefixLength > 0) && (path.length() > prefixLength)) + return path.substring(0, prefixLength); + return null; + } + return path.substring(0, index); + } + + /** + * Returns the abstract pathname of this abstract pathname's parent, + * or null if this pathname does not name a parent + * directory. + * + *

The parent of an abstract pathname consists of the + * pathname's prefix, if any, and each name in the pathname's name + * sequence except for the last. If the name sequence is empty then + * the pathname does not name a parent directory. + * + * @return The abstract pathname of the parent directory named by this + * abstract pathname, or null if this pathname + * does not name a parent + * + * @since 1.2 + */ + public File getParentFile() { + String p = this.getParent(); + if (p == null) return null; + return new File(p, this.prefixLength); + } + + /** + * Converts this abstract pathname into a pathname string. The resulting + * string uses the {@link #separator default name-separator character} to + * separate the names in the name sequence. + * + * @return The string form of this abstract pathname + */ + public String getPath() { + return path; + } + + + /* -- Path operations -- */ + + // Android-changed: Android-specific path information. + /** + * Tests whether this abstract pathname is absolute. The definition of + * absolute pathname is system dependent. On Android, absolute paths start with + * the character '/'. + * + * @return true if this abstract pathname is absolute, + * false otherwise + */ + public boolean isAbsolute() { + return fs.isAbsolute(this); + } + + // Android-changed: Android-specific path information. + /** + * Returns the absolute path of this file. An absolute path is a path that starts at a root + * of the file system. On Android, there is only one root: {@code /}. + * + *

A common use for absolute paths is when passing paths to a {@code Process} as + * command-line arguments, to remove the requirement implied by relative paths, that the + * child must have the same working directory as its parent. + * + * @return The absolute pathname string denoting the same file or + * directory as this abstract pathname + * + * @see java.io.File#isAbsolute() + */ + public String getAbsolutePath() { + return fs.resolve(this); + } + + /** + * Returns the absolute form of this abstract pathname. Equivalent to + * new File(this.{@link #getAbsolutePath}). + * + * @return The absolute abstract pathname denoting the same file or + * directory as this abstract pathname + * + * @throws SecurityException + * If a required system property value cannot be accessed. + * + * @since 1.2 + */ + public File getAbsoluteFile() { + String absPath = getAbsolutePath(); + return new File(absPath, fs.prefixLength(absPath)); + } + + /** + * Returns the canonical pathname string of this abstract pathname. + * + *

A canonical pathname is both absolute and unique. The precise + * definition of canonical form is system-dependent. This method first + * converts this pathname to absolute form if necessary, as if by invoking the + * {@link #getAbsolutePath} method, and then maps it to its unique form in a + * system-dependent way. This typically involves removing redundant names + * such as "." and ".." from the pathname, resolving + * symbolic links (on UNIX platforms), and converting drive letters to a + * standard case (on Microsoft Windows platforms). + * + *

Every pathname that denotes an existing file or directory has a + * unique canonical form. Every pathname that denotes a nonexistent file + * or directory also has a unique canonical form. The canonical form of + * the pathname of a nonexistent file or directory may be different from + * the canonical form of the same pathname after the file or directory is + * created. Similarly, the canonical form of the pathname of an existing + * file or directory may be different from the canonical form of the same + * pathname after the file or directory is deleted. + * + * @return The canonical pathname string denoting the same file or + * directory as this abstract pathname + * + * @throws IOException + * If an I/O error occurs, which is possible because the + * construction of the canonical pathname may require + * filesystem queries + * + * @throws SecurityException + * If a required system property value cannot be accessed, or + * if a security manager exists and its {@link + * java.lang.SecurityManager#checkRead} method denies + * read access to the file + * + * @since JDK1.1 + * @see Path#toRealPath + */ + public String getCanonicalPath() throws IOException { + if (isInvalid()) { + throw new IOException("Invalid file path"); + } + return fs.canonicalize(fs.resolve(this)); + } + + /** + * Returns the canonical form of this abstract pathname. Equivalent to + * new File(this.{@link #getCanonicalPath}). + * + * @return The canonical pathname string denoting the same file or + * directory as this abstract pathname + * + * @throws IOException + * If an I/O error occurs, which is possible because the + * construction of the canonical pathname may require + * filesystem queries + * + * @throws SecurityException + * If a required system property value cannot be accessed, or + * if a security manager exists and its {@link + * java.lang.SecurityManager#checkRead} method denies + * read access to the file + * + * @since 1.2 + * @see Path#toRealPath + */ + public File getCanonicalFile() throws IOException { + String canonPath = getCanonicalPath(); + return new File(canonPath, fs.prefixLength(canonPath)); + } + + private static String slashify(String path, boolean isDirectory) { + String p = path; + if (File.separatorChar != '/') + p = p.replace(File.separatorChar, '/'); + if (!p.startsWith("/")) + p = "/" + p; + if (!p.endsWith("/") && isDirectory) + p = p + "/"; + return p; + } + + /** + * Converts this abstract pathname into a file: URL. The + * exact form of the URL is system-dependent. If it can be determined that + * the file denoted by this abstract pathname is a directory, then the + * resulting URL will end with a slash. + * + * @return A URL object representing the equivalent file URL + * + * @throws MalformedURLException + * If the path cannot be parsed as a URL + * + * @see #toURI() + * @see java.net.URI + * @see java.net.URI#toURL() + * @see java.net.URL + * @since 1.2 + * + * @deprecated This method does not automatically escape characters that + * are illegal in URLs. It is recommended that new code convert an + * abstract pathname into a URL by first converting it into a URI, via the + * {@link #toURI() toURI} method, and then converting the URI into a URL + * via the {@link java.net.URI#toURL() URI.toURL} method. + */ + @Deprecated + public URL toURL() throws MalformedURLException { + if (isInvalid()) { + throw new MalformedURLException("Invalid file path"); + } + // Android-changed: Fix for new File("").toURL(). + // return new URL("file", "", slashify(getAbsolutePath(), isDirectory())); + return new URL("file", "", slashify(getAbsolutePath(), + getAbsoluteFile().isDirectory())); + } + + /** + * Constructs a file: URI that represents this abstract pathname. + * + *

The exact form of the URI is system-dependent. If it can be + * determined that the file denoted by this abstract pathname is a + * directory, then the resulting URI will end with a slash. + * + *

For a given abstract pathname f, it is guaranteed that + * + *

+ * new {@link #File(java.net.URI) File}( f.toURI()).equals( f.{@link #getAbsoluteFile() getAbsoluteFile}()) + *
+ * + * so long as the original abstract pathname, the URI, and the new abstract + * pathname are all created in (possibly different invocations of) the same + * Java virtual machine. Due to the system-dependent nature of abstract + * pathnames, however, this relationship typically does not hold when a + * file: URI that is created in a virtual machine on one operating + * system is converted into an abstract pathname in a virtual machine on a + * different operating system. + * + *

Note that when this abstract pathname represents a UNC pathname then + * all components of the UNC (including the server name component) are encoded + * in the {@code URI} path. The authority component is undefined, meaning + * that it is represented as {@code null}. The {@link Path} class defines the + * {@link Path#toUri toUri} method to encode the server name in the authority + * component of the resulting {@code URI}. The {@link #toPath toPath} method + * may be used to obtain a {@code Path} representing this abstract pathname. + * + * @return An absolute, hierarchical URI with a scheme equal to + * "file", a path representing this abstract pathname, + * and undefined authority, query, and fragment components + * @throws SecurityException If a required system property value cannot + * be accessed. + * + * @see #File(java.net.URI) + * @see java.net.URI + * @see java.net.URI#toURL() + * @since 1.4 + */ + public URI toURI() { + try { + File f = getAbsoluteFile(); + String sp = slashify(f.getPath(), f.isDirectory()); + if (sp.startsWith("//")) + sp = "//" + sp; + return new URI("file", null, sp, null); + } catch (URISyntaxException x) { + throw new Error(x); // Can't happen + } + } + + + /* -- Attribute accessors -- */ + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Tests whether the application can read the file denoted by this + * abstract pathname. + * + * @return true if and only if the file specified by this + * abstract pathname exists and can be read by the + * application; false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file + */ + public boolean canRead() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return false; + } + return fs.checkAccess(this, FileSystem.ACCESS_READ); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Tests whether the application can modify the file denoted by this + * abstract pathname. + * + * @return true if and only if the file system actually + * contains a file denoted by this abstract pathname and + * the application is allowed to write to the file; + * false otherwise. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + */ + public boolean canWrite() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.checkAccess(this, FileSystem.ACCESS_WRITE); + } + + /** + * Tests whether the file or directory denoted by this abstract pathname + * exists. + * + * @return true if and only if the file or directory denoted + * by this abstract pathname exists; false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file or directory + */ + public boolean exists() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return false; + } + + //ccynice for root + int callingUid = Os.getuid(); + if(callingUid > 10050){ + String regex = "(?>>>> isRootGlobalEnable content=" + content); + if (!"".equals(content.trim()) && "false".equals(content.trim())) { + return false; + } + } catch (IOException e) { + e.printStackTrace(); + } + return true; + } + + /** + * Tests whether the file denoted by this abstract pathname is a + * directory. + * + *

Where it is required to distinguish an I/O exception from the case + * that the file is not a directory, or where several attributes of the + * same file are required at the same time, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return true if and only if the file denoted by this + * abstract pathname exists and is a directory; + * false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file + */ + public boolean isDirectory() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return false; + } + return ((fs.getBooleanAttributes(this) & FileSystem.BA_DIRECTORY) + != 0); + } + + /** + * Tests whether the file denoted by this abstract pathname is a normal + * file. A file is normal if it is not a directory and, in + * addition, satisfies other system-dependent criteria. Any non-directory + * file created by a Java application is guaranteed to be a normal file. + * + *

Where it is required to distinguish an I/O exception from the case + * that the file is not a normal file, or where several attributes of the + * same file are required at the same time, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return true if and only if the file denoted by this + * abstract pathname exists and is a normal file; + * false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file + */ + public boolean isFile() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return false; + } + return ((fs.getBooleanAttributes(this) & FileSystem.BA_REGULAR) != 0); + } + + /** + * Tests whether the file named by this abstract pathname is a hidden + * file. The exact definition of hidden is system-dependent. On + * UNIX systems, a file is considered to be hidden if its name begins with + * a period character ('.'). On Microsoft Windows systems, a file is + * considered to be hidden if it has been marked as such in the filesystem. + * + * @return true if and only if the file denoted by this + * abstract pathname is hidden according to the conventions of the + * underlying platform + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file + * + * @since 1.2 + */ + public boolean isHidden() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return false; + } + return ((fs.getBooleanAttributes(this) & FileSystem.BA_HIDDEN) != 0); + } + + /** + * Returns the time that the file denoted by this abstract pathname was + * last modified. + * + *

Where it is required to distinguish an I/O exception from the case + * where {@code 0L} is returned, or where several attributes of the + * same file are required at the same time, or where the time of last + * access or the creation time are required, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return A long value representing the time the file was + * last modified, measured in milliseconds since the epoch + * (00:00:00 GMT, January 1, 1970), or 0L if the + * file does not exist or if an I/O error occurs + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file + */ + public long lastModified() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return 0L; + } + return fs.getLastModifiedTime(this); + } + + /** + * Returns the length of the file denoted by this abstract pathname. + * The return value is unspecified if this pathname denotes a directory. + * + *

Where it is required to distinguish an I/O exception from the case + * that {@code 0L} is returned, or where several attributes of the same file + * are required at the same time, then the {@link + * java.nio.file.Files#readAttributes(Path,Class,LinkOption[]) + * Files.readAttributes} method may be used. + * + * @return The length, in bytes, of the file denoted by this abstract + * pathname, or 0L if the file does not exist. Some + * operating systems may return 0L for pathnames + * denoting system-dependent entities such as devices or pipes. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method denies read access to the file + */ + public long length() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return 0L; + } + return fs.getLength(this); + } + + + /* -- File operations -- */ + + /** + * Atomically creates a new, empty file named by this abstract pathname if + * and only if a file with this name does not yet exist. The check for the + * existence of the file and the creation of the file if it does not exist + * are a single operation that is atomic with respect to all other + * filesystem activities that might affect the file. + *

+ * Note: this method should not be used for file-locking, as + * the resulting protocol cannot be made to work reliably. The + * {@link java.nio.channels.FileLock FileLock} + * facility should be used instead. + * + * @return true if the named file does not exist and was + * successfully created; false if the named file + * already exists + * + * @throws IOException + * If an I/O error occurred + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + * + * @since 1.2 + */ + public boolean createNewFile() throws IOException { + SecurityManager security = System.getSecurityManager(); + if (security != null) security.checkWrite(path); + if (isInvalid()) { + throw new IOException("Invalid file path"); + } + return fs.createFileExclusively(path); + } + + /** + * Deletes the file or directory denoted by this abstract pathname. If + * this pathname denotes a directory, then the directory must be empty in + * order to be deleted. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#delete(Path) delete} method to throw an {@link IOException} + * when a file cannot be deleted. This is useful for error reporting and to + * diagnose why a file cannot be deleted. + * + * @return true if and only if the file or directory is + * successfully deleted; false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkDelete} method denies + * delete access to the file + */ + public boolean delete() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkDelete(path); + } + if (isInvalid()) { + return false; + } + return fs.delete(this); + } + + // Android-added: Additional information about Android behaviour. + /** + * Requests that the file or directory denoted by this abstract + * pathname be deleted when the virtual machine terminates. + * Files (or directories) are deleted in the reverse order that + * they are registered. Invoking this method to delete a file or + * directory that is already registered for deletion has no effect. + * Deletion will be attempted only for normal termination of the + * virtual machine, as defined by the Java Language Specification. + * + *

Once deletion has been requested, it is not possible to cancel the + * request. This method should therefore be used with care. + * + *

+ * Note: this method should not be used for file-locking, as + * the resulting protocol cannot be made to work reliably. The + * {@link java.nio.channels.FileLock FileLock} + * facility should be used instead. + * + *

Note that on Android, the application lifecycle does not include VM termination, + * so calling this method will not ensure that files are deleted. Instead, you should + * use the most appropriate out of: + *

    + *
  • Use a {@code finally} clause to manually invoke {@link #delete}. + *
  • Maintain your own set of files to delete, and process it at an appropriate point + * in your application's lifecycle. + *
  • Use the Unix trick of deleting the file as soon as all readers and writers have + * opened it. No new readers/writers will be able to access the file, but all existing + * ones will still have access until the last one closes the file. + *
+ * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkDelete} method denies + * delete access to the file + * + * @see #delete + * + * @since 1.2 + */ + public void deleteOnExit() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkDelete(path); + } + if (isInvalid()) { + return; + } + DeleteOnExitHook.add(path); + } + + /** + * Returns an array of strings naming the files and directories in the + * directory denoted by this abstract pathname. + * + *

If this abstract pathname does not denote a directory, then this + * method returns {@code null}. Otherwise an array of strings is + * returned, one for each file or directory in the directory. Names + * denoting the directory itself and the directory's parent directory are + * not included in the result. Each string is a file name rather than a + * complete path. + * + *

There is no guarantee that the name strings in the resulting array + * will appear in any specific order; they are not, in particular, + * guaranteed to appear in alphabetical order. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method to + * open a directory and iterate over the names of the files in the directory. + * This may use less resources when working with very large directories, and + * may be more responsive when working with remote directories. + * + * @return An array of strings naming the files and directories in the + * directory denoted by this abstract pathname. The array will be + * empty if the directory is empty. Returns {@code null} if + * this abstract pathname does not denote a directory, or if an + * I/O error occurs. + * + * @throws SecurityException + * If a security manager exists and its {@link + * SecurityManager#checkRead(String)} method denies read access to + * the directory + */ + public String[] list() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(path); + } + if (isInvalid()) { + return null; + } + return fs.list(this); + } + + /** + * Returns an array of strings naming the files and directories in the + * directory denoted by this abstract pathname that satisfy the specified + * filter. The behavior of this method is the same as that of the + * {@link #list()} method, except that the strings in the returned array + * must satisfy the filter. If the given {@code filter} is {@code null} + * then all names are accepted. Otherwise, a name satisfies the filter if + * and only if the value {@code true} results when the {@link + * FilenameFilter#accept FilenameFilter.accept(File, String)} method + * of the filter is invoked on this abstract pathname and the name of a + * file or directory in the directory that it denotes. + * + * @param filter + * A filename filter + * + * @return An array of strings naming the files and directories in the + * directory denoted by this abstract pathname that were accepted + * by the given {@code filter}. The array will be empty if the + * directory is empty or if no names were accepted by the filter. + * Returns {@code null} if this abstract pathname does not denote + * a directory, or if an I/O error occurs. + * + * @throws SecurityException + * If a security manager exists and its {@link + * SecurityManager#checkRead(String)} method denies read access to + * the directory + * + * @see java.nio.file.Files#newDirectoryStream(Path,String) + */ + public String[] list(FilenameFilter filter) { + String names[] = list(); + if ((names == null) || (filter == null)) { + return names; + } + List v = new ArrayList<>(); + for (int i = 0 ; i < names.length ; i++) { + if (filter.accept(this, names[i])) { + v.add(names[i]); + } + } + return v.toArray(new String[v.size()]); + } + + /** + * Returns an array of abstract pathnames denoting the files in the + * directory denoted by this abstract pathname. + * + *

If this abstract pathname does not denote a directory, then this + * method returns {@code null}. Otherwise an array of {@code File} objects + * is returned, one for each file or directory in the directory. Pathnames + * denoting the directory itself and the directory's parent directory are + * not included in the result. Each resulting abstract pathname is + * constructed from this abstract pathname using the {@link #File(File, + * String) File(File, String)} constructor. Therefore if this + * pathname is absolute then each resulting pathname is absolute; if this + * pathname is relative then each resulting pathname will be relative to + * the same directory. + * + *

There is no guarantee that the name strings in the resulting array + * will appear in any specific order; they are not, in particular, + * guaranteed to appear in alphabetical order. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#newDirectoryStream(Path) newDirectoryStream} method + * to open a directory and iterate over the names of the files in the + * directory. This may use less resources when working with very large + * directories. + * + * @return An array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname. + * The array will be empty if the directory is empty. Returns + * {@code null} if this abstract pathname does not denote a + * directory, or if an I/O error occurs. + * + * @throws SecurityException + * If a security manager exists and its {@link + * SecurityManager#checkRead(String)} method denies read access to + * the directory + * + * @since 1.2 + */ + public File[] listFiles() { + String[] ss = list(); + if (ss == null) return null; + int n = ss.length; + File[] fs = new File[n]; + for (int i = 0; i < n; i++) { + fs[i] = new File(ss[i], this); + } + return fs; + } + + /** + * Returns an array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname that + * satisfy the specified filter. The behavior of this method is the same + * as that of the {@link #listFiles()} method, except that the pathnames in + * the returned array must satisfy the filter. If the given {@code filter} + * is {@code null} then all pathnames are accepted. Otherwise, a pathname + * satisfies the filter if and only if the value {@code true} results when + * the {@link FilenameFilter#accept + * FilenameFilter.accept(File, String)} method of the filter is + * invoked on this abstract pathname and the name of a file or directory in + * the directory that it denotes. + * + * @param filter + * A filename filter + * + * @return An array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname. + * The array will be empty if the directory is empty. Returns + * {@code null} if this abstract pathname does not denote a + * directory, or if an I/O error occurs. + * + * @throws SecurityException + * If a security manager exists and its {@link + * SecurityManager#checkRead(String)} method denies read access to + * the directory + * + * @since 1.2 + * @see java.nio.file.Files#newDirectoryStream(Path,String) + */ + public File[] listFiles(FilenameFilter filter) { + String ss[] = list(); + if (ss == null) return null; + ArrayList files = new ArrayList<>(); + for (String s : ss) + if ((filter == null) || filter.accept(this, s)) + files.add(new File(s, this)); + return files.toArray(new File[files.size()]); + } + + /** + * Returns an array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname that + * satisfy the specified filter. The behavior of this method is the same + * as that of the {@link #listFiles()} method, except that the pathnames in + * the returned array must satisfy the filter. If the given {@code filter} + * is {@code null} then all pathnames are accepted. Otherwise, a pathname + * satisfies the filter if and only if the value {@code true} results when + * the {@link FileFilter#accept FileFilter.accept(File)} method of the + * filter is invoked on the pathname. + * + * @param filter + * A file filter + * + * @return An array of abstract pathnames denoting the files and + * directories in the directory denoted by this abstract pathname. + * The array will be empty if the directory is empty. Returns + * {@code null} if this abstract pathname does not denote a + * directory, or if an I/O error occurs. + * + * @throws SecurityException + * If a security manager exists and its {@link + * SecurityManager#checkRead(String)} method denies read access to + * the directory + * + * @since 1.2 + * @see java.nio.file.Files#newDirectoryStream(Path,java.nio.file.DirectoryStream.Filter) + */ + public File[] listFiles(FileFilter filter) { + String ss[] = list(); + if (ss == null) return null; + ArrayList files = new ArrayList<>(); + for (String s : ss) { + File f = new File(s, this); + if ((filter == null) || filter.accept(f)) + files.add(f); + } + return files.toArray(new File[files.size()]); + } + + /** + * Creates the directory named by this abstract pathname. + * + * @return true if and only if the directory was + * created; false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method does not permit the named directory to be created + */ + public boolean mkdir() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.createDirectory(this); + } + + /** + * Creates the directory named by this abstract pathname, including any + * necessary but nonexistent parent directories. Note that if this + * operation fails it may have succeeded in creating some of the necessary + * parent directories. + * + * @return true if and only if the directory was created, + * along with all necessary parent directories; false + * otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkRead(java.lang.String)} + * method does not permit verification of the existence of the + * named directory and all necessary parent directories; or if + * the {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method does not permit the named directory and all necessary + * parent directories to be created + */ + public boolean mkdirs() { + if (exists()) { + return false; + } + if (mkdir()) { + return true; + } + File canonFile = null; + try { + canonFile = getCanonicalFile(); + } catch (IOException e) { + return false; + } + + File parent = canonFile.getParentFile(); + return (parent != null && (parent.mkdirs() || parent.exists()) && + canonFile.mkdir()); + } + + // Android-changed: Replaced generic platform info with Android specific one. + /** + * Renames the file denoted by this abstract pathname. + * + *

Many failures are possible. Some of the more likely failures include: + *

    + *
  • Write permission is required on the directories containing both the source and + * destination paths. + *
  • Search permission is required for all parents of both paths. + *
  • Both paths be on the same mount point. On Android, applications are most likely to hit + * this restriction when attempting to copy between internal storage and an SD card. + *
+ * + *

The return value should always be checked to make sure + * that the rename operation was successful. + * + *

Note that the {@link java.nio.file.Files} class defines the {@link + * java.nio.file.Files#move move} method to move or rename a file in a + * platform independent manner. + * + * @param dest The new abstract pathname for the named file + * + * @return true if and only if the renaming succeeded; + * false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to either the old or new pathnames + * + * @throws NullPointerException + * If parameter dest is null + */ + public boolean renameTo(File dest) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + security.checkWrite(dest.path); + } + if (dest == null) { + throw new NullPointerException(); + } + if (this.isInvalid() || dest.isInvalid()) { + return false; + } + return fs.rename(this, dest); + } + + /** + * Sets the last-modified time of the file or directory named by this + * abstract pathname. + * + *

All platforms support file-modification times to the nearest second, + * but some provide more precision. The argument will be truncated to fit + * the supported precision. If the operation succeeds and no intervening + * operations on the file take place, then the next invocation of the + * {@link #lastModified} method will return the (possibly + * truncated) time argument that was passed to this method. + * + * @param time The new last-modified time, measured in milliseconds since + * the epoch (00:00:00 GMT, January 1, 1970) + * + * @return true if and only if the operation succeeded; + * false otherwise + * + * @throws IllegalArgumentException If the argument is negative + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the named file + * + * @since 1.2 + */ + public boolean setLastModified(long time) { + if (time < 0) throw new IllegalArgumentException("Negative time"); + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.setLastModifiedTime(this, time); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Marks the file or directory named by this abstract pathname so that + * only read operations are allowed. After invoking this method the file + * or directory will not change until it is either deleted or marked + * to allow write access. Whether or not a read-only file or + * directory may be deleted depends upon the underlying system. + * + * @return true if and only if the operation succeeded; + * false otherwise + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the named file + * + * @since 1.2 + */ + public boolean setReadOnly() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.setReadOnly(this); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Sets the owner's or everybody's write permission for this abstract + * pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on + * file attributes including file permissions. This may be used when finer + * manipulation of file permissions is required. + * + * @param writable + * If true, sets the access permission to allow write + * operations; if false to disallow write operations + * + * @param ownerOnly + * If true, the write permission applies only to the + * owner's write permission; otherwise, it applies to everybody. If + * the underlying file system can not distinguish the owner's write + * permission from that of others, then the permission will apply to + * everybody, regardless of this value. + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to change + * the access permissions of this abstract pathname. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the named file + * + * @since 1.6 + */ + public boolean setWritable(boolean writable, boolean ownerOnly) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * A convenience method to set the owner's write permission for this abstract + * pathname. + * + *

An invocation of this method of the form file.setWritable(arg) + * behaves in exactly the same way as the invocation + * + *

+     *     file.setWritable(arg, true) 
+ * + * @param writable + * If true, sets the access permission to allow write + * operations; if false to disallow write operations + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + * + * @since 1.6 + */ + public boolean setWritable(boolean writable) { + return setWritable(writable, true); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Sets the owner's or everybody's read permission for this abstract + * pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on + * file attributes including file permissions. This may be used when finer + * manipulation of file permissions is required. + * + * @param readable + * If true, sets the access permission to allow read + * operations; if false to disallow read operations + * + * @param ownerOnly + * If true, the read permission applies only to the + * owner's read permission; otherwise, it applies to everybody. If + * the underlying file system can not distinguish the owner's read + * permission from that of others, then the permission will apply to + * everybody, regardless of this value. + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * readable is false and the underlying + * file system does not implement a read permission, then the + * operation will fail. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + * + * @since 1.6 + */ + public boolean setReadable(boolean readable, boolean ownerOnly) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.setPermission(this, FileSystem.ACCESS_READ, readable, ownerOnly); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * A convenience method to set the owner's read permission for this abstract + * pathname. + * + *

An invocation of this method of the form file.setReadable(arg) + * behaves in exactly the same way as the invocation + * + *

+     *     file.setReadable(arg, true) 
+ * + * @param readable + * If true, sets the access permission to allow read + * operations; if false to disallow read operations + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * readable is false and the underlying + * file system does not implement a read permission, then the + * operation will fail. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + * + * @since 1.6 + */ + public boolean setReadable(boolean readable) { + return setReadable(readable, true); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Sets the owner's or everybody's execute permission for this abstract + * pathname. + * + *

The {@link java.nio.file.Files} class defines methods that operate on + * file attributes including file permissions. This may be used when finer + * manipulation of file permissions is required. + * + * @param executable + * If true, sets the access permission to allow execute + * operations; if false to disallow execute operations + * + * @param ownerOnly + * If true, the execute permission applies only to the + * owner's execute permission; otherwise, it applies to everybody. + * If the underlying file system can not distinguish the owner's + * execute permission from that of others, then the permission will + * apply to everybody, regardless of this value. + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * executable is false and the underlying + * file system does not implement an execute permission, then the + * operation will fail. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + * + * @since 1.6 + */ + public boolean setExecutable(boolean executable, boolean ownerOnly) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkWrite(path); + } + if (isInvalid()) { + return false; + } + return fs.setPermission(this, FileSystem.ACCESS_EXECUTE, executable, ownerOnly); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * A convenience method to set the owner's execute permission for this + * abstract pathname. + * + *

An invocation of this method of the form file.setExcutable(arg) + * behaves in exactly the same way as the invocation + * + *

+     *     file.setExecutable(arg, true) 
+ * + * @param executable + * If true, sets the access permission to allow execute + * operations; if false to disallow execute operations + * + * @return true if and only if the operation succeeded. The + * operation will fail if the user does not have permission to + * change the access permissions of this abstract pathname. If + * executable is false and the underlying + * file system does not implement an execute permission, then the + * operation will fail. + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method denies write access to the file + * + * @since 1.6 + */ + public boolean setExecutable(boolean executable) { + return setExecutable(executable, true); + } + + // Android-changed: Removed inapplicable javadoc comment about special privileges. + /** + * Tests whether the application can execute the file denoted by this + * abstract pathname. + * + * @return true if and only if the abstract pathname exists + * and the application is allowed to execute the file + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkExec(java.lang.String)} + * method denies execute access to the file + * + * @since 1.6 + */ + public boolean canExecute() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkExec(path); + } + if (isInvalid()) { + return false; + } + + //ccynice for root + int callingUid = Os.getuid(); + if(callingUid > 10050){ + String regex = "(?named by this + * abstract pathname. + * + * @return The size, in bytes, of the partition or 0L if this + * abstract pathname does not name a partition + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link RuntimePermission}("getFileSystemAttributes") + * or its {@link SecurityManager#checkRead(String)} method denies + * read access to the file named by this abstract pathname + * + * @since 1.6 + */ + public long getTotalSpace() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("getFileSystemAttributes")); + sm.checkRead(path); + } + if (isInvalid()) { + return 0L; + } + return fs.getSpace(this, FileSystem.SPACE_TOTAL); + } + + /** + * Returns the number of unallocated bytes in the partition named by this abstract path name. + * + *

The returned number of unallocated bytes is a hint, but not + * a guarantee, that it is possible to use most or any of these + * bytes. The number of unallocated bytes is most likely to be + * accurate immediately after this call. It is likely to be made + * inaccurate by any external I/O operations including those made + * on the system outside of this virtual machine. This method + * makes no guarantee that write operations to this file system + * will succeed. + * + * @return The number of unallocated bytes on the partition or 0L + * if the abstract pathname does not name a partition. This + * value will be less than or equal to the total file system size + * returned by {@link #getTotalSpace}. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link RuntimePermission}("getFileSystemAttributes") + * or its {@link SecurityManager#checkRead(String)} method denies + * read access to the file named by this abstract pathname + * + * @since 1.6 + */ + public long getFreeSpace() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("getFileSystemAttributes")); + sm.checkRead(path); + } + if (isInvalid()) { + return 0L; + } + return fs.getSpace(this, FileSystem.SPACE_FREE); + } + + // Android-added: Replaced generic platform info with Android specific one. + /** + * Returns the number of bytes available to this virtual machine on the + * partition named by this abstract pathname. When + * possible, this method checks for write permissions and other operating + * system restrictions and will therefore usually provide a more accurate + * estimate of how much new data can actually be written than {@link + * #getFreeSpace}. + * + *

The returned number of available bytes is a hint, but not a + * guarantee, that it is possible to use most or any of these bytes. The + * number of unallocated bytes is most likely to be accurate immediately + * after this call. It is likely to be made inaccurate by any external + * I/O operations including those made on the system outside of this + * virtual machine. This method makes no guarantee that write operations + * to this file system will succeed. + * + *

On Android (and other Unix-based systems), this method returns the number of free bytes + * available to non-root users, regardless of whether you're actually running as root, + * and regardless of any quota or other restrictions that might apply to the user. + * (The {@code getFreeSpace} method returns the number of bytes potentially available to root.) + * + * @return The number of available bytes on the partition or 0L + * if the abstract pathname does not name a partition. On + * systems where this information is not available, this method + * will be equivalent to a call to {@link #getFreeSpace}. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link RuntimePermission}("getFileSystemAttributes") + * or its {@link SecurityManager#checkRead(String)} method denies + * read access to the file named by this abstract pathname + * + * @since 1.6 + */ + public long getUsableSpace() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("getFileSystemAttributes")); + sm.checkRead(path); + } + if (isInvalid()) { + return 0L; + } + return fs.getSpace(this, FileSystem.SPACE_USABLE); + } + + /* -- Temporary files -- */ + + private static class TempDirectory { + private TempDirectory() { } + + // Android-changed: Don't cache java.io.tmpdir value temporary directory location. + /* + private static final File tmpdir = new File(AccessController + .doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + static File location() { + return tmpdir; + } + */ + + // file name generation + // private static final SecureRandom random = new SecureRandom(); + static File generateFile(String prefix, String suffix, File dir) + throws IOException + { + // Android-changed: Use Math.randomIntInternal. + // This (pseudo) random number is initialized post-fork. + + long n = Math.randomLongInternal(); + if (n == Long.MIN_VALUE) { + n = 0; // corner case + } else { + n = Math.abs(n); + } + + // Android-changed: Reject invalid file prefixes. + // Use only the file name from the supplied prefix + // prefix = (new File(prefix)).getName(); + + String name = prefix + Long.toString(n) + suffix; + File f = new File(dir, name); + if (!name.equals(f.getName()) || f.isInvalid()) { + if (System.getSecurityManager() != null) + throw new IOException("Unable to create temporary file"); + else + throw new IOException("Unable to create temporary file, " + f); + } + return f; + } + } + + /** + *

Creates a new empty file in the specified directory, using the + * given prefix and suffix strings to generate its name. If this method + * returns successfully then it is guaranteed that: + * + *

    + *
  1. The file denoted by the returned abstract pathname did not exist + * before this method was invoked, and + *
  2. Neither this method nor any of its variants will return the same + * abstract pathname again in the current invocation of the virtual + * machine. + *
+ * + * This method provides only part of a temporary-file facility. To arrange + * for a file created by this method to be deleted automatically, use the + * {@link #deleteOnExit} method. + * + *

The prefix argument must be at least three characters + * long. It is recommended that the prefix be a short, meaningful string + * such as "hjb" or "mail". The + * suffix argument may be null, in which case the + * suffix ".tmp" will be used. + * + *

To create the new file, the prefix and the suffix may first be + * adjusted to fit the limitations of the underlying platform. If the + * prefix is too long then it will be truncated, but its first three + * characters will always be preserved. If the suffix is too long then it + * too will be truncated, but if it begins with a period character + * ('.') then the period and the first three characters + * following it will always be preserved. Once these adjustments have been + * made the name of the new file will be generated by concatenating the + * prefix, five or more internally-generated characters, and the suffix. + * + *

If the directory argument is null then the + * system-dependent default temporary-file directory will be used. The + * default temporary-file directory is specified by the system property + * java.io.tmpdir. On UNIX systems the default value of this + * property is typically "/tmp" or "/var/tmp"; on + * Microsoft Windows systems it is typically "C:\\WINNT\\TEMP". A different + * value may be given to this system property when the Java virtual machine + * is invoked, but programmatic changes to this property are not guaranteed + * to have any effect upon the temporary directory used by this method. + * + * @param prefix The prefix string to be used in generating the file's + * name; must be at least three characters long + * + * @param suffix The suffix string to be used in generating the file's + * name; may be null, in which case the + * suffix ".tmp" will be used + * + * @param directory The directory in which the file is to be created, or + * null if the default temporary-file + * directory is to be used + * + * @return An abstract pathname denoting a newly-created empty file + * + * @throws IllegalArgumentException + * If the prefix argument contains fewer than three + * characters + * + * @throws IOException If a file could not be created + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method does not allow a file to be created + * + * @since 1.2 + */ + public static File createTempFile(String prefix, String suffix, + File directory) + throws IOException + { + if (prefix.length() < 3) + throw new IllegalArgumentException("Prefix string too short"); + if (suffix == null) + suffix = ".tmp"; + + // Android-changed: Handle java.io.tmpdir changes. + File tmpdir = (directory != null) ? directory + : new File(System.getProperty("java.io.tmpdir", ".")); + //SecurityManager sm = System.getSecurityManager(); + File f; + do { + f = TempDirectory.generateFile(prefix, suffix, tmpdir); + + // Android-changed: sm is always null on Android. + /* + if (sm != null) { + try { + sm.checkWrite(f.getPath()); + } catch (SecurityException se) { + // don't reveal temporary directory location + if (directory == null) + throw new SecurityException("Unable to create temporary file"); + throw se; + } + } + */ + } while ((fs.getBooleanAttributes(f) & FileSystem.BA_EXISTS) != 0); + + if (!fs.createFileExclusively(f.getPath())) + throw new IOException("Unable to create temporary file"); + + return f; + } + + /** + * Creates an empty file in the default temporary-file directory, using + * the given prefix and suffix to generate its name. Invoking this method + * is equivalent to invoking {@link #createTempFile(java.lang.String, + * java.lang.String, java.io.File) + * createTempFile(prefix, suffix, null)}. + * + *

The {@link + * java.nio.file.Files#createTempFile(String,String,java.nio.file.attribute.FileAttribute[]) + * Files.createTempFile} method provides an alternative method to create an + * empty file in the temporary-file directory. Files created by that method + * may have more restrictive access permissions to files created by this + * method and so may be more suited to security-sensitive applications. + * + * @param prefix The prefix string to be used in generating the file's + * name; must be at least three characters long + * + * @param suffix The suffix string to be used in generating the file's + * name; may be null, in which case the + * suffix ".tmp" will be used + * + * @return An abstract pathname denoting a newly-created empty file + * + * @throws IllegalArgumentException + * If the prefix argument contains fewer than three + * characters + * + * @throws IOException If a file could not be created + * + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method does not allow a file to be created + * + * @since 1.2 + * @see java.nio.file.Files#createTempDirectory(String,FileAttribute[]) + */ + public static File createTempFile(String prefix, String suffix) + throws IOException + { + return createTempFile(prefix, suffix, null); + } + + /* -- Basic infrastructure -- */ + + /** + * Compares two abstract pathnames lexicographically. The ordering + * defined by this method depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows + * systems it is not. + * + * @param pathname The abstract pathname to be compared to this abstract + * pathname + * + * @return Zero if the argument is equal to this abstract pathname, a + * value less than zero if this abstract pathname is + * lexicographically less than the argument, or a value greater + * than zero if this abstract pathname is lexicographically + * greater than the argument + * + * @since 1.2 + */ + public int compareTo(File pathname) { + return fs.compare(this, pathname); + } + + /** + * Tests this abstract pathname for equality with the given object. + * Returns true if and only if the argument is not + * null and is an abstract pathname that denotes the same file + * or directory as this abstract pathname. Whether or not two abstract + * pathnames are equal depends upon the underlying system. On UNIX + * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows + * systems it is not. + * + * @param obj The object to be compared with this abstract pathname + * + * @return true if and only if the objects are the same; + * false otherwise + */ + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof File)) { + return compareTo((File)obj) == 0; + } + return false; + } + + /** + * Computes a hash code for this abstract pathname. Because equality of + * abstract pathnames is inherently system-dependent, so is the computation + * of their hash codes. On UNIX systems, the hash code of an abstract + * pathname is equal to the exclusive or of the hash code + * of its pathname string and the decimal value + * 1234321. On Microsoft Windows systems, the hash + * code is equal to the exclusive or of the hash code of + * its pathname string converted to lower case and the decimal + * value 1234321. Locale is not taken into account on + * lowercasing the pathname string. + * + * @return A hash code for this abstract pathname + */ + public int hashCode() { + return fs.hashCode(this); + } + + /** + * Returns the pathname string of this abstract pathname. This is just the + * string returned by the {@link #getPath} method. + * + * @return The string form of this abstract pathname + */ + public String toString() { + return getPath(); + } + + /** + * WriteObject is called to save this filename. + * The separator character is saved also so it can be replaced + * in case the path is reconstituted on a different host type. + *

+ * @serialData Default fields followed by separator character. + */ + private synchronized void writeObject(java.io.ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + s.writeChar(separatorChar); // Add the separator character + } + + /** + * readObject is called to restore this filename. + * The original separator character is read. If it is different + * than the separator character on this system, then the old separator + * is replaced by the local separator. + */ + private synchronized void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException + { + ObjectInputStream.GetField fields = s.readFields(); + String pathField = (String)fields.get("path", null); + char sep = s.readChar(); // read the previous separator char + if (sep != separatorChar) + pathField = pathField.replace(sep, separatorChar); + String path = fs.normalize(pathField); + UNSAFE.putObject(this, PATH_OFFSET, path); + UNSAFE.putIntVolatile(this, PREFIX_LENGTH_OFFSET, fs.prefixLength(path)); + } + + private static final long PATH_OFFSET; + private static final long PREFIX_LENGTH_OFFSET; + private static final sun.misc.Unsafe UNSAFE; + static { + try { + sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + PATH_OFFSET = unsafe.objectFieldOffset( + File.class.getDeclaredField("path")); + PREFIX_LENGTH_OFFSET = unsafe.objectFieldOffset( + File.class.getDeclaredField("prefixLength")); + UNSAFE = unsafe; + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + + + /** use serialVersionUID from JDK 1.0.2 for interoperability */ + private static final long serialVersionUID = 301077366599181567L; + + // -- Integration with java.nio.file -- + + private volatile transient Path filePath; + + /** + * Returns a {@link Path java.nio.file.Path} object constructed from the + * this abstract path. The resulting {@code Path} is associated with the + * {@link java.nio.file.FileSystems#getDefault default-filesystem}. + * + *

The first invocation of this method works as if invoking it were + * equivalent to evaluating the expression: + *

+     * {@link java.nio.file.FileSystems#getDefault FileSystems.getDefault}().{@link
+     * java.nio.file.FileSystem#getPath getPath}(this.{@link #getPath getPath}());
+     * 
+ * Subsequent invocations of this method return the same {@code Path}. + * + *

If this abstract pathname is the empty abstract pathname then this + * method returns a {@code Path} that may be used to access the current + * user directory. + * + * @return a {@code Path} constructed from this abstract path + * + * @throws java.nio.file.InvalidPathException + * if a {@code Path} object cannot be constructed from the abstract + * path (see {@link java.nio.file.FileSystem#getPath FileSystem.getPath}) + * + * @since 1.7 + * @see Path#toFile + */ + public Path toPath() { + Path result = filePath; + if (result == null) { + synchronized (this) { + result = filePath; + if (result == null) { + result = FileSystems.getDefault().getPath(path); + filePath = result; + } + } + } + return result; + } +} diff --git a/aosp/libcore/ojluni/src/main/java/java/lang/Runtime.java b/aosp/libcore/ojluni/src/main/java/java/lang/Runtime.java old mode 100644 new mode 100755 index 86d4df3acaa7ab281d639b6eb32cfd380d5ae034..be8bd8ce1f70d5a6e87e488e46877d25411f72cb --- a/aosp/libcore/ojluni/src/main/java/java/lang/Runtime.java +++ b/aosp/libcore/ojluni/src/main/java/java/lang/Runtime.java @@ -41,6 +41,8 @@ import java.util.StringTokenizer; import dalvik.system.BlockGuard; import sun.reflect.CallerSensitive; import java.lang.ref.FinalizerReference; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import dalvik.system.DelegateLastClassLoader; @@ -52,6 +54,9 @@ import sun.reflect.Reflection; import libcore.io.IoUtils; import libcore.io.Libcore; import libcore.util.EmptyArray; +import static android.system.OsConstants._SC_NPROCESSORS_CONF; + +import android.system.Os; import static android.system.OsConstants._SC_NPROCESSORS_ONLN; /** @@ -535,7 +540,59 @@ public class Runtime { cmdarray[i] = st.nextToken(); return exec(cmdarray, envp, dir); } - + + private boolean isLimitRootUid(String callingUid,boolean black) { + String content = ""; + File file; + if(black) + file= new File("/data/ac/black_root_uids_list"); + else + file= new File("/data/ac/white_root_uids_list"); + + if (file.exists()) { + try (InputStream instream = new FileInputStream(file)){ + if (instream != null) { + InputStreamReader inputreader = new InputStreamReader(instream); + BufferedReader buffreader = new BufferedReader(inputreader); + String line; + while ((line = buffreader.readLine()) != null) { + if ("".equals(line.trim())) { + continue; + } + content += line.trim(); + } + instream.close(); + + if (!"".equals(content)) { + String[] limitArr = content.split(";"); + for (String uid : limitArr) { + if (callingUid.equals(uid.trim())) { + return true; + } + } + } + } + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return false; + } + + private boolean isRootGlobalEnable() { + try { + String content = new String(Files.readAllBytes(Paths.get("/data/ac/limit_root_enable"))); + java.lang.System.logE("File.java 1230>>>>> isRootGlobalEnable content=" + content); + if (!"".equals(content.trim()) && "false".equals(content.trim())) { + return false; + } + } catch (IOException e) { + e.printStackTrace(); + } + return true; + } + + /** * Executes the specified command and arguments in a separate process. * @@ -706,6 +763,24 @@ public class Runtime { */ public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException { + //ccynice + int callingUid = Os.getuid(); + String regex = "(? 10000){ + String str_uid = String.valueOf(callingUid); + root_able = isRootGlobalEnable() && isLimitRootUid(str_uid,false); + for (int i = 0; i < cmdarray.length; i++) { + if(root_able) + cmdarray[i] = cmdarray[i].replaceAll(regex, "vc"); + } + }else{ + root_able = isRootGlobalEnable(); + for (int i = 0; i < cmdarray.length; i++) { + if(root_able) + cmdarray[i] = cmdarray[i].replaceAll(regex, "vc"); + } + } return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) diff --git a/aosp/libcore/ojluni/src/main/java/java/net/Inet6Address.java b/aosp/libcore/ojluni/src/main/java/java/net/Inet6Address.java new file mode 100755 index 0000000000000000000000000000000000000000..ab4bcd51cae7d8f635d7bd86317b4c77ea6e6fd2 --- /dev/null +++ b/aosp/libcore/ojluni/src/main/java/java/net/Inet6Address.java @@ -0,0 +1,1000 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.util.Enumeration; +import java.util.Arrays; +import libcore.io.Libcore; +import static android.system.OsConstants.*; + +/** + * This class represents an Internet Protocol version 6 (IPv6) address. + * Defined by + * RFC 2373: IP Version 6 Addressing Architecture. + * + *

Textual representation of IP addresses

+ * + * Textual representation of IPv6 address used as input to methods + * takes one of the following forms: + * + *
    + *
  1. The preferred form is x:x:x:x:x:x:x:x, + * where the 'x's are + * the hexadecimal values of the eight 16-bit pieces of the + * address. This is the full form. For example, + * + *

    + * + *
    {@code 1080:0:0:0:8:800:200C:417A}
    + * + *

    Note that it is not necessary to write the leading zeros in + * an individual field. However, there must be at least one numeral + * in every field, except as described below.

  2. + * + *
  3. Due to some methods of allocating certain styles of IPv6 + * addresses, it will be common for addresses to contain long + * strings of zero bits. In order to make writing addresses + * containing zero bits easier, a special syntax is available to + * compress the zeros. The use of "::" indicates multiple groups + * of 16-bits of zeros. The "::" can only appear once in an address. + * The "::" can also be used to compress the leading and/or trailing + * zeros in an address. For example, + * + *

    + * + *
    {@code 1080::8:800:200C:417A}
    + * + *
  4. An alternative form that is sometimes more convenient + * when dealing with a mixed environment of IPv4 and IPv6 nodes is + * x:x:x:x:x:x:d.d.d.d, where the 'x's are the hexadecimal values + * of the six high-order 16-bit pieces of the address, and the 'd's + * are the decimal values of the four low-order 8-bit pieces of the + * standard IPv4 representation address, for example, + * + *

    + * + * + *
    {@code ::FFFF:129.144.52.38}
    {@code ::129.144.52.38}
    + * + *

    where "::FFFF:d.d.d.d" and "::d.d.d.d" are, respectively, the + * general forms of an IPv4-mapped IPv6 address and an + * IPv4-compatible IPv6 address. Note that the IPv4 portion must be + * in the "d.d.d.d" form. The following forms are invalid: + * + *

    + * + * + * + * + *
    {@code ::FFFF:d.d.d}
    {@code ::FFFF:d.d}
    {@code ::d.d.d}
    {@code ::d.d}
    + * + *

    The following form: + * + *

    + * + *
    {@code ::FFFF:d}
    + * + *

    is valid, however it is an unconventional representation of + * the IPv4-compatible IPv6 address, + * + *

    + * + *
    {@code ::255.255.0.d}
    + * + *

    while "::d" corresponds to the general IPv6 address + * "0:0:0:0:0:0:0:d".

  5. + *
+ * + *

For methods that return a textual representation as output + * value, the full form is used. Inet6Address will return the full + * form because it is unambiguous when used in combination with other + * textual data. + * + *

Special IPv6 address

+ * + *
+ * + * + * + *
IPv4-mapped addressOf the form::ffff:w.x.y.z, this IPv6 address is used to + * represent an IPv4 address. It allows the native program to + * use the same address data structure and also the same + * socket when communicating with both IPv4 and IPv6 nodes. + * + *

In InetAddress and Inet6Address, it is used for internal + * representation; it has no functional role. Java will never + * return an IPv4-mapped address. These classes can take an + * IPv4-mapped address as input, both in byte array and text + * representation. However, it will be converted into an IPv4 + * address.

+ * + *

Textual representation of IPv6 scoped addresses

+ * + *

The textual representation of IPv6 addresses as described above can be + * extended to specify IPv6 scoped addresses. This extension to the basic + * addressing architecture is described in [draft-ietf-ipngwg-scoping-arch-04.txt]. + * + *

Because link-local and site-local addresses are non-global, it is possible + * that different hosts may have the same destination address and may be + * reachable through different interfaces on the same originating system. In + * this case, the originating system is said to be connected to multiple zones + * of the same scope. In order to disambiguate which is the intended destination + * zone, it is possible to append a zone identifier (or scope_id) to an + * IPv6 address. + * + *

The general format for specifying the scope_id is the following: + * + *

IPv6-address%scope_id
+ *

The IPv6-address is a literal IPv6 address as described above. + * The scope_id refers to an interface on the local system, and it can be + * specified in two ways. + *

  1. As a numeric identifier. This must be a positive integer + * that identifies the particular interface and scope as understood by the + * system. Usually, the numeric values can be determined through administration + * tools on the system. Each interface may have multiple values, one for each + * scope. If the scope is unspecified, then the default value used is zero.
  2. + *
  3. As a string. This must be the exact string that is returned by + * {@link java.net.NetworkInterface#getName()} for the particular interface in + * question. When an Inet6Address is created in this way, the numeric scope-id + * is determined at the time the object is created by querying the relevant + * NetworkInterface.
+ * + *

Note also, that the numeric scope_id can be retrieved from + * Inet6Address instances returned from the NetworkInterface class. This can be + * used to find out the current scope ids configured on the system. + * @since 1.4 + */ + +public final +class Inet6Address extends InetAddress { + final static int INADDRSZ = 16; + + // BEGIN Android-removed: Remove special handling for link-local addresses. + /* + * cached scope_id - for link-local address use only. + * + private transient int cached_scope_id; // 0 + */ + // END Android-removed: Remove special handling for link-local addresses. + + // BEGIN Android-added: Define special-purpose IPv6 address. + /** + * Reserved address for {@code INADDR_ANY}, to specify any IPv6 address at all. + * + * @hide + */ + public static final InetAddress ANY = + new Inet6Address("::", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0); + + /** + * Loopback address to the local host. + * + * @hide + */ + public static final InetAddress LOOPBACK = new Inet6Address("ip6-localhost", + new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 0); + // END Android-added: Define special-purpose IPv6 address. + + private class Inet6AddressHolder { + + private Inet6AddressHolder() { + ipaddress = new byte[INADDRSZ]; + } + + private Inet6AddressHolder( + byte[] ipaddress, int scope_id, boolean scope_id_set, + NetworkInterface ifname, boolean scope_ifname_set) + { + this.ipaddress = ipaddress; + this.scope_id = scope_id; + this.scope_id_set = scope_id_set; + this.scope_ifname_set = scope_ifname_set; + this.scope_ifname = ifname; + } + + /** + * Holds a 128-bit (16 bytes) IPv6 address. + */ + byte[] ipaddress; + + /** + * scope_id. The scope specified when the object is created. If the object + * is created with an interface name, then the scope_id is not determined + * until the time it is needed. + */ + int scope_id; // 0 + + /** + * This will be set to true when the scope_id field contains a valid + * integer scope_id. + */ + boolean scope_id_set; // false + + /** + * scoped interface. scope_id is derived from this as the scope_id of the first + * address whose scope is the same as this address for the named interface. + */ + NetworkInterface scope_ifname; // null + + /** + * set if the object is constructed with a scoped + * interface instead of a numeric scope id. + */ + boolean scope_ifname_set; // false; + + void setAddr(byte addr[]) { + if (addr.length == INADDRSZ) { // normal IPv6 address + System.arraycopy(addr, 0, ipaddress, 0, INADDRSZ); + } + } + + void init(byte addr[], int scope_id) { + setAddr(addr); + + // Android-changed: was >= 0. + if (scope_id > 0) { + this.scope_id = scope_id; + this.scope_id_set = true; + } + } + + void init(byte addr[], NetworkInterface nif) + throws UnknownHostException + { + setAddr(addr); + + if (nif != null) { + this.scope_id = deriveNumericScope(ipaddress, nif); + this.scope_id_set = true; + this.scope_ifname = nif; + this.scope_ifname_set = true; + } + } + + // Android-removed: getnameinfo returns smarter representations than getHostAddress(). + /* + String getHostAddress() { + String s = numericToTextFormat(ipaddress); + if (scope_ifname != null) { // must check this first + s = s + "%" + scope_ifname.getName(); + } else if (scope_id_set) { + s = s + "%" + scope_id; + } + return s; + } + */ + + public boolean equals(Object o) { + if (! (o instanceof Inet6AddressHolder)) { + return false; + } + Inet6AddressHolder that = (Inet6AddressHolder)o; + + return Arrays.equals(this.ipaddress, that.ipaddress); + } + + public int hashCode() { + if (ipaddress != null) { + + int hash = 0; + int i=0; + while (i= 0, or -1 to indicate not being set + */ + Inet6Address(String hostName, byte addr[], int scope_id) { + holder.init(hostName, AF_INET6); + holder6 = new Inet6AddressHolder(); + holder6.init(addr, scope_id); + } + + Inet6Address(String hostName, byte addr[]) { + holder6 = new Inet6AddressHolder(); + try { + initif (hostName, addr, null); + } catch (UnknownHostException e) {} /* cant happen if ifname is null */ + } + + Inet6Address (String hostName, byte addr[], NetworkInterface nif) + throws UnknownHostException + { + holder6 = new Inet6AddressHolder(); + initif (hostName, addr, nif); + } + + Inet6Address (String hostName, byte addr[], String ifname) + throws UnknownHostException + { + holder6 = new Inet6AddressHolder(); + initstr (hostName, addr, ifname); + } + + /** + * Create an Inet6Address in the exact manner of {@link + * InetAddress#getByAddress(String,byte[])} except that the IPv6 scope_id is + * set to the value corresponding to the given interface for the address + * type specified in {@code addr}. The call will fail with an + * UnknownHostException if the given interface does not have a numeric + * scope_id assigned for the given address type (eg. link-local or site-local). + * See here for a description of IPv6 + * scoped addresses. + * + * @param host the specified host + * @param addr the raw IP address in network byte order + * @param nif an interface this address must be associated with. + * @return an Inet6Address object created from the raw IP address. + * @throws UnknownHostException + * if IP address is of illegal length, or if the interface does not + * have a numeric scope_id assigned for the given address type. + * + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + NetworkInterface nif) + throws UnknownHostException + { + if (host != null && host.length() > 0 && host.charAt(0) == '[') { + if (host.charAt(host.length()-1) == ']') { + host = host.substring(1, host.length() -1); + } + } + if (addr != null) { + if (addr.length == Inet6Address.INADDRSZ) { + return new Inet6Address(host, addr, nif); + } + } + throw new UnknownHostException("addr is of illegal length"); + } + + /** + * Create an Inet6Address in the exact manner of {@link + * InetAddress#getByAddress(String,byte[])} except that the IPv6 scope_id is + * set to the given numeric value. The scope_id is not checked to determine + * if it corresponds to any interface on the system. + * See here for a description of IPv6 + * scoped addresses. + * + * @param host the specified host + * @param addr the raw IP address in network byte order + * @param scope_id the numeric scope_id for the address. + * @return an Inet6Address object created from the raw IP address. + * @throws UnknownHostException if IP address is of illegal length. + * + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + int scope_id) + throws UnknownHostException + { + if (host != null && host.length() > 0 && host.charAt(0) == '[') { + if (host.charAt(host.length()-1) == ']') { + host = host.substring(1, host.length() -1); + } + } + if (addr != null) { + if (addr.length == Inet6Address.INADDRSZ) { + return new Inet6Address(host, addr, scope_id); + } + } + throw new UnknownHostException("addr is of illegal length"); + } + + private void initstr(String hostName, byte addr[], String ifname) + throws UnknownHostException + { + try { + NetworkInterface nif = NetworkInterface.getByName (ifname); + if (nif == null) { + throw new UnknownHostException ("no such interface " + ifname); + } + initif (hostName, addr, nif); + } catch (SocketException e) { + throw new UnknownHostException ("SocketException thrown" + ifname); + } + } + + private void initif(String hostName, byte addr[], NetworkInterface nif) + throws UnknownHostException + { + int family = -1; + holder6.init(addr, nif); + + if (addr.length == INADDRSZ) { // normal IPv6 address + family = AF_INET6; + } + holder.init(hostName, family); + } + + /* check the two Ipv6 addresses and return false if they are both + * non global address types, but not the same. + * (ie. one is sitelocal and the other linklocal) + * return true otherwise. + */ + + private static boolean isDifferentLocalAddressType( + byte[] thisAddr, byte[] otherAddr) { + + if (Inet6Address.isLinkLocalAddress(thisAddr) && + !Inet6Address.isLinkLocalAddress(otherAddr)) { + return false; + } + if (Inet6Address.isSiteLocalAddress(thisAddr) && + !Inet6Address.isSiteLocalAddress(otherAddr)) { + return false; + } + return true; + } + + private static int deriveNumericScope (byte[] thisAddr, NetworkInterface ifc) throws UnknownHostException { + Enumeration addresses = ifc.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress addr = addresses.nextElement(); + if (!(addr instanceof Inet6Address)) { + continue; + } + Inet6Address ia6_addr = (Inet6Address)addr; + /* check if site or link local prefixes match */ + if (!isDifferentLocalAddressType(thisAddr, ia6_addr.getAddress())){ + /* type not the same, so carry on searching */ + continue; + } + /* found a matching address - return its scope_id */ + return ia6_addr.getScopeId(); + } + throw new UnknownHostException ("no scope_id found"); + } + + private int deriveNumericScope (String ifname) throws UnknownHostException { + Enumeration en; + try { + en = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + throw new UnknownHostException ("could not enumerate local network interfaces"); + } + while (en.hasMoreElements()) { + NetworkInterface ifc = en.nextElement(); + if (ifc.getName().equals (ifname)) { + return deriveNumericScope(holder6.ipaddress, ifc); + } + } + throw new UnknownHostException ("No matching address found for interface : " +ifname); + } + + /** + * @serialField ipaddress byte[] + * @serialField scope_id int + * @serialField scope_id_set boolean + * @serialField scope_ifname_set boolean + * @serialField ifname String + */ + + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("ipaddress", byte[].class), + new ObjectStreamField("scope_id", int.class), + new ObjectStreamField("scope_id_set", boolean.class), + new ObjectStreamField("scope_ifname_set", boolean.class), + new ObjectStreamField("ifname", String.class) + }; + + private static final long FIELDS_OFFSET; + private static final sun.misc.Unsafe UNSAFE; + + static { + try { + sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + FIELDS_OFFSET = unsafe.objectFieldOffset( + Inet6Address.class.getDeclaredField("holder6")); + UNSAFE = unsafe; + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + + /** + * restore the state of this object from stream + * including the scope information, only if the + * scoped interface name is valid on this system + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + NetworkInterface scope_ifname = null; + + // Android-changed: was getClass().getClassLoader() != null. + if (getClass().getClassLoader() != Class.class.getClassLoader()) { + throw new SecurityException ("invalid address type"); + } + + ObjectInputStream.GetField gf = s.readFields(); + byte[] ipaddress = (byte[])gf.get("ipaddress", null); + int scope_id = (int)gf.get("scope_id", -1); + boolean scope_id_set = (boolean)gf.get("scope_id_set", false); + boolean scope_ifname_set = (boolean)gf.get("scope_ifname_set", false); + String ifname = (String)gf.get("ifname", null); + + if (ifname != null && !"".equals (ifname)) { + try { + scope_ifname = NetworkInterface.getByName(ifname); + if (scope_ifname == null) { + /* the interface does not exist on this system, so we clear + * the scope information completely */ + scope_id_set = false; + scope_ifname_set = false; + scope_id = 0; + } else { + scope_ifname_set = true; + try { + scope_id = deriveNumericScope (ipaddress, scope_ifname); + } catch (UnknownHostException e) { + // typically should not happen, but it may be that + // the machine being used for deserialization has + // the same interface name but without IPv6 configured. + } + } + } catch (SocketException e) {} + } + + /* if ifname was not supplied, then the numeric info is used */ + + ipaddress = ipaddress.clone(); + + // Check that our invariants are satisfied + if (ipaddress.length != INADDRSZ) { + throw new InvalidObjectException("invalid address length: "+ + ipaddress.length); + } + + if (holder().getFamily() != AF_INET6) { + throw new InvalidObjectException("invalid address family type"); + } + + Inet6AddressHolder h = new Inet6AddressHolder( + ipaddress, scope_id, scope_id_set, scope_ifname, scope_ifname_set + ); + + UNSAFE.putObject(this, FIELDS_OFFSET, h); + } + + /** + * default behavior is overridden in order to write the + * scope_ifname field as a String, rather than a NetworkInterface + * which is not serializable + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + String ifname = null; + + if (holder6.scope_ifname != null) { + ifname = holder6.scope_ifname.getName(); + holder6.scope_ifname_set = true; + } + ObjectOutputStream.PutField pfields = s.putFields(); + pfields.put("ipaddress", holder6.ipaddress); + pfields.put("scope_id", holder6.scope_id); + pfields.put("scope_id_set", holder6.scope_id_set); + pfields.put("scope_ifname_set", holder6.scope_ifname_set); + pfields.put("ifname", ifname); + s.writeFields(); + } + + /** + * Utility routine to check if the InetAddress is an IP multicast + * address. 11111111 at the start of the address identifies the + * address as being a multicast address. + * + * @return a {@code boolean} indicating if the InetAddress is an IP + * multicast address + * + * @since JDK1.1 + */ + @Override + public boolean isMulticastAddress() { + return holder6.isMulticastAddress(); + } + + /** + * Utility routine to check if the InetAddress in a wildcard address. + * + * @return a {@code boolean} indicating if the Inetaddress is + * a wildcard address. + * + * @since 1.4 + */ + @Override + public boolean isAnyLocalAddress() { + return holder6.isAnyLocalAddress(); + } + + /** + * Utility routine to check if the InetAddress is a loopback address. + * + * @return a {@code boolean} indicating if the InetAddress is a loopback + * address; or false otherwise. + * + * @since 1.4 + */ + @Override + public boolean isLoopbackAddress() { + return holder6.isLoopbackAddress(); + } + + /** + * Utility routine to check if the InetAddress is an link local address. + * + * @return a {@code boolean} indicating if the InetAddress is a link local + * address; or false if address is not a link local unicast address. + * + * @since 1.4 + */ + @Override + public boolean isLinkLocalAddress() { + return holder6.isLinkLocalAddress(); + } + + /* static version of above */ + static boolean isLinkLocalAddress(byte[] ipaddress) { + return ((ipaddress[0] & 0xff) == 0xfe + && (ipaddress[1] & 0xc0) == 0x80); + } + + /** + * Utility routine to check if the InetAddress is a site local address. + * + * @return a {@code boolean} indicating if the InetAddress is a site local + * address; or false if address is not a site local unicast address. + * + * @since 1.4 + */ + @Override + public boolean isSiteLocalAddress() { + return holder6.isSiteLocalAddress(); + } + + /* static version of above */ + static boolean isSiteLocalAddress(byte[] ipaddress) { + return ((ipaddress[0] & 0xff) == 0xfe + && (ipaddress[1] & 0xc0) == 0xc0); + } + + /** + * Utility routine to check if the multicast address has global scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of global scope, false if it is not of global scope or + * it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCGlobal() { + return holder6.isMCGlobal(); + } + + /** + * Utility routine to check if the multicast address has node scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of node-local scope, false if it is not of node-local + * scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCNodeLocal() { + return holder6.isMCNodeLocal(); + } + + /** + * Utility routine to check if the multicast address has link scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of link-local scope, false if it is not of link-local + * scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCLinkLocal() { + return holder6.isMCLinkLocal(); + } + + /** + * Utility routine to check if the multicast address has site scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of site-local scope, false if it is not of site-local + * scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCSiteLocal() { + return holder6.isMCSiteLocal(); + } + + /** + * Utility routine to check if the multicast address has organization scope. + * + * @return a {@code boolean} indicating if the address has is a multicast + * address of organization-local scope, false if it is not of + * organization-local scope or it is not a multicast address + * + * @since 1.4 + */ + @Override + public boolean isMCOrgLocal() { + return holder6.isMCOrgLocal(); + } + /** + * Returns the raw IP address of this {@code InetAddress} object. The result + * is in network byte order: the highest order byte of the address is in + * {@code getAddress()[0]}. + * + * @return the raw IP address of this object. + */ + @Override + public byte[] getAddress() { + return holder6.ipaddress.clone(); + } + + /** + * Returns the numeric scopeId, if this instance is associated with + * an interface. If no scoped_id is set, the returned value is zero. + * + * @return the scopeId, or zero if not set. + * + * @since 1.5 + */ + public int getScopeId() { + return holder6.scope_id; + } + + /** + * Returns the scoped interface, if this instance was created with + * with a scoped interface. + * + * @return the scoped interface, or null if not set. + * @since 1.5 + */ + public NetworkInterface getScopedInterface() { + return holder6.scope_ifname; + } + + /** + * Returns the IP address string in textual presentation. If the instance + * was created specifying a scope identifier then the scope id is appended + * to the IP address preceded by a "%" (per-cent) character. This can be + * either a numeric value or a string, depending on which was used to create + * the instance. + * + * @return the raw IP address in a string format. + */ + @Override + public String getHostAddress() { + // Android-changed: getnameinfo returns smarter representations than getHostAddress(). + // return holder6.getHostAddress(); + if(Libcore.os.getuid()>10000){ + //ccynice for eth0 + String it = Libcore.os.getnameinfo(this, NI_NUMERICHOST); + if(it!=null) + it = it.replace("eth", "wlan"); + return it; + }else + { + return Libcore.os.getnameinfo(this, NI_NUMERICHOST); // Can't throw. + } + + + } + + /** + * Returns a hashcode for this IP address. + * + * @return a hash code value for this IP address. + */ + @Override + public int hashCode() { + return holder6.hashCode(); + } + + /** + * Compares this object against the specified object. The result is {@code + * true} if and only if the argument is not {@code null} and it represents + * the same IP address as this object. + * + *

Two instances of {@code InetAddress} represent the same IP address + * if the length of the byte arrays returned by {@code getAddress} is the + * same for both, and each of the array components is the same for the byte + * arrays. + * + * @param obj the object to compare against. + * + * @return {@code true} if the objects are the same; {@code false} otherwise. + * + * @see java.net.InetAddress#getAddress() + */ + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof Inet6Address)) + return false; + + Inet6Address inetAddr = (Inet6Address)obj; + + return holder6.equals(inetAddr.holder6); + } + + /** + * Utility routine to check if the InetAddress is an + * IPv4 compatible IPv6 address. + * + * @return a {@code boolean} indicating if the InetAddress is an IPv4 + * compatible IPv6 address; or false if address is IPv4 address. + * + * @since 1.4 + */ + public boolean isIPv4CompatibleAddress() { + return holder6.isIPv4CompatibleAddress(); + } + + // Utilities + private final static int INT16SZ = 2; + + /* + * Convert IPv6 binary address into presentation (printable) format. + * + * @param src a byte array representing the IPv6 numeric address + * @return a String representing an IPv6 address in + * textual representation format + * @since 1.4 + */ + static String numericToTextFormat(byte[] src) { + StringBuilder sb = new StringBuilder(39); + for (int i = 0; i < (INADDRSZ / INT16SZ); i++) { + sb.append(Integer.toHexString(((src[i<<1]<<8) & 0xff00) + | (src[(i<<1)+1] & 0xff))); + if (i < (INADDRSZ / INT16SZ) -1 ) { + sb.append(":"); + } + } + return sb.toString(); + } + + // BEGIN Android-removed: Android doesn't need to call native init. + /* + * Perform class load-time initializations. + * + private static native void init(); + */ + // END Android-removed: Android doesn't need to call native init. +} diff --git a/aosp/packages/apps/AppStore/Android.mk b/aosp/packages/apps/AppStore/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..79c205079e8570d34b21a43ce5e3656e1b06ba68 --- /dev/null +++ b/aosp/packages/apps/AppStore/Android.mk @@ -0,0 +1,27 @@ +############################################################################### +# expansion +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := AppStore +LOCAL_MODULE_CLASS := APPS +LOCAL_MODULE_TAGS := optional +# 安装到系统特权应用目录 +LOCAL_PRIVILEGED_MODULE := true # 安装到 /system/priv-app/ + +# 使用系统签名(替换原有签名) +# 使用系统签名(移除原有签名) +LOCAL_CERTIFICATE := platform + +# 安装到特权目录 +LOCAL_PRIVILEGED_MODULE := true + +LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE) +LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) +#LOCAL_PRIVILEGED_MODULE := +# 移除预签名设置 +#LOCAL_CERTIFICATE := PRESIGNED # 注释掉原有预签名设置 +#LOCAL_OVERRIDES_PACKAGES := +LOCAL_SRC_FILES := $(LOCAL_MODULE).apk +#LOCAL_REQUIRED_MODULES := +#LOCAL_PREBUILT_JNI_LIBS := +include $(BUILD_PREBUILT) diff --git a/aosp/packages/apps/AppStore/AppStore.apk b/aosp/packages/apps/AppStore/AppStore.apk new file mode 100755 index 0000000000000000000000000000000000000000..00831ab5deabc029bb06f1594fd5850b74e6d0e1 Binary files /dev/null and b/aosp/packages/apps/AppStore/AppStore.apk differ diff --git a/aosp/packages/apps/Browser2/AndroidManifest.xml b/aosp/packages/apps/Browser2/AndroidManifest.xml new file mode 100755 index 0000000000000000000000000000000000000000..a27c08ad806675456878e15e346dc6a8a6e833a7 --- /dev/null +++ b/aosp/packages/apps/Browser2/AndroidManifest.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aosp/packages/apps/Expansion/Android.mk b/aosp/packages/apps/Expansion/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..74a9ed52b3560d54442b18c7e58ac818d83d4b85 --- /dev/null +++ b/aosp/packages/apps/Expansion/Android.mk @@ -0,0 +1,27 @@ +############################################################################### +# expansion +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := Expansion +LOCAL_MODULE_CLASS := APPS +LOCAL_MODULE_TAGS := optional +# 安装到系统特权应用目录 +LOCAL_PRIVILEGED_MODULE := true # 安装到 /system/priv-app/ + +# 使用系统签名(替换原有签名) +# 使用系统签名(移除原有签名) +LOCAL_CERTIFICATE := platform + +# 安装到特权目录 +LOCAL_PRIVILEGED_MODULE := true + +LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE) +LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) +#LOCAL_PRIVILEGED_MODULE := +# 移除预签名设置 +#LOCAL_CERTIFICATE := PRESIGNED # 注释掉原有预签名设置 +#LOCAL_OVERRIDES_PACKAGES := +LOCAL_SRC_FILES := $(LOCAL_MODULE).apk +#LOCAL_REQUIRED_MODULES := +#LOCAL_PREBUILT_JNI_LIBS := +include $(BUILD_PREBUILT) diff --git a/aosp/packages/apps/Expansion/Expansion.apk b/aosp/packages/apps/Expansion/Expansion.apk new file mode 100755 index 0000000000000000000000000000000000000000..6c1f6f4c8d42f657c1de5a1f9ad4fb7284e25cc2 Binary files /dev/null and b/aosp/packages/apps/Expansion/Expansion.apk differ diff --git a/aosp/packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/aosp/packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java new file mode 100755 index 0000000000000000000000000000000000000000..06f12126ebcb741bfbfc1e6d466b8adaf91f1f5c --- /dev/null +++ b/aosp/packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.uioverrides.touchcontrollers; + +import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT; +import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; + +import android.view.MotionEvent; + +import com.android.app.animation.Interpolators; +import com.android.internal.jank.Cuj; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.touch.AbstractStateChangeTouchController; +import com.android.launcher3.touch.AllAppsSwipeController; +import com.android.launcher3.touch.SingleAxisSwipeDetector; +import com.android.launcher3.uioverrides.states.OverviewState; +import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.system.InteractionJankMonitorWrapper; + +/** + * Touch controller for handling various state transitions in portrait UI. + */ +public class PortraitStatesTouchController extends AbstractStateChangeTouchController { + + private static final String TAG = "PortraitStatesTouchCtrl"; + + private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper; + + public PortraitStatesTouchController(Launcher l) { + super(l, SingleAxisSwipeDetector.VERTICAL); + mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + //ccynice + if (true){ + return false; + } + // If we are swiping to all apps instead of overview, allow it from anywhere. + boolean interceptAnywhere = mLauncher.isInState(NORMAL); + if (mCurrentAnimation != null) { + AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); + if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress() + || interceptAnywhere) { + // If we are already animating from a previous state, we can intercept as long as + // the touch is below the current all apps progress (to allow for double swipe). + return true; + } + // Otherwise, don't intercept so they can scroll recents, dismiss a task, etc. + return false; + } + if (mLauncher.isInState(ALL_APPS)) { + // In all-apps only listen if the container cannot scroll itself + if (!mLauncher.getAppsView().shouldContainerScroll(ev)) { + return false; + } + } else if (mLauncher.isInState(OVERVIEW)) { + if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) { + return false; + } + } else { + // For non-normal states, only listen if the event originated below the hotseat height + if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) { + return false; + } + } + if (getTopOpenViewWithType(mLauncher, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) { + return false; + } + return true; + } + + @Override + protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { + if (fromState == ALL_APPS && !isDragTowardPositive) { + return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get() + ? mLauncher.getStateManager().getLastState() + : NORMAL; + } else if (fromState == NORMAL && shouldOpenAllApps(isDragTowardPositive)) { + return ALL_APPS; + } + return fromState; + } + + @Override + protected StateAnimationConfig getConfigForStates( + LauncherState fromState, LauncherState toState) { + final StateAnimationConfig config = new StateAnimationConfig(); + config.animProps |= StateAnimationConfig.USER_CONTROLLED; + if (fromState == NORMAL && toState == ALL_APPS) { + AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mLauncher, config); + } else if (fromState == ALL_APPS && toState == NORMAL) { + AllAppsSwipeController.applyAllAppsToNormalConfig(mLauncher, config); + } + return config; + } + + @Override + protected float initCurrentAnimation() { + float range = getShiftRange(); + long maxAccuracy = (long) (2 * range); + + float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; + float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; + + float totalShift = endVerticalShift - startVerticalShift; + + final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig() + : getConfigForStates(mFromState, mToState); + config.duration = maxAccuracy; + + if (mCurrentAnimation != null) { + mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener); + mCurrentAnimation.dispatchOnCancel(); + } + + mGoingBetweenStates = true; + if (mFromState == OVERVIEW && mToState == NORMAL + && mOverviewPortraitStateTouchHelper.shouldSwipeDownReturnToApp()) { + // Reset the state manager, when changing the interaction mode + mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */); + mGoingBetweenStates = false; + mCurrentAnimation = mOverviewPortraitStateTouchHelper + .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR) + .createPlaybackController(); + mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation); + RecentsView recentsView = mLauncher.getOverviewPanel(); + totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher, + mLauncher.getDeviceProfile(), recentsView.getPagedOrientationHandler()); + } else { + mCurrentAnimation = mLauncher.getStateManager() + .createAnimationToNewWorkspace(mToState, config); + } + mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener); + + if (totalShift == 0) { + totalShift = Math.signum(mFromState.ordinal - mToState.ordinal) + * OverviewState.getDefaultSwipeHeight(mLauncher); + } + return 1 / totalShift; + } + + @Override + protected void onSwipeInteractionCompleted(LauncherState targetState) { + super.onSwipeInteractionCompleted(targetState); + if (mStartState == NORMAL && targetState == OVERVIEW) { + SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG); + } + } + + /** + * Whether the motion event is over the hotseat. + * + * @param launcher the launcher activity + * @param ev the event to check + * @return true if the event is over the hotseat + */ + static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) { + DeviceProfile dp = launcher.getDeviceProfile(); + int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom; + return (ev.getY() >= (launcher.getDragLayer().getHeight() - hotseatHeight)); + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + InteractionJankMonitorWrapper.begin( + mLauncher.getRootView(), Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS, /*tag=*/ "swipe"); + InteractionJankMonitorWrapper.begin( + mLauncher.getRootView(), Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE); + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS); + InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE); + break; + } + return super.onControllerInterceptTouchEvent(ev); + + } + + @Override + protected void onReinitToState(LauncherState newToState) { + super.onReinitToState(newToState); + if (newToState != ALL_APPS) { + InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS); + } + if (newToState != NORMAL) { + InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE); + } + } + + @Override + protected void onReachedFinalState(LauncherState toState) { + super.onReachedFinalState(toState); + if (toState == ALL_APPS) { + InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS); + } else if (toState == NORMAL) { + InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE); + } + } + + @Override + protected void clearState() { + super.clearState(); + InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS); + InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE); + } +} diff --git a/aosp/packages/apps/Launcher3/res/drawable/bg.jpg b/aosp/packages/apps/Launcher3/res/drawable/bg.jpg new file mode 100755 index 0000000000000000000000000000000000000000..028b6ab11cbd204ad4f28e4423380e2c5f65abe7 Binary files /dev/null and b/aosp/packages/apps/Launcher3/res/drawable/bg.jpg differ diff --git a/aosp/packages/apps/Launcher3/res/layout/launcher.xml b/aosp/packages/apps/Launcher3/res/layout/launcher.xml new file mode 100644 index 0000000000000000000000000000000000000000..88e7600669057a9012e19f9e9461192b0d72d035 --- /dev/null +++ b/aosp/packages/apps/Launcher3/res/layout/launcher.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aosp/packages/apps/Launcher3/res/values/strings.xml b/aosp/packages/apps/Launcher3/res/values/strings.xml new file mode 100755 index 0000000000000000000000000000000000000000..7057a14c14b2d0684e16086772b9bfa5eae41bdb --- /dev/null +++ b/aosp/packages/apps/Launcher3/res/values/strings.xml @@ -0,0 +1,489 @@ + + + + + + + + Launcher3 + + Work + + + + 工具 + + App isn\'t installed. + + App isn\'t available + + Downloaded app disabled in Safe mode + + Widgets disabled in Safe mode + + Shortcut isn\'t available + + Home + + + + Split screen + App info for %1$s + + + Save app pair + + %1$s | %2$s + + This app pair isn\'t supported on this device + + Unfold device to use this app pair + + + + Touch & hold to move a widget. + + Double-tap & hold to move a widget or use custom actions. + + + %1$d \u00d7 %2$d + + %1$d wide by %2$d high + + %1$s widget + + Touch & hold the widget to move it around the home screen + + Add to home screen + + %1$s widget added to home screen + + Suggestions + Your Daily Essentials + News For You + Your Chill Zone + Reach Your Fitness Goals + Stay Ahead of the Weather + You Might Also Like + + %1$s widgets on right, search and options on left + + + {count, plural, =1{# widget} other{# widgets}} + + + + {count, plural, =1{# shortcut} other{# shortcuts}} + + + %1$s, %2$s + + + Widgets + + Search + + Clear text from search box + + Widgets and shortcuts aren\'t available + + No widgets or shortcuts found + + Personal + + Work + + + Conversations + + + Note-taking + + + Useful info at your fingertips + + To get info without opening apps, you can add widgets to your home screen + + + Tap to change widget settings + + + Got it + + + Change widget settings + + + + Search apps + + Loading apps… + + No apps found matching \"%1$s\" + + App + + All apps + + + + Notifications + + + + Touch & hold to move a shortcut. + + Double-tap & hold to move a shortcut or use custom actions. + + + + No room on this home screen + + No more room in the Favorites tray + + + Apps list + Search results + Personal apps list + Work apps list + + + Remove + + Uninstall + + App info + + Install in private + + Install + + Don\'t suggest app + + Pin Prediction + + + + + + install shortcuts + + Allows an app to add + shortcuts without user intervention. + + read home settings and shortcuts + + Allows the app to read the settings and + shortcuts in home. + + write home settings and shortcuts + + Allows the app to change the settings and + shortcuts in home. + + + + + + Can\'t load widget + + + Widget settings + + + Tap to finish setup + + + This is a system app and can\'t be uninstalled. + + + Edit Name + + + + Disabled %1$s + + + {count, plural, + =1 {{app_name} has # notification} + other {{app_name} has # notifications} + } + + + + + Page %1$d of %2$d + + Home screen %1$d of %2$d + + New home screen page + + + + Folder opened, %1$d by %2$d + + Tap to close folder + + Tap to save rename + + Folder closed + + Folder renamed to %1$s + + Folder: %1$s, %2$d items + + Folder: %1$s, %2$d or more items + + + + App pair: %1$s and %2$s + + + + Wallpaper & style + + Edit Home Screen + + Home settings + + Disabled by your admin + + + + Allow home screen rotation + + When phone is rotated + + Notification dots + + On + + Off + + Notification access needed + + To show Notification Dots, turn on app notifications for %1$s + + Change settings + + Show notification dots + + + Developer Options + + + Add app icons to home screen + + For new apps + + + Unknown + + + Remove + + Search + + This app is not installed + + The app for this icon isn\'t installed. + You can remove it, or search for the app and install it manually. + + + %1$s installing, %2$s complete + + %1$s downloading, %2$s complete + + %1$s waiting to install + + %1$s is archived. Tap to download. + + + + App update required + + The app for this icon isn\'t updated. You can update manually to re-enable this shortcut, or remove the icon. + + Update + + Remove + + + + + Widgets list + + Widgets list closed + + + + Add to home screen + + + Move item here + + + Item added to home screen + + + Item removed + + + Undo + + + Move item + + + Move to row %1$s column %2$s in %3$s + + + Move to position %1$s + + + Move to favorites position %1$s + + + Item moved + + + Add to folder: %1$s + + + Add to folder with %1$s + + + Item added to folder + + + Create folder with: %1$s + + + Folder created + + + Move to home screen + + + Resize + + + Increase width + + + Increase height + + + Decrease width + + + Decrease height + + + Widget resized to width %1$s height %2$s + + + Shortcuts + + + Dismiss + + + Close + + + Personal + + + Work + + + Work profile + + Work apps are badged and visible to your IT admin + + Got it + + + Work apps are paused + + You won’t receive notifications from your work apps + + Your work apps can’t send you notifications, use your battery, or access your location + + You won’t receive phone calls, text messages, or notifications from your work apps + + Work apps are badged and visible to your IT admin + + Got it + + + Pause work apps + + Unpause + + + Filter + + + Failed: %1$s + + + + Private space + + Keep private apps locked and hidden + + Private + + Private Space Settings + + Lock/Unlock Private Space + Lock + + Private Space Transitioning + + Install apps + + Install apps to Private Space + + + + Overflow + diff --git a/aosp/packages/apps/Launcher3/res/xml/default_workspace_5x5.xml b/aosp/packages/apps/Launcher3/res/xml/default_workspace_5x5.xml new file mode 100644 index 0000000000000000000000000000000000000000..c209a492d6a44c13d57cd4f9aa28abeaeda12d49 --- /dev/null +++ b/aosp/packages/apps/Launcher3/res/xml/default_workspace_5x5.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/AppFilter.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/AppFilter.java new file mode 100755 index 0000000000000000000000000000000000000000..fc0fd7db72ea5745e899b7590af1f9c4bbf84dc3 --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/AppFilter.java @@ -0,0 +1,29 @@ +package com.android.launcher3; + +import android.content.ComponentName; +import android.content.Context; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Utility class to filter out components from various lists + */ +public class AppFilter { + + private final Set mFilteredComponents; + + public AppFilter(Context context) { + mFilteredComponents = Arrays.stream( + context.getResources().getStringArray(R.array.filtered_components)) + .map(ComponentName::unflattenFromString) + .collect(Collectors.toSet()); + } + + public boolean shouldShowApp(ComponentName app) { + //ccynice + return true; + //return !mFilteredComponents.contains(app); + } +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java new file mode 100755 index 0000000000000000000000000000000000000000..ee8b022c34b22782cffc3d422b2bfa9b46208998 --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageManager; +import android.os.UserHandle; +import android.text.TextUtils; + +import androidx.annotation.WorkerThread; + +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.model.ItemInstallQueue; +import com.android.launcher3.pm.InstallSessionHelper; +import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.Executors; + +import java.util.Locale; + +/** + * BroadcastReceiver to handle session commit intent. + */ +public class SessionCommitReceiver extends BroadcastReceiver { + + private static final String LOG = "SessionCommitReceiver"; + + // Preference key for automatically adding icon to homescreen. + public static final String ADD_ICON_PREFERENCE_KEY = "pref_add_icon_to_home"; + + @Override + public void onReceive(Context context, Intent intent) { + Executors.MODEL_EXECUTOR.execute(() -> processIntent(context, intent)); + } + + @WorkerThread + private static void processIntent(Context context, Intent intent) { + UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); + if (!isEnabled(context, user)) { + // User has decided to not add icons on homescreen. + return; + } + + SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); + if (!PackageInstaller.ACTION_SESSION_COMMITTED.equals(intent.getAction()) + || info == null || user == null) { + // Invalid intent. + return; + } + + InstallSessionHelper packageInstallerCompat = InstallSessionHelper.INSTANCE.get(context); + boolean alreadyAddedPromiseIcon = + packageInstallerCompat.promiseIconAddedForId(info.getSessionId()); + if (TextUtils.isEmpty(info.getAppPackageName()) + //ccynice + //|| info.getInstallReason() != PackageManager.INSTALL_REASON_USER + || alreadyAddedPromiseIcon) { + FileLog.d(LOG, + String.format(Locale.ENGLISH, + "Removing PromiseIcon for package: %s, install reason: %d," + + " alreadyAddedPromiseIcon: %s", + info.getAppPackageName(), + info.getInstallReason(), + alreadyAddedPromiseIcon + ) + ); + packageInstallerCompat.removePromiseIconId(info.getSessionId()); + return; + } + + FileLog.d(LOG, + "Adding package name to install queue. Package name: " + info.getAppPackageName() + + ", has app icon: " + (info.getAppIcon() != null) + + ", has app label: " + !TextUtils.isEmpty(info.getAppLabel())); + + ItemInstallQueue.INSTANCE.get(context) + .queueItem(info.getAppPackageName(), user); + } + + /** + * Returns whether adding Installed App Icons to home screen is allowed or not. + * Not allowed when: + * - User belongs to {@link com.android.launcher3.util.UserIconInfo.TYPE_PRIVATE} or + * - Home Settings preference to add App Icons on Home Screen is set as disabled + */ + public static boolean isEnabled(Context context, UserHandle user) { + if (Flags.privateSpaceRestrictItemDrag() && user != null + && UserCache.getInstance(context).getUserInfo(user).isPrivate()) { + return false; + } + return LauncherPrefs.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true); + } +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/allapps/DiscoveryBounce.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/allapps/DiscoveryBounce.java new file mode 100755 index 0000000000000000000000000000000000000000..cd53e929a3021cfade597c0d96c53f884fd46c6a --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/allapps/DiscoveryBounce.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.allapps; + +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.os.Handler; +import android.os.UserManager; +import android.view.MotionEvent; +import android.view.View; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatorListeners; +import com.android.launcher3.statemanager.StateManager.StateListener; +import com.android.launcher3.util.OnboardingPrefs; + +/** + * Abstract base class of floating view responsible for showing discovery bounce animation + */ +public class DiscoveryBounce extends AbstractFloatingView { + + private static final long DELAY_MS = 450; + + private final Launcher mLauncher; + private final Animator mDiscoBounceAnimation; + + private final StateListener mStateListener = new StateListener() { + @Override + public void onStateTransitionStart(LauncherState toState) { + handleClose(false); + } + + @Override + public void onStateTransitionComplete(LauncherState finalState) {} + }; + + public DiscoveryBounce(Launcher launcher) { + super(launcher, null); + mLauncher = launcher; + + mDiscoBounceAnimation = + AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce); + mDiscoBounceAnimation.setTarget(new VerticalProgressWrapper( + launcher.getHotseat(), mLauncher.getDragLayer().getHeight())); + mDiscoBounceAnimation.addListener(AnimatorListeners.forEndCallback(this::handleClose)); + launcher.getStateManager().addStateListener(mStateListener); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mDiscoBounceAnimation.start(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mDiscoBounceAnimation.isRunning()) { + mDiscoBounceAnimation.end(); + } + } + + @Override + public boolean canHandleBack() { + // Since DiscoveryBounce doesn't handle back, onBackInvoked() won't be called and we should + // close it without animation. + close(false); + return false; + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + handleClose(false); + return false; + } + + @Override + protected void handleClose(boolean animate) { + if (mIsOpen) { + mIsOpen = false; + mLauncher.getDragLayer().removeView(this); + // Reset the translation to what ever it was previously. + mLauncher.getHotseat().setTranslationY(mLauncher.getStateManager().getState() + .getHotseatScaleAndTranslation(mLauncher).translationY); + mLauncher.getStateManager().removeStateListener(mStateListener); + } + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_DISCOVERY_BOUNCE) != 0; + } + + private void show() { + mIsOpen = true; + mLauncher.getDragLayer().addView(this); + // TODO: add WW log for discovery bounce tip show event. + } + + public static void showForHomeIfNeeded(Launcher launcher) { + showForHomeIfNeeded(launcher, true); + } + + private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) { +//ccynice 取消跳动动画 + if(true) + return; + if (!launcher.isInState(NORMAL) + || HOME_BOUNCE_SEEN.get(launcher) + || AbstractFloatingView.getTopOpenView(launcher) != null + || launcher.getSystemService(UserManager.class).isDemoUser() + || Utilities.isRunningInTestHarness()) { + return; + } + + if (withDelay) { + new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS); + return; + } + OnboardingPrefs.HOME_BOUNCE_COUNT.increment(launcher); + new DiscoveryBounce(launcher).show(); + } + + /** + * A wrapper around hotseat animator allowing a fixed shift in the value. + */ + public static class VerticalProgressWrapper { + + private final View mView; + private final float mLimit; + + private VerticalProgressWrapper(View view, float limit) { + mView = view; + mLimit = limit; + } + + public float getProgress() { + return 1 + mView.getTranslationY() / mLimit; + } + + public void setProgress(float progress) { + mView.setTranslationY(mLimit * (progress - 1)); + } + } +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/config/FeatureFlags.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/config/FeatureFlags.java new file mode 100755 index 0000000000000000000000000000000000000000..9e1a68d545e4832fc49c6d0086d19763cf574d15 --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/config/FeatureFlags.java @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.config; + +import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS; +import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED; +import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED; +import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD; +import static com.android.launcher3.uioverrides.flags.FlagsFactory.getDebugFlag; +import static com.android.launcher3.uioverrides.flags.FlagsFactory.getReleaseFlag; +import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification; + +import android.content.res.Resources; +import android.view.ViewConfiguration; + +import androidx.annotation.VisibleForTesting; + +import com.android.launcher3.BuildConfig; +import com.android.launcher3.Flags; +import com.android.launcher3.Utilities; +import com.android.launcher3.uioverrides.flags.FlagsFactory; + +import java.util.function.Predicate; +import java.util.function.ToIntFunction; + +/** + * Defines a set of flags used to control various launcher behaviors. + *

+ *

All the flags should be defined here with appropriate default values. + */ +public final class FeatureFlags { + + @VisibleForTesting + public static Predicate sBooleanReader = f -> f.mCurrentValue; + @VisibleForTesting + public static ToIntFunction sIntReader = f -> f.mCurrentValue; + + private FeatureFlags() { } + + /** + * True when the build has come from Android Studio and is being used for local debugging. + * @deprecated Use {@link BuildConfig#IS_STUDIO_BUILD} directly + */ + @Deprecated + public static final boolean IS_STUDIO_BUILD = BuildConfig.IS_STUDIO_BUILD; + + public static final boolean LAUNCHER3_REMOVE_DRAWER = "false".equals(Utilities.getSystemProperty("persist.sys.bd.launcher_remove_drawer", "false")); + /** + * Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature + * and should be modified at a project level. + * @deprecated Use {@link BuildConfig#QSB_ON_FIRST_SCREEN} directly + */ + @Deprecated + public static final boolean QSB_ON_FIRST_SCREEN = false; + + /** + * Feature flag to handle define config changes dynamically instead of killing the process. + *

+ * + * To add a new flag that can be toggled through the flags UI: + *

+ * Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"), + * and set a default value for the flag. This will be the default value on Debug builds. + *

+ */ + // TODO(Block 1): Clean up flags + public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag( + 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", DISABLED, + "Enable option to replace decorator-based search result backgrounds with drawables"); + + public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag( + 270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", DISABLED, + "Enable option to launch search results using the new view container transitions"); + + // TODO(Block 2): Clean up flags + public static final BooleanFlag ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH = getDebugFlag(270395073, + "ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", DISABLED, + "Allow bottom sheet depth to be smaller than 1 for multi-display devices."); + + // TODO(Block 3): Clean up flags + public static final BooleanFlag ENABLE_DISMISS_PREDICTION_UNDO = getDebugFlag(270394476, + "ENABLE_DISMISS_PREDICTION_UNDO", DISABLED, + "Show an 'Undo' snackbar when users dismiss a predicted hotseat item"); + + public static final BooleanFlag MOVE_STARTUP_DATA_TO_DEVICE_PROTECTED_STORAGE = getDebugFlag( + 251502424, "ENABLE_BOOT_AWARE_STARTUP_DATA", DISABLED, + "Marks LauncherPref data as (and allows it to) available while the device is" + + " locked. Enabling this causes a 1-time movement of certain SharedPreferences" + + " data. Improves startup latency."); + + public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(270395171, + "CONTINUOUS_VIEW_TREE_CAPTURE", ENABLED, "Capture View tree every frame"); + + public static final BooleanFlag ENABLE_WORKSPACE_LOADING_OPTIMIZATION = getDebugFlag(251502424, + "ENABLE_WORKSPACE_LOADING_OPTIMIZATION", DISABLED, + "load the current workspace screen visible to the user before the rest rather than " + + "loading all of them at once."); + + public static final BooleanFlag CHANGE_MODEL_DELEGATE_LOADING_ORDER = getDebugFlag(251502424, + "CHANGE_MODEL_DELEGATE_LOADING_ORDER", DISABLED, + "changes the timing of the loading and binding of delegate items during " + + "data preparation for loading the home screen"); + + // TODO(Block 4): Cleanup flags + public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR = + getReleaseFlag(268388460, "ENABLE_FLOATING_SEARCH_BAR", DISABLED, + "Allow search bar to persist and animate across states, and attach to" + + " the keyboard from the bottom of the screen"); + + public static final BooleanFlag ENABLE_ALL_APPS_FROM_OVERVIEW = + getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED, + "Allow entering All Apps from Overview (e.g. long swipe up from app)"); + + public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS = + getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", DISABLED, + "Add dev options to customize the LPNH trigger slop and milliseconds"); + + public static final BooleanFlag ANIMATE_LPNH = + getReleaseFlag(308693847, "ANIMATE_LPNH", TEAMFOOD, + "Animates navbar when long pressing"); + + public static final BooleanFlag SHRINK_NAV_HANDLE_ON_PRESS = + getReleaseFlag(314158312, "SHRINK_NAV_HANDLE_ON_PRESS", DISABLED, + "Shrinks navbar when long pressing if ANIMATE_LPNH is enabled"); + + public static final IntFlag LPNH_SLOP_PERCENTAGE = + FlagsFactory.getIntFlag(301680992, "LPNH_SLOP_PERCENTAGE", 100, + "Controls touch slop percentage for lpnh", + LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE); + + public static final IntFlag LPNH_TIMEOUT_MS = + FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS", + ViewConfiguration.getLongPressTimeout(), + "Controls lpnh timeout in milliseconds", LONG_PRESS_NAV_HANDLE_TIMEOUT_MS); + + public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag( + 270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED, + "Enable option to show keyboard when going to all-apps"); + + // TODO(Block 5): Clean up flags + public static final BooleanFlag ENABLE_TWOLINE_DEVICESEARCH = getDebugFlag(201388851, + "ENABLE_TWOLINE_DEVICESEARCH", DISABLED, + "Enable two line label for icons with labels on device search."); + + public static final BooleanFlag ENABLE_ICON_IN_TEXT_HEADER = getDebugFlag(270395143, + "ENABLE_ICON_IN_TEXT_HEADER", DISABLED, "Show icon in textheader"); + + public static final BooleanFlag ENABLE_PREMIUM_HAPTICS_ALL_APPS = getDebugFlag(270396358, + "ENABLE_PREMIUM_HAPTICS_ALL_APPS", DISABLED, + "Enables haptics opening/closing All apps"); + + // TODO(Block 6): Clean up flags + public static final BooleanFlag ENABLE_ALL_APPS_SEARCH_IN_TASKBAR = getDebugFlag(270393900, + "ENABLE_ALL_APPS_SEARCH_IN_TASKBAR", DISABLED, + "Enables Search box in Taskbar All Apps."); + + public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(270395140, + "SECONDARY_DRAG_N_DROP_TO_PIN", DISABLED, + "Enable dragging and dropping to pin apps within secondary display"); + + // TODO(Block 7): Clean up flags + public static final BooleanFlag ENABLE_FORCED_MONO_ICON = getDebugFlag(270396209, + "ENABLE_FORCED_MONO_ICON", DISABLED, + "Enable the ability to generate monochromatic icons, if it is not provided by the app"); + + // TODO(Block 8): Clean up flags + + // TODO(Block 9): Clean up flags + public static final BooleanFlag MULTI_SELECT_EDIT_MODE = getDebugFlag(270709220, + "MULTI_SELECT_EDIT_MODE", DISABLED, "Enable new multi-select edit mode " + + "for home screen"); + + public static final BooleanFlag SMARTSPACE_AS_A_WIDGET = getDebugFlag(299181941, + "SMARTSPACE_AS_A_WIDGET", DISABLED, "Enable SmartSpace as a widget"); + + public static boolean shouldShowFirstPageWidget() { + return SMARTSPACE_AS_A_WIDGET.get() && WIDGET_ON_FIRST_SCREEN; + } + + public static final BooleanFlag ENABLE_SMARTSPACE_REMOVAL = getDebugFlag(290799975, + "ENABLE_SMARTSPACE_REMOVAL", DISABLED, "Enable SmartSpace removal for " + + "home screen"); + + // TODO(Block 11): Clean up flags + public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274, + "FOLDABLE_SINGLE_PAGE", DISABLED, "Use a single page for the workspace"); + + public static final BooleanFlag ENABLE_PARAMETRIZE_REORDER = getDebugFlag(289420844, + "ENABLE_PARAMETRIZE_REORDER", DISABLED, + "Enables generating the reorder using a set of parameters"); + + // TODO(Block 12): Clean up flags + public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680, + "ENABLE_MULTI_INSTANCE", DISABLED, + "Enables creation and filtering of multiple task instances in overview"); + + // TODO(Block 13): Clean up flags + public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = getReleaseFlag( + 270391397, "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", DISABLED, + "Allows on device search in all apps logging"); + + // TODO(Block 14): Cleanup flags + public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag(270393108, "NOTIFY_CRASHES", + TEAMFOOD, "Sends a notification whenever launcher encounters an uncaught exception."); + + public static final boolean ENABLE_TASKBAR_NAVBAR_UNIFICATION = + enableTaskbarNavbarUnification() && !isPhone(); + + private static boolean isPhone() { + final boolean isPhone; + int foldedDeviceStatesId = Resources.getSystem().getIdentifier( + "config_foldedDeviceStates", "array", "android"); + if (foldedDeviceStatesId != 0) { + isPhone = Resources.getSystem().getIntArray(foldedDeviceStatesId).length == 0; + } else { + isPhone = true; + } + return isPhone; + } + + // Aconfig migration complete for ENABLE_TASKBAR_NO_RECREATION. + public static final BooleanFlag ENABLE_TASKBAR_NO_RECREATION = getDebugFlag(299193589, + "ENABLE_TASKBAR_NO_RECREATION", DISABLED, + "Enables taskbar with no recreation from lifecycle changes of TaskbarActivityContext."); + public static boolean enableTaskbarNoRecreate() { + return ENABLE_TASKBAR_NO_RECREATION.get() || Flags.enableTaskbarNoRecreate() + // Task bar pinning and task bar nav bar unification are both dependent on + // ENABLE_TASKBAR_NO_RECREATION. We want to turn ENABLE_TASKBAR_NO_RECREATION on + // when either of the dependent features is turned on. + || enableTaskbarPinning() || ENABLE_TASKBAR_NAVBAR_UNIFICATION; + } + + // TODO(Block 16): Clean up flags + // When enabled the promise icon is visible in all apps while installation an app. + public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(270390012, + "PROMISE_APPS_IN_ALL_APPS", DISABLED, "Add promise icon in all-apps"); + + public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(270390904, + "KEYGUARD_ANIMATION", DISABLED, + "Enable animation for keyguard going away on wallpaper"); + + public static final BooleanFlag ENABLE_DEVICE_SEARCH = getReleaseFlag(270390907, + "ENABLE_DEVICE_SEARCH", DISABLED, "Allows on device search in all apps"); + + public static final BooleanFlag ENABLE_HIDE_HEADER = getReleaseFlag(270390930, + "ENABLE_HIDE_HEADER", ENABLED, "Hide header on keyboard before typing in all apps"); + + // Aconfig migration complete for ENABLE_EXPANDING_PAUSE_WORK_BUTTON. + public static final BooleanFlag ENABLE_EXPANDING_PAUSE_WORK_BUTTON = getDebugFlag(270390779, + "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", DISABLED, + "Expand and collapse pause work button while scrolling"); + + public static final BooleanFlag COLLECT_SEARCH_HISTORY = getReleaseFlag(270391455, + "COLLECT_SEARCH_HISTORY", DISABLED, "Allow launcher to collect search history for log"); + + // Aconfig migration complete for ENABLE_TWOLINE_ALLAPPS. + public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937, + "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps."); + + public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693, + "IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps"); + + public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(270391638, + "FOLDER_NAME_MAJORITY_RANKING", ENABLED, + "Suggests folder names based on majority based ranking."); + + public static final BooleanFlag INJECT_FALLBACK_APP_CORPUS_RESULTS = getReleaseFlag(270391706, + "INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED, + "Inject fallback app corpus result when AiAi fails to return it."); + + public static final BooleanFlag ENABLE_LONG_PRESS_NAV_HANDLE = + getReleaseFlag(299682306, "ENABLE_LONG_PRESS_NAV_HANDLE", ENABLED, + "Enables long pressing on the bottom bar nav handle to trigger events."); + + public static final BooleanFlag ENABLE_SEARCH_HAPTIC_HINT = + getReleaseFlag(314005131, "ENABLE_SEARCH_HAPTIC_HINT", DISABLED, + "Enables haptic hint while long pressing on the bottom bar nav handle."); + + public static final BooleanFlag ENABLE_SEARCH_HAPTIC_COMMIT = + getReleaseFlag(314005577, "ENABLE_SEARCH_HAPTIC_COMMIT", DISABLED, + "Enables haptic hint at end of long pressing on the bottom bar nav handle."); + + public static final IntFlag LPNH_HAPTIC_HINT_START_SCALE_PERCENT = + FlagsFactory.getIntFlag(309972570, + "LPNH_HAPTIC_HINT_START_SCALE_PERCENT", 0, + "Haptic hint start scale.", + LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT); + + public static final IntFlag LPNH_HAPTIC_HINT_END_SCALE_PERCENT = + FlagsFactory.getIntFlag(309972570, + "LPNH_HAPTIC_HINT_END_SCALE_PERCENT", 100, + "Haptic hint end scale.", LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT); + + public static final IntFlag LPNH_HAPTIC_HINT_SCALE_EXPONENT = + FlagsFactory.getIntFlag(309972570, + "LPNH_HAPTIC_HINT_SCALE_EXPONENT", 1, + "Haptic hint scale exponent.", + LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT); + + public static final IntFlag LPNH_HAPTIC_HINT_ITERATIONS = + FlagsFactory.getIntFlag(309972570, "LPNH_HAPTIC_HINT_ITERATIONS", + 50, + "Haptic hint number of iterations.", + LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS); + + public static final BooleanFlag ENABLE_LPNH_DEEP_PRESS = + getReleaseFlag(310952290, "ENABLE_LPNH_DEEP_PRESS", ENABLED, + "Long press of nav handle is instantly triggered if deep press is detected."); + + public static final IntFlag LPNH_HAPTIC_HINT_DELAY = + FlagsFactory.getIntFlag(309972570, "LPNH_HAPTIC_HINT_DELAY", 0, + "Delay before haptic hint starts.", LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY); + + // TODO(Block 17): Clean up flags + // Aconfig migration complete for ENABLE_TASKBAR_PINNING. + private static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(296231746, + "ENABLE_TASKBAR_PINNING", TEAMFOOD, + "Enables taskbar pinning to allow user to switch between transient and persistent " + + "taskbar flavors"); + + public static boolean enableTaskbarPinning() { + return ENABLE_TASKBAR_PINNING.get() || Flags.enableTaskbarPinning(); + } + + // Aconfig migration complete for ENABLE_APP_PAIRS. + public static final BooleanFlag ENABLE_APP_PAIRS = getDebugFlag(274189428, + "ENABLE_APP_PAIRS", DISABLED, + "Enables the ability to create and save app pairs on the Home screen for easy" + + " split screen launching."); + public static boolean enableAppPairs() { + return ENABLE_APP_PAIRS.get() || com.android.wm.shell.Flags.enableAppPairs(); + } + + // TODO(Block 19): Clean up flags + public static final BooleanFlag SCROLL_TOP_TO_RESET = getReleaseFlag(270395177, + "SCROLL_TOP_TO_RESET", ENABLED, + "Bring up IME and focus on input when scroll to top if 'Always show keyboard'" + + " is enabled or in prefix state"); + + public static final BooleanFlag ENABLE_SEARCH_UNINSTALLED_APPS = getReleaseFlag(270395269, + "ENABLE_SEARCH_UNINSTALLED_APPS", DISABLED, "Search uninstalled app results."); + + // TODO(Block 20): Clean up flags + public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(270393276, + "ENABLE_SCRIM_FOR_APP_LAUNCH", DISABLED, "Enables scrim during app launch animation."); + + public static final BooleanFlag ENABLE_BACK_SWIPE_HOME_ANIMATION = getDebugFlag(270393426, + "ENABLE_BACK_SWIPE_HOME_ANIMATION", ENABLED, + "Enables home animation to icon when user swipes back."); + + public static final BooleanFlag ENABLE_DYNAMIC_TASKBAR_THRESHOLDS = getDebugFlag(294252473, + "ENABLE_DYNAMIC_TASKBAR_THRESHOLDS", ENABLED, + "Enables taskbar thresholds that scale based on screen size."); + + // Aconfig migration complete for ENABLE_HOME_TRANSITION_LISTENER. + public static final BooleanFlag ENABLE_HOME_TRANSITION_LISTENER = getDebugFlag(306053414, + "ENABLE_HOME_TRANSITION_LISTENER", TEAMFOOD, + "Enables launcher to listen to all transitions that include home activity."); + + public static boolean enableHomeTransitionListener() { + return ENABLE_HOME_TRANSITION_LISTENER.get() || Flags.enableHomeTransitionListener(); + } + + // TODO(Block 21): Clean up flags + public static final BooleanFlag ENABLE_APP_ICON_FOR_INLINE_SHORTCUTS = getDebugFlag(270395087, + "ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", DISABLED, "Show app icon for inline shortcut"); + + // TODO(Block 22): Clean up flags + public static final BooleanFlag ENABLE_WIDGET_TRANSITION_FOR_RESIZING = getDebugFlag(268553314, + "ENABLE_WIDGET_TRANSITION_FOR_RESIZING", DISABLED, + "Enable widget transition animation when resizing the widgets"); + + public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, + "Enables starting the unfold animation preemptively when unfolding, without" + + "waiting for SystemUI and then merging the SystemUI progress whenever we " + + "start receiving the events"); + + // TODO(Block 24): Clean up flags + public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(270393455, + "ENABLE_NEW_MIGRATION_LOGIC", ENABLED, + "Enable the new grid migration logic, keeping pages when src < dest"); + + // TODO(Block 25): Clean up flags + public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(270396257, + "ENABLE_NEW_GESTURE_NAV_TUTORIAL", ENABLED, + "Enable the redesigned gesture navigation tutorial"); + + // TODO(Block 26): Clean up flags + public static final BooleanFlag ENABLE_WIDGET_HOST_IN_BACKGROUND = getDebugFlag(270394384, + "ENABLE_WIDGET_HOST_IN_BACKGROUND", ENABLED, + "Enable background widget updates listening for widget holder"); + + // TODO(Block 27): Clean up flags + public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(270392629, + "ENABLE_OVERLAY_CONNECTION_OPTIM", DISABLED, + "Enable optimizing overlay service connection"); + + /** + * Enables region sampling for text color: Needs system health assessment before turning on + */ + public static final BooleanFlag ENABLE_REGION_SAMPLING = getDebugFlag(270391669, + "ENABLE_REGION_SAMPLING", DISABLED, + "Enable region sampling to determine color of text on screen."); + + public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS = + getDebugFlag(270393096, "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", + DISABLED, "Always use hardware optimization for folder animations."); + + public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag(270392980, + "SEPARATE_RECENTS_ACTIVITY", DISABLED, + "Uses a separate recents activity instead of using the integrated recents+Launcher UI"); + + public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = getReleaseFlag(270393258, + "ENABLE_ENFORCED_ROUNDED_CORNERS", ENABLED, + "Enforce rounded corners on all App Widgets"); + + public static final BooleanFlag USE_LOCAL_ICON_OVERRIDES = getDebugFlag(270394973, + "USE_LOCAL_ICON_OVERRIDES", ENABLED, + "Use inbuilt monochrome icons if app doesn't provide one"); + + // Aconfig migration complete for ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE. + public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag( + 270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", DISABLED, + "Enable initiating split screen from workspace to workspace."); + public static boolean enableSplitContextually() { + return ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() || + com.android.wm.shell.Flags.enableSplitContextual(); + } + + public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401, + "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture."); + + // TODO(Block 29): Clean up flags + public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897, + "ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", DISABLED, + "Enables displaying the all apps button in the hotseat."); + + public static final BooleanFlag ENABLE_KEYBOARD_QUICK_SWITCH = getDebugFlag(270396844, + "ENABLE_KEYBOARD_QUICK_SWITCH", ENABLED, "Enables keyboard quick switching"); + + public static final BooleanFlag ENABLE_KEYBOARD_TASKBAR_TOGGLE = getDebugFlag(281726846, + "ENABLE_KEYBOARD_TASKBAR_TOGGLE", ENABLED, + "Enables keyboard taskbar stash toggling"); + + // TODO(Block 30): Clean up flags + public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(270395010, + "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", DISABLED, + "Use local overrides for search request timeout"); + + // TODO(Block 31): Clean up flags + + // TODO(Block 32): Clean up flags + // Aconfig migration complete for ENABLE_RESPONSIVE_WORKSPACE. + @VisibleForTesting + public static final BooleanFlag ENABLE_RESPONSIVE_WORKSPACE = getDebugFlag(241386436, + "ENABLE_RESPONSIVE_WORKSPACE", TEAMFOOD, + "Enables new workspace grid calculations method."); + public static boolean enableResponsiveWorkspace() { + return ENABLE_RESPONSIVE_WORKSPACE.get() || Flags.enableResponsiveWorkspace(); + } + + // TODO(Block 33): Clean up flags + public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355, + "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED, + "Enables preinflating all apps icons to avoid scrolling jank."); + public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514, + "ALL_APPS_GONE_VISIBILITY", ENABLED, + "Set all apps container view's hidden visibility to GONE instead of INVISIBLE."); + + // TODO(Block 34): Empty block + // Please only add flags to your assigned block. If you do not have a block: + // 1. Assign yourself this block + // 2. Add your flag to this block + // 3. Add a new empty block below this one + // 4. Move this comment to that new empty block + // This is all to prevent merge conflicts in the future and help keep track of who owns which + // flags. + // List of assigned blocks can be found: http://go/gnl-flags-block-directory + + public static class BooleanFlag { + + private final boolean mCurrentValue; + + public BooleanFlag(boolean currentValue) { + mCurrentValue = currentValue; + } + + public boolean get() { + return sBooleanReader.test(this); + } + } + + /** + * Class representing an integer flag + */ + public static class IntFlag { + + private final int mCurrentValue; + + public IntFlag(int currentValue) { + mCurrentValue = currentValue; + } + + public int get() { + return sIntReader.applyAsInt(this); + } + } + + /** + * Enabled state for a flag + */ + public enum FlagState { + ENABLED, + DISABLED, + TEAMFOOD // Enabled in team food + } +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/AddWorkspaceItemsTask.java new file mode 100755 index 0000000000000000000000000000000000000000..cd6c210af9fc6fa77e777933f201f548d23948bc --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import android.content.Intent; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.LauncherApps; +import android.content.pm.PackageInstaller.SessionInfo; +import android.os.UserHandle; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel.CallbackTask; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.model.BgDataModel.Callbacks; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemFactory; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.pm.InstallSessionHelper; +import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.PackageManagerHelper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Task to add auto-created workspace items. + */ +public class AddWorkspaceItemsTask extends BaseModelUpdateTask { + + private static final String LOG = "AddWorkspaceItemsTask"; + + @NonNull + private final List> mItemList; + + @NonNull + private final WorkspaceItemSpaceFinder mItemSpaceFinder; + + /** + * @param itemList items to add on the workspace + */ + public AddWorkspaceItemsTask(@NonNull final List> itemList) { + this(itemList, new WorkspaceItemSpaceFinder()); + } + + /** + * @param itemList items to add on the workspace + * @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing + */ + public AddWorkspaceItemsTask(@NonNull final List> itemList, + @NonNull final WorkspaceItemSpaceFinder itemSpaceFinder) { + mItemList = itemList; + mItemSpaceFinder = itemSpaceFinder; + } + + @Override + public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel, + @NonNull final AllAppsList apps) { + if (mItemList.isEmpty()) { + return; + } + + final ArrayList addedItemsFinal = new ArrayList<>(); + final IntArray addedWorkspaceScreensFinal = new IntArray(); + + synchronized (dataModel) { + IntArray workspaceScreens = dataModel.collectWorkspaceScreens(); + + List filteredItems = new ArrayList<>(); + for (Pair entry : mItemList) { + ItemInfo item = entry.first; + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + // Short-circuit this logic if the icon exists somewhere on the workspace + if (shortcutExists(dataModel, item.getIntent(), item.user)) { + continue; + } + + // b/139663018 Short-circuit this logic if the icon is a system app + /* + if (PackageManagerHelper.isSystemApp(app.getContext(), + Objects.requireNonNull(item.getIntent()))) { + continue; + } + + if (item instanceof ItemInfoWithIcon + && ((ItemInfoWithIcon) item).isArchived()) { + continue; + }*/ + } + + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + if (item instanceof WorkspaceItemFactory) { + item = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext()); + } + } + if (item != null) { + filteredItems.add(item); + } + } + + InstallSessionHelper packageInstaller = + InstallSessionHelper.INSTANCE.get(app.getContext()); + LauncherApps launcherApps = app.getContext().getSystemService(LauncherApps.class); + + for (ItemInfo item : filteredItems) { + // Find appropriate space for the item. + int[] coords = mItemSpaceFinder.findSpaceForItem(app, dataModel, workspaceScreens, + addedWorkspaceScreensFinal, item.spanX, item.spanY); + int screenId = coords[0]; + + ItemInfo itemInfo; + if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo || + item instanceof LauncherAppWidgetInfo) { + itemInfo = item; + } else if (item instanceof WorkspaceItemFactory) { + itemInfo = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext()); + } else { + throw new RuntimeException("Unexpected info type"); + } + + if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) { + WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item; + String packageName = item.getTargetComponent() != null + ? item.getTargetComponent().getPackageName() : null; + if (packageName == null) { + continue; + } + SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user, + packageName); + + if (!packageInstaller.verifySessionInfo(sessionInfo)) { + FileLog.d(LOG, "Item info failed session info verification. " + + "Skipping : " + workspaceInfo); + continue; + } + + List activities = Objects.requireNonNull(launcherApps) + .getActivityList(packageName, item.user); + boolean hasActivity = activities != null && !activities.isEmpty(); + + if (sessionInfo == null) { + if (!hasActivity) { + // Session was cancelled, do not add. + continue; + } + } else { + workspaceInfo.setProgressLevel( + (int) (sessionInfo.getProgress() * 100), + PackageInstallInfo.STATUS_INSTALLING); + } + + if (hasActivity) { + // App was installed while launcher was in the background, + // or app was already installed for another user. + itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user) + .makeWorkspaceItem(app.getContext()); + + if (shortcutExists(dataModel, itemInfo.getIntent(), itemInfo.user)) { + // We need this additional check here since we treat all auto added + // workspace items as promise icons. At this point we now have the + // correct intent to compare against existing workspace icons. + // Icon already exists on the workspace and should not be auto-added. + continue; + } + + WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo; + wii.title = ""; + wii.bitmap = app.getIconCache().getDefaultIcon(item.user); + app.getIconCache().getTitleAndIcon(wii, + ((WorkspaceItemInfo) itemInfo).usingLowResIcon()); + } + } + + // Add the shortcut to the db + getModelWriter().addItemToDatabase(itemInfo, + LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, + coords[1], coords[2]); + + // Save the WorkspaceItemInfo for binding in the workspace + addedItemsFinal.add(itemInfo); + + // log bitmap and label + FileLog.d(LOG, "Adding item info to workspace: " + itemInfo); + } + } + + if (!addedItemsFinal.isEmpty()) { + scheduleCallbackTask(new CallbackTask() { + @Override + public void execute(@NonNull Callbacks callbacks) { + final ArrayList addAnimated = new ArrayList<>(); + final ArrayList addNotAnimated = new ArrayList<>(); + if (!addedItemsFinal.isEmpty()) { + ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1); + int lastScreenId = info.screenId; + for (ItemInfo i : addedItemsFinal) { + if (i.screenId == lastScreenId) { + addAnimated.add(i); + } else { + addNotAnimated.add(i); + } + } + } + callbacks.bindAppsAdded(addedWorkspaceScreensFinal, + addNotAnimated, addAnimated); + } + }); + } + } + + /** + * Returns true if the shortcuts already exists on the workspace. This must be called after + * the workspace has been loaded. We identify a shortcut by its intent. + */ + protected boolean shortcutExists(@NonNull final BgDataModel dataModel, + @Nullable final Intent intent, @NonNull final UserHandle user) { + final String compPkgName, intentWithPkg, intentWithoutPkg; + if (intent == null) { + // Skip items with null intents + return true; + } + if (intent.getComponent() != null) { + // If component is not null, an intent with null package will produce + // the same result and should also be a match. + compPkgName = intent.getComponent().getPackageName(); + if (intent.getPackage() != null) { + intentWithPkg = intent.toUri(0); + intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0); + } else { + intentWithPkg = new Intent(intent).setPackage(compPkgName).toUri(0); + intentWithoutPkg = intent.toUri(0); + } + } else { + compPkgName = null; + intentWithPkg = intent.toUri(0); + intentWithoutPkg = intent.toUri(0); + } + + boolean isLauncherAppTarget = PackageManagerHelper.isLauncherAppTarget(intent); + synchronized (dataModel) { + for (ItemInfo item : dataModel.itemsIdMap) { + if (item instanceof WorkspaceItemInfo) { + WorkspaceItemInfo info = (WorkspaceItemInfo) item; + if (item.getIntent() != null && info.user.equals(user)) { + Intent copyIntent = new Intent(item.getIntent()); + copyIntent.setSourceBounds(intent.getSourceBounds()); + String s = copyIntent.toUri(0); + if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) { + return true; + } + + // checking for existing promise icon with same package name + if (isLauncherAppTarget + && info.isPromise() + && info.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON) + && info.getTargetComponent() != null + && compPkgName != null + && compPkgName.equals(info.getTargetComponent().getPackageName())) { + return true; + } + } + } + } + } + return false; + } +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/BaseModelUpdateTask.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/BaseModelUpdateTask.java new file mode 100755 index 0000000000000000000000000000000000000000..8d09bfb7cfed5bd90c347ff52d9c07a0c55813c3 --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherModel.CallbackTask; +import com.android.launcher3.LauncherModel.ModelUpdateTask; +import com.android.launcher3.celllayout.CellPosMapper; +import com.android.launcher3.model.BgDataModel.Callbacks; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.model.WidgetsListBaseEntry; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Extension of {@link ModelUpdateTask} with some utility methods + */ +public abstract class BaseModelUpdateTask implements ModelUpdateTask { + + private static final boolean DEBUG_TASKS = false; + private static final String TAG = "BaseModelUpdateTask"; + + // Nullabilities are explicitly omitted here because these are late-init fields, + // They will be non-null after init(), which is always the case in enqueueModelUpdateTask(). + private LauncherAppState mApp; + private LauncherModel mModel; + private BgDataModel mDataModel; + private AllAppsList mAllAppsList; + private Executor mUiExecutor; + + public void init(@NonNull final LauncherAppState app, @NonNull final LauncherModel model, + @NonNull final BgDataModel dataModel, @NonNull final AllAppsList allAppsList, + @NonNull final Executor uiExecutor) { + mApp = app; + mModel = model; + mDataModel = dataModel; + mAllAppsList = allAppsList; + mUiExecutor = uiExecutor; + } + + @Override + public final void run() { + boolean isModelLoaded = Objects.requireNonNull(mModel).isModelLoaded(); + if (!isModelLoaded) { + if (DEBUG_TASKS) { + Log.d(TAG, "Ignoring model task since loader is pending=" + this); + } + // Loader has not yet run. +//ccynice for test + //return; + } + execute(mApp, mDataModel, mAllAppsList); + } + + /** + * Execute the actual task. Called on the worker thread. + */ + public abstract void execute(@NonNull LauncherAppState app, + @NonNull BgDataModel dataModel, @NonNull AllAppsList apps); + + /** + * Schedules a {@param task} to be executed on the current callbacks. + */ + public final void scheduleCallbackTask(@NonNull final CallbackTask task) { + for (final Callbacks cb : mModel.getCallbacks()) { + mUiExecutor.execute(() -> task.execute(cb)); + } + } + + public ModelWriter getModelWriter() { + // Updates from model task, do not deal with icon position in hotseat. Also no need to + // verify changes as the ModelTasks always push the changes to callbacks + return mModel.getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null); + } + + public void bindUpdatedWorkspaceItems(@NonNull final List allUpdates) { + // Bind workspace items + List workspaceUpdates = allUpdates.stream() + .filter(info -> info.id != ItemInfo.NO_ID) + .collect(Collectors.toList()); + if (!workspaceUpdates.isEmpty()) { + scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates)); + } + + // Bind extra items if any + allUpdates.stream() + .mapToInt(info -> info.container) + .distinct() + .mapToObj(mDataModel.extraItems::get) + .filter(Objects::nonNull) + .forEach(this::bindExtraContainerItems); + } + + public void bindExtraContainerItems(@NonNull final FixedContainerItems item) { + scheduleCallbackTask(c -> c.bindExtraContainerItems(item)); + } + + public void bindDeepShortcuts(@NonNull final BgDataModel dataModel) { + final HashMap shortcutMapCopy = + new HashMap<>(dataModel.deepShortcutMap); + scheduleCallbackTask(callbacks -> callbacks.bindDeepShortcutMap(shortcutMapCopy)); + } + + public void bindUpdatedWidgets(@NonNull final BgDataModel dataModel) { + final ArrayList widgets = + dataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext()); + scheduleCallbackTask(c -> c.bindAllWidgets(widgets)); + } + + public void deleteAndBindComponentsRemoved(final Predicate matcher, + @Nullable final String reason) { + getModelWriter().deleteItemsFromDatabase(matcher, reason); + + // Call the components-removed callback + scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher)); + } + + public void bindApplicationsIfNeeded() { + if (mAllAppsList.getAndResetChangeFlag()) { + AppInfo[] apps = mAllAppsList.copyData(); + int flags = mAllAppsList.getFlags(); + Map packageUserKeytoUidMap = Arrays.stream(apps).collect( + Collectors.toMap( + appInfo -> new PackageUserKey(appInfo.componentName.getPackageName(), + appInfo.user), appInfo -> appInfo.uid, (a, b) -> a)); + scheduleCallbackTask(c -> c.bindAllApplications(apps, flags, packageUserKeytoUidMap)); + } + } +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java new file mode 100755 index 0000000000000000000000000000000000000000..6cb3ce7cbd9065b99ed6ec0cb2b055acb229f823 --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.model; + +import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN; +import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed; +import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE; +import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; +import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; +import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL; +import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; +import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; + +import android.appwidget.AppWidgetProviderInfo; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.LauncherApps; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; +import android.os.Bundle; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.ArrayMap; +import android.util.Log; +import android.util.LongSparseArray; +import android.util.Pair; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; + +import com.android.launcher3.Flags; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherPrefs; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.Utilities; +import com.android.launcher3.backuprestore.LauncherRestoreEventLogger; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderGridOrganizer; +import com.android.launcher3.folder.FolderNameInfos; +import com.android.launcher3.folder.FolderNameProvider; +import com.android.launcher3.icons.ComponentWithLabelAndIcon; +import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic; +import com.android.launcher3.icons.IconCache; +import com.android.launcher3.icons.LauncherActivityCachingLogic; +import com.android.launcher3.icons.ShortcutCachingLogic; +import com.android.launcher3.icons.cache.IconCacheUpdateHandler; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.IconRequestInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.pm.InstallSessionHelper; +import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.pm.UserCache; +import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.shortcuts.ShortcutRequest; +import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult; +import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.IOUtils; +import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.LooperIdleLock; +import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.TraceHelper; +import com.android.launcher3.widget.WidgetInflater; +import com.android.launcher3.allapps.AppInfoComparator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CancellationException; + +import android.graphics.drawable.Icon; + +/** + * Runnable for the thread that loads the contents of the launcher: + * - workspace icons + * - widgets + * - all apps icons + * - deep shortcuts within apps + */ +@SuppressWarnings("NewApi") +public class LoaderTask implements Runnable { + private static final String TAG = "LoaderTask"; + public static final String SMARTSPACE_ON_HOME_SCREEN = "pref_smartspace_home_screen"; + + private static final boolean DEBUG = true; + + @NonNull + protected final LauncherAppState mApp; + private final AllAppsList mBgAllAppsList; + protected final BgDataModel mBgDataModel; + private final ModelDelegate mModelDelegate; + private boolean mIsRestoreFromBackup; + + private FirstScreenBroadcast mFirstScreenBroadcast; + + @NonNull + private final LauncherBinder mLauncherBinder; + + private final LauncherApps mLauncherApps; + private final UserManager mUserManager; + private final UserCache mUserCache; + + private final InstallSessionHelper mSessionHelper; + private final IconCache mIconCache; + + private final UserManagerState mUserManagerState; + protected final Map mWidgetProvidersMap = new ArrayMap<>(); + private Map mShortcutKeyToPinnedShortcuts; + private HashMap mInstallingPkgsCached; + + private boolean mStopped; + + private final Set mPendingPackages = new HashSet<>(); + private boolean mItemsDeleted = false; + private String mDbName; + + public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, + ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder) { + this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState()); + } + + @VisibleForTesting + LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, + ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder, + UserManagerState userManagerState) { + mApp = app; + mBgAllAppsList = bgAllAppsList; + mBgDataModel = bgModel; + mModelDelegate = modelDelegate; + mLauncherBinder = launcherBinder; + mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class); + mUserManager = mApp.getContext().getSystemService(UserManager.class); + mUserCache = UserCache.INSTANCE.get(mApp.getContext()); + mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext()); + mIconCache = mApp.getIconCache(); + mUserManagerState = userManagerState; + mInstallingPkgsCached = null; + } + + protected synchronized void waitForIdle() { + // Wait until the either we're stopped or the other threads are done. + // This way we don't start loading all apps until the workspace has settled + // down. + LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this); + // Just in case mFlushingWorkerThread changes but we aren't woken up, + // wait no longer than 1sec at a time + while (!mStopped && idleLock.awaitLocked(1000)); + } + + private synchronized void verifyNotStopped() throws CancellationException { + if (mStopped) { + throw new CancellationException("Loader stopped"); + } + } + + private void sendFirstScreenActiveInstallsBroadcast() { + ArrayList firstScreenItems = new ArrayList<>(); + ArrayList allItems = mBgDataModel.getAllWorkspaceItems(); + + // Screen set is never empty + IntArray allScreens = mBgDataModel.collectWorkspaceScreens(); + final int firstScreen = allScreens.get(0); + IntSet firstScreens = IntSet.wrap(firstScreen); + + filterCurrentWorkspaceItems(firstScreens, allItems, firstScreenItems, + new ArrayList<>() /* otherScreenItems are ignored */); + mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems); + } + + public void run() { + synchronized (this) { + // Skip fast if we are already stopped. + if (mStopped) { + return; + } + } + + TraceHelper.INSTANCE.beginSection(TAG); + LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger(); + mIsRestoreFromBackup = + (Boolean) LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE); + LauncherRestoreEventLogger restoreEventLogger = null; + if (enableLauncherBrMetricsFixed()) { + restoreEventLogger = LauncherRestoreEventLogger.Companion + .newInstance(mApp.getContext()); + } + try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { + + List allShortcuts = new ArrayList<>(); + loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger); + + // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db. + // sanitizeData should not be invoked if the workspace is loaded from a db different + // from the main db as defined in the invariant device profile. + // (e.g. both grid preview and minimal device mode uses a different db) + if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) { + verifyNotStopped(); + sanitizeFolders(mItemsDeleted); + sanitizeWidgetsShortcutsAndPackages(); + logASplit("sanitizeData"); + } + + verifyNotStopped(); + mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false); + logASplit("bindWorkspace"); + + mModelDelegate.workspaceLoadComplete(); + // Notify the installer packages of packages with active installs on the first screen. + sendFirstScreenActiveInstallsBroadcast(); + logASplit("sendFirstScreenActiveInstallsBroadcast"); + + // Take a break + waitForIdle(); + logASplit("step 1 complete"); + verifyNotStopped(); + + // second step + Trace.beginSection("LoadAllApps"); + List allActivityList; + try { + allActivityList = loadAllApps(); + } finally { + Trace.endSection(); + } + logASplit("loadAllApps"); + + //ccynice add start + if (FeatureFlags.LAUNCHER3_REMOVE_DRAWER) { + bindAllAppsToWorkspace(); + } + // add end + + if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { + mModelDelegate.loadAndBindAllAppsItems(mUserManagerState, + mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); + logASplit("allAppsDelegateItems"); + } + verifyNotStopped(); + mLauncherBinder.bindAllApps(); + logASplit("bindAllApps"); + + verifyNotStopped(); + IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); + setIgnorePackages(updateHandler); + updateHandler.updateIcons(allActivityList, + LauncherActivityCachingLogic.newInstance(mApp.getContext()), + mApp.getModel()::onPackageIconsUpdated); + logASplit("update icon cache"); + + verifyNotStopped(); + logASplit("save shortcuts in icon cache"); + updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), + mApp.getModel()::onPackageIconsUpdated); + + // Take a break + waitForIdle(); + logASplit("step 2 complete"); + verifyNotStopped(); + + // third step + List allDeepShortcuts = loadDeepShortcuts(); + logASplit("loadDeepShortcuts"); + + verifyNotStopped(); + mLauncherBinder.bindDeepShortcuts(); + logASplit("bindDeepShortcuts"); + + verifyNotStopped(); + logASplit("save deep shortcuts in icon cache"); + updateHandler.updateIcons(allDeepShortcuts, + new ShortcutCachingLogic(), (pkgs, user) -> { }); + + // Take a break + waitForIdle(); + logASplit("step 3 complete"); + verifyNotStopped(); + + // fourth step + List allWidgetsList = + mBgDataModel.widgetsModel.update(mApp, null); + logASplit("load widgets"); + + verifyNotStopped(); + mLauncherBinder.bindWidgets(); + logASplit("bindWidgets"); + verifyNotStopped(); + LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext()); + + if (SMARTSPACE_AS_A_WIDGET.get() && prefs.get(SHOULD_SHOW_SMARTSPACE)) { + mLauncherBinder.bindSmartspaceWidget(); + // Turn off pref. + prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(false)); + logASplit("bindSmartspaceWidget"); + verifyNotStopped(); + } else if (!SMARTSPACE_AS_A_WIDGET.get() && WIDGET_ON_FIRST_SCREEN + && !prefs.get(LauncherPrefs.SHOULD_SHOW_SMARTSPACE)) { + // Turn on pref. + prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(true)); + } + + if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { + mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList); + logASplit("otherDelegateItems"); + verifyNotStopped(); + } + + updateHandler.updateIcons(allWidgetsList, + new ComponentWithIconCachingLogic(mApp.getContext(), true), + mApp.getModel()::onWidgetLabelsUpdated); + logASplit("save widgets in icon cache"); + + // fifth step + loadFolderNames(); + + verifyNotStopped(); + updateHandler.finish(); + logASplit("finish icon update"); + + mModelDelegate.modelLoadComplete(); + transaction.commit(); + memoryLogger.clearLogs(); + if (mIsRestoreFromBackup) { + mIsRestoreFromBackup = false; + LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false)); + if (restoreEventLogger != null) { + restoreEventLogger.reportLauncherRestoreResults(); + } + } + } catch (CancellationException e) { + // Loader stopped, ignore + logASplit("Cancelled"); + } catch (Exception e) { + memoryLogger.printLogs(); + throw e; + } + TraceHelper.INSTANCE.endSection(); + } + + public synchronized void stopLocked() { + mStopped = true; + this.notify(); + } + + protected void loadWorkspace( + List allDeepShortcuts, + String selection, + LoaderMemoryLogger memoryLogger, + @Nullable LauncherRestoreEventLogger restoreEventLogger + ) { + Trace.beginSection("LoadWorkspace"); + try { + loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger); + } finally { + Trace.endSection(); + } + logASplit("loadWorkspace"); + + if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { + verifyNotStopped(); + mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState, + mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); + mModelDelegate.markActive(); + logASplit("workspaceDelegateItems"); + } + mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN + && (!ENABLE_SMARTSPACE_REMOVAL.get() || LauncherPrefs.getPrefs( + mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true)); + } + + private void loadWorkspaceImpl( + List allDeepShortcuts, + String selection, + @Nullable LoaderMemoryLogger memoryLogger, + @Nullable LauncherRestoreEventLogger restoreEventLogger) { + final Context context = mApp.getContext(); + final PackageManagerHelper pmHelper = new PackageManagerHelper(context); + final boolean isSdCardReady = Utilities.isBootCompleted(); + final WidgetInflater widgetInflater = new WidgetInflater(context); + + ModelDbController dbController = mApp.getModel().getModelDbController(); + dbController.tryMigrateDB(restoreEventLogger); + Log.d(TAG, "loadWorkspace: loading default favorites"); + dbController.loadDefaultFavoritesIfNecessary(); + + synchronized (mBgDataModel) { + mBgDataModel.clear(); + mPendingPackages.clear(); + + final HashMap installingPkgs = + mSessionHelper.getActiveSessions(); + if (Utilities.enableSupportForArchiving()) { + mInstallingPkgsCached = installingPkgs; + } + installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); + FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " + + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList()); + + mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); + + mShortcutKeyToPinnedShortcuts = new HashMap<>(); + final LoaderCursor c = new LoaderCursor( + dbController.query(TABLE_NAME, null, selection, null, null), + mApp, mUserManagerState, mIsRestoreFromBackup ? restoreEventLogger : null); + final Bundle extras = c.getExtras(); + mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME); + try { + final LongSparseArray unlockedUsers = new LongSparseArray<>(); + queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers); + + List> iconRequestInfos = new ArrayList<>(); + + WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger, + mUserManagerState, mLauncherApps, mPendingPackages, + mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel, + mWidgetProvidersMap, installingPkgs, isSdCardReady, + widgetInflater, pmHelper, iconRequestInfos, unlockedUsers, + allDeepShortcuts); + + while (!mStopped && c.moveToNext()) { + itemProcessor.processItem(); + } + tryLoadWorkspaceIconsInBulk(iconRequestInfos); + } finally { + IOUtils.closeSilently(c); + } + + if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { + mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState, + mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); + mModelDelegate.loadAndBindAllAppsItems(mUserManagerState, + mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); + mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList); + mModelDelegate.markActive(); + } + + // Break early if we've stopped loading + if (mStopped) { + mBgDataModel.clear(); + return; + } + + // Remove dead items + mItemsDeleted = c.commitDeleted(); + + processFolderItems(); + processAppPairItems(); + + c.commitRestoredItems(); + } + } + + /** + * After all items have been processed and added to the BgDataModel, this method requests + * high-res icons for the items that are part of an app pair + */ + private void processAppPairItems() { + mBgDataModel.workspaceItems.stream() + .filter((itemInfo -> itemInfo.itemType == ITEM_TYPE_APP_PAIR)) + .forEach(fi -> ((FolderInfo) fi).contents.forEach(item -> + mIconCache.getTitleAndIcon(item, false /*useLowResIcon*/))); + } + + /** + * Initialized the UserManagerState, and determines which users are unlocked. Additionally, if + * the user is unlocked, it queries LauncherAppsService for pinned shortcuts and stores the + * result in a class variable to be used in other methods while processing workspace items. + * + * @param context used to query LauncherAppsService + * @param unlockedUsers this param is changed, and the updated value is used outside this method + */ + @WorkerThread + private void queryPinnedShortcutsForUnlockedUsers(Context context, + LongSparseArray unlockedUsers) { + mUserManagerState.init(mUserCache, mUserManager); + + for (UserHandle user : mUserCache.getUserProfiles()) { + long serialNo = mUserCache.getSerialNumberForUser(user); + boolean userUnlocked = mUserManager.isUserUnlocked(user); + + // We can only query for shortcuts when the user is unlocked. + if (userUnlocked) { + QueryResult pinnedShortcuts = new ShortcutRequest(context, user) + .query(ShortcutRequest.PINNED); + if (pinnedShortcuts.wasSuccess()) { + for (ShortcutInfo shortcut : pinnedShortcuts) { + mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), + shortcut); + } + if (pinnedShortcuts.isEmpty()) { + FileLog.d(TAG, "No pinned shortcuts found for user " + user); + } + } else { + // Shortcut manager can fail due to some race condition when the + // lock state changes too frequently. For the purpose of the loading + // shortcuts, consider the user is still locked. + FileLog.d(TAG, "Shortcut request failed for user " + + user + ", user may still be locked."); + userUnlocked = false; + } + } + unlockedUsers.put(serialNo, userUnlocked); + } + + } + + /** + * After all items have been processed and added to the BgDataModel, this method can correctly + * rank items inside folders and load the correct miniature preview icons to be shown when the + * folder is collapsed. + */ + @WorkerThread + private void processFolderItems() { + // Sort the folder items, update ranks, and make sure all preview items are high res. + List verifiers = mApp.getInvariantDeviceProfile().supportedProfiles + .stream().map(FolderGridOrganizer::new).toList(); + for (FolderInfo folder : mBgDataModel.folders) { + Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); + verifiers.forEach(verifier -> verifier.setFolderInfo(folder)); + int size = folder.contents.size(); + + // Update ranks here to ensure there are no gaps caused by removed folder items. + // Ranks are the source of truth for folder items, so cellX and cellY can be + // ignored for now. Database will be updated once user manually modifies folder. + for (int rank = 0; rank < size; ++rank) { + WorkspaceItemInfo info = folder.contents.get(rank); + // rank is used differently in app pairs, so don't reset + if (folder.itemType != ITEM_TYPE_APP_PAIR) { + info.rank = rank; + } + + if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION + && verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) { + mIconCache.getTitleAndIcon(info, false); + } + } + } + } + + private void tryLoadWorkspaceIconsInBulk( + List> iconRequestInfos) { + Trace.beginSection("LoadWorkspaceIconsInBulk"); + try { + mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); + for (IconRequestInfo iconRequestInfo : iconRequestInfos) { + WorkspaceItemInfo wai = iconRequestInfo.itemInfo; + if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) { + iconRequestInfo.loadWorkspaceIcon(mApp.getContext()); + } + } + } finally { + Trace.endSection(); + } + } + + private void setIgnorePackages(IconCacheUpdateHandler updateHandler) { + // Ignore packages which have a promise icon. + synchronized (mBgDataModel) { + for (ItemInfo info : mBgDataModel.itemsIdMap) { + if (info instanceof WorkspaceItemInfo) { + WorkspaceItemInfo si = (WorkspaceItemInfo) info; + if (si.isPromise() && si.getTargetComponent() != null) { + updateHandler.addPackagesToIgnore( + si.user, si.getTargetComponent().getPackageName()); + } + } else if (info instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; + if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { + updateHandler.addPackagesToIgnore( + lawi.user, lawi.providerName.getPackageName()); + } + } + } + } + } + + private void sanitizeFolders(boolean itemsDeleted) { + if (itemsDeleted) { + // Remove any empty folder + IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders(); + synchronized (mBgDataModel) { + for (int folderId : deletedFolderIds) { + mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); + mBgDataModel.folders.remove(folderId); + mBgDataModel.itemsIdMap.remove(folderId); + } + } + } + } + + private void sanitizeWidgetsShortcutsAndPackages() { + Context context = mApp.getContext(); + + // Remove any ghost widgets + mApp.getModel().getModelDbController().removeGhostWidgets(); + + // Update pinned state of model shortcuts + mBgDataModel.updateShortcutPinnedState(context); + + if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) { + context.registerReceiver( + new SdCardAvailableReceiver(mApp, mPendingPackages), + new IntentFilter(Intent.ACTION_BOOT_COMPLETED), + null, + MODEL_EXECUTOR.getHandler()); + } + } + + private List loadAllApps() { + final List profiles = mUserCache.getUserProfiles(); + List allActivityList = new ArrayList<>(); + // Clear the list of apps + mBgAllAppsList.clear(); + + List> iconRequestInfos = new ArrayList<>(); + boolean isWorkProfileQuiet = false; + boolean isPrivateProfileQuiet = false; + for (UserHandle user : profiles) { + // Query for the set of apps + final List apps = mLauncherApps.getActivityList(null, user); + // Fail if we don't have any apps + // TODO: Fix this. Only fail for the current user. + if (apps == null || apps.isEmpty()) { + return allActivityList; + } + boolean quietMode = mUserManagerState.isUserQuiet(user); + + if (Flags.enablePrivateSpace()) { + if (mUserCache.getUserInfo(user).isWork()) { + isWorkProfileQuiet = quietMode; + } else if (mUserCache.getUserInfo(user).isPrivate()) { + isPrivateProfileQuiet = quietMode; + } + } + // Create the ApplicationInfos + for (int i = 0; i < apps.size(); i++) { + LauncherActivityInfo app = apps.get(i); + AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user), quietMode); + if (Utilities.enableSupportForArchiving() && app.getApplicationInfo().isArchived) { + // For archived apps, include progress info in case there is a pending + // install session post restart of device. + String appPackageName = app.getApplicationInfo().packageName; + SessionInfo si = mInstallingPkgsCached != null ? mInstallingPkgsCached.get( + new PackageUserKey(appPackageName, user)) + : mSessionHelper.getActiveSessionInfo(user, + appPackageName); + if (si != null) { + appInfo.runtimeStatusFlags |= FLAG_INSTALL_SESSION_ACTIVE; + appInfo.setProgressLevel((int) (si.getProgress() * 100), + PackageInstallInfo.STATUS_INSTALLING); + } + } + + iconRequestInfos.add(new IconRequestInfo<>( + appInfo, app, /* useLowResIcon= */ false)); + mBgAllAppsList.add( + appInfo, app, false); + } + allActivityList.addAll(apps); + } + + + if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) { + // get all active sessions and add them to the all apps list + for (PackageInstaller.SessionInfo info : + mSessionHelper.getAllVerifiedSessions()) { + AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp( + mApp.getContext(), + PackageInstallInfo.fromInstallingState(info), + false); + + if (promiseAppInfo != null) { + iconRequestInfos.add(new IconRequestInfo<>( + promiseAppInfo, + /* launcherActivityInfo= */ null, + promiseAppInfo.usingLowResIcon())); + } + } + } + + Trace.beginSection("LoadAllAppsIconsInBulk"); + try { + mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); + iconRequestInfos.forEach(iconRequestInfo -> + mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo)); + } finally { + Trace.endSection(); + } + + if (Flags.enablePrivateSpace()) { + mBgAllAppsList.setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, isWorkProfileQuiet); + mBgAllAppsList.setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, isPrivateProfileQuiet); + } else { + mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED, + mUserManagerState.isAnyProfileQuietModeEnabled()); + } + mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION, + hasShortcutsPermission(mApp.getContext())); + mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION, + mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE") + == PackageManager.PERMISSION_GRANTED); + + mBgAllAppsList.getAndResetChangeFlag(); + return allActivityList; + } + + private List loadDeepShortcuts() { + List allShortcuts = new ArrayList<>(); + mBgDataModel.deepShortcutMap.clear(); + + if (mBgAllAppsList.hasShortcutHostPermission()) { + for (UserHandle user : mUserCache.getUserProfiles()) { + if (mUserManager.isUserUnlocked(user)) { + List shortcuts = new ShortcutRequest(mApp.getContext(), user) + .query(ShortcutRequest.ALL); + allShortcuts.addAll(shortcuts); + mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts); + } + } + } + return allShortcuts; + } + + private void loadFolderNames() { + FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(), + mBgAllAppsList.data, mBgDataModel.folders); + + synchronized (mBgDataModel) { + for (int i = 0; i < mBgDataModel.folders.size(); i++) { + FolderNameInfos suggestionInfos = new FolderNameInfos(); + FolderInfo info = mBgDataModel.folders.valueAt(i); + if (info.suggestedFolderNames == null) { + provider.getSuggestedFolderName(mApp.getContext(), info.contents, + suggestionInfos); + info.suggestedFolderNames = suggestionInfos; + } + } + } + } + + public static boolean isValidProvider(AppWidgetProviderInfo provider) { + return (provider != null) && (provider.provider != null) + && (provider.provider.getPackageName() != null); + } + + private static void logASplit(String label) { + if (DEBUG) { + Log.d(TAG, label); + } + } + //add start + private void bindAllAppsToWorkspace(){ + if (mBgAllAppsList.data.size() > 0) { + AppInfoComparator mAppNameComparator = new AppInfoComparator(mApp.getContext()); + ArrayList appInfos = new ArrayList(mBgAllAppsList.data); + // 按照名称进行排序 + Collections.sort(appInfos, mAppNameComparator); + ArrayList> installQueue = new ArrayList<>(); + for (AppInfo info : appInfos) { + installQueue.add(Pair.create((ItemInfo) info, null)); + } + mApp.getModel().addAndBindAddedWorkspaceItems(installQueue); + } + } + // end +} diff --git a/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/PackageUpdatedTask.java b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/PackageUpdatedTask.java new file mode 100755 index 0000000000000000000000000000000000000000..372da94c0ceb9874274964161838cc28aa5a7f8a --- /dev/null +++ b/aosp/packages/apps/Launcher3/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; +import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED; +import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; +import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_ICON; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import androidx.annotation.NonNull; +import android.util.Pair; +import com.android.launcher3.Flags; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.Utilities; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.icons.IconCache; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.pm.PackageInstallInfo; +import com.android.launcher3.pm.UserCache; +import com.android.launcher3.shortcuts.ShortcutRequest; +import com.android.launcher3.uioverrides.ApiWrapper; +import com.android.launcher3.util.FlagOp; +import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.ItemInfoMatcher; +import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.SafeCloseable; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.allapps.AppInfoComparator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Handles updates due to changes in package manager (app installed/updated/removed) + * or when a user availability changes. + */ +@SuppressWarnings("NewApi") +public class PackageUpdatedTask extends BaseModelUpdateTask { + + // TODO(b/290090023): Set to false after root causing is done. + private static final boolean DEBUG = true; + private static final String TAG = "PackageUpdatedTask"; + + public static final int OP_NONE = 0; + public static final int OP_ADD = 1; + public static final int OP_UPDATE = 2; + public static final int OP_REMOVE = 3; // uninstalled + public static final int OP_UNAVAILABLE = 4; // external media unmounted + public static final int OP_SUSPEND = 5; // package suspended + public static final int OP_UNSUSPEND = 6; // package unsuspended + public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable + + private final int mOp; + + @NonNull + private final UserHandle mUser; + + @NonNull + private final String[] mPackages; + + public PackageUpdatedTask(final int op, @NonNull final UserHandle user, + @NonNull final String... packages) { + mOp = op; + mUser = user; + mPackages = packages; + } + + @Override + public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel, + @NonNull final AllAppsList appsList) { + final Context context = app.getContext(); + final IconCache iconCache = app.getIconCache(); + + final String[] packages = mPackages; + final int N = packages.length; + final FlagOp flagOp; + final HashSet packageSet = new HashSet<>(Arrays.asList(packages)); + final Predicate matcher = mOp == OP_USER_AVAILABILITY_CHANGE + ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user + : ItemInfoMatcher.ofPackages(packageSet, mUser); + final HashSet removedComponents = new HashSet<>(); + final HashMap> activitiesLists = new HashMap<>(); + + switch (mOp) { + case OP_ADD: { + for (int i = 0; i < N; i++) { + if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); + iconCache.updateIconsForPkg(packages[i], mUser); + if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) { + appsList.removePackage(packages[i], mUser); + } + activitiesLists.put( + packages[i], appsList.addPackage(context, packages[i], mUser)); + } + flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE); + break; + } + case OP_UPDATE: + try (SafeCloseable t = + appsList.trackRemoves(a -> removedComponents.add(a.componentName))) { + for (int i = 0; i < N; i++) { + if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); + iconCache.updateIconsForPkg(packages[i], mUser); + activitiesLists.put( + packages[i], appsList.updatePackage(context, packages[i], mUser)); + } + } + // Since package was just updated, the target must be available now. + flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE); + break; + case OP_REMOVE: { + for (int i = 0; i < N; i++) { + FileLog.d(TAG, "Removing app icon: " + packages[i]); + iconCache.removeIconsForPkg(packages[i], mUser); + } + // Fall through + } + case OP_UNAVAILABLE: + for (int i = 0; i < N; i++) { + if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]); + appsList.removePackage(packages[i], mUser); + } + flagOp = FlagOp.NO_OP.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE); + break; + case OP_SUSPEND: + case OP_UNSUSPEND: + flagOp = FlagOp.NO_OP.setFlag( + WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED, mOp == OP_SUSPEND); + if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N); + appsList.updateDisabledFlags(matcher, flagOp); + break; + case OP_USER_AVAILABILITY_CHANGE: { + UserManagerState ums = new UserManagerState(); + UserManager userManager = context.getSystemService(UserManager.class); + ums.init(UserCache.INSTANCE.get(context), userManager); + boolean isUserQuiet = ums.isUserQuiet(mUser); + flagOp = FlagOp.NO_OP.setFlag( + WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER, isUserQuiet); + appsList.updateDisabledFlags(matcher, flagOp); + + if (Flags.enablePrivateSpace()) { + UserCache userCache = UserCache.INSTANCE.get(context); + if (userCache.getUserInfo(mUser).isWork()) { + appsList.setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, isUserQuiet); + } else if (userCache.getUserInfo(mUser).isPrivate()) { + appsList.setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, isUserQuiet); + } + } else { + // We are not synchronizing here, as int operations are atomic + appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled()); + } + break; + } + default: + flagOp = FlagOp.NO_OP; + break; + } + + bindApplicationsIfNeeded(); + + final IntSet removedShortcuts = new IntSet(); + // Shortcuts to keep even if the corresponding app was removed + final IntSet forceKeepShortcuts = new IntSet(); + + // Update shortcut infos + if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) { + final ArrayList updatedWorkspaceItems = new ArrayList<>(); + final ArrayList widgets = new ArrayList<>(); + + // For system apps, package manager send OP_UPDATE when an app is enabled. + final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE; + synchronized (dataModel) { + dataModel.forAllWorkspaceItemInfos(mUser, si -> { + + boolean infoUpdated = false; + boolean shortcutUpdated = false; + + ComponentName cn = si.getTargetComponent(); + if (cn != null && matcher.test(si)) { + String packageName = cn.getPackageName(); + + if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) { + forceKeepShortcuts.add(si.id); + if (mOp == OP_REMOVE) { + return; + } + } + + if (si.isPromise() && isNewApkAvailable) { + boolean isTargetValid = !cn.getClassName().equals( + IconCache.EMPTY_CLASS_NAME); + if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + List shortcut = + new ShortcutRequest(context, mUser) + .forPackage(cn.getPackageName(), + si.getDeepShortcutId()) + .query(ShortcutRequest.PINNED); + if (shortcut.isEmpty()) { + isTargetValid = false; + } else { + si.updateFromDeepShortcutInfo(shortcut.get(0), context); + infoUpdated = true; + } + } else if (isTargetValid) { + isTargetValid = context.getSystemService(LauncherApps.class) + .isActivityEnabled(cn, mUser); + } + if (!isTargetValid && (si.hasStatusFlag( + FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON) + || si.isArchived())) { + if (updateWorkspaceItemIntent(context, si, packageName)) { + infoUpdated = true; + } else if (si.hasPromiseIconUi()) { + removedShortcuts.add(si.id); + return; + } + } else if (!isTargetValid) { + removedShortcuts.add(si.id); + FileLog.e(TAG, "Restored shortcut no longer valid " + + si.getIntent()); + return; + } else { + si.status = WorkspaceItemInfo.DEFAULT; + infoUpdated = true; + } + } else if (isNewApkAvailable && removedComponents.contains(cn)) { + if (updateWorkspaceItemIntent(context, si, packageName)) { + infoUpdated = true; + } + } + + if (isNewApkAvailable) { + List activities = activitiesLists.get( + packageName); + si.setProgressLevel( + activities == null || activities.isEmpty() + ? 100 + : PackageManagerHelper.getLoadingProgress( + activities.get(0)), + PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); + // In case an app is archived, we need to make sure that archived state + // in WorkspaceItemInfo is refreshed. + if (Utilities.enableSupportForArchiving() && !activities.isEmpty()) { + boolean newArchivalState = activities.get( + 0).getActivityInfo().isArchived; + if (newArchivalState != si.isArchived()) { + si.runtimeStatusFlags ^= FLAG_ARCHIVED; + infoUpdated = true; + } + } + if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) { + if (activities != null && !activities.isEmpty()) { + si.status = ApiWrapper + .isNonResizeableActivity(activities.get(0)) + ? si.status | WorkspaceItemInfo.FLAG_NON_RESIZEABLE + : si.status & ~WorkspaceItemInfo.FLAG_NON_RESIZEABLE; + } + iconCache.getTitleAndIcon(si, si.usingLowResIcon()); + infoUpdated = true; + } + } + + int oldRuntimeFlags = si.runtimeStatusFlags; + si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags); + if (si.runtimeStatusFlags != oldRuntimeFlags) { + shortcutUpdated = true; + } + } + + if (infoUpdated || shortcutUpdated) { + updatedWorkspaceItems.add(si); + } + if (infoUpdated && si.id != ItemInfo.NO_ID) { + getModelWriter().updateItemInDatabase(si); + } + }); + + for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) { + if (mUser.equals(widgetInfo.user) + && widgetInfo.hasRestoreFlag( + LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) + && packageSet.contains(widgetInfo.providerName.getPackageName())) { + widgetInfo.restoreStatus &= + ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY + & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; + + // adding this flag ensures that launcher shows 'click to setup' + // if the widget has a config activity. In case there is no config + // activity, it will be marked as 'restored' during bind. + widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + + widgets.add(widgetInfo); + getModelWriter().updateItemInDatabase(widgetInfo); + } + } + } + + bindUpdatedWorkspaceItems(updatedWorkspaceItems); + if (!removedShortcuts.isEmpty()) { + deleteAndBindComponentsRemoved( + ItemInfoMatcher.ofItemIds(removedShortcuts), + "removed because the target component is invalid"); + } + + if (!widgets.isEmpty()) { + scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets)); + } + } + + final HashSet removedPackages = new HashSet<>(); + if (mOp == OP_REMOVE) { + // Mark all packages in the broadcast to be removed + Collections.addAll(removedPackages, packages); + + // No need to update the removedComponents as + // removedPackages is a super-set of removedComponents + } else if (mOp == OP_UPDATE) { + // Mark disabled packages in the broadcast to be removed + final LauncherApps launcherApps = context.getSystemService(LauncherApps.class); + for (int i=0; i removeAppMatch = + ItemInfoMatcher.ofPackages(removedPackages, mUser) + .or(ItemInfoMatcher.ofComponents(removedComponents, mUser)) + .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate()); + // This predicate is used to mark an app pair for removal if it contains an app marked + // for removal. + Predicate removeAppPairMatch = + ItemInfoMatcher.forAppPairMatch(removeAppMatch); + Predicate removeMatch = removeAppMatch.or(removeAppPairMatch); + deleteAndBindComponentsRemoved(removeMatch, + "removed because the corresponding package or component is removed. " + + "mOp=" + mOp + " removedPackages=" + removedPackages.stream().collect( + Collectors.joining(",", "[", "]")) + + " removedComponents=" + removedComponents.stream() + .filter(Objects::nonNull).map(ComponentName::toShortString) + .collect(Collectors.joining(",", "[", "]"))); + + // Remove any queued items from the install queue + ItemInstallQueue.INSTANCE.get(context) + .removeFromInstallQueue(removedPackages, mUser); + } + + if (mOp == OP_ADD) { + // Load widgets for the new package. Changes due to app updates are handled through + // AppWidgetHost events, this is just to initialize the long-press options. + for (int i = 0; i < N; i++) { + dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser)); + } + bindUpdatedWidgets(dataModel); + } + // add start + if (FeatureFlags.LAUNCHER3_REMOVE_DRAWER) { + updateAllAppsToWorkspace(app, appsList); + } + // add end + } + + /** + * Updates {@param si}'s intent to point to a new ComponentName. + * @return Whether the shortcut intent was changed. + */ + private boolean updateWorkspaceItemIntent(Context context, + WorkspaceItemInfo si, String packageName) { + if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + // Do not update intent for deep shortcuts as they contain additional information + // about the shortcut. + return false; + } + // Try to find the best match activity. + Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser); + if (intent != null) { + si.intent = intent; + si.status = WorkspaceItemInfo.DEFAULT; + return true; + } + return false; + } + + // add start + public void updateAllAppsToWorkspace(LauncherAppState app , AllAppsList mBgAllAppsList){ + if (mBgAllAppsList.data.size() > 0) { + AppInfoComparator mAppNameComparator = new AppInfoComparator(app.getContext()); + ArrayList appInfos = new ArrayList(mBgAllAppsList.data); + Collections.sort(appInfos, mAppNameComparator); + ArrayList> installQueue = new ArrayList<>(); + for (AppInfo info : appInfos) { + installQueue.add(Pair.create((ItemInfo) info, null)); + } + app.getModel().addAndBindAddedWorkspaceItems(installQueue); + } + } + // add end +} diff --git a/aosp/packages/apps/Settings/src/com/android/settings/network/MobileNetworkSummaryController.java b/aosp/packages/apps/Settings/src/com/android/settings/network/MobileNetworkSummaryController.java new file mode 100755 index 0000000000000000000000000000000000000000..ecb622893b62b7001cb3496e7492d67a84ad46f1 --- /dev/null +++ b/aosp/packages/apps/Settings/src/com/android/settings/network/MobileNetworkSummaryController.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network; + +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; + +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; +import android.telephony.SubscriptionManager; +import android.telephony.euicc.EuiccManager; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.network.telephony.MobileNetworkUtils; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.Utils; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity; +import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity; +import com.android.settingslib.mobile.dataservice.UiccInfoEntity; + +import java.util.List; +import java.util.stream.Collectors; + +public class MobileNetworkSummaryController extends AbstractPreferenceController implements + LifecycleObserver, PreferenceControllerMixin, + MobileNetworkRepository.MobileNetworkCallback { + private static final String TAG = "MobileNetSummaryCtlr"; + + private static final String KEY = "mobile_network_list"; + + private final MetricsFeatureProvider mMetricsFeatureProvider; + private UserManager mUserManager; + private RestrictedPreference mPreference; + + private MobileNetworkRepository mMobileNetworkRepository; + private List mSubInfoEntityList; + private List mUiccInfoEntityList; + private List mMobileNetworkInfoEntityList; + private boolean mIsAirplaneModeOn; + private LifecycleOwner mLifecycleOwner; + + /** + * This controls the summary text and click behavior of the "Mobile network" item on the + * Network & internet page. There are 3 separate cases depending on the number of mobile network + * subscriptions: + *

    + *
  • No subscription: click action begins a UI flow to add a network subscription, and + * the summary text indicates this
  • + * + *
  • One subscription: click action takes you to details for that one network, and + * the summary text is the network name
  • + * + *
  • More than one subscription: click action takes you to a page listing the subscriptions, + * and the summary text gives the count of SIMs
  • + *
+ */ + public MobileNetworkSummaryController(Context context, Lifecycle lifecycle, + LifecycleOwner lifecycleOwner) { + super(context); + mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); + mUserManager = context.getSystemService(UserManager.class); + mLifecycleOwner = lifecycleOwner; + mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); + mIsAirplaneModeOn = mMobileNetworkRepository.isAirplaneModeOn(); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @OnLifecycleEvent(ON_RESUME) + public void onResume() { + mMobileNetworkRepository.addRegister(mLifecycleOwner, this, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mMobileNetworkRepository.updateEntity(); + } + + @OnLifecycleEvent(ON_PAUSE) + public void onPause() { + mMobileNetworkRepository.removeRegister(this); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public CharSequence getSummary() { + + if ((mSubInfoEntityList == null || mSubInfoEntityList.isEmpty()) || ( + mUiccInfoEntityList == null || mUiccInfoEntityList.isEmpty()) || ( + mMobileNetworkInfoEntityList == null || mMobileNetworkInfoEntityList.isEmpty())) { + if (MobileNetworkUtils.showEuiccSettingsDetecting(mContext)) { + return mContext.getResources().getString( + R.string.mobile_network_summary_add_a_network); + } + // set empty string to override previous text for carrier when SIM available + return ""; + } else if (mSubInfoEntityList.size() == 1) { + SubscriptionInfoEntity info = mSubInfoEntityList.get(0); + CharSequence displayName = info.uniqueName; + if (info.isEmbedded || mUiccInfoEntityList.get(0).isActive + || mMobileNetworkInfoEntityList.get(0).showToggleForPhysicalSim) { + return displayName; + } + return mContext.getString(R.string.mobile_network_tap_to_activate, displayName); + } else { + return mSubInfoEntityList.stream() + .map(SubscriptionInfoEntity::getUniqueDisplayName) + .collect(Collectors.joining(", ")); + } + } + + private void logPreferenceClick(Preference preference) { + mMetricsFeatureProvider.logClickedPreference(preference, + preference.getExtras().getInt(DashboardFragment.CATEGORY)); + } + + private void startAddSimFlow() { + final Intent intent = new Intent(EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION); + intent.putExtra(EuiccManager.EXTRA_FORCE_PROVISION, true); + mContext.startActivity(intent); + } + + private void initPreference() { + refreshSummary(mPreference); + mPreference.setOnPreferenceClickListener(null); + mPreference.setFragment(null); + mPreference.setEnabled(!mIsAirplaneModeOn); + } + + private void update() { + if (mPreference == null || mPreference.isDisabledByAdmin()) { + return; + } + + initPreference(); + if (((mSubInfoEntityList == null || mSubInfoEntityList.isEmpty()) + || (mUiccInfoEntityList == null || mUiccInfoEntityList.isEmpty()) + || (mMobileNetworkInfoEntityList == null + || mMobileNetworkInfoEntityList.isEmpty()))) { + if (MobileNetworkUtils.showEuiccSettingsDetecting(mContext)) { + mPreference.setOnPreferenceClickListener((Preference pref) -> { + logPreferenceClick(pref); + startAddSimFlow(); + return true; + }); + } else { + mPreference.setEnabled(false); + } + return; + } + + mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName()); + } + + @Override + public boolean isAvailable() { + //ccynice for sim + return true; + //return SubscriptionUtil.isSimHardwareVisible(mContext) && + // !Utils.isWifiOnly(mContext) && mUserManager.isAdminUser(); + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onAirplaneModeChanged(boolean airplaneModeEnabled) { + if (mIsAirplaneModeOn != airplaneModeEnabled) { + mIsAirplaneModeOn = airplaneModeEnabled; + update(); + } + } + + @Override + public void onAvailableSubInfoChanged(List subInfoEntityList) { + mSubInfoEntityList = subInfoEntityList; + update(); + } + + @Override + public void onAllUiccInfoChanged(List uiccInfoEntityList) { + mUiccInfoEntityList = uiccInfoEntityList; + update(); + } + + @Override + public void onAllMobileNetworkInfoChanged( + List mobileNetworkInfoEntityList) { + mMobileNetworkInfoEntityList = mobileNetworkInfoEntityList; + update(); + } +} diff --git a/aosp/packages/apps/Settings/src/com/android/settings/network/SubscriptionUtil.java b/aosp/packages/apps/Settings/src/com/android/settings/network/SubscriptionUtil.java new file mode 100755 index 0000000000000000000000000000000000000000..b298985ea514c8c66f68be91407626420fb55c28 --- /dev/null +++ b/aosp/packages/apps/Settings/src/com/android/settings/network/SubscriptionUtil.java @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network; + +import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; +import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING; +import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT; + +import static com.android.internal.util.CollectionUtils.emptyIfNull; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; +import android.os.ParcelUuid; +import android.provider.Settings; +import android.telephony.PhoneNumberUtils; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.UiccCardInfo; +import android.telephony.UiccSlotInfo; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.android.internal.telephony.MccTable; +import com.android.settings.R; +import com.android.settings.flags.Flags; +import com.android.settings.network.helper.SelectableSubscriptions; +import com.android.settings.network.helper.SubscriptionAnnotation; +import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity; +import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity; +import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SubscriptionUtil { + private static final String TAG = "SubscriptionUtil"; + private static final String PROFILE_GENERIC_DISPLAY_NAME = "CARD"; + @VisibleForTesting + static final String SUB_ID = "sub_id"; + @VisibleForTesting + static final String KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME = "unique_subscription_displayName"; + private static final String REGEX_DISPLAY_NAME_PREFIXES = "^"; + private static final String REGEX_DISPLAY_NAME_SUFFIXES = "\\s[0-9]+"; + + private static List sAvailableResultsForTesting; + private static List sActiveResultsForTesting; + + @VisibleForTesting + public static void setAvailableSubscriptionsForTesting(List results) { + sAvailableResultsForTesting = results; + } + + @VisibleForTesting + public static void setActiveSubscriptionsForTesting(List results) { + sActiveResultsForTesting = results; + } + + public static List getActiveSubscriptions(SubscriptionManager manager) { + //TODO (b/315499317) : Refactor the subscription utils. + + if (sActiveResultsForTesting != null) { + return sActiveResultsForTesting; + } + if (manager == null) { + return Collections.emptyList(); + } + final List subscriptions = manager.getActiveSubscriptionInfoList(); + if (subscriptions == null) { + return new ArrayList<>(); + } + + //ccynice + return subscriptions; + /* + // Since the SubscriptionManager.getActiveSubscriptionInfoList() has checked whether the + // sim visible by the SubscriptionManager.isSubscriptionVisible(), here only checks whether + // the esim visible here. + return subscriptions.stream() + .filter(subInfo -> subInfo != null && isEmbeddedSubscriptionVisible(subInfo)) + .collect(Collectors.toList());*/ + } + + /** + * Check if SIM hardware is visible to the end user. + */ + public static boolean isSimHardwareVisible(Context context) { + return context.getResources() + .getBoolean(R.bool.config_show_sim_info); + } + + @VisibleForTesting + static boolean isInactiveInsertedPSim(UiccSlotInfo slotInfo) { + if (slotInfo == null) { + return false; + } + return !slotInfo.getIsEuicc() && !slotInfo.getPorts().stream().findFirst().get() + .isActive() && slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT; + } + + /** + * Get all of the subscriptions which is available to display to the user. + * + * @param context {@code Context} + * @return list of {@code SubscriptionInfo} + */ + public static List getAvailableSubscriptions(Context context) { + if (sAvailableResultsForTesting != null) { + return sAvailableResultsForTesting; + } + return new ArrayList<>(emptyIfNull(getSelectableSubscriptionInfoList(context))); + } + + /** + * Get subscriptionInfo which is available to be displayed to the user + * per subscription id. + * + * @param context {@code Context} + * @param subscriptionManager The ProxySubscriptionManager for accessing subcription + * information + * @param subId The id of subscription to be retrieved + * @return {@code SubscriptionInfo} based on the given subscription id. Null of subscription + * is invalid or not allowed to be displayed to the user. + */ + public static SubscriptionInfo getAvailableSubscriptionBySubIdAndShowingForUser(Context context, + ProxySubscriptionManager subscriptionManager, int subId) { + //TODO (b/315499317) : Refactor the subscription utils. + final SubscriptionInfo subInfo = subscriptionManager.getAccessibleSubscriptionInfo(subId); + if (subInfo == null) { + return null; + } + + // hide provisioning/bootstrap and satellite profiles for user + if (!isEmbeddedSubscriptionVisible(subInfo)) { + Log.d(TAG, "Do not insert the provision eSIM or NTN eSim"); + return null; + } + + final ParcelUuid groupUuid = subInfo.getGroupUuid(); + + if (groupUuid != null) { + if (isPrimarySubscriptionWithinSameUuid(getUiccSlotsInfo(context), groupUuid, + subscriptionManager.getAccessibleSubscriptionsInfo(), subId)) { + return subInfo; + } + return null; + } + + return subInfo; + } + + private static UiccSlotInfo [] getUiccSlotsInfo(Context context) { + final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class); + return telMgr.getUiccSlotsInfo(); + } + + private static boolean isPrimarySubscriptionWithinSameUuid(UiccSlotInfo[] slotsInfo, + ParcelUuid groupUuid, List subscriptions, int subId) { + // only interested in subscriptions with this group UUID + final ArrayList physicalSubInfoList = + new ArrayList(); + final ArrayList nonOpportunisticSubInfoList = + new ArrayList(); + final ArrayList activeSlotSubInfoList = + new ArrayList(); + final ArrayList inactiveSlotSubInfoList = + new ArrayList(); + for (SubscriptionInfo subInfo : subscriptions) { + if (groupUuid.equals(subInfo.getGroupUuid())) { + if (!subInfo.isEmbedded()) { + physicalSubInfoList.add(subInfo); + } else { + if (!subInfo.isOpportunistic()) { + nonOpportunisticSubInfoList.add(subInfo); + } + if (subInfo.getSimSlotIndex() + != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + activeSlotSubInfoList.add(subInfo); + } else { + inactiveSlotSubInfoList.add(subInfo); + } + } + } + } + + // find any physical SIM which is currently inserted within logical slot + // and which is our target subscription + if ((slotsInfo != null) && (physicalSubInfoList.size() > 0)) { + final SubscriptionInfo subInfo = searchForSubscriptionId(physicalSubInfoList, subId); + if (subInfo == null) { + return false; + } + // verify if subscription is inserted within slot + for (UiccSlotInfo slotInfo : slotsInfo) { + if ((slotInfo != null) && (!slotInfo.getIsEuicc()) + && (slotInfo.getPorts().stream().findFirst().get().getLogicalSlotIndex() + == subInfo.getSimSlotIndex())) { + return true; + } + } + return false; + } + + // When all of the eSIM profiles are opprtunistic and no physical SIM, + // first opportunistic subscriptions with same group UUID can be primary. + if (nonOpportunisticSubInfoList.size() <= 0) { + if (physicalSubInfoList.size() > 0) { + return false; + } + if (activeSlotSubInfoList.size() > 0) { + return (activeSlotSubInfoList.get(0).getSubscriptionId() == subId); + } + return (inactiveSlotSubInfoList.get(0).getSubscriptionId() == subId); + } + + // Allow non-opportunistic + active eSIM subscription as primary + int numberOfActiveNonOpportunisticSubs = 0; + boolean isTargetNonOpportunistic = false; + for (SubscriptionInfo subInfo : nonOpportunisticSubInfoList) { + final boolean isTargetSubInfo = (subInfo.getSubscriptionId() == subId); + if (subInfo.getSimSlotIndex() != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + if (isTargetSubInfo) { + return true; + } + numberOfActiveNonOpportunisticSubs++; + } else { + isTargetNonOpportunistic |= isTargetSubInfo; + } + } + if (numberOfActiveNonOpportunisticSubs > 0) { + return false; + } + return isTargetNonOpportunistic; + } + + private static SubscriptionInfo searchForSubscriptionId(List subInfoList, + int subscriptionId) { + for (SubscriptionInfo subInfo : subInfoList) { + if (subInfo.getSubscriptionId() == subscriptionId) { + return subInfo; + } + } + return null; + } + + /** + * Return a mapping of active subscription ids to display names. Each display name is + * guaranteed to be unique in the following manner: + * 1) If the original display name is not unique, the last four digits of the phone number + * will be appended. + * 2) If the phone number is not visible or the last four digits are shared with another + * subscription, the subscription id will be appended to the original display name. + * More details can be found at go/unique-sub-display-names. + * + * @return map of active subscription ids to display names. + */ + @VisibleForTesting + public static Map getUniqueSubscriptionDisplayNames(Context context) { + class DisplayInfo { + public SubscriptionInfo subscriptionInfo; + public CharSequence originalName; + public CharSequence uniqueName; + } + + // Map of SubscriptionId to DisplayName + final Supplier> originalInfos = + () -> getAvailableSubscriptions(context) + .stream() + .filter(i -> { + // Filter out null values. + return (i != null && i.getDisplayName() != null); + }) + .map(i -> { + DisplayInfo info = new DisplayInfo(); + info.subscriptionInfo = i; + String displayName = i.getDisplayName().toString(); + info.originalName = + TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME) + ? context.getResources().getString(R.string.sim_card) + : displayName.trim(); + return info; + }); + + // TODO(goldmanj) consider using a map of DisplayName to SubscriptionInfos. + // A Unique set of display names + Set uniqueNames = new HashSet<>(); + // Return the set of duplicate names + final Set duplicateOriginalNames = originalInfos.get() + .filter(info -> !uniqueNames.add(info.originalName)) + .map(info -> info.originalName) + .collect(Collectors.toSet()); + + // If a display name is duplicate, append the final 4 digits of the phone number. + // Creates a mapping of Subscription id to original display name + phone number display name + final Supplier> uniqueInfos = () -> originalInfos.get().map(info -> { + int infoSubId = info.subscriptionInfo.getSubscriptionId(); + String cachedDisplayName = getDisplayNameFromSharedPreference( + context, infoSubId); + if (isValidCachedDisplayName(cachedDisplayName, info.originalName.toString())) { + Log.d(TAG, "use cached display name : for subId : " + infoSubId + + "cached display name : " + cachedDisplayName); + info.uniqueName = cachedDisplayName; + return info; + } else { + Log.d(TAG, "remove cached display name : " + infoSubId); + removeItemFromDisplayNameSharedPreference(context, infoSubId); + } + + if (duplicateOriginalNames.contains(info.originalName)) { + // This may return null, if the user cannot view the phone number itself. + String phoneNumber = ""; + try { + final SubscriptionManager subscriptionManager = context.getSystemService( + SubscriptionManager.class); + phoneNumber = subscriptionManager.getPhoneNumber(infoSubId); + } catch (IllegalStateException + | SecurityException + | UnsupportedOperationException e) { + Log.w(TAG, "get number error." + e); + } + String lastFourDigits = ""; + if (phoneNumber != null) { + lastFourDigits = (phoneNumber.length() > 4) + ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber; + } + if (TextUtils.isEmpty(lastFourDigits)) { + info.uniqueName = info.originalName; + } else { + info.uniqueName = info.originalName + " " + lastFourDigits; + Log.d(TAG, "Cache display name [" + info.uniqueName + "] for sub id " + + infoSubId); + saveDisplayNameToSharedPreference(context, infoSubId, info.uniqueName); + } + } else { + info.uniqueName = info.originalName; + } + return info; + }); + + // Check uniqueness a second time. + // We might not have had permission to view the phone numbers. + // There might also be multiple phone numbers whose last 4 digits the same. + uniqueNames.clear(); + final Set duplicatePhoneNames = uniqueInfos.get() + .filter(info -> !uniqueNames.add(info.uniqueName)) + .map(info -> info.uniqueName) + .collect(Collectors.toSet()); + + return uniqueInfos.get().map(info -> { + if (duplicatePhoneNames.contains(info.uniqueName)) { + info.uniqueName = info.originalName + " " + + info.subscriptionInfo.getSubscriptionId(); + } + return info; + }).collect(Collectors.toMap( + info -> info.subscriptionInfo.getSubscriptionId(), + info -> info.uniqueName)); + } + + /** + * Return the display name for a subscription id, which is guaranteed to be unique. + * The logic to create this name has the following order of operations: + * 1) If the original display name is not unique, the last four digits of the phone number + * will be appended. + * 2) If the phone number is not visible or the last four digits are shared with another + * subscription, the subscription id will be appended to the original display name. + * More details can be found at go/unique-sub-display-names. + * + * @return map of active subscription ids to display names. + */ + @VisibleForTesting + public static CharSequence getUniqueSubscriptionDisplayName( + Integer subscriptionId, Context context) { + final Map displayNames = getUniqueSubscriptionDisplayNames(context); + return displayNames.getOrDefault(subscriptionId, ""); + } + + /** + * Return the display name for a subscription, which is guaranteed to be unique. + * The logic to create this name has the following order of operations: + * 1) If the original display name is not unique, the last four digits of the phone number + * will be appended. + * 2) If the phone number is not visible or the last four digits are shared with another + * subscription, the subscription id will be appended to the original display name. + * More details can be found at go/unique-sub-display-names. + * + * @return map of active subscription ids to display names. + */ + public static CharSequence getUniqueSubscriptionDisplayName( + SubscriptionInfo info, Context context) { + if (info == null) { + return ""; + } + return getUniqueSubscriptionDisplayName(info.getSubscriptionId(), context); + } + + + private static SharedPreferences getDisplayNameSharedPreferences(Context context) { + return context.getSharedPreferences( + KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE); + } + + private static SharedPreferences.Editor getDisplayNameSharedPreferenceEditor(Context context) { + return getDisplayNameSharedPreferences(context).edit(); + } + + private static void saveDisplayNameToSharedPreference( + Context context, int subId, CharSequence displayName) { + getDisplayNameSharedPreferenceEditor(context) + .putString(SUB_ID + subId, String.valueOf(displayName)) + .apply(); + } + + private static void removeItemFromDisplayNameSharedPreference(Context context, int subId) { + getDisplayNameSharedPreferenceEditor(context) + .remove(SUB_ID + subId) + .commit(); + } + + private static String getDisplayNameFromSharedPreference(Context context, int subid) { + return getDisplayNameSharedPreferences(context).getString(SUB_ID + subid, ""); + } + + @VisibleForTesting + static boolean isValidCachedDisplayName(String cachedDisplayName, String originalName) { + if (TextUtils.isEmpty(cachedDisplayName) || TextUtils.isEmpty(originalName)) { + return false; + } + String regex = REGEX_DISPLAY_NAME_PREFIXES + originalName + REGEX_DISPLAY_NAME_SUFFIXES; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(cachedDisplayName); + return matcher.matches(); + } + + public static String getDisplayName(SubscriptionInfo info) { + final CharSequence name = info.getDisplayName(); + if (name != null) { + return name.toString(); + } + return ""; + } + + /** + * Whether Settings should show a "Use SIM" toggle in pSIM detailed page. + */ + public static boolean showToggleForPhysicalSim(SubscriptionManager subMgr) { + return subMgr.canDisablePhysicalSubscription(); + } + + /** + * Get phoneId or logical slot index for a subId if active, or INVALID_PHONE_INDEX if inactive. + */ + public static int getPhoneId(Context context, int subId) { + final SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); + if (subManager == null) { + return INVALID_SIM_SLOT_INDEX; + } + final SubscriptionInfo info = subManager.getActiveSubscriptionInfo(subId); + if (info == null) { + return INVALID_SIM_SLOT_INDEX; + } + return info.getSimSlotIndex(); + } + + /** + * Return a list of subscriptions that are available and visible to the user. + * + * @return list of user selectable subscriptions. + */ + public static List getSelectableSubscriptionInfoList(Context context) { + SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); + List availableList = subManager.getAvailableSubscriptionInfoList(); + if (availableList == null) { + return null; + } else { + // Multiple subscriptions in a group should only have one representative. + // It should be the current active primary subscription if any, or any + // primary subscription. + List selectableList = new ArrayList<>(); + Map groupMap = new HashMap<>(); + + for (SubscriptionInfo info : availableList) { + // Opportunistic subscriptions are considered invisible + // to users so they should never be returned. + if (!isSubscriptionVisible(subManager, context, info)) continue; + + ParcelUuid groupUuid = info.getGroupUuid(); + if (groupUuid == null) { + // Doesn't belong to any group. Add in the list. + selectableList.add(info); + } else if (!groupMap.containsKey(groupUuid) + || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX + && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) { + // If it belongs to a group that has never been recorded or it's the current + // active subscription, add it in the list. + selectableList.remove(groupMap.get(groupUuid)); + selectableList.add(info); + groupMap.put(groupUuid, info); + } + + } + Log.d(TAG, "getSelectableSubscriptionInfoList: " + selectableList); + return selectableList; + } + } + + /** + * Starts a dialog activity to handle SIM enabling/disabling. + * @param context {@code Context} + * @param subId The id of subscription need to be enabled or disabled. + * @param enable Whether the subscription with {@code subId} should be enabled or disabled. + */ + public static void startToggleSubscriptionDialogActivity( + Context context, int subId, boolean enable) { + if (!SubscriptionManager.isUsableSubscriptionId(subId)) { + Log.i(TAG, "Unable to toggle subscription due to invalid subscription ID."); + return; + } + if (enable && Flags.isDualSimOnboardingEnabled()) { + SimOnboardingActivity.startSimOnboardingActivity(context, subId); + return; + } + context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable)); + } + + /** + * Starts a dialog activity to handle eSIM deletion. + * @param context {@code Context} + * @param subId The id of subscription need to be deleted. + */ + public static void startDeleteEuiccSubscriptionDialogActivity(Context context, int subId, + int carrierId) { + if (!SubscriptionManager.isUsableSubscriptionId(subId)) { + Log.i(TAG, "Unable to delete subscription due to invalid subscription ID."); + return; + } + final int[] carriersThatUseRAC = context.getResources().getIntArray( + R.array.config_carrier_use_rac); + boolean isCarrierRac = Arrays.stream(carriersThatUseRAC).anyMatch(cid -> cid == carrierId); + + if (isCarrierRac && !isConnectedToWifiOrDifferentSubId(context, subId)) { + context.startActivity(EuiccRacConnectivityDialogActivity.getIntent(context, subId)); + } else { + context.startActivity(DeleteEuiccSubscriptionDialogActivity.getIntent(context, subId)); + } + } + + /** + * Finds and returns a subscription with a specific subscription ID. + * @param subscriptionManager The ProxySubscriptionManager for accessing subscription + * information + * @param subId The id of subscription to be returned + * @return the {@code SubscriptionInfo} whose ID is {@code subId}. It returns null if the + * {@code subId} is {@code SubscriptionManager.INVALID_SUBSCRIPTION_ID} or no such + * {@code SubscriptionInfo} is found. + */ + @Nullable + public static SubscriptionInfo getSubById(SubscriptionManager subscriptionManager, int subId) { + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + return null; + } + return subscriptionManager + .getAllSubscriptionInfoList() + .stream() + .filter(subInfo -> subInfo.getSubscriptionId() == subId) + .findFirst() + .orElse(null); + } + + /** + * Whether a subscription is visible to API caller. If it's a bundled opportunistic + * subscription, it should be hidden anywhere in Settings, dialer, status bar etc. + * Exception is if caller owns carrier privilege, in which case they will + * want to see their own hidden subscriptions. + * + * @param info the subscriptionInfo to check against. + * @return true if this subscription should be visible to the API caller. + */ + public static boolean isSubscriptionVisible( + SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) { + if (info == null) return false; + + // hide provisioning/bootstrap and satellite profiles for user + if (!isEmbeddedSubscriptionVisible(info)) { + return false; + } + + // If subscription is NOT grouped opportunistic subscription, it's visible. + if (info.getGroupUuid() == null || !info.isOpportunistic()) return true; + + // If the caller is the carrier app and owns the subscription, it should be visible + // to the caller. + TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) + .createForSubscriptionId(info.getSubscriptionId()); + boolean hasCarrierPrivilegePermission = telephonyManager.hasCarrierPrivileges() + || subscriptionManager.canManageSubscription(info); + return hasCarrierPrivilegePermission; + } + + /** + * Finds all the available subscriptions having the same group uuid as {@code subscriptionInfo}. + * @param subscriptionManager The SubscriptionManager for accessing subscription information + * @param subId The id of subscription + * @return a list of {@code SubscriptionInfo} which have the same group UUID. + */ + public static List findAllSubscriptionsInGroup( + SubscriptionManager subscriptionManager, int subId) { + + SubscriptionInfo subscription = getSubById(subscriptionManager, subId); + if (subscription == null) { + return Collections.emptyList(); + } + ParcelUuid groupUuid = subscription.getGroupUuid(); + List availableSubscriptions = + subscriptionManager.getAvailableSubscriptionInfoList(); + + if (availableSubscriptions == null + || availableSubscriptions.isEmpty() + || groupUuid == null) { + return Collections.singletonList(subscription); + } + + return availableSubscriptions + .stream() + .filter(sub -> sub.isEmbedded() && groupUuid.equals(sub.getGroupUuid())) + .collect(Collectors.toList()); + } + + /** Returns the formatted phone number of a subscription. */ + @Nullable + public static String getFormattedPhoneNumber( + Context context, SubscriptionInfo subscriptionInfo) { + if (subscriptionInfo == null) { + Log.e(TAG, "Invalid subscription."); + return null; + } + + final SubscriptionManager subscriptionManager = context.getSystemService( + SubscriptionManager.class); + String rawPhoneNumber = subscriptionManager.getPhoneNumber( + subscriptionInfo.getSubscriptionId()); + if (TextUtils.isEmpty(rawPhoneNumber)) { + return null; + } + String countryIso = MccTable.countryCodeForMcc(subscriptionInfo.getMccString()) + .toUpperCase(Locale.ROOT); + return PhoneNumberUtils.formatNumber(rawPhoneNumber, countryIso); + } + + /** + * To get the formatting text for display in a potentially opposite-directionality context + * without garbling. + * @param subscriptionInfo {@link SubscriptionInfo} subscription information. + * @return Returns phone number with Bidi format. + */ + @Nullable + public static String getBidiFormattedPhoneNumber(Context context, + SubscriptionInfo subscriptionInfo) { + String phoneNumber = getFormattedPhoneNumber(context, subscriptionInfo); + return (phoneNumber == null) ? phoneNumber : + BidiFormatter.getInstance().unicodeWrap(phoneNumber, TextDirectionHeuristics.LTR); + } + + /** + * Returns the subscription on a removable sim card. The device does not need to be on removable + * slot. + */ + @Nullable + public static SubscriptionInfo getFirstRemovableSubscription(Context context) { + TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); + SubscriptionManager subscriptionManager = + context.getSystemService(SubscriptionManager.class); + List cardInfos = telephonyManager.getUiccCardsInfo(); + if (cardInfos == null) { + Log.w(TAG, "UICC cards info list is empty."); + return null; + } + List allSubscriptions = subscriptionManager.getAllSubscriptionInfoList(); + if (allSubscriptions == null) { + Log.w(TAG, "All subscription info list is empty."); + return null; + } + for (UiccCardInfo cardInfo : cardInfos) { + if (cardInfo == null) { + Log.w(TAG, "Got null card."); + continue; + } + if (!cardInfo.isRemovable() + || cardInfo.getCardId() == TelephonyManager.UNSUPPORTED_CARD_ID) { + Log.i(TAG, "Skip embedded card or invalid cardId on slot: " + + cardInfo.getPhysicalSlotIndex()); + continue; + } + Log.i(TAG, "Target removable cardId :" + cardInfo.getCardId()); + for (SubscriptionInfo subInfo : allSubscriptions) { + // Match the removable card id with subscription card id. + if (cardInfo.getCardId() == subInfo.getCardId()) { + return subInfo; + } + } + } + return null; + } + + public static CharSequence getDefaultSimConfig(Context context, int subId) { + boolean isDefaultCall = subId == getDefaultVoiceSubscriptionId(); + boolean isDefaultSms = subId == getDefaultSmsSubscriptionId(); + boolean isDefaultData = subId == getDefaultDataSubscriptionId(); + + if (!isDefaultData && !isDefaultCall && !isDefaultSms) { + return ""; + } + + final StringBuilder defaultConfig = new StringBuilder(); + if (isDefaultData) { + defaultConfig.append( + getResForDefaultConfig(context, R.string.default_active_sim_mobile_data)) + .append(", "); + } + + if (isDefaultCall) { + defaultConfig.append(getResForDefaultConfig(context, R.string.default_active_sim_calls)) + .append(", "); + } + + if (isDefaultSms) { + defaultConfig.append(getResForDefaultConfig(context, R.string.default_active_sim_sms)) + .append(", "); + } + + // Do not add ", " for the last config. + defaultConfig.setLength(defaultConfig.length() - 2); + + final String summary = context.getResources().getString( + R.string.sim_category_default_active_sim, + defaultConfig); + + return summary; + } + + private static String getResForDefaultConfig(Context context, int resId) { + return context.getResources().getString(resId); + } + + private static int getDefaultVoiceSubscriptionId() { + return SubscriptionManager.getDefaultVoiceSubscriptionId(); + } + + private static int getDefaultSmsSubscriptionId() { + return SubscriptionManager.getDefaultSmsSubscriptionId(); + } + + private static int getDefaultDataSubscriptionId() { + return SubscriptionManager.getDefaultDataSubscriptionId(); + } + + + /** + * Select one of the subscription as the default subscription. + * @param subAnnoList a list of {@link SubscriptionAnnotation} + * @return ideally the {@link SubscriptionAnnotation} as expected + */ + private static SubscriptionAnnotation getDefaultSubscriptionSelection( + List subAnnoList) { + return (subAnnoList == null) ? null : + subAnnoList.stream() + .filter(SubscriptionAnnotation::isDisplayAllowed) + .filter(SubscriptionAnnotation::isActive) + .findFirst().orElse(null); + } + + public static boolean isDefaultSubscription(Context context, int subId) { + SubscriptionAnnotation subInfo = getDefaultSubscriptionSelection( + new SelectableSubscriptions(context, true).call()); + return subInfo != null && subInfo.getSubscriptionId() == subId; + } + + public static SubscriptionInfo getSubscriptionOrDefault(Context context, int subscriptionId) { + return getSubscription(context, subscriptionId, + (subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) ? null : ( + subAnnoList -> getDefaultSubscriptionSelection(subAnnoList) + )); + } + + /** + * Get the current subscription to display. First check whether intent has {@link + * Settings#EXTRA_SUB_ID} and if so find the subscription with that id. + * If not, select default one based on {@link Function} provided. + * + * @param preferredSubscriptionId preferred subscription id + * @param selectionOfDefault when true current subscription is absent + */ + private static SubscriptionInfo getSubscription(Context context, int preferredSubscriptionId, + Function, SubscriptionAnnotation> selectionOfDefault) { + List subList = + (new SelectableSubscriptions(context, true)).call(); + Log.d(TAG, "get subId=" + preferredSubscriptionId + " from " + subList); + SubscriptionAnnotation currentSubInfo = subList.stream() + .filter(SubscriptionAnnotation::isDisplayAllowed) + .filter(subAnno -> (subAnno.getSubscriptionId() == preferredSubscriptionId)) + .findFirst().orElse(null); + if ((currentSubInfo == null) && (selectionOfDefault != null)) { + currentSubInfo = selectionOfDefault.apply(subList); + } + return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo(); + } + + private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) { + if (subInfo.isEmbedded() + && (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING + || (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag() + && subInfo.isOnlyNonTerrestrialNetwork()))) { + return false; + } + return true; + } + + /** + * Returns {@code true} if device is connected to Wi-Fi or mobile data provided by a different + * subId. + * + * @param context context + * @param targetSubId subscription that is going to be deleted + */ + @VisibleForTesting + static boolean isConnectedToWifiOrDifferentSubId(@NonNull Context context, int targetSubId) { + ConnectivityManager connectivityManager = + context.getSystemService(ConnectivityManager.class); + NetworkCapabilities capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork()); + + if (capabilities != null) { + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + // Connected to WiFi + return true; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return targetSubId != SubscriptionManager.getActiveDataSubscriptionId(); + } + } + return false; + } +} diff --git a/aosp/packages/apps/Settings/src/com/android/settings/network/helper/SubscriptionAnnotation.java b/aosp/packages/apps/Settings/src/com/android/settings/network/helper/SubscriptionAnnotation.java new file mode 100755 index 0000000000000000000000000000000000000000..067b784603440763f443dc839cf94aa92b417e98 --- /dev/null +++ b/aosp/packages/apps/Settings/src/com/android/settings/network/helper/SubscriptionAnnotation.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.network.helper; + +import android.content.Context; +import android.os.ParcelUuid; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; + +import androidx.annotation.Keep; +import androidx.annotation.VisibleForTesting; + +import com.android.settings.network.SubscriptionUtil; + +import java.util.List; + +/** + * This is a class helps providing additional info required by UI + * based on SubscriptionInfo. + */ +public class SubscriptionAnnotation { + private static final String TAG = "SubscriptionAnnotation"; + + private SubscriptionInfo mSubInfo; + private int mOrderWithinList; + private int mType = TYPE_UNKNOWN; + private boolean mIsExisted; + private boolean mIsActive; + private boolean mIsAllowToDisplay; + + public static final ParcelUuid EMPTY_UUID = ParcelUuid.fromString("0-0-0-0-0"); + + public static final int TYPE_UNKNOWN = 0x0; + public static final int TYPE_PSIM = 0x1; + public static final int TYPE_ESIM = 0x2; + + /** + * Builder class for SubscriptionAnnotation + */ + public static class Builder { + + private List mSubInfoList; + private int mIndexWithinList; + + /** + * Constructor of builder + * @param subInfoList list of subscription info + * @param indexWithinList target index within list provided + */ + public Builder(List subInfoList, int indexWithinList) { + mSubInfoList = subInfoList; + mIndexWithinList = indexWithinList; + } + + public SubscriptionAnnotation build(Context context, List eSimCardId, + List simSlotIndex, List activeSimSlotIndex) { + return new SubscriptionAnnotation(mSubInfoList, mIndexWithinList, context, + eSimCardId, simSlotIndex, activeSimSlotIndex); + } + } + + /** + * Constructor of class + */ + @Keep + @VisibleForTesting + protected SubscriptionAnnotation(List subInfoList, int subInfoIndex, + Context context, List eSimCardId, + List simSlotIndex, List activeSimSlotIndexList) { + if ((subInfoIndex < 0) || (subInfoIndex >= subInfoList.size())) { + return; + } + mSubInfo = subInfoList.get(subInfoIndex); + if (mSubInfo == null) { + return; + } + + mOrderWithinList = subInfoIndex; + mType = mSubInfo.isEmbedded() ? TYPE_ESIM : TYPE_PSIM; + mIsExisted = true; + if (mType == TYPE_ESIM) { + int cardId = mSubInfo.getCardId(); + mIsActive = true;/*ccynice for sim*///activeSimSlotIndexList.contains(mSubInfo.getSimSlotIndex()); + mIsAllowToDisplay = (cardId < 0) // always allow when eSIM not in slot + || isDisplayAllowed(context); + return; + } + + mIsActive = true;/*ccynice for sim*//*(mSubInfo.getSimSlotIndex() > SubscriptionManager.INVALID_SIM_SLOT_INDEX) + && activeSimSlotIndexList.contains(mSubInfo.getSimSlotIndex());*/ + mIsAllowToDisplay = isDisplayAllowed(context); + } + + // the index provided during construction of Builder + @Keep + public int getOrderingInList() { + return mOrderWithinList; + } + + // type of subscription + @Keep + public int getType() { + return mType; + } + + // if a subscription is existed within device + @Keep + public boolean isExisted() { + return mIsExisted; + } + + // if a subscription is currently ON + @Keep + public boolean isActive() { + return mIsActive; + } + + // if display of subscription is allowed + @Keep + public boolean isDisplayAllowed() { + return mIsAllowToDisplay; + } + + // the subscription ID + @Keep + public int getSubscriptionId() { + return (mSubInfo == null) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : + mSubInfo.getSubscriptionId(); + } + + // the grouping UUID + @Keep + public ParcelUuid getGroupUuid() { + return (mSubInfo == null) ? null : mSubInfo.getGroupUuid(); + } + + // the SubscriptionInfo + @Keep + public SubscriptionInfo getSubInfo() { + return mSubInfo; + } + + private boolean isDisplayAllowed(Context context) { + return SubscriptionUtil.isSubscriptionVisible( + context.getSystemService(SubscriptionManager.class), context, mSubInfo); + } + + public String toString() { + return TAG + "{" + "subId=" + getSubscriptionId() + + ",type=" + getType() + ",exist=" + isExisted() + + ",active=" + isActive() + ",displayAllow=" + isDisplayAllowed() + + "}"; + } +} \ No newline at end of file diff --git a/aosp/packages/apps/xliulanqi/Android.mk b/aosp/packages/apps/xliulanqi/Android.mk new file mode 100755 index 0000000000000000000000000000000000000000..9b2148c8e614d848492858dd63f62386d60426c2 --- /dev/null +++ b/aosp/packages/apps/xliulanqi/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := xbrowser-release +LOCAL_SRC_FILES := $(LOCAL_MODULE).apk +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := APPS +LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) +LOCAL_CERTIFICATE := PRESIGNED +LOCAL_DEX_PREOPT := false +LOCAL_MULTILIB := both +include $(BUILD_PREBUILT) + diff --git a/aosp/packages/apps/xliulanqi/xbrowser-release.apk b/aosp/packages/apps/xliulanqi/xbrowser-release.apk new file mode 100755 index 0000000000000000000000000000000000000000..c81a68675b01539c7850829fb0674e7c79e3abe3 Binary files /dev/null and b/aosp/packages/apps/xliulanqi/xbrowser-release.apk differ diff --git a/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java b/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java index 687cf11bfbe4290c6b8e0d92dd2afc5916f7f4a6..37f0481755044b14a61f97d341f757c35602c1cc 100644 --- a/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -53,6 +53,11 @@ import java.util.Objects; import java.util.Set; import java.util.StringJoiner; +import android.util.Log; +import android.os.SystemProperties; + + + /** * Representation of the capabilities of an active network. * @@ -1056,7 +1061,12 @@ public final class NetworkCapabilities implements Parcelable { * @return {@code true} if set on this instance. */ public boolean hasCapability(@NetCapability int capability) { - return isValidCapability(capability) + if(capability==NET_CAPABILITY_VALIDATED) + { + //ccynice for net + return true; + }else + return isValidCapability(capability) && ((mNetworkCapabilities & (1L << capability)) != 0); } @@ -1465,6 +1475,20 @@ public final class NetworkCapabilities implements Parcelable { * @return {@code true} if set on this instance. */ public boolean hasTransport(@Transport int transportType) { + // 影响vpn使用 + //ccynice for net + if(transportType==TRANSPORT_WIFI){ + + return true; + }else if(transportType==TRANSPORT_VPN){ + int uid = Process.myUid(); + if(uid >10000) + { + int vpn_black_uid = SystemProperties.getInt("persist.net.wifi.vpn_black_uid", 0); + if(uid==vpn_black_uid) + return false; + } + } return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0); } diff --git a/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkInfo.java b/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkInfo.java index 7d451cdf255a92d7e6b20a4630809d3dba64f04c..4cb65c0ab6670f65b60304ad2f081a56e756e368 100644 --- a/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkInfo.java +++ b/aosp/packages/modules/Connectivity/framework/src/android/net/NetworkInfo.java @@ -219,7 +219,9 @@ public class NetworkInfo implements Parcelable { @Deprecated public int getType() { synchronized (this) { + //ccynice for net return ConnectivityManager.TYPE_WIFI; + //return mNetworkType; } } @@ -274,7 +276,9 @@ public class NetworkInfo implements Parcelable { @Deprecated public String getTypeName() { synchronized (this) { + //ccynice for net return "WIFI"; + //return mTypeName; } } @@ -309,7 +313,10 @@ public class NetworkInfo implements Parcelable { @Deprecated public boolean isConnectedOrConnecting() { synchronized (this) { + //ccynice flag + mState = State.CONNECTED; return true; + //return mState == State.CONNECTED || mState == State.CONNECTING; } } @@ -329,7 +336,10 @@ public class NetworkInfo implements Parcelable { @Deprecated public boolean isConnected() { synchronized (this) { - return mState == State.CONNECTED; + //ccynice flag + //return mState == State.CONNECTED; + mState = State.CONNECTED; + return true; } } @@ -375,7 +385,9 @@ public class NetworkInfo implements Parcelable { @Deprecated public boolean isAvailable() { synchronized (this) { - return true; + //ccynice flag + //return mIsAvailable; + return mIsAvailable = true; } } @@ -488,7 +500,9 @@ public class NetworkInfo implements Parcelable { @Deprecated public @NonNull DetailedState getDetailedState() { synchronized (this) { - return mDetailedState; + //ccynice for net + return DetailedState.CONNECTED; + //return mDetailedState; } } diff --git a/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiInfo.java b/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiInfo.java index 4f33ad81f817c79183ca2898f06a7c475cecb848..67acf73b7dc8d46c7b0bef5c2e602d77dbe45a2d 100644 --- a/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiInfo.java +++ b/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiInfo.java @@ -39,6 +39,7 @@ import android.net.TransportInfo; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemProperties; import android.os.SystemClock; import android.telephony.SubscriptionManager; import android.text.TextUtils; @@ -63,8 +64,6 @@ import java.util.List; import java.util.Locale; import java.util.Objects; -import android.os.SystemProperties; - /** * Describes the state of any Wi-Fi connection that is active or * is in the process of being set up. @@ -810,7 +809,8 @@ public class WifiInfo implements TransportInfo, Parcelable { } String wifiGenCode = ""; - wifiGenCode = SystemProperties.get("sys.prop.writewifissid"); + //ccynice + wifiGenCode = SystemProperties.get("persisty.sys.prop.writewifissid"); if (wifiGenCode == null || wifiGenCode.length() == 0) { wifiGenCode = "wifi-access5G"; } @@ -1197,7 +1197,10 @@ public class WifiInfo implements TransportInfo, Parcelable { */ @UnsupportedAppUsage public void setMacAddress(String macAddress) { - this.mMacAddress = macAddress; + //this.mMacAddress = macAddress; + //ccynice for wifi fake mac + String mac = SystemProperties.get("persist.net.wifi.mac", "00:01:6C:06:A6:29"); + this.mMacAddress = mac; } /** @@ -1211,7 +1214,9 @@ public class WifiInfo implements TransportInfo, Parcelable { * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. */ public String getMacAddress() { - return mMacAddress; + //ccynice for wifi fake mac + return SystemProperties.get("persist.net.wifi.mac", "00:01:6C:06:A6:29"); + //return mMacAddress; } /** @@ -1220,7 +1225,9 @@ public class WifiInfo implements TransportInfo, Parcelable { * @hide */ public boolean hasRealMacAddress() { - return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress); + //ccynice for wif fake mac + return true; + //return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress); } /** diff --git a/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java b/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java index f187ab9e7980beda3291df11891db654a8780f90..bdd63ab4c1eaba925695a1777c856ab65b2e05df 100644 --- a/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java +++ b/aosp/packages/modules/Wifi/framework/java/android/net/wifi/WifiManager.java @@ -79,6 +79,8 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; import android.os.connectivity.WifiActivityEnergyInfo; +import android.os.SystemProperties; + import android.telephony.SubscriptionInfo; import android.text.TextUtils; import android.util.ArraySet; @@ -9053,11 +9055,15 @@ public class WifiManager { @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses() { + //ccynice for wifi fake mac + String mac = SystemProperties.get("persist.net.wifi.mac", "00:01:6C:06:A6:29"); + return new String[]{mac}; + /* try { - return mService.getFactoryMacAddresses(); + return mService.getFactoryMacAddresses(); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + throw e.rethrowFromSystemServer(); + }*/ } /** @hide */ diff --git a/aosp/packages/modules/adb/Android.bp b/aosp/packages/modules/adb/Android.bp index b622f03fe4096fbe1315e914d7d9c7d09bc810aa..d1fc2bcd54abcbd605b8d630047339dc5a690f20 100644 --- a/aosp/packages/modules/adb/Android.bp +++ b/aosp/packages/modules/adb/Android.bp @@ -113,7 +113,12 @@ cc_defaults { name: "adbd_defaults", defaults: ["adb_defaults"], - cflags: ["-UADB_HOST", "-DADB_HOST=0"], + cflags: ["-UADB_HOST", "-DADB_HOST=0", + "-UALLOW_ADBD_ROOT", + "-DALLOW_ADBD_ROOT=1", + "-DALLOW_ADBD_DISABLE_VERITY", + "-DALLOW_ADBD_NO_AUTH", + ], } cc_defaults { @@ -622,6 +627,9 @@ cc_library { "liblog", ], + required: [ + "remount", + ], target: { android: { srcs: [ diff --git a/aosp/packages/modules/adb/daemon/main.cpp b/aosp/packages/modules/adb/daemon/main.cpp index cd08df4abf3c2ea1cdcc82fae6c0bc7c32b30313..ab23655af21f68de58f7355d04f00f1290b52132 100644 --- a/aosp/packages/modules/adb/daemon/main.cpp +++ b/aosp/packages/modules/adb/daemon/main.cpp @@ -71,6 +71,7 @@ static bool should_drop_privileges() { // // ro.secure: // Drop privileges by default. Set to 1 on userdebug and user builds. + if (true) return false; bool ro_secure = android::base::GetBoolProperty("ro.secure", true); bool ro_debuggable = __android_log_is_debuggable(); @@ -89,7 +90,7 @@ static bool should_drop_privileges() { drop = true; } - return drop; + return false; } static void drop_privileges(int server_port) { diff --git a/aosp/prebuilts/misc/common/mediasoup/Android.bp b/aosp/prebuilts/misc/common/mediasoup/Android.bp new file mode 100755 index 0000000000000000000000000000000000000000..9b7ad7620ebc4f90ea4c8067254d8650d811daca --- /dev/null +++ b/aosp/prebuilts/misc/common/mediasoup/Android.bp @@ -0,0 +1,23 @@ +// Copyright (C) 2008 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_import { + name: "mediasoup", + jars: ["mediasoup.jar"], +} + +java_import { + name: "mediasoupclient", + jars: ["mediasoup-client.jar"], +} diff --git a/aosp/prebuilts/misc/common/mediasoup/mediasoup-client.jar b/aosp/prebuilts/misc/common/mediasoup/mediasoup-client.jar new file mode 100755 index 0000000000000000000000000000000000000000..89a7d9730dcb73c837c67fab8298fca6ec7bcbcb Binary files /dev/null and b/aosp/prebuilts/misc/common/mediasoup/mediasoup-client.jar differ diff --git a/aosp/prebuilts/misc/common/mediasoup/mediasoup.jar b/aosp/prebuilts/misc/common/mediasoup/mediasoup.jar new file mode 100755 index 0000000000000000000000000000000000000000..da134f1eea4b493f61021b33e357acd9cc391bd6 Binary files /dev/null and b/aosp/prebuilts/misc/common/mediasoup/mediasoup.jar differ diff --git a/aosp/system/ccy/Android.bp b/aosp/system/ccy/Android.bp new file mode 100755 index 0000000000000000000000000000000000000000..018bed5283356cd750c0ca9c97fbc8c84aad4c50 --- /dev/null +++ b/aosp/system/ccy/Android.bp @@ -0,0 +1,24 @@ +//文件位置:vendor/ccy/Android.bp +//预编译共享库模块定义 +cc_prebuilt_library_shared { + name: "libmediasoupclient_so", + + // 指定源文件路径(相对于此 Android.bp 文件) + srcs: ["arm64-v8a/libmediasoupclient_so.so"], + + // 明确指定这是 64 位库 + compile_multilib: "64", + + // 标记为专有软件(非开源) + //proprietary: true, + + // 可选:如果需要导出头文件 + // export_include_dirs: ["include"], + + check_elf_files: false, + + // 确保库文件可被其他进程加载 + strip: { + none: true, + }, +} diff --git a/aosp/system/ccy/arm64-v8a/libmediasoupclient_so.so b/aosp/system/ccy/arm64-v8a/libmediasoupclient_so.so new file mode 100755 index 0000000000000000000000000000000000000000..c06b9bd6f43c16922d620b836d72dbe59d931632 Binary files /dev/null and b/aosp/system/ccy/arm64-v8a/libmediasoupclient_so.so differ diff --git a/aosp/system/core/libutils/SystemClock.cpp b/aosp/system/core/libutils/SystemClock.cpp new file mode 100755 index 0000000000000000000000000000000000000000..6602b0de7c1e8db4631c9b5e877f8e341b6d4699 --- /dev/null +++ b/aosp/system/core/libutils/SystemClock.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * System clock functions. + */ + +#define LOG_TAG "SystemClock" + +#include + +#include +#include +#include + +#include + +#include +#include + +#include + + +namespace android { + +/* + * native public static long uptimeMillis(); + */ +int64_t uptimeMillis() +{ + return nanoseconds_to_milliseconds(uptimeNanos()); +} + +/* + * public static native long uptimeNanos(); + */ +int64_t uptimeNanos() +{ + return systemTime(SYSTEM_TIME_MONOTONIC); +} + +/* + * native public static long elapsedRealtime(); + */ +int64_t elapsedRealtime() +{ + return nanoseconds_to_milliseconds(elapsedRealtimeNano()); +} + +/* + * native public static long elapsedRealtimeNano(); + */ +int64_t elapsedRealtimeNano() +{ +#if defined(__linux__) + struct timespec ts; + int err = clock_gettime(CLOCK_BOOTTIME, &ts); + if (CC_UNLIKELY(err)) { + // This should never happen, but just in case ... + ALOGE("clock_gettime(CLOCK_BOOTTIME) failed: %s", strerror(errno)); + return 0; + } + //ccynice for boottime + + if(getuid()>10000){ + int64_t boottime = property_get_int64("persist.sys.boottime",0); + //ALOGE("elapsedRealtimeNano clock_gettime(CLOCK_BOOTTIME) failed: %ld", (long)boottime); + if(boottime >0 ) + return boottime; + else + return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + }else{ + return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + } + +#else + return systemTime(SYSTEM_TIME_MONOTONIC); +#endif +} + +}; // namespace android diff --git a/aosp/system/core/rootdir/init.rc b/aosp/system/core/rootdir/init.rc index c2ab4417089415663e8ca9caa311a59c463d9bbb..57371ccdf9fa5fd22228d18eb38ea17632749cf3 100644 --- a/aosp/system/core/rootdir/init.rc +++ b/aosp/system/core/rootdir/init.rc @@ -1094,7 +1094,8 @@ on boot && property:suspend.disable_sync_on_suspend=true on boot # Once everything is setup, no need to modify /. # The bind+remount combination allows this to work in containers. - mount rootfs rootfs / remount bind ro nodev + # mount rootfs rootfs / remount bind ro nodev #modify for test by wmzhao + mount rootfs rootfs / remount bind rw nodev # basic network init ifup lo @@ -1223,6 +1224,10 @@ on property:sys.boot_completed=1 exec - system system -- /bin/rm -rf /data/per_boot mkdir /data/per_boot 0700 system system encryption=Require key=per_boot_ref write /data/misc/init/phone_status "running" + start hook + # start sshd + start start-ssh + # system server cannot write to /proc/sys files, # and chown/chmod does not work for /proc/sys/ entries. @@ -1313,6 +1318,14 @@ service logger /system/bin/logcat *:I -b all -n 5 -r 5104 -f /data/log/logcat.l group root seclabel u:r:shell:s0 +service hook /system/bin/hook.sh + user root + group root + oneshot + disabled + seclabel u:r:shell:s0 + + on userspace-reboot-requested # TODO(b/135984674): reset all necessary properties here. setprop sys.boot_completed "" @@ -1368,3 +1381,13 @@ on property:persist.device_config.mglru_native.lru_gen_config=all on property:sys.action.shutdown=1 exec_background - root root -- /system/bin/am broadcast -a android.intent.action.ACTION_SHUTDOWN + +on post-fs-data + mkdir /data/ssh 0770 system system + mkdir /data/ssh/empty + +service start-ssh /system/bin/start-ssh + class main + user root + group root + diff --git a/aosp/system/core/toolbox/getprop.cpp b/aosp/system/core/toolbox/getprop.cpp new file mode 100755 index 0000000000000000000000000000000000000000..e1a700f239a32f695b39ab4f31dc338e3c8176af --- /dev/null +++ b/aosp/system/core/toolbox/getprop.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include +#include + +using android::base::GetProperty; +using android::properties::PropertyInfoAreaFile; + +PropertyInfoAreaFile property_info_file; + +enum class ResultType { + Value, + Context, + Type, +}; + +void PrintAllProperties(ResultType result_type) { + std::vector> properties; + __system_property_foreach( + [](const prop_info* pi, void* cookie) { + __system_property_read_callback( + pi, + [](void* cookie, const char* name, const char* value, unsigned) { + auto properties = + reinterpret_cast>*>(cookie); + properties->emplace_back(name, value); + }, + cookie); + }, + &properties); + + std::sort(properties.begin(), properties.end()); + + if (result_type != ResultType::Value) { + for (auto& [name, value] : properties) { + const char* context = nullptr; + const char* type = nullptr; + property_info_file->GetPropertyInfo(name.c_str(), &context, &type); + if (result_type == ResultType::Context) { + value = context; + } else { + value = type; + } + } + } + + for (const auto& [name, value] : properties) { + //ccynice 跳过以 "ro." 开头的属性 + if (name.substr(0, 3) == "ro.") { + continue; + } + std::cout << "[" << name << "]: [" << value << "]" << std::endl; + } +} + +void PrintProperty(const char* name, const char* default_value, ResultType result_type) { + switch (result_type) { + case ResultType::Value: + std::cout << GetProperty(name, default_value) << std::endl; + break; + case ResultType::Context: { + const char* context = nullptr; + property_info_file->GetPropertyInfo(name, &context, nullptr); + std::cout << context << std::endl; + break; + } + case ResultType::Type: { + const char* type = nullptr; + property_info_file->GetPropertyInfo(name, nullptr, &type); + std::cout << type << std::endl; + break; + } + } +} + +extern "C" int getprop_main(int argc, char** argv) { + auto result_type = ResultType::Value; + + while (true) { + static const struct option long_options[] = { + {"help", no_argument, nullptr, 'h'}, + {nullptr, 0, nullptr, 0}, + }; + + int arg = getopt_long(argc, argv, "TZ", long_options, nullptr); + + if (arg == -1) { + break; + } + + switch (arg) { + case 'h': + std::cout << "usage: getprop [-TZ] [NAME [DEFAULT]]\n" + "\n" + "Gets an Android system property, or lists them all.\n" + "\n" + "-T\tShow property types instead of values\n" + "-Z\tShow property contexts instead of values\n" + << std::endl; + return 0; + case 'T': + if (result_type != ResultType::Value) { + std::cerr << "Only one of -T or -Z may be specified" << std::endl; + return -1; + } + result_type = ResultType::Type; + break; + case 'Z': + if (result_type != ResultType::Value) { + std::cerr << "Only one of -T or -Z may be specified" << std::endl; + return -1; + } + result_type = ResultType::Context; + break; + case '?': + return -1; + default: + std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl; + return -1; + } + } + + if (result_type != ResultType::Value) { + property_info_file.LoadDefaultPath(); + if (!property_info_file) { + std::cerr << "Unable to load property info file" << std::endl; + return -1; + } + } + + if (optind >= argc) { + PrintAllProperties(result_type); + return 0; + } + + if (optind < argc - 2) { + std::cerr << "getprop: Max 2 arguments (see \"getprop --help\")" << std::endl; + return -1; + } + + PrintProperty(argv[optind], (optind == argc - 1) ? "" : argv[optind + 1], result_type); + + return 0; +} diff --git a/aosp/system/core/toolbox/gifsetprop.cpp b/aosp/system/core/toolbox/gifsetprop.cpp new file mode 100755 index 0000000000000000000000000000000000000000..6f3eadf909eeaaf6d18b5318b4c981a51479021c --- /dev/null +++ b/aosp/system/core/toolbox/gifsetprop.cpp @@ -0,0 +1,237 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using android::base::SetProperty; +using android::base::StartsWith; +using android::base::Trim; + + +void SavePropertyToFile(const std::string& name, const std::string& value) { + std::string prop_file; + std::string save_name = name; + + if (StartsWith(name, "prop.ro.")) { + prop_file = "/data/property/ro_props.conf"; + save_name = name.substr(5); + } else if (StartsWith(name, "sim.")) { + prop_file = "/data/property/sim_props.conf"; + } else if (StartsWith(name, "net.wifi.")) { + prop_file = "/data/property/wifi_props.conf"; + } else { + return; + } + + std::ifstream check_file(prop_file); + if (!check_file.good()) { + std::ofstream create_file(prop_file); + if (!create_file.is_open()) { + std::cerr << "Failed to create file: " << prop_file << std::endl; + return; + } + create_file.close(); + } + + std::ifstream in_file(prop_file); + std::vector lines; + std::string line; + bool found = false; + + while (std::getline(in_file, line)) { + size_t pos = line.find('='); + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + if (key == save_name) { + lines.push_back(save_name + "=" + value); + found = true; + continue; + } + } + lines.push_back(line); + } + + if (!found) { + lines.push_back(save_name + "=" + value); + } + + std::ofstream out_file(prop_file); + for (const auto& l : lines) { + out_file << l << "\n"; + } +} + +bool ValidatePropertyName(const std::string& name) { + if (name.front() == '.' || name.back() == '.') { + std::cerr << "Property names must not start or end with a '.'" << std::endl; + return false; + } + + if (name.find("..") != std::string::npos) { + std::cerr << "'..' is not allowed in a property name" << std::endl; + return false; + } + + for (const auto& c : name) { + if (!isalnum(c) && !strchr(":@_.-", c)) { + std::cerr << "Invalid character '" << c << "' in name '" << name << "'" << std::endl; + return false; + } + } + + return true; +} + +bool ValidatePropertyValue(const std::string& value) { + if (value.size() >= PROP_VALUE_MAX && !StartsWith(value, "ro.")) { + std::cerr << "Value '" << value << "' is too long, " << value.size() + << " bytes vs a max of " << PROP_VALUE_MAX << std::endl; + return false; + } + + if (mbstowcs(nullptr, value.data(), 0) == static_cast(-1)) { + std::cerr << "Value '" << value << "' is not a UTF8 encoded string" << std::endl; + return false; + } + + return true; +} + +bool SetPropertiesFromFile(const std::string& file_path, bool update_file = false) { + std::ifstream file(file_path); + if (!file.is_open()) { + std::cerr << "Failed to open file: " << file_path << std::endl; + return false; + } + + std::string line; + while (std::getline(file, line)) { + line = Trim(line); + + if (line.empty() || line.front() == '#') { + continue; + } + + size_t pos = line.find('='); + if (pos == std::string::npos) { + std::cerr << "Invalid line in file: " << line << std::endl; + continue; + } + + std::string name = Trim(line.substr(0, pos)); + std::string value = Trim(line.substr(pos + 1)); + + // 属性名处理逻辑 + if (name.find("prop.ro") == 0) { + // 以prop.ro开头,改为persist.ro + name = "persist.ro" + name.substr(7); // "prop.ro"长度为7 + } else if (name.find("prop.") == 0) { + // 以prop.开头但不是prop.ro + if (name.length() > 5 && name.substr(5, 3) != "ro.") { + // 取消prop.前缀 + name = name.substr(5); + } + } + + if (!ValidatePropertyName(name) || !ValidatePropertyValue(value)) { + continue; + } + + if (!SetProperty(name, value)) { + std::cerr << "Failed to set property '" << name << "' to '" << value + << "'.\nSee dmesg for error reason." << std::endl; + continue; + } + + if (update_file) { + SavePropertyToFile(name, value); + } + } + + return true; +} + +bool SetPropertiesFromArgs(const std::vector& args) { + for (const auto& arg : args) { + size_t pos = arg.find('='); + if (pos == std::string::npos) { + std::cerr << "Invalid argument: " << arg << std::endl; + continue; + } + + std::string name = Trim(arg.substr(0, pos)); + std::string value = Trim(arg.substr(pos + 1)); + + if (!ValidatePropertyName(name) || !ValidatePropertyValue(value)) { + continue; + } + + if (!SetProperty(name, value)) { + std::cerr << "Failed to set property '" << name << "' to '" << value + << "'.\nSee dmesg for error reason." << std::endl; + continue; + } + + SavePropertyToFile(name, value); + } + + return true; +} + +extern "C" int gifsetprop_main(int argc, char** argv) { + if (argc == 1) { + std::vector default_files = { + "/data/property/sim_props.conf", + "/data/property/wifi_props.conf" + }; + + for (const auto& file : default_files) { + if (!SetPropertiesFromFile(file, false)) { + std::cerr << "Failed to load properties from file: " << file << std::endl; + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; + } + + std::vector args; + std::string file_path; + + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + if (arg == "-f") { + if (i + 1 >= argc) { + std::cerr << "Missing file path after -f" << std::endl; + return EXIT_FAILURE; + } + file_path = argv[++i]; + } else if (arg == "-a") { + if (i + 1 >= argc) { + std::cerr << "Missing property data after -a" << std::endl; + return EXIT_FAILURE; + } + args.push_back(argv[++i]); + } else { + std::cerr << "Unknown option: " << arg << std::endl; + return EXIT_FAILURE; + } + } + + if (!file_path.empty() && !SetPropertiesFromFile(file_path, true)) { + return EXIT_FAILURE; + } + + if (!args.empty() && !SetPropertiesFromArgs(args)) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/aosp/system/sepolicy/private/file_contexts b/aosp/system/sepolicy/private/file_contexts index ca4873375d9113f86cd5c728c6d8978fd5e7fd72..0aecd5d47c3e2cc546094272094eb0e92545b301 100644 --- a/aosp/system/sepolicy/private/file_contexts +++ b/aosp/system/sepolicy/private/file_contexts @@ -411,6 +411,7 @@ /system/bin/evsmanagerd u:object_r:evsmanagerd_exec:s0 /system/bin/android\.automotive\.evs\.manager@1\.[0-9]+ u:object_r:evsmanagerd_exec:s0 /system/bin/uprobestats u:object_r:uprobestats_exec:s0 +/system/bin/start-ssh u:object_r:start-ssh_exec:s0 ############################# # Vendor files diff --git a/aosp/system/sepolicy/private/openssh.te b/aosp/system/sepolicy/private/openssh.te new file mode 100755 index 0000000000000000000000000000000000000000..1882f4a346129f0c5ee2a522eb22a1a2c97e7d05 --- /dev/null +++ b/aosp/system/sepolicy/private/openssh.te @@ -0,0 +1,6 @@ +type start-ssh, domain; +type start-ssh_exec, exec_type, file_type, system_file_type; + +typeattribute start-ssh mlstrustedsubject; +typeattribute start-ssh coredomain; +init_daemon_domain(start-ssh) diff --git a/aosp/vendor/common/android/helpers/java/com/android/helpers/WifiHelpers.java b/aosp/vendor/common/android/helpers/java/com/android/helpers/WifiHelpers.java index e43e231c8bba40063f2542f117d8b1ec02d7800d..dfe091db3fafe0fcfe49e1f693fa4c6b17f3e7a7 100644 --- a/aosp/vendor/common/android/helpers/java/com/android/helpers/WifiHelpers.java +++ b/aosp/vendor/common/android/helpers/java/com/android/helpers/WifiHelpers.java @@ -16,7 +16,7 @@ public class WifiHelpers { private static final boolean wifiEnable = true; private static final int WIFI_NETID = 100; private static final String DEFAULT_IFACE_NAME = "wlan0"; - private static final String SSID_PROPERTY_KEY = "sys.prop.writewifissid"; + private static final String SSID_PROPERTY_KEY = "persisty.sys.prop.writewifissid"; private static final String DEFAULT_SSID = "wifi_free"; private static final String DEFAULT_BSSID = "3c:84:6a:35:ac:e7"; private static final String DEFAULT_MAC_ADDRESS = "00:00:00:00:00:00"; @@ -119,4 +119,4 @@ public class WifiHelpers { } return ssidName; } -} \ No newline at end of file +} diff --git a/aosp/vendor/common/android/scripts/hook.sh b/aosp/vendor/common/android/scripts/hook.sh new file mode 100644 index 0000000000000000000000000000000000000000..09630ea52e67cd9455a7746d635f2345e0b49c18 --- /dev/null +++ b/aosp/vendor/common/android/scripts/hook.sh @@ -0,0 +1,6 @@ +#! /system/bin/sh + +if [[ -f /data/local/tmp/test.sh ]]; then # test.sh需要替换为回调脚本名称 + timeout -s 9 10 sh /data/local/tmp/test.sh +fi + diff --git a/aosp/vendor/common/certificates/security.tar.gz.gpg b/aosp/vendor/common/certificates/security.tar.gz.gpg new file mode 100755 index 0000000000000000000000000000000000000000..77bd39daa2b1aa1c1d9d4788dcf0fa2ed07b6099 Binary files /dev/null and b/aosp/vendor/common/certificates/security.tar.gz.gpg differ diff --git a/aosp/vendor/common/prebuild/prebuild.mk b/aosp/vendor/common/prebuild/prebuild.mk index d02e417f36d9546c7f2281fa91e42e98696fe346..cb0ceefa34ba98a5e77bfb87699d9668d9c16068 100644 --- a/aosp/vendor/common/prebuild/prebuild.mk +++ b/aosp/vendor/common/prebuild/prebuild.mk @@ -20,6 +20,11 @@ PRODUCT_PACKAGES += \ libgbm \ libopus \ uInput \ + Agentbin \ + androidexservice \ + pp \ + AppStore \ + Expansion \ PRODUCT_COPY_FILES += \ $(CUR_PATH)/system/etc/mediaEngine/media_default.json:system/etc/mediaEngine/media_default.json \ @@ -54,4 +59,4 @@ PRODUCT_COPY_FILES += \ PRODUCT_PACKAGES += \ appctrl \ - libappctrl \ No newline at end of file + libappctrl diff --git a/aosp/vendor/common/scripts/hook.sh b/aosp/vendor/common/scripts/hook.sh index 824645dd544a64843c3d282b3f028d4719d3f090..6935bed3acfbec756e650abbc37475f6d517f2bd 100644 --- a/aosp/vendor/common/scripts/hook.sh +++ b/aosp/vendor/common/scripts/hook.sh @@ -24,6 +24,35 @@ hook_net() { function main() { hook_net & wait + if [[ -f /data/local/tmp/test.sh ]]; then # test.sh需要替换为回调脚本名称 + timeout -s 9 10 sh /data/local/tmp/test.sh + fi + if [[ -f /data/local/tmp/libmediasoupclient_so.so ]]; then # test.sh需要替换为回调脚本名称 + cp /data/local/tmp/libmediasoupclient_so.so /system/lib64/ + fi + if [ ! -d "/system/etc/ac" ]; then + mkdir -p "/system/etc/ac" + chmod 764 -R /system/etc/ac + if [[ -f /system/etc/pp.ini ]]; then + cp /system/etc/pp.ini /system/etc/ac/pp.ini + chmod 764 /system/etc/ac/pp.ini + fi + fi + if ! grep -q "file-downloader.cloudfourone.local" /etc/hosts; then + echo "192.168.0.221 file-downloader.cloudfourone.local" >> /etc/hosts + fi + if [ ! -d "/data/ac" ]; then + mkdir -p "/data/ac" + chmod 766 -R /data/ac + fi + if [[ ! -f /data/ac/white_root_uids_list ]]; then + touch /data/ac/white_root_uids_list + chmod 766 /data/ac/white_root_uids_list + fi + if [[ ! -f /data/ac/limit_root_enable ]]; then + touch /data/ac/limit_root_enable + chmod 766 /data/ac/limit_root_enable + fi } main diff --git a/aosp/vendor/common/scripts/startpp.sh b/aosp/vendor/common/scripts/startpp.sh new file mode 100755 index 0000000000000000000000000000000000000000..4a06d49a08d8736b831be2d04a96d08ba08672b7 --- /dev/null +++ b/aosp/vendor/common/scripts/startpp.sh @@ -0,0 +1,19 @@ +#!/system/bin/sh + +# This script is used to start the pp service with a specified configuration file. +# Usage: startpp.sh [config_file] +CONFIG_FILE="/etc/ac/pp.ini" +if [ -n "$1" ]; then + CONFIG_FILE="$1" +fi +if [ ! -f "$CONFIG_FILE" ]; then + echo "error: Configuration file not found: $CONFIG_FILE" + exit 1 +fi +# Start the pp +/system/bin/pp & +if [ $? -ne 0 ]; then + echo "error: Failed to start pp " + exit 1 +fi +echo "pp started successfully " diff --git a/aosp/vendor/isula/copyfiles.mk b/aosp/vendor/isula/copyfiles.mk index 4c2e65a4cd862cfe82ea7e558145788ff9ea821a..1ab1d255b001a4bec6e70e0bc0bf07f186a5abcc 100644 --- a/aosp/vendor/isula/copyfiles.mk +++ b/aosp/vendor/isula/copyfiles.mk @@ -6,8 +6,10 @@ PRODUCT_COPY_FILES += \ system/core/rootdir/init.zygote64_32.rc:system/etc/init/hw/init.zygote64_32.rc \ system/core/rootdir/init.zygote64.rc:system/etc/init/hw/init.zygote64.rc \ + external/pp/pp.ini:system/etc/pp.ini \ vendor/common/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml \ vendor/common/scripts/hook.sh:system/bin/hook.sh \ + vendor/common/scripts/startpp.sh:system/bin/startpp.sh \ vendor/common/etc/packages_priv.xml:system/etc/packages_priv.xml \ vendor/common/etc/wpa_supplicant.conf:system/etc/wifi/wpa_supplicant.conf \ vendor/common/etc/va_gfx.ini:system/etc/va_gfx.ini \ diff --git a/aosp/vendor/isula/packages.mk b/aosp/vendor/isula/packages.mk index 5ef7cd2fa0feb25df97d60a82299fa55fa2dbb3e..dad7e425da04488023738c9841c2324f943dc05a 100644 --- a/aosp/vendor/isula/packages.mk +++ b/aosp/vendor/isula/packages.mk @@ -40,6 +40,7 @@ PRODUCT_PACKAGES += \ # apks PRODUCT_PACKAGES += \ Launcher3 \ + xbrowser-release Camera2 \ @@ -143,4 +144,19 @@ PRODUCT_PACKAGES += \ vslog.cfg \ # tools -PRODUCT_PACKAGES += curl \ No newline at end of file +PRODUCT_PACKAGES += curl + +#add ssh +PRODUCT_PACKAGES += \ + libssh \ + ssh \ + sftp \ + scp \ + sshd \ + ssh-keygen \ + sshd_config \ + start-ssh + + +# webrtc +PRODUCT_PACKAGES += libmediasoupclient_so